From c131280c03bd1c225c2e64e9ef75873ffca3d96e Mon Sep 17 00:00:00 2001 From: Stephan Gerhold Date: Mon, 18 Nov 2019 14:02:52 +0100 Subject: [PATCH 0001/1437] drm/mcde: dsi: Fix invalid pointer dereference if panel cannot be found The "panel" pointer is not reset to NULL if of_drm_find_panel() returns an error. Therefore we later assume that a panel was found, and try to dereference the error pointer, resulting in: mcde-dsi a0351000.dsi: failed to find panel try bridge (4294966779) Unable to handle kernel paging request at virtual address fffffe03 PC is at drm_panel_bridge_add.part.0+0x10/0x5c LR is at mcde_dsi_bind+0x120/0x464 ... Reset "panel" to NULL to avoid this problem. Also change the format string of the error to %ld to print the negative errors correctly. The crash above then becomes: mcde-dsi a0351000.dsi: failed to find panel try bridge (-517) mcde-dsi a0351000.dsi: no panel or bridge ... Fixes: 5fc537bfd000 ("drm/mcde: Add new driver for ST-Ericsson MCDE") Signed-off-by: Stephan Gerhold Signed-off-by: Linus Walleij Link: https://patchwork.freedesktop.org/patch/msgid/20191118130252.170324-1-stephan@gerhold.net --- drivers/gpu/drm/mcde/mcde_dsi.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/mcde/mcde_dsi.c b/drivers/gpu/drm/mcde/mcde_dsi.c index f9c9e32b299c9..35bb825d19188 100644 --- a/drivers/gpu/drm/mcde/mcde_dsi.c +++ b/drivers/gpu/drm/mcde/mcde_dsi.c @@ -935,11 +935,13 @@ static int mcde_dsi_bind(struct device *dev, struct device *master, for_each_available_child_of_node(dev->of_node, child) { panel = of_drm_find_panel(child); if (IS_ERR(panel)) { - dev_err(dev, "failed to find panel try bridge (%lu)\n", + dev_err(dev, "failed to find panel try bridge (%ld)\n", PTR_ERR(panel)); + panel = NULL; + bridge = of_drm_find_bridge(child); if (IS_ERR(bridge)) { - dev_err(dev, "failed to find bridge (%lu)\n", + dev_err(dev, "failed to find bridge (%ld)\n", PTR_ERR(bridge)); return PTR_ERR(bridge); } -- GitLab From 6645d42d79d33e8a9fe262660a75d5f4556bbea9 Mon Sep 17 00:00:00 2001 From: Navid Emamdoost Date: Fri, 22 Nov 2019 16:09:55 -0600 Subject: [PATCH 0002/1437] dma-buf: Fix memory leak in sync_file_merge() In the implementation of sync_file_merge() the allocated sync_file is leaked if number of fences overflows. Release sync_file by goto err. Fixes: a02b9dc90d84 ("dma-buf/sync_file: refactor fence storage in struct sync_file") Signed-off-by: Navid Emamdoost Cc: stable@vger.kernel.org Signed-off-by: Daniel Vetter Link: https://patchwork.freedesktop.org/patch/msgid/20191122220957.30427-1-navid.emamdoost@gmail.com --- drivers/dma-buf/sync_file.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/dma-buf/sync_file.c b/drivers/dma-buf/sync_file.c index 25c5c071645bd..91185db9a952f 100644 --- a/drivers/dma-buf/sync_file.c +++ b/drivers/dma-buf/sync_file.c @@ -221,7 +221,7 @@ static struct sync_file *sync_file_merge(const char *name, struct sync_file *a, a_fences = get_fences(a, &a_num_fences); b_fences = get_fences(b, &b_num_fences); if (a_num_fences > INT_MAX - b_num_fences) - return NULL; + goto err; num_fences = a_num_fences + b_num_fences; -- GitLab From 8d985555ddaa6735c5d1f3d6e4d4d9cb47f27878 Mon Sep 17 00:00:00 2001 From: Alagu Sankar Date: Fri, 15 Nov 2019 09:20:58 +0200 Subject: [PATCH 0003/1437] ath10k: enable RX bundle receive for sdio The existing implementation of initiating multiple sdio transfers for receive bundling is slowing down the receive speed. Combining the transfers using a bundle method would be ideal. The transmission utilization ratio for sdio bus for small packet is slow, because the space and time cost for sdio bus is same for large length packet and small length packet. So the speed of data for large length packet is higher than small length. Test result of different length of data: data packet(byte) cost time(us) calculated rate(Mbps) 256 28 73 512 33 124 1024 35 234 1792 45 318 14336 168 682 28672 333 688 57344 660 695 Tested with QCA6174 SDIO with firmware WLAN.RMH.4.4.1-00017-QCARMSWPZ-1 Signed-off-by: Alagu Sankar Signed-off-by: Wen Gong Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/sdio.c | 110 ++++++++++++++++--------- drivers/net/wireless/ath/ath10k/sdio.h | 11 ++- 2 files changed, 79 insertions(+), 42 deletions(-) diff --git a/drivers/net/wireless/ath/ath10k/sdio.c b/drivers/net/wireless/ath/ath10k/sdio.c index 120200a93bcc3..7f25079dde2da 100644 --- a/drivers/net/wireless/ath/ath10k/sdio.c +++ b/drivers/net/wireless/ath/ath10k/sdio.c @@ -24,6 +24,8 @@ #include "trace.h" #include "sdio.h" +#define ATH10K_SDIO_VSG_BUF_SIZE (32 * 1024) + /* inlined helper functions */ static inline int ath10k_sdio_calc_txrx_padded_len(struct ath10k_sdio *ar_sdio, @@ -484,11 +486,11 @@ out: return ret; } -static int ath10k_sdio_mbox_alloc_pkt_bundle(struct ath10k *ar, - struct ath10k_sdio_rx_data *rx_pkts, - struct ath10k_htc_hdr *htc_hdr, - size_t full_len, size_t act_len, - size_t *bndl_cnt) +static int ath10k_sdio_mbox_alloc_bundle(struct ath10k *ar, + struct ath10k_sdio_rx_data *rx_pkts, + struct ath10k_htc_hdr *htc_hdr, + size_t full_len, size_t act_len, + size_t *bndl_cnt) { int ret, i; @@ -529,6 +531,7 @@ static int ath10k_sdio_mbox_rx_alloc(struct ath10k *ar, size_t full_len, act_len; bool last_in_bundle; int ret, i; + int pkt_cnt = 0; if (n_lookaheads > ATH10K_SDIO_MAX_RX_MSGS) { ath10k_warn(ar, @@ -572,20 +575,19 @@ static int ath10k_sdio_mbox_rx_alloc(struct ath10k *ar, */ size_t bndl_cnt; - ret = ath10k_sdio_mbox_alloc_pkt_bundle(ar, - &ar_sdio->rx_pkts[i], - htc_hdr, - full_len, - act_len, - &bndl_cnt); + ret = ath10k_sdio_mbox_alloc_bundle(ar, + &ar_sdio->rx_pkts[pkt_cnt], + htc_hdr, + full_len, + act_len, + &bndl_cnt); if (ret) { ath10k_warn(ar, "alloc_bundle error %d\n", ret); goto err; } - n_lookaheads += bndl_cnt; - i += bndl_cnt; + pkt_cnt += bndl_cnt; /*Next buffer will be the last in the bundle */ last_in_bundle = true; } @@ -597,7 +599,7 @@ static int ath10k_sdio_mbox_rx_alloc(struct ath10k *ar, if (htc_hdr->flags & ATH10K_HTC_FLAGS_RECV_1MORE_BLOCK) full_len += ATH10K_HIF_MBOX_BLOCK_SIZE; - ret = ath10k_sdio_mbox_alloc_rx_pkt(&ar_sdio->rx_pkts[i], + ret = ath10k_sdio_mbox_alloc_rx_pkt(&ar_sdio->rx_pkts[pkt_cnt], act_len, full_len, last_in_bundle, @@ -606,9 +608,11 @@ static int ath10k_sdio_mbox_rx_alloc(struct ath10k *ar, ath10k_warn(ar, "alloc_rx_pkt error %d\n", ret); goto err; } + + pkt_cnt++; } - ar_sdio->n_rx_pkts = i; + ar_sdio->n_rx_pkts = pkt_cnt; return 0; @@ -622,59 +626,76 @@ err: return ret; } -static int ath10k_sdio_mbox_rx_packet(struct ath10k *ar, - struct ath10k_sdio_rx_data *pkt) +static int ath10k_sdio_mbox_rx_fetch(struct ath10k *ar) { struct ath10k_sdio *ar_sdio = ath10k_sdio_priv(ar); + struct ath10k_sdio_rx_data *pkt = &ar_sdio->rx_pkts[0]; struct sk_buff *skb = pkt->skb; struct ath10k_htc_hdr *htc_hdr; int ret; ret = ath10k_sdio_readsb(ar, ar_sdio->mbox_info.htc_addr, skb->data, pkt->alloc_len); - if (ret) - goto out; - /* Update actual length. The original length may be incorrect, - * as the FW will bundle multiple packets as long as their sizes - * fit within the same aligned length (pkt->alloc_len). - */ - htc_hdr = (struct ath10k_htc_hdr *)skb->data; - pkt->act_len = le16_to_cpu(htc_hdr->len) + sizeof(*htc_hdr); - if (pkt->act_len > pkt->alloc_len) { - ath10k_warn(ar, "rx packet too large (%zu > %zu)\n", - pkt->act_len, pkt->alloc_len); - ret = -EMSGSIZE; - goto out; + if (ret) { + ar_sdio->n_rx_pkts = 0; + ath10k_sdio_mbox_free_rx_pkt(pkt); + return ret; } - skb_put(skb, pkt->act_len); - -out: + htc_hdr = (struct ath10k_htc_hdr *)skb->data; + pkt->act_len = le16_to_cpu(htc_hdr->len) + sizeof(*htc_hdr); pkt->status = ret; + skb_put(skb, pkt->act_len); return ret; } -static int ath10k_sdio_mbox_rx_fetch(struct ath10k *ar) +static int ath10k_sdio_mbox_rx_fetch_bundle(struct ath10k *ar) { struct ath10k_sdio *ar_sdio = ath10k_sdio_priv(ar); + struct ath10k_sdio_rx_data *pkt; + struct ath10k_htc_hdr *htc_hdr; int ret, i; + u32 pkt_offset, virt_pkt_len; + virt_pkt_len = 0; + for (i = 0; i < ar_sdio->n_rx_pkts; i++) + virt_pkt_len += ar_sdio->rx_pkts[i].alloc_len; + + if (virt_pkt_len > ATH10K_SDIO_VSG_BUF_SIZE) { + ath10k_warn(ar, "sdio vsg buffer size limit: %d\n", virt_pkt_len); + ret = -E2BIG; + goto err; + } + + ret = ath10k_sdio_readsb(ar, ar_sdio->mbox_info.htc_addr, + ar_sdio->vsg_buffer, virt_pkt_len); + if (ret) { + ath10k_warn(ar, "failed to read bundle packets: %d", ret); + goto err; + } + + pkt_offset = 0; for (i = 0; i < ar_sdio->n_rx_pkts; i++) { - ret = ath10k_sdio_mbox_rx_packet(ar, - &ar_sdio->rx_pkts[i]); - if (ret) - goto err; + pkt = &ar_sdio->rx_pkts[i]; + htc_hdr = (struct ath10k_htc_hdr *)(ar_sdio->vsg_buffer + pkt_offset); + pkt->act_len = le16_to_cpu(htc_hdr->len) + sizeof(*htc_hdr); + + skb_put_data(pkt->skb, htc_hdr, pkt->act_len); + pkt->status = 0; + pkt_offset += pkt->alloc_len; } return 0; err: /* Free all packets that was not successfully fetched. */ - for (; i < ar_sdio->n_rx_pkts; i++) + for (i = 0; i < ar_sdio->n_rx_pkts; i++) ath10k_sdio_mbox_free_rx_pkt(&ar_sdio->rx_pkts[i]); + ar_sdio->n_rx_pkts = 0; + return ret; } @@ -717,7 +738,10 @@ static int ath10k_sdio_mbox_rxmsg_pending_handler(struct ath10k *ar, */ *done = false; - ret = ath10k_sdio_mbox_rx_fetch(ar); + if (ar_sdio->n_rx_pkts > 1) + ret = ath10k_sdio_mbox_rx_fetch_bundle(ar); + else + ret = ath10k_sdio_mbox_rx_fetch(ar); /* Process fetched packets. This will potentially update * n_lookaheads depending on if the packets contain lookahead @@ -2020,6 +2044,12 @@ static int ath10k_sdio_probe(struct sdio_func *func, goto err_core_destroy; } + ar_sdio->vsg_buffer = devm_kmalloc(ar->dev, ATH10K_SDIO_VSG_BUF_SIZE, GFP_KERNEL); + if (!ar_sdio->vsg_buffer) { + ret = -ENOMEM; + goto err_core_destroy; + } + ar_sdio->irq_data.irq_en_reg = devm_kzalloc(ar->dev, sizeof(struct ath10k_sdio_irq_enable_regs), GFP_KERNEL); diff --git a/drivers/net/wireless/ath/ath10k/sdio.h b/drivers/net/wireless/ath/ath10k/sdio.h index b8c7ac0330bd4..8d5b09f8b4f36 100644 --- a/drivers/net/wireless/ath/ath10k/sdio.h +++ b/drivers/net/wireless/ath/ath10k/sdio.h @@ -138,8 +138,8 @@ struct ath10k_sdio_irq_proc_regs { u8 rx_lookahead_valid; u8 host_int_status2; u8 gmbox_rx_avail; - __le32 rx_lookahead[2]; - __le32 rx_gmbox_lookahead_alias[2]; + __le32 rx_lookahead[2 * ATH10K_HIF_MBOX_NUM_MAX]; + __le32 int_status_enable; }; struct ath10k_sdio_irq_enable_regs { @@ -196,6 +196,13 @@ struct ath10k_sdio { struct ath10k *ar; struct ath10k_sdio_irq_data irq_data; + /* temporary buffer for sdio read. + * It is allocated when probe, and used for receive bundled packets, + * the read for bundled packets is not parallel, so it does not need + * protected. + */ + u8 *vsg_buffer; + /* temporary buffer for BMI requests */ u8 *bmi_buf; -- GitLab From 224776520ead69e9e85e33e5eb8c705c3552c4e1 Mon Sep 17 00:00:00 2001 From: Wen Gong Date: Fri, 15 Nov 2019 09:21:01 +0200 Subject: [PATCH 0004/1437] ath10k: change max RX bundle size from 8 to 32 for sdio The max bundle size support by firmware is 32, change it from 8 to 32 will help performance. This results in significant performance improvement on RX path. The real max rx bundle is decided in ath10k_htc_wait_target(), it is the min value of HTC_HOST_MAX_MSG_PER_RX_BUNDLE and the value reported from firmware. So this change shouldn't cause any regressions with other hardware supported by ath10k. Tested with QCA6174 SDIO with firmware WLAN.RMH.4.4.1-00017-QCARMSWPZ-1. Signed-off-by: Wen Gong Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/htc.h | 21 +++++++++++++++++++-- drivers/net/wireless/ath/ath10k/sdio.c | 4 ++-- drivers/net/wireless/ath/ath10k/sdio.h | 4 ++-- 3 files changed, 23 insertions(+), 6 deletions(-) diff --git a/drivers/net/wireless/ath/ath10k/htc.h b/drivers/net/wireless/ath/ath10k/htc.h index f55d3caec61f9..16102175e07a0 100644 --- a/drivers/net/wireless/ath/ath10k/htc.h +++ b/drivers/net/wireless/ath/ath10k/htc.h @@ -12,6 +12,7 @@ #include #include #include +#include struct ath10k; @@ -39,7 +40,7 @@ struct ath10k; * 4-byte aligned. */ -#define HTC_HOST_MAX_MSG_PER_RX_BUNDLE 8 +#define HTC_HOST_MAX_MSG_PER_RX_BUNDLE 32 enum ath10k_htc_tx_flags { ATH10K_HTC_FLAG_NEED_CREDIT_UPDATE = 0x01, @@ -49,9 +50,25 @@ enum ath10k_htc_tx_flags { enum ath10k_htc_rx_flags { ATH10K_HTC_FLAGS_RECV_1MORE_BLOCK = 0x01, ATH10K_HTC_FLAG_TRAILER_PRESENT = 0x02, - ATH10K_HTC_FLAG_BUNDLE_MASK = 0xF0 }; +#define ATH10K_HTC_FLAG_BUNDLE_MASK GENMASK(7, 4) + +/* bits 2-3 are for extra bundle count bits 4-5 */ +#define ATH10K_HTC_BUNDLE_EXTRA_MASK GENMASK(3, 2) +#define ATH10K_HTC_BUNDLE_EXTRA_SHIFT 4 + +static inline unsigned int ath10k_htc_get_bundle_count(u8 flags) +{ + unsigned int count, extra_count; + + count = FIELD_GET(ATH10K_HTC_FLAG_BUNDLE_MASK, flags); + extra_count = FIELD_GET(ATH10K_HTC_BUNDLE_EXTRA_MASK, flags) << + ATH10K_HTC_BUNDLE_EXTRA_SHIFT; + + return count + extra_count; +} + struct ath10k_htc_hdr { u8 eid; /* @enum ath10k_htc_ep_id */ u8 flags; /* @enum ath10k_htc_tx_flags, ath10k_htc_rx_flags */ diff --git a/drivers/net/wireless/ath/ath10k/sdio.c b/drivers/net/wireless/ath/ath10k/sdio.c index 7f25079dde2da..23a6176fbde67 100644 --- a/drivers/net/wireless/ath/ath10k/sdio.c +++ b/drivers/net/wireless/ath/ath10k/sdio.c @@ -24,7 +24,7 @@ #include "trace.h" #include "sdio.h" -#define ATH10K_SDIO_VSG_BUF_SIZE (32 * 1024) +#define ATH10K_SDIO_VSG_BUF_SIZE (64 * 1024) /* inlined helper functions */ @@ -494,7 +494,7 @@ static int ath10k_sdio_mbox_alloc_bundle(struct ath10k *ar, { int ret, i; - *bndl_cnt = FIELD_GET(ATH10K_HTC_FLAG_BUNDLE_MASK, htc_hdr->flags); + *bndl_cnt = ath10k_htc_get_bundle_count(htc_hdr->flags); if (*bndl_cnt > HTC_HOST_MAX_MSG_PER_RX_BUNDLE) { ath10k_warn(ar, diff --git a/drivers/net/wireless/ath/ath10k/sdio.h b/drivers/net/wireless/ath/ath10k/sdio.h index 8d5b09f8b4f36..00bd4caa7fa18 100644 --- a/drivers/net/wireless/ath/ath10k/sdio.h +++ b/drivers/net/wireless/ath/ath10k/sdio.h @@ -89,10 +89,10 @@ * to the maximum value (HTC_HOST_MAX_MSG_PER_RX_BUNDLE). * * in this case the driver must allocate - * (HTC_HOST_MAX_MSG_PER_RX_BUNDLE * HTC_HOST_MAX_MSG_PER_RX_BUNDLE) skb's. + * (HTC_HOST_MAX_MSG_PER_RX_BUNDLE * 2) skb's. */ #define ATH10K_SDIO_MAX_RX_MSGS \ - (HTC_HOST_MAX_MSG_PER_RX_BUNDLE * HTC_HOST_MAX_MSG_PER_RX_BUNDLE) + (HTC_HOST_MAX_MSG_PER_RX_BUNDLE * 2) #define ATH10K_FIFO_TIMEOUT_AND_CHIP_CONTROL 0x00000868u #define ATH10K_FIFO_TIMEOUT_AND_CHIP_CONTROL_DISABLE_SLEEP_OFF 0xFFFEFFFF -- GitLab From 67654b26c903de9949202f23f4864a74a09e22d5 Mon Sep 17 00:00:00 2001 From: Wen Gong Date: Fri, 15 Nov 2019 09:21:03 +0200 Subject: [PATCH 0005/1437] ath10k: add workqueue for RX path of sdio For RX, it has two parts, one is to read data from sdio, another is to indicate the packets to upper stack. Recently it has only one thread to do all RX things, it results that it is sequential for RX and low throughout, change RX to parallel for the two parts will increase throughout. This patch move the indication to a workqueue, it results in significant performance improvement on RX path. Udp rx throughout is 200Mbps without this patch, and it arrives 400Mbps with this patch. Tested with QCA6174 SDIO with firmware WLAN.RMH.4.4.1-00017-QCARMSWPZ-1 Signed-off-by: Wen Gong Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/core.h | 1 + drivers/net/wireless/ath/ath10k/htt_rx.c | 3 ++ drivers/net/wireless/ath/ath10k/sdio.c | 38 ++++++++++++++++++++++-- drivers/net/wireless/ath/ath10k/sdio.h | 5 ++++ 4 files changed, 44 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h index af68eb5d07768..c5407f5080b27 100644 --- a/drivers/net/wireless/ath/ath10k/core.h +++ b/drivers/net/wireless/ath/ath10k/core.h @@ -124,6 +124,7 @@ struct ath10k_skb_cb { struct ath10k_skb_rxcb { dma_addr_t paddr; struct hlist_node hlist; + u8 eid; }; static inline struct ath10k_skb_cb *ATH10K_SKB_CB(struct sk_buff *skb) diff --git a/drivers/net/wireless/ath/ath10k/htt_rx.c b/drivers/net/wireless/ath/ath10k/htt_rx.c index d95b63f133abf..edda6dd93476b 100644 --- a/drivers/net/wireless/ath/ath10k/htt_rx.c +++ b/drivers/net/wireless/ath/ath10k/htt_rx.c @@ -2235,7 +2235,10 @@ static bool ath10k_htt_rx_proc_rx_ind_hl(struct ath10k_htt *htt, hdr = (struct ieee80211_hdr *)skb->data; qos = ieee80211_is_data_qos(hdr->frame_control); + rx_status = IEEE80211_SKB_RXCB(skb); + memset(rx_status, 0, sizeof(*rx_status)); + rx_status->chains |= BIT(0); if (rx->ppdu.combined_rssi == 0) { /* SDIO firmware does not provide signal */ diff --git a/drivers/net/wireless/ath/ath10k/sdio.c b/drivers/net/wireless/ath/ath10k/sdio.c index 23a6176fbde67..c7d09b07a382f 100644 --- a/drivers/net/wireless/ath/ath10k/sdio.c +++ b/drivers/net/wireless/ath/ath10k/sdio.c @@ -419,6 +419,7 @@ static int ath10k_sdio_mbox_rx_process_packets(struct ath10k *ar, struct ath10k_htc *htc = &ar->htc; struct ath10k_sdio_rx_data *pkt; struct ath10k_htc_ep *ep; + struct ath10k_skb_rxcb *cb; enum ath10k_htc_ep_id id; int ret, i, *n_lookahead_local; u32 *lookaheads_local; @@ -464,10 +465,16 @@ static int ath10k_sdio_mbox_rx_process_packets(struct ath10k *ar, if (ret) goto out; - if (!pkt->trailer_only) - ep->ep_ops.ep_rx_complete(ar_sdio->ar, pkt->skb); - else + if (!pkt->trailer_only) { + cb = ATH10K_SKB_RXCB(pkt->skb); + cb->eid = id; + + skb_queue_tail(&ar_sdio->rx_head, pkt->skb); + queue_work(ar->workqueue_aux, + &ar_sdio->async_work_rx); + } else { kfree_skb(pkt->skb); + } /* The RX complete handler now owns the skb...*/ pkt->skb = NULL; @@ -1317,6 +1324,28 @@ static void __ath10k_sdio_write_async(struct ath10k *ar, ath10k_sdio_free_bus_req(ar, req); } +/* To improve throughput use workqueue to deliver packets to HTC layer, + * this way SDIO bus is utilised much better. + */ +static void ath10k_rx_indication_async_work(struct work_struct *work) +{ + struct ath10k_sdio *ar_sdio = container_of(work, struct ath10k_sdio, + async_work_rx); + struct ath10k *ar = ar_sdio->ar; + struct ath10k_htc_ep *ep; + struct ath10k_skb_rxcb *cb; + struct sk_buff *skb; + + while (true) { + skb = skb_dequeue(&ar_sdio->rx_head); + if (!skb) + break; + cb = ATH10K_SKB_RXCB(skb); + ep = &ar->htc.endpoint[cb->eid]; + ep->ep_ops.ep_rx_complete(ar, skb); + } +} + static void ath10k_sdio_write_async_work(struct work_struct *work) { struct ath10k_sdio *ar_sdio = container_of(work, struct ath10k_sdio, @@ -2087,6 +2116,9 @@ static int ath10k_sdio_probe(struct sdio_func *func, for (i = 0; i < ATH10K_SDIO_BUS_REQUEST_MAX_NUM; i++) ath10k_sdio_free_bus_req(ar, &ar_sdio->bus_req[i]); + skb_queue_head_init(&ar_sdio->rx_head); + INIT_WORK(&ar_sdio->async_work_rx, ath10k_rx_indication_async_work); + dev_id_base = FIELD_GET(QCA_MANUFACTURER_ID_BASE, id->device); switch (dev_id_base) { case QCA_MANUFACTURER_ID_AR6005_BASE: diff --git a/drivers/net/wireless/ath/ath10k/sdio.h b/drivers/net/wireless/ath/ath10k/sdio.h index 00bd4caa7fa18..9acaf0229cf27 100644 --- a/drivers/net/wireless/ath/ath10k/sdio.h +++ b/drivers/net/wireless/ath/ath10k/sdio.h @@ -187,6 +187,9 @@ struct ath10k_sdio { struct ath10k_sdio_bus_request bus_req[ATH10K_SDIO_BUS_REQUEST_MAX_NUM]; /* free list of bus requests */ struct list_head bus_req_freeq; + + struct sk_buff_head rx_head; + /* protects access to bus_req_freeq */ spinlock_t lock; @@ -213,6 +216,8 @@ struct ath10k_sdio { struct list_head wr_asyncq; /* protects access to wr_asyncq */ spinlock_t wr_async_lock; + + struct work_struct async_work_rx; }; static inline struct ath10k_sdio *ath10k_sdio_priv(struct ath10k *ar) -- GitLab From efd2f4c5016bd24f33357487adcf873359116e9a Mon Sep 17 00:00:00 2001 From: Kalle Valo Date: Fri, 15 Nov 2019 09:21:09 +0200 Subject: [PATCH 0006/1437] ath10k: sdio: cosmetic cleanup Do some cosmetic cleanup while reviewing the files. No functional changes. Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/sdio.c | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/drivers/net/wireless/ath/ath10k/sdio.c b/drivers/net/wireless/ath/ath10k/sdio.c index c7d09b07a382f..347e339095841 100644 --- a/drivers/net/wireless/ath/ath10k/sdio.c +++ b/drivers/net/wireless/ath/ath10k/sdio.c @@ -541,10 +541,8 @@ static int ath10k_sdio_mbox_rx_alloc(struct ath10k *ar, int pkt_cnt = 0; if (n_lookaheads > ATH10K_SDIO_MAX_RX_MSGS) { - ath10k_warn(ar, - "the total number of pkgs to be fetched (%u) exceeds maximum %u\n", - n_lookaheads, - ATH10K_SDIO_MAX_RX_MSGS); + ath10k_warn(ar, "the total number of pkgs to be fetched (%u) exceeds maximum %u\n", + n_lookaheads, ATH10K_SDIO_MAX_RX_MSGS); ret = -ENOMEM; goto err; } @@ -553,10 +551,8 @@ static int ath10k_sdio_mbox_rx_alloc(struct ath10k *ar, htc_hdr = (struct ath10k_htc_hdr *)&lookaheads[i]; last_in_bundle = false; - if (le16_to_cpu(htc_hdr->len) > - ATH10K_HTC_MBOX_MAX_PAYLOAD_LENGTH) { - ath10k_warn(ar, - "payload length %d exceeds max htc length: %zu\n", + if (le16_to_cpu(htc_hdr->len) > ATH10K_HTC_MBOX_MAX_PAYLOAD_LENGTH) { + ath10k_warn(ar, "payload length %d exceeds max htc length: %zu\n", le16_to_cpu(htc_hdr->len), ATH10K_HTC_MBOX_MAX_PAYLOAD_LENGTH); ret = -ENOMEM; @@ -567,8 +563,7 @@ static int ath10k_sdio_mbox_rx_alloc(struct ath10k *ar, full_len = ath10k_sdio_calc_txrx_padded_len(ar_sdio, act_len); if (full_len > ATH10K_SDIO_MAX_BUFFER_SIZE) { - ath10k_warn(ar, - "rx buffer requested with invalid htc_hdr length (%d, 0x%x): %d\n", + ath10k_warn(ar, "rx buffer requested with invalid htc_hdr length (%d, 0x%x): %d\n", htc_hdr->eid, htc_hdr->flags, le16_to_cpu(htc_hdr->len)); ret = -EINVAL; @@ -590,12 +585,14 @@ static int ath10k_sdio_mbox_rx_alloc(struct ath10k *ar, &bndl_cnt); if (ret) { - ath10k_warn(ar, "alloc_bundle error %d\n", ret); + ath10k_warn(ar, "failed to allocate a bundle: %d\n", + ret); goto err; } pkt_cnt += bndl_cnt; - /*Next buffer will be the last in the bundle */ + + /* next buffer will be the last in the bundle */ last_in_bundle = true; } -- GitLab From 2246c215659060b214c9cc7fcd4abec95874ae5a Mon Sep 17 00:00:00 2001 From: Kalle Valo Date: Fri, 15 Nov 2019 09:21:11 +0200 Subject: [PATCH 0007/1437] ath10k: sdio: remove struct ath10k_sdio_rx_data::status It seems to be unused. Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/sdio.c | 2 -- drivers/net/wireless/ath/ath10k/sdio.h | 1 - 2 files changed, 3 deletions(-) diff --git a/drivers/net/wireless/ath/ath10k/sdio.c b/drivers/net/wireless/ath/ath10k/sdio.c index 347e339095841..2bf96e9665cd4 100644 --- a/drivers/net/wireless/ath/ath10k/sdio.c +++ b/drivers/net/wireless/ath/ath10k/sdio.c @@ -649,7 +649,6 @@ static int ath10k_sdio_mbox_rx_fetch(struct ath10k *ar) htc_hdr = (struct ath10k_htc_hdr *)skb->data; pkt->act_len = le16_to_cpu(htc_hdr->len) + sizeof(*htc_hdr); - pkt->status = ret; skb_put(skb, pkt->act_len); return ret; @@ -687,7 +686,6 @@ static int ath10k_sdio_mbox_rx_fetch_bundle(struct ath10k *ar) pkt->act_len = le16_to_cpu(htc_hdr->len) + sizeof(*htc_hdr); skb_put_data(pkt->skb, htc_hdr, pkt->act_len); - pkt->status = 0; pkt_offset += pkt->alloc_len; } diff --git a/drivers/net/wireless/ath/ath10k/sdio.h b/drivers/net/wireless/ath/ath10k/sdio.h index 9acaf0229cf27..33195f49acabc 100644 --- a/drivers/net/wireless/ath/ath10k/sdio.h +++ b/drivers/net/wireless/ath/ath10k/sdio.h @@ -126,7 +126,6 @@ struct ath10k_sdio_rx_data { bool part_of_bundle; bool last_in_bundle; bool trailer_only; - int status; }; struct ath10k_sdio_irq_proc_regs { -- GitLab From e01cc82c4d1ec3bddcbb7cd991cf5dc0131ed9a1 Mon Sep 17 00:00:00 2001 From: Wen Gong Date: Fri, 15 Nov 2019 09:21:32 +0200 Subject: [PATCH 0008/1437] ath10k: correct the tlv len of ath10k_wmi_tlv_op_gen_config_pno_start the tlv len is set to the total len of the wmi cmd, it will trigger firmware crash, correct the tlv len. Tested with QCA6174 SDIO with firmware WLAN.RMH.4.4.1-00017-QCARMSWP-1 and QCA6174 PCIE with firmware WLAN.RM.4.4.1-00110-QCARMSWPZ-1. Fixes: ce834e280f2f875 ("ath10k: support NET_DETECT WoWLAN feature") Signed-off-by: Wen Gong Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/wmi-tlv.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath10k/wmi-tlv.c b/drivers/net/wireless/ath/ath10k/wmi-tlv.c index 69a1ec53df294..7b358484940ec 100644 --- a/drivers/net/wireless/ath/ath10k/wmi-tlv.c +++ b/drivers/net/wireless/ath/ath10k/wmi-tlv.c @@ -3707,6 +3707,7 @@ ath10k_wmi_tlv_op_gen_config_pno_start(struct ath10k *ar, struct wmi_tlv *tlv; struct sk_buff *skb; __le32 *channel_list; + u16 tlv_len; size_t len; void *ptr; u32 i; @@ -3764,10 +3765,12 @@ ath10k_wmi_tlv_op_gen_config_pno_start(struct ath10k *ar, /* nlo_configured_parameters(nlo_list) */ cmd->no_of_ssids = __cpu_to_le32(min_t(u8, pno->uc_networks_count, WMI_NLO_MAX_SSIDS)); + tlv_len = __le32_to_cpu(cmd->no_of_ssids) * + sizeof(struct nlo_configured_parameters); tlv = ptr; tlv->tag = __cpu_to_le16(WMI_TLV_TAG_ARRAY_STRUCT); - tlv->len = __cpu_to_le16(len); + tlv->len = __cpu_to_le16(tlv_len); ptr += sizeof(*tlv); nlo_list = ptr; -- GitLab From d58f466a5dee814b7820aff9aaa393b3782559d9 Mon Sep 17 00:00:00 2001 From: Wen Gong Date: Fri, 15 Nov 2019 09:21:35 +0200 Subject: [PATCH 0009/1437] ath10k: add large size for BMI download data for SDIO Download firmware time cost of SDIO is too long, it is about 480ms, add large size 2048 bytes for BMI download for SDIO chip, its time cost will reduced to 240ms. This will optimize the download firmware time cost. Tested with QCA6174 SDIO with firmware WLAN.RMH.4.4.1-00017-QCARMSWP-1. Signed-off-by: Wen Gong Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/bmi.c | 52 +++++++++++++++++++++++++- drivers/net/wireless/ath/ath10k/bmi.h | 10 +++++ drivers/net/wireless/ath/ath10k/core.c | 1 + drivers/net/wireless/ath/ath10k/hw.h | 3 ++ drivers/net/wireless/ath/ath10k/sdio.c | 2 +- 5 files changed, 66 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/ath/ath10k/bmi.c b/drivers/net/wireless/ath/ath10k/bmi.c index 95dc4be82e5cc..ea908107581da 100644 --- a/drivers/net/wireless/ath/ath10k/bmi.c +++ b/drivers/net/wireless/ath/ath10k/bmi.c @@ -346,6 +346,52 @@ int ath10k_bmi_execute(struct ath10k *ar, u32 address, u32 param, u32 *result) return 0; } +static int ath10k_bmi_lz_data_large(struct ath10k *ar, const void *buffer, u32 length) +{ + struct bmi_cmd *cmd; + u32 hdrlen = sizeof(cmd->id) + sizeof(cmd->lz_data); + u32 txlen; + int ret; + size_t buf_len; + + ath10k_dbg(ar, ATH10K_DBG_BMI, "large bmi lz data buffer 0x%pK length %d\n", + buffer, length); + + if (ar->bmi.done_sent) { + ath10k_warn(ar, "command disallowed\n"); + return -EBUSY; + } + + buf_len = sizeof(*cmd) + BMI_MAX_LARGE_DATA_SIZE - BMI_MAX_DATA_SIZE; + cmd = kzalloc(buf_len, GFP_KERNEL); + if (!cmd) + return -ENOMEM; + + while (length) { + txlen = min(length, BMI_MAX_LARGE_DATA_SIZE - hdrlen); + + WARN_ON_ONCE(txlen & 3); + + cmd->id = __cpu_to_le32(BMI_LZ_DATA); + cmd->lz_data.len = __cpu_to_le32(txlen); + memcpy(cmd->lz_data.payload, buffer, txlen); + + ret = ath10k_hif_exchange_bmi_msg(ar, cmd, hdrlen + txlen, + NULL, NULL); + if (ret) { + ath10k_warn(ar, "unable to write to the device\n"); + return ret; + } + + buffer += txlen; + length -= txlen; + } + + kfree(cmd); + + return 0; +} + int ath10k_bmi_lz_data(struct ath10k *ar, const void *buffer, u32 length) { struct bmi_cmd cmd; @@ -430,7 +476,11 @@ int ath10k_bmi_fast_download(struct ath10k *ar, if (trailer_len > 0) memcpy(trailer, buffer + head_len, trailer_len); - ret = ath10k_bmi_lz_data(ar, buffer, head_len); + if (ar->hw_params.bmi_large_size_download) + ret = ath10k_bmi_lz_data_large(ar, buffer, head_len); + else + ret = ath10k_bmi_lz_data(ar, buffer, head_len); + if (ret) return ret; diff --git a/drivers/net/wireless/ath/ath10k/bmi.h b/drivers/net/wireless/ath/ath10k/bmi.h index ef3bdba43bed7..f6fadcbdd86ef 100644 --- a/drivers/net/wireless/ath/ath10k/bmi.h +++ b/drivers/net/wireless/ath/ath10k/bmi.h @@ -45,6 +45,15 @@ sizeof(u32) + \ sizeof(u32)) +/* Maximum data size used for large BMI transfers */ +#define BMI_MAX_LARGE_DATA_SIZE 2048 + +/* len = cmd + addr + length */ +#define BMI_MAX_LARGE_CMDBUF_SIZE (BMI_MAX_LARGE_DATA_SIZE + \ + sizeof(u32) + \ + sizeof(u32) + \ + sizeof(u32)) + /* BMI Commands */ enum bmi_cmd_id { @@ -258,6 +267,7 @@ int ath10k_bmi_write_memory(struct ath10k *ar, u32 address, int ath10k_bmi_execute(struct ath10k *ar, u32 address, u32 param, u32 *result); int ath10k_bmi_lz_stream_start(struct ath10k *ar, u32 address); int ath10k_bmi_lz_data(struct ath10k *ar, const void *buffer, u32 length); + int ath10k_bmi_fast_download(struct ath10k *ar, u32 address, const void *buffer, u32 length); int ath10k_bmi_read_soc_reg(struct ath10k *ar, u32 address, u32 *reg_val); diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c index 4f76ba5d78a98..0d345852c04a9 100644 --- a/drivers/net/wireless/ath/ath10k/core.c +++ b/drivers/net/wireless/ath/ath10k/core.c @@ -189,6 +189,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .num_wds_entries = 0x20, .uart_pin_workaround = true, .tx_stats_over_pktlog = false, + .bmi_large_size_download = true, }, { .id = QCA6174_HW_2_1_VERSION, diff --git a/drivers/net/wireless/ath/ath10k/hw.h b/drivers/net/wireless/ath/ath10k/hw.h index 35a362329a4f1..21b7a2a873b00 100644 --- a/drivers/net/wireless/ath/ath10k/hw.h +++ b/drivers/net/wireless/ath/ath10k/hw.h @@ -613,6 +613,9 @@ struct ath10k_hw_params { /* target supporting fw download via diag ce */ bool fw_diag_ce_download; + /* target supporting fw download via large size BMI */ + bool bmi_large_size_download; + /* need to set uart pin if disable uart print, workaround for a * firmware bug */ diff --git a/drivers/net/wireless/ath/ath10k/sdio.c b/drivers/net/wireless/ath/ath10k/sdio.c index 2bf96e9665cd4..3e63adfe3e9c4 100644 --- a/drivers/net/wireless/ath/ath10k/sdio.c +++ b/drivers/net/wireless/ath/ath10k/sdio.c @@ -2082,7 +2082,7 @@ static int ath10k_sdio_probe(struct sdio_func *func, goto err_core_destroy; } - ar_sdio->bmi_buf = devm_kzalloc(ar->dev, BMI_MAX_CMDBUF_SIZE, GFP_KERNEL); + ar_sdio->bmi_buf = devm_kzalloc(ar->dev, BMI_MAX_LARGE_CMDBUF_SIZE, GFP_KERNEL); if (!ar_sdio->bmi_buf) { ret = -ENOMEM; goto err_core_destroy; -- GitLab From 501d4152b0184b7f0a4df03a1dc748b381410a11 Mon Sep 17 00:00:00 2001 From: Jeffrey Hugo Date: Fri, 15 Nov 2019 09:21:43 +0200 Subject: [PATCH 0010/1437] ath10k: Handle when FW doesn't support QMI_WLFW_HOST_CAP_REQ_V01 Firmware with the build id QC_IMAGE_VERSION_STRING=WLAN.HL.1.0.2-XXXX does not support the QMI_WLFW_HOST_CAP_REQ_V01 message and will return the QMI not supported error to the ath10k driver. Since not supporting this message is not fatal to the firmware nor the ath10k driver, lets catch this particular scenario and ignore it so that we can still bring up wifi services successfully. Signed-off-by: Jeffrey Hugo Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/qmi.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath10k/qmi.c b/drivers/net/wireless/ath/ath10k/qmi.c index a0ba07b853626..ed69e97751437 100644 --- a/drivers/net/wireless/ath/ath10k/qmi.c +++ b/drivers/net/wireless/ath/ath10k/qmi.c @@ -635,7 +635,9 @@ static int ath10k_qmi_host_cap_send_sync(struct ath10k_qmi *qmi) if (ret < 0) goto out; - if (resp.resp.result != QMI_RESULT_SUCCESS_V01) { + /* older FW didn't support this request, which is not fatal */ + if (resp.resp.result != QMI_RESULT_SUCCESS_V01 && + resp.resp.error != QMI_ERR_NOT_SUPPORTED_V01) { ath10k_err(ar, "host capability request rejected: %d\n", resp.resp.error); ret = -EINVAL; goto out; -- GitLab From 23b5156a856b6342c8a1ef7045d387c00cdb17c5 Mon Sep 17 00:00:00 2001 From: Wen Gong Date: Fri, 8 Nov 2019 17:27:06 +0800 Subject: [PATCH 0011/1437] ath10k: add NL80211_FEATURE_ND_RANDOM_MAC_ADDR for NLO Add NL80211_FEATURE_ND_RANDOM_MAC_ADDR for NLO will enable the random mac address for netdetect case. iw command: iw phy0 wowlan enable net-detect net-detect randomize=AA:7B:A1:AC:B2:41/FF:FF:FF:FF:FF:FF interval 5000 delay 30 freqs 2412 matches ssid foo. After suspend, DUT will send probe request with mac AA:7B:A1:AC:B2:41. WCN3990, QCA9377, QCA6174 PCI also support this feature. Tested with QCA6174 SDIO with firmware WLAN.RMH.4.4.1-00029. Signed-off-by: Wen Gong Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/mac.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index 83cc8778ca1e3..6c6eb9341e6d4 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -8908,6 +8908,7 @@ int ath10k_mac_register(struct ath10k *ar) WMI_PNO_MAX_SCHED_SCAN_PLAN_INT; ar->hw->wiphy->max_sched_scan_plan_iterations = WMI_PNO_MAX_SCHED_SCAN_PLAN_ITRNS; + ar->hw->wiphy->features |= NL80211_FEATURE_ND_RANDOM_MAC_ADDR; } ar->hw->vif_data_size = sizeof(struct ath10k_vif); -- GitLab From f8a595a87e93a33a10879f4b856be818d2f53c84 Mon Sep 17 00:00:00 2001 From: Jeffrey Hugo Date: Wed, 13 Nov 2019 07:40:16 -0800 Subject: [PATCH 0012/1437] ath10k: Fix qmi init error handling When ath10k_qmi_init() fails, the error handling does not free the irq resources, which causes an issue if we EPROBE_DEFER as we'll attempt to (re-)register irqs which are already registered. Fix this by doing a power off since we just powered on the hardware, and freeing the irqs as error handling. Fixes: ba94c753ccb4 ("ath10k: add QMI message handshake for wcn3990 client") Signed-off-by: Jeffrey Hugo Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/snoc.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath10k/snoc.c b/drivers/net/wireless/ath/ath10k/snoc.c index 16177497bba76..7e85c4916e7f5 100644 --- a/drivers/net/wireless/ath/ath10k/snoc.c +++ b/drivers/net/wireless/ath/ath10k/snoc.c @@ -1563,13 +1563,16 @@ static int ath10k_snoc_probe(struct platform_device *pdev) ret = ath10k_qmi_init(ar, msa_size); if (ret) { ath10k_warn(ar, "failed to register wlfw qmi client: %d\n", ret); - goto err_core_destroy; + goto err_power_off; } ath10k_dbg(ar, ATH10K_DBG_SNOC, "snoc probe\n"); return 0; +err_power_off: + ath10k_hw_power_off(ar); + err_free_irq: ath10k_snoc_free_irq(ar); -- GitLab From 319c2b71041f324e06dbee03c1011106cf746795 Mon Sep 17 00:00:00 2001 From: Jeffrey Hugo Date: Wed, 13 Nov 2019 07:46:46 -0800 Subject: [PATCH 0013/1437] ath10k: Handle "invalid" BDFs for msm8998 devices When the BDF download QMI message has the end field set to 1, it signals the end of the transfer, and triggers the firmware to do a CRC check. The BDFs for msm8998 devices fail this check, yet the firmware is happy to still use the BDF. It appears that this error is not caught by the downstream drive by concidence, therefore there are production devices in the field where this issue needs to be handled otherwise we cannot support wifi on them. So, attempt to detect this scenario as best we can and treat it as non-fatal. Signed-off-by: Jeffrey Hugo Reviewed-by: Bjorn Andersson Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/qmi.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath10k/qmi.c b/drivers/net/wireless/ath/ath10k/qmi.c index ed69e97751437..7b524b614f225 100644 --- a/drivers/net/wireless/ath/ath10k/qmi.c +++ b/drivers/net/wireless/ath/ath10k/qmi.c @@ -279,7 +279,15 @@ static int ath10k_qmi_bdf_dnld_send_sync(struct ath10k_qmi *qmi) if (ret < 0) goto out; - if (resp.resp.result != QMI_RESULT_SUCCESS_V01) { + /* end = 1 triggers a CRC check on the BDF. If this fails, we + * get a QMI_ERR_MALFORMED_MSG_V01 error, but the FW is still + * willing to use the BDF. For some platforms, all the valid + * released BDFs fail this CRC check, so attempt to detect this + * scenario and treat it as non-fatal. + */ + if (resp.resp.result != QMI_RESULT_SUCCESS_V01 && + !(req->end == 1 && + resp.resp.result == QMI_ERR_MALFORMED_MSG_V01)) { ath10k_err(ar, "failed to download board data file: %d\n", resp.resp.error); ret = -EINVAL; -- GitLab From 7005eafc1f1cb0a525631c2214f760d6fb636ebb Mon Sep 17 00:00:00 2001 From: Wen Gong Date: Thu, 14 Nov 2019 19:19:31 +0800 Subject: [PATCH 0014/1437] ath10k: report rssi of each chain to mac80211 for sdio iw command only show rssi without each chain's rssi on sdio iw wlan0 station dump Station a0:40:a0:93:3e:de (on wlan0) signal: -82 dBm signal avg: -82 dBm after this patch, it will show each chain's rssi on sdio Station a0:40:a0:93:3e:de (on wlan0) signal: -82 [-84, -88] dBm signal avg: -82 [-84, -87] dBm For QCA6174 PCIe, the ppdu have the correct rssi of each chain, it indicate rssi of rx data by ath10k_htt_rx_h_signal. For sdio chip, the rssi of each chain stored in rx management reported by firmware, the ath10k_wmi_tlv_op_pull_mgmt_rx_ev which used for tlv wmi will get the rssi of each chain and stored them in wmi_mgmt_rx_ev_arg, then indicate them to mac80211. For non-tlv wmi chip, it will not get the rssi of each chain and not indicate to mac80211, for non-tlv wmi chip, this patch will not have impact. For tlv wmi chip, if the rssi of chain in mgmt is valid, it will be indicate to mac80211, tested with QCA6174 PCIe/SDIO, the rssi of 2 chain in mgmt is valid. rssi of chains in mgmt of QCA6174 SDIO: 92096.652780: ath10k:ath10k_log_warn: ath10k_sdio mmc1:0001:1 rssi[0]:70 92096.657324: ath10k:ath10k_log_warn: ath10k_sdio mmc1:0001:1 rssi[1]:68 92096.662009: ath10k:ath10k_log_warn: ath10k_sdio mmc1:0001:1 rssi[2]:128 92096.666647: ath10k:ath10k_log_warn: ath10k_sdio mmc1:0001:1 rssi[3]:128 rssi of chains in mgmt of QCA6174 PCIe: [ 1581.049816] ath10k_pci 0000:02:00.0: mgmt rssi[0]:17 [ 1581.049818] ath10k_pci 0000:02:00.0: mgmt rssi[1]:22 [ 1581.049821] ath10k_pci 0000:02:00.0: mgmt rssi[2]:128 [ 1581.049823] ath10k_pci 0000:02:00.0: mgmt rssi[3]:128 after apply this patch, the iw's rssi of PCIe do not changed, result is same with before. iw wlan0 station dump of QCA6174 PCIe: Station 6c:e8:73:b8:92:dc (on wlan0) signal: -70 [-77, -72] dBm signal avg: -69 [-78, -72] dBm iw wlan-5000mhz station dump of QCA9984 PCIe connected with 2 client which has 2 chain: Station 70:48:0f:1f:1a:b2 (on wlan-5000mhz) signal: -47 [-55, -48, -87, -88] dBm signal avg: -42 [-50, -43, -83, -86] dBm Station ac:c1:ee:39:e3:83 (on wlan-5000mhz) signal: -43 [-46, -45, -79, -84] dBm signal avg: -43 [-46, -46, -82, -83] dBm Tested with QCA6174 SDIO with firmware WLAN.RMH.4.4.1-00017-QCARMSWP-1. Tested with QCA6174 PCIe with firmware WLAN.RM.4.4.1-00110-QCARMSWP-1. Tested with QCA9984 PCIe with firmware 10.4-3.9.0.2-00040. Signed-off-by: Wen Gong Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/htt_rx.c | 1 - drivers/net/wireless/ath/ath10k/wmi-tlv.c | 5 ++++- drivers/net/wireless/ath/ath10k/wmi.c | 18 ++++++++++++++++-- drivers/net/wireless/ath/ath10k/wmi.h | 2 ++ 4 files changed, 22 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/ath/ath10k/htt_rx.c b/drivers/net/wireless/ath/ath10k/htt_rx.c index edda6dd93476b..0f1f71e43b9b5 100644 --- a/drivers/net/wireless/ath/ath10k/htt_rx.c +++ b/drivers/net/wireless/ath/ath10k/htt_rx.c @@ -2239,7 +2239,6 @@ static bool ath10k_htt_rx_proc_rx_ind_hl(struct ath10k_htt *htt, rx_status = IEEE80211_SKB_RXCB(skb); memset(rx_status, 0, sizeof(*rx_status)); - rx_status->chains |= BIT(0); if (rx->ppdu.combined_rssi == 0) { /* SDIO firmware does not provide signal */ rx_status->signal = 0; diff --git a/drivers/net/wireless/ath/ath10k/wmi-tlv.c b/drivers/net/wireless/ath/ath10k/wmi-tlv.c index 7b358484940ec..4e68debda9bfc 100644 --- a/drivers/net/wireless/ath/ath10k/wmi-tlv.c +++ b/drivers/net/wireless/ath/ath10k/wmi-tlv.c @@ -841,7 +841,7 @@ static int ath10k_wmi_tlv_op_pull_mgmt_rx_ev(struct ath10k *ar, const struct wmi_tlv_mgmt_rx_ev *ev; const u8 *frame; u32 msdu_len; - int ret; + int ret, i; tb = ath10k_wmi_tlv_parse_alloc(ar, skb->data, skb->len, GFP_ATOMIC); if (IS_ERR(tb)) { @@ -865,6 +865,9 @@ static int ath10k_wmi_tlv_op_pull_mgmt_rx_ev(struct ath10k *ar, arg->phy_mode = ev->phy_mode; arg->rate = ev->rate; + for (i = 0; i < ARRAY_SIZE(ev->rssi); i++) + arg->rssi[i] = ev->rssi[i]; + msdu_len = __le32_to_cpu(arg->buf_len); if (skb->len < (frame - skb->data) + msdu_len) { diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c index 9f564e2b7a148..13f7531d945dd 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.c +++ b/drivers/net/wireless/ath/ath10k/wmi.c @@ -2463,10 +2463,10 @@ int ath10k_wmi_event_mgmt_rx(struct ath10k *ar, struct sk_buff *skb) u32 rx_status; u32 channel; u32 phy_mode; - u32 snr; + u32 snr, rssi; u32 rate; u16 fc; - int ret; + int ret, i; ret = ath10k_wmi_pull_mgmt_rx(ar, skb, &arg); if (ret) { @@ -2525,6 +2525,20 @@ int ath10k_wmi_event_mgmt_rx(struct ath10k *ar, struct sk_buff *skb) status->freq = ieee80211_channel_to_frequency(channel, status->band); status->signal = snr + ATH10K_DEFAULT_NOISE_FLOOR; + + BUILD_BUG_ON(ARRAY_SIZE(status->chain_signal) != ARRAY_SIZE(arg.rssi)); + + for (i = 0; i < ARRAY_SIZE(status->chain_signal); i++) { + status->chains &= ~BIT(i); + rssi = __le32_to_cpu(arg.rssi[i]); + ath10k_dbg(ar, ATH10K_DBG_MGMT, "mgmt rssi[%d]:%d\n", i, arg.rssi[i]); + + if (rssi != ATH10K_INVALID_RSSI && rssi != 0) { + status->chain_signal[i] = ATH10K_DEFAULT_NOISE_FLOOR + rssi; + status->chains |= BIT(i); + } + } + status->rate_idx = ath10k_mac_bitrate_to_idx(sband, rate / 100); hdr = (struct ieee80211_hdr *)skb->data; diff --git a/drivers/net/wireless/ath/ath10k/wmi.h b/drivers/net/wireless/ath/ath10k/wmi.h index 74adce1dd3a99..972d53d77654d 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.h +++ b/drivers/net/wireless/ath/ath10k/wmi.h @@ -6786,6 +6786,7 @@ struct wmi_peer_delete_resp_ev_arg { struct wmi_mac_addr peer_addr; }; +#define WMI_MGMT_RX_NUM_RSSI 4 struct wmi_mgmt_rx_ev_arg { __le32 channel; __le32 snr; @@ -6794,6 +6795,7 @@ struct wmi_mgmt_rx_ev_arg { __le32 buf_len; __le32 status; /* %WMI_RX_STATUS_ */ struct wmi_mgmt_rx_ext_info ext_info; + __le32 rssi[WMI_MGMT_RX_NUM_RSSI]; }; struct wmi_ch_info_ev_arg { -- GitLab From ea0c3e2a470271573d480f48a67e74633566a317 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Linus=20L=C3=BCssing?= Date: Fri, 15 Nov 2019 11:56:12 +0100 Subject: [PATCH 0015/1437] ath10k: fix RX of frames with broken FCS in monitor mode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit So far, frames were forwarded regardless of the FCS correctness leading to userspace applications listening on the monitor mode interface to receive potentially broken frames, even with the "fcsfail" flag unset. By default, with the "fcsfail" flag of a monitor mode interface unset, frames with FCS errors should be dropped. With this patch, the fcsfail flag is taken into account correctly. Tested-on: QCA4019 firmware-5-ct-full-community-12.bin-lede.011 Cc: Simon Wunderlich Signed-off-by: Linus Lüssing Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/core.h | 1 + drivers/net/wireless/ath/ath10k/debug.c | 2 ++ drivers/net/wireless/ath/ath10k/htt_rx.c | 7 +++++++ 3 files changed, 10 insertions(+) diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h index c5407f5080b27..5101bf2b5b158 100644 --- a/drivers/net/wireless/ath/ath10k/core.h +++ b/drivers/net/wireless/ath/ath10k/core.h @@ -1181,6 +1181,7 @@ struct ath10k { struct { /* protected by data_lock */ + u32 rx_crc_err_drop; u32 fw_crash_counter; u32 fw_warm_reset_counter; u32 fw_cold_reset_counter; diff --git a/drivers/net/wireless/ath/ath10k/debug.c b/drivers/net/wireless/ath/ath10k/debug.c index 04c50a26a4f47..e000677ac516b 100644 --- a/drivers/net/wireless/ath/ath10k/debug.c +++ b/drivers/net/wireless/ath/ath10k/debug.c @@ -1094,6 +1094,7 @@ static const char ath10k_gstrings_stats[][ETH_GSTRING_LEN] = { "d_rts_good", "d_tx_power", /* in .5 dbM I think */ "d_rx_crc_err", /* fcs_bad */ + "d_rx_crc_err_drop", /* frame with FCS error, dropped late in kernel */ "d_no_beacon", "d_tx_mpdus_queued", "d_tx_msdu_queued", @@ -1193,6 +1194,7 @@ void ath10k_debug_get_et_stats(struct ieee80211_hw *hw, data[i++] = pdev_stats->rts_good; data[i++] = pdev_stats->chan_tx_power; data[i++] = pdev_stats->fcs_bad; + data[i++] = ar->stats.rx_crc_err_drop; data[i++] = pdev_stats->no_beacons; data[i++] = pdev_stats->mpdu_enqued; data[i++] = pdev_stats->msdu_enqued; diff --git a/drivers/net/wireless/ath/ath10k/htt_rx.c b/drivers/net/wireless/ath/ath10k/htt_rx.c index 0f1f71e43b9b5..9faecfa5f9d7d 100644 --- a/drivers/net/wireless/ath/ath10k/htt_rx.c +++ b/drivers/net/wireless/ath/ath10k/htt_rx.c @@ -1285,6 +1285,13 @@ static void ath10k_process_rx(struct ath10k *ar, struct sk_buff *skb) status = IEEE80211_SKB_RXCB(skb); + if (!(ar->filter_flags & FIF_FCSFAIL) && + status->flag & RX_FLAG_FAILED_FCS_CRC) { + ar->stats.rx_crc_err_drop++; + dev_kfree_skb_any(skb); + return; + } + ath10k_dbg(ar, ATH10K_DBG_DATA, "rx skb %pK len %u peer %pM %s %s sn %u %s%s%s%s%s%s %srate_idx %u vht_nss %u freq %u band %u flag 0x%x fcs-err %i mic-err %i amsdu-more %i\n", skb, -- GitLab From 5b1413f00b5beb9f5fed94e43ea0c497d5db9633 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Fri, 15 Nov 2019 12:09:53 +0000 Subject: [PATCH 0016/1437] wil6210: fix break that is never reached because of zero'ing of a retry counter There is a check on the retry counter invalid_buf_id_retry that is always false because invalid_buf_id_retry is initialized to zero on each iteration of a while-loop. Fix this by initializing the retry counter before the while-loop starts. Addresses-Coverity: ("Logically dead code") Fixes: b4a967b7d0f5 ("wil6210: reset buff id in status message after completion") Signed-off-by: Colin Ian King Reviewed-by: Maya Erez Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/wil6210/txrx_edma.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/wil6210/txrx_edma.c b/drivers/net/wireless/ath/wil6210/txrx_edma.c index 778b63be6a9a4..02548d40253c7 100644 --- a/drivers/net/wireless/ath/wil6210/txrx_edma.c +++ b/drivers/net/wireless/ath/wil6210/txrx_edma.c @@ -869,6 +869,7 @@ static struct sk_buff *wil_sring_reap_rx_edma(struct wil6210_priv *wil, u8 data_offset; struct wil_rx_status_extended *s; u16 sring_idx = sring - wil->srings; + int invalid_buff_id_retry; BUILD_BUG_ON(sizeof(struct wil_rx_status_extended) > sizeof(skb->cb)); @@ -882,9 +883,9 @@ again: /* Extract the buffer ID from the status message */ buff_id = le16_to_cpu(wil_rx_status_get_buff_id(msg)); + invalid_buff_id_retry = 0; while (!buff_id) { struct wil_rx_status_extended *s; - int invalid_buff_id_retry = 0; wil_dbg_txrx(wil, "buff_id is not updated yet by HW, (swhead 0x%x)\n", -- GitLab From dae0978d4174422b4a2aa08a96252a1e60d40ef5 Mon Sep 17 00:00:00 2001 From: Kalle Valo Date: Fri, 15 Nov 2019 11:28:25 +0200 Subject: [PATCH 0017/1437] dt: bindings: net: add qcom,ath11k.yaml ath11k is a driver for Qualcomm IEEE 802.11ax devices. Add a bindings document for the driver, first documenting IPQ8074 which is the only device ath11k currently supports. Signed-off-by: Anilkumar Kolli Reviewed-by: Rob Herring Signed-off-by: Kalle Valo --- .../bindings/net/wireless/qcom,ath11k.yaml | 273 ++++++++++++++++++ 1 file changed, 273 insertions(+) create mode 100644 Documentation/devicetree/bindings/net/wireless/qcom,ath11k.yaml diff --git a/Documentation/devicetree/bindings/net/wireless/qcom,ath11k.yaml b/Documentation/devicetree/bindings/net/wireless/qcom,ath11k.yaml new file mode 100644 index 0000000000000..a1717db36dba1 --- /dev/null +++ b/Documentation/devicetree/bindings/net/wireless/qcom,ath11k.yaml @@ -0,0 +1,273 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +# Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. + +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/net/wireless/qcom,ath11k.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm Technologies ath11k wireless devices Generic Binding + +maintainers: + - Kalle Valo + +description: | + These are dt entries for Qualcomm Technologies, Inc. IEEE 802.11ax + devices, for example like AHB based IPQ8074. + +properties: + compatible: + const: qcom,ipq8074-wifi + + reg: + maxItems: 1 + + interrupts: + items: + - description: misc-pulse1 interrupt events + - description: misc-latch interrupt events + - description: sw exception interrupt events + - description: watchdog interrupt events + - description: interrupt event for ring CE0 + - description: interrupt event for ring CE1 + - description: interrupt event for ring CE2 + - description: interrupt event for ring CE3 + - description: interrupt event for ring CE4 + - description: interrupt event for ring CE5 + - description: interrupt event for ring CE6 + - description: interrupt event for ring CE7 + - description: interrupt event for ring CE8 + - description: interrupt event for ring CE9 + - description: interrupt event for ring CE10 + - description: interrupt event for ring CE11 + - description: interrupt event for ring host2wbm-desc-feed + - description: interrupt event for ring host2reo-re-injection + - description: interrupt event for ring host2reo-command + - description: interrupt event for ring host2rxdma-monitor-ring3 + - description: interrupt event for ring host2rxdma-monitor-ring2 + - description: interrupt event for ring host2rxdma-monitor-ring1 + - description: interrupt event for ring reo2ost-exception + - description: interrupt event for ring wbm2host-rx-release + - description: interrupt event for ring reo2host-status + - description: interrupt event for ring reo2host-destination-ring4 + - description: interrupt event for ring reo2host-destination-ring3 + - description: interrupt event for ring reo2host-destination-ring2 + - description: interrupt event for ring reo2host-destination-ring1 + - description: interrupt event for ring rxdma2host-monitor-destination-mac3 + - description: interrupt event for ring rxdma2host-monitor-destination-mac2 + - description: interrupt event for ring rxdma2host-monitor-destination-mac1 + - description: interrupt event for ring ppdu-end-interrupts-mac3 + - description: interrupt event for ring ppdu-end-interrupts-mac2 + - description: interrupt event for ring ppdu-end-interrupts-mac1 + - description: interrupt event for ring rxdma2host-monitor-status-ring-mac3 + - description: interrupt event for ring rxdma2host-monitor-status-ring-mac2 + - description: interrupt event for ring rxdma2host-monitor-status-ring-mac1 + - description: interrupt event for ring host2rxdma-host-buf-ring-mac3 + - description: interrupt event for ring host2rxdma-host-buf-ring-mac2 + - description: interrupt event for ring host2rxdma-host-buf-ring-mac1 + - description: interrupt event for ring rxdma2host-destination-ring-mac3 + - description: interrupt event for ring rxdma2host-destination-ring-mac2 + - description: interrupt event for ring rxdma2host-destination-ring-mac1 + - description: interrupt event for ring host2tcl-input-ring4 + - description: interrupt event for ring host2tcl-input-ring3 + - description: interrupt event for ring host2tcl-input-ring2 + - description: interrupt event for ring host2tcl-input-ring1 + - description: interrupt event for ring wbm2host-tx-completions-ring3 + - description: interrupt event for ring wbm2host-tx-completions-ring2 + - description: interrupt event for ring wbm2host-tx-completions-ring1 + - description: interrupt event for ring tcl2host-status-ring + + + interrupt-names: + items: + - const: misc-pulse1 + - const: misc-latch + - const: sw-exception + - const: watchdog + - const: ce0 + - const: ce1 + - const: ce2 + - const: ce3 + - const: ce4 + - const: ce5 + - const: ce6 + - const: ce7 + - const: ce8 + - const: ce9 + - const: ce10 + - const: ce11 + - const: host2wbm-desc-feed + - const: host2reo-re-injection + - const: host2reo-command + - const: host2rxdma-monitor-ring3 + - const: host2rxdma-monitor-ring2 + - const: host2rxdma-monitor-ring1 + - const: reo2ost-exception + - const: wbm2host-rx-release + - const: reo2host-status + - const: reo2host-destination-ring4 + - const: reo2host-destination-ring3 + - const: reo2host-destination-ring2 + - const: reo2host-destination-ring1 + - const: rxdma2host-monitor-destination-mac3 + - const: rxdma2host-monitor-destination-mac2 + - const: rxdma2host-monitor-destination-mac1 + - const: ppdu-end-interrupts-mac3 + - const: ppdu-end-interrupts-mac2 + - const: ppdu-end-interrupts-mac1 + - const: rxdma2host-monitor-status-ring-mac3 + - const: rxdma2host-monitor-status-ring-mac2 + - const: rxdma2host-monitor-status-ring-mac1 + - const: host2rxdma-host-buf-ring-mac3 + - const: host2rxdma-host-buf-ring-mac2 + - const: host2rxdma-host-buf-ring-mac1 + - const: rxdma2host-destination-ring-mac3 + - const: rxdma2host-destination-ring-mac2 + - const: rxdma2host-destination-ring-mac1 + - const: host2tcl-input-ring4 + - const: host2tcl-input-ring3 + - const: host2tcl-input-ring2 + - const: host2tcl-input-ring1 + - const: wbm2host-tx-completions-ring3 + - const: wbm2host-tx-completions-ring2 + - const: wbm2host-tx-completions-ring1 + - const: tcl2host-status-ring + + qcom,rproc: + $ref: /schemas/types.yaml#definitions/phandle + description: + DT entry of q6v5-wcss remoteproc driver. + Phandle to a node that can contain the following properties + * compatible + * reg + * reg-names + +required: + - compatible + - reg + - interrupts + - interrupt-names + - qcom,rproc + +additionalProperties: false + +examples: + - | + + q6v5_wcss: q6v5_wcss@CD00000 { + compatible = "qcom,ipq8074-wcss-pil"; + reg = <0xCD00000 0x4040>, + <0x4AB000 0x20>; + reg-names = "qdsp6", + "rmb"; + }; + + wifi0: wifi@c000000 { + compatible = "qcom,ipq8074-wifi"; + reg = <0xc000000 0x2000000>; + interrupts = <0 320 1>, + <0 319 1>, + <0 318 1>, + <0 317 1>, + <0 316 1>, + <0 315 1>, + <0 314 1>, + <0 311 1>, + <0 310 1>, + <0 411 1>, + <0 410 1>, + <0 40 1>, + <0 39 1>, + <0 302 1>, + <0 301 1>, + <0 37 1>, + <0 36 1>, + <0 296 1>, + <0 295 1>, + <0 294 1>, + <0 293 1>, + <0 292 1>, + <0 291 1>, + <0 290 1>, + <0 289 1>, + <0 288 1>, + <0 239 1>, + <0 236 1>, + <0 235 1>, + <0 234 1>, + <0 233 1>, + <0 232 1>, + <0 231 1>, + <0 230 1>, + <0 229 1>, + <0 228 1>, + <0 224 1>, + <0 223 1>, + <0 203 1>, + <0 183 1>, + <0 180 1>, + <0 179 1>, + <0 178 1>, + <0 177 1>, + <0 176 1>, + <0 163 1>, + <0 162 1>, + <0 160 1>, + <0 159 1>, + <0 158 1>, + <0 157 1>, + <0 156 1>; + interrupt-names = "misc-pulse1", + "misc-latch", + "sw-exception", + "watchdog", + "ce0", + "ce1", + "ce2", + "ce3", + "ce4", + "ce5", + "ce6", + "ce7", + "ce8", + "ce9", + "ce10", + "ce11", + "host2wbm-desc-feed", + "host2reo-re-injection", + "host2reo-command", + "host2rxdma-monitor-ring3", + "host2rxdma-monitor-ring2", + "host2rxdma-monitor-ring1", + "reo2ost-exception", + "wbm2host-rx-release", + "reo2host-status", + "reo2host-destination-ring4", + "reo2host-destination-ring3", + "reo2host-destination-ring2", + "reo2host-destination-ring1", + "rxdma2host-monitor-destination-mac3", + "rxdma2host-monitor-destination-mac2", + "rxdma2host-monitor-destination-mac1", + "ppdu-end-interrupts-mac3", + "ppdu-end-interrupts-mac2", + "ppdu-end-interrupts-mac1", + "rxdma2host-monitor-status-ring-mac3", + "rxdma2host-monitor-status-ring-mac2", + "rxdma2host-monitor-status-ring-mac1", + "host2rxdma-host-buf-ring-mac3", + "host2rxdma-host-buf-ring-mac2", + "host2rxdma-host-buf-ring-mac1", + "rxdma2host-destination-ring-mac3", + "rxdma2host-destination-ring-mac2", + "rxdma2host-destination-ring-mac1", + "host2tcl-input-ring4", + "host2tcl-input-ring3", + "host2tcl-input-ring2", + "host2tcl-input-ring1", + "wbm2host-tx-completions-ring3", + "wbm2host-tx-completions-ring2", + "wbm2host-tx-completions-ring1", + "tcl2host-status-ring"; + qcom,rproc = <&q6v5_wcss>; + }; -- GitLab From d5c65159f2895379e11ca13f62feabe93278985d Mon Sep 17 00:00:00 2001 From: Kalle Valo Date: Sat, 23 Nov 2019 09:58:40 +0200 Subject: [PATCH 0018/1437] ath11k: driver for Qualcomm IEEE 802.11ax devices ath11k is a new driver for Qualcomm IEEE 802.11ax devices, first supporting only IPQ8074 SoC using the shared memory AHB bus. ath11k uses mac80211 and supports AP, Station and Mesh modes. Even though ath11k has some similar code as with ath10k (especially the WMI layer) it was concluded to be simpler to have a "clean start" for ath11k code base and not try to share the code with ath10k. This makes maintenance easier and avoids major changes in ath10k, which would have significantly increased the risk of regressions in existing setups. Even though the driver is very similar with ath10k but there are major differences as well. The datapath is completely different. ath11k supports multiple MACs, called "soc" in the firmware interface. And there's only one WMI interface to support. Currently ath11k supports only IEEE 802.11ac mode, but patches for 802.11ax are available and they will be submitted after ath11k is accepted to upstream. The firmware images are available from ath11k-firmware repository but they will be also submitted to linux-firmware: https://github.com/kvalo/ath11k-firmware This was tested with firmware version WLAN.HK.2.1.0.1-00629-QCAHKSWPL_SILICONZ-1. The driver has had multiple authors who are listed in alphabetical order below. Signed-off-by: Anilkumar Kolli Signed-off-by: Bhagavathi Perumal S Signed-off-by: Ganesh Sesetti Signed-off-by: Govindaraj Saminathan Signed-off-by: John Crispin Signed-off-by: Julia Lawall Signed-off-by: Kalle Valo Signed-off-by: Karthikeyan Periyasamy Signed-off-by: kbuild test robot Signed-off-by: Maharaja Kennadyrajan Signed-off-by: Manikanta Pubbisetty Signed-off-by: Miles Hu Signed-off-by: Muna Sinada Signed-off-by: Pradeep Kumar Chitrapu Signed-off-by: Rajkumar Manoharan Signed-off-by: Sathishkumar Muruganandam Signed-off-by: Shashidhar Lakkavalli Signed-off-by: Sriram R Signed-off-by: Sven Eckelmann Signed-off-by: Vasanthakumar Thiagarajan Signed-off-by: Venkateswara Naralasetty --- drivers/net/wireless/ath/Kconfig | 1 + drivers/net/wireless/ath/Makefile | 1 + drivers/net/wireless/ath/ath11k/Kconfig | 35 + drivers/net/wireless/ath/ath11k/Makefile | 26 + drivers/net/wireless/ath/ath11k/ahb.c | 1004 +++ drivers/net/wireless/ath/ath11k/ahb.h | 35 + drivers/net/wireless/ath/ath11k/ce.c | 805 +++ drivers/net/wireless/ath/ath11k/ce.h | 178 + drivers/net/wireless/ath/ath11k/core.c | 772 +++ drivers/net/wireless/ath/ath11k/core.h | 823 +++ drivers/net/wireless/ath/ath11k/debug.c | 1060 ++++ drivers/net/wireless/ath/ath11k/debug.h | 278 + .../net/wireless/ath/ath11k/debug_htt_stats.c | 4432 +++++++++++++ .../net/wireless/ath/ath11k/debug_htt_stats.h | 1620 +++++ drivers/net/wireless/ath/ath11k/debugfs_sta.c | 538 ++ drivers/net/wireless/ath/ath11k/dp.c | 898 +++ drivers/net/wireless/ath/ath11k/dp.h | 1526 +++++ drivers/net/wireless/ath/ath11k/dp_rx.c | 4143 ++++++++++++ drivers/net/wireless/ath/ath11k/dp_rx.h | 84 + drivers/net/wireless/ath/ath11k/dp_tx.c | 943 +++ drivers/net/wireless/ath/ath11k/dp_tx.h | 40 + drivers/net/wireless/ath/ath11k/hal.c | 1124 ++++ drivers/net/wireless/ath/ath11k/hal.h | 897 +++ drivers/net/wireless/ath/ath11k/hal_desc.h | 2469 ++++++++ drivers/net/wireless/ath/ath11k/hal_rx.c | 1221 ++++ drivers/net/wireless/ath/ath11k/hal_rx.h | 352 ++ drivers/net/wireless/ath/ath11k/hal_tx.c | 226 + drivers/net/wireless/ath/ath11k/hal_tx.h | 80 + drivers/net/wireless/ath/ath11k/htc.c | 773 +++ drivers/net/wireless/ath/ath11k/htc.h | 313 + drivers/net/wireless/ath/ath11k/hw.h | 127 + drivers/net/wireless/ath/ath11k/mac.c | 5427 ++++++++++++++++ drivers/net/wireless/ath/ath11k/mac.h | 145 + drivers/net/wireless/ath/ath11k/peer.c | 236 + drivers/net/wireless/ath/ath11k/peer.h | 35 + drivers/net/wireless/ath/ath11k/qmi.c | 2435 +++++++ drivers/net/wireless/ath/ath11k/qmi.h | 445 ++ drivers/net/wireless/ath/ath11k/reg.c | 701 ++ drivers/net/wireless/ath/ath11k/reg.h | 35 + drivers/net/wireless/ath/ath11k/rx_desc.h | 1212 ++++ drivers/net/wireless/ath/ath11k/testmode.c | 199 + drivers/net/wireless/ath/ath11k/testmode.h | 29 + drivers/net/wireless/ath/ath11k/testmode_i.h | 50 + drivers/net/wireless/ath/ath11k/trace.c | 9 + drivers/net/wireless/ath/ath11k/trace.h | 110 + drivers/net/wireless/ath/ath11k/wmi.c | 5632 +++++++++++++++++ drivers/net/wireless/ath/ath11k/wmi.h | 4664 ++++++++++++++ 47 files changed, 48188 insertions(+) create mode 100644 drivers/net/wireless/ath/ath11k/Kconfig create mode 100644 drivers/net/wireless/ath/ath11k/Makefile create mode 100644 drivers/net/wireless/ath/ath11k/ahb.c create mode 100644 drivers/net/wireless/ath/ath11k/ahb.h create mode 100644 drivers/net/wireless/ath/ath11k/ce.c create mode 100644 drivers/net/wireless/ath/ath11k/ce.h create mode 100644 drivers/net/wireless/ath/ath11k/core.c create mode 100644 drivers/net/wireless/ath/ath11k/core.h create mode 100644 drivers/net/wireless/ath/ath11k/debug.c create mode 100644 drivers/net/wireless/ath/ath11k/debug.h create mode 100644 drivers/net/wireless/ath/ath11k/debug_htt_stats.c create mode 100644 drivers/net/wireless/ath/ath11k/debug_htt_stats.h create mode 100644 drivers/net/wireless/ath/ath11k/debugfs_sta.c create mode 100644 drivers/net/wireless/ath/ath11k/dp.c create mode 100644 drivers/net/wireless/ath/ath11k/dp.h create mode 100644 drivers/net/wireless/ath/ath11k/dp_rx.c create mode 100644 drivers/net/wireless/ath/ath11k/dp_rx.h create mode 100644 drivers/net/wireless/ath/ath11k/dp_tx.c create mode 100644 drivers/net/wireless/ath/ath11k/dp_tx.h create mode 100644 drivers/net/wireless/ath/ath11k/hal.c create mode 100644 drivers/net/wireless/ath/ath11k/hal.h create mode 100644 drivers/net/wireless/ath/ath11k/hal_desc.h create mode 100644 drivers/net/wireless/ath/ath11k/hal_rx.c create mode 100644 drivers/net/wireless/ath/ath11k/hal_rx.h create mode 100644 drivers/net/wireless/ath/ath11k/hal_tx.c create mode 100644 drivers/net/wireless/ath/ath11k/hal_tx.h create mode 100644 drivers/net/wireless/ath/ath11k/htc.c create mode 100644 drivers/net/wireless/ath/ath11k/htc.h create mode 100644 drivers/net/wireless/ath/ath11k/hw.h create mode 100644 drivers/net/wireless/ath/ath11k/mac.c create mode 100644 drivers/net/wireless/ath/ath11k/mac.h create mode 100644 drivers/net/wireless/ath/ath11k/peer.c create mode 100644 drivers/net/wireless/ath/ath11k/peer.h create mode 100644 drivers/net/wireless/ath/ath11k/qmi.c create mode 100644 drivers/net/wireless/ath/ath11k/qmi.h create mode 100644 drivers/net/wireless/ath/ath11k/reg.c create mode 100644 drivers/net/wireless/ath/ath11k/reg.h create mode 100644 drivers/net/wireless/ath/ath11k/rx_desc.h create mode 100644 drivers/net/wireless/ath/ath11k/testmode.c create mode 100644 drivers/net/wireless/ath/ath11k/testmode.h create mode 100644 drivers/net/wireless/ath/ath11k/testmode_i.h create mode 100644 drivers/net/wireless/ath/ath11k/trace.c create mode 100644 drivers/net/wireless/ath/ath11k/trace.h create mode 100644 drivers/net/wireless/ath/ath11k/wmi.c create mode 100644 drivers/net/wireless/ath/ath11k/wmi.h diff --git a/drivers/net/wireless/ath/Kconfig b/drivers/net/wireless/ath/Kconfig index 56616d988c965..d6df17d312cdc 100644 --- a/drivers/net/wireless/ath/Kconfig +++ b/drivers/net/wireless/ath/Kconfig @@ -62,5 +62,6 @@ source "drivers/net/wireless/ath/ar5523/Kconfig" source "drivers/net/wireless/ath/wil6210/Kconfig" source "drivers/net/wireless/ath/ath10k/Kconfig" source "drivers/net/wireless/ath/wcn36xx/Kconfig" +source "drivers/net/wireless/ath/ath11k/Kconfig" endif diff --git a/drivers/net/wireless/ath/Makefile b/drivers/net/wireless/ath/Makefile index ee2b2431e5a32..8e4ae9de5ae49 100644 --- a/drivers/net/wireless/ath/Makefile +++ b/drivers/net/wireless/ath/Makefile @@ -7,6 +7,7 @@ obj-$(CONFIG_AR5523) += ar5523/ obj-$(CONFIG_WIL6210) += wil6210/ obj-$(CONFIG_ATH10K) += ath10k/ obj-$(CONFIG_WCN36XX) += wcn36xx/ +obj-$(CONFIG_ATH11K) += ath11k/ obj-$(CONFIG_ATH_COMMON) += ath.o diff --git a/drivers/net/wireless/ath/ath11k/Kconfig b/drivers/net/wireless/ath/ath11k/Kconfig new file mode 100644 index 0000000000000..cfab4fb86aef6 --- /dev/null +++ b/drivers/net/wireless/ath/ath11k/Kconfig @@ -0,0 +1,35 @@ +# SPDX-License-Identifier: BSD-3-Clause-Clear +config ATH11K + tristate "Qualcomm Technologies 802.11ax chipset support" + depends on MAC80211 && HAS_DMA + depends on REMOTEPROC + depends on ARCH_QCOM || COMPILE_TEST + select ATH_COMMON + select QCOM_QMI_HELPERS + ---help--- + This module adds support for Qualcomm Technologies 802.11ax family of + chipsets. + + If you choose to build a module, it'll be called ath11k. + +config ATH11K_DEBUG + bool "QCA ath11k debugging" + depends on ATH11K + ---help--- + Enables debug support + + If unsure, say Y to make it easier to debug problems. + +config ATH11K_DEBUGFS + bool "QCA ath11k debugfs support" + depends on ATH11K && DEBUG_FS + ---help--- + Enable ath11k debugfs support + + If unsure, say Y to make it easier to debug problems. + +config ATH11K_TRACING + bool "ath11k tracing support" + depends on ATH11K && EVENT_TRACING + ---help--- + Select this to use ath11k tracing infrastructure. diff --git a/drivers/net/wireless/ath/ath11k/Makefile b/drivers/net/wireless/ath/ath11k/Makefile new file mode 100644 index 0000000000000..a91d75c1cfebd --- /dev/null +++ b/drivers/net/wireless/ath/ath11k/Makefile @@ -0,0 +1,26 @@ +# SPDX-License-Identifier: BSD-3-Clause-Clear +obj-$(CONFIG_ATH11K) += ath11k.o +ath11k-y += core.o \ + hal.o \ + hal_tx.o \ + hal_rx.o \ + ahb.o \ + wmi.o \ + mac.o \ + reg.o \ + htc.o \ + qmi.o \ + dp.o \ + dp_tx.o \ + dp_rx.o \ + debug.o \ + ce.o \ + peer.o + +ath11k-$(CONFIG_ATH11K_DEBUGFS) += debug_htt_stats.o +ath11k-$(CONFIG_MAC80211_DEBUGFS) += debugfs_sta.o +ath11k-$(CONFIG_NL80211_TESTMODE) += testmode.o +ath11k-$(CONFIG_ATH11K_TRACING) += trace.o + +# for tracing framework to find trace.h +CFLAGS_trace.o := -I$(src) diff --git a/drivers/net/wireless/ath/ath11k/ahb.c b/drivers/net/wireless/ath/ath11k/ahb.c new file mode 100644 index 0000000000000..f80173b8afc6b --- /dev/null +++ b/drivers/net/wireless/ath/ath11k/ahb.c @@ -0,0 +1,1004 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* + * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. + */ + +#include +#include +#include +#include +#include +#include "ahb.h" +#include "debug.h" +#include + +static const struct of_device_id ath11k_ahb_of_match[] = { + /* TODO: Should we change the compatible string to something similar + * to one that ath10k uses? + */ + { .compatible = "qcom,ipq8074-wifi", + .data = (void *)ATH11K_HW_IPQ8074, + }, + { } +}; + +MODULE_DEVICE_TABLE(of, ath11k_ahb_of_match); + +/* Target firmware's Copy Engine configuration. */ +static const struct ce_pipe_config target_ce_config_wlan[] = { + /* CE0: host->target HTC control and raw streams */ + { + .pipenum = __cpu_to_le32(0), + .pipedir = __cpu_to_le32(PIPEDIR_OUT), + .nentries = __cpu_to_le32(32), + .nbytes_max = __cpu_to_le32(2048), + .flags = __cpu_to_le32(CE_ATTR_FLAGS), + .reserved = __cpu_to_le32(0), + }, + + /* CE1: target->host HTT + HTC control */ + { + .pipenum = __cpu_to_le32(1), + .pipedir = __cpu_to_le32(PIPEDIR_IN), + .nentries = __cpu_to_le32(32), + .nbytes_max = __cpu_to_le32(2048), + .flags = __cpu_to_le32(CE_ATTR_FLAGS), + .reserved = __cpu_to_le32(0), + }, + + /* CE2: target->host WMI */ + { + .pipenum = __cpu_to_le32(2), + .pipedir = __cpu_to_le32(PIPEDIR_IN), + .nentries = __cpu_to_le32(32), + .nbytes_max = __cpu_to_le32(2048), + .flags = __cpu_to_le32(CE_ATTR_FLAGS), + .reserved = __cpu_to_le32(0), + }, + + /* CE3: host->target WMI */ + { + .pipenum = __cpu_to_le32(3), + .pipedir = __cpu_to_le32(PIPEDIR_OUT), + .nentries = __cpu_to_le32(32), + .nbytes_max = __cpu_to_le32(2048), + .flags = __cpu_to_le32(CE_ATTR_FLAGS), + .reserved = __cpu_to_le32(0), + }, + + /* CE4: host->target HTT */ + { + .pipenum = __cpu_to_le32(4), + .pipedir = __cpu_to_le32(PIPEDIR_OUT), + .nentries = __cpu_to_le32(256), + .nbytes_max = __cpu_to_le32(256), + .flags = __cpu_to_le32(CE_ATTR_FLAGS | CE_ATTR_DIS_INTR), + .reserved = __cpu_to_le32(0), + }, + + /* CE5: target->host Pktlog */ + { + .pipenum = __cpu_to_le32(5), + .pipedir = __cpu_to_le32(PIPEDIR_IN), + .nentries = __cpu_to_le32(32), + .nbytes_max = __cpu_to_le32(2048), + .flags = __cpu_to_le32(0), + .reserved = __cpu_to_le32(0), + }, + + /* CE6: Reserved for target autonomous hif_memcpy */ + { + .pipenum = __cpu_to_le32(6), + .pipedir = __cpu_to_le32(PIPEDIR_INOUT), + .nentries = __cpu_to_le32(32), + .nbytes_max = __cpu_to_le32(65535), + .flags = __cpu_to_le32(CE_ATTR_FLAGS), + .reserved = __cpu_to_le32(0), + }, + + /* CE7 used only by Host */ + { + .pipenum = __cpu_to_le32(7), + .pipedir = __cpu_to_le32(PIPEDIR_OUT), + .nentries = __cpu_to_le32(32), + .nbytes_max = __cpu_to_le32(2048), + .flags = __cpu_to_le32(CE_ATTR_FLAGS), + .reserved = __cpu_to_le32(0), + }, + + /* CE8 target->host used only by IPA */ + { + .pipenum = __cpu_to_le32(8), + .pipedir = __cpu_to_le32(PIPEDIR_INOUT), + .nentries = __cpu_to_le32(32), + .nbytes_max = __cpu_to_le32(65535), + .flags = __cpu_to_le32(CE_ATTR_FLAGS), + .reserved = __cpu_to_le32(0), + }, + + /* CE9 host->target HTT */ + { + .pipenum = __cpu_to_le32(9), + .pipedir = __cpu_to_le32(PIPEDIR_OUT), + .nentries = __cpu_to_le32(32), + .nbytes_max = __cpu_to_le32(2048), + .flags = __cpu_to_le32(CE_ATTR_FLAGS), + .reserved = __cpu_to_le32(0), + }, + + /* CE10 target->host HTT */ + { + .pipenum = __cpu_to_le32(10), + .pipedir = __cpu_to_le32(PIPEDIR_INOUT_H2H), + .nentries = __cpu_to_le32(0), + .nbytes_max = __cpu_to_le32(0), + .flags = __cpu_to_le32(CE_ATTR_FLAGS), + .reserved = __cpu_to_le32(0), + }, + + /* CE11 Not used */ + { + .pipenum = __cpu_to_le32(0), + .pipedir = __cpu_to_le32(0), + .nentries = __cpu_to_le32(0), + .nbytes_max = __cpu_to_le32(0), + .flags = __cpu_to_le32(CE_ATTR_FLAGS), + .reserved = __cpu_to_le32(0), + }, +}; + +/* Map from service/endpoint to Copy Engine. + * This table is derived from the CE_PCI TABLE, above. + * It is passed to the Target at startup for use by firmware. + */ +static const struct service_to_pipe target_service_to_ce_map_wlan[] = { + { + .service_id = __cpu_to_le32(ATH11K_HTC_SVC_ID_WMI_DATA_VO), + .pipedir = __cpu_to_le32(PIPEDIR_OUT), /* out = UL = host -> target */ + .pipenum = __cpu_to_le32(3), + }, + { + .service_id = __cpu_to_le32(ATH11K_HTC_SVC_ID_WMI_DATA_VO), + .pipedir = __cpu_to_le32(PIPEDIR_IN), /* in = DL = target -> host */ + .pipenum = __cpu_to_le32(2), + }, + { + .service_id = __cpu_to_le32(ATH11K_HTC_SVC_ID_WMI_DATA_BK), + .pipedir = __cpu_to_le32(PIPEDIR_OUT), /* out = UL = host -> target */ + .pipenum = __cpu_to_le32(3), + }, + { + .service_id = __cpu_to_le32(ATH11K_HTC_SVC_ID_WMI_DATA_BK), + .pipedir = __cpu_to_le32(PIPEDIR_IN), /* in = DL = target -> host */ + .pipenum = __cpu_to_le32(2), + }, + { + .service_id = __cpu_to_le32(ATH11K_HTC_SVC_ID_WMI_DATA_BE), + .pipedir = __cpu_to_le32(PIPEDIR_OUT), /* out = UL = host -> target */ + .pipenum = __cpu_to_le32(3), + }, + { + .service_id = __cpu_to_le32(ATH11K_HTC_SVC_ID_WMI_DATA_BE), + .pipedir = __cpu_to_le32(PIPEDIR_IN), /* in = DL = target -> host */ + .pipenum = __cpu_to_le32(2), + }, + { + .service_id = __cpu_to_le32(ATH11K_HTC_SVC_ID_WMI_DATA_VI), + .pipedir = __cpu_to_le32(PIPEDIR_OUT), /* out = UL = host -> target */ + .pipenum = __cpu_to_le32(3), + }, + { + .service_id = __cpu_to_le32(ATH11K_HTC_SVC_ID_WMI_DATA_VI), + .pipedir = __cpu_to_le32(PIPEDIR_IN), /* in = DL = target -> host */ + .pipenum = __cpu_to_le32(2), + }, + { + .service_id = __cpu_to_le32(ATH11K_HTC_SVC_ID_WMI_CONTROL), + .pipedir = __cpu_to_le32(PIPEDIR_OUT), /* out = UL = host -> target */ + .pipenum = __cpu_to_le32(3), + }, + { + .service_id = __cpu_to_le32(ATH11K_HTC_SVC_ID_WMI_CONTROL), + .pipedir = __cpu_to_le32(PIPEDIR_IN), /* in = DL = target -> host */ + .pipenum = __cpu_to_le32(2), + }, + { + .service_id = __cpu_to_le32(ATH11K_HTC_SVC_ID_WMI_CONTROL_MAC1), + .pipedir = __cpu_to_le32(PIPEDIR_OUT), /* out = UL = host -> target */ + .pipenum = __cpu_to_le32(7), + }, + { + .service_id = __cpu_to_le32(ATH11K_HTC_SVC_ID_WMI_CONTROL_MAC1), + .pipedir = __cpu_to_le32(PIPEDIR_IN), /* in = DL = target -> host */ + .pipenum = __cpu_to_le32(2), + }, + { + .service_id = __cpu_to_le32(ATH11K_HTC_SVC_ID_WMI_CONTROL_MAC2), + .pipedir = __cpu_to_le32(PIPEDIR_OUT), /* out = UL = host -> target */ + .pipenum = __cpu_to_le32(9), + }, + { + .service_id = __cpu_to_le32(ATH11K_HTC_SVC_ID_WMI_CONTROL_MAC2), + .pipedir = __cpu_to_le32(PIPEDIR_IN), /* in = DL = target -> host */ + .pipenum = __cpu_to_le32(2), + }, + { + .service_id = __cpu_to_le32(ATH11K_HTC_SVC_ID_RSVD_CTRL), + .pipedir = __cpu_to_le32(PIPEDIR_OUT), /* out = UL = host -> target */ + .pipenum = __cpu_to_le32(0), + }, + { + .service_id = __cpu_to_le32(ATH11K_HTC_SVC_ID_RSVD_CTRL), + .pipedir = __cpu_to_le32(PIPEDIR_IN), /* in = DL = target -> host */ + .pipenum = __cpu_to_le32(1), + }, + { /* not used */ + .service_id = __cpu_to_le32(ATH11K_HTC_SVC_ID_TEST_RAW_STREAMS), + .pipedir = __cpu_to_le32(PIPEDIR_OUT), /* out = UL = host -> target */ + .pipenum = __cpu_to_le32(0), + }, + { /* not used */ + .service_id = __cpu_to_le32(ATH11K_HTC_SVC_ID_TEST_RAW_STREAMS), + .pipedir = __cpu_to_le32(PIPEDIR_IN), /* in = DL = target -> host */ + .pipenum = __cpu_to_le32(1), + }, + { + .service_id = __cpu_to_le32(ATH11K_HTC_SVC_ID_HTT_DATA_MSG), + .pipedir = __cpu_to_le32(PIPEDIR_OUT), /* out = UL = host -> target */ + .pipenum = __cpu_to_le32(4), + }, + { + .service_id = __cpu_to_le32(ATH11K_HTC_SVC_ID_HTT_DATA_MSG), + .pipedir = __cpu_to_le32(PIPEDIR_IN), /* in = DL = target -> host */ + .pipenum = __cpu_to_le32(1), + }, + { + .service_id = __cpu_to_le32(ATH11K_HTC_SVC_ID_PKT_LOG), + .pipedir = __cpu_to_le32(PIPEDIR_IN), /* in = DL = target -> host */ + .pipenum = __cpu_to_le32(5), + }, + + /* (Additions here) */ + + { /* terminator entry */ } +}; + +#define ATH11K_IRQ_CE0_OFFSET 4 + +static const char *irq_name[ATH11K_IRQ_NUM_MAX] = { + "misc-pulse1", + "misc-latch", + "sw-exception", + "watchdog", + "ce0", + "ce1", + "ce2", + "ce3", + "ce4", + "ce5", + "ce6", + "ce7", + "ce8", + "ce9", + "ce10", + "ce11", + "host2wbm-desc-feed", + "host2reo-re-injection", + "host2reo-command", + "host2rxdma-monitor-ring3", + "host2rxdma-monitor-ring2", + "host2rxdma-monitor-ring1", + "reo2ost-exception", + "wbm2host-rx-release", + "reo2host-status", + "reo2host-destination-ring4", + "reo2host-destination-ring3", + "reo2host-destination-ring2", + "reo2host-destination-ring1", + "rxdma2host-monitor-destination-mac3", + "rxdma2host-monitor-destination-mac2", + "rxdma2host-monitor-destination-mac1", + "ppdu-end-interrupts-mac3", + "ppdu-end-interrupts-mac2", + "ppdu-end-interrupts-mac1", + "rxdma2host-monitor-status-ring-mac3", + "rxdma2host-monitor-status-ring-mac2", + "rxdma2host-monitor-status-ring-mac1", + "host2rxdma-host-buf-ring-mac3", + "host2rxdma-host-buf-ring-mac2", + "host2rxdma-host-buf-ring-mac1", + "rxdma2host-destination-ring-mac3", + "rxdma2host-destination-ring-mac2", + "rxdma2host-destination-ring-mac1", + "host2tcl-input-ring4", + "host2tcl-input-ring3", + "host2tcl-input-ring2", + "host2tcl-input-ring1", + "wbm2host-tx-completions-ring3", + "wbm2host-tx-completions-ring2", + "wbm2host-tx-completions-ring1", + "tcl2host-status-ring", +}; + +#define ATH11K_TX_RING_MASK_0 0x1 +#define ATH11K_TX_RING_MASK_1 0x2 +#define ATH11K_TX_RING_MASK_2 0x4 + +#define ATH11K_RX_RING_MASK_0 0x1 +#define ATH11K_RX_RING_MASK_1 0x2 +#define ATH11K_RX_RING_MASK_2 0x4 +#define ATH11K_RX_RING_MASK_3 0x8 + +#define ATH11K_RX_ERR_RING_MASK_0 0x1 + +#define ATH11K_RX_WBM_REL_RING_MASK_0 0x1 + +#define ATH11K_REO_STATUS_RING_MASK_0 0x1 + +#define ATH11K_RXDMA2HOST_RING_MASK_0 0x1 +#define ATH11K_RXDMA2HOST_RING_MASK_1 0x2 +#define ATH11K_RXDMA2HOST_RING_MASK_2 0x4 + +#define ATH11K_HOST2RXDMA_RING_MASK_0 0x1 +#define ATH11K_HOST2RXDMA_RING_MASK_1 0x2 +#define ATH11K_HOST2RXDMA_RING_MASK_2 0x4 + +#define ATH11K_RX_MON_STATUS_RING_MASK_0 0x1 +#define ATH11K_RX_MON_STATUS_RING_MASK_1 0x2 +#define ATH11K_RX_MON_STATUS_RING_MASK_2 0x4 + +const u8 ath11k_tx_ring_mask[ATH11K_EXT_IRQ_GRP_NUM_MAX] = { + ATH11K_TX_RING_MASK_0, + ATH11K_TX_RING_MASK_1, + ATH11K_TX_RING_MASK_2, +}; + +const u8 rx_mon_status_ring_mask[ATH11K_EXT_IRQ_GRP_NUM_MAX] = { + 0, 0, 0, 0, + ATH11K_RX_MON_STATUS_RING_MASK_0, + ATH11K_RX_MON_STATUS_RING_MASK_1, + ATH11K_RX_MON_STATUS_RING_MASK_2, +}; + +const u8 ath11k_rx_ring_mask[ATH11K_EXT_IRQ_GRP_NUM_MAX] = { + 0, 0, 0, 0, 0, 0, 0, + ATH11K_RX_RING_MASK_0, + ATH11K_RX_RING_MASK_1, + ATH11K_RX_RING_MASK_2, + ATH11K_RX_RING_MASK_3, +}; + +const u8 ath11k_rx_err_ring_mask[ATH11K_EXT_IRQ_GRP_NUM_MAX] = { + ATH11K_RX_ERR_RING_MASK_0, +}; + +const u8 ath11k_rx_wbm_rel_ring_mask[ATH11K_EXT_IRQ_GRP_NUM_MAX] = { + ATH11K_RX_WBM_REL_RING_MASK_0, +}; + +const u8 ath11k_reo_status_ring_mask[ATH11K_EXT_IRQ_GRP_NUM_MAX] = { + ATH11K_REO_STATUS_RING_MASK_0, +}; + +const u8 ath11k_rxdma2host_ring_mask[ATH11K_EXT_IRQ_GRP_NUM_MAX] = { + ATH11K_RXDMA2HOST_RING_MASK_0, + ATH11K_RXDMA2HOST_RING_MASK_1, + ATH11K_RXDMA2HOST_RING_MASK_2, +}; + +const u8 ath11k_host2rxdma_ring_mask[ATH11K_EXT_IRQ_GRP_NUM_MAX] = { + ATH11K_HOST2RXDMA_RING_MASK_0, + ATH11K_HOST2RXDMA_RING_MASK_1, + ATH11K_HOST2RXDMA_RING_MASK_2, +}; + +/* enum ext_irq_num - irq numbers that can be used by external modules + * like datapath + */ +enum ext_irq_num { + host2wbm_desc_feed = 16, + host2reo_re_injection, + host2reo_command, + host2rxdma_monitor_ring3, + host2rxdma_monitor_ring2, + host2rxdma_monitor_ring1, + reo2host_exception, + wbm2host_rx_release, + reo2host_status, + reo2host_destination_ring4, + reo2host_destination_ring3, + reo2host_destination_ring2, + reo2host_destination_ring1, + rxdma2host_monitor_destination_mac3, + rxdma2host_monitor_destination_mac2, + rxdma2host_monitor_destination_mac1, + ppdu_end_interrupts_mac3, + ppdu_end_interrupts_mac2, + ppdu_end_interrupts_mac1, + rxdma2host_monitor_status_ring_mac3, + rxdma2host_monitor_status_ring_mac2, + rxdma2host_monitor_status_ring_mac1, + host2rxdma_host_buf_ring_mac3, + host2rxdma_host_buf_ring_mac2, + host2rxdma_host_buf_ring_mac1, + rxdma2host_destination_ring_mac3, + rxdma2host_destination_ring_mac2, + rxdma2host_destination_ring_mac1, + host2tcl_input_ring4, + host2tcl_input_ring3, + host2tcl_input_ring2, + host2tcl_input_ring1, + wbm2host_tx_completions_ring3, + wbm2host_tx_completions_ring2, + wbm2host_tx_completions_ring1, + tcl2host_status_ring, +}; + +static void ath11k_ahb_kill_tasklets(struct ath11k_base *ab) +{ + int i; + + for (i = 0; i < CE_COUNT; i++) { + struct ath11k_ce_pipe *ce_pipe = &ab->ce.ce_pipe[i]; + + if (ath11k_ce_get_attr_flags(i) & CE_ATTR_DIS_INTR) + continue; + + tasklet_kill(&ce_pipe->intr_tq); + } +} + +static void ath11k_ahb_ext_grp_disable(struct ath11k_ext_irq_grp *irq_grp) +{ + int i; + + for (i = 0; i < irq_grp->num_irq; i++) + disable_irq_nosync(irq_grp->ab->irq_num[irq_grp->irqs[i]]); +} + +static void __ath11k_ahb_ext_irq_disable(struct ath11k_base *ab) +{ + struct sk_buff *skb; + int i; + + for (i = 0; i < ATH11K_EXT_IRQ_GRP_NUM_MAX; i++) { + struct ath11k_ext_irq_grp *irq_grp = &ab->ext_irq_grp[i]; + + ath11k_ahb_ext_grp_disable(irq_grp); + + napi_synchronize(&irq_grp->napi); + napi_disable(&irq_grp->napi); + + while ((skb = __skb_dequeue(&irq_grp->pending_q))) + dev_kfree_skb_any(skb); + } +} + +static void ath11k_ahb_ext_grp_enable(struct ath11k_ext_irq_grp *irq_grp) +{ + int i; + + for (i = 0; i < irq_grp->num_irq; i++) + enable_irq(irq_grp->ab->irq_num[irq_grp->irqs[i]]); +} + +static void ath11k_ahb_setbit32(struct ath11k_base *ab, u8 bit, u32 offset) +{ + u32 val; + + val = ath11k_ahb_read32(ab, offset); + ath11k_ahb_write32(ab, offset, val | BIT(bit)); +} + +static void ath11k_ahb_clearbit32(struct ath11k_base *ab, u8 bit, u32 offset) +{ + u32 val; + + val = ath11k_ahb_read32(ab, offset); + ath11k_ahb_write32(ab, offset, val & ~BIT(bit)); +} + +static void ath11k_ahb_ce_irq_enable(struct ath11k_base *ab, u16 ce_id) +{ + const struct ce_pipe_config *ce_config; + + ce_config = &target_ce_config_wlan[ce_id]; + if (__le32_to_cpu(ce_config->pipedir) & PIPEDIR_OUT) + ath11k_ahb_setbit32(ab, ce_id, CE_HOST_IE_ADDRESS); + + if (__le32_to_cpu(ce_config->pipedir) & PIPEDIR_IN) { + ath11k_ahb_setbit32(ab, ce_id, CE_HOST_IE_2_ADDRESS); + ath11k_ahb_setbit32(ab, ce_id + CE_HOST_IE_3_SHIFT, + CE_HOST_IE_3_ADDRESS); + } +} + +static void ath11k_ahb_ce_irq_disable(struct ath11k_base *ab, u16 ce_id) +{ + const struct ce_pipe_config *ce_config; + + ce_config = &target_ce_config_wlan[ce_id]; + if (__le32_to_cpu(ce_config->pipedir) & PIPEDIR_OUT) + ath11k_ahb_clearbit32(ab, ce_id, CE_HOST_IE_ADDRESS); + + if (__le32_to_cpu(ce_config->pipedir) & PIPEDIR_IN) { + ath11k_ahb_clearbit32(ab, ce_id, CE_HOST_IE_2_ADDRESS); + ath11k_ahb_clearbit32(ab, ce_id + CE_HOST_IE_3_SHIFT, + CE_HOST_IE_3_ADDRESS); + } +} + +static void ath11k_ahb_sync_ce_irqs(struct ath11k_base *ab) +{ + int i; + int irq_idx; + + for (i = 0; i < CE_COUNT; i++) { + if (ath11k_ce_get_attr_flags(i) & CE_ATTR_DIS_INTR) + continue; + + irq_idx = ATH11K_IRQ_CE0_OFFSET + i; + synchronize_irq(ab->irq_num[irq_idx]); + } +} + +static void ath11k_ahb_sync_ext_irqs(struct ath11k_base *ab) +{ + int i, j; + int irq_idx; + + for (i = 0; i < ATH11K_EXT_IRQ_GRP_NUM_MAX; i++) { + struct ath11k_ext_irq_grp *irq_grp = &ab->ext_irq_grp[i]; + + for (j = 0; j < irq_grp->num_irq; j++) { + irq_idx = irq_grp->irqs[j]; + synchronize_irq(ab->irq_num[irq_idx]); + } + } +} + +static void ath11k_ahb_ce_irqs_enable(struct ath11k_base *ab) +{ + int i; + + for (i = 0; i < CE_COUNT; i++) { + if (ath11k_ce_get_attr_flags(i) & CE_ATTR_DIS_INTR) + continue; + ath11k_ahb_ce_irq_enable(ab, i); + } +} + +static void ath11k_ahb_ce_irqs_disable(struct ath11k_base *ab) +{ + int i; + + for (i = 0; i < CE_COUNT; i++) { + if (ath11k_ce_get_attr_flags(i) & CE_ATTR_DIS_INTR) + continue; + ath11k_ahb_ce_irq_disable(ab, i); + } +} + +int ath11k_ahb_start(struct ath11k_base *ab) +{ + ath11k_ahb_ce_irqs_enable(ab); + ath11k_ce_rx_post_buf(ab); + + return 0; +} + +void ath11k_ahb_ext_irq_enable(struct ath11k_base *ab) +{ + int i; + + for (i = 0; i < ATH11K_EXT_IRQ_GRP_NUM_MAX; i++) { + struct ath11k_ext_irq_grp *irq_grp = &ab->ext_irq_grp[i]; + + napi_enable(&irq_grp->napi); + ath11k_ahb_ext_grp_enable(irq_grp); + } +} + +void ath11k_ahb_ext_irq_disable(struct ath11k_base *ab) +{ + __ath11k_ahb_ext_irq_disable(ab); + ath11k_ahb_sync_ext_irqs(ab); +} + +void ath11k_ahb_stop(struct ath11k_base *ab) +{ + if (!test_bit(ATH11K_FLAG_CRASH_FLUSH, &ab->dev_flags)) + ath11k_ahb_ce_irqs_disable(ab); + ath11k_ahb_sync_ce_irqs(ab); + ath11k_ahb_kill_tasklets(ab); + del_timer_sync(&ab->rx_replenish_retry); + ath11k_ce_cleanup_pipes(ab); +} + +int ath11k_ahb_power_up(struct ath11k_base *ab) +{ + int ret; + + ret = rproc_boot(ab->tgt_rproc); + if (ret) + ath11k_err(ab, "failed to boot the remote processor Q6\n"); + + return ret; +} + +void ath11k_ahb_power_down(struct ath11k_base *ab) +{ + rproc_shutdown(ab->tgt_rproc); +} + +static void ath11k_ahb_init_qmi_ce_config(struct ath11k_base *ab) +{ + struct ath11k_qmi_ce_cfg *cfg = &ab->qmi.ce_cfg; + + cfg->tgt_ce = (u8 *)target_ce_config_wlan; + cfg->tgt_ce_len = sizeof(target_ce_config_wlan); + + cfg->svc_to_ce_map = (u8 *)target_service_to_ce_map_wlan; + cfg->svc_to_ce_map_len = sizeof(target_service_to_ce_map_wlan); +} + +static void ath11k_ahb_free_ext_irq(struct ath11k_base *ab) +{ + int i, j; + + for (i = 0; i < ATH11K_EXT_IRQ_GRP_NUM_MAX; i++) { + struct ath11k_ext_irq_grp *irq_grp = &ab->ext_irq_grp[i]; + + for (j = 0; j < irq_grp->num_irq; j++) + free_irq(ab->irq_num[irq_grp->irqs[j]], irq_grp); + } +} + +static void ath11k_ahb_free_irq(struct ath11k_base *ab) +{ + int irq_idx; + int i; + + for (i = 0; i < CE_COUNT; i++) { + if (ath11k_ce_get_attr_flags(i) & CE_ATTR_DIS_INTR) + continue; + irq_idx = ATH11K_IRQ_CE0_OFFSET + i; + free_irq(ab->irq_num[irq_idx], &ab->ce.ce_pipe[i]); + } + + ath11k_ahb_free_ext_irq(ab); +} + +static void ath11k_ahb_ce_tasklet(unsigned long data) +{ + struct ath11k_ce_pipe *ce_pipe = (struct ath11k_ce_pipe *)data; + + ath11k_ce_per_engine_service(ce_pipe->ab, ce_pipe->pipe_num); + + ath11k_ahb_ce_irq_enable(ce_pipe->ab, ce_pipe->pipe_num); +} + +static irqreturn_t ath11k_ahb_ce_interrupt_handler(int irq, void *arg) +{ + struct ath11k_ce_pipe *ce_pipe = arg; + + ath11k_ahb_ce_irq_disable(ce_pipe->ab, ce_pipe->pipe_num); + + tasklet_schedule(&ce_pipe->intr_tq); + + return IRQ_HANDLED; +} + +static int ath11k_ahb_ext_grp_napi_poll(struct napi_struct *napi, int budget) +{ + struct ath11k_ext_irq_grp *irq_grp = container_of(napi, + struct ath11k_ext_irq_grp, + napi); + struct ath11k_base *ab = irq_grp->ab; + int work_done; + + work_done = ath11k_dp_service_srng(ab, irq_grp, budget); + if (work_done < budget) { + napi_complete_done(napi, work_done); + ath11k_ahb_ext_grp_enable(irq_grp); + } + + if (work_done > budget) + work_done = budget; + + return work_done; +} + +static irqreturn_t ath11k_ahb_ext_interrupt_handler(int irq, void *arg) +{ + struct ath11k_ext_irq_grp *irq_grp = arg; + + ath11k_ahb_ext_grp_disable(irq_grp); + + napi_schedule(&irq_grp->napi); + + return IRQ_HANDLED; +} + +static int ath11k_ahb_ext_irq_config(struct ath11k_base *ab) +{ + int i, j; + int irq; + int ret; + + for (i = 0; i < ATH11K_EXT_IRQ_GRP_NUM_MAX; i++) { + struct ath11k_ext_irq_grp *irq_grp = &ab->ext_irq_grp[i]; + u32 num_irq = 0; + + irq_grp->ab = ab; + irq_grp->grp_id = i; + init_dummy_netdev(&irq_grp->napi_ndev); + netif_napi_add(&irq_grp->napi_ndev, &irq_grp->napi, + ath11k_ahb_ext_grp_napi_poll, NAPI_POLL_WEIGHT); + __skb_queue_head_init(&irq_grp->pending_q); + + for (j = 0; j < ATH11K_EXT_IRQ_NUM_MAX; j++) { + if (ath11k_tx_ring_mask[i] & BIT(j)) { + irq_grp->irqs[num_irq++] = + wbm2host_tx_completions_ring1 - j; + } + + if (ath11k_rx_ring_mask[i] & BIT(j)) { + irq_grp->irqs[num_irq++] = + reo2host_destination_ring1 - j; + } + + if (ath11k_rx_err_ring_mask[i] & BIT(j)) + irq_grp->irqs[num_irq++] = reo2host_exception; + + if (ath11k_rx_wbm_rel_ring_mask[i] & BIT(j)) + irq_grp->irqs[num_irq++] = wbm2host_rx_release; + + if (ath11k_reo_status_ring_mask[i] & BIT(j)) + irq_grp->irqs[num_irq++] = reo2host_status; + + if (j < MAX_RADIOS) { + if (ath11k_rxdma2host_ring_mask[i] & BIT(j)) { + irq_grp->irqs[num_irq++] = + rxdma2host_destination_ring_mac1 + - ath11k_core_get_hw_mac_id(ab, j); + } + + if (ath11k_host2rxdma_ring_mask[i] & BIT(j)) { + irq_grp->irqs[num_irq++] = + host2rxdma_host_buf_ring_mac1 + - ath11k_core_get_hw_mac_id(ab, j); + } + + if (rx_mon_status_ring_mask[i] & BIT(j)) { + irq_grp->irqs[num_irq++] = + ppdu_end_interrupts_mac1 - + ath11k_core_get_hw_mac_id(ab, j); + irq_grp->irqs[num_irq++] = + rxdma2host_monitor_status_ring_mac1 - + ath11k_core_get_hw_mac_id(ab, j); + } + } + } + irq_grp->num_irq = num_irq; + + for (j = 0; j < irq_grp->num_irq; j++) { + int irq_idx = irq_grp->irqs[j]; + + irq = platform_get_irq_byname(ab->pdev, + irq_name[irq_idx]); + ab->irq_num[irq_idx] = irq; + irq_set_status_flags(irq, IRQ_NOAUTOEN); + ret = request_irq(irq, ath11k_ahb_ext_interrupt_handler, + IRQF_TRIGGER_RISING, + irq_name[irq_idx], irq_grp); + if (ret) { + ath11k_err(ab, "failed request_irq for %d\n", + irq); + } + } + } + + return 0; +} + +static int ath11k_ahb_config_irq(struct ath11k_base *ab) +{ + int irq, irq_idx, i; + int ret; + + /* Configure CE irqs */ + for (i = 0; i < CE_COUNT; i++) { + struct ath11k_ce_pipe *ce_pipe = &ab->ce.ce_pipe[i]; + + if (ath11k_ce_get_attr_flags(i) & CE_ATTR_DIS_INTR) + continue; + + irq_idx = ATH11K_IRQ_CE0_OFFSET + i; + + tasklet_init(&ce_pipe->intr_tq, ath11k_ahb_ce_tasklet, + (unsigned long)ce_pipe); + irq = platform_get_irq_byname(ab->pdev, irq_name[irq_idx]); + ret = request_irq(irq, ath11k_ahb_ce_interrupt_handler, + IRQF_TRIGGER_RISING, irq_name[irq_idx], + ce_pipe); + if (ret) + return ret; + + ab->irq_num[irq_idx] = irq; + } + + /* Configure external interrupts */ + ret = ath11k_ahb_ext_irq_config(ab); + + return ret; +} + +int ath11k_ahb_map_service_to_pipe(struct ath11k_base *ab, u16 service_id, + u8 *ul_pipe, u8 *dl_pipe) +{ + const struct service_to_pipe *entry; + bool ul_set = false, dl_set = false; + int i; + + for (i = 0; i < ARRAY_SIZE(target_service_to_ce_map_wlan); i++) { + entry = &target_service_to_ce_map_wlan[i]; + + if (__le32_to_cpu(entry->service_id) != service_id) + continue; + + switch (__le32_to_cpu(entry->pipedir)) { + case PIPEDIR_NONE: + break; + case PIPEDIR_IN: + WARN_ON(dl_set); + *dl_pipe = __le32_to_cpu(entry->pipenum); + dl_set = true; + break; + case PIPEDIR_OUT: + WARN_ON(ul_set); + *ul_pipe = __le32_to_cpu(entry->pipenum); + ul_set = true; + break; + case PIPEDIR_INOUT: + WARN_ON(dl_set); + WARN_ON(ul_set); + *dl_pipe = __le32_to_cpu(entry->pipenum); + *ul_pipe = __le32_to_cpu(entry->pipenum); + dl_set = true; + ul_set = true; + break; + } + } + + if (WARN_ON(!ul_set || !dl_set)) + return -ENOENT; + + return 0; +} + +static int ath11k_ahb_probe(struct platform_device *pdev) +{ + struct ath11k_base *ab; + const struct of_device_id *of_id; + struct resource *mem_res; + void __iomem *mem; + int ret; + + of_id = of_match_device(ath11k_ahb_of_match, &pdev->dev); + if (!of_id) { + dev_err(&pdev->dev, "failed to find matching device tree id\n"); + return -EINVAL; + } + + mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!mem_res) { + dev_err(&pdev->dev, "failed to get IO memory resource\n"); + return -ENXIO; + } + + mem = devm_ioremap_resource(&pdev->dev, mem_res); + if (IS_ERR(mem)) { + dev_err(&pdev->dev, "ioremap error\n"); + return PTR_ERR(mem); + } + + ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); + if (ret) { + dev_err(&pdev->dev, "failed to set 32-bit consistent dma\n"); + return ret; + } + + ab = ath11k_core_alloc(&pdev->dev); + if (!ab) { + dev_err(&pdev->dev, "failed to allocate ath11k base\n"); + return -ENOMEM; + } + + ab->pdev = pdev; + ab->hw_rev = (enum ath11k_hw_rev)of_id->data; + ab->mem = mem; + ab->mem_len = resource_size(mem_res); + platform_set_drvdata(pdev, ab); + + ret = ath11k_hal_srng_init(ab); + if (ret) + goto err_core_free; + + ret = ath11k_ce_alloc_pipes(ab); + if (ret) { + ath11k_err(ab, "failed to allocate ce pipes: %d\n", ret); + goto err_hal_srng_deinit; + } + + ath11k_ahb_init_qmi_ce_config(ab); + + ret = ath11k_ahb_config_irq(ab); + if (ret) { + ath11k_err(ab, "failed to configure irq: %d\n", ret); + goto err_ce_free; + } + + ret = ath11k_core_init(ab); + if (ret) { + ath11k_err(ab, "failed to init core: %d\n", ret); + goto err_ce_free; + } + + return 0; + +err_ce_free: + ath11k_ce_free_pipes(ab); + +err_hal_srng_deinit: + ath11k_hal_srng_deinit(ab); + +err_core_free: + ath11k_core_free(ab); + platform_set_drvdata(pdev, NULL); + + return ret; +} + +static int ath11k_ahb_remove(struct platform_device *pdev) +{ + struct ath11k_base *ab = platform_get_drvdata(pdev); + + reinit_completion(&ab->driver_recovery); + + if (test_bit(ATH11K_FLAG_RECOVERY, &ab->dev_flags)) + wait_for_completion_timeout(&ab->driver_recovery, + ATH11K_AHB_RECOVERY_TIMEOUT); + + set_bit(ATH11K_FLAG_UNREGISTERING, &ab->dev_flags); + cancel_work_sync(&ab->restart_work); + + ath11k_core_deinit(ab); + ath11k_ahb_free_irq(ab); + + ath11k_hal_srng_deinit(ab); + ath11k_ce_free_pipes(ab); + ath11k_core_free(ab); + platform_set_drvdata(pdev, NULL); + + return 0; +} + +static struct platform_driver ath11k_ahb_driver = { + .driver = { + .name = "ath11k", + .of_match_table = ath11k_ahb_of_match, + }, + .probe = ath11k_ahb_probe, + .remove = ath11k_ahb_remove, +}; + +int ath11k_ahb_init(void) +{ + return platform_driver_register(&ath11k_ahb_driver); +} + +void ath11k_ahb_exit(void) +{ + platform_driver_unregister(&ath11k_ahb_driver); +} diff --git a/drivers/net/wireless/ath/ath11k/ahb.h b/drivers/net/wireless/ath/ath11k/ahb.h new file mode 100644 index 0000000000000..93f46dfe22df5 --- /dev/null +++ b/drivers/net/wireless/ath/ath11k/ahb.h @@ -0,0 +1,35 @@ +/* SPDX-License-Identifier: BSD-3-Clause-Clear */ +/* + * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. + */ +#ifndef ATH11K_AHB_H +#define ATH11K_AHB_H + +#include "core.h" + +#define ATH11K_AHB_RECOVERY_TIMEOUT (3 * HZ) +struct ath11k_base; + +static inline u32 ath11k_ahb_read32(struct ath11k_base *ab, u32 offset) +{ + return ioread32(ab->mem + offset); +} + +static inline void ath11k_ahb_write32(struct ath11k_base *ab, u32 offset, u32 value) +{ + iowrite32(value, ab->mem + offset); +} + +void ath11k_ahb_ext_irq_enable(struct ath11k_base *ab); +void ath11k_ahb_ext_irq_disable(struct ath11k_base *ab); +int ath11k_ahb_start(struct ath11k_base *ab); +void ath11k_ahb_stop(struct ath11k_base *ab); +int ath11k_ahb_power_up(struct ath11k_base *ab); +void ath11k_ahb_power_down(struct ath11k_base *ab); +int ath11k_ahb_map_service_to_pipe(struct ath11k_base *ab, u16 service_id, + u8 *ul_pipe, u8 *dl_pipe); + +int ath11k_ahb_init(void); +void ath11k_ahb_exit(void); + +#endif diff --git a/drivers/net/wireless/ath/ath11k/ce.c b/drivers/net/wireless/ath/ath11k/ce.c new file mode 100644 index 0000000000000..a6572b4143033 --- /dev/null +++ b/drivers/net/wireless/ath/ath11k/ce.c @@ -0,0 +1,805 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* + * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. + */ + +#include "dp_rx.h" +#include "debug.h" + +static const struct ce_attr host_ce_config_wlan[] = { + /* CE0: host->target HTC control and raw streams */ + { + .flags = CE_ATTR_FLAGS, + .src_nentries = 16, + .src_sz_max = 2048, + .dest_nentries = 0, + }, + + /* CE1: target->host HTT + HTC control */ + { + .flags = CE_ATTR_FLAGS, + .src_nentries = 0, + .src_sz_max = 2048, + .dest_nentries = 512, + .recv_cb = ath11k_htc_rx_completion_handler, + }, + + /* CE2: target->host WMI */ + { + .flags = CE_ATTR_FLAGS, + .src_nentries = 0, + .src_sz_max = 2048, + .dest_nentries = 512, + .recv_cb = ath11k_htc_rx_completion_handler, + }, + + /* CE3: host->target WMI (mac0) */ + { + .flags = CE_ATTR_FLAGS, + .src_nentries = 32, + .src_sz_max = 2048, + .dest_nentries = 0, + }, + + /* CE4: host->target HTT */ + { + .flags = CE_ATTR_FLAGS | CE_ATTR_DIS_INTR, + .src_nentries = 2048, + .src_sz_max = 256, + .dest_nentries = 0, + }, + + /* CE5: target->host pktlog */ + { + .flags = CE_ATTR_FLAGS, + .src_nentries = 0, + .src_sz_max = 2048, + .dest_nentries = 512, + .recv_cb = ath11k_dp_htt_htc_t2h_msg_handler, + }, + + /* CE6: target autonomous hif_memcpy */ + { + .flags = CE_ATTR_FLAGS | CE_ATTR_DIS_INTR, + .src_nentries = 0, + .src_sz_max = 0, + .dest_nentries = 0, + }, + + /* CE7: host->target WMI (mac1) */ + { + .flags = CE_ATTR_FLAGS, + .src_nentries = 32, + .src_sz_max = 2048, + .dest_nentries = 0, + }, + + /* CE8: target autonomous hif_memcpy */ + { + .flags = CE_ATTR_FLAGS, + .src_nentries = 0, + .src_sz_max = 0, + .dest_nentries = 0, + }, + + /* CE9: host->target WMI (mac2) */ + { + .flags = CE_ATTR_FLAGS, + .src_nentries = 32, + .src_sz_max = 2048, + .dest_nentries = 0, + }, + + /* CE10: target->host HTT */ + { + .flags = CE_ATTR_FLAGS, + .src_nentries = 0, + .src_sz_max = 2048, + .dest_nentries = 512, + .recv_cb = ath11k_htc_rx_completion_handler, + }, + + /* CE11: Not used */ + { + .flags = CE_ATTR_FLAGS, + .src_nentries = 0, + .src_sz_max = 0, + .dest_nentries = 0, + }, +}; + +static int ath11k_ce_rx_buf_enqueue_pipe(struct ath11k_ce_pipe *pipe, + struct sk_buff *skb, dma_addr_t paddr) +{ + struct ath11k_base *ab = pipe->ab; + struct ath11k_ce_ring *ring = pipe->dest_ring; + struct hal_srng *srng; + unsigned int write_index; + unsigned int nentries_mask = ring->nentries_mask; + u32 *desc; + int ret; + + lockdep_assert_held(&ab->ce.ce_lock); + + write_index = ring->write_index; + + srng = &ab->hal.srng_list[ring->hal_ring_id]; + + spin_lock_bh(&srng->lock); + + ath11k_hal_srng_access_begin(ab, srng); + + if (unlikely(ath11k_hal_srng_src_num_free(ab, srng, false) < 1)) { + ret = -ENOSPC; + goto exit; + } + + desc = ath11k_hal_srng_src_get_next_entry(ab, srng); + if (!desc) { + ret = -ENOSPC; + goto exit; + } + + ath11k_hal_ce_dst_set_desc(desc, paddr); + + ring->skb[write_index] = skb; + write_index = CE_RING_IDX_INCR(nentries_mask, write_index); + ring->write_index = write_index; + + pipe->rx_buf_needed--; + + ret = 0; +exit: + ath11k_hal_srng_access_end(ab, srng); + + spin_unlock_bh(&srng->lock); + + return ret; +} + +static int ath11k_ce_rx_post_pipe(struct ath11k_ce_pipe *pipe) +{ + struct ath11k_base *ab = pipe->ab; + struct sk_buff *skb; + dma_addr_t paddr; + int ret = 0; + + if (!(pipe->dest_ring || pipe->status_ring)) + return 0; + + spin_lock_bh(&ab->ce.ce_lock); + while (pipe->rx_buf_needed) { + skb = dev_alloc_skb(pipe->buf_sz); + if (!skb) { + ret = -ENOMEM; + goto exit; + } + + WARN_ON_ONCE(!IS_ALIGNED((unsigned long)skb->data, 4)); + + paddr = dma_map_single(ab->dev, skb->data, + skb->len + skb_tailroom(skb), + DMA_FROM_DEVICE); + if (unlikely(dma_mapping_error(ab->dev, paddr))) { + ath11k_warn(ab, "failed to dma map ce rx buf\n"); + dev_kfree_skb_any(skb); + ret = -EIO; + goto exit; + } + + ATH11K_SKB_RXCB(skb)->paddr = paddr; + + ret = ath11k_ce_rx_buf_enqueue_pipe(pipe, skb, paddr); + + if (ret) { + ath11k_warn(ab, "failed to enqueue rx buf: %d\n", ret); + dma_unmap_single(ab->dev, paddr, + skb->len + skb_tailroom(skb), + DMA_FROM_DEVICE); + dev_kfree_skb_any(skb); + goto exit; + } + } + +exit: + spin_unlock_bh(&ab->ce.ce_lock); + return ret; +} + +static int ath11k_ce_completed_recv_next(struct ath11k_ce_pipe *pipe, + struct sk_buff **skb, int *nbytes) +{ + struct ath11k_base *ab = pipe->ab; + struct hal_srng *srng; + unsigned int sw_index; + unsigned int nentries_mask; + u32 *desc; + int ret = 0; + + spin_lock_bh(&ab->ce.ce_lock); + + sw_index = pipe->dest_ring->sw_index; + nentries_mask = pipe->dest_ring->nentries_mask; + + srng = &ab->hal.srng_list[pipe->status_ring->hal_ring_id]; + + spin_lock_bh(&srng->lock); + + ath11k_hal_srng_access_begin(ab, srng); + + desc = ath11k_hal_srng_dst_get_next_entry(ab, srng); + if (!desc) { + ret = -EIO; + goto err; + } + + *nbytes = ath11k_hal_ce_dst_status_get_length(desc); + if (*nbytes == 0) { + ret = -EIO; + goto err; + } + + *skb = pipe->dest_ring->skb[sw_index]; + pipe->dest_ring->skb[sw_index] = NULL; + + sw_index = CE_RING_IDX_INCR(nentries_mask, sw_index); + pipe->dest_ring->sw_index = sw_index; + + pipe->rx_buf_needed++; +err: + ath11k_hal_srng_access_end(ab, srng); + + spin_unlock_bh(&srng->lock); + + spin_unlock_bh(&ab->ce.ce_lock); + + return ret; +} + +static void ath11k_ce_recv_process_cb(struct ath11k_ce_pipe *pipe) +{ + struct ath11k_base *ab = pipe->ab; + struct sk_buff *skb; + struct sk_buff_head list; + unsigned int nbytes, max_nbytes; + int ret; + + __skb_queue_head_init(&list); + while (ath11k_ce_completed_recv_next(pipe, &skb, &nbytes) == 0) { + max_nbytes = skb->len + skb_tailroom(skb); + dma_unmap_single(ab->dev, ATH11K_SKB_RXCB(skb)->paddr, + max_nbytes, DMA_FROM_DEVICE); + + if (unlikely(max_nbytes < nbytes)) { + ath11k_warn(ab, "rxed more than expected (nbytes %d, max %d)", + nbytes, max_nbytes); + dev_kfree_skb_any(skb); + continue; + } + + skb_put(skb, nbytes); + __skb_queue_tail(&list, skb); + } + + while ((skb = __skb_dequeue(&list))) { + ath11k_dbg(ab, ATH11K_DBG_AHB, "rx ce pipe %d len %d\n", + pipe->pipe_num, skb->len); + pipe->recv_cb(ab, skb); + } + + ret = ath11k_ce_rx_post_pipe(pipe); + if (ret && ret != -ENOSPC) { + ath11k_warn(ab, "failed to post rx buf to pipe: %d err: %d\n", + pipe->pipe_num, ret); + mod_timer(&ab->rx_replenish_retry, + jiffies + ATH11K_CE_RX_POST_RETRY_JIFFIES); + } +} + +static struct sk_buff *ath11k_ce_completed_send_next(struct ath11k_ce_pipe *pipe) +{ + struct ath11k_base *ab = pipe->ab; + struct hal_srng *srng; + unsigned int sw_index; + unsigned int nentries_mask; + struct sk_buff *skb; + u32 *desc; + + spin_lock_bh(&ab->ce.ce_lock); + + sw_index = pipe->src_ring->sw_index; + nentries_mask = pipe->src_ring->nentries_mask; + + srng = &ab->hal.srng_list[pipe->src_ring->hal_ring_id]; + + spin_lock_bh(&srng->lock); + + ath11k_hal_srng_access_begin(ab, srng); + + desc = ath11k_hal_srng_src_reap_next(ab, srng); + if (!desc) { + skb = ERR_PTR(-EIO); + goto err_unlock; + } + + skb = pipe->src_ring->skb[sw_index]; + + pipe->src_ring->skb[sw_index] = NULL; + + sw_index = CE_RING_IDX_INCR(nentries_mask, sw_index); + pipe->src_ring->sw_index = sw_index; + +err_unlock: + spin_unlock_bh(&srng->lock); + + spin_unlock_bh(&ab->ce.ce_lock); + + return skb; +} + +static void ath11k_ce_send_done_cb(struct ath11k_ce_pipe *pipe) +{ + struct ath11k_base *ab = pipe->ab; + struct sk_buff *skb; + + while (!IS_ERR(skb = ath11k_ce_completed_send_next(pipe))) { + if (!skb) + continue; + + dma_unmap_single(ab->dev, ATH11K_SKB_CB(skb)->paddr, skb->len, + DMA_TO_DEVICE); + dev_kfree_skb_any(skb); + } +} + +static int ath11k_ce_init_ring(struct ath11k_base *ab, + struct ath11k_ce_ring *ce_ring, + int ce_id, enum hal_ring_type type) +{ + struct hal_srng_params params = { 0 }; + int ret; + + params.ring_base_paddr = ce_ring->base_addr_ce_space; + params.ring_base_vaddr = ce_ring->base_addr_owner_space; + params.num_entries = ce_ring->nentries; + + switch (type) { + case HAL_CE_SRC: + if (!(CE_ATTR_DIS_INTR & host_ce_config_wlan[ce_id].flags)) + params.intr_batch_cntr_thres_entries = 1; + break; + case HAL_CE_DST: + params.max_buffer_len = host_ce_config_wlan[ce_id].src_sz_max; + if (!(host_ce_config_wlan[ce_id].flags & CE_ATTR_DIS_INTR)) { + params.intr_timer_thres_us = 1024; + params.flags |= HAL_SRNG_FLAGS_LOW_THRESH_INTR_EN; + params.low_threshold = ce_ring->nentries - 3; + } + break; + case HAL_CE_DST_STATUS: + if (!(host_ce_config_wlan[ce_id].flags & CE_ATTR_DIS_INTR)) { + params.intr_batch_cntr_thres_entries = 1; + params.intr_timer_thres_us = 0x1000; + } + break; + default: + ath11k_warn(ab, "Invalid CE ring type %d\n", type); + return -EINVAL; + } + + /* TODO: Init other params needed by HAL to init the ring */ + + ret = ath11k_hal_srng_setup(ab, type, ce_id, 0, ¶ms); + if (ret < 0) { + ath11k_warn(ab, "failed to setup srng: %d ring_id %d\n", + ret, ce_id); + return ret; + } + ce_ring->hal_ring_id = ret; + + return 0; +} + +static struct ath11k_ce_ring * +ath11k_ce_alloc_ring(struct ath11k_base *ab, int nentries, int desc_sz) +{ + struct ath11k_ce_ring *ce_ring; + dma_addr_t base_addr; + + ce_ring = kzalloc(struct_size(ce_ring, skb, nentries), GFP_KERNEL); + if (ce_ring == NULL) + return ERR_PTR(-ENOMEM); + + ce_ring->nentries = nentries; + ce_ring->nentries_mask = nentries - 1; + + /* Legacy platforms that do not support cache + * coherent DMA are unsupported + */ + ce_ring->base_addr_owner_space_unaligned = + dma_alloc_coherent(ab->dev, + nentries * desc_sz + CE_DESC_RING_ALIGN, + &base_addr, GFP_KERNEL); + if (!ce_ring->base_addr_owner_space_unaligned) { + kfree(ce_ring); + return ERR_PTR(-ENOMEM); + } + + ce_ring->base_addr_ce_space_unaligned = base_addr; + + ce_ring->base_addr_owner_space = PTR_ALIGN( + ce_ring->base_addr_owner_space_unaligned, + CE_DESC_RING_ALIGN); + ce_ring->base_addr_ce_space = ALIGN( + ce_ring->base_addr_ce_space_unaligned, + CE_DESC_RING_ALIGN); + + return ce_ring; +} + +static int ath11k_ce_alloc_pipe(struct ath11k_base *ab, int ce_id) +{ + struct ath11k_ce_pipe *pipe = &ab->ce.ce_pipe[ce_id]; + const struct ce_attr *attr = &host_ce_config_wlan[ce_id]; + int nentries; + int desc_sz; + + pipe->attr_flags = attr->flags; + + if (attr->src_nentries) { + pipe->send_cb = ath11k_ce_send_done_cb; + nentries = roundup_pow_of_two(attr->src_nentries); + desc_sz = ath11k_hal_ce_get_desc_size(HAL_CE_DESC_SRC); + pipe->src_ring = ath11k_ce_alloc_ring(ab, nentries, desc_sz); + if (!pipe->src_ring) + return -ENOMEM; + } + + if (attr->dest_nentries) { + pipe->recv_cb = attr->recv_cb; + nentries = roundup_pow_of_two(attr->dest_nentries); + desc_sz = ath11k_hal_ce_get_desc_size(HAL_CE_DESC_DST); + pipe->dest_ring = ath11k_ce_alloc_ring(ab, nentries, desc_sz); + + if (!pipe->dest_ring) + return -ENOMEM; + + desc_sz = ath11k_hal_ce_get_desc_size(HAL_CE_DESC_DST_STATUS); + pipe->status_ring = ath11k_ce_alloc_ring(ab, nentries, desc_sz); + if (!pipe->status_ring) + return -ENOMEM; + } + + return 0; +} + +void ath11k_ce_per_engine_service(struct ath11k_base *ab, u16 ce_id) +{ + struct ath11k_ce_pipe *pipe = &ab->ce.ce_pipe[ce_id]; + + if (pipe->send_cb) + pipe->send_cb(pipe); + + if (pipe->recv_cb) + ath11k_ce_recv_process_cb(pipe); +} + +void ath11k_ce_poll_send_completed(struct ath11k_base *ab, u8 pipe_id) +{ + struct ath11k_ce_pipe *pipe = &ab->ce.ce_pipe[pipe_id]; + + if ((pipe->attr_flags & CE_ATTR_DIS_INTR) && pipe->send_cb) + pipe->send_cb(pipe); +} + +int ath11k_ce_send(struct ath11k_base *ab, struct sk_buff *skb, u8 pipe_id, + u16 transfer_id) +{ + struct ath11k_ce_pipe *pipe = &ab->ce.ce_pipe[pipe_id]; + struct hal_srng *srng; + u32 *desc; + unsigned int write_index, sw_index; + unsigned int nentries_mask; + int ret = 0; + u8 byte_swap_data = 0; + int num_used; + + /* Check if some entries could be regained by handling tx completion if + * the CE has interrupts disabled and the used entries is more than the + * defined usage threshold. + */ + if (pipe->attr_flags & CE_ATTR_DIS_INTR) { + spin_lock_bh(&ab->ce.ce_lock); + write_index = pipe->src_ring->write_index; + + sw_index = pipe->src_ring->sw_index; + + if (write_index >= sw_index) + num_used = write_index - sw_index; + else + num_used = pipe->src_ring->nentries - sw_index + + write_index; + + spin_unlock_bh(&ab->ce.ce_lock); + + if (num_used > ATH11K_CE_USAGE_THRESHOLD) + ath11k_ce_poll_send_completed(ab, pipe->pipe_num); + } + + if (test_bit(ATH11K_FLAG_CRASH_FLUSH, &ab->dev_flags)) + return -ESHUTDOWN; + + spin_lock_bh(&ab->ce.ce_lock); + + write_index = pipe->src_ring->write_index; + nentries_mask = pipe->src_ring->nentries_mask; + + srng = &ab->hal.srng_list[pipe->src_ring->hal_ring_id]; + + spin_lock_bh(&srng->lock); + + ath11k_hal_srng_access_begin(ab, srng); + + if (unlikely(ath11k_hal_srng_src_num_free(ab, srng, false) < 1)) { + ath11k_hal_srng_access_end(ab, srng); + ret = -ENOBUFS; + goto err_unlock; + } + + desc = ath11k_hal_srng_src_get_next_reaped(ab, srng); + if (!desc) { + ath11k_hal_srng_access_end(ab, srng); + ret = -ENOBUFS; + goto err_unlock; + } + + if (pipe->attr_flags & CE_ATTR_BYTE_SWAP_DATA) + byte_swap_data = 1; + + ath11k_hal_ce_src_set_desc(desc, ATH11K_SKB_CB(skb)->paddr, + skb->len, transfer_id, byte_swap_data); + + pipe->src_ring->skb[write_index] = skb; + pipe->src_ring->write_index = CE_RING_IDX_INCR(nentries_mask, + write_index); + + ath11k_hal_srng_access_end(ab, srng); + + spin_unlock_bh(&srng->lock); + + spin_unlock_bh(&ab->ce.ce_lock); + + return 0; + +err_unlock: + spin_unlock_bh(&srng->lock); + + spin_unlock_bh(&ab->ce.ce_lock); + + return ret; +} + +static void ath11k_ce_rx_pipe_cleanup(struct ath11k_ce_pipe *pipe) +{ + struct ath11k_base *ab = pipe->ab; + struct ath11k_ce_ring *ring = pipe->dest_ring; + struct sk_buff *skb; + int i; + + if (!(ring && pipe->buf_sz)) + return; + + for (i = 0; i < ring->nentries; i++) { + skb = ring->skb[i]; + if (!skb) + continue; + + ring->skb[i] = NULL; + dma_unmap_single(ab->dev, ATH11K_SKB_RXCB(skb)->paddr, + skb->len + skb_tailroom(skb), DMA_FROM_DEVICE); + dev_kfree_skb_any(skb); + } +} + +void ath11k_ce_cleanup_pipes(struct ath11k_base *ab) +{ + struct ath11k_ce_pipe *pipe; + int pipe_num; + + for (pipe_num = 0; pipe_num < CE_COUNT; pipe_num++) { + pipe = &ab->ce.ce_pipe[pipe_num]; + ath11k_ce_rx_pipe_cleanup(pipe); + + /* Cleanup any src CE's which have interrupts disabled */ + ath11k_ce_poll_send_completed(ab, pipe_num); + + /* NOTE: Should we also clean up tx buffer in all pipes? */ + } +} + +void ath11k_ce_rx_post_buf(struct ath11k_base *ab) +{ + struct ath11k_ce_pipe *pipe; + int i; + int ret; + + for (i = 0; i < CE_COUNT; i++) { + pipe = &ab->ce.ce_pipe[i]; + ret = ath11k_ce_rx_post_pipe(pipe); + if (ret) { + if (ret == -ENOSPC) + continue; + + ath11k_warn(ab, "failed to post rx buf to pipe: %d err: %d\n", + i, ret); + mod_timer(&ab->rx_replenish_retry, + jiffies + ATH11K_CE_RX_POST_RETRY_JIFFIES); + + return; + } + } +} + +void ath11k_ce_rx_replenish_retry(struct timer_list *t) +{ + struct ath11k_base *ab = from_timer(ab, t, rx_replenish_retry); + + ath11k_ce_rx_post_buf(ab); +} + +int ath11k_ce_init_pipes(struct ath11k_base *ab) +{ + struct ath11k_ce_pipe *pipe; + int i; + int ret; + + for (i = 0; i < CE_COUNT; i++) { + pipe = &ab->ce.ce_pipe[i]; + + if (pipe->src_ring) { + ret = ath11k_ce_init_ring(ab, pipe->src_ring, i, + HAL_CE_SRC); + if (ret) { + ath11k_warn(ab, "failed to init src ring: %d\n", + ret); + /* Should we clear any partial init */ + return ret; + } + + pipe->src_ring->write_index = 0; + pipe->src_ring->sw_index = 0; + } + + if (pipe->dest_ring) { + ret = ath11k_ce_init_ring(ab, pipe->dest_ring, i, + HAL_CE_DST); + if (ret) { + ath11k_warn(ab, "failed to init dest ring: %d\n", + ret); + /* Should we clear any partial init */ + return ret; + } + + pipe->rx_buf_needed = pipe->dest_ring->nentries ? + pipe->dest_ring->nentries - 2 : 0; + + pipe->dest_ring->write_index = 0; + pipe->dest_ring->sw_index = 0; + } + + if (pipe->status_ring) { + ret = ath11k_ce_init_ring(ab, pipe->status_ring, i, + HAL_CE_DST_STATUS); + if (ret) { + ath11k_warn(ab, "failed to init dest status ing: %d\n", + ret); + /* Should we clear any partial init */ + return ret; + } + + pipe->status_ring->write_index = 0; + pipe->status_ring->sw_index = 0; + } + } + + return 0; +} + +void ath11k_ce_free_pipes(struct ath11k_base *ab) +{ + struct ath11k_ce_pipe *pipe; + int desc_sz; + int i; + + for (i = 0; i < CE_COUNT; i++) { + pipe = &ab->ce.ce_pipe[i]; + + if (pipe->src_ring) { + desc_sz = ath11k_hal_ce_get_desc_size(HAL_CE_DESC_SRC); + dma_free_coherent(ab->dev, + pipe->src_ring->nentries * desc_sz + + CE_DESC_RING_ALIGN, + pipe->src_ring->base_addr_owner_space, + pipe->src_ring->base_addr_ce_space); + kfree(pipe->src_ring); + pipe->src_ring = NULL; + } + + if (pipe->dest_ring) { + desc_sz = ath11k_hal_ce_get_desc_size(HAL_CE_DESC_DST); + dma_free_coherent(ab->dev, + pipe->dest_ring->nentries * desc_sz + + CE_DESC_RING_ALIGN, + pipe->dest_ring->base_addr_owner_space, + pipe->dest_ring->base_addr_ce_space); + kfree(pipe->dest_ring); + pipe->dest_ring = NULL; + } + + if (pipe->status_ring) { + desc_sz = + ath11k_hal_ce_get_desc_size(HAL_CE_DESC_DST_STATUS); + dma_free_coherent(ab->dev, + pipe->status_ring->nentries * desc_sz + + CE_DESC_RING_ALIGN, + pipe->status_ring->base_addr_owner_space, + pipe->status_ring->base_addr_ce_space); + kfree(pipe->status_ring); + pipe->status_ring = NULL; + } + } +} + +int ath11k_ce_alloc_pipes(struct ath11k_base *ab) +{ + struct ath11k_ce_pipe *pipe; + int i; + int ret; + const struct ce_attr *attr; + + spin_lock_init(&ab->ce.ce_lock); + + for (i = 0; i < CE_COUNT; i++) { + attr = &host_ce_config_wlan[i]; + pipe = &ab->ce.ce_pipe[i]; + pipe->pipe_num = i; + pipe->ab = ab; + pipe->buf_sz = attr->src_sz_max; + + ret = ath11k_ce_alloc_pipe(ab, i); + if (ret) { + /* Free any parial successful allocation */ + ath11k_ce_free_pipes(ab); + return ret; + } + } + + return 0; +} + +/* For Big Endian Host, Copy Engine byte_swap is enabled + * When Copy Engine does byte_swap, need to byte swap again for the + * Host to get/put buffer content in the correct byte order + */ +void ath11k_ce_byte_swap(void *mem, u32 len) +{ + int i; + + if (IS_ENABLED(CONFIG_CPU_BIG_ENDIAN)) { + if (!mem) + return; + + for (i = 0; i < (len / 4); i++) { + *(u32 *)mem = swab32(*(u32 *)mem); + mem += 4; + } + } +} + +int ath11k_ce_get_attr_flags(int ce_id) +{ + if (ce_id >= CE_COUNT) + return -EINVAL; + + return host_ce_config_wlan[ce_id].flags; +} diff --git a/drivers/net/wireless/ath/ath11k/ce.h b/drivers/net/wireless/ath/ath11k/ce.h new file mode 100644 index 0000000000000..6174eac84410a --- /dev/null +++ b/drivers/net/wireless/ath/ath11k/ce.h @@ -0,0 +1,178 @@ +/* SPDX-License-Identifier: BSD-3-Clause-Clear */ +/* + * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. + */ + +#ifndef ATH11K_CE_H +#define ATH11K_CE_H + +#define CE_COUNT 12 + +/* Byte swap data words */ +#define CE_ATTR_BYTE_SWAP_DATA 2 + +/* no interrupt on copy completion */ +#define CE_ATTR_DIS_INTR 8 + +/* Host software's Copy Engine configuration. */ +#ifdef __BIG_ENDIAN +#define CE_ATTR_FLAGS CE_ATTR_BYTE_SWAP_DATA +#else +#define CE_ATTR_FLAGS 0 +#endif + +/* Threshold to poll for tx completion in case of Interrupt disabled CE's */ +#define ATH11K_CE_USAGE_THRESHOLD 32 + +void ath11k_ce_byte_swap(void *mem, u32 len); + +/* + * Directions for interconnect pipe configuration. + * These definitions may be used during configuration and are shared + * between Host and Target. + * + * Pipe Directions are relative to the Host, so PIPEDIR_IN means + * "coming IN over air through Target to Host" as with a WiFi Rx operation. + * Conversely, PIPEDIR_OUT means "going OUT from Host through Target over air" + * as with a WiFi Tx operation. This is somewhat awkward for the "middle-man" + * Target since things that are "PIPEDIR_OUT" are coming IN to the Target + * over the interconnect. + */ +#define PIPEDIR_NONE 0 +#define PIPEDIR_IN 1 /* Target-->Host, WiFi Rx direction */ +#define PIPEDIR_OUT 2 /* Host->Target, WiFi Tx direction */ +#define PIPEDIR_INOUT 3 /* bidirectional */ +#define PIPEDIR_INOUT_H2H 4 /* bidirectional, host to host */ + +/* CE address/mask */ +#define CE_HOST_IE_ADDRESS 0x00A1803C +#define CE_HOST_IE_2_ADDRESS 0x00A18040 +#define CE_HOST_IE_3_ADDRESS CE_HOST_IE_ADDRESS + +#define CE_HOST_IE_3_SHIFT 0xC + +#define CE_RING_IDX_INCR(nentries_mask, idx) (((idx) + 1) & (nentries_mask)) + +#define ATH11K_CE_RX_POST_RETRY_JIFFIES 50 + +struct ath11k_base; + +/* Establish a mapping between a service/direction and a pipe. */ +struct service_to_pipe { + __le32 service_id; + __le32 pipedir; + __le32 pipenum; +}; + +/* + * Configuration information for a Copy Engine pipe. + * Passed from Host to Target during startup (one per CE). + * + * NOTE: Structure is shared between Host software and Target firmware! + */ +struct ce_pipe_config { + __le32 pipenum; + __le32 pipedir; + __le32 nentries; + __le32 nbytes_max; + __le32 flags; + __le32 reserved; +}; + +struct ce_attr { + /* CE_ATTR_* values */ + unsigned int flags; + + /* #entries in source ring - Must be a power of 2 */ + unsigned int src_nentries; + + /* + * Max source send size for this CE. + * This is also the minimum size of a destination buffer. + */ + unsigned int src_sz_max; + + /* #entries in destination ring - Must be a power of 2 */ + unsigned int dest_nentries; + + void (*recv_cb)(struct ath11k_base *, struct sk_buff *); +}; + +#define CE_DESC_RING_ALIGN 8 + +struct ath11k_ce_ring { + /* Number of entries in this ring; must be power of 2 */ + unsigned int nentries; + unsigned int nentries_mask; + + /* For dest ring, this is the next index to be processed + * by software after it was/is received into. + * + * For src ring, this is the last descriptor that was sent + * and completion processed by software. + * + * Regardless of src or dest ring, this is an invariant + * (modulo ring size): + * write index >= read index >= sw_index + */ + unsigned int sw_index; + /* cached copy */ + unsigned int write_index; + + /* Start of DMA-coherent area reserved for descriptors */ + /* Host address space */ + void *base_addr_owner_space_unaligned; + /* CE address space */ + u32 base_addr_ce_space_unaligned; + + /* Actual start of descriptors. + * Aligned to descriptor-size boundary. + * Points into reserved DMA-coherent area, above. + */ + /* Host address space */ + void *base_addr_owner_space; + + /* CE address space */ + u32 base_addr_ce_space; + + /* HAL ring id */ + u32 hal_ring_id; + + /* keep last */ + struct sk_buff *skb[0]; +}; + +struct ath11k_ce_pipe { + struct ath11k_base *ab; + u16 pipe_num; + unsigned int attr_flags; + unsigned int buf_sz; + unsigned int rx_buf_needed; + + void (*send_cb)(struct ath11k_ce_pipe *); + void (*recv_cb)(struct ath11k_base *, struct sk_buff *); + + struct tasklet_struct intr_tq; + struct ath11k_ce_ring *src_ring; + struct ath11k_ce_ring *dest_ring; + struct ath11k_ce_ring *status_ring; +}; + +struct ath11k_ce { + struct ath11k_ce_pipe ce_pipe[CE_COUNT]; + /* Protects rings of all ce pipes */ + spinlock_t ce_lock; +}; + +void ath11k_ce_cleanup_pipes(struct ath11k_base *ab); +void ath11k_ce_rx_replenish_retry(struct timer_list *t); +void ath11k_ce_per_engine_service(struct ath11k_base *ab, u16 ce_id); +int ath11k_ce_send(struct ath11k_base *ab, struct sk_buff *skb, u8 pipe_id, + u16 transfer_id); +void ath11k_ce_rx_post_buf(struct ath11k_base *ab); +int ath11k_ce_init_pipes(struct ath11k_base *ab); +int ath11k_ce_alloc_pipes(struct ath11k_base *ab); +void ath11k_ce_free_pipes(struct ath11k_base *ab); +int ath11k_ce_get_attr_flags(int ce_id); +void ath11k_ce_poll_send_completed(struct ath11k_base *ab, u8 pipe_id); +#endif diff --git a/drivers/net/wireless/ath/ath11k/core.c b/drivers/net/wireless/ath/ath11k/core.c new file mode 100644 index 0000000000000..b0c71b5879b1a --- /dev/null +++ b/drivers/net/wireless/ath/ath11k/core.c @@ -0,0 +1,772 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* + * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. + */ + +#include +#include +#include +#include +#include "ahb.h" +#include "core.h" +#include "dp_tx.h" +#include "debug.h" + +unsigned int ath11k_debug_mask; +module_param_named(debug_mask, ath11k_debug_mask, uint, 0644); +MODULE_PARM_DESC(debug_mask, "Debugging mask"); + +static const struct ath11k_hw_params ath11k_hw_params = { + .name = "ipq8074", + .fw = { + .dir = IPQ8074_FW_DIR, + .board_size = IPQ8074_MAX_BOARD_DATA_SZ, + .cal_size = IPQ8074_MAX_CAL_DATA_SZ, + }, +}; + +/* Map from pdev index to hw mac index */ +u8 ath11k_core_get_hw_mac_id(struct ath11k_base *ab, int pdev_idx) +{ + switch (pdev_idx) { + case 0: + return 0; + case 1: + return 2; + case 2: + return 1; + default: + ath11k_warn(ab, "Invalid pdev idx %d\n", pdev_idx); + return ATH11K_INVALID_HW_MAC_ID; + } +} + +static int ath11k_core_create_board_name(struct ath11k_base *ab, char *name, + size_t name_len) +{ + /* Note: bus is fixed to ahb. When other bus type supported, + * make it to dynamic. + */ + scnprintf(name, name_len, + "bus=ahb,qmi-chip-id=%d,qmi-board-id=%d", + ab->qmi.target.chip_id, + ab->qmi.target.board_id); + + ath11k_dbg(ab, ATH11K_DBG_BOOT, "boot using board name '%s'\n", name); + + return 0; +} + +static const struct firmware *ath11k_fetch_fw_file(struct ath11k_base *ab, + const char *dir, + const char *file) +{ + char filename[100]; + const struct firmware *fw; + int ret; + + if (file == NULL) + return ERR_PTR(-ENOENT); + + if (dir == NULL) + dir = "."; + + snprintf(filename, sizeof(filename), "%s/%s", dir, file); + ret = firmware_request_nowarn(&fw, filename, ab->dev); + ath11k_dbg(ab, ATH11K_DBG_BOOT, "boot fw request '%s': %d\n", + filename, ret); + + if (ret) + return ERR_PTR(ret); + ath11k_warn(ab, "Downloading BDF: %s, size: %zu\n", + filename, fw->size); + + return fw; +} + +void ath11k_core_free_bdf(struct ath11k_base *ab, struct ath11k_board_data *bd) +{ + if (!IS_ERR(bd->fw)) + release_firmware(bd->fw); + + memset(bd, 0, sizeof(*bd)); +} + +static int ath11k_core_parse_bd_ie_board(struct ath11k_base *ab, + struct ath11k_board_data *bd, + const void *buf, size_t buf_len, + const char *boardname, + int bd_ie_type) +{ + const struct ath11k_fw_ie *hdr; + bool name_match_found; + int ret, board_ie_id; + size_t board_ie_len; + const void *board_ie_data; + + name_match_found = false; + + /* go through ATH11K_BD_IE_BOARD_ elements */ + while (buf_len > sizeof(struct ath11k_fw_ie)) { + hdr = buf; + board_ie_id = le32_to_cpu(hdr->id); + board_ie_len = le32_to_cpu(hdr->len); + board_ie_data = hdr->data; + + buf_len -= sizeof(*hdr); + buf += sizeof(*hdr); + + if (buf_len < ALIGN(board_ie_len, 4)) { + ath11k_err(ab, "invalid ATH11K_BD_IE_BOARD length: %zu < %zu\n", + buf_len, ALIGN(board_ie_len, 4)); + ret = -EINVAL; + goto out; + } + + switch (board_ie_id) { + case ATH11K_BD_IE_BOARD_NAME: + ath11k_dbg_dump(ab, ATH11K_DBG_BOOT, "board name", "", + board_ie_data, board_ie_len); + + if (board_ie_len != strlen(boardname)) + break; + + ret = memcmp(board_ie_data, boardname, strlen(boardname)); + if (ret) + break; + + name_match_found = true; + ath11k_dbg(ab, ATH11K_DBG_BOOT, + "boot found match for name '%s'", + boardname); + break; + case ATH11K_BD_IE_BOARD_DATA: + if (!name_match_found) + /* no match found */ + break; + + ath11k_dbg(ab, ATH11K_DBG_BOOT, + "boot found board data for '%s'", boardname); + + bd->data = board_ie_data; + bd->len = board_ie_len; + + ret = 0; + goto out; + default: + ath11k_warn(ab, "unknown ATH11K_BD_IE_BOARD found: %d\n", + board_ie_id); + break; + } + + /* jump over the padding */ + board_ie_len = ALIGN(board_ie_len, 4); + + buf_len -= board_ie_len; + buf += board_ie_len; + } + + /* no match found */ + ret = -ENOENT; + +out: + return ret; +} + +static int ath11k_core_fetch_board_data_api_n(struct ath11k_base *ab, + struct ath11k_board_data *bd, + const char *boardname) +{ + size_t len, magic_len; + const u8 *data; + char *filename = ATH11K_BOARD_API2_FILE; + size_t ie_len; + struct ath11k_fw_ie *hdr; + int ret, ie_id; + + if (!bd->fw) + bd->fw = ath11k_fetch_fw_file(ab, + ab->hw_params.fw.dir, + filename); + if (IS_ERR(bd->fw)) + return PTR_ERR(bd->fw); + + data = bd->fw->data; + len = bd->fw->size; + + /* magic has extra null byte padded */ + magic_len = strlen(ATH11K_BOARD_MAGIC) + 1; + if (len < magic_len) { + ath11k_err(ab, "failed to find magic value in %s/%s, file too short: %zu\n", + ab->hw_params.fw.dir, filename, len); + ret = -EINVAL; + goto err; + } + + if (memcmp(data, ATH11K_BOARD_MAGIC, magic_len)) { + ath11k_err(ab, "found invalid board magic\n"); + ret = -EINVAL; + goto err; + } + + /* magic is padded to 4 bytes */ + magic_len = ALIGN(magic_len, 4); + if (len < magic_len) { + ath11k_err(ab, "failed: %s/%s too small to contain board data, len: %zu\n", + ab->hw_params.fw.dir, filename, len); + ret = -EINVAL; + goto err; + } + + data += magic_len; + len -= magic_len; + + while (len > sizeof(struct ath11k_fw_ie)) { + hdr = (struct ath11k_fw_ie *)data; + ie_id = le32_to_cpu(hdr->id); + ie_len = le32_to_cpu(hdr->len); + + len -= sizeof(*hdr); + data = hdr->data; + + if (len < ALIGN(ie_len, 4)) { + ath11k_err(ab, "invalid length for board ie_id %d ie_len %zu len %zu\n", + ie_id, ie_len, len); + return -EINVAL; + } + + switch (ie_id) { + case ATH11K_BD_IE_BOARD: + ret = ath11k_core_parse_bd_ie_board(ab, bd, data, + ie_len, + boardname, + ATH11K_BD_IE_BOARD); + if (ret == -ENOENT) + /* no match found, continue */ + break; + else if (ret) + /* there was an error, bail out */ + goto err; + /* either found or error, so stop searching */ + goto out; + } + + /* jump over the padding */ + ie_len = ALIGN(ie_len, 4); + + len -= ie_len; + data += ie_len; + } + +out: + if (!bd->data || !bd->len) { + ath11k_err(ab, + "failed to fetch board data for %s from %s/%s\n", + boardname, ab->hw_params.fw.dir, filename); + ret = -ENODATA; + goto err; + } + + return 0; + +err: + ath11k_core_free_bdf(ab, bd); + return ret; +} + +static int ath11k_core_fetch_board_data_api_1(struct ath11k_base *ab, + struct ath11k_board_data *bd) +{ + bd->fw = ath11k_fetch_fw_file(ab, + ab->hw_params.fw.dir, + ATH11K_DEFAULT_BOARD_FILE); + if (IS_ERR(bd->fw)) + return PTR_ERR(bd->fw); + + bd->data = bd->fw->data; + bd->len = bd->fw->size; + + return 0; +} + +#define BOARD_NAME_SIZE 100 +int ath11k_core_fetch_bdf(struct ath11k_base *ab, struct ath11k_board_data *bd) +{ + char boardname[BOARD_NAME_SIZE]; + int ret; + + ret = ath11k_core_create_board_name(ab, boardname, BOARD_NAME_SIZE); + if (ret) { + ath11k_err(ab, "failed to create board name: %d", ret); + return ret; + } + + ab->bd_api = 2; + ret = ath11k_core_fetch_board_data_api_n(ab, bd, boardname); + if (!ret) + goto success; + + ab->bd_api = 1; + ret = ath11k_core_fetch_board_data_api_1(ab, bd); + if (ret) { + ath11k_err(ab, "failed to fetch board-2.bin or board.bin from %s\n", + ab->hw_params.fw.dir); + return ret; + } + +success: + ath11k_dbg(ab, ATH11K_DBG_BOOT, "using board api %d\n", ab->bd_api); + return 0; +} + +static void ath11k_core_stop(struct ath11k_base *ab) +{ + if (!test_bit(ATH11K_FLAG_CRASH_FLUSH, &ab->dev_flags)) + ath11k_qmi_firmware_stop(ab); + ath11k_ahb_stop(ab); + ath11k_wmi_detach(ab); + + /* De-Init of components as needed */ +} + +static int ath11k_core_soc_create(struct ath11k_base *ab) +{ + int ret; + + ret = ath11k_qmi_init_service(ab); + if (ret) { + ath11k_err(ab, "failed to initialize qmi :%d\n", ret); + return ret; + } + + ret = ath11k_debug_soc_create(ab); + if (ret) { + ath11k_err(ab, "failed to create ath11k debugfs\n"); + goto err_qmi_deinit; + } + + ret = ath11k_ahb_power_up(ab); + if (ret) { + ath11k_err(ab, "failed to power up :%d\n", ret); + goto err_debugfs_reg; + } + + return 0; + +err_debugfs_reg: + ath11k_debug_soc_destroy(ab); +err_qmi_deinit: + ath11k_qmi_deinit_service(ab); + return ret; +} + +static void ath11k_core_soc_destroy(struct ath11k_base *ab) +{ + ath11k_debug_soc_destroy(ab); + ath11k_dp_free(ab); + ath11k_reg_free(ab); + ath11k_qmi_deinit_service(ab); +} + +static int ath11k_core_pdev_create(struct ath11k_base *ab) +{ + int ret; + + ret = ath11k_debug_pdev_create(ab); + if (ret) { + ath11k_err(ab, "failed to create core pdev debugfs: %d\n", ret); + return ret; + } + + ret = ath11k_mac_create(ab); + if (ret) { + ath11k_err(ab, "failed to create new hw device with mac80211 :%d\n", + ret); + goto err_pdev_debug; + } + + ret = ath11k_dp_pdev_alloc(ab); + if (ret) { + ath11k_err(ab, "failed to attach DP pdev: %d\n", ret); + goto err_mac_destroy; + } + + return 0; + +err_mac_destroy: + ath11k_mac_destroy(ab); + +err_pdev_debug: + ath11k_debug_pdev_destroy(ab); + + return ret; +} + +static void ath11k_core_pdev_destroy(struct ath11k_base *ab) +{ + ath11k_mac_unregister(ab); + ath11k_ahb_ext_irq_disable(ab); + ath11k_dp_pdev_free(ab); + ath11k_debug_pdev_destroy(ab); +} + +static int ath11k_core_start(struct ath11k_base *ab, + enum ath11k_firmware_mode mode) +{ + int ret; + + ret = ath11k_qmi_firmware_start(ab, mode); + if (ret) { + ath11k_err(ab, "failed to attach wmi: %d\n", ret); + return ret; + } + + ret = ath11k_wmi_attach(ab); + if (ret) { + ath11k_err(ab, "failed to attach wmi: %d\n", ret); + goto err_firmware_stop; + } + + ret = ath11k_htc_init(ab); + if (ret) { + ath11k_err(ab, "failed to init htc: %d\n", ret); + goto err_wmi_detach; + } + + ret = ath11k_ahb_start(ab); + if (ret) { + ath11k_err(ab, "failed to start HIF: %d\n", ret); + goto err_wmi_detach; + } + + ret = ath11k_htc_wait_target(&ab->htc); + if (ret) { + ath11k_err(ab, "failed to connect to HTC: %d\n", ret); + goto err_hif_stop; + } + + ret = ath11k_dp_htt_connect(&ab->dp); + if (ret) { + ath11k_err(ab, "failed to connect to HTT: %d\n", ret); + goto err_hif_stop; + } + + ret = ath11k_wmi_connect(ab); + if (ret) { + ath11k_err(ab, "failed to connect wmi: %d\n", ret); + goto err_hif_stop; + } + + ret = ath11k_htc_start(&ab->htc); + if (ret) { + ath11k_err(ab, "failed to start HTC: %d\n", ret); + goto err_hif_stop; + } + + ret = ath11k_wmi_wait_for_service_ready(ab); + if (ret) { + ath11k_err(ab, "failed to receive wmi service ready event: %d\n", + ret); + goto err_hif_stop; + } + + ret = ath11k_wmi_cmd_init(ab); + if (ret) { + ath11k_err(ab, "failed to send wmi init cmd: %d\n", ret); + goto err_hif_stop; + } + + ret = ath11k_wmi_wait_for_unified_ready(ab); + if (ret) { + ath11k_err(ab, "failed to receive wmi unified ready event: %d\n", + ret); + goto err_hif_stop; + } + + ret = ath11k_dp_tx_htt_h2t_ver_req_msg(ab); + if (ret) { + ath11k_err(ab, "failed to send htt version request message: %d\n", + ret); + goto err_hif_stop; + } + + return 0; + +err_hif_stop: + ath11k_ahb_stop(ab); +err_wmi_detach: + ath11k_wmi_detach(ab); +err_firmware_stop: + ath11k_qmi_firmware_stop(ab); + + return ret; +} + +int ath11k_core_qmi_firmware_ready(struct ath11k_base *ab) +{ + int ret; + + ret = ath11k_ce_init_pipes(ab); + if (ret) { + ath11k_err(ab, "failed to initialize CE: %d\n", ret); + return ret; + } + + ret = ath11k_dp_alloc(ab); + if (ret) { + ath11k_err(ab, "failed to init DP: %d\n", ret); + return ret; + } + + mutex_lock(&ab->core_lock); + ret = ath11k_core_start(ab, ATH11K_FIRMWARE_MODE_NORMAL); + if (ret) { + ath11k_err(ab, "failed to start core: %d\n", ret); + goto err_dp_free; + } + + ret = ath11k_core_pdev_create(ab); + if (ret) { + ath11k_err(ab, "failed to create pdev core: %d\n", ret); + goto err_core_stop; + } + ath11k_ahb_ext_irq_enable(ab); + mutex_unlock(&ab->core_lock); + + return 0; + +err_core_stop: + ath11k_core_stop(ab); +err_dp_free: + ath11k_dp_free(ab); + return ret; +} + +static int ath11k_core_reconfigure_on_crash(struct ath11k_base *ab) +{ + int ret; + + mutex_lock(&ab->core_lock); + ath11k_ahb_ext_irq_disable(ab); + ath11k_dp_pdev_free(ab); + ath11k_ahb_stop(ab); + ath11k_wmi_detach(ab); + mutex_unlock(&ab->core_lock); + + ath11k_dp_free(ab); + ath11k_hal_srng_deinit(ab); + + ab->free_vdev_map = (1LL << (ab->num_radios * TARGET_NUM_VDEVS)) - 1; + + ret = ath11k_hal_srng_init(ab); + if (ret) + return ret; + + clear_bit(ATH11K_FLAG_CRASH_FLUSH, &ab->dev_flags); + + ret = ath11k_core_qmi_firmware_ready(ab); + if (ret) + goto err_hal_srng_deinit; + + clear_bit(ATH11K_FLAG_RECOVERY, &ab->dev_flags); + + return 0; + +err_hal_srng_deinit: + ath11k_hal_srng_deinit(ab); + return ret; +} + +void ath11k_core_halt(struct ath11k *ar) +{ + struct ath11k_base *ab = ar->ab; + + lockdep_assert_held(&ar->conf_mutex); + + ar->num_created_vdevs = 0; + + ath11k_mac_scan_finish(ar); + ath11k_mac_peer_cleanup_all(ar); + cancel_delayed_work_sync(&ar->scan.timeout); + cancel_work_sync(&ar->regd_update_work); + + rcu_assign_pointer(ab->pdevs_active[ar->pdev_idx], NULL); + synchronize_rcu(); + INIT_LIST_HEAD(&ar->arvifs); + idr_init(&ar->txmgmt_idr); +} + +static void ath11k_core_restart(struct work_struct *work) +{ + struct ath11k_base *ab = container_of(work, struct ath11k_base, restart_work); + struct ath11k *ar; + struct ath11k_pdev *pdev; + int i, ret = 0; + + spin_lock_bh(&ab->base_lock); + ab->stats.fw_crash_counter++; + spin_unlock_bh(&ab->base_lock); + + for (i = 0; i < ab->num_radios; i++) { + pdev = &ab->pdevs[i]; + ar = pdev->ar; + if (!ar || ar->state == ATH11K_STATE_OFF) + continue; + + ieee80211_stop_queues(ar->hw); + ath11k_mac_drain_tx(ar); + complete(&ar->scan.started); + complete(&ar->scan.completed); + complete(&ar->peer_assoc_done); + complete(&ar->install_key_done); + complete(&ar->vdev_setup_done); + complete(&ar->bss_survey_done); + + wake_up(&ar->dp.tx_empty_waitq); + idr_for_each(&ar->txmgmt_idr, + ath11k_mac_tx_mgmt_pending_free, ar); + idr_destroy(&ar->txmgmt_idr); + } + + wake_up(&ab->wmi_sc.tx_credits_wq); + wake_up(&ab->peer_mapping_wq); + + ret = ath11k_core_reconfigure_on_crash(ab); + if (ret) { + ath11k_err(ab, "failed to reconfigure driver on crash recovery\n"); + return; + } + + for (i = 0; i < ab->num_radios; i++) { + pdev = &ab->pdevs[i]; + ar = pdev->ar; + if (!ar || ar->state == ATH11K_STATE_OFF) + continue; + + mutex_lock(&ar->conf_mutex); + + switch (ar->state) { + case ATH11K_STATE_ON: + ar->state = ATH11K_STATE_RESTARTING; + ath11k_core_halt(ar); + ieee80211_restart_hw(ar->hw); + break; + case ATH11K_STATE_OFF: + ath11k_warn(ab, + "cannot restart radio %d that hasn't been started\n", + i); + break; + case ATH11K_STATE_RESTARTING: + break; + case ATH11K_STATE_RESTARTED: + ar->state = ATH11K_STATE_WEDGED; + /* fall through */ + case ATH11K_STATE_WEDGED: + ath11k_warn(ab, + "device is wedged, will not restart radio %d\n", i); + break; + } + mutex_unlock(&ar->conf_mutex); + } + complete(&ab->driver_recovery); +} + +int ath11k_core_init(struct ath11k_base *ab) +{ + struct device *dev = ab->dev; + struct rproc *prproc; + phandle rproc_phandle; + int ret; + + if (of_property_read_u32(dev->of_node, "qcom,rproc", &rproc_phandle)) { + ath11k_err(ab, "failed to get q6_rproc handle\n"); + return -ENOENT; + } + + prproc = rproc_get_by_phandle(rproc_phandle); + if (!prproc) { + ath11k_err(ab, "failed to get rproc\n"); + return -EINVAL; + } + ab->tgt_rproc = prproc; + ab->hw_params = ath11k_hw_params; + + ret = ath11k_core_soc_create(ab); + if (ret) { + ath11k_err(ab, "failed to create soc core: %d\n", ret); + return ret; + } + + return 0; +} + +void ath11k_core_deinit(struct ath11k_base *ab) +{ + mutex_lock(&ab->core_lock); + + ath11k_core_pdev_destroy(ab); + ath11k_core_stop(ab); + + mutex_unlock(&ab->core_lock); + + ath11k_ahb_power_down(ab); + ath11k_mac_destroy(ab); + ath11k_core_soc_destroy(ab); +} + +void ath11k_core_free(struct ath11k_base *ab) +{ + kfree(ab); +} + +struct ath11k_base *ath11k_core_alloc(struct device *dev) +{ + struct ath11k_base *ab; + + ab = kzalloc(sizeof(*ab), GFP_KERNEL); + if (!ab) + return NULL; + + init_completion(&ab->driver_recovery); + + ab->workqueue = create_singlethread_workqueue("ath11k_wq"); + if (!ab->workqueue) + goto err_sc_free; + + mutex_init(&ab->core_lock); + spin_lock_init(&ab->base_lock); + + INIT_LIST_HEAD(&ab->peers); + init_waitqueue_head(&ab->peer_mapping_wq); + init_waitqueue_head(&ab->wmi_sc.tx_credits_wq); + INIT_WORK(&ab->restart_work, ath11k_core_restart); + timer_setup(&ab->rx_replenish_retry, ath11k_ce_rx_replenish_retry, 0); + ab->dev = dev; + + return ab; + +err_sc_free: + kfree(ab); + return NULL; +} + +static int __init ath11k_init(void) +{ + int ret; + + ret = ath11k_ahb_init(); + if (ret) + printk(KERN_ERR "failed to register ath11k ahb driver: %d\n", + ret); + return ret; +} +module_init(ath11k_init); + +static void __exit ath11k_exit(void) +{ + ath11k_ahb_exit(); +} +module_exit(ath11k_exit); + +MODULE_DESCRIPTION("Driver support for Qualcomm Technologies 802.11ax wireless chip"); +MODULE_LICENSE("Dual BSD/GPL"); diff --git a/drivers/net/wireless/ath/ath11k/core.h b/drivers/net/wireless/ath/ath11k/core.h new file mode 100644 index 0000000000000..95447d1be1047 --- /dev/null +++ b/drivers/net/wireless/ath/ath11k/core.h @@ -0,0 +1,823 @@ +/* SPDX-License-Identifier: BSD-3-Clause-Clear */ +/* + * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. + */ + +#ifndef ATH11K_CORE_H +#define ATH11K_CORE_H + +#include +#include +#include +#include +#include "qmi.h" +#include "htc.h" +#include "wmi.h" +#include "hal.h" +#include "dp.h" +#include "ce.h" +#include "mac.h" +#include "hw.h" +#include "hal_rx.h" +#include "reg.h" + +#define SM(_v, _f) (((_v) << _f##_LSB) & _f##_MASK) + +#define ATH11K_TX_MGMT_NUM_PENDING_MAX 512 + +#define ATH11K_TX_MGMT_TARGET_MAX_SUPPORT_WMI 64 + +/* Pending management packets threshold for dropping probe responses */ +#define ATH11K_PRB_RSP_DROP_THRESHOLD ((ATH11K_TX_MGMT_TARGET_MAX_SUPPORT_WMI * 3) / 4) + +#define ATH11K_INVALID_HW_MAC_ID 0xFF + +enum ath11k_supported_bw { + ATH11K_BW_20 = 0, + ATH11K_BW_40 = 1, + ATH11K_BW_80 = 2, + ATH11K_BW_160 = 3, +}; + +enum wme_ac { + WME_AC_BE, + WME_AC_BK, + WME_AC_VI, + WME_AC_VO, + WME_NUM_AC +}; + +#define ATH11K_HT_MCS_MAX 7 +#define ATH11K_VHT_MCS_MAX 9 +#define ATH11K_HE_MCS_MAX 11 + +static inline enum wme_ac ath11k_tid_to_ac(u32 tid) +{ + return (((tid == 0) || (tid == 3)) ? WME_AC_BE : + ((tid == 1) || (tid == 2)) ? WME_AC_BK : + ((tid == 4) || (tid == 5)) ? WME_AC_VI : + WME_AC_VO); +} + +struct ath11k_skb_cb { + dma_addr_t paddr; + u8 eid; + struct ath11k *ar; + struct ieee80211_vif *vif; +} __packed; + +struct ath11k_skb_rxcb { + dma_addr_t paddr; + bool is_first_msdu; + bool is_last_msdu; + bool is_continuation; + struct hal_rx_desc *rx_desc; + u8 err_rel_src; + u8 err_code; + u8 mac_id; + u8 unmapped; +}; + +enum ath11k_hw_rev { + ATH11K_HW_IPQ8074, +}; + +enum ath11k_firmware_mode { + /* the default mode, standard 802.11 functionality */ + ATH11K_FIRMWARE_MODE_NORMAL, + + /* factory tests etc */ + ATH11K_FIRMWARE_MODE_FTM, +}; + +#define ATH11K_IRQ_NUM_MAX 52 +#define ATH11K_EXT_IRQ_GRP_NUM_MAX 11 +#define ATH11K_EXT_IRQ_NUM_MAX 16 + +extern const u8 ath11k_reo_status_ring_mask[ATH11K_EXT_IRQ_GRP_NUM_MAX]; +extern const u8 ath11k_tx_ring_mask[ATH11K_EXT_IRQ_GRP_NUM_MAX]; +extern const u8 ath11k_rx_ring_mask[ATH11K_EXT_IRQ_GRP_NUM_MAX]; +extern const u8 ath11k_rx_err_ring_mask[ATH11K_EXT_IRQ_GRP_NUM_MAX]; +extern const u8 ath11k_rx_wbm_rel_ring_mask[ATH11K_EXT_IRQ_GRP_NUM_MAX]; +extern const u8 ath11k_rxdma2host_ring_mask[ATH11K_EXT_IRQ_GRP_NUM_MAX]; +extern const u8 ath11k_host2rxdma_ring_mask[ATH11K_EXT_IRQ_GRP_NUM_MAX]; +extern const u8 rx_mon_status_ring_mask[ATH11K_EXT_IRQ_GRP_NUM_MAX]; + +struct ath11k_ext_irq_grp { + struct ath11k_base *ab; + u32 irqs[ATH11K_EXT_IRQ_NUM_MAX]; + u32 num_irq; + u32 grp_id; + struct napi_struct napi; + struct net_device napi_ndev; + /* Queue of pending packets, not expected to be accessed concurrently + * to avoid locking overhead. + */ + struct sk_buff_head pending_q; +}; + +#define HEHANDLE_CAP_PHYINFO_SIZE 3 +#define HECAP_PHYINFO_SIZE 9 +#define HECAP_MACINFO_SIZE 5 +#define HECAP_TXRX_MCS_NSS_SIZE 2 +#define HECAP_PPET16_PPET8_MAX_SIZE 25 + +#define HE_PPET16_PPET8_SIZE 8 + +/* 802.11ax PPE (PPDU packet Extension) threshold */ +struct he_ppe_threshold { + u32 numss_m1; + u32 ru_mask; + u32 ppet16_ppet8_ru3_ru0[HE_PPET16_PPET8_SIZE]; +}; + +struct ath11k_he { + u8 hecap_macinfo[HECAP_MACINFO_SIZE]; + u32 hecap_rxmcsnssmap; + u32 hecap_txmcsnssmap; + u32 hecap_phyinfo[HEHANDLE_CAP_PHYINFO_SIZE]; + struct he_ppe_threshold hecap_ppet; + u32 heop_param; +}; + +#define MAX_RADIOS 3 + +enum { + WMI_HOST_TP_SCALE_MAX = 0, + WMI_HOST_TP_SCALE_50 = 1, + WMI_HOST_TP_SCALE_25 = 2, + WMI_HOST_TP_SCALE_12 = 3, + WMI_HOST_TP_SCALE_MIN = 4, + WMI_HOST_TP_SCALE_SIZE = 5, +}; + +enum ath11k_scan_state { + ATH11K_SCAN_IDLE, + ATH11K_SCAN_STARTING, + ATH11K_SCAN_RUNNING, + ATH11K_SCAN_ABORTING, +}; + +enum ath11k_dev_flags { + ATH11K_CAC_RUNNING, + ATH11K_FLAG_CORE_REGISTERED, + ATH11K_FLAG_CRASH_FLUSH, + ATH11K_FLAG_RAW_MODE, + ATH11K_FLAG_HW_CRYPTO_DISABLED, + ATH11K_FLAG_BTCOEX, + ATH11K_FLAG_RECOVERY, + ATH11K_FLAG_UNREGISTERING, + ATH11K_FLAG_REGISTERED, +}; + +enum ath11k_monitor_flags { + ATH11K_FLAG_MONITOR_ENABLED, +}; + +struct ath11k_vif { + u32 vdev_id; + enum wmi_vdev_type vdev_type; + enum wmi_vdev_subtype vdev_subtype; + u32 beacon_interval; + u32 dtim_period; + u16 ast_hash; + u16 tcl_metadata; + u8 hal_addr_search_flags; + u8 search_type; + + struct ath11k *ar; + struct ieee80211_vif *vif; + + u16 tx_seq_no; + struct wmi_wmm_params_all_arg wmm_params; + struct list_head list; + union { + struct { + u32 uapsd; + } sta; + struct { + /* 127 stations; wmi limit */ + u8 tim_bitmap[16]; + u8 tim_len; + u32 ssid_len; + u8 ssid[IEEE80211_MAX_SSID_LEN]; + bool hidden_ssid; + /* P2P_IE with NoA attribute for P2P_GO case */ + u32 noa_len; + u8 *noa_data; + } ap; + } u; + + bool is_started; + bool is_up; + u32 aid; + u8 bssid[ETH_ALEN]; + struct cfg80211_bitrate_mask bitrate_mask; + int num_legacy_stations; + int rtscts_prot_mode; + int txpower; +}; + +struct ath11k_vif_iter { + u32 vdev_id; + struct ath11k_vif *arvif; +}; + +struct ath11k_rx_peer_stats { + u64 num_msdu; + u64 num_mpdu_fcs_ok; + u64 num_mpdu_fcs_err; + u64 tcp_msdu_count; + u64 udp_msdu_count; + u64 other_msdu_count; + u64 ampdu_msdu_count; + u64 non_ampdu_msdu_count; + u64 stbc_count; + u64 beamformed_count; + u64 mcs_count[HAL_RX_MAX_MCS + 1]; + u64 nss_count[HAL_RX_MAX_NSS]; + u64 bw_count[HAL_RX_BW_MAX]; + u64 gi_count[HAL_RX_GI_MAX]; + u64 coding_count[HAL_RX_SU_MU_CODING_MAX]; + u64 tid_count[IEEE80211_NUM_TIDS + 1]; + u64 pream_cnt[HAL_RX_PREAMBLE_MAX]; + u64 reception_type[HAL_RX_RECEPTION_TYPE_MAX]; + u64 rx_duration; +}; + +#define ATH11K_HE_MCS_NUM 12 +#define ATH11K_VHT_MCS_NUM 10 +#define ATH11K_BW_NUM 4 +#define ATH11K_NSS_NUM 4 +#define ATH11K_LEGACY_NUM 12 +#define ATH11K_GI_NUM 4 +#define ATH11K_HT_MCS_NUM 32 + +enum ath11k_pkt_rx_err { + ATH11K_PKT_RX_ERR_FCS, + ATH11K_PKT_RX_ERR_TKIP, + ATH11K_PKT_RX_ERR_CRYPT, + ATH11K_PKT_RX_ERR_PEER_IDX_INVAL, + ATH11K_PKT_RX_ERR_MAX, +}; + +enum ath11k_ampdu_subfrm_num { + ATH11K_AMPDU_SUBFRM_NUM_10, + ATH11K_AMPDU_SUBFRM_NUM_20, + ATH11K_AMPDU_SUBFRM_NUM_30, + ATH11K_AMPDU_SUBFRM_NUM_40, + ATH11K_AMPDU_SUBFRM_NUM_50, + ATH11K_AMPDU_SUBFRM_NUM_60, + ATH11K_AMPDU_SUBFRM_NUM_MORE, + ATH11K_AMPDU_SUBFRM_NUM_MAX, +}; + +enum ath11k_amsdu_subfrm_num { + ATH11K_AMSDU_SUBFRM_NUM_1, + ATH11K_AMSDU_SUBFRM_NUM_2, + ATH11K_AMSDU_SUBFRM_NUM_3, + ATH11K_AMSDU_SUBFRM_NUM_4, + ATH11K_AMSDU_SUBFRM_NUM_MORE, + ATH11K_AMSDU_SUBFRM_NUM_MAX, +}; + +enum ath11k_counter_type { + ATH11K_COUNTER_TYPE_BYTES, + ATH11K_COUNTER_TYPE_PKTS, + ATH11K_COUNTER_TYPE_MAX, +}; + +enum ath11k_stats_type { + ATH11K_STATS_TYPE_SUCC, + ATH11K_STATS_TYPE_FAIL, + ATH11K_STATS_TYPE_RETRY, + ATH11K_STATS_TYPE_AMPDU, + ATH11K_STATS_TYPE_MAX, +}; + +struct ath11k_htt_data_stats { + u64 legacy[ATH11K_COUNTER_TYPE_MAX][ATH11K_LEGACY_NUM]; + u64 ht[ATH11K_COUNTER_TYPE_MAX][ATH11K_HT_MCS_NUM]; + u64 vht[ATH11K_COUNTER_TYPE_MAX][ATH11K_VHT_MCS_NUM]; + u64 he[ATH11K_COUNTER_TYPE_MAX][ATH11K_HE_MCS_NUM]; + u64 bw[ATH11K_COUNTER_TYPE_MAX][ATH11K_BW_NUM]; + u64 nss[ATH11K_COUNTER_TYPE_MAX][ATH11K_NSS_NUM]; + u64 gi[ATH11K_COUNTER_TYPE_MAX][ATH11K_GI_NUM]; +}; + +struct ath11k_htt_tx_stats { + struct ath11k_htt_data_stats stats[ATH11K_STATS_TYPE_MAX]; + u64 tx_duration; + u64 ba_fails; + u64 ack_fails; +}; + +struct ath11k_per_ppdu_tx_stats { + u16 succ_pkts; + u16 failed_pkts; + u16 retry_pkts; + u32 succ_bytes; + u32 failed_bytes; + u32 retry_bytes; +}; + +struct ath11k_sta { + struct ath11k_vif *arvif; + + /* the following are protected by ar->data_lock */ + u32 changed; /* IEEE80211_RC_* */ + u32 bw; + u32 nss; + u32 smps; + + struct work_struct update_wk; + struct ieee80211_tx_info tx_info; + struct rate_info txrate; + struct rate_info last_txrate; + u64 rx_duration; + u8 rssi_comb; + struct ath11k_htt_tx_stats *tx_stats; + struct ath11k_rx_peer_stats *rx_stats; +}; + +#define ATH11K_NUM_CHANS 41 +#define ATH11K_MAX_5G_CHAN 173 + +enum ath11k_state { + ATH11K_STATE_OFF, + ATH11K_STATE_ON, + ATH11K_STATE_RESTARTING, + ATH11K_STATE_RESTARTED, + ATH11K_STATE_WEDGED, + /* Add other states as required */ +}; + +/* Antenna noise floor */ +#define ATH11K_DEFAULT_NOISE_FLOOR -95 + +struct ath11k_fw_stats { + struct dentry *debugfs_fwstats; + u32 pdev_id; + u32 stats_id; + struct list_head pdevs; + struct list_head vdevs; + struct list_head bcn; +}; + +struct ath11k_dbg_htt_stats { + u8 type; + u8 reset; + struct debug_htt_stats_req *stats_req; + /* protects shared stats req buffer */ + spinlock_t lock; +}; + +struct ath11k_debug { + struct dentry *debugfs_pdev; + struct ath11k_dbg_htt_stats htt_stats; + u32 extd_tx_stats; + struct ath11k_fw_stats fw_stats; + struct completion fw_stats_complete; + bool fw_stats_done; + u32 extd_rx_stats; + u32 pktlog_filter; + u32 pktlog_mode; + u32 pktlog_peer_valid; + u8 pktlog_peer_addr[ETH_ALEN]; +}; + +struct ath11k_per_peer_tx_stats { + u32 succ_bytes; + u32 retry_bytes; + u32 failed_bytes; + u16 succ_pkts; + u16 retry_pkts; + u16 failed_pkts; + u32 duration; + u8 ba_fails; + bool is_ampdu; +}; + +#define ATH11K_FLUSH_TIMEOUT (5 * HZ) + +struct ath11k_vdev_stop_status { + bool stop_in_progress; + u32 vdev_id; +}; + +struct ath11k { + struct ath11k_base *ab; + struct ath11k_pdev *pdev; + struct ieee80211_hw *hw; + struct ieee80211_ops *ops; + struct ath11k_pdev_wmi *wmi; + struct ath11k_pdev_dp dp; + u8 mac_addr[ETH_ALEN]; + u32 ht_cap_info; + u32 vht_cap_info; + struct ath11k_he ar_he; + enum ath11k_state state; + struct { + struct completion started; + struct completion completed; + struct completion on_channel; + struct delayed_work timeout; + enum ath11k_scan_state state; + bool is_roc; + int vdev_id; + int roc_freq; + bool roc_notify; + } scan; + + struct { + struct ieee80211_supported_band sbands[NUM_NL80211_BANDS]; + } mac; + unsigned long dev_flags; + unsigned int filter_flags; + unsigned long monitor_flags; + u32 min_tx_power; + u32 max_tx_power; + u32 txpower_limit_2g; + u32 txpower_limit_5g; + u32 txpower_scale; + u32 power_scale; + u32 chan_tx_pwr; + u32 num_stations; + u32 max_num_stations; + bool monitor_present; + /* To synchronize concurrent synchronous mac80211 callback operations, + * concurrent debugfs configuration and concurrent FW statistics events. + */ + struct mutex conf_mutex; + /* protects the radio specific data like debug stats, ppdu_stats_info stats, + * vdev_stop_status info, scan data, ath11k_sta info, ath11k_vif info, + * channel context data, survey info, test mode data. + */ + spinlock_t data_lock; + + struct list_head arvifs; + /* should never be NULL; needed for regular htt rx */ + struct ieee80211_channel *rx_channel; + + /* valid during scan; needed for mgmt rx during scan */ + struct ieee80211_channel *scan_channel; + + u8 cfg_tx_chainmask; + u8 cfg_rx_chainmask; + u8 num_rx_chains; + u8 num_tx_chains; + /* pdev_idx starts from 0 whereas pdev->pdev_id starts with 1 */ + u8 pdev_idx; + u8 lmac_id; + + struct completion peer_assoc_done; + + int install_key_status; + struct completion install_key_done; + + int last_wmi_vdev_start_status; + struct ath11k_vdev_stop_status vdev_stop_status; + struct completion vdev_setup_done; + + int num_peers; + int max_num_peers; + u32 num_started_vdevs; + u32 num_created_vdevs; + + struct idr txmgmt_idr; + /* protects txmgmt_idr data */ + spinlock_t txmgmt_idr_lock; + atomic_t num_pending_mgmt_tx; + + /* cycle count is reported twice for each visited channel during scan. + * access protected by data_lock + */ + u32 survey_last_rx_clear_count; + u32 survey_last_cycle_count; + + /* Channel info events are expected to come in pairs without and with + * COMPLETE flag set respectively for each channel visit during scan. + * + * However there are deviations from this rule. This flag is used to + * avoid reporting garbage data. + */ + bool ch_info_can_report_survey; + struct survey_info survey[ATH11K_NUM_CHANS]; + struct completion bss_survey_done; + + struct work_struct regd_update_work; + + struct work_struct wmi_mgmt_tx_work; + struct sk_buff_head wmi_mgmt_tx_queue; + + struct ath11k_per_peer_tx_stats peer_tx_stats; + struct list_head ppdu_stats_info; + u32 ppdu_stat_list_depth; + + struct ath11k_per_peer_tx_stats cached_stats; + u32 last_ppdu_id; + u32 cached_ppdu_id; +#ifdef CONFIG_ATH11K_DEBUGFS + struct ath11k_debug debug; +#endif + bool dfs_block_radar_events; +}; + +struct ath11k_band_cap { + u32 max_bw_supported; + u32 ht_cap_info; + u32 he_cap_info[2]; + u32 he_mcs; + u32 he_cap_phy_info[PSOC_HOST_MAX_PHY_SIZE]; + struct ath11k_ppe_threshold he_ppet; +}; + +struct ath11k_pdev_cap { + u32 supported_bands; + u32 ampdu_density; + u32 vht_cap; + u32 vht_mcs; + u32 he_mcs; + u32 tx_chain_mask; + u32 rx_chain_mask; + u32 tx_chain_mask_shift; + u32 rx_chain_mask_shift; + struct ath11k_band_cap band[NUM_NL80211_BANDS]; +}; + +struct ath11k_pdev { + struct ath11k *ar; + u32 pdev_id; + struct ath11k_pdev_cap cap; + u8 mac_addr[ETH_ALEN]; +}; + +struct ath11k_board_data { + const struct firmware *fw; + const void *data; + size_t len; +}; + +/* IPQ8074 HW channel counters frequency value in hertz */ +#define IPQ8074_CC_FREQ_HERTZ 320000 + +struct ath11k_soc_dp_rx_stats { + u32 err_ring_pkts; + u32 invalid_rbm; + u32 rxdma_error[HAL_REO_ENTR_RING_RXDMA_ECODE_MAX]; + u32 reo_error[HAL_REO_DEST_RING_ERROR_CODE_MAX]; + u32 hal_reo_error[DP_REO_DST_RING_MAX]; +}; + +/* Master structure to hold the hw data which may be used in core module */ +struct ath11k_base { + enum ath11k_hw_rev hw_rev; + struct platform_device *pdev; + struct device *dev; + struct ath11k_qmi qmi; + struct ath11k_wmi_base wmi_sc; + struct completion fw_ready; + struct rproc *tgt_rproc; + int num_radios; + /* HW channel counters frequency value in hertz common to all MACs */ + u32 cc_freq_hz; + + struct ath11k_htc htc; + + struct ath11k_dp dp; + + void __iomem *mem; + unsigned long mem_len; + + const struct ath11k_hif_ops *hif_ops; + + struct ath11k_ce ce; + struct timer_list rx_replenish_retry; + struct ath11k_hal hal; + /* To synchronize core_start/core_stop */ + struct mutex core_lock; + /* Protects data like peers */ + spinlock_t base_lock; + struct ath11k_pdev pdevs[MAX_RADIOS]; + struct ath11k_pdev __rcu *pdevs_active[MAX_RADIOS]; + struct ath11k_hal_reg_capabilities_ext hal_reg_cap[MAX_RADIOS]; + unsigned long long free_vdev_map; + struct list_head peers; + wait_queue_head_t peer_mapping_wq; + u8 mac_addr[ETH_ALEN]; + bool wmi_ready; + u32 wlan_init_status; + int irq_num[ATH11K_IRQ_NUM_MAX]; + struct ath11k_ext_irq_grp ext_irq_grp[ATH11K_EXT_IRQ_GRP_NUM_MAX]; + struct napi_struct *napi; + struct ath11k_targ_cap target_caps; + u32 ext_service_bitmap[WMI_SERVICE_EXT_BM_SIZE]; + bool pdevs_macaddr_valid; + int bd_api; + struct ath11k_hw_params hw_params; + const struct firmware *cal_file; + + /* Below regd's are protected by ab->data_lock */ + /* This is the regd set for every radio + * by the firmware during initializatin + */ + struct ieee80211_regdomain *default_regd[MAX_RADIOS]; + /* This regd is set during dynamic country setting + * This may or may not be used during the runtime + */ + struct ieee80211_regdomain *new_regd[MAX_RADIOS]; + + /* Current DFS Regulatory */ + enum ath11k_dfs_region dfs_region; +#ifdef CONFIG_ATH11K_DEBUGFS + struct dentry *debugfs_soc; + struct dentry *debugfs_ath11k; +#endif + struct ath11k_soc_dp_rx_stats soc_stats; + + unsigned long dev_flags; + struct completion driver_recovery; + struct workqueue_struct *workqueue; + struct work_struct restart_work; + struct { + /* protected by data_lock */ + u32 fw_crash_counter; + } stats; +}; + +struct ath11k_fw_stats_pdev { + struct list_head list; + + /* PDEV stats */ + s32 ch_noise_floor; + /* Cycles spent transmitting frames */ + u32 tx_frame_count; + /* Cycles spent receiving frames */ + u32 rx_frame_count; + /* Total channel busy time, evidently */ + u32 rx_clear_count; + /* Total on-channel time */ + u32 cycle_count; + u32 phy_err_count; + u32 chan_tx_power; + u32 ack_rx_bad; + u32 rts_bad; + u32 rts_good; + u32 fcs_bad; + u32 no_beacons; + u32 mib_int_count; + + /* PDEV TX stats */ + /* Num HTT cookies queued to dispatch list */ + s32 comp_queued; + /* Num HTT cookies dispatched */ + s32 comp_delivered; + /* Num MSDU queued to WAL */ + s32 msdu_enqued; + /* Num MPDU queue to WAL */ + s32 mpdu_enqued; + /* Num MSDUs dropped by WMM limit */ + s32 wmm_drop; + /* Num Local frames queued */ + s32 local_enqued; + /* Num Local frames done */ + s32 local_freed; + /* Num queued to HW */ + s32 hw_queued; + /* Num PPDU reaped from HW */ + s32 hw_reaped; + /* Num underruns */ + s32 underrun; + /* Num PPDUs cleaned up in TX abort */ + s32 tx_abort; + /* Num MPDUs requed by SW */ + s32 mpdus_requed; + /* excessive retries */ + u32 tx_ko; + /* data hw rate code */ + u32 data_rc; + /* Scheduler self triggers */ + u32 self_triggers; + /* frames dropped due to excessive sw retries */ + u32 sw_retry_failure; + /* illegal rate phy errors */ + u32 illgl_rate_phy_err; + /* wal pdev continuous xretry */ + u32 pdev_cont_xretry; + /* wal pdev tx timeouts */ + u32 pdev_tx_timeout; + /* wal pdev resets */ + u32 pdev_resets; + /* frames dropped due to non-availability of stateless TIDs */ + u32 stateless_tid_alloc_failure; + /* PhY/BB underrun */ + u32 phy_underrun; + /* MPDU is more than txop limit */ + u32 txop_ovf; + + /* PDEV RX stats */ + /* Cnts any change in ring routing mid-ppdu */ + s32 mid_ppdu_route_change; + /* Total number of statuses processed */ + s32 status_rcvd; + /* Extra frags on rings 0-3 */ + s32 r0_frags; + s32 r1_frags; + s32 r2_frags; + s32 r3_frags; + /* MSDUs / MPDUs delivered to HTT */ + s32 htt_msdus; + s32 htt_mpdus; + /* MSDUs / MPDUs delivered to local stack */ + s32 loc_msdus; + s32 loc_mpdus; + /* AMSDUs that have more MSDUs than the status ring size */ + s32 oversize_amsdu; + /* Number of PHY errors */ + s32 phy_errs; + /* Number of PHY errors drops */ + s32 phy_err_drop; + /* Number of mpdu errors - FCS, MIC, ENC etc. */ + s32 mpdu_errs; +}; + +struct ath11k_fw_stats_vdev { + struct list_head list; + + u32 vdev_id; + u32 beacon_snr; + u32 data_snr; + u32 num_tx_frames[WLAN_MAX_AC]; + u32 num_rx_frames; + u32 num_tx_frames_retries[WLAN_MAX_AC]; + u32 num_tx_frames_failures[WLAN_MAX_AC]; + u32 num_rts_fail; + u32 num_rts_success; + u32 num_rx_err; + u32 num_rx_discard; + u32 num_tx_not_acked; + u32 tx_rate_history[MAX_TX_RATE_VALUES]; + u32 beacon_rssi_history[MAX_TX_RATE_VALUES]; +}; + +struct ath11k_fw_stats_bcn { + struct list_head list; + + u32 vdev_id; + u32 tx_bcn_succ_cnt; + u32 tx_bcn_outage_cnt; +}; + +void ath11k_peer_unmap_event(struct ath11k_base *ab, u16 peer_id); +void ath11k_peer_map_event(struct ath11k_base *ab, u8 vdev_id, u16 peer_id, + u8 *mac_addr, u16 ast_hash); +struct ath11k_peer *ath11k_peer_find(struct ath11k_base *ab, int vdev_id, + const u8 *addr); +struct ath11k_peer *ath11k_peer_find_by_addr(struct ath11k_base *ab, + const u8 *addr); +struct ath11k_peer *ath11k_peer_find_by_id(struct ath11k_base *ab, int peer_id); +int ath11k_core_qmi_firmware_ready(struct ath11k_base *ab); +int ath11k_core_init(struct ath11k_base *ath11k); +void ath11k_core_deinit(struct ath11k_base *ath11k); +struct ath11k_base *ath11k_core_alloc(struct device *dev); +void ath11k_core_free(struct ath11k_base *ath11k); +int ath11k_core_fetch_bdf(struct ath11k_base *ath11k, + struct ath11k_board_data *bd); +void ath11k_core_free_bdf(struct ath11k_base *ab, struct ath11k_board_data *bd); + +void ath11k_core_halt(struct ath11k *ar); +u8 ath11k_core_get_hw_mac_id(struct ath11k_base *ab, int pdev_idx); + +static inline const char *ath11k_scan_state_str(enum ath11k_scan_state state) +{ + switch (state) { + case ATH11K_SCAN_IDLE: + return "idle"; + case ATH11K_SCAN_STARTING: + return "starting"; + case ATH11K_SCAN_RUNNING: + return "running"; + case ATH11K_SCAN_ABORTING: + return "aborting"; + } + + return "unknown"; +} + +static inline struct ath11k_skb_cb *ATH11K_SKB_CB(struct sk_buff *skb) +{ + return (struct ath11k_skb_cb *)&IEEE80211_SKB_CB(skb)->driver_data; +} + +static inline struct ath11k_skb_rxcb *ATH11K_SKB_RXCB(struct sk_buff *skb) +{ + BUILD_BUG_ON(sizeof(struct ath11k_skb_rxcb) > sizeof(skb->cb)); + return (struct ath11k_skb_rxcb *)skb->cb; +} + +static inline struct ath11k_vif *ath11k_vif_to_arvif(struct ieee80211_vif *vif) +{ + return (struct ath11k_vif *)vif->drv_priv; +} + +#endif /* _CORE_H_ */ diff --git a/drivers/net/wireless/ath/ath11k/debug.c b/drivers/net/wireless/ath/ath11k/debug.c new file mode 100644 index 0000000000000..c27fffd13a5d8 --- /dev/null +++ b/drivers/net/wireless/ath/ath11k/debug.c @@ -0,0 +1,1060 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* + * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. + */ + +#include +#include "core.h" +#include "debug.h" +#include "wmi.h" +#include "hal_rx.h" +#include "dp_tx.h" +#include "debug_htt_stats.h" +#include "peer.h" + +void ath11k_info(struct ath11k_base *ab, const char *fmt, ...) +{ + struct va_format vaf = { + .fmt = fmt, + }; + va_list args; + + va_start(args, fmt); + vaf.va = &args; + dev_info(ab->dev, "%pV", &vaf); + /* TODO: Trace the log */ + va_end(args); +} + +void ath11k_err(struct ath11k_base *ab, const char *fmt, ...) +{ + struct va_format vaf = { + .fmt = fmt, + }; + va_list args; + + va_start(args, fmt); + vaf.va = &args; + dev_err(ab->dev, "%pV", &vaf); + /* TODO: Trace the log */ + va_end(args); +} + +void ath11k_warn(struct ath11k_base *ab, const char *fmt, ...) +{ + struct va_format vaf = { + .fmt = fmt, + }; + va_list args; + + va_start(args, fmt); + vaf.va = &args; + dev_warn_ratelimited(ab->dev, "%pV", &vaf); + /* TODO: Trace the log */ + va_end(args); +} + +#ifdef CONFIG_ATH11K_DEBUG +void __ath11k_dbg(struct ath11k_base *ab, enum ath11k_debug_mask mask, + const char *fmt, ...) +{ + struct va_format vaf; + va_list args; + + va_start(args, fmt); + + vaf.fmt = fmt; + vaf.va = &args; + + if (ath11k_debug_mask & mask) + dev_printk(KERN_DEBUG, ab->dev, "%pV", &vaf); + + /* TODO: trace log */ + + va_end(args); +} + +void ath11k_dbg_dump(struct ath11k_base *ab, + enum ath11k_debug_mask mask, + const char *msg, const char *prefix, + const void *buf, size_t len) +{ + char linebuf[256]; + size_t linebuflen; + const void *ptr; + + if (ath11k_debug_mask & mask) { + if (msg) + __ath11k_dbg(ab, mask, "%s\n", msg); + + for (ptr = buf; (ptr - buf) < len; ptr += 16) { + linebuflen = 0; + linebuflen += scnprintf(linebuf + linebuflen, + sizeof(linebuf) - linebuflen, + "%s%08x: ", + (prefix ? prefix : ""), + (unsigned int)(ptr - buf)); + hex_dump_to_buffer(ptr, len - (ptr - buf), 16, 1, + linebuf + linebuflen, + sizeof(linebuf) - linebuflen, true); + dev_printk(KERN_DEBUG, ab->dev, "%s\n", linebuf); + } + } +} + +#endif + +#ifdef CONFIG_ATH11K_DEBUGFS +static void ath11k_fw_stats_pdevs_free(struct list_head *head) +{ + struct ath11k_fw_stats_pdev *i, *tmp; + + list_for_each_entry_safe(i, tmp, head, list) { + list_del(&i->list); + kfree(i); + } +} + +static void ath11k_fw_stats_vdevs_free(struct list_head *head) +{ + struct ath11k_fw_stats_vdev *i, *tmp; + + list_for_each_entry_safe(i, tmp, head, list) { + list_del(&i->list); + kfree(i); + } +} + +static void ath11k_fw_stats_bcn_free(struct list_head *head) +{ + struct ath11k_fw_stats_bcn *i, *tmp; + + list_for_each_entry_safe(i, tmp, head, list) { + list_del(&i->list); + kfree(i); + } +} + +static void ath11k_debug_fw_stats_reset(struct ath11k *ar) +{ + spin_lock_bh(&ar->data_lock); + ar->debug.fw_stats_done = false; + ath11k_fw_stats_pdevs_free(&ar->debug.fw_stats.pdevs); + ath11k_fw_stats_vdevs_free(&ar->debug.fw_stats.vdevs); + spin_unlock_bh(&ar->data_lock); +} + +void ath11k_debug_fw_stats_process(struct ath11k_base *ab, struct sk_buff *skb) +{ + struct ath11k_fw_stats stats = {}; + struct ath11k *ar; + struct ath11k_pdev *pdev; + bool is_end; + static unsigned int num_vdev, num_bcn; + size_t total_vdevs_started = 0; + int i, ret; + + INIT_LIST_HEAD(&stats.pdevs); + INIT_LIST_HEAD(&stats.vdevs); + INIT_LIST_HEAD(&stats.bcn); + + ret = ath11k_wmi_pull_fw_stats(ab, skb, &stats); + if (ret) { + ath11k_warn(ab, "failed to pull fw stats: %d\n", ret); + goto free; + } + + rcu_read_lock(); + ar = ath11k_mac_get_ar_by_pdev_id(ab, stats.pdev_id); + if (!ar) { + rcu_read_unlock(); + ath11k_warn(ab, "failed to get ar for pdev_id %d: %d\n", + stats.pdev_id, ret); + goto free; + } + + spin_lock_bh(&ar->data_lock); + + if (stats.stats_id == WMI_REQUEST_PDEV_STAT) { + list_splice_tail_init(&stats.pdevs, &ar->debug.fw_stats.pdevs); + ar->debug.fw_stats_done = true; + goto complete; + } + + if (stats.stats_id == WMI_REQUEST_VDEV_STAT) { + if (list_empty(&stats.vdevs)) { + ath11k_warn(ab, "empty vdev stats"); + goto complete; + } + /* FW sends all the active VDEV stats irrespective of PDEV, + * hence limit until the count of all VDEVs started + */ + for (i = 0; i < ab->num_radios; i++) { + pdev = rcu_dereference(ab->pdevs_active[i]); + if (pdev && pdev->ar) + total_vdevs_started += ar->num_started_vdevs; + } + + is_end = ((++num_vdev) == total_vdevs_started ? true : false); + + list_splice_tail_init(&stats.vdevs, + &ar->debug.fw_stats.vdevs); + + if (is_end) { + ar->debug.fw_stats_done = true; + num_vdev = 0; + } + goto complete; + } + + if (stats.stats_id == WMI_REQUEST_BCN_STAT) { + if (list_empty(&stats.bcn)) { + ath11k_warn(ab, "empty bcn stats"); + goto complete; + } + /* Mark end until we reached the count of all started VDEVs + * within the PDEV + */ + is_end = ((++num_bcn) == ar->num_started_vdevs ? true : false); + + list_splice_tail_init(&stats.bcn, + &ar->debug.fw_stats.bcn); + + if (is_end) { + ar->debug.fw_stats_done = true; + num_bcn = 0; + } + } +complete: + complete(&ar->debug.fw_stats_complete); + rcu_read_unlock(); + spin_unlock_bh(&ar->data_lock); + +free: + ath11k_fw_stats_pdevs_free(&stats.pdevs); + ath11k_fw_stats_vdevs_free(&stats.vdevs); + ath11k_fw_stats_bcn_free(&stats.bcn); +} + +static int ath11k_debug_fw_stats_request(struct ath11k *ar, + struct stats_request_params *req_param) +{ + struct ath11k_base *ab = ar->ab; + unsigned long timeout, time_left; + int ret; + + lockdep_assert_held(&ar->conf_mutex); + + /* FW stats can get split when exceeding the stats data buffer limit. + * In that case, since there is no end marking for the back-to-back + * received 'update stats' event, we keep a 3 seconds timeout in case, + * fw_stats_done is not marked yet + */ + timeout = jiffies + msecs_to_jiffies(3 * HZ); + + ath11k_debug_fw_stats_reset(ar); + + reinit_completion(&ar->debug.fw_stats_complete); + + ret = ath11k_wmi_send_stats_request_cmd(ar, req_param); + + if (ret) { + ath11k_warn(ab, "could not request fw stats (%d)\n", + ret); + return ret; + } + + time_left = + wait_for_completion_timeout(&ar->debug.fw_stats_complete, + 1 * HZ); + if (!time_left) + return -ETIMEDOUT; + + for (;;) { + if (time_after(jiffies, timeout)) + break; + + spin_lock_bh(&ar->data_lock); + if (ar->debug.fw_stats_done) { + spin_unlock_bh(&ar->data_lock); + break; + } + spin_unlock_bh(&ar->data_lock); + } + return 0; +} + +static int ath11k_open_pdev_stats(struct inode *inode, struct file *file) +{ + struct ath11k *ar = inode->i_private; + struct ath11k_base *ab = ar->ab; + struct stats_request_params req_param; + void *buf = NULL; + int ret; + + mutex_lock(&ar->conf_mutex); + + if (ar->state != ATH11K_STATE_ON) { + ret = -ENETDOWN; + goto err_unlock; + } + + buf = vmalloc(ATH11K_FW_STATS_BUF_SIZE); + if (!buf) { + ret = -ENOMEM; + goto err_unlock; + } + + req_param.pdev_id = ar->pdev->pdev_id; + req_param.vdev_id = 0; + req_param.stats_id = WMI_REQUEST_PDEV_STAT; + + ret = ath11k_debug_fw_stats_request(ar, &req_param); + if (ret) { + ath11k_warn(ab, "failed to request fw pdev stats: %d\n", ret); + goto err_free; + } + + ath11k_wmi_fw_stats_fill(ar, &ar->debug.fw_stats, req_param.stats_id, + buf); + + file->private_data = buf; + + mutex_unlock(&ar->conf_mutex); + return 0; + +err_free: + vfree(buf); + +err_unlock: + mutex_unlock(&ar->conf_mutex); + return ret; +} + +static int ath11k_release_pdev_stats(struct inode *inode, struct file *file) +{ + vfree(file->private_data); + + return 0; +} + +static ssize_t ath11k_read_pdev_stats(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + const char *buf = file->private_data; + size_t len = strlen(buf); + + return simple_read_from_buffer(user_buf, count, ppos, buf, len); +} + +static const struct file_operations fops_pdev_stats = { + .open = ath11k_open_pdev_stats, + .release = ath11k_release_pdev_stats, + .read = ath11k_read_pdev_stats, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +static int ath11k_open_vdev_stats(struct inode *inode, struct file *file) +{ + struct ath11k *ar = inode->i_private; + struct stats_request_params req_param; + void *buf = NULL; + int ret; + + mutex_lock(&ar->conf_mutex); + + if (ar->state != ATH11K_STATE_ON) { + ret = -ENETDOWN; + goto err_unlock; + } + + buf = vmalloc(ATH11K_FW_STATS_BUF_SIZE); + if (!buf) { + ret = -ENOMEM; + goto err_unlock; + } + + req_param.pdev_id = ar->pdev->pdev_id; + /* VDEV stats is always sent for all active VDEVs from FW */ + req_param.vdev_id = 0; + req_param.stats_id = WMI_REQUEST_VDEV_STAT; + + ret = ath11k_debug_fw_stats_request(ar, &req_param); + if (ret) { + ath11k_warn(ar->ab, "failed to request fw vdev stats: %d\n", ret); + goto err_free; + } + + ath11k_wmi_fw_stats_fill(ar, &ar->debug.fw_stats, req_param.stats_id, + buf); + + file->private_data = buf; + + mutex_unlock(&ar->conf_mutex); + return 0; + +err_free: + vfree(buf); + +err_unlock: + mutex_unlock(&ar->conf_mutex); + return ret; +} + +static int ath11k_release_vdev_stats(struct inode *inode, struct file *file) +{ + vfree(file->private_data); + + return 0; +} + +static ssize_t ath11k_read_vdev_stats(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + const char *buf = file->private_data; + size_t len = strlen(buf); + + return simple_read_from_buffer(user_buf, count, ppos, buf, len); +} + +static const struct file_operations fops_vdev_stats = { + .open = ath11k_open_vdev_stats, + .release = ath11k_release_vdev_stats, + .read = ath11k_read_vdev_stats, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +static int ath11k_open_bcn_stats(struct inode *inode, struct file *file) +{ + struct ath11k *ar = inode->i_private; + struct ath11k_vif *arvif; + struct stats_request_params req_param; + void *buf = NULL; + int ret; + + mutex_lock(&ar->conf_mutex); + + if (ar->state != ATH11K_STATE_ON) { + ret = -ENETDOWN; + goto err_unlock; + } + + buf = vmalloc(ATH11K_FW_STATS_BUF_SIZE); + if (!buf) { + ret = -ENOMEM; + goto err_unlock; + } + + req_param.stats_id = WMI_REQUEST_BCN_STAT; + req_param.pdev_id = ar->pdev->pdev_id; + + /* loop all active VDEVs for bcn stats */ + list_for_each_entry(arvif, &ar->arvifs, list) { + if (!arvif->is_up) + continue; + + req_param.vdev_id = arvif->vdev_id; + ret = ath11k_debug_fw_stats_request(ar, &req_param); + if (ret) { + ath11k_warn(ar->ab, "failed to request fw bcn stats: %d\n", ret); + goto err_free; + } + } + + ath11k_wmi_fw_stats_fill(ar, &ar->debug.fw_stats, req_param.stats_id, + buf); + + /* since beacon stats request is looped for all active VDEVs, saved fw + * stats is not freed for each request until done for all active VDEVs + */ + spin_lock_bh(&ar->data_lock); + ath11k_fw_stats_bcn_free(&ar->debug.fw_stats.bcn); + spin_unlock_bh(&ar->data_lock); + + file->private_data = buf; + + mutex_unlock(&ar->conf_mutex); + return 0; + +err_free: + vfree(buf); + +err_unlock: + mutex_unlock(&ar->conf_mutex); + return ret; +} + +static int ath11k_release_bcn_stats(struct inode *inode, struct file *file) +{ + vfree(file->private_data); + + return 0; +} + +static ssize_t ath11k_read_bcn_stats(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + const char *buf = file->private_data; + size_t len = strlen(buf); + + return simple_read_from_buffer(user_buf, count, ppos, buf, len); +} + +static const struct file_operations fops_bcn_stats = { + .open = ath11k_open_bcn_stats, + .release = ath11k_release_bcn_stats, + .read = ath11k_read_bcn_stats, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +static ssize_t ath11k_read_simulate_fw_crash(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + const char buf[] = + "To simulate firmware crash write one of the keywords to this file:\n" + "`assert` - this will send WMI_FORCE_FW_HANG_CMDID to firmware to cause assert.\n" + "`hw-restart` - this will simply queue hw restart without fw/hw actually crashing.\n"; + + return simple_read_from_buffer(user_buf, count, ppos, buf, strlen(buf)); +} + +/* Simulate firmware crash: + * 'soft': Call wmi command causing firmware hang. This firmware hang is + * recoverable by warm firmware reset. + * 'hard': Force firmware crash by setting any vdev parameter for not allowed + * vdev id. This is hard firmware crash because it is recoverable only by cold + * firmware reset. + */ +static ssize_t ath11k_write_simulate_fw_crash(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath11k_base *ab = file->private_data; + struct ath11k_pdev *pdev; + struct ath11k *ar = ab->pdevs[0].ar; + char buf[32] = {0}; + ssize_t rc; + int i, ret, radioup; + + for (i = 0; i < ab->num_radios; i++) { + pdev = &ab->pdevs[i]; + ar = pdev->ar; + if (ar && ar->state == ATH11K_STATE_ON) { + radioup = 1; + break; + } + } + /* filter partial writes and invalid commands */ + if (*ppos != 0 || count >= sizeof(buf) || count == 0) + return -EINVAL; + + rc = simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, user_buf, count); + if (rc < 0) + return rc; + + /* drop the possible '\n' from the end */ + if (buf[*ppos - 1] == '\n') + buf[*ppos - 1] = '\0'; + + if (radioup == 0) { + ret = -ENETDOWN; + goto exit; + } + + if (!strcmp(buf, "assert")) { + ath11k_info(ab, "simulating firmware assert crash\n"); + ret = ath11k_wmi_force_fw_hang_cmd(ar, + ATH11K_WMI_FW_HANG_ASSERT_TYPE, + ATH11K_WMI_FW_HANG_DELAY); + } else { + ret = -EINVAL; + goto exit; + } + + if (ret) { + ath11k_warn(ab, "failed to simulate firmware crash: %d\n", ret); + goto exit; + } + + ret = count; + +exit: + return ret; +} + +static const struct file_operations fops_simulate_fw_crash = { + .read = ath11k_read_simulate_fw_crash, + .write = ath11k_write_simulate_fw_crash, + .open = simple_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +static ssize_t ath11k_write_enable_extd_tx_stats(struct file *file, + const char __user *ubuf, + size_t count, loff_t *ppos) +{ + struct ath11k *ar = file->private_data; + u32 filter; + int ret; + + if (kstrtouint_from_user(ubuf, count, 0, &filter)) + return -EINVAL; + + mutex_lock(&ar->conf_mutex); + + if (ar->state != ATH11K_STATE_ON) { + ret = -ENETDOWN; + goto out; + } + + if (filter == ar->debug.extd_tx_stats) { + ret = count; + goto out; + } + + ar->debug.extd_tx_stats = filter; + ret = count; + +out: + mutex_unlock(&ar->conf_mutex); + return ret; +} + +static ssize_t ath11k_read_enable_extd_tx_stats(struct file *file, + char __user *ubuf, + size_t count, loff_t *ppos) + +{ + char buf[32] = {0}; + struct ath11k *ar = file->private_data; + int len = 0; + + mutex_lock(&ar->conf_mutex); + len = scnprintf(buf, sizeof(buf) - len, "%08x\n", + ar->debug.extd_tx_stats); + mutex_unlock(&ar->conf_mutex); + + return simple_read_from_buffer(ubuf, count, ppos, buf, len); +} + +static const struct file_operations fops_extd_tx_stats = { + .read = ath11k_read_enable_extd_tx_stats, + .write = ath11k_write_enable_extd_tx_stats, + .open = simple_open +}; + +static ssize_t ath11k_write_extd_rx_stats(struct file *file, + const char __user *ubuf, + size_t count, loff_t *ppos) +{ + struct ath11k *ar = file->private_data; + struct htt_rx_ring_tlv_filter tlv_filter = {0}; + u32 enable, rx_filter = 0, ring_id; + int ret; + + if (kstrtouint_from_user(ubuf, count, 0, &enable)) + return -EINVAL; + + mutex_lock(&ar->conf_mutex); + + if (ar->state != ATH11K_STATE_ON) { + ret = -ENETDOWN; + goto exit; + } + + if (enable > 1) { + ret = -EINVAL; + goto exit; + } + + if (enable == ar->debug.extd_rx_stats) { + ret = count; + goto exit; + } + + if (enable) { + rx_filter = HTT_RX_FILTER_TLV_FLAGS_MPDU_START; + rx_filter |= HTT_RX_FILTER_TLV_FLAGS_PPDU_START; + rx_filter |= HTT_RX_FILTER_TLV_FLAGS_PPDU_END; + rx_filter |= HTT_RX_FILTER_TLV_FLAGS_PPDU_END_USER_STATS; + rx_filter |= HTT_RX_FILTER_TLV_FLAGS_PPDU_END_USER_STATS_EXT; + rx_filter |= HTT_RX_FILTER_TLV_FLAGS_PPDU_END_STATUS_DONE; + + tlv_filter.rx_filter = rx_filter; + tlv_filter.pkt_filter_flags0 = HTT_RX_FP_MGMT_FILTER_FLAGS0; + tlv_filter.pkt_filter_flags1 = HTT_RX_FP_MGMT_FILTER_FLAGS1; + tlv_filter.pkt_filter_flags2 = HTT_RX_FP_CTRL_FILTER_FLASG2; + tlv_filter.pkt_filter_flags3 = HTT_RX_FP_CTRL_FILTER_FLASG3 | + HTT_RX_FP_DATA_FILTER_FLASG3; + } else { + tlv_filter = ath11k_mac_mon_status_filter_default; + } + + ring_id = ar->dp.rx_mon_status_refill_ring.refill_buf_ring.ring_id; + ret = ath11k_dp_tx_htt_rx_filter_setup(ar->ab, ring_id, ar->dp.mac_id, + HAL_RXDMA_MONITOR_STATUS, + DP_RX_BUFFER_SIZE, &tlv_filter); + + if (ret) { + ath11k_warn(ar->ab, "failed to set rx filter for moniter status ring\n"); + goto exit; + } + + ar->debug.extd_rx_stats = enable; + ret = count; +exit: + mutex_unlock(&ar->conf_mutex); + return ret; +} + +static ssize_t ath11k_read_extd_rx_stats(struct file *file, + char __user *ubuf, + size_t count, loff_t *ppos) +{ + struct ath11k *ar = file->private_data; + char buf[32]; + int len = 0; + + mutex_lock(&ar->conf_mutex); + len = scnprintf(buf, sizeof(buf) - len, "%d\n", + ar->debug.extd_rx_stats); + mutex_unlock(&ar->conf_mutex); + + return simple_read_from_buffer(ubuf, count, ppos, buf, len); +} + +static const struct file_operations fops_extd_rx_stats = { + .read = ath11k_read_extd_rx_stats, + .write = ath11k_write_extd_rx_stats, + .open = simple_open, +}; + +static ssize_t ath11k_debug_dump_soc_rx_stats(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath11k_base *ab = file->private_data; + struct ath11k_soc_dp_rx_stats *soc_stats = &ab->soc_stats; + int len = 0, i, retval; + const int size = 4096; + static const char *rxdma_err[HAL_REO_ENTR_RING_RXDMA_ECODE_MAX] = { + "Overflow", "MPDU len", "FCS", "Decrypt", "TKIP MIC", + "Unencrypt", "MSDU len", "MSDU limit", "WiFi parse", + "AMSDU parse", "SA timeout", "DA timeout", + "Flow timeout", "Flush req"}; + static const char *reo_err[HAL_REO_DEST_RING_ERROR_CODE_MAX] = { + "Desc addr zero", "Desc inval", "AMPDU in non BA", + "Non BA dup", "BA dup", "Frame 2k jump", "BAR 2k jump", + "Frame OOR", "BAR OOR", "No BA session", + "Frame SN equal SSN", "PN check fail", "2k err", + "PN err", "Desc blocked"}; + + char *buf; + + buf = kzalloc(size, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + len += scnprintf(buf + len, size - len, "SOC RX STATS:\n\n"); + len += scnprintf(buf + len, size - len, "err ring pkts: %u\n", + soc_stats->err_ring_pkts); + len += scnprintf(buf + len, size - len, "Invalid RBM: %u\n\n", + soc_stats->invalid_rbm); + len += scnprintf(buf + len, size - len, "RXDMA errors:\n"); + for (i = 0; i < HAL_REO_ENTR_RING_RXDMA_ECODE_MAX; i++) + len += scnprintf(buf + len, size - len, "%s: %u\n", + rxdma_err[i], soc_stats->rxdma_error[i]); + + len += scnprintf(buf + len, size - len, "\nREO errors:\n"); + for (i = 0; i < HAL_REO_DEST_RING_ERROR_CODE_MAX; i++) + len += scnprintf(buf + len, size - len, "%s: %u\n", + reo_err[i], soc_stats->reo_error[i]); + + len += scnprintf(buf + len, size - len, "\nHAL REO errors:\n"); + len += scnprintf(buf + len, size - len, + "ring0: %u\nring1: %u\nring2: %u\nring3: %u\n", + soc_stats->hal_reo_error[0], + soc_stats->hal_reo_error[1], + soc_stats->hal_reo_error[2], + soc_stats->hal_reo_error[3]); + + if (len > size) + len = size; + retval = simple_read_from_buffer(user_buf, count, ppos, buf, len); + kfree(buf); + + return retval; +} + +static const struct file_operations fops_soc_rx_stats = { + .read = ath11k_debug_dump_soc_rx_stats, + .open = simple_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +int ath11k_debug_pdev_create(struct ath11k_base *ab) +{ + ab->debugfs_soc = debugfs_create_dir(ab->hw_params.name, ab->debugfs_ath11k); + + if (IS_ERR_OR_NULL(ab->debugfs_soc)) { + if (IS_ERR(ab->debugfs_soc)) + return PTR_ERR(ab->debugfs_soc); + return -ENOMEM; + } + + debugfs_create_file("simulate_fw_crash", 0600, ab->debugfs_soc, ab, + &fops_simulate_fw_crash); + + debugfs_create_file("soc_rx_stats", 0600, ab->debugfs_soc, ab, + &fops_soc_rx_stats); + + return 0; +} + +void ath11k_debug_pdev_destroy(struct ath11k_base *ab) +{ + debugfs_remove_recursive(ab->debugfs_ath11k); + ab->debugfs_ath11k = NULL; +} + +int ath11k_debug_soc_create(struct ath11k_base *ab) +{ + ab->debugfs_ath11k = debugfs_create_dir("ath11k", NULL); + + if (IS_ERR_OR_NULL(ab->debugfs_ath11k)) { + if (IS_ERR(ab->debugfs_ath11k)) + return PTR_ERR(ab->debugfs_ath11k); + return -ENOMEM; + } + + return 0; +} + +void ath11k_debug_soc_destroy(struct ath11k_base *ab) +{ + debugfs_remove_recursive(ab->debugfs_soc); + ab->debugfs_soc = NULL; +} + +void ath11k_debug_fw_stats_init(struct ath11k *ar) +{ + struct dentry *fwstats_dir = debugfs_create_dir("fw_stats", + ar->debug.debugfs_pdev); + + ar->debug.fw_stats.debugfs_fwstats = fwstats_dir; + + /* all stats debugfs files created are under "fw_stats" directory + * created per PDEV + */ + debugfs_create_file("pdev_stats", 0600, fwstats_dir, ar, + &fops_pdev_stats); + debugfs_create_file("vdev_stats", 0600, fwstats_dir, ar, + &fops_vdev_stats); + debugfs_create_file("beacon_stats", 0600, fwstats_dir, ar, + &fops_bcn_stats); + + INIT_LIST_HEAD(&ar->debug.fw_stats.pdevs); + INIT_LIST_HEAD(&ar->debug.fw_stats.vdevs); + INIT_LIST_HEAD(&ar->debug.fw_stats.bcn); + + init_completion(&ar->debug.fw_stats_complete); +} + +static ssize_t ath11k_write_pktlog_filter(struct file *file, + const char __user *ubuf, + size_t count, loff_t *ppos) +{ + struct ath11k *ar = file->private_data; + struct htt_rx_ring_tlv_filter tlv_filter = {0}; + u32 rx_filter = 0, ring_id, filter, mode; + u8 buf[128] = {0}; + int ret; + ssize_t rc; + + mutex_lock(&ar->conf_mutex); + if (ar->state != ATH11K_STATE_ON) { + ret = -ENETDOWN; + goto out; + } + + rc = simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, ubuf, count); + if (rc < 0) { + ret = rc; + goto out; + } + buf[rc] = '\0'; + + ret = sscanf(buf, "0x%x %u", &filter, &mode); + if (ret != 2) { + ret = -EINVAL; + goto out; + } + + if (filter) { + ret = ath11k_wmi_pdev_pktlog_enable(ar, filter); + if (ret) { + ath11k_warn(ar->ab, + "failed to enable pktlog filter %x: %d\n", + ar->debug.pktlog_filter, ret); + goto out; + } + } else { + ret = ath11k_wmi_pdev_pktlog_disable(ar); + if (ret) { + ath11k_warn(ar->ab, "failed to disable pktlog: %d\n", ret); + goto out; + } + } + +#define HTT_RX_FILTER_TLV_LITE_MODE \ + (HTT_RX_FILTER_TLV_FLAGS_PPDU_START | \ + HTT_RX_FILTER_TLV_FLAGS_PPDU_END | \ + HTT_RX_FILTER_TLV_FLAGS_PPDU_END_USER_STATS | \ + HTT_RX_FILTER_TLV_FLAGS_PPDU_END_USER_STATS_EXT | \ + HTT_RX_FILTER_TLV_FLAGS_PPDU_END_STATUS_DONE | \ + HTT_RX_FILTER_TLV_FLAGS_MPDU_START) + + if (mode == ATH11K_PKTLOG_MODE_FULL) { + rx_filter = HTT_RX_FILTER_TLV_LITE_MODE | + HTT_RX_FILTER_TLV_FLAGS_MSDU_START | + HTT_RX_FILTER_TLV_FLAGS_MSDU_END | + HTT_RX_FILTER_TLV_FLAGS_MPDU_END | + HTT_RX_FILTER_TLV_FLAGS_PACKET_HEADER | + HTT_RX_FILTER_TLV_FLAGS_ATTENTION; + } else if (mode == ATH11K_PKTLOG_MODE_LITE) { + rx_filter = HTT_RX_FILTER_TLV_LITE_MODE; + } + + tlv_filter.rx_filter = rx_filter; + if (rx_filter) { + tlv_filter.pkt_filter_flags0 = HTT_RX_FP_MGMT_FILTER_FLAGS0; + tlv_filter.pkt_filter_flags1 = HTT_RX_FP_MGMT_FILTER_FLAGS1; + tlv_filter.pkt_filter_flags2 = HTT_RX_FP_CTRL_FILTER_FLASG2; + tlv_filter.pkt_filter_flags3 = HTT_RX_FP_CTRL_FILTER_FLASG3 | + HTT_RX_FP_DATA_FILTER_FLASG3; + } + + ring_id = ar->dp.rx_mon_status_refill_ring.refill_buf_ring.ring_id; + ret = ath11k_dp_tx_htt_rx_filter_setup(ar->ab, ring_id, ar->dp.mac_id, + HAL_RXDMA_MONITOR_STATUS, + DP_RX_BUFFER_SIZE, &tlv_filter); + if (ret) { + ath11k_warn(ar->ab, "failed to set rx filter for moniter status ring\n"); + goto out; + } + + ath11k_dbg(ar->ab, ATH11K_DBG_WMI, "pktlog filter %d mode %s\n", + filter, ((mode == ATH11K_PKTLOG_MODE_FULL) ? "full" : "lite")); + + ar->debug.pktlog_filter = filter; + ar->debug.pktlog_mode = mode; + ret = count; + +out: + mutex_unlock(&ar->conf_mutex); + return ret; +} + +static ssize_t ath11k_read_pktlog_filter(struct file *file, + char __user *ubuf, + size_t count, loff_t *ppos) + +{ + char buf[32] = {0}; + struct ath11k *ar = file->private_data; + int len = 0; + + mutex_lock(&ar->conf_mutex); + len = scnprintf(buf, sizeof(buf) - len, "%08x %08x\n", + ar->debug.pktlog_filter, + ar->debug.pktlog_mode); + mutex_unlock(&ar->conf_mutex); + + return simple_read_from_buffer(ubuf, count, ppos, buf, len); +} + +static const struct file_operations fops_pktlog_filter = { + .read = ath11k_read_pktlog_filter, + .write = ath11k_write_pktlog_filter, + .open = simple_open +}; + +static ssize_t ath11k_write_simulate_radar(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath11k *ar = file->private_data; + int ret; + + ret = ath11k_wmi_simulate_radar(ar); + if (ret) + return ret; + + return count; +} + +static const struct file_operations fops_simulate_radar = { + .write = ath11k_write_simulate_radar, + .open = simple_open +}; + +int ath11k_debug_register(struct ath11k *ar) +{ + struct ath11k_base *ab = ar->ab; + char pdev_name[5]; + char buf[100] = {0}; + + snprintf(pdev_name, sizeof(pdev_name), "%s%d", "mac", ar->pdev_idx); + + ar->debug.debugfs_pdev = debugfs_create_dir(pdev_name, ab->debugfs_soc); + + if (IS_ERR_OR_NULL(ar->debug.debugfs_pdev)) { + if (IS_ERR(ar->debug.debugfs_pdev)) + return PTR_ERR(ar->debug.debugfs_pdev); + + return -ENOMEM; + } + + /* Create a symlink under ieee80211/phy* */ + snprintf(buf, 100, "../../ath11k/%pd2", ar->debug.debugfs_pdev); + debugfs_create_symlink("ath11k", ar->hw->wiphy->debugfsdir, buf); + + ath11k_debug_htt_stats_init(ar); + + ath11k_debug_fw_stats_init(ar); + + debugfs_create_file("ext_tx_stats", 0644, + ar->debug.debugfs_pdev, ar, + &fops_extd_tx_stats); + debugfs_create_file("ext_rx_stats", 0644, + ar->debug.debugfs_pdev, ar, + &fops_extd_rx_stats); + debugfs_create_file("pktlog_filter", 0644, + ar->debug.debugfs_pdev, ar, + &fops_pktlog_filter); + + if (ar->hw->wiphy->bands[NL80211_BAND_5GHZ]) { + debugfs_create_file("dfs_simulate_radar", 0200, + ar->debug.debugfs_pdev, ar, + &fops_simulate_radar); + debugfs_create_bool("dfs_block_radar_events", 0200, + ar->debug.debugfs_pdev, + &ar->dfs_block_radar_events); + } + + return 0; +} + +void ath11k_debug_unregister(struct ath11k *ar) +{ +} +#endif /* CONFIG_ATH11K_DEBUGFS */ diff --git a/drivers/net/wireless/ath/ath11k/debug.h b/drivers/net/wireless/ath/ath11k/debug.h new file mode 100644 index 0000000000000..aef33f83c9b16 --- /dev/null +++ b/drivers/net/wireless/ath/ath11k/debug.h @@ -0,0 +1,278 @@ +/* SPDX-License-Identifier: BSD-3-Clause-Clear */ +/* + * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. + */ + +#ifndef _ATH11K_DEBUG_H_ +#define _ATH11K_DEBUG_H_ + +#include "hal_tx.h" +#include "trace.h" + +enum ath11k_debug_mask { + ATH11K_DBG_AHB = 0x00000001, + ATH11K_DBG_WMI = 0x00000002, + ATH11K_DBG_HTC = 0x00000004, + ATH11K_DBG_DP_HTT = 0x00000008, + ATH11K_DBG_MAC = 0x00000010, + ATH11K_DBG_BOOT = 0x00000020, + ATH11K_DBG_QMI = 0x00000040, + ATH11K_DBG_DATA = 0x00000080, + ATH11K_DBG_MGMT = 0x00000100, + ATH11K_DBG_REG = 0x00000200, + ATH11K_DBG_TESTMODE = 0x00000400, + ATH11k_DBG_HAL = 0x00000800, + ATH11K_DBG_ANY = 0xffffffff, +}; + +/* htt_dbg_ext_stats_type */ +enum ath11k_dbg_htt_ext_stats_type { + ATH11K_DBG_HTT_EXT_STATS_RESET = 0, + ATH11K_DBG_HTT_EXT_STATS_PDEV_TX = 1, + ATH11K_DBG_HTT_EXT_STATS_PDEV_RX = 2, + ATH11K_DBG_HTT_EXT_STATS_PDEV_TX_HWQ = 3, + ATH11K_DBG_HTT_EXT_STATS_PDEV_TX_SCHED = 4, + ATH11K_DBG_HTT_EXT_STATS_PDEV_ERROR = 5, + ATH11K_DBG_HTT_EXT_STATS_PDEV_TQM = 6, + ATH11K_DBG_HTT_EXT_STATS_TQM_CMDQ = 7, + ATH11K_DBG_HTT_EXT_STATS_TX_DE_INFO = 8, + ATH11K_DBG_HTT_EXT_STATS_PDEV_TX_RATE = 9, + ATH11K_DBG_HTT_EXT_STATS_PDEV_RX_RATE = 10, + ATH11K_DBG_HTT_EXT_STATS_PEER_INFO = 11, + ATH11K_DBG_HTT_EXT_STATS_TX_SELFGEN_INFO = 12, + ATH11K_DBG_HTT_EXT_STATS_TX_MU_HWQ = 13, + ATH11K_DBG_HTT_EXT_STATS_RING_IF_INFO = 14, + ATH11K_DBG_HTT_EXT_STATS_SRNG_INFO = 15, + ATH11K_DBG_HTT_EXT_STATS_SFM_INFO = 16, + ATH11K_DBG_HTT_EXT_STATS_PDEV_TX_MU = 17, + ATH11K_DBG_HTT_EXT_STATS_ACTIVE_PEERS_LIST = 18, + ATH11K_DBG_HTT_EXT_STATS_PDEV_CCA_STATS = 19, + ATH11K_DBG_HTT_EXT_STATS_TWT_SESSIONS = 20, + ATH11K_DBG_HTT_EXT_STATS_REO_RESOURCE_STATS = 21, + ATH11K_DBG_HTT_EXT_STATS_TX_SOUNDING_INFO = 22, + + /* keep this last */ + ATH11K_DBG_HTT_NUM_EXT_STATS, +}; + +struct debug_htt_stats_req { + bool done; + u8 pdev_id; + u8 type; + u8 peer_addr[ETH_ALEN]; + struct completion cmpln; + u32 buf_len; + u8 buf[0]; +}; + +#define ATH11K_HTT_STATS_BUF_SIZE (1024 * 512) + +#define ATH11K_FW_STATS_BUF_SIZE (1024 * 1024) + +#define ATH11K_HTT_PKTLOG_MAX_SIZE 2048 + +enum ath11k_pktlog_filter { + ATH11K_PKTLOG_RX = 0x000000001, + ATH11K_PKTLOG_TX = 0x000000002, + ATH11K_PKTLOG_RCFIND = 0x000000004, + ATH11K_PKTLOG_RCUPDATE = 0x000000008, + ATH11K_PKTLOG_EVENT_SMART_ANT = 0x000000020, + ATH11K_PKTLOG_EVENT_SW = 0x000000040, + ATH11K_PKTLOG_ANY = 0x00000006f, +}; + +enum ath11k_pktlog_mode { + ATH11K_PKTLOG_MODE_LITE = 1, + ATH11K_PKTLOG_MODE_FULL = 2, +}; + +enum ath11k_pktlog_enum { + ATH11K_PKTLOG_TYPE_TX_CTRL = 1, + ATH11K_PKTLOG_TYPE_TX_STAT = 2, + ATH11K_PKTLOG_TYPE_TX_MSDU_ID = 3, + ATH11K_PKTLOG_TYPE_RX_STAT = 5, + ATH11K_PKTLOG_TYPE_RC_FIND = 6, + ATH11K_PKTLOG_TYPE_RC_UPDATE = 7, + ATH11K_PKTLOG_TYPE_TX_VIRT_ADDR = 8, + ATH11K_PKTLOG_TYPE_RX_CBF = 10, + ATH11K_PKTLOG_TYPE_RX_STATBUF = 22, + ATH11K_PKTLOG_TYPE_PPDU_STATS = 23, + ATH11K_PKTLOG_TYPE_LITE_RX = 24, +}; + +__printf(2, 3) void ath11k_info(struct ath11k_base *ab, const char *fmt, ...); +__printf(2, 3) void ath11k_err(struct ath11k_base *ab, const char *fmt, ...); +__printf(2, 3) void ath11k_warn(struct ath11k_base *ab, const char *fmt, ...); + +extern unsigned int ath11k_debug_mask; + +#ifdef CONFIG_ATH11K_DEBUG +__printf(3, 4) void __ath11k_dbg(struct ath11k_base *ab, + enum ath11k_debug_mask mask, + const char *fmt, ...); +void ath11k_dbg_dump(struct ath11k_base *ab, + enum ath11k_debug_mask mask, + const char *msg, const char *prefix, + const void *buf, size_t len); +#else /* CONFIG_ATH11K_DEBUG */ +static inline int __ath11k_dbg(struct ath11k_base *ab, + enum ath11k_debug_mask dbg_mask, + const char *fmt, ...) +{ + return 0; +} + +static inline void ath11k_dbg_dump(struct ath11k_base *ab, + enum ath11k_debug_mask mask, + const char *msg, const char *prefix, + const void *buf, size_t len) +{ +} +#endif /* CONFIG_ATH11K_DEBUG */ + +#ifdef CONFIG_ATH11K_DEBUGFS +int ath11k_debug_soc_create(struct ath11k_base *ab); +void ath11k_debug_soc_destroy(struct ath11k_base *ab); +int ath11k_debug_pdev_create(struct ath11k_base *ab); +void ath11k_debug_pdev_destroy(struct ath11k_base *ab); +int ath11k_debug_register(struct ath11k *ar); +void ath11k_debug_unregister(struct ath11k *ar); +void ath11k_dbg_htt_ext_stats_handler(struct ath11k_base *ab, + struct sk_buff *skb); +void ath11k_debug_fw_stats_process(struct ath11k_base *ab, struct sk_buff *skb); + +void ath11k_debug_fw_stats_init(struct ath11k *ar); +int ath11k_dbg_htt_stats_req(struct ath11k *ar); + +static inline bool ath11k_debug_is_pktlog_lite_mode_enabled(struct ath11k *ar) +{ + return (ar->debug.pktlog_mode == ATH11K_PKTLOG_MODE_LITE); +} + +static inline bool ath11k_debug_is_pktlog_rx_stats_enabled(struct ath11k *ar) +{ + return (!ar->debug.pktlog_peer_valid && ar->debug.pktlog_mode); +} + +static inline bool ath11k_debug_is_pktlog_peer_valid(struct ath11k *ar, u8 *addr) +{ + return (ar->debug.pktlog_peer_valid && ar->debug.pktlog_mode && + ether_addr_equal(addr, ar->debug.pktlog_peer_addr)); +} + +static inline int ath11k_debug_is_extd_tx_stats_enabled(struct ath11k *ar) +{ + return ar->debug.extd_tx_stats; +} + +static inline int ath11k_debug_is_extd_rx_stats_enabled(struct ath11k *ar) +{ + return ar->debug.extd_rx_stats; +} +#else +static inline int ath11k_debug_soc_create(struct ath11k_base *ab) +{ + return 0; +} + +static inline void ath11k_debug_soc_destroy(struct ath11k_base *ab) +{ +} + +static inline int ath11k_debug_pdev_create(struct ath11k_base *ab) +{ + return 0; +} + +static inline void ath11k_debug_pdev_destroy(struct ath11k_base *ab) +{ +} + +static inline int ath11k_debug_register(struct ath11k *ar) +{ + return 0; +} + +static inline void ath11k_debug_unregister(struct ath11k *ar) +{ +} + +static inline void ath11k_dbg_htt_ext_stats_handler(struct ath11k_base *ab, + struct sk_buff *skb) +{ +} + +static inline void ath11k_debug_fw_stats_process(struct ath11k_base *ab, + struct sk_buff *skb) +{ +} + +static inline void ath11k_debug_fw_stats_init(struct ath11k *ar) +{ +} + +static inline int ath11k_debug_is_extd_tx_stats_enabled(struct ath11k *ar) +{ + return 0; +} + +static inline int ath11k_debug_is_extd_rx_stats_enabled(struct ath11k *ar) +{ + return 0; +} + +static inline int ath11k_dbg_htt_stats_req(struct ath11k *ar) +{ + return 0; +} + +static inline bool ath11k_debug_is_pktlog_lite_mode_enabled(struct ath11k *ar) +{ + return false; +} + +static inline bool ath11k_debug_is_pktlog_rx_stats_enabled(struct ath11k *ar) +{ + return false; +} + +static inline bool ath11k_debug_is_pktlog_peer_valid(struct ath11k *ar, u8 *addr) +{ + return false; +} +#endif /* CONFIG_ATH11K_DEBUGFS */ + +#ifdef CONFIG_MAC80211_DEBUGFS +void ath11k_sta_add_debugfs(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_sta *sta, struct dentry *dir); +void +ath11k_accumulate_per_peer_tx_stats(struct ath11k_sta *arsta, + struct ath11k_per_peer_tx_stats *peer_stats, + u8 legacy_rate_idx); +void ath11k_update_per_peer_stats_from_txcompl(struct ath11k *ar, + struct sk_buff *msdu, + struct hal_tx_status *ts); +#else /* !CONFIG_MAC80211_DEBUGFS */ +static inline void +ath11k_accumulate_per_peer_tx_stats(struct ath11k_sta *arsta, + struct ath11k_per_peer_tx_stats *peer_stats, + u8 legacy_rate_idx) +{ +} + +static inline void +ath11k_update_per_peer_stats_from_txcompl(struct ath11k *ar, + struct sk_buff *msdu, + struct hal_tx_status *ts) +{ +} + +#endif /* CONFIG_MAC80211_DEBUGFS*/ + +#define ath11k_dbg(ar, dbg_mask, fmt, ...) \ +do { \ + if (ath11k_debug_mask & dbg_mask) \ + __ath11k_dbg(ar, dbg_mask, fmt, ##__VA_ARGS__); \ +} while (0) + +#endif /* _ATH11K_DEBUG_H_ */ diff --git a/drivers/net/wireless/ath/ath11k/debug_htt_stats.c b/drivers/net/wireless/ath/ath11k/debug_htt_stats.c new file mode 100644 index 0000000000000..27b301bc1a1b1 --- /dev/null +++ b/drivers/net/wireless/ath/ath11k/debug_htt_stats.c @@ -0,0 +1,4432 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* + * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. + */ + +#include +#include "core.h" +#include "dp_tx.h" +#include "dp_rx.h" +#include "debug.h" +#include "debug_htt_stats.h" + +#define HTT_DBG_OUT(buf, len, fmt, ...) \ + scnprintf(buf, len, fmt "\n", ##__VA_ARGS__) + +#define HTT_MAX_STRING_LEN 256 +#define HTT_MAX_PRINT_CHAR_PER_ELEM 15 + +#define HTT_TLV_HDR_LEN 4 + +#define ARRAY_TO_STRING(out, arr, len) \ + do { \ + int index = 0; u8 i; \ + for (i = 0; i < len; i++) { \ + index += snprintf(out + index, HTT_MAX_STRING_LEN - index, \ + " %u:%u,", i, arr[i]); \ + if (index < 0 || index >= HTT_MAX_STRING_LEN) \ + break; \ + } \ + } while (0) + +static inline void htt_print_stats_string_tlv(const void *tag_buf, + u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_stats_string_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + u8 i; + u16 index = 0; + char data[HTT_MAX_STRING_LEN] = {0}; + + tag_len = tag_len >> 2; + + len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_STATS_STRING_TLV:"); + + for (i = 0; i < tag_len; i++) { + index += snprintf(&data[index], + HTT_MAX_STRING_LEN - index, + "%.*s", 4, (char *)&(htt_stats_buf->data[i])); + if (index >= HTT_MAX_STRING_LEN) + break; + } + + len += HTT_DBG_OUT(buf + len, buf_len - len, "data = %s\n", data); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void htt_print_tx_pdev_stats_cmn_tlv(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_tx_pdev_stats_cmn_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + + len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_TX_PDEV_STATS_CMN_TLV:"); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mac_id = %u", + htt_stats_buf->mac_id__word & 0xFF); + len += HTT_DBG_OUT(buf + len, buf_len - len, "hw_queued = %u", + htt_stats_buf->hw_queued); + len += HTT_DBG_OUT(buf + len, buf_len - len, "hw_reaped = %u", + htt_stats_buf->hw_reaped); + len += HTT_DBG_OUT(buf + len, buf_len - len, "underrun = %u", + htt_stats_buf->underrun); + len += HTT_DBG_OUT(buf + len, buf_len - len, "hw_paused = %u", + htt_stats_buf->hw_paused); + len += HTT_DBG_OUT(buf + len, buf_len - len, "hw_flush = %u", + htt_stats_buf->hw_flush); + len += HTT_DBG_OUT(buf + len, buf_len - len, "hw_filt = %u", + htt_stats_buf->hw_filt); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tx_abort = %u", + htt_stats_buf->tx_abort); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mpdu_requeued = %u", + htt_stats_buf->mpdu_requed); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tx_xretry = %u", + htt_stats_buf->tx_xretry); + len += HTT_DBG_OUT(buf + len, buf_len - len, "data_rc = %u", + htt_stats_buf->data_rc); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mpdu_dropped_xretry = %u", + htt_stats_buf->mpdu_dropped_xretry); + len += HTT_DBG_OUT(buf + len, buf_len - len, "illegal_rate_phy_err = %u", + htt_stats_buf->illgl_rate_phy_err); + len += HTT_DBG_OUT(buf + len, buf_len - len, "cont_xretry = %u", + htt_stats_buf->cont_xretry); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tx_timeout = %u", + htt_stats_buf->tx_timeout); + len += HTT_DBG_OUT(buf + len, buf_len - len, "pdev_resets = %u", + htt_stats_buf->pdev_resets); + len += HTT_DBG_OUT(buf + len, buf_len - len, "phy_underrun = %u", + htt_stats_buf->phy_underrun); + len += HTT_DBG_OUT(buf + len, buf_len - len, "txop_ovf = %u", + htt_stats_buf->txop_ovf); + len += HTT_DBG_OUT(buf + len, buf_len - len, "seq_posted = %u", + htt_stats_buf->seq_posted); + len += HTT_DBG_OUT(buf + len, buf_len - len, "seq_failed_queueing = %u", + htt_stats_buf->seq_failed_queueing); + len += HTT_DBG_OUT(buf + len, buf_len - len, "seq_completed = %u", + htt_stats_buf->seq_completed); + len += HTT_DBG_OUT(buf + len, buf_len - len, "seq_restarted = %u", + htt_stats_buf->seq_restarted); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mu_seq_posted = %u", + htt_stats_buf->mu_seq_posted); + len += HTT_DBG_OUT(buf + len, buf_len - len, "seq_switch_hw_paused = %u", + htt_stats_buf->seq_switch_hw_paused); + len += HTT_DBG_OUT(buf + len, buf_len - len, "next_seq_posted_dsr = %u", + htt_stats_buf->next_seq_posted_dsr); + len += HTT_DBG_OUT(buf + len, buf_len - len, "seq_posted_isr = %u", + htt_stats_buf->seq_posted_isr); + len += HTT_DBG_OUT(buf + len, buf_len - len, "seq_ctrl_cached = %u", + htt_stats_buf->seq_ctrl_cached); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mpdu_count_tqm = %u", + htt_stats_buf->mpdu_count_tqm); + len += HTT_DBG_OUT(buf + len, buf_len - len, "msdu_count_tqm = %u", + htt_stats_buf->msdu_count_tqm); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mpdu_removed_tqm = %u", + htt_stats_buf->mpdu_removed_tqm); + len += HTT_DBG_OUT(buf + len, buf_len - len, "msdu_removed_tqm = %u", + htt_stats_buf->msdu_removed_tqm); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mpdus_sw_flush = %u", + htt_stats_buf->mpdus_sw_flush); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mpdus_hw_filter = %u", + htt_stats_buf->mpdus_hw_filter); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mpdus_truncated = %u", + htt_stats_buf->mpdus_truncated); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mpdus_ack_failed = %u", + htt_stats_buf->mpdus_ack_failed); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mpdus_expired = %u", + htt_stats_buf->mpdus_expired); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mpdus_seq_hw_retry = %u", + htt_stats_buf->mpdus_seq_hw_retry); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ack_tlv_proc = %u", + htt_stats_buf->ack_tlv_proc); + len += HTT_DBG_OUT(buf + len, buf_len - len, "coex_abort_mpdu_cnt_valid = %u", + htt_stats_buf->coex_abort_mpdu_cnt_valid); + len += HTT_DBG_OUT(buf + len, buf_len - len, "coex_abort_mpdu_cnt = %u", + htt_stats_buf->coex_abort_mpdu_cnt); + len += HTT_DBG_OUT(buf + len, buf_len - len, "num_total_ppdus_tried_ota = %u", + htt_stats_buf->num_total_ppdus_tried_ota); + len += HTT_DBG_OUT(buf + len, buf_len - len, "num_data_ppdus_tried_ota = %u", + htt_stats_buf->num_data_ppdus_tried_ota); + len += HTT_DBG_OUT(buf + len, buf_len - len, "local_ctrl_mgmt_enqued = %u", + htt_stats_buf->local_ctrl_mgmt_enqued); + len += HTT_DBG_OUT(buf + len, buf_len - len, "local_ctrl_mgmt_freed = %u", + htt_stats_buf->local_ctrl_mgmt_freed); + len += HTT_DBG_OUT(buf + len, buf_len - len, "local_data_enqued = %u", + htt_stats_buf->local_data_enqued); + len += HTT_DBG_OUT(buf + len, buf_len - len, "local_data_freed = %u", + htt_stats_buf->local_data_freed); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mpdu_tried = %u", + htt_stats_buf->mpdu_tried); + len += HTT_DBG_OUT(buf + len, buf_len - len, "isr_wait_seq_posted = %u", + htt_stats_buf->isr_wait_seq_posted); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tx_active_dur_us_low = %u", + htt_stats_buf->tx_active_dur_us_low); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tx_active_dur_us_high = %u\n", + htt_stats_buf->tx_active_dur_us_high); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_tx_pdev_stats_urrn_tlv_v(const void *tag_buf, + u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_tx_pdev_stats_urrn_tlv_v *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + char urrn_stats[HTT_MAX_STRING_LEN] = {0}; + u16 num_elems = min_t(u16, (tag_len >> 2), HTT_TX_PDEV_MAX_URRN_STATS); + + len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_TX_PDEV_STATS_URRN_TLV_V:"); + + ARRAY_TO_STRING(urrn_stats, htt_stats_buf->urrn_stats, num_elems); + len += HTT_DBG_OUT(buf + len, buf_len - len, "urrn_stats = %s\n", urrn_stats); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_tx_pdev_stats_flush_tlv_v(const void *tag_buf, + u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_tx_pdev_stats_flush_tlv_v *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + char flush_errs[HTT_MAX_STRING_LEN] = {0}; + u16 num_elems = min_t(u16, (tag_len >> 2), HTT_TX_PDEV_MAX_FLUSH_REASON_STATS); + + len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_TX_PDEV_STATS_FLUSH_TLV_V:"); + + ARRAY_TO_STRING(flush_errs, htt_stats_buf->flush_errs, num_elems); + len += HTT_DBG_OUT(buf + len, buf_len - len, "flush_errs = %s\n", flush_errs); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_tx_pdev_stats_sifs_tlv_v(const void *tag_buf, + u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_tx_pdev_stats_sifs_tlv_v *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + char sifs_status[HTT_MAX_STRING_LEN] = {0}; + u16 num_elems = min_t(u16, (tag_len >> 2), HTT_TX_PDEV_MAX_SIFS_BURST_STATS); + + len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_TX_PDEV_STATS_SIFS_TLV_V:"); + + ARRAY_TO_STRING(sifs_status, htt_stats_buf->sifs_status, num_elems); + len += HTT_DBG_OUT(buf + len, buf_len - len, "sifs_status = %s\n", + sifs_status); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_tx_pdev_stats_phy_err_tlv_v(const void *tag_buf, + u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_tx_pdev_stats_phy_err_tlv_v *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + char phy_errs[HTT_MAX_STRING_LEN] = {0}; + u16 num_elems = min_t(u16, (tag_len >> 2), HTT_TX_PDEV_MAX_PHY_ERR_STATS); + + len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_TX_PDEV_STATS_PHY_ERR_TLV_V:"); + + ARRAY_TO_STRING(phy_errs, htt_stats_buf->phy_errs, num_elems); + len += HTT_DBG_OUT(buf + len, buf_len - len, "phy_errs = %s\n", phy_errs); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_tx_pdev_stats_sifs_hist_tlv_v(const void *tag_buf, + u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_tx_pdev_stats_sifs_hist_tlv_v *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + char sifs_hist_status[HTT_MAX_STRING_LEN] = {0}; + u16 num_elems = min_t(u16, (tag_len >> 2), HTT_TX_PDEV_MAX_SIFS_BURST_HIST_STATS); + + len += HTT_DBG_OUT(buf + len, buf_len - len, + "HTT_TX_PDEV_STATS_SIFS_HIST_TLV_V:"); + + ARRAY_TO_STRING(sifs_hist_status, htt_stats_buf->sifs_hist_status, num_elems); + len += HTT_DBG_OUT(buf + len, buf_len - len, "sifs_hist_status = %s\n", + sifs_hist_status); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_tx_pdev_stats_tx_ppdu_stats_tlv_v(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_tx_pdev_stats_tx_ppdu_stats_tlv_v *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + + len += HTT_DBG_OUT(buf + len, buf_len - len, + "HTT_TX_PDEV_STATS_TX_PPDU_STATS_TLV_V:"); + + len += HTT_DBG_OUT(buf + len, buf_len - len, "num_data_ppdus_legacy_su = %u", + htt_stats_buf->num_data_ppdus_legacy_su); + + len += HTT_DBG_OUT(buf + len, buf_len - len, "num_data_ppdus_ac_su = %u", + htt_stats_buf->num_data_ppdus_ac_su); + + len += HTT_DBG_OUT(buf + len, buf_len - len, "num_data_ppdus_ax_su = %u", + htt_stats_buf->num_data_ppdus_ax_su); + + len += HTT_DBG_OUT(buf + len, buf_len - len, "num_data_ppdus_ac_su_txbf = %u", + htt_stats_buf->num_data_ppdus_ac_su_txbf); + + len += HTT_DBG_OUT(buf + len, buf_len - len, "num_data_ppdus_ax_su_txbf = %u\n", + htt_stats_buf->num_data_ppdus_ax_su_txbf); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_tx_pdev_stats_tried_mpdu_cnt_hist_tlv_v(const void *tag_buf, + u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_tx_pdev_stats_tried_mpdu_cnt_hist_tlv_v *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + char tried_mpdu_cnt_hist[HTT_MAX_STRING_LEN] = {0}; + u32 num_elements = ((tag_len - sizeof(htt_stats_buf->hist_bin_size)) >> 2); + u32 required_buffer_size = HTT_MAX_PRINT_CHAR_PER_ELEM * num_elements; + + len += HTT_DBG_OUT(buf + len, buf_len - len, + "HTT_TX_PDEV_STATS_TRIED_MPDU_CNT_HIST_TLV_V:"); + len += HTT_DBG_OUT(buf + len, buf_len - len, "TRIED_MPDU_CNT_HIST_BIN_SIZE : %u", + htt_stats_buf->hist_bin_size); + + if (required_buffer_size < HTT_MAX_STRING_LEN) { + ARRAY_TO_STRING(tried_mpdu_cnt_hist, + htt_stats_buf->tried_mpdu_cnt_hist, + num_elements); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tried_mpdu_cnt_hist = %s\n", + tried_mpdu_cnt_hist); + } else { + len += HTT_DBG_OUT(buf + len, buf_len - len, + "INSUFFICIENT PRINT BUFFER\n"); + } + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void htt_print_hw_stats_intr_misc_tlv(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_hw_stats_intr_misc_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + char hw_intr_name[HTT_STATS_MAX_HW_INTR_NAME_LEN + 1] = {0}; + + len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_HW_STATS_INTR_MISC_TLV:"); + memcpy(hw_intr_name, &(htt_stats_buf->hw_intr_name[0]), + HTT_STATS_MAX_HW_INTR_NAME_LEN); + len += HTT_DBG_OUT(buf + len, buf_len - len, "hw_intr_name = %s ", hw_intr_name); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mask = %u", + htt_stats_buf->mask); + len += HTT_DBG_OUT(buf + len, buf_len - len, "count = %u\n", + htt_stats_buf->count); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_hw_stats_wd_timeout_tlv(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_hw_stats_wd_timeout_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + char hw_module_name[HTT_STATS_MAX_HW_MODULE_NAME_LEN + 1] = {0}; + + len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_HW_STATS_WD_TIMEOUT_TLV:"); + memcpy(hw_module_name, &(htt_stats_buf->hw_module_name[0]), + HTT_STATS_MAX_HW_MODULE_NAME_LEN); + len += HTT_DBG_OUT(buf + len, buf_len - len, "hw_module_name = %s ", + hw_module_name); + len += HTT_DBG_OUT(buf + len, buf_len - len, "count = %u", + htt_stats_buf->count); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void htt_print_hw_stats_pdev_errs_tlv(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_hw_stats_pdev_errs_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + + len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_HW_STATS_PDEV_ERRS_TLV:"); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mac_id = %u", + htt_stats_buf->mac_id__word & 0xFF); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tx_abort = %u", + htt_stats_buf->tx_abort); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tx_abort_fail_count = %u", + htt_stats_buf->tx_abort_fail_count); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_abort = %u", + htt_stats_buf->rx_abort); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_abort_fail_count = %u", + htt_stats_buf->rx_abort_fail_count); + len += HTT_DBG_OUT(buf + len, buf_len - len, "warm_reset = %u", + htt_stats_buf->warm_reset); + len += HTT_DBG_OUT(buf + len, buf_len - len, "cold_reset = %u", + htt_stats_buf->cold_reset); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tx_flush = %u", + htt_stats_buf->tx_flush); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tx_glb_reset = %u", + htt_stats_buf->tx_glb_reset); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tx_txq_reset = %u", + htt_stats_buf->tx_txq_reset); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_timeout_reset = %u\n", + htt_stats_buf->rx_timeout_reset); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void htt_print_msdu_flow_stats_tlv(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_msdu_flow_stats_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + + len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_MSDU_FLOW_STATS_TLV:"); + len += HTT_DBG_OUT(buf + len, buf_len - len, "last_update_timestamp = %u", + htt_stats_buf->last_update_timestamp); + len += HTT_DBG_OUT(buf + len, buf_len - len, "last_add_timestamp = %u", + htt_stats_buf->last_add_timestamp); + len += HTT_DBG_OUT(buf + len, buf_len - len, "last_remove_timestamp = %u", + htt_stats_buf->last_remove_timestamp); + len += HTT_DBG_OUT(buf + len, buf_len - len, "total_processed_msdu_count = %u", + htt_stats_buf->total_processed_msdu_count); + len += HTT_DBG_OUT(buf + len, buf_len - len, "cur_msdu_count_in_flowq = %u", + htt_stats_buf->cur_msdu_count_in_flowq); + len += HTT_DBG_OUT(buf + len, buf_len - len, "sw_peer_id = %u", + htt_stats_buf->sw_peer_id); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tx_flow_no = %u", + htt_stats_buf->tx_flow_no__tid_num__drop_rule & 0xFFFF); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tid_num = %u", + (htt_stats_buf->tx_flow_no__tid_num__drop_rule & 0xF0000) >> + 16); + len += HTT_DBG_OUT(buf + len, buf_len - len, "drop_rule = %u", + (htt_stats_buf->tx_flow_no__tid_num__drop_rule & 0x100000) >> + 20); + len += HTT_DBG_OUT(buf + len, buf_len - len, "last_cycle_enqueue_count = %u", + htt_stats_buf->last_cycle_enqueue_count); + len += HTT_DBG_OUT(buf + len, buf_len - len, "last_cycle_dequeue_count = %u", + htt_stats_buf->last_cycle_dequeue_count); + len += HTT_DBG_OUT(buf + len, buf_len - len, "last_cycle_drop_count = %u", + htt_stats_buf->last_cycle_drop_count); + len += HTT_DBG_OUT(buf + len, buf_len - len, "current_drop_th = %u\n", + htt_stats_buf->current_drop_th); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void htt_print_tx_tid_stats_tlv(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_tx_tid_stats_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + char tid_name[MAX_HTT_TID_NAME + 1] = {0}; + + len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_TX_TID_STATS_TLV:"); + memcpy(tid_name, &(htt_stats_buf->tid_name[0]), MAX_HTT_TID_NAME); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tid_name = %s ", tid_name); + len += HTT_DBG_OUT(buf + len, buf_len - len, "sw_peer_id = %u", + htt_stats_buf->sw_peer_id__tid_num & 0xFFFF); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tid_num = %u", + (htt_stats_buf->sw_peer_id__tid_num & 0xFFFF0000) >> 16); + len += HTT_DBG_OUT(buf + len, buf_len - len, "num_sched_pending = %u", + htt_stats_buf->num_sched_pending__num_ppdu_in_hwq & 0xFF); + len += HTT_DBG_OUT(buf + len, buf_len - len, "num_ppdu_in_hwq = %u", + (htt_stats_buf->num_sched_pending__num_ppdu_in_hwq & + 0xFF00) >> 8); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tid_flags = 0x%x", + htt_stats_buf->tid_flags); + len += HTT_DBG_OUT(buf + len, buf_len - len, "hw_queued = %u", + htt_stats_buf->hw_queued); + len += HTT_DBG_OUT(buf + len, buf_len - len, "hw_reaped = %u", + htt_stats_buf->hw_reaped); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mpdus_hw_filter = %u", + htt_stats_buf->mpdus_hw_filter); + len += HTT_DBG_OUT(buf + len, buf_len - len, "qdepth_bytes = %u", + htt_stats_buf->qdepth_bytes); + len += HTT_DBG_OUT(buf + len, buf_len - len, "qdepth_num_msdu = %u", + htt_stats_buf->qdepth_num_msdu); + len += HTT_DBG_OUT(buf + len, buf_len - len, "qdepth_num_mpdu = %u", + htt_stats_buf->qdepth_num_mpdu); + len += HTT_DBG_OUT(buf + len, buf_len - len, "last_scheduled_tsmp = %u", + htt_stats_buf->last_scheduled_tsmp); + len += HTT_DBG_OUT(buf + len, buf_len - len, "pause_module_id = %u", + htt_stats_buf->pause_module_id); + len += HTT_DBG_OUT(buf + len, buf_len - len, "block_module_id = %u\n", + htt_stats_buf->block_module_id); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void htt_print_tx_tid_stats_v1_tlv(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_tx_tid_stats_v1_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + char tid_name[MAX_HTT_TID_NAME + 1] = {0}; + + len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_TX_TID_STATS_V1_TLV:"); + memcpy(tid_name, &(htt_stats_buf->tid_name[0]), MAX_HTT_TID_NAME); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tid_name = %s ", tid_name); + len += HTT_DBG_OUT(buf + len, buf_len - len, "sw_peer_id = %u", + htt_stats_buf->sw_peer_id__tid_num & 0xFFFF); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tid_num = %u", + (htt_stats_buf->sw_peer_id__tid_num & 0xFFFF0000) >> 16); + len += HTT_DBG_OUT(buf + len, buf_len - len, "num_sched_pending = %u", + htt_stats_buf->num_sched_pending__num_ppdu_in_hwq & 0xFF); + len += HTT_DBG_OUT(buf + len, buf_len - len, "num_ppdu_in_hwq = %u", + (htt_stats_buf->num_sched_pending__num_ppdu_in_hwq & + 0xFF00) >> 8); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tid_flags = 0x%x", + htt_stats_buf->tid_flags); + len += HTT_DBG_OUT(buf + len, buf_len - len, "max_qdepth_bytes = %u", + htt_stats_buf->max_qdepth_bytes); + len += HTT_DBG_OUT(buf + len, buf_len - len, "max_qdepth_n_msdus = %u", + htt_stats_buf->max_qdepth_n_msdus); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rsvd = %u", + htt_stats_buf->rsvd); + len += HTT_DBG_OUT(buf + len, buf_len - len, "qdepth_bytes = %u", + htt_stats_buf->qdepth_bytes); + len += HTT_DBG_OUT(buf + len, buf_len - len, "qdepth_num_msdu = %u", + htt_stats_buf->qdepth_num_msdu); + len += HTT_DBG_OUT(buf + len, buf_len - len, "qdepth_num_mpdu = %u", + htt_stats_buf->qdepth_num_mpdu); + len += HTT_DBG_OUT(buf + len, buf_len - len, "last_scheduled_tsmp = %u", + htt_stats_buf->last_scheduled_tsmp); + len += HTT_DBG_OUT(buf + len, buf_len - len, "pause_module_id = %u", + htt_stats_buf->pause_module_id); + len += HTT_DBG_OUT(buf + len, buf_len - len, "block_module_id = %u", + htt_stats_buf->block_module_id); + len += HTT_DBG_OUT(buf + len, buf_len - len, "allow_n_flags = 0x%x", + htt_stats_buf->allow_n_flags); + len += HTT_DBG_OUT(buf + len, buf_len - len, "sendn_frms_allowed = %u\n", + htt_stats_buf->sendn_frms_allowed); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void htt_print_rx_tid_stats_tlv(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_rx_tid_stats_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + char tid_name[MAX_HTT_TID_NAME + 1] = {0}; + + len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_RX_TID_STATS_TLV:"); + len += HTT_DBG_OUT(buf + len, buf_len - len, "sw_peer_id = %u", + htt_stats_buf->sw_peer_id__tid_num & 0xFFFF); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tid_num = %u", + (htt_stats_buf->sw_peer_id__tid_num & 0xFFFF0000) >> 16); + memcpy(tid_name, &(htt_stats_buf->tid_name[0]), MAX_HTT_TID_NAME); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tid_name = %s ", tid_name); + len += HTT_DBG_OUT(buf + len, buf_len - len, "dup_in_reorder = %u", + htt_stats_buf->dup_in_reorder); + len += HTT_DBG_OUT(buf + len, buf_len - len, "dup_past_outside_window = %u", + htt_stats_buf->dup_past_outside_window); + len += HTT_DBG_OUT(buf + len, buf_len - len, "dup_past_within_window = %u", + htt_stats_buf->dup_past_within_window); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rxdesc_err_decrypt = %u\n", + htt_stats_buf->rxdesc_err_decrypt); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void htt_print_counter_tlv(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_counter_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + char counter_name[HTT_MAX_STRING_LEN] = {0}; + + len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_COUNTER_TLV:"); + + ARRAY_TO_STRING(counter_name, + htt_stats_buf->counter_name, + HTT_MAX_COUNTER_NAME); + len += HTT_DBG_OUT(buf + len, buf_len - len, "counter_name = %s ", counter_name); + len += HTT_DBG_OUT(buf + len, buf_len - len, "count = %u\n", + htt_stats_buf->count); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void htt_print_peer_stats_cmn_tlv(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_peer_stats_cmn_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + + len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_PEER_STATS_CMN_TLV:"); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ppdu_cnt = %u", + htt_stats_buf->ppdu_cnt); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mpdu_cnt = %u", + htt_stats_buf->mpdu_cnt); + len += HTT_DBG_OUT(buf + len, buf_len - len, "msdu_cnt = %u", + htt_stats_buf->msdu_cnt); + len += HTT_DBG_OUT(buf + len, buf_len - len, "pause_bitmap = %u", + htt_stats_buf->pause_bitmap); + len += HTT_DBG_OUT(buf + len, buf_len - len, "block_bitmap = %u", + htt_stats_buf->block_bitmap); + len += HTT_DBG_OUT(buf + len, buf_len - len, "last_rssi = %d", + htt_stats_buf->rssi); + len += HTT_DBG_OUT(buf + len, buf_len - len, "enqueued_count = %llu", + htt_stats_buf->peer_enqueued_count_low | + ((u64)htt_stats_buf->peer_enqueued_count_high << 32)); + len += HTT_DBG_OUT(buf + len, buf_len - len, "dequeued_count = %llu", + htt_stats_buf->peer_dequeued_count_low | + ((u64)htt_stats_buf->peer_dequeued_count_high << 32)); + len += HTT_DBG_OUT(buf + len, buf_len - len, "dropped_count = %llu", + htt_stats_buf->peer_dropped_count_low | + ((u64)htt_stats_buf->peer_dropped_count_high << 32)); + len += HTT_DBG_OUT(buf + len, buf_len - len, "transmitted_ppdu_bytes = %llu", + htt_stats_buf->ppdu_transmitted_bytes_low | + ((u64)htt_stats_buf->ppdu_transmitted_bytes_high << 32)); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ttl_removed_count = %u", + htt_stats_buf->peer_ttl_removed_count); + len += HTT_DBG_OUT(buf + len, buf_len - len, "inactive_time = %u\n", + htt_stats_buf->inactive_time); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void htt_print_peer_details_tlv(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_peer_details_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + + len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_PEER_DETAILS_TLV:"); + len += HTT_DBG_OUT(buf + len, buf_len - len, "peer_type = %u", + htt_stats_buf->peer_type); + len += HTT_DBG_OUT(buf + len, buf_len - len, "sw_peer_id = %u", + htt_stats_buf->sw_peer_id); + len += HTT_DBG_OUT(buf + len, buf_len - len, "vdev_id = %u", + htt_stats_buf->vdev_pdev_ast_idx & 0xFF); + len += HTT_DBG_OUT(buf + len, buf_len - len, "pdev_id = %u", + (htt_stats_buf->vdev_pdev_ast_idx & 0xFF00) >> 8); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ast_idx = %u", + (htt_stats_buf->vdev_pdev_ast_idx & 0xFFFF0000) >> 16); + len += HTT_DBG_OUT(buf + len, buf_len - len, + "mac_addr = %02x:%02x:%02x:%02x:%02x:%02x", + htt_stats_buf->mac_addr.mac_addr_l32 & 0xFF, + (htt_stats_buf->mac_addr.mac_addr_l32 & 0xFF00) >> 8, + (htt_stats_buf->mac_addr.mac_addr_l32 & 0xFF0000) >> 16, + (htt_stats_buf->mac_addr.mac_addr_l32 & 0xFF000000) >> 24, + (htt_stats_buf->mac_addr.mac_addr_h16 & 0xFF), + (htt_stats_buf->mac_addr.mac_addr_h16 & 0xFF00) >> 8); + len += HTT_DBG_OUT(buf + len, buf_len - len, "peer_flags = 0x%x", + htt_stats_buf->peer_flags); + len += HTT_DBG_OUT(buf + len, buf_len - len, "qpeer_flags = 0x%x\n", + htt_stats_buf->qpeer_flags); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void htt_print_tx_peer_rate_stats_tlv(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_tx_peer_rate_stats_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + char str_buf[HTT_MAX_STRING_LEN] = {0}; + char *tx_gi[HTT_TX_PEER_STATS_NUM_GI_COUNTERS]; + u8 j; + + for (j = 0; j < HTT_TX_PEER_STATS_NUM_GI_COUNTERS; j++) + tx_gi[j] = kmalloc(HTT_MAX_STRING_LEN, GFP_ATOMIC); + + len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_TX_PEER_RATE_STATS_TLV:"); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tx_ldpc = %u", + htt_stats_buf->tx_ldpc); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rts_cnt = %u", + htt_stats_buf->rts_cnt); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ack_rssi = %u", + htt_stats_buf->ack_rssi); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + ARRAY_TO_STRING(str_buf, htt_stats_buf->tx_mcs, + HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tx_mcs = %s ", str_buf); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + ARRAY_TO_STRING(str_buf, htt_stats_buf->tx_su_mcs, + HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tx_su_mcs = %s ", str_buf); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + ARRAY_TO_STRING(str_buf, htt_stats_buf->tx_mu_mcs, + HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tx_mu_mcs = %s ", str_buf); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + ARRAY_TO_STRING(str_buf, + htt_stats_buf->tx_nss, + HTT_TX_PDEV_STATS_NUM_SPATIAL_STREAMS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tx_nss = %s ", str_buf); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + ARRAY_TO_STRING(str_buf, + htt_stats_buf->tx_bw, + HTT_TX_PDEV_STATS_NUM_BW_COUNTERS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tx_bw = %s ", str_buf); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + ARRAY_TO_STRING(str_buf, htt_stats_buf->tx_stbc, + HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tx_stbc = %s ", str_buf); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + ARRAY_TO_STRING(str_buf, htt_stats_buf->tx_pream, + HTT_TX_PDEV_STATS_NUM_PREAMBLE_TYPES); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tx_pream = %s ", str_buf); + + for (j = 0; j < HTT_TX_PEER_STATS_NUM_GI_COUNTERS; j++) { + ARRAY_TO_STRING(tx_gi[j], + htt_stats_buf->tx_gi[j], + HTT_TX_PEER_STATS_NUM_MCS_COUNTERS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tx_gi[%u] = %s ", + j, tx_gi[j]); + } + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + ARRAY_TO_STRING(str_buf, + htt_stats_buf->tx_dcm, + HTT_TX_PDEV_STATS_NUM_DCM_COUNTERS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tx_dcm = %s\n", str_buf); + + for (j = 0; j < HTT_TX_PEER_STATS_NUM_GI_COUNTERS; j++) + kfree(tx_gi[j]); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void htt_print_rx_peer_rate_stats_tlv(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_rx_peer_rate_stats_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + u8 j; + char *rssi_chain[HTT_RX_PEER_STATS_NUM_SPATIAL_STREAMS]; + char *rx_gi[HTT_RX_PEER_STATS_NUM_GI_COUNTERS]; + char str_buf[HTT_MAX_STRING_LEN] = {0}; + + for (j = 0; j < HTT_RX_PEER_STATS_NUM_SPATIAL_STREAMS; j++) + rssi_chain[j] = kmalloc(HTT_MAX_STRING_LEN, GFP_ATOMIC); + + for (j = 0; j < HTT_RX_PEER_STATS_NUM_GI_COUNTERS; j++) + rx_gi[j] = kmalloc(HTT_MAX_STRING_LEN, GFP_ATOMIC); + + len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_RX_PEER_RATE_STATS_TLV:"); + len += HTT_DBG_OUT(buf + len, buf_len - len, "nsts = %u", + htt_stats_buf->nsts); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_ldpc = %u", + htt_stats_buf->rx_ldpc); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rts_cnt = %u", + htt_stats_buf->rts_cnt); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rssi_mgmt = %u", + htt_stats_buf->rssi_mgmt); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rssi_data = %u", + htt_stats_buf->rssi_data); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rssi_comb = %u", + htt_stats_buf->rssi_comb); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + ARRAY_TO_STRING(str_buf, htt_stats_buf->rx_mcs, + HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_mcs = %s ", str_buf); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + ARRAY_TO_STRING(str_buf, htt_stats_buf->rx_nss, + HTT_RX_PDEV_STATS_NUM_SPATIAL_STREAMS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_nss = %s ", str_buf); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + ARRAY_TO_STRING(str_buf, htt_stats_buf->rx_dcm, + HTT_RX_PDEV_STATS_NUM_DCM_COUNTERS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_dcm = %s ", str_buf); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + ARRAY_TO_STRING(str_buf, htt_stats_buf->rx_stbc, + HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_stbc = %s ", str_buf); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + ARRAY_TO_STRING(str_buf, htt_stats_buf->rx_bw, + HTT_RX_PDEV_STATS_NUM_BW_COUNTERS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_bw = %s ", str_buf); + + for (j = 0; j < HTT_RX_PEER_STATS_NUM_SPATIAL_STREAMS; j++) { + ARRAY_TO_STRING(rssi_chain[j], htt_stats_buf->rssi_chain[j], + HTT_RX_PEER_STATS_NUM_BW_COUNTERS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rssi_chain[%u] = %s ", + j, rssi_chain[j]); + } + + for (j = 0; j < HTT_RX_PEER_STATS_NUM_GI_COUNTERS; j++) { + ARRAY_TO_STRING(rx_gi[j], htt_stats_buf->rx_gi[j], + HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_gi[%u] = %s ", + j, rx_gi[j]); + } + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + ARRAY_TO_STRING(str_buf, htt_stats_buf->rx_pream, + HTT_RX_PDEV_STATS_NUM_PREAMBLE_TYPES); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_pream = %s\n", str_buf); + + for (j = 0; j < HTT_RX_PEER_STATS_NUM_SPATIAL_STREAMS; j++) + kfree(rssi_chain[j]); + + for (j = 0; j < HTT_RX_PEER_STATS_NUM_GI_COUNTERS; j++) + kfree(rx_gi[j]); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_tx_hwq_mu_mimo_sch_stats_tlv(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_tx_hwq_mu_mimo_sch_stats_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + + len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_TX_HWQ_MU_MIMO_SCH_STATS_TLV:"); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mu_mimo_sch_posted = %u", + htt_stats_buf->mu_mimo_sch_posted); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mu_mimo_sch_failed = %u", + htt_stats_buf->mu_mimo_sch_failed); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mu_mimo_ppdu_posted = %u\n", + htt_stats_buf->mu_mimo_ppdu_posted); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_tx_hwq_mu_mimo_mpdu_stats_tlv(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_tx_hwq_mu_mimo_mpdu_stats_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + + len += HTT_DBG_OUT(buf + len, buf_len - len, + "HTT_TX_HWQ_MU_MIMO_MPDU_STATS_TLV:"); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mu_mimo_mpdus_queued_usr = %u", + htt_stats_buf->mu_mimo_mpdus_queued_usr); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mu_mimo_mpdus_tried_usr = %u", + htt_stats_buf->mu_mimo_mpdus_tried_usr); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mu_mimo_mpdus_failed_usr = %u", + htt_stats_buf->mu_mimo_mpdus_failed_usr); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mu_mimo_mpdus_requeued_usr = %u", + htt_stats_buf->mu_mimo_mpdus_requeued_usr); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mu_mimo_err_no_ba_usr = %u", + htt_stats_buf->mu_mimo_err_no_ba_usr); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mu_mimo_mpdu_underrun_usr = %u", + htt_stats_buf->mu_mimo_mpdu_underrun_usr); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mu_mimo_ampdu_underrun_usr = %u\n", + htt_stats_buf->mu_mimo_ampdu_underrun_usr); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_tx_hwq_mu_mimo_cmn_stats_tlv(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_tx_hwq_mu_mimo_cmn_stats_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + + len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_TX_HWQ_MU_MIMO_CMN_STATS_TLV:"); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mac_id = %u", + htt_stats_buf->mac_id__hwq_id__word & 0xFF); + len += HTT_DBG_OUT(buf + len, buf_len - len, "hwq_id = %u\n", + (htt_stats_buf->mac_id__hwq_id__word & 0xFF00) >> 8); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_tx_hwq_stats_cmn_tlv(const void *tag_buf, struct debug_htt_stats_req *stats_req) +{ + const struct htt_tx_hwq_stats_cmn_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + + /* TODO: HKDBG */ + len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_TX_HWQ_STATS_CMN_TLV:"); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mac_id = %u", + htt_stats_buf->mac_id__hwq_id__word & 0xFF); + len += HTT_DBG_OUT(buf + len, buf_len - len, "hwq_id = %u", + (htt_stats_buf->mac_id__hwq_id__word & 0xFF00) >> 8); + len += HTT_DBG_OUT(buf + len, buf_len - len, "xretry = %u", + htt_stats_buf->xretry); + len += HTT_DBG_OUT(buf + len, buf_len - len, "underrun_cnt = %u", + htt_stats_buf->underrun_cnt); + len += HTT_DBG_OUT(buf + len, buf_len - len, "flush_cnt = %u", + htt_stats_buf->flush_cnt); + len += HTT_DBG_OUT(buf + len, buf_len - len, "filt_cnt = %u", + htt_stats_buf->filt_cnt); + len += HTT_DBG_OUT(buf + len, buf_len - len, "null_mpdu_bmap = %u", + htt_stats_buf->null_mpdu_bmap); + len += HTT_DBG_OUT(buf + len, buf_len - len, "user_ack_failure = %u", + htt_stats_buf->user_ack_failure); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ack_tlv_proc = %u", + htt_stats_buf->ack_tlv_proc); + len += HTT_DBG_OUT(buf + len, buf_len - len, "sched_id_proc = %u", + htt_stats_buf->sched_id_proc); + len += HTT_DBG_OUT(buf + len, buf_len - len, "null_mpdu_tx_count = %u", + htt_stats_buf->null_mpdu_tx_count); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mpdu_bmap_not_recvd = %u", + htt_stats_buf->mpdu_bmap_not_recvd); + len += HTT_DBG_OUT(buf + len, buf_len - len, "num_bar = %u", + htt_stats_buf->num_bar); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rts = %u", + htt_stats_buf->rts); + len += HTT_DBG_OUT(buf + len, buf_len - len, "cts2self = %u", + htt_stats_buf->cts2self); + len += HTT_DBG_OUT(buf + len, buf_len - len, "qos_null = %u", + htt_stats_buf->qos_null); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mpdu_tried_cnt = %u", + htt_stats_buf->mpdu_tried_cnt); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mpdu_queued_cnt = %u", + htt_stats_buf->mpdu_queued_cnt); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mpdu_ack_fail_cnt = %u", + htt_stats_buf->mpdu_ack_fail_cnt); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mpdu_filt_cnt = %u", + htt_stats_buf->mpdu_filt_cnt); + len += HTT_DBG_OUT(buf + len, buf_len - len, "false_mpdu_ack_count = %u", + htt_stats_buf->false_mpdu_ack_count); + len += HTT_DBG_OUT(buf + len, buf_len - len, "txq_timeout = %u\n", + htt_stats_buf->txq_timeout); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_tx_hwq_difs_latency_stats_tlv_v(const void *tag_buf, + u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_tx_hwq_difs_latency_stats_tlv_v *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + u16 data_len = min_t(u16, (tag_len >> 2), HTT_TX_HWQ_MAX_DIFS_LATENCY_BINS); + char difs_latency_hist[HTT_MAX_STRING_LEN] = {0}; + + len += HTT_DBG_OUT(buf + len, buf_len - len, + "HTT_TX_HWQ_DIFS_LATENCY_STATS_TLV_V:"); + len += HTT_DBG_OUT(buf + len, buf_len - len, "hist_intvl = %u", + htt_stats_buf->hist_intvl); + + ARRAY_TO_STRING(difs_latency_hist, htt_stats_buf->difs_latency_hist, + data_len); + len += HTT_DBG_OUT(buf + len, buf_len - len, "difs_latency_hist = %s\n", + difs_latency_hist); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_tx_hwq_cmd_result_stats_tlv_v(const void *tag_buf, + u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_tx_hwq_cmd_result_stats_tlv_v *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + u16 data_len; + char cmd_result[HTT_MAX_STRING_LEN] = {0}; + + data_len = min_t(u16, (tag_len >> 2), HTT_TX_HWQ_MAX_CMD_RESULT_STATS); + + len += HTT_DBG_OUT(buf + len, buf_len - len, + "HTT_TX_HWQ_CMD_RESULT_STATS_TLV_V:"); + + ARRAY_TO_STRING(cmd_result, htt_stats_buf->cmd_result, data_len); + + len += HTT_DBG_OUT(buf + len, buf_len - len, "cmd_result = %s\n", cmd_result); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_tx_hwq_cmd_stall_stats_tlv_v(const void *tag_buf, + u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_tx_hwq_cmd_stall_stats_tlv_v *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + u16 num_elems; + char cmd_stall_status[HTT_MAX_STRING_LEN] = {0}; + + num_elems = min_t(u16, (tag_len >> 2), HTT_TX_HWQ_MAX_CMD_STALL_STATS); + + len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_TX_HWQ_CMD_STALL_STATS_TLV_V:"); + + ARRAY_TO_STRING(cmd_stall_status, htt_stats_buf->cmd_stall_status, num_elems); + len += HTT_DBG_OUT(buf + len, buf_len - len, "cmd_stall_status = %s\n", + cmd_stall_status); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_tx_hwq_fes_result_stats_tlv_v(const void *tag_buf, + u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_tx_hwq_fes_result_stats_tlv_v *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + u16 num_elems; + char fes_result[HTT_MAX_STRING_LEN] = {0}; + + num_elems = min_t(u16, (tag_len >> 2), HTT_TX_HWQ_MAX_FES_RESULT_STATS); + + len += HTT_DBG_OUT(buf + len, buf_len - len, + "HTT_TX_HWQ_FES_RESULT_STATS_TLV_V:"); + + ARRAY_TO_STRING(fes_result, htt_stats_buf->fes_result, num_elems); + len += HTT_DBG_OUT(buf + len, buf_len - len, "fes_result = %s\n", fes_result); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_tx_hwq_tried_mpdu_cnt_hist_tlv_v(const void *tag_buf, + u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_tx_hwq_tried_mpdu_cnt_hist_tlv_v *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + char tried_mpdu_cnt_hist[HTT_MAX_STRING_LEN] = {0}; + u32 num_elements = ((tag_len - + sizeof(htt_stats_buf->hist_bin_size)) >> 2); + u32 required_buffer_size = HTT_MAX_PRINT_CHAR_PER_ELEM * num_elements; + + len += HTT_DBG_OUT(buf + len, buf_len - len, + "HTT_TX_HWQ_TRIED_MPDU_CNT_HIST_TLV_V:"); + len += HTT_DBG_OUT(buf + len, buf_len - len, "TRIED_MPDU_CNT_HIST_BIN_SIZE : %u", + htt_stats_buf->hist_bin_size); + + if (required_buffer_size < HTT_MAX_STRING_LEN) { + ARRAY_TO_STRING(tried_mpdu_cnt_hist, + htt_stats_buf->tried_mpdu_cnt_hist, + num_elements); + len += HTT_DBG_OUT(buf + len, buf_len - len, + "tried_mpdu_cnt_hist = %s\n", + tried_mpdu_cnt_hist); + } else { + len += HTT_DBG_OUT(buf + len, buf_len - len, + "INSUFFICIENT PRINT BUFFER "); + } + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_tx_hwq_txop_used_cnt_hist_tlv_v(const void *tag_buf, + u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_tx_hwq_txop_used_cnt_hist_tlv_v *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + char txop_used_cnt_hist[HTT_MAX_STRING_LEN] = {0}; + u32 num_elements = tag_len >> 2; + u32 required_buffer_size = HTT_MAX_PRINT_CHAR_PER_ELEM * num_elements; + + len += HTT_DBG_OUT(buf + len, buf_len - len, + "HTT_TX_HWQ_TXOP_USED_CNT_HIST_TLV_V:"); + + if (required_buffer_size < HTT_MAX_STRING_LEN) { + ARRAY_TO_STRING(txop_used_cnt_hist, + htt_stats_buf->txop_used_cnt_hist, + num_elements); + len += HTT_DBG_OUT(buf + len, buf_len - len, "txop_used_cnt_hist = %s\n", + txop_used_cnt_hist); + } else { + len += HTT_DBG_OUT(buf + len, buf_len - len, + "INSUFFICIENT PRINT BUFFER "); + } + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void htt_print_tx_sounding_stats_tlv(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + s32 i; + const struct htt_tx_sounding_stats_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + const u32 *cbf_20 = htt_stats_buf->cbf_20; + const u32 *cbf_40 = htt_stats_buf->cbf_40; + const u32 *cbf_80 = htt_stats_buf->cbf_80; + const u32 *cbf_160 = htt_stats_buf->cbf_160; + + if (htt_stats_buf->tx_sounding_mode == HTT_TX_AC_SOUNDING_MODE) { + len += HTT_DBG_OUT(buf + len, buf_len - len, + "\nHTT_TX_AC_SOUNDING_STATS_TLV:\n"); + len += HTT_DBG_OUT(buf + len, buf_len - len, + "ac_cbf_20 = IBF : %u, SU_SIFS : %u, SU_RBO : %u, MU_SIFS : %u, MU_RBO : %u ", + cbf_20[HTT_IMPLICIT_TXBF_STEER_STATS], + cbf_20[HTT_EXPLICIT_TXBF_SU_SIFS_STEER_STATS], + cbf_20[HTT_EXPLICIT_TXBF_SU_RBO_STEER_STATS], + cbf_20[HTT_EXPLICIT_TXBF_MU_SIFS_STEER_STATS], + cbf_20[HTT_EXPLICIT_TXBF_MU_RBO_STEER_STATS]); + len += HTT_DBG_OUT(buf + len, buf_len - len, + "ac_cbf_40 = IBF : %u, SU_SIFS : %u, SU_RBO : %u, MU_SIFS : %u, MU_RBO : %u", + cbf_40[HTT_IMPLICIT_TXBF_STEER_STATS], + cbf_40[HTT_EXPLICIT_TXBF_SU_SIFS_STEER_STATS], + cbf_40[HTT_EXPLICIT_TXBF_SU_RBO_STEER_STATS], + cbf_40[HTT_EXPLICIT_TXBF_MU_SIFS_STEER_STATS], + cbf_40[HTT_EXPLICIT_TXBF_MU_RBO_STEER_STATS]); + len += HTT_DBG_OUT(buf + len, buf_len - len, + "ac_cbf_80 = IBF : %u, SU_SIFS : %u, SU_RBO : %u, MU_SIFS : %u, MU_RBO : %u", + cbf_80[HTT_IMPLICIT_TXBF_STEER_STATS], + cbf_80[HTT_EXPLICIT_TXBF_SU_SIFS_STEER_STATS], + cbf_80[HTT_EXPLICIT_TXBF_SU_RBO_STEER_STATS], + cbf_80[HTT_EXPLICIT_TXBF_MU_SIFS_STEER_STATS], + cbf_80[HTT_EXPLICIT_TXBF_MU_RBO_STEER_STATS]); + len += HTT_DBG_OUT(buf + len, buf_len - len, + "ac_cbf_160 = IBF : %u, SU_SIFS : %u, SU_RBO : %u, MU_SIFS : %u, MU_RBO : %u", + cbf_160[HTT_IMPLICIT_TXBF_STEER_STATS], + cbf_160[HTT_EXPLICIT_TXBF_SU_SIFS_STEER_STATS], + cbf_160[HTT_EXPLICIT_TXBF_SU_RBO_STEER_STATS], + cbf_160[HTT_EXPLICIT_TXBF_MU_SIFS_STEER_STATS], + cbf_160[HTT_EXPLICIT_TXBF_MU_RBO_STEER_STATS]); + + for (i = 0; i < HTT_TX_PDEV_STATS_NUM_AC_MUMIMO_USER_STATS; i++) { + len += HTT_DBG_OUT(buf + len, buf_len - len, + "Sounding User %u = 20MHz: %u, 40MHz : %u, 80MHz: %u, 160MHz: %u ", + i, + htt_stats_buf->sounding[0], + htt_stats_buf->sounding[1], + htt_stats_buf->sounding[2], + htt_stats_buf->sounding[3]); + } + } else if (htt_stats_buf->tx_sounding_mode == HTT_TX_AX_SOUNDING_MODE) { + len += HTT_DBG_OUT(buf + len, buf_len - len, + "\nHTT_TX_AX_SOUNDING_STATS_TLV:\n"); + len += HTT_DBG_OUT(buf + len, buf_len - len, + "ax_cbf_20 = IBF : %u, SU_SIFS : %u, SU_RBO : %u, MU_SIFS : %u, MU_RBO : %u ", + cbf_20[HTT_IMPLICIT_TXBF_STEER_STATS], + cbf_20[HTT_EXPLICIT_TXBF_SU_SIFS_STEER_STATS], + cbf_20[HTT_EXPLICIT_TXBF_SU_RBO_STEER_STATS], + cbf_20[HTT_EXPLICIT_TXBF_MU_SIFS_STEER_STATS], + cbf_20[HTT_EXPLICIT_TXBF_MU_RBO_STEER_STATS]); + len += HTT_DBG_OUT(buf + len, buf_len - len, + "ax_cbf_40 = IBF : %u, SU_SIFS : %u, SU_RBO : %u, MU_SIFS : %u, MU_RBO : %u", + cbf_40[HTT_IMPLICIT_TXBF_STEER_STATS], + cbf_40[HTT_EXPLICIT_TXBF_SU_SIFS_STEER_STATS], + cbf_40[HTT_EXPLICIT_TXBF_SU_RBO_STEER_STATS], + cbf_40[HTT_EXPLICIT_TXBF_MU_SIFS_STEER_STATS], + cbf_40[HTT_EXPLICIT_TXBF_MU_RBO_STEER_STATS]); + len += HTT_DBG_OUT(buf + len, buf_len - len, + "ax_cbf_80 = IBF : %u, SU_SIFS : %u, SU_RBO : %u, MU_SIFS : %u, MU_RBO : %u", + cbf_80[HTT_IMPLICIT_TXBF_STEER_STATS], + cbf_80[HTT_EXPLICIT_TXBF_SU_SIFS_STEER_STATS], + cbf_80[HTT_EXPLICIT_TXBF_SU_RBO_STEER_STATS], + cbf_80[HTT_EXPLICIT_TXBF_MU_SIFS_STEER_STATS], + cbf_80[HTT_EXPLICIT_TXBF_MU_RBO_STEER_STATS]); + len += HTT_DBG_OUT(buf + len, buf_len - len, + "ax_cbf_160 = IBF : %u, SU_SIFS : %u, SU_RBO : %u, MU_SIFS : %u, MU_RBO : %u", + cbf_160[HTT_IMPLICIT_TXBF_STEER_STATS], + cbf_160[HTT_EXPLICIT_TXBF_SU_SIFS_STEER_STATS], + cbf_160[HTT_EXPLICIT_TXBF_SU_RBO_STEER_STATS], + cbf_160[HTT_EXPLICIT_TXBF_MU_SIFS_STEER_STATS], + cbf_160[HTT_EXPLICIT_TXBF_MU_RBO_STEER_STATS]); + + for (i = 0; i < HTT_TX_PDEV_STATS_NUM_AX_MUMIMO_USER_STATS; i++) { + len += HTT_DBG_OUT(buf + len, buf_len - len, + "Sounding User %u = 20MHz: %u, 40MHz : %u, 80MHz: %u, 160MHz: %u ", + i, + htt_stats_buf->sounding[0], + htt_stats_buf->sounding[1], + htt_stats_buf->sounding[2], + htt_stats_buf->sounding[3]); + } + } + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_tx_selfgen_cmn_stats_tlv(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_tx_selfgen_cmn_stats_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + + len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_TX_SELFGEN_CMN_STATS_TLV:"); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mac_id = %u", + htt_stats_buf->mac_id__word & 0xFF); + len += HTT_DBG_OUT(buf + len, buf_len - len, "su_bar = %u", + htt_stats_buf->su_bar); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rts = %u", + htt_stats_buf->rts); + len += HTT_DBG_OUT(buf + len, buf_len - len, "cts2self = %u", + htt_stats_buf->cts2self); + len += HTT_DBG_OUT(buf + len, buf_len - len, "qos_null = %u", + htt_stats_buf->qos_null); + len += HTT_DBG_OUT(buf + len, buf_len - len, "delayed_bar_1 = %u", + htt_stats_buf->delayed_bar_1); + len += HTT_DBG_OUT(buf + len, buf_len - len, "delayed_bar_2 = %u", + htt_stats_buf->delayed_bar_2); + len += HTT_DBG_OUT(buf + len, buf_len - len, "delayed_bar_3 = %u", + htt_stats_buf->delayed_bar_3); + len += HTT_DBG_OUT(buf + len, buf_len - len, "delayed_bar_4 = %u", + htt_stats_buf->delayed_bar_4); + len += HTT_DBG_OUT(buf + len, buf_len - len, "delayed_bar_5 = %u", + htt_stats_buf->delayed_bar_5); + len += HTT_DBG_OUT(buf + len, buf_len - len, "delayed_bar_6 = %u", + htt_stats_buf->delayed_bar_6); + len += HTT_DBG_OUT(buf + len, buf_len - len, "delayed_bar_7 = %u\n", + htt_stats_buf->delayed_bar_7); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_tx_selfgen_ac_stats_tlv(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_tx_selfgen_ac_stats_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + + len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_TX_SELFGEN_AC_STATS_TLV:"); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ac_su_ndpa = %u", + htt_stats_buf->ac_su_ndpa); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ac_su_ndp = %u", + htt_stats_buf->ac_su_ndp); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ac_mu_mimo_ndpa = %u", + htt_stats_buf->ac_mu_mimo_ndpa); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ac_mu_mimo_ndp = %u", + htt_stats_buf->ac_mu_mimo_ndp); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ac_mu_mimo_brpoll_1 = %u", + htt_stats_buf->ac_mu_mimo_brpoll_1); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ac_mu_mimo_brpoll_2 = %u", + htt_stats_buf->ac_mu_mimo_brpoll_2); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ac_mu_mimo_brpoll_3 = %u\n", + htt_stats_buf->ac_mu_mimo_brpoll_3); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_tx_selfgen_ax_stats_tlv(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_tx_selfgen_ax_stats_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + + len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_TX_SELFGEN_AX_STATS_TLV:"); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ax_su_ndpa = %u", + htt_stats_buf->ax_su_ndpa); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ax_su_ndp = %u", + htt_stats_buf->ax_su_ndp); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ax_mu_mimo_ndpa = %u", + htt_stats_buf->ax_mu_mimo_ndpa); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ax_mu_mimo_ndp = %u", + htt_stats_buf->ax_mu_mimo_ndp); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ax_mu_mimo_brpoll_1 = %u", + htt_stats_buf->ax_mu_mimo_brpoll_1); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ax_mu_mimo_brpoll_2 = %u", + htt_stats_buf->ax_mu_mimo_brpoll_2); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ax_mu_mimo_brpoll_3 = %u", + htt_stats_buf->ax_mu_mimo_brpoll_3); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ax_mu_mimo_brpoll_4 = %u", + htt_stats_buf->ax_mu_mimo_brpoll_4); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ax_mu_mimo_brpoll_5 = %u", + htt_stats_buf->ax_mu_mimo_brpoll_5); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ax_mu_mimo_brpoll_6 = %u", + htt_stats_buf->ax_mu_mimo_brpoll_6); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ax_mu_mimo_brpoll_7 = %u", + htt_stats_buf->ax_mu_mimo_brpoll_7); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ax_basic_trigger = %u", + htt_stats_buf->ax_basic_trigger); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ax_bsr_trigger = %u", + htt_stats_buf->ax_bsr_trigger); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ax_mu_bar_trigger = %u", + htt_stats_buf->ax_mu_bar_trigger); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ax_mu_rts_trigger = %u\n", + htt_stats_buf->ax_mu_rts_trigger); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_tx_selfgen_ac_err_stats_tlv(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_tx_selfgen_ac_err_stats_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + + len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_TX_SELFGEN_AC_ERR_STATS_TLV:"); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ac_su_ndp_err = %u", + htt_stats_buf->ac_su_ndp_err); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ac_su_ndpa_err = %u", + htt_stats_buf->ac_su_ndpa_err); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ac_mu_mimo_ndpa_err = %u", + htt_stats_buf->ac_mu_mimo_ndpa_err); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ac_mu_mimo_ndp_err = %u", + htt_stats_buf->ac_mu_mimo_ndp_err); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ac_mu_mimo_brp1_err = %u", + htt_stats_buf->ac_mu_mimo_brp1_err); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ac_mu_mimo_brp2_err = %u", + htt_stats_buf->ac_mu_mimo_brp2_err); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ac_mu_mimo_brp3_err = %u\n", + htt_stats_buf->ac_mu_mimo_brp3_err); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_tx_selfgen_ax_err_stats_tlv(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_tx_selfgen_ax_err_stats_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + + len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_TX_SELFGEN_AX_ERR_STATS_TLV:"); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ax_su_ndp_err = %u", + htt_stats_buf->ax_su_ndp_err); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ax_su_ndpa_err = %u", + htt_stats_buf->ax_su_ndpa_err); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ax_mu_mimo_ndpa_err = %u", + htt_stats_buf->ax_mu_mimo_ndpa_err); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ax_mu_mimo_ndp_err = %u", + htt_stats_buf->ax_mu_mimo_ndp_err); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ax_mu_mimo_brp1_err = %u", + htt_stats_buf->ax_mu_mimo_brp1_err); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ax_mu_mimo_brp2_err = %u", + htt_stats_buf->ax_mu_mimo_brp2_err); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ax_mu_mimo_brp3_err = %u", + htt_stats_buf->ax_mu_mimo_brp3_err); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ax_mu_mimo_brp4_err = %u", + htt_stats_buf->ax_mu_mimo_brp4_err); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ax_mu_mimo_brp5_err = %u", + htt_stats_buf->ax_mu_mimo_brp5_err); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ax_mu_mimo_brp6_err = %u", + htt_stats_buf->ax_mu_mimo_brp6_err); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ax_mu_mimo_brp7_err = %u", + htt_stats_buf->ax_mu_mimo_brp7_err); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ax_basic_trigger_err = %u", + htt_stats_buf->ax_basic_trigger_err); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ax_bsr_trigger_err = %u", + htt_stats_buf->ax_bsr_trigger_err); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ax_mu_bar_trigger_err = %u", + htt_stats_buf->ax_mu_bar_trigger_err); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ax_mu_rts_trigger_err = %u\n", + htt_stats_buf->ax_mu_rts_trigger_err); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_tx_pdev_mu_mimo_sch_stats_tlv(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_tx_pdev_mu_mimo_sch_stats_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + u8 i; + + len += HTT_DBG_OUT(buf + len, buf_len - len, + "HTT_TX_PDEV_MU_MIMO_SCH_STATS_TLV:"); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mu_mimo_sch_posted = %u", + htt_stats_buf->mu_mimo_sch_posted); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mu_mimo_sch_failed = %u", + htt_stats_buf->mu_mimo_sch_failed); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mu_mimo_ppdu_posted = %u\n", + htt_stats_buf->mu_mimo_ppdu_posted); + + len += HTT_DBG_OUT(buf + len, buf_len - len, "11ac MU_MIMO SCH STATS:"); + + for (i = 0; i < HTT_TX_PDEV_STATS_NUM_AC_MUMIMO_USER_STATS; i++) + len += HTT_DBG_OUT(buf + len, buf_len - len, + "ac_mu_mimo_sch_nusers_%u = %u", + i, htt_stats_buf->ac_mu_mimo_sch_nusers[i]); + + len += HTT_DBG_OUT(buf + len, buf_len - len, "\n11ax MU_MIMO SCH STATS:"); + + for (i = 0; i < HTT_TX_PDEV_STATS_NUM_AX_MUMIMO_USER_STATS; i++) + len += HTT_DBG_OUT(buf + len, buf_len - len, + "ax_mu_mimo_sch_nusers_%u = %u", + i, htt_stats_buf->ax_mu_mimo_sch_nusers[i]); + + len += HTT_DBG_OUT(buf + len, buf_len - len, "\n11ax OFDMA SCH STATS:"); + + for (i = 0; i < HTT_TX_PDEV_STATS_NUM_OFDMA_USER_STATS; i++) + len += HTT_DBG_OUT(buf + len, buf_len - len, + "ax_ofdma_sch_nusers_%u = %u", + i, htt_stats_buf->ax_ofdma_sch_nusers[i]); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_tx_pdev_mu_mimo_mpdu_stats_tlv(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_tx_pdev_mpdu_stats_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + + if (htt_stats_buf->tx_sched_mode == HTT_STATS_TX_SCHED_MODE_MU_MIMO_AC) { + if (!htt_stats_buf->user_index) + len += HTT_DBG_OUT(buf + len, buf_len - len, + "HTT_TX_PDEV_MU_MIMO_AC_MPDU_STATS:\n"); + + if (htt_stats_buf->user_index < + HTT_TX_PDEV_STATS_NUM_AC_MUMIMO_USER_STATS) { + len += HTT_DBG_OUT(buf + len, buf_len - len, + "ac_mu_mimo_mpdus_queued_usr_%u = %u", + htt_stats_buf->user_index, + htt_stats_buf->mpdus_queued_usr); + len += HTT_DBG_OUT(buf + len, buf_len - len, + "ac_mu_mimo_mpdus_tried_usr_%u = %u", + htt_stats_buf->user_index, + htt_stats_buf->mpdus_tried_usr); + len += HTT_DBG_OUT(buf + len, buf_len - len, + "ac_mu_mimo_mpdus_failed_usr_%u = %u", + htt_stats_buf->user_index, + htt_stats_buf->mpdus_failed_usr); + len += HTT_DBG_OUT(buf + len, buf_len - len, + "ac_mu_mimo_mpdus_requeued_usr_%u = %u", + htt_stats_buf->user_index, + htt_stats_buf->mpdus_requeued_usr); + len += HTT_DBG_OUT(buf + len, buf_len - len, + "ac_mu_mimo_err_no_ba_usr_%u = %u", + htt_stats_buf->user_index, + htt_stats_buf->err_no_ba_usr); + len += HTT_DBG_OUT(buf + len, buf_len - len, + "ac_mu_mimo_mpdu_underrun_usr_%u = %u", + htt_stats_buf->user_index, + htt_stats_buf->mpdu_underrun_usr); + len += HTT_DBG_OUT(buf + len, buf_len - len, + "ac_mu_mimo_ampdu_underrun_usr_%u = %u\n", + htt_stats_buf->user_index, + htt_stats_buf->ampdu_underrun_usr); + } + } + + if (htt_stats_buf->tx_sched_mode == HTT_STATS_TX_SCHED_MODE_MU_MIMO_AX) { + if (!htt_stats_buf->user_index) + len += HTT_DBG_OUT(buf + len, buf_len - len, + "HTT_TX_PDEV_MU_MIMO_AX_MPDU_STATS:\n"); + + if (htt_stats_buf->user_index < + HTT_TX_PDEV_STATS_NUM_AX_MUMIMO_USER_STATS) { + len += HTT_DBG_OUT(buf + len, buf_len - len, + "ax_mu_mimo_mpdus_queued_usr_%u = %u", + htt_stats_buf->user_index, + htt_stats_buf->mpdus_queued_usr); + len += HTT_DBG_OUT(buf + len, buf_len - len, + "ax_mu_mimo_mpdus_tried_usr_%u = %u", + htt_stats_buf->user_index, + htt_stats_buf->mpdus_tried_usr); + len += HTT_DBG_OUT(buf + len, buf_len - len, + "ax_mu_mimo_mpdus_failed_usr_%u = %u", + htt_stats_buf->user_index, + htt_stats_buf->mpdus_failed_usr); + len += HTT_DBG_OUT(buf + len, buf_len - len, + "ax_mu_mimo_mpdus_requeued_usr_%u = %u", + htt_stats_buf->user_index, + htt_stats_buf->mpdus_requeued_usr); + len += HTT_DBG_OUT(buf + len, buf_len - len, + "ax_mu_mimo_err_no_ba_usr_%u = %u", + htt_stats_buf->user_index, + htt_stats_buf->err_no_ba_usr); + len += HTT_DBG_OUT(buf + len, buf_len - len, + "ax_mu_mimo_mpdu_underrun_usr_%u = %u", + htt_stats_buf->user_index, + htt_stats_buf->mpdu_underrun_usr); + len += HTT_DBG_OUT(buf + len, buf_len - len, + "ax_mu_mimo_ampdu_underrun_usr_%u = %u\n", + htt_stats_buf->user_index, + htt_stats_buf->ampdu_underrun_usr); + } + } + + if (htt_stats_buf->tx_sched_mode == HTT_STATS_TX_SCHED_MODE_MU_OFDMA_AX) { + if (!htt_stats_buf->user_index) + len += HTT_DBG_OUT(buf + len, buf_len - len, + "HTT_TX_PDEV_AX_MU_OFDMA_MPDU_STATS:\n"); + + if (htt_stats_buf->user_index < HTT_TX_PDEV_STATS_NUM_OFDMA_USER_STATS) { + len += HTT_DBG_OUT(buf + len, buf_len - len, + "ax_mu_ofdma_mpdus_queued_usr_%u = %u", + htt_stats_buf->user_index, + htt_stats_buf->mpdus_queued_usr); + len += HTT_DBG_OUT(buf + len, buf_len - len, + "ax_mu_ofdma_mpdus_tried_usr_%u = %u", + htt_stats_buf->user_index, + htt_stats_buf->mpdus_tried_usr); + len += HTT_DBG_OUT(buf + len, buf_len - len, + "ax_mu_ofdma_mpdus_failed_usr_%u = %u", + htt_stats_buf->user_index, + htt_stats_buf->mpdus_failed_usr); + len += HTT_DBG_OUT(buf + len, buf_len - len, + "ax_mu_ofdma_mpdus_requeued_usr_%u = %u", + htt_stats_buf->user_index, + htt_stats_buf->mpdus_requeued_usr); + len += HTT_DBG_OUT(buf + len, buf_len - len, + "ax_mu_ofdma_err_no_ba_usr_%u = %u", + htt_stats_buf->user_index, + htt_stats_buf->err_no_ba_usr); + len += HTT_DBG_OUT(buf + len, buf_len - len, + "ax_mu_ofdma_mpdu_underrun_usr_%u = %u", + htt_stats_buf->user_index, + htt_stats_buf->mpdu_underrun_usr); + len += HTT_DBG_OUT(buf + len, buf_len - len, + "ax_mu_ofdma_ampdu_underrun_usr_%u = %u\n", + htt_stats_buf->user_index, + htt_stats_buf->ampdu_underrun_usr); + } + } + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_sched_txq_cmd_posted_tlv_v(const void *tag_buf, + u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_sched_txq_cmd_posted_tlv_v *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + char sched_cmd_posted[HTT_MAX_STRING_LEN] = {0}; + u16 num_elements = min_t(u16, (tag_len >> 2), HTT_TX_PDEV_SCHED_TX_MODE_MAX); + + len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_SCHED_TXQ_CMD_POSTED_TLV_V:"); + + ARRAY_TO_STRING(sched_cmd_posted, htt_stats_buf->sched_cmd_posted, + num_elements); + len += HTT_DBG_OUT(buf + len, buf_len - len, "sched_cmd_posted = %s\n", + sched_cmd_posted); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_sched_txq_cmd_reaped_tlv_v(const void *tag_buf, + u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_sched_txq_cmd_reaped_tlv_v *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + char sched_cmd_reaped[HTT_MAX_STRING_LEN] = {0}; + u16 num_elements = min_t(u16, (tag_len >> 2), HTT_TX_PDEV_SCHED_TX_MODE_MAX); + + len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_SCHED_TXQ_CMD_REAPED_TLV_V:"); + + ARRAY_TO_STRING(sched_cmd_reaped, htt_stats_buf->sched_cmd_reaped, + num_elements); + len += HTT_DBG_OUT(buf + len, buf_len - len, "sched_cmd_reaped = %s\n", + sched_cmd_reaped); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_sched_txq_sched_order_su_tlv_v(const void *tag_buf, + u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_sched_txq_sched_order_su_tlv_v *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + char sched_order_su[HTT_MAX_STRING_LEN] = {0}; + /* each entry is u32, i.e. 4 bytes */ + u32 sched_order_su_num_entries = + min_t(u32, (tag_len >> 2), HTT_TX_PDEV_NUM_SCHED_ORDER_LOG); + + len += HTT_DBG_OUT(buf + len, buf_len - len, + "HTT_SCHED_TXQ_SCHED_ORDER_SU_TLV_V:"); + + ARRAY_TO_STRING(sched_order_su, htt_stats_buf->sched_order_su, + sched_order_su_num_entries); + len += HTT_DBG_OUT(buf + len, buf_len - len, "sched_order_su = %s\n", + sched_order_su); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_sched_txq_sched_ineligibility_tlv_v(const void *tag_buf, + u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_sched_txq_sched_ineligibility_tlv_v *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + char sched_ineligibility[HTT_MAX_STRING_LEN] = {0}; + /* each entry is u32, i.e. 4 bytes */ + u32 sched_ineligibility_num_entries = tag_len >> 2; + + len += HTT_DBG_OUT(buf + len, buf_len - len, + "HTT_SCHED_TXQ_SCHED_INELIGIBILITY_V:"); + + ARRAY_TO_STRING(sched_ineligibility, htt_stats_buf->sched_ineligibility, + sched_ineligibility_num_entries); + len += HTT_DBG_OUT(buf + len, buf_len - len, "sched_ineligibility = %s\n", + sched_ineligibility); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_tx_pdev_stats_sched_per_txq_tlv(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_tx_pdev_stats_sched_per_txq_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + + len += HTT_DBG_OUT(buf + len, buf_len - len, + "HTT_TX_PDEV_STATS_SCHED_PER_TXQ_TLV:"); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mac_id = %u", + htt_stats_buf->mac_id__txq_id__word & 0xFF); + len += HTT_DBG_OUT(buf + len, buf_len - len, "txq_id = %u", + (htt_stats_buf->mac_id__txq_id__word & 0xFF00) >> 8); + len += HTT_DBG_OUT(buf + len, buf_len - len, "sched_policy = %u", + htt_stats_buf->sched_policy); + len += HTT_DBG_OUT(buf + len, buf_len - len, + "last_sched_cmd_posted_timestamp = %u", + htt_stats_buf->last_sched_cmd_posted_timestamp); + len += HTT_DBG_OUT(buf + len, buf_len - len, + "last_sched_cmd_compl_timestamp = %u", + htt_stats_buf->last_sched_cmd_compl_timestamp); + len += HTT_DBG_OUT(buf + len, buf_len - len, "sched_2_tac_lwm_count = %u", + htt_stats_buf->sched_2_tac_lwm_count); + len += HTT_DBG_OUT(buf + len, buf_len - len, "sched_2_tac_ring_full = %u", + htt_stats_buf->sched_2_tac_ring_full); + len += HTT_DBG_OUT(buf + len, buf_len - len, "sched_cmd_post_failure = %u", + htt_stats_buf->sched_cmd_post_failure); + len += HTT_DBG_OUT(buf + len, buf_len - len, "num_active_tids = %u", + htt_stats_buf->num_active_tids); + len += HTT_DBG_OUT(buf + len, buf_len - len, "num_ps_schedules = %u", + htt_stats_buf->num_ps_schedules); + len += HTT_DBG_OUT(buf + len, buf_len - len, "sched_cmds_pending = %u", + htt_stats_buf->sched_cmds_pending); + len += HTT_DBG_OUT(buf + len, buf_len - len, "num_tid_register = %u", + htt_stats_buf->num_tid_register); + len += HTT_DBG_OUT(buf + len, buf_len - len, "num_tid_unregister = %u", + htt_stats_buf->num_tid_unregister); + len += HTT_DBG_OUT(buf + len, buf_len - len, "num_qstats_queried = %u", + htt_stats_buf->num_qstats_queried); + len += HTT_DBG_OUT(buf + len, buf_len - len, "qstats_update_pending = %u", + htt_stats_buf->qstats_update_pending); + len += HTT_DBG_OUT(buf + len, buf_len - len, "last_qstats_query_timestamp = %u", + htt_stats_buf->last_qstats_query_timestamp); + len += HTT_DBG_OUT(buf + len, buf_len - len, "num_tqm_cmdq_full = %u", + htt_stats_buf->num_tqm_cmdq_full); + len += HTT_DBG_OUT(buf + len, buf_len - len, "num_de_sched_algo_trigger = %u", + htt_stats_buf->num_de_sched_algo_trigger); + len += HTT_DBG_OUT(buf + len, buf_len - len, "num_rt_sched_algo_trigger = %u", + htt_stats_buf->num_rt_sched_algo_trigger); + len += HTT_DBG_OUT(buf + len, buf_len - len, "num_tqm_sched_algo_trigger = %u", + htt_stats_buf->num_tqm_sched_algo_trigger); + len += HTT_DBG_OUT(buf + len, buf_len - len, "notify_sched = %u\n", + htt_stats_buf->notify_sched); + len += HTT_DBG_OUT(buf + len, buf_len - len, "dur_based_sendn_term = %u\n", + htt_stats_buf->dur_based_sendn_term); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void htt_print_stats_tx_sched_cmn_tlv(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_stats_tx_sched_cmn_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + + len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_STATS_TX_SCHED_CMN_TLV:"); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mac_id = %u", + htt_stats_buf->mac_id__word & 0xFF); + len += HTT_DBG_OUT(buf + len, buf_len - len, "current_timestamp = %u\n", + htt_stats_buf->current_timestamp); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_tx_tqm_gen_mpdu_stats_tlv_v(const void *tag_buf, + u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_tx_tqm_gen_mpdu_stats_tlv_v *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + char gen_mpdu_end_reason[HTT_MAX_STRING_LEN] = {0}; + u16 num_elements = min_t(u16, (tag_len >> 2), + HTT_TX_TQM_MAX_LIST_MPDU_END_REASON); + + len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_TX_TQM_GEN_MPDU_STATS_TLV_V:"); + + ARRAY_TO_STRING(gen_mpdu_end_reason, htt_stats_buf->gen_mpdu_end_reason, + num_elements); + len += HTT_DBG_OUT(buf + len, buf_len - len, "gen_mpdu_end_reason = %s\n", + gen_mpdu_end_reason); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_tx_tqm_list_mpdu_stats_tlv_v(const void *tag_buf, + u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_tx_tqm_list_mpdu_stats_tlv_v *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + char list_mpdu_end_reason[HTT_MAX_STRING_LEN] = {0}; + u16 num_elems = min_t(u16, (tag_len >> 2), HTT_TX_TQM_MAX_LIST_MPDU_END_REASON); + + len += HTT_DBG_OUT(buf + len, buf_len - len, + "HTT_TX_TQM_LIST_MPDU_STATS_TLV_V:"); + + ARRAY_TO_STRING(list_mpdu_end_reason, htt_stats_buf->list_mpdu_end_reason, + num_elems); + len += HTT_DBG_OUT(buf + len, buf_len - len, "list_mpdu_end_reason = %s\n", + list_mpdu_end_reason); + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_tx_tqm_list_mpdu_cnt_tlv_v(const void *tag_buf, + u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_tx_tqm_list_mpdu_cnt_tlv_v *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + char list_mpdu_cnt_hist[HTT_MAX_STRING_LEN] = {0}; + u16 num_elems = min_t(u16, (tag_len >> 2), + HTT_TX_TQM_MAX_LIST_MPDU_CNT_HISTOGRAM_BINS); + + len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_TX_TQM_LIST_MPDU_CNT_TLV_V:"); + + ARRAY_TO_STRING(list_mpdu_cnt_hist, htt_stats_buf->list_mpdu_cnt_hist, + num_elems); + len += HTT_DBG_OUT(buf + len, buf_len - len, "list_mpdu_cnt_hist = %s\n", + list_mpdu_cnt_hist); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_tx_tqm_pdev_stats_tlv_v(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_tx_tqm_pdev_stats_tlv_v *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + + len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_TX_TQM_PDEV_STATS_TLV_V:"); + len += HTT_DBG_OUT(buf + len, buf_len - len, "msdu_count = %u", + htt_stats_buf->msdu_count); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mpdu_count = %u", + htt_stats_buf->mpdu_count); + len += HTT_DBG_OUT(buf + len, buf_len - len, "remove_msdu = %u", + htt_stats_buf->remove_msdu); + len += HTT_DBG_OUT(buf + len, buf_len - len, "remove_mpdu = %u", + htt_stats_buf->remove_mpdu); + len += HTT_DBG_OUT(buf + len, buf_len - len, "remove_msdu_ttl = %u", + htt_stats_buf->remove_msdu_ttl); + len += HTT_DBG_OUT(buf + len, buf_len - len, "send_bar = %u", + htt_stats_buf->send_bar); + len += HTT_DBG_OUT(buf + len, buf_len - len, "bar_sync = %u", + htt_stats_buf->bar_sync); + len += HTT_DBG_OUT(buf + len, buf_len - len, "notify_mpdu = %u", + htt_stats_buf->notify_mpdu); + len += HTT_DBG_OUT(buf + len, buf_len - len, "sync_cmd = %u", + htt_stats_buf->sync_cmd); + len += HTT_DBG_OUT(buf + len, buf_len - len, "write_cmd = %u", + htt_stats_buf->write_cmd); + len += HTT_DBG_OUT(buf + len, buf_len - len, "hwsch_trigger = %u", + htt_stats_buf->hwsch_trigger); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ack_tlv_proc = %u", + htt_stats_buf->ack_tlv_proc); + len += HTT_DBG_OUT(buf + len, buf_len - len, "gen_mpdu_cmd = %u", + htt_stats_buf->gen_mpdu_cmd); + len += HTT_DBG_OUT(buf + len, buf_len - len, "gen_list_cmd = %u", + htt_stats_buf->gen_list_cmd); + len += HTT_DBG_OUT(buf + len, buf_len - len, "remove_mpdu_cmd = %u", + htt_stats_buf->remove_mpdu_cmd); + len += HTT_DBG_OUT(buf + len, buf_len - len, "remove_mpdu_tried_cmd = %u", + htt_stats_buf->remove_mpdu_tried_cmd); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mpdu_queue_stats_cmd = %u", + htt_stats_buf->mpdu_queue_stats_cmd); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mpdu_head_info_cmd = %u", + htt_stats_buf->mpdu_head_info_cmd); + len += HTT_DBG_OUT(buf + len, buf_len - len, "msdu_flow_stats_cmd = %u", + htt_stats_buf->msdu_flow_stats_cmd); + len += HTT_DBG_OUT(buf + len, buf_len - len, "remove_msdu_cmd = %u", + htt_stats_buf->remove_msdu_cmd); + len += HTT_DBG_OUT(buf + len, buf_len - len, "remove_msdu_ttl_cmd = %u", + htt_stats_buf->remove_msdu_ttl_cmd); + len += HTT_DBG_OUT(buf + len, buf_len - len, "flush_cache_cmd = %u", + htt_stats_buf->flush_cache_cmd); + len += HTT_DBG_OUT(buf + len, buf_len - len, "update_mpduq_cmd = %u", + htt_stats_buf->update_mpduq_cmd); + len += HTT_DBG_OUT(buf + len, buf_len - len, "enqueue = %u", + htt_stats_buf->enqueue); + len += HTT_DBG_OUT(buf + len, buf_len - len, "enqueue_notify = %u", + htt_stats_buf->enqueue_notify); + len += HTT_DBG_OUT(buf + len, buf_len - len, "notify_mpdu_at_head = %u", + htt_stats_buf->notify_mpdu_at_head); + len += HTT_DBG_OUT(buf + len, buf_len - len, "notify_mpdu_state_valid = %u", + htt_stats_buf->notify_mpdu_state_valid); + len += HTT_DBG_OUT(buf + len, buf_len - len, "sched_udp_notify1 = %u", + htt_stats_buf->sched_udp_notify1); + len += HTT_DBG_OUT(buf + len, buf_len - len, "sched_udp_notify2 = %u", + htt_stats_buf->sched_udp_notify2); + len += HTT_DBG_OUT(buf + len, buf_len - len, "sched_nonudp_notify1 = %u", + htt_stats_buf->sched_nonudp_notify1); + len += HTT_DBG_OUT(buf + len, buf_len - len, "sched_nonudp_notify2 = %u\n", + htt_stats_buf->sched_nonudp_notify2); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void htt_print_tx_tqm_cmn_stats_tlv(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_tx_tqm_cmn_stats_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + + len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_TX_TQM_CMN_STATS_TLV:"); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mac_id = %u", + htt_stats_buf->mac_id__word & 0xFF); + len += HTT_DBG_OUT(buf + len, buf_len - len, "max_cmdq_id = %u", + htt_stats_buf->max_cmdq_id); + len += HTT_DBG_OUT(buf + len, buf_len - len, "list_mpdu_cnt_hist_intvl = %u", + htt_stats_buf->list_mpdu_cnt_hist_intvl); + len += HTT_DBG_OUT(buf + len, buf_len - len, "add_msdu = %u", + htt_stats_buf->add_msdu); + len += HTT_DBG_OUT(buf + len, buf_len - len, "q_empty = %u", + htt_stats_buf->q_empty); + len += HTT_DBG_OUT(buf + len, buf_len - len, "q_not_empty = %u", + htt_stats_buf->q_not_empty); + len += HTT_DBG_OUT(buf + len, buf_len - len, "drop_notification = %u", + htt_stats_buf->drop_notification); + len += HTT_DBG_OUT(buf + len, buf_len - len, "desc_threshold = %u\n", + htt_stats_buf->desc_threshold); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void htt_print_tx_tqm_error_stats_tlv(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_tx_tqm_error_stats_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + + len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_TX_TQM_ERROR_STATS_TLV:"); + len += HTT_DBG_OUT(buf + len, buf_len - len, "q_empty_failure = %u", + htt_stats_buf->q_empty_failure); + len += HTT_DBG_OUT(buf + len, buf_len - len, "q_not_empty_failure = %u", + htt_stats_buf->q_not_empty_failure); + len += HTT_DBG_OUT(buf + len, buf_len - len, "add_msdu_failure = %u\n", + htt_stats_buf->add_msdu_failure); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void htt_print_tx_tqm_cmdq_status_tlv(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_tx_tqm_cmdq_status_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + + len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_TX_TQM_CMDQ_STATUS_TLV:"); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mac_id = %u", + htt_stats_buf->mac_id__cmdq_id__word & 0xFF); + len += HTT_DBG_OUT(buf + len, buf_len - len, "cmdq_id = %u\n", + (htt_stats_buf->mac_id__cmdq_id__word & 0xFF00) >> 8); + len += HTT_DBG_OUT(buf + len, buf_len - len, "sync_cmd = %u", + htt_stats_buf->sync_cmd); + len += HTT_DBG_OUT(buf + len, buf_len - len, "write_cmd = %u", + htt_stats_buf->write_cmd); + len += HTT_DBG_OUT(buf + len, buf_len - len, "gen_mpdu_cmd = %u", + htt_stats_buf->gen_mpdu_cmd); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mpdu_queue_stats_cmd = %u", + htt_stats_buf->mpdu_queue_stats_cmd); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mpdu_head_info_cmd = %u", + htt_stats_buf->mpdu_head_info_cmd); + len += HTT_DBG_OUT(buf + len, buf_len - len, "msdu_flow_stats_cmd = %u", + htt_stats_buf->msdu_flow_stats_cmd); + len += HTT_DBG_OUT(buf + len, buf_len - len, "remove_mpdu_cmd = %u", + htt_stats_buf->remove_mpdu_cmd); + len += HTT_DBG_OUT(buf + len, buf_len - len, "remove_msdu_cmd = %u", + htt_stats_buf->remove_msdu_cmd); + len += HTT_DBG_OUT(buf + len, buf_len - len, "flush_cache_cmd = %u", + htt_stats_buf->flush_cache_cmd); + len += HTT_DBG_OUT(buf + len, buf_len - len, "update_mpduq_cmd = %u", + htt_stats_buf->update_mpduq_cmd); + len += HTT_DBG_OUT(buf + len, buf_len - len, "update_msduq_cmd = %u\n", + htt_stats_buf->update_msduq_cmd); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_tx_de_eapol_packets_stats_tlv(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_tx_de_eapol_packets_stats_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + + len += HTT_DBG_OUT(buf + len, buf_len - len, + "HTT_TX_DE_EAPOL_PACKETS_STATS_TLV:"); + len += HTT_DBG_OUT(buf + len, buf_len - len, "m1_packets = %u", + htt_stats_buf->m1_packets); + len += HTT_DBG_OUT(buf + len, buf_len - len, "m2_packets = %u", + htt_stats_buf->m2_packets); + len += HTT_DBG_OUT(buf + len, buf_len - len, "m3_packets = %u", + htt_stats_buf->m3_packets); + len += HTT_DBG_OUT(buf + len, buf_len - len, "m4_packets = %u", + htt_stats_buf->m4_packets); + len += HTT_DBG_OUT(buf + len, buf_len - len, "g1_packets = %u", + htt_stats_buf->g1_packets); + len += HTT_DBG_OUT(buf + len, buf_len - len, "g2_packets = %u\n", + htt_stats_buf->g2_packets); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_tx_de_classify_failed_stats_tlv(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_tx_de_classify_failed_stats_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + + len += HTT_DBG_OUT(buf + len, buf_len - len, + "HTT_TX_DE_CLASSIFY_FAILED_STATS_TLV:"); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ap_bss_peer_not_found = %u", + htt_stats_buf->ap_bss_peer_not_found); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ap_bcast_mcast_no_peer = %u", + htt_stats_buf->ap_bcast_mcast_no_peer); + len += HTT_DBG_OUT(buf + len, buf_len - len, "sta_delete_in_progress = %u", + htt_stats_buf->sta_delete_in_progress); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ibss_no_bss_peer = %u", + htt_stats_buf->ibss_no_bss_peer); + len += HTT_DBG_OUT(buf + len, buf_len - len, "invalid_vdev_type = %u", + htt_stats_buf->invalid_vdev_type); + len += HTT_DBG_OUT(buf + len, buf_len - len, "invalid_ast_peer_entry = %u", + htt_stats_buf->invalid_ast_peer_entry); + len += HTT_DBG_OUT(buf + len, buf_len - len, "peer_entry_invalid = %u", + htt_stats_buf->peer_entry_invalid); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ethertype_not_ip = %u", + htt_stats_buf->ethertype_not_ip); + len += HTT_DBG_OUT(buf + len, buf_len - len, "eapol_lookup_failed = %u", + htt_stats_buf->eapol_lookup_failed); + len += HTT_DBG_OUT(buf + len, buf_len - len, "qpeer_not_allow_data = %u", + htt_stats_buf->qpeer_not_allow_data); + len += HTT_DBG_OUT(buf + len, buf_len - len, "fse_tid_override = %u", + htt_stats_buf->fse_tid_override); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ipv6_jumbogram_zero_length = %u", + htt_stats_buf->ipv6_jumbogram_zero_length); + len += HTT_DBG_OUT(buf + len, buf_len - len, "qos_to_non_qos_in_prog = %u\n", + htt_stats_buf->qos_to_non_qos_in_prog); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_tx_de_classify_stats_tlv(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_tx_de_classify_stats_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + + len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_TX_DE_CLASSIFY_STATS_TLV:"); + len += HTT_DBG_OUT(buf + len, buf_len - len, "arp_packets = %u", + htt_stats_buf->arp_packets); + len += HTT_DBG_OUT(buf + len, buf_len - len, "igmp_packets = %u", + htt_stats_buf->igmp_packets); + len += HTT_DBG_OUT(buf + len, buf_len - len, "dhcp_packets = %u", + htt_stats_buf->dhcp_packets); + len += HTT_DBG_OUT(buf + len, buf_len - len, "host_inspected = %u", + htt_stats_buf->host_inspected); + len += HTT_DBG_OUT(buf + len, buf_len - len, "htt_included = %u", + htt_stats_buf->htt_included); + len += HTT_DBG_OUT(buf + len, buf_len - len, "htt_valid_mcs = %u", + htt_stats_buf->htt_valid_mcs); + len += HTT_DBG_OUT(buf + len, buf_len - len, "htt_valid_nss = %u", + htt_stats_buf->htt_valid_nss); + len += HTT_DBG_OUT(buf + len, buf_len - len, "htt_valid_preamble_type = %u", + htt_stats_buf->htt_valid_preamble_type); + len += HTT_DBG_OUT(buf + len, buf_len - len, "htt_valid_chainmask = %u", + htt_stats_buf->htt_valid_chainmask); + len += HTT_DBG_OUT(buf + len, buf_len - len, "htt_valid_guard_interval = %u", + htt_stats_buf->htt_valid_guard_interval); + len += HTT_DBG_OUT(buf + len, buf_len - len, "htt_valid_retries = %u", + htt_stats_buf->htt_valid_retries); + len += HTT_DBG_OUT(buf + len, buf_len - len, "htt_valid_bw_info = %u", + htt_stats_buf->htt_valid_bw_info); + len += HTT_DBG_OUT(buf + len, buf_len - len, "htt_valid_power = %u", + htt_stats_buf->htt_valid_power); + len += HTT_DBG_OUT(buf + len, buf_len - len, "htt_valid_key_flags = 0x%x", + htt_stats_buf->htt_valid_key_flags); + len += HTT_DBG_OUT(buf + len, buf_len - len, "htt_valid_no_encryption = %u", + htt_stats_buf->htt_valid_no_encryption); + len += HTT_DBG_OUT(buf + len, buf_len - len, "fse_entry_count = %u", + htt_stats_buf->fse_entry_count); + len += HTT_DBG_OUT(buf + len, buf_len - len, "fse_priority_be = %u", + htt_stats_buf->fse_priority_be); + len += HTT_DBG_OUT(buf + len, buf_len - len, "fse_priority_high = %u", + htt_stats_buf->fse_priority_high); + len += HTT_DBG_OUT(buf + len, buf_len - len, "fse_priority_low = %u", + htt_stats_buf->fse_priority_low); + len += HTT_DBG_OUT(buf + len, buf_len - len, "fse_traffic_ptrn_be = %u", + htt_stats_buf->fse_traffic_ptrn_be); + len += HTT_DBG_OUT(buf + len, buf_len - len, "fse_traffic_ptrn_over_sub = %u", + htt_stats_buf->fse_traffic_ptrn_over_sub); + len += HTT_DBG_OUT(buf + len, buf_len - len, "fse_traffic_ptrn_bursty = %u", + htt_stats_buf->fse_traffic_ptrn_bursty); + len += HTT_DBG_OUT(buf + len, buf_len - len, "fse_traffic_ptrn_interactive = %u", + htt_stats_buf->fse_traffic_ptrn_interactive); + len += HTT_DBG_OUT(buf + len, buf_len - len, "fse_traffic_ptrn_periodic = %u", + htt_stats_buf->fse_traffic_ptrn_periodic); + len += HTT_DBG_OUT(buf + len, buf_len - len, "fse_hwqueue_alloc = %u", + htt_stats_buf->fse_hwqueue_alloc); + len += HTT_DBG_OUT(buf + len, buf_len - len, "fse_hwqueue_created = %u", + htt_stats_buf->fse_hwqueue_created); + len += HTT_DBG_OUT(buf + len, buf_len - len, "fse_hwqueue_send_to_host = %u", + htt_stats_buf->fse_hwqueue_send_to_host); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mcast_entry = %u", + htt_stats_buf->mcast_entry); + len += HTT_DBG_OUT(buf + len, buf_len - len, "bcast_entry = %u", + htt_stats_buf->bcast_entry); + len += HTT_DBG_OUT(buf + len, buf_len - len, "htt_update_peer_cache = %u", + htt_stats_buf->htt_update_peer_cache); + len += HTT_DBG_OUT(buf + len, buf_len - len, "htt_learning_frame = %u", + htt_stats_buf->htt_learning_frame); + len += HTT_DBG_OUT(buf + len, buf_len - len, "fse_invalid_peer = %u", + htt_stats_buf->fse_invalid_peer); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mec_notify = %u\n", + htt_stats_buf->mec_notify); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_tx_de_classify_status_stats_tlv(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_tx_de_classify_status_stats_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + + len += HTT_DBG_OUT(buf + len, buf_len - len, + "HTT_TX_DE_CLASSIFY_STATUS_STATS_TLV:"); + len += HTT_DBG_OUT(buf + len, buf_len - len, "eok = %u", + htt_stats_buf->eok); + len += HTT_DBG_OUT(buf + len, buf_len - len, "classify_done = %u", + htt_stats_buf->classify_done); + len += HTT_DBG_OUT(buf + len, buf_len - len, "lookup_failed = %u", + htt_stats_buf->lookup_failed); + len += HTT_DBG_OUT(buf + len, buf_len - len, "send_host_dhcp = %u", + htt_stats_buf->send_host_dhcp); + len += HTT_DBG_OUT(buf + len, buf_len - len, "send_host_mcast = %u", + htt_stats_buf->send_host_mcast); + len += HTT_DBG_OUT(buf + len, buf_len - len, "send_host_unknown_dest = %u", + htt_stats_buf->send_host_unknown_dest); + len += HTT_DBG_OUT(buf + len, buf_len - len, "send_host = %u", + htt_stats_buf->send_host); + len += HTT_DBG_OUT(buf + len, buf_len - len, "status_invalid = %u\n", + htt_stats_buf->status_invalid); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_tx_de_enqueue_packets_stats_tlv(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_tx_de_enqueue_packets_stats_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + + len += HTT_DBG_OUT(buf + len, buf_len - len, + "HTT_TX_DE_ENQUEUE_PACKETS_STATS_TLV:"); + len += HTT_DBG_OUT(buf + len, buf_len - len, "enqueued_pkts = %u", + htt_stats_buf->enqueued_pkts); + len += HTT_DBG_OUT(buf + len, buf_len - len, "to_tqm = %u", + htt_stats_buf->to_tqm); + len += HTT_DBG_OUT(buf + len, buf_len - len, "to_tqm_bypass = %u\n", + htt_stats_buf->to_tqm_bypass); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_tx_de_enqueue_discard_stats_tlv(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_tx_de_enqueue_discard_stats_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + + len += HTT_DBG_OUT(buf + len, buf_len - len, + "HTT_TX_DE_ENQUEUE_DISCARD_STATS_TLV:"); + len += HTT_DBG_OUT(buf + len, buf_len - len, "discarded_pkts = %u", + htt_stats_buf->discarded_pkts); + len += HTT_DBG_OUT(buf + len, buf_len - len, "local_frames = %u", + htt_stats_buf->local_frames); + len += HTT_DBG_OUT(buf + len, buf_len - len, "is_ext_msdu = %u\n", + htt_stats_buf->is_ext_msdu); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void htt_print_tx_de_compl_stats_tlv(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_tx_de_compl_stats_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + + len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_TX_DE_COMPL_STATS_TLV:"); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tcl_dummy_frame = %u", + htt_stats_buf->tcl_dummy_frame); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tqm_dummy_frame = %u", + htt_stats_buf->tqm_dummy_frame); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tqm_notify_frame = %u", + htt_stats_buf->tqm_notify_frame); + len += HTT_DBG_OUT(buf + len, buf_len - len, "fw2wbm_enq = %u", + htt_stats_buf->fw2wbm_enq); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tqm_bypass_frame = %u\n", + htt_stats_buf->tqm_bypass_frame); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_tx_de_fw2wbm_ring_full_hist_tlv(const void *tag_buf, + u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_tx_de_fw2wbm_ring_full_hist_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + char fw2wbm_ring_full_hist[HTT_MAX_STRING_LEN] = {0}; + u16 num_elements = tag_len >> 2; + u32 required_buffer_size = HTT_MAX_PRINT_CHAR_PER_ELEM * num_elements; + + len += HTT_DBG_OUT(buf + len, buf_len - len, + "HTT_TX_DE_FW2WBM_RING_FULL_HIST_TLV"); + + if (required_buffer_size < HTT_MAX_STRING_LEN) { + ARRAY_TO_STRING(fw2wbm_ring_full_hist, + htt_stats_buf->fw2wbm_ring_full_hist, + num_elements); + len += HTT_DBG_OUT(buf + len, buf_len - len, + "fw2wbm_ring_full_hist = %s\n", + fw2wbm_ring_full_hist); + } else { + len += HTT_DBG_OUT(buf + len, buf_len - len, + "INSUFFICIENT PRINT BUFFER "); + } + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_tx_de_cmn_stats_tlv(const void *tag_buf, struct debug_htt_stats_req *stats_req) +{ + const struct htt_tx_de_cmn_stats_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + + len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_TX_DE_CMN_STATS_TLV:"); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mac_id = %u", + htt_stats_buf->mac_id__word & 0xFF); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tcl2fw_entry_count = %u", + htt_stats_buf->tcl2fw_entry_count); + len += HTT_DBG_OUT(buf + len, buf_len - len, "not_to_fw = %u", + htt_stats_buf->not_to_fw); + len += HTT_DBG_OUT(buf + len, buf_len - len, "invalid_pdev_vdev_peer = %u", + htt_stats_buf->invalid_pdev_vdev_peer); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tcl_res_invalid_addrx = %u", + htt_stats_buf->tcl_res_invalid_addrx); + len += HTT_DBG_OUT(buf + len, buf_len - len, "wbm2fw_entry_count = %u", + htt_stats_buf->wbm2fw_entry_count); + len += HTT_DBG_OUT(buf + len, buf_len - len, "invalid_pdev = %u\n", + htt_stats_buf->invalid_pdev); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void htt_print_ring_if_stats_tlv(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_ring_if_stats_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + char low_wm_hit_count[HTT_MAX_STRING_LEN] = {0}; + char high_wm_hit_count[HTT_MAX_STRING_LEN] = {0}; + + len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_RING_IF_STATS_TLV:"); + len += HTT_DBG_OUT(buf + len, buf_len - len, "base_addr = %u", + htt_stats_buf->base_addr); + len += HTT_DBG_OUT(buf + len, buf_len - len, "elem_size = %u", + htt_stats_buf->elem_size); + len += HTT_DBG_OUT(buf + len, buf_len - len, "num_elems = %u", + htt_stats_buf->num_elems__prefetch_tail_idx & 0xFFFF); + len += HTT_DBG_OUT(buf + len, buf_len - len, "prefetch_tail_idx = %u", + (htt_stats_buf->num_elems__prefetch_tail_idx & + 0xFFFF0000) >> 16); + len += HTT_DBG_OUT(buf + len, buf_len - len, "head_idx = %u", + htt_stats_buf->head_idx__tail_idx & 0xFFFF); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tail_idx = %u", + (htt_stats_buf->head_idx__tail_idx & 0xFFFF0000) >> 16); + len += HTT_DBG_OUT(buf + len, buf_len - len, "shadow_head_idx = %u", + htt_stats_buf->shadow_head_idx__shadow_tail_idx & 0xFFFF); + len += HTT_DBG_OUT(buf + len, buf_len - len, "shadow_tail_idx = %u", + (htt_stats_buf->shadow_head_idx__shadow_tail_idx & + 0xFFFF0000) >> 16); + len += HTT_DBG_OUT(buf + len, buf_len - len, "num_tail_incr = %u", + htt_stats_buf->num_tail_incr); + len += HTT_DBG_OUT(buf + len, buf_len - len, "lwm_thresh = %u", + htt_stats_buf->lwm_thresh__hwm_thresh & 0xFFFF); + len += HTT_DBG_OUT(buf + len, buf_len - len, "hwm_thresh = %u", + (htt_stats_buf->lwm_thresh__hwm_thresh & 0xFFFF0000) >> 16); + len += HTT_DBG_OUT(buf + len, buf_len - len, "overrun_hit_count = %u", + htt_stats_buf->overrun_hit_count); + len += HTT_DBG_OUT(buf + len, buf_len - len, "underrun_hit_count = %u", + htt_stats_buf->underrun_hit_count); + len += HTT_DBG_OUT(buf + len, buf_len - len, "prod_blockwait_count = %u", + htt_stats_buf->prod_blockwait_count); + len += HTT_DBG_OUT(buf + len, buf_len - len, "cons_blockwait_count = %u", + htt_stats_buf->cons_blockwait_count); + + ARRAY_TO_STRING(low_wm_hit_count, htt_stats_buf->low_wm_hit_count, + HTT_STATS_LOW_WM_BINS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "low_wm_hit_count = %s ", + low_wm_hit_count); + + ARRAY_TO_STRING(high_wm_hit_count, htt_stats_buf->high_wm_hit_count, + HTT_STATS_HIGH_WM_BINS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "high_wm_hit_count = %s\n", + high_wm_hit_count); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void htt_print_ring_if_cmn_tlv(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_ring_if_cmn_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + + len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_RING_IF_CMN_TLV:"); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mac_id = %u", + htt_stats_buf->mac_id__word & 0xFF); + len += HTT_DBG_OUT(buf + len, buf_len - len, "num_records = %u\n", + htt_stats_buf->num_records); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void htt_print_sfm_client_user_tlv_v(const void *tag_buf, + u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_sfm_client_user_tlv_v *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + char dwords_used_by_user_n[HTT_MAX_STRING_LEN] = {0}; + u16 num_elems = tag_len >> 2; + + len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_SFM_CLIENT_USER_TLV_V:"); + + ARRAY_TO_STRING(dwords_used_by_user_n, + htt_stats_buf->dwords_used_by_user_n, + num_elems); + len += HTT_DBG_OUT(buf + len, buf_len - len, "dwords_used_by_user_n = %s\n", + dwords_used_by_user_n); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void htt_print_sfm_client_tlv(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_sfm_client_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + + len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_SFM_CLIENT_TLV:"); + len += HTT_DBG_OUT(buf + len, buf_len - len, "client_id = %u", + htt_stats_buf->client_id); + len += HTT_DBG_OUT(buf + len, buf_len - len, "buf_min = %u", + htt_stats_buf->buf_min); + len += HTT_DBG_OUT(buf + len, buf_len - len, "buf_max = %u", + htt_stats_buf->buf_max); + len += HTT_DBG_OUT(buf + len, buf_len - len, "buf_busy = %u", + htt_stats_buf->buf_busy); + len += HTT_DBG_OUT(buf + len, buf_len - len, "buf_alloc = %u", + htt_stats_buf->buf_alloc); + len += HTT_DBG_OUT(buf + len, buf_len - len, "buf_avail = %u", + htt_stats_buf->buf_avail); + len += HTT_DBG_OUT(buf + len, buf_len - len, "num_users = %u\n", + htt_stats_buf->num_users); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void htt_print_sfm_cmn_tlv(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_sfm_cmn_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + + len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_SFM_CMN_TLV:"); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mac_id = %u", + htt_stats_buf->mac_id__word & 0xFF); + len += HTT_DBG_OUT(buf + len, buf_len - len, "buf_total = %u", + htt_stats_buf->buf_total); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mem_empty = %u", + htt_stats_buf->mem_empty); + len += HTT_DBG_OUT(buf + len, buf_len - len, "deallocate_bufs = %u", + htt_stats_buf->deallocate_bufs); + len += HTT_DBG_OUT(buf + len, buf_len - len, "num_records = %u\n", + htt_stats_buf->num_records); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void htt_print_sring_stats_tlv(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_sring_stats_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + + len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_SRING_STATS_TLV:"); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mac_id = %u", + htt_stats_buf->mac_id__ring_id__arena__ep & 0xFF); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ring_id = %u", + (htt_stats_buf->mac_id__ring_id__arena__ep & 0xFF00) >> 8); + len += HTT_DBG_OUT(buf + len, buf_len - len, "arena = %u", + (htt_stats_buf->mac_id__ring_id__arena__ep & 0xFF0000) >> 16); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ep = %u", + (htt_stats_buf->mac_id__ring_id__arena__ep & 0x1000000) >> 24); + len += HTT_DBG_OUT(buf + len, buf_len - len, "base_addr_lsb = 0x%x", + htt_stats_buf->base_addr_lsb); + len += HTT_DBG_OUT(buf + len, buf_len - len, "base_addr_msb = 0x%x", + htt_stats_buf->base_addr_msb); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ring_size = %u", + htt_stats_buf->ring_size); + len += HTT_DBG_OUT(buf + len, buf_len - len, "elem_size = %u", + htt_stats_buf->elem_size); + len += HTT_DBG_OUT(buf + len, buf_len - len, "num_avail_words = %u", + htt_stats_buf->num_avail_words__num_valid_words & 0xFFFF); + len += HTT_DBG_OUT(buf + len, buf_len - len, "num_valid_words = %u", + (htt_stats_buf->num_avail_words__num_valid_words & + 0xFFFF0000) >> 16); + len += HTT_DBG_OUT(buf + len, buf_len - len, "head_ptr = %u", + htt_stats_buf->head_ptr__tail_ptr & 0xFFFF); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tail_ptr = %u", + (htt_stats_buf->head_ptr__tail_ptr & 0xFFFF0000) >> 16); + len += HTT_DBG_OUT(buf + len, buf_len - len, "consumer_empty = %u", + htt_stats_buf->consumer_empty__producer_full & 0xFFFF); + len += HTT_DBG_OUT(buf + len, buf_len - len, "producer_full = %u", + (htt_stats_buf->consumer_empty__producer_full & + 0xFFFF0000) >> 16); + len += HTT_DBG_OUT(buf + len, buf_len - len, "prefetch_count = %u", + htt_stats_buf->prefetch_count__internal_tail_ptr & 0xFFFF); + len += HTT_DBG_OUT(buf + len, buf_len - len, "internal_tail_ptr = %u\n", + (htt_stats_buf->prefetch_count__internal_tail_ptr & + 0xFFFF0000) >> 16); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void htt_print_sring_cmn_tlv(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_sring_cmn_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + + len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_SRING_CMN_TLV:"); + len += HTT_DBG_OUT(buf + len, buf_len - len, "num_records = %u\n", + htt_stats_buf->num_records); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void htt_print_tx_pdev_rate_stats_tlv(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_tx_pdev_rate_stats_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + u8 j; + char str_buf[HTT_MAX_STRING_LEN] = {0}; + char *tx_gi[HTT_TX_PEER_STATS_NUM_GI_COUNTERS]; + + for (j = 0; j < HTT_TX_PEER_STATS_NUM_GI_COUNTERS; j++) + tx_gi[j] = kmalloc(HTT_MAX_STRING_LEN, GFP_ATOMIC); + + len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_TX_PDEV_RATE_STATS_TLV:"); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mac_id = %u", + htt_stats_buf->mac_id__word & 0xFF); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tx_ldpc = %u", + htt_stats_buf->tx_ldpc); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ac_mu_mimo_tx_ldpc = %u", + htt_stats_buf->ac_mu_mimo_tx_ldpc); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ax_mu_mimo_tx_ldpc = %u", + htt_stats_buf->ax_mu_mimo_tx_ldpc); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ofdma_tx_ldpc = %u", + htt_stats_buf->ofdma_tx_ldpc); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rts_cnt = %u", + htt_stats_buf->rts_cnt); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rts_success = %u", + htt_stats_buf->rts_success); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ack_rssi = %u", + htt_stats_buf->ack_rssi); + + len += HTT_DBG_OUT(buf + len, buf_len - len, + "Legacy CCK Rates: 1 Mbps: %u, 2 Mbps: %u, 5.5 Mbps: %u, 11 Mbps: %u", + htt_stats_buf->tx_legacy_cck_rate[0], + htt_stats_buf->tx_legacy_cck_rate[1], + htt_stats_buf->tx_legacy_cck_rate[2], + htt_stats_buf->tx_legacy_cck_rate[3]); + + len += HTT_DBG_OUT(buf + len, buf_len - len, + "Legacy OFDM Rates: 6 Mbps: %u, 9 Mbps: %u, 12 Mbps: %u, 18 Mbps: %u\n" + " 24 Mbps: %u, 36 Mbps: %u, 48 Mbps: %u, 54 Mbps: %u", + htt_stats_buf->tx_legacy_ofdm_rate[0], + htt_stats_buf->tx_legacy_ofdm_rate[1], + htt_stats_buf->tx_legacy_ofdm_rate[2], + htt_stats_buf->tx_legacy_ofdm_rate[3], + htt_stats_buf->tx_legacy_ofdm_rate[4], + htt_stats_buf->tx_legacy_ofdm_rate[5], + htt_stats_buf->tx_legacy_ofdm_rate[6], + htt_stats_buf->tx_legacy_ofdm_rate[7]); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + ARRAY_TO_STRING(str_buf, htt_stats_buf->tx_mcs, + HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tx_mcs = %s ", str_buf); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + ARRAY_TO_STRING(str_buf, htt_stats_buf->ac_mu_mimo_tx_mcs, + HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ac_mu_mimo_tx_mcs = %s ", str_buf); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + ARRAY_TO_STRING(str_buf, htt_stats_buf->ax_mu_mimo_tx_mcs, + HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ax_mu_mimo_tx_mcs = %s ", str_buf); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + ARRAY_TO_STRING(str_buf, htt_stats_buf->ofdma_tx_mcs, + HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ofdma_tx_mcs = %s ", str_buf); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + ARRAY_TO_STRING(str_buf, htt_stats_buf->tx_nss, + HTT_TX_PDEV_STATS_NUM_SPATIAL_STREAMS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tx_nss = %s ", str_buf); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + ARRAY_TO_STRING(str_buf, htt_stats_buf->ac_mu_mimo_tx_nss, + HTT_TX_PDEV_STATS_NUM_SPATIAL_STREAMS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ac_mu_mimo_tx_nss = %s ", str_buf); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + ARRAY_TO_STRING(str_buf, htt_stats_buf->ax_mu_mimo_tx_nss, + HTT_TX_PDEV_STATS_NUM_SPATIAL_STREAMS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ax_mu_mimo_tx_nss = %s ", str_buf); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + ARRAY_TO_STRING(str_buf, htt_stats_buf->ofdma_tx_nss, + HTT_TX_PDEV_STATS_NUM_SPATIAL_STREAMS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ofdma_tx_nss = %s ", str_buf); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + ARRAY_TO_STRING(str_buf, htt_stats_buf->tx_bw, + HTT_TX_PDEV_STATS_NUM_BW_COUNTERS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tx_bw = %s ", str_buf); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + ARRAY_TO_STRING(str_buf, htt_stats_buf->ac_mu_mimo_tx_bw, + HTT_TX_PDEV_STATS_NUM_BW_COUNTERS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ac_mu_mimo_tx_bw = %s ", str_buf); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + ARRAY_TO_STRING(str_buf, htt_stats_buf->ax_mu_mimo_tx_bw, + HTT_TX_PDEV_STATS_NUM_BW_COUNTERS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ax_mu_mimo_tx_bw = %s ", str_buf); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + ARRAY_TO_STRING(str_buf, htt_stats_buf->ofdma_tx_bw, + HTT_TX_PDEV_STATS_NUM_BW_COUNTERS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ofdma_tx_bw = %s ", str_buf); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + ARRAY_TO_STRING(str_buf, htt_stats_buf->tx_stbc, + HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tx_stbc = %s ", str_buf); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + ARRAY_TO_STRING(str_buf, htt_stats_buf->tx_pream, + HTT_TX_PDEV_STATS_NUM_PREAMBLE_TYPES); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tx_pream = %s ", str_buf); + + len += HTT_DBG_OUT(buf + len, buf_len - len, "HE LTF: 1x: %u, 2x: %u, 4x: %u", + htt_stats_buf->tx_he_ltf[1], + htt_stats_buf->tx_he_ltf[2], + htt_stats_buf->tx_he_ltf[3]); + + /* SU GI Stats */ + for (j = 0; j < HTT_TX_PDEV_STATS_NUM_GI_COUNTERS; j++) { + ARRAY_TO_STRING(tx_gi[j], htt_stats_buf->tx_gi[j], + HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tx_gi[%u] = %s ", + j, tx_gi[j]); + } + + /* AC MU-MIMO GI Stats */ + for (j = 0; j < HTT_TX_PDEV_STATS_NUM_GI_COUNTERS; j++) { + ARRAY_TO_STRING(tx_gi[j], htt_stats_buf->ac_mu_mimo_tx_gi[j], + HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS); + len += HTT_DBG_OUT(buf + len, buf_len - len, + "ac_mu_mimo_tx_gi[%u] = %s ", + j, tx_gi[j]); + } + + /* AX MU-MIMO GI Stats */ + for (j = 0; j < HTT_TX_PDEV_STATS_NUM_GI_COUNTERS; j++) { + ARRAY_TO_STRING(tx_gi[j], htt_stats_buf->ax_mu_mimo_tx_gi[j], + HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS); + len += HTT_DBG_OUT(buf + len, buf_len - len, + "ax_mu_mimo_tx_gi[%u] = %s ", + j, tx_gi[j]); + } + + /* DL OFDMA GI Stats */ + for (j = 0; j < HTT_TX_PDEV_STATS_NUM_GI_COUNTERS; j++) { + ARRAY_TO_STRING(tx_gi[j], htt_stats_buf->ofdma_tx_gi[j], + HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ofdma_tx_gi[%u] = %s ", + j, tx_gi[j]); + } + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + ARRAY_TO_STRING(str_buf, htt_stats_buf->tx_dcm, + HTT_TX_PDEV_STATS_NUM_DCM_COUNTERS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tx_dcm = %s\n", str_buf); + + for (j = 0; j < HTT_TX_PEER_STATS_NUM_GI_COUNTERS; j++) + kfree(tx_gi[j]); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void htt_print_rx_pdev_rate_stats_tlv(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_rx_pdev_rate_stats_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + u8 j; + char *rssi_chain[HTT_RX_PDEV_STATS_NUM_SPATIAL_STREAMS]; + char *rx_gi[HTT_RX_PDEV_STATS_NUM_GI_COUNTERS]; + char str_buf[HTT_MAX_STRING_LEN] = {0}; + + for (j = 0; j < HTT_RX_PDEV_STATS_NUM_SPATIAL_STREAMS; j++) + rssi_chain[j] = kmalloc(HTT_MAX_STRING_LEN, GFP_ATOMIC); + + for (j = 0; j < HTT_RX_PDEV_STATS_NUM_GI_COUNTERS; j++) + rx_gi[j] = kmalloc(HTT_MAX_STRING_LEN, GFP_ATOMIC); + + len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_RX_PDEV_RATE_STATS_TLV:"); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mac_id = %u", + htt_stats_buf->mac_id__word & 0xFF); + len += HTT_DBG_OUT(buf + len, buf_len - len, "nsts = %u", + htt_stats_buf->nsts); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_ldpc = %u", + htt_stats_buf->rx_ldpc); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rts_cnt = %u", + htt_stats_buf->rts_cnt); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rssi_mgmt = %u", + htt_stats_buf->rssi_mgmt); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rssi_data = %u", + htt_stats_buf->rssi_data); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rssi_comb = %u", + htt_stats_buf->rssi_comb); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rssi_in_dbm = %d", + htt_stats_buf->rssi_in_dbm); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + ARRAY_TO_STRING(str_buf, htt_stats_buf->rx_mcs, + HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_mcs = %s ", str_buf); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + ARRAY_TO_STRING(str_buf, htt_stats_buf->rx_nss, + HTT_RX_PDEV_STATS_NUM_SPATIAL_STREAMS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_nss = %s ", str_buf); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + ARRAY_TO_STRING(str_buf, htt_stats_buf->rx_dcm, + HTT_RX_PDEV_STATS_NUM_DCM_COUNTERS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_dcm = %s ", str_buf); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + ARRAY_TO_STRING(str_buf, htt_stats_buf->rx_stbc, + HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_stbc = %s ", str_buf); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + ARRAY_TO_STRING(str_buf, htt_stats_buf->rx_bw, + HTT_RX_PDEV_STATS_NUM_BW_COUNTERS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_bw = %s ", str_buf); + + for (j = 0; j < HTT_RX_PDEV_STATS_NUM_SPATIAL_STREAMS; j++) { + ARRAY_TO_STRING(rssi_chain[j], htt_stats_buf->rssi_chain[j], + HTT_RX_PDEV_STATS_NUM_BW_COUNTERS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rssi_chain[%u] = %s ", + j, rssi_chain[j]); + } + + for (j = 0; j < HTT_RX_PDEV_STATS_NUM_GI_COUNTERS; j++) { + ARRAY_TO_STRING(rx_gi[j], htt_stats_buf->rx_gi[j], + HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_gi[%u] = %s ", + j, rx_gi[j]); + } + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + ARRAY_TO_STRING(str_buf, htt_stats_buf->rx_pream, + HTT_RX_PDEV_STATS_NUM_PREAMBLE_TYPES); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_pream = %s", str_buf); + + for (j = 0; j < HTT_RX_PDEV_STATS_NUM_SPATIAL_STREAMS; j++) + kfree(rssi_chain[j]); + + for (j = 0; j < HTT_RX_PDEV_STATS_NUM_GI_COUNTERS; j++) + kfree(rx_gi[j]); + + len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_11ax_su_ext = %u", + htt_stats_buf->rx_11ax_su_ext); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_11ac_mumimo = %u", + htt_stats_buf->rx_11ac_mumimo); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_11ax_mumimo = %u", + htt_stats_buf->rx_11ax_mumimo); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_11ax_ofdma = %u", + htt_stats_buf->rx_11ax_ofdma); + len += HTT_DBG_OUT(buf + len, buf_len - len, "txbf = %u", + htt_stats_buf->txbf); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + ARRAY_TO_STRING(str_buf, htt_stats_buf->rx_legacy_cck_rate, + HTT_RX_PDEV_STATS_NUM_LEGACY_CCK_STATS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_legacy_cck_rate = %s ", + str_buf); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + ARRAY_TO_STRING(str_buf, htt_stats_buf->rx_legacy_ofdm_rate, + HTT_RX_PDEV_STATS_NUM_LEGACY_OFDM_STATS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_legacy_ofdm_rate = %s ", + str_buf); + + len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_active_dur_us_low = %u", + htt_stats_buf->rx_active_dur_us_low); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_active_dur_us_high = %u\n", + htt_stats_buf->rx_active_dur_us_high); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void htt_print_rx_soc_fw_stats_tlv(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_rx_soc_fw_stats_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + + len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_RX_SOC_FW_STATS_TLV:"); + len += HTT_DBG_OUT(buf + len, buf_len - len, "fw_reo_ring_data_msdu = %u", + htt_stats_buf->fw_reo_ring_data_msdu); + len += HTT_DBG_OUT(buf + len, buf_len - len, "fw_to_host_data_msdu_bcmc = %u", + htt_stats_buf->fw_to_host_data_msdu_bcmc); + len += HTT_DBG_OUT(buf + len, buf_len - len, "fw_to_host_data_msdu_uc = %u", + htt_stats_buf->fw_to_host_data_msdu_uc); + len += HTT_DBG_OUT(buf + len, buf_len - len, + "ofld_remote_data_buf_recycle_cnt = %u", + htt_stats_buf->ofld_remote_data_buf_recycle_cnt); + len += HTT_DBG_OUT(buf + len, buf_len - len, + "ofld_remote_free_buf_indication_cnt = %u", + htt_stats_buf->ofld_remote_free_buf_indication_cnt); + len += HTT_DBG_OUT(buf + len, buf_len - len, + "ofld_buf_to_host_data_msdu_uc = %u", + htt_stats_buf->ofld_buf_to_host_data_msdu_uc); + len += HTT_DBG_OUT(buf + len, buf_len - len, + "reo_fw_ring_to_host_data_msdu_uc = %u", + htt_stats_buf->reo_fw_ring_to_host_data_msdu_uc); + len += HTT_DBG_OUT(buf + len, buf_len - len, "wbm_sw_ring_reap = %u", + htt_stats_buf->wbm_sw_ring_reap); + len += HTT_DBG_OUT(buf + len, buf_len - len, "wbm_forward_to_host_cnt = %u", + htt_stats_buf->wbm_forward_to_host_cnt); + len += HTT_DBG_OUT(buf + len, buf_len - len, "wbm_target_recycle_cnt = %u", + htt_stats_buf->wbm_target_recycle_cnt); + len += HTT_DBG_OUT(buf + len, buf_len - len, + "target_refill_ring_recycle_cnt = %u", + htt_stats_buf->target_refill_ring_recycle_cnt); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_rx_soc_fw_refill_ring_empty_tlv_v(const void *tag_buf, + u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_rx_soc_fw_refill_ring_empty_tlv_v *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + char refill_ring_empty_cnt[HTT_MAX_STRING_LEN] = {0}; + u16 num_elems = min_t(u16, (tag_len >> 2), HTT_RX_STATS_REFILL_MAX_RING); + + len += HTT_DBG_OUT(buf + len, buf_len - len, + "HTT_RX_SOC_FW_REFILL_RING_EMPTY_TLV_V:"); + + ARRAY_TO_STRING(refill_ring_empty_cnt, + htt_stats_buf->refill_ring_empty_cnt, + num_elems); + len += HTT_DBG_OUT(buf + len, buf_len - len, "refill_ring_empty_cnt = %s\n", + refill_ring_empty_cnt); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_rx_soc_fw_refill_ring_num_rxdma_err_tlv_v(const void *tag_buf, + u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_rx_soc_fw_refill_ring_num_rxdma_err_tlv_v *htt_stats_buf = + tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + char rxdma_err_cnt[HTT_MAX_STRING_LEN] = {0}; + u16 num_elems = min_t(u16, (tag_len >> 2), HTT_RX_RXDMA_MAX_ERR_CODE); + + len += HTT_DBG_OUT(buf + len, buf_len - len, + "HTT_RX_SOC_FW_REFILL_RING_NUM_RXDMA_ERR_TLV_V:"); + + ARRAY_TO_STRING(rxdma_err_cnt, + htt_stats_buf->rxdma_err, + num_elems); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rxdma_err = %s\n", + rxdma_err_cnt); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_rx_soc_fw_refill_ring_num_reo_err_tlv_v(const void *tag_buf, + u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_rx_soc_fw_refill_ring_num_reo_err_tlv_v *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + char reo_err_cnt[HTT_MAX_STRING_LEN] = {0}; + u16 num_elems = min_t(u16, (tag_len >> 2), HTT_RX_REO_MAX_ERR_CODE); + + len += HTT_DBG_OUT(buf + len, buf_len - len, + "HTT_RX_SOC_FW_REFILL_RING_NUM_REO_ERR_TLV_V:"); + + ARRAY_TO_STRING(reo_err_cnt, + htt_stats_buf->reo_err, + num_elems); + len += HTT_DBG_OUT(buf + len, buf_len - len, "reo_err = %s\n", + reo_err_cnt); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_rx_reo_debug_stats_tlv_v(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_rx_reo_resource_stats_tlv_v *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + + len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_RX_REO_RESOURCE_STATS_TLV:"); + len += HTT_DBG_OUT(buf + len, buf_len - len, "sample_id = %u", + htt_stats_buf->sample_id); + len += HTT_DBG_OUT(buf + len, buf_len - len, "total_max = %u", + htt_stats_buf->total_max); + len += HTT_DBG_OUT(buf + len, buf_len - len, "total_avg = %u", + htt_stats_buf->total_avg); + len += HTT_DBG_OUT(buf + len, buf_len - len, "total_sample = %u", + htt_stats_buf->total_sample); + len += HTT_DBG_OUT(buf + len, buf_len - len, "non_zeros_avg = %u", + htt_stats_buf->non_zeros_avg); + len += HTT_DBG_OUT(buf + len, buf_len - len, "non_zeros_sample = %u", + htt_stats_buf->non_zeros_sample); + len += HTT_DBG_OUT(buf + len, buf_len - len, "last_non_zeros_max = %u", + htt_stats_buf->last_non_zeros_max); + len += HTT_DBG_OUT(buf + len, buf_len - len, "last_non_zeros_min %u", + htt_stats_buf->last_non_zeros_min); + len += HTT_DBG_OUT(buf + len, buf_len - len, "last_non_zeros_avg %u", + htt_stats_buf->last_non_zeros_avg); + len += HTT_DBG_OUT(buf + len, buf_len - len, "last_non_zeros_sample %u\n", + htt_stats_buf->last_non_zeros_sample); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_rx_soc_fw_refill_ring_num_refill_tlv_v(const void *tag_buf, + u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_rx_soc_fw_refill_ring_num_refill_tlv_v *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + char refill_ring_num_refill[HTT_MAX_STRING_LEN] = {0}; + u16 num_elems = min_t(u16, (tag_len >> 2), HTT_RX_STATS_REFILL_MAX_RING); + + len += HTT_DBG_OUT(buf + len, buf_len - len, + "HTT_RX_SOC_FW_REFILL_RING_NUM_REFILL_TLV_V:"); + + ARRAY_TO_STRING(refill_ring_num_refill, + htt_stats_buf->refill_ring_num_refill, + num_elems); + len += HTT_DBG_OUT(buf + len, buf_len - len, "refill_ring_num_refill = %s\n", + refill_ring_num_refill); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void htt_print_rx_pdev_fw_stats_tlv(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_rx_pdev_fw_stats_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + char fw_ring_mgmt_subtype[HTT_MAX_STRING_LEN] = {0}; + char fw_ring_ctrl_subtype[HTT_MAX_STRING_LEN] = {0}; + + len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_RX_PDEV_FW_STATS_TLV:"); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mac_id = %u", + htt_stats_buf->mac_id__word & 0xFF); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ppdu_recvd = %u", + htt_stats_buf->ppdu_recvd); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mpdu_cnt_fcs_ok = %u", + htt_stats_buf->mpdu_cnt_fcs_ok); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mpdu_cnt_fcs_err = %u", + htt_stats_buf->mpdu_cnt_fcs_err); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tcp_msdu_cnt = %u", + htt_stats_buf->tcp_msdu_cnt); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tcp_ack_msdu_cnt = %u", + htt_stats_buf->tcp_ack_msdu_cnt); + len += HTT_DBG_OUT(buf + len, buf_len - len, "udp_msdu_cnt = %u", + htt_stats_buf->udp_msdu_cnt); + len += HTT_DBG_OUT(buf + len, buf_len - len, "other_msdu_cnt = %u", + htt_stats_buf->other_msdu_cnt); + len += HTT_DBG_OUT(buf + len, buf_len - len, "fw_ring_mpdu_ind = %u", + htt_stats_buf->fw_ring_mpdu_ind); + + ARRAY_TO_STRING(fw_ring_mgmt_subtype, + htt_stats_buf->fw_ring_mgmt_subtype, + HTT_STATS_SUBTYPE_MAX); + len += HTT_DBG_OUT(buf + len, buf_len - len, "fw_ring_mgmt_subtype = %s ", + fw_ring_mgmt_subtype); + + ARRAY_TO_STRING(fw_ring_ctrl_subtype, + htt_stats_buf->fw_ring_ctrl_subtype, + HTT_STATS_SUBTYPE_MAX); + len += HTT_DBG_OUT(buf + len, buf_len - len, "fw_ring_ctrl_subtype = %s ", + fw_ring_ctrl_subtype); + len += HTT_DBG_OUT(buf + len, buf_len - len, "fw_ring_mcast_data_msdu = %u", + htt_stats_buf->fw_ring_mcast_data_msdu); + len += HTT_DBG_OUT(buf + len, buf_len - len, "fw_ring_bcast_data_msdu = %u", + htt_stats_buf->fw_ring_bcast_data_msdu); + len += HTT_DBG_OUT(buf + len, buf_len - len, "fw_ring_ucast_data_msdu = %u", + htt_stats_buf->fw_ring_ucast_data_msdu); + len += HTT_DBG_OUT(buf + len, buf_len - len, "fw_ring_null_data_msdu = %u", + htt_stats_buf->fw_ring_null_data_msdu); + len += HTT_DBG_OUT(buf + len, buf_len - len, "fw_ring_mpdu_drop = %u", + htt_stats_buf->fw_ring_mpdu_drop); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ofld_local_data_ind_cnt = %u", + htt_stats_buf->ofld_local_data_ind_cnt); + len += HTT_DBG_OUT(buf + len, buf_len - len, + "ofld_local_data_buf_recycle_cnt = %u", + htt_stats_buf->ofld_local_data_buf_recycle_cnt); + len += HTT_DBG_OUT(buf + len, buf_len - len, "drx_local_data_ind_cnt = %u", + htt_stats_buf->drx_local_data_ind_cnt); + len += HTT_DBG_OUT(buf + len, buf_len - len, + "drx_local_data_buf_recycle_cnt = %u", + htt_stats_buf->drx_local_data_buf_recycle_cnt); + len += HTT_DBG_OUT(buf + len, buf_len - len, "local_nondata_ind_cnt = %u", + htt_stats_buf->local_nondata_ind_cnt); + len += HTT_DBG_OUT(buf + len, buf_len - len, "local_nondata_buf_recycle_cnt = %u", + htt_stats_buf->local_nondata_buf_recycle_cnt); + len += HTT_DBG_OUT(buf + len, buf_len - len, "fw_status_buf_ring_refill_cnt = %u", + htt_stats_buf->fw_status_buf_ring_refill_cnt); + len += HTT_DBG_OUT(buf + len, buf_len - len, "fw_status_buf_ring_empty_cnt = %u", + htt_stats_buf->fw_status_buf_ring_empty_cnt); + len += HTT_DBG_OUT(buf + len, buf_len - len, "fw_pkt_buf_ring_refill_cnt = %u", + htt_stats_buf->fw_pkt_buf_ring_refill_cnt); + len += HTT_DBG_OUT(buf + len, buf_len - len, "fw_pkt_buf_ring_empty_cnt = %u", + htt_stats_buf->fw_pkt_buf_ring_empty_cnt); + len += HTT_DBG_OUT(buf + len, buf_len - len, "fw_link_buf_ring_refill_cnt = %u", + htt_stats_buf->fw_link_buf_ring_refill_cnt); + len += HTT_DBG_OUT(buf + len, buf_len - len, "fw_link_buf_ring_empty_cnt = %u", + htt_stats_buf->fw_link_buf_ring_empty_cnt); + len += HTT_DBG_OUT(buf + len, buf_len - len, "host_pkt_buf_ring_refill_cnt = %u", + htt_stats_buf->host_pkt_buf_ring_refill_cnt); + len += HTT_DBG_OUT(buf + len, buf_len - len, "host_pkt_buf_ring_empty_cnt = %u", + htt_stats_buf->host_pkt_buf_ring_empty_cnt); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mon_pkt_buf_ring_refill_cnt = %u", + htt_stats_buf->mon_pkt_buf_ring_refill_cnt); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mon_pkt_buf_ring_empty_cnt = %u", + htt_stats_buf->mon_pkt_buf_ring_empty_cnt); + len += HTT_DBG_OUT(buf + len, buf_len - len, + "mon_status_buf_ring_refill_cnt = %u", + htt_stats_buf->mon_status_buf_ring_refill_cnt); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mon_status_buf_ring_empty_cnt = %u", + htt_stats_buf->mon_status_buf_ring_empty_cnt); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mon_desc_buf_ring_refill_cnt = %u", + htt_stats_buf->mon_desc_buf_ring_refill_cnt); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mon_desc_buf_ring_empty_cnt = %u", + htt_stats_buf->mon_desc_buf_ring_empty_cnt); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mon_dest_ring_update_cnt = %u", + htt_stats_buf->mon_dest_ring_update_cnt); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mon_dest_ring_full_cnt = %u", + htt_stats_buf->mon_dest_ring_full_cnt); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_suspend_cnt = %u", + htt_stats_buf->rx_suspend_cnt); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_suspend_fail_cnt = %u", + htt_stats_buf->rx_suspend_fail_cnt); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_resume_cnt = %u", + htt_stats_buf->rx_resume_cnt); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_resume_fail_cnt = %u", + htt_stats_buf->rx_resume_fail_cnt); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_ring_switch_cnt = %u", + htt_stats_buf->rx_ring_switch_cnt); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_ring_restore_cnt = %u", + htt_stats_buf->rx_ring_restore_cnt); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_flush_cnt = %u", + htt_stats_buf->rx_flush_cnt); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_recovery_reset_cnt = %u\n", + htt_stats_buf->rx_recovery_reset_cnt); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_rx_pdev_fw_ring_mpdu_err_tlv_v(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_rx_pdev_fw_ring_mpdu_err_tlv_v *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + char fw_ring_mpdu_err[HTT_MAX_STRING_LEN] = {0}; + + len += HTT_DBG_OUT(buf + len, buf_len - len, + "HTT_RX_PDEV_FW_RING_MPDU_ERR_TLV_V:"); + + ARRAY_TO_STRING(fw_ring_mpdu_err, + htt_stats_buf->fw_ring_mpdu_err, + HTT_RX_STATS_RXDMA_MAX_ERR); + len += HTT_DBG_OUT(buf + len, buf_len - len, "fw_ring_mpdu_err = %s\n", + fw_ring_mpdu_err); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_rx_pdev_fw_mpdu_drop_tlv_v(const void *tag_buf, + u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_rx_pdev_fw_mpdu_drop_tlv_v *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + char fw_mpdu_drop[HTT_MAX_STRING_LEN] = {0}; + u16 num_elems = min_t(u16, (tag_len >> 2), HTT_RX_STATS_FW_DROP_REASON_MAX); + + len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_RX_PDEV_FW_MPDU_DROP_TLV_V:"); + + ARRAY_TO_STRING(fw_mpdu_drop, + htt_stats_buf->fw_mpdu_drop, + num_elems); + len += HTT_DBG_OUT(buf + len, buf_len - len, "fw_mpdu_drop = %s\n", fw_mpdu_drop); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_rx_pdev_fw_stats_phy_err_tlv(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_rx_pdev_fw_stats_phy_err_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + char phy_errs[HTT_MAX_STRING_LEN] = {0}; + + len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_RX_PDEV_FW_STATS_PHY_ERR_TLV:"); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mac_id__word = %u", + htt_stats_buf->mac_id__word); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tota_phy_err_nct = %u", + htt_stats_buf->total_phy_err_cnt); + + ARRAY_TO_STRING(phy_errs, + htt_stats_buf->phy_err, + HTT_STATS_PHY_ERR_MAX); + len += HTT_DBG_OUT(buf + len, buf_len - len, "phy_errs = %s\n", phy_errs); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_pdev_cca_stats_hist_tlv(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_pdev_cca_stats_hist_v1_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + + len += HTT_DBG_OUT(buf + len, buf_len - len, "\nHTT_PDEV_CCA_STATS_HIST_TLV:"); + len += HTT_DBG_OUT(buf + len, buf_len - len, "chan_num = %u", + htt_stats_buf->chan_num); + len += HTT_DBG_OUT(buf + len, buf_len - len, "num_records = %u", + htt_stats_buf->num_records); + len += HTT_DBG_OUT(buf + len, buf_len - len, "valid_cca_counters_bitmap = 0x%x", + htt_stats_buf->valid_cca_counters_bitmap); + len += HTT_DBG_OUT(buf + len, buf_len - len, "collection_interval = %u\n", + htt_stats_buf->collection_interval); + + len += HTT_DBG_OUT(buf + len, buf_len - len, + "HTT_PDEV_STATS_CCA_COUNTERS_TLV:(in usec)"); + len += HTT_DBG_OUT(buf + len, buf_len - len, + "| tx_frame| rx_frame| rx_clear| my_rx_frame| cnt| med_rx_idle| med_tx_idle_global| cca_obss|"); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_pdev_stats_cca_counters_tlv(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_pdev_stats_cca_counters_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + + len += HTT_DBG_OUT(buf + len, buf_len - len, + "|%10u| %10u| %10u| %11u| %10u| %11u| %18u| %10u|", + htt_stats_buf->tx_frame_usec, + htt_stats_buf->rx_frame_usec, + htt_stats_buf->rx_clear_usec, + htt_stats_buf->my_rx_frame_usec, + htt_stats_buf->usec_cnt, + htt_stats_buf->med_rx_idle_usec, + htt_stats_buf->med_tx_idle_global_usec, + htt_stats_buf->cca_obss_usec); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void htt_print_hw_stats_whal_tx_tlv(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_hw_stats_whal_tx_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + + len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_HW_STATS_WHAL_TX_TLV:"); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mac_id = %u", + htt_stats_buf->mac_id__word & 0xFF); + len += HTT_DBG_OUT(buf + len, buf_len - len, "last_unpause_ppdu_id = %u", + htt_stats_buf->last_unpause_ppdu_id); + len += HTT_DBG_OUT(buf + len, buf_len - len, "hwsch_unpause_wait_tqm_write = %u", + htt_stats_buf->hwsch_unpause_wait_tqm_write); + len += HTT_DBG_OUT(buf + len, buf_len - len, "hwsch_dummy_tlv_skipped = %u", + htt_stats_buf->hwsch_dummy_tlv_skipped); + len += HTT_DBG_OUT(buf + len, buf_len - len, + "hwsch_misaligned_offset_received = %u", + htt_stats_buf->hwsch_misaligned_offset_received); + len += HTT_DBG_OUT(buf + len, buf_len - len, "hwsch_reset_count = %u", + htt_stats_buf->hwsch_reset_count); + len += HTT_DBG_OUT(buf + len, buf_len - len, "hwsch_dev_reset_war = %u", + htt_stats_buf->hwsch_dev_reset_war); + len += HTT_DBG_OUT(buf + len, buf_len - len, "hwsch_delayed_pause = %u", + htt_stats_buf->hwsch_delayed_pause); + len += HTT_DBG_OUT(buf + len, buf_len - len, "hwsch_long_delayed_pause = %u", + htt_stats_buf->hwsch_long_delayed_pause); + len += HTT_DBG_OUT(buf + len, buf_len - len, "sch_rx_ppdu_no_response = %u", + htt_stats_buf->sch_rx_ppdu_no_response); + len += HTT_DBG_OUT(buf + len, buf_len - len, "sch_selfgen_response = %u", + htt_stats_buf->sch_selfgen_response); + len += HTT_DBG_OUT(buf + len, buf_len - len, "sch_rx_sifs_resp_trigger= %u\n", + htt_stats_buf->sch_rx_sifs_resp_trigger); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_pdev_stats_twt_sessions_tlv(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_pdev_stats_twt_sessions_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + + len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_PDEV_STATS_TWT_SESSIONS_TLV:"); + len += HTT_DBG_OUT(buf + len, buf_len - len, "pdev_id = %u", + htt_stats_buf->pdev_id); + len += HTT_DBG_OUT(buf + len, buf_len - len, "num_sessions = %u\n", + htt_stats_buf->num_sessions); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_pdev_stats_twt_session_tlv(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_pdev_stats_twt_session_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + + len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_PDEV_STATS_TWT_SESSION_TLV:"); + len += HTT_DBG_OUT(buf + len, buf_len - len, "vdev_id = %u", + htt_stats_buf->vdev_id); + len += HTT_DBG_OUT(buf + len, buf_len - len, + "peer_mac = %02x:%02x:%02x:%02x:%02x:%02x", + htt_stats_buf->peer_mac.mac_addr_l32 & 0xFF, + (htt_stats_buf->peer_mac.mac_addr_l32 & 0xFF00) >> 8, + (htt_stats_buf->peer_mac.mac_addr_l32 & 0xFF0000) >> 16, + (htt_stats_buf->peer_mac.mac_addr_l32 & 0xFF000000) >> 24, + (htt_stats_buf->peer_mac.mac_addr_h16 & 0xFF), + (htt_stats_buf->peer_mac.mac_addr_h16 & 0xFF00) >> 8); + len += HTT_DBG_OUT(buf + len, buf_len - len, "flow_id_flags = %u", + htt_stats_buf->flow_id_flags); + len += HTT_DBG_OUT(buf + len, buf_len - len, "dialog_id = %u", + htt_stats_buf->dialog_id); + len += HTT_DBG_OUT(buf + len, buf_len - len, "wake_dura_us = %u", + htt_stats_buf->wake_dura_us); + len += HTT_DBG_OUT(buf + len, buf_len - len, "wake_intvl_us = %u", + htt_stats_buf->wake_intvl_us); + len += HTT_DBG_OUT(buf + len, buf_len - len, "sp_offset_us = %u\n", + htt_stats_buf->sp_offset_us); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_pdev_obss_pd_stats_tlv_v(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_pdev_obss_pd_stats_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + + len += HTT_DBG_OUT(buf + len, buf_len - len, "OBSS Tx success PPDU = %u", + htt_stats_buf->num_obss_tx_ppdu_success); + len += HTT_DBG_OUT(buf + len, buf_len - len, "OBSS Tx failures PPDU = %u\n", + htt_stats_buf->num_obss_tx_ppdu_failure); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void htt_htt_stats_debug_dump(const u32 *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + u32 tlv_len = 0, i = 0, word_len = 0; + + tlv_len = FIELD_GET(HTT_TLV_LEN, *tag_buf) + HTT_TLV_HDR_LEN; + word_len = (tlv_len % 4) == 0 ? (tlv_len / 4) : ((tlv_len / 4) + 1); + len += HTT_DBG_OUT(buf + len, buf_len - len, + "============================================"); + len += HTT_DBG_OUT(buf + len, buf_len - len, + "HKDBG TLV DUMP: (tag_len=%u bytes, words=%u)", + tlv_len, word_len); + + for (i = 0; i + 3 < word_len; i += 4) { + len += HTT_DBG_OUT(buf + len, buf_len - len, + "0x%08x 0x%08x 0x%08x 0x%08x", + tag_buf[i], tag_buf[i + 1], + tag_buf[i + 2], tag_buf[i + 3]); + } + + if (i + 3 == word_len) { + len += HTT_DBG_OUT(buf + len, buf_len - len, "0x%08x 0x%08x 0x%08x ", + tag_buf[i], tag_buf[i + 1], tag_buf[i + 2]); + } else if (i + 2 == word_len) { + len += HTT_DBG_OUT(buf + len, buf_len - len, "0x%08x 0x%08x ", + tag_buf[i], tag_buf[i + 1]); + } else if (i + 1 == word_len) { + len += HTT_DBG_OUT(buf + len, buf_len - len, "0x%08x ", + tag_buf[i]); + } + len += HTT_DBG_OUT(buf + len, buf_len - len, + "============================================"); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static int ath11k_dbg_htt_ext_stats_parse(struct ath11k_base *ab, + u16 tag, u16 len, const void *tag_buf, + void *user_data) +{ + struct debug_htt_stats_req *stats_req = user_data; + + switch (tag) { + case HTT_STATS_TX_PDEV_CMN_TAG: + htt_print_tx_pdev_stats_cmn_tlv(tag_buf, stats_req); + break; + case HTT_STATS_TX_PDEV_UNDERRUN_TAG: + htt_print_tx_pdev_stats_urrn_tlv_v(tag_buf, len, stats_req); + break; + case HTT_STATS_TX_PDEV_SIFS_TAG: + htt_print_tx_pdev_stats_sifs_tlv_v(tag_buf, len, stats_req); + break; + case HTT_STATS_TX_PDEV_FLUSH_TAG: + htt_print_tx_pdev_stats_flush_tlv_v(tag_buf, len, stats_req); + break; + case HTT_STATS_TX_PDEV_PHY_ERR_TAG: + htt_print_tx_pdev_stats_phy_err_tlv_v(tag_buf, len, stats_req); + break; + case HTT_STATS_TX_PDEV_SIFS_HIST_TAG: + htt_print_tx_pdev_stats_sifs_hist_tlv_v(tag_buf, len, stats_req); + break; + + case HTT_STATS_TX_PDEV_TX_PPDU_STATS_TAG: + htt_print_tx_pdev_stats_tx_ppdu_stats_tlv_v(tag_buf, stats_req); + break; + + case HTT_STATS_TX_PDEV_TRIED_MPDU_CNT_HIST_TAG: + htt_print_tx_pdev_stats_tried_mpdu_cnt_hist_tlv_v(tag_buf, len, + stats_req); + break; + + case HTT_STATS_STRING_TAG: + htt_print_stats_string_tlv(tag_buf, len, stats_req); + break; + + case HTT_STATS_TX_HWQ_CMN_TAG: + htt_print_tx_hwq_stats_cmn_tlv(tag_buf, stats_req); + break; + + case HTT_STATS_TX_HWQ_DIFS_LATENCY_TAG: + htt_print_tx_hwq_difs_latency_stats_tlv_v(tag_buf, len, stats_req); + break; + + case HTT_STATS_TX_HWQ_CMD_RESULT_TAG: + htt_print_tx_hwq_cmd_result_stats_tlv_v(tag_buf, len, stats_req); + break; + + case HTT_STATS_TX_HWQ_CMD_STALL_TAG: + htt_print_tx_hwq_cmd_stall_stats_tlv_v(tag_buf, len, stats_req); + break; + + case HTT_STATS_TX_HWQ_FES_STATUS_TAG: + htt_print_tx_hwq_fes_result_stats_tlv_v(tag_buf, len, stats_req); + break; + + case HTT_STATS_TX_HWQ_TRIED_MPDU_CNT_HIST_TAG: + htt_print_tx_hwq_tried_mpdu_cnt_hist_tlv_v(tag_buf, len, stats_req); + break; + + case HTT_STATS_TX_HWQ_TXOP_USED_CNT_HIST_TAG: + htt_print_tx_hwq_txop_used_cnt_hist_tlv_v(tag_buf, len, stats_req); + break; + case HTT_STATS_TX_TQM_GEN_MPDU_TAG: + htt_print_tx_tqm_gen_mpdu_stats_tlv_v(tag_buf, len, stats_req); + break; + + case HTT_STATS_TX_TQM_LIST_MPDU_TAG: + htt_print_tx_tqm_list_mpdu_stats_tlv_v(tag_buf, len, stats_req); + break; + + case HTT_STATS_TX_TQM_LIST_MPDU_CNT_TAG: + htt_print_tx_tqm_list_mpdu_cnt_tlv_v(tag_buf, len, stats_req); + break; + + case HTT_STATS_TX_TQM_CMN_TAG: + htt_print_tx_tqm_cmn_stats_tlv(tag_buf, stats_req); + break; + + case HTT_STATS_TX_TQM_PDEV_TAG: + htt_print_tx_tqm_pdev_stats_tlv_v(tag_buf, stats_req); + break; + + case HTT_STATS_TX_TQM_CMDQ_STATUS_TAG: + htt_print_tx_tqm_cmdq_status_tlv(tag_buf, stats_req); + break; + + case HTT_STATS_TX_DE_EAPOL_PACKETS_TAG: + htt_print_tx_de_eapol_packets_stats_tlv(tag_buf, stats_req); + break; + + case HTT_STATS_TX_DE_CLASSIFY_FAILED_TAG: + htt_print_tx_de_classify_failed_stats_tlv(tag_buf, stats_req); + break; + + case HTT_STATS_TX_DE_CLASSIFY_STATS_TAG: + htt_print_tx_de_classify_stats_tlv(tag_buf, stats_req); + break; + + case HTT_STATS_TX_DE_CLASSIFY_STATUS_TAG: + htt_print_tx_de_classify_status_stats_tlv(tag_buf, stats_req); + break; + + case HTT_STATS_TX_DE_ENQUEUE_PACKETS_TAG: + htt_print_tx_de_enqueue_packets_stats_tlv(tag_buf, stats_req); + break; + + case HTT_STATS_TX_DE_ENQUEUE_DISCARD_TAG: + htt_print_tx_de_enqueue_discard_stats_tlv(tag_buf, stats_req); + break; + + case HTT_STATS_TX_DE_FW2WBM_RING_FULL_HIST_TAG: + htt_print_tx_de_fw2wbm_ring_full_hist_tlv(tag_buf, len, stats_req); + break; + + case HTT_STATS_TX_DE_CMN_TAG: + htt_print_tx_de_cmn_stats_tlv(tag_buf, stats_req); + break; + + case HTT_STATS_RING_IF_TAG: + htt_print_ring_if_stats_tlv(tag_buf, stats_req); + break; + + case HTT_STATS_TX_PDEV_MU_MIMO_STATS_TAG: + htt_print_tx_pdev_mu_mimo_sch_stats_tlv(tag_buf, stats_req); + break; + + case HTT_STATS_SFM_CMN_TAG: + htt_print_sfm_cmn_tlv(tag_buf, stats_req); + break; + + case HTT_STATS_SRING_STATS_TAG: + htt_print_sring_stats_tlv(tag_buf, stats_req); + break; + + case HTT_STATS_RX_PDEV_FW_STATS_TAG: + htt_print_rx_pdev_fw_stats_tlv(tag_buf, stats_req); + break; + + case HTT_STATS_RX_PDEV_FW_RING_MPDU_ERR_TAG: + htt_print_rx_pdev_fw_ring_mpdu_err_tlv_v(tag_buf, stats_req); + break; + + case HTT_STATS_RX_PDEV_FW_MPDU_DROP_TAG: + htt_print_rx_pdev_fw_mpdu_drop_tlv_v(tag_buf, len, stats_req); + break; + + case HTT_STATS_RX_SOC_FW_STATS_TAG: + htt_print_rx_soc_fw_stats_tlv(tag_buf, stats_req); + break; + + case HTT_STATS_RX_SOC_FW_REFILL_RING_EMPTY_TAG: + htt_print_rx_soc_fw_refill_ring_empty_tlv_v(tag_buf, len, stats_req); + break; + + case HTT_STATS_RX_SOC_FW_REFILL_RING_NUM_REFILL_TAG: + htt_print_rx_soc_fw_refill_ring_num_refill_tlv_v( + tag_buf, len, stats_req); + break; + case HTT_STATS_RX_REFILL_RXDMA_ERR_TAG: + htt_print_rx_soc_fw_refill_ring_num_rxdma_err_tlv_v( + tag_buf, len, stats_req); + break; + + case HTT_STATS_RX_REFILL_REO_ERR_TAG: + htt_print_rx_soc_fw_refill_ring_num_reo_err_tlv_v( + tag_buf, len, stats_req); + break; + + case HTT_STATS_RX_REO_RESOURCE_STATS_TAG: + htt_print_rx_reo_debug_stats_tlv_v( + tag_buf, stats_req); + break; + case HTT_STATS_RX_PDEV_FW_STATS_PHY_ERR_TAG: + htt_print_rx_pdev_fw_stats_phy_err_tlv(tag_buf, stats_req); + break; + + case HTT_STATS_TX_PDEV_RATE_STATS_TAG: + htt_print_tx_pdev_rate_stats_tlv(tag_buf, stats_req); + break; + + case HTT_STATS_RX_PDEV_RATE_STATS_TAG: + htt_print_rx_pdev_rate_stats_tlv(tag_buf, stats_req); + break; + + case HTT_STATS_TX_PDEV_SCHEDULER_TXQ_STATS_TAG: + htt_print_tx_pdev_stats_sched_per_txq_tlv(tag_buf, stats_req); + break; + case HTT_STATS_TX_SCHED_CMN_TAG: + htt_print_stats_tx_sched_cmn_tlv(tag_buf, stats_req); + break; + + case HTT_STATS_TX_PDEV_MPDU_STATS_TAG: + htt_print_tx_pdev_mu_mimo_mpdu_stats_tlv(tag_buf, stats_req); + break; + + case HTT_STATS_SCHED_TXQ_CMD_POSTED_TAG: + htt_print_sched_txq_cmd_posted_tlv_v(tag_buf, len, stats_req); + break; + + case HTT_STATS_RING_IF_CMN_TAG: + htt_print_ring_if_cmn_tlv(tag_buf, stats_req); + break; + + case HTT_STATS_SFM_CLIENT_USER_TAG: + htt_print_sfm_client_user_tlv_v(tag_buf, len, stats_req); + break; + + case HTT_STATS_SFM_CLIENT_TAG: + htt_print_sfm_client_tlv(tag_buf, stats_req); + break; + + case HTT_STATS_TX_TQM_ERROR_STATS_TAG: + htt_print_tx_tqm_error_stats_tlv(tag_buf, stats_req); + break; + + case HTT_STATS_SCHED_TXQ_CMD_REAPED_TAG: + htt_print_sched_txq_cmd_reaped_tlv_v(tag_buf, len, stats_req); + break; + + case HTT_STATS_SRING_CMN_TAG: + htt_print_sring_cmn_tlv(tag_buf, stats_req); + break; + + case HTT_STATS_TX_SOUNDING_STATS_TAG: + htt_print_tx_sounding_stats_tlv(tag_buf, stats_req); + break; + + case HTT_STATS_TX_SELFGEN_AC_ERR_STATS_TAG: + htt_print_tx_selfgen_ac_err_stats_tlv(tag_buf, stats_req); + break; + + case HTT_STATS_TX_SELFGEN_CMN_STATS_TAG: + htt_print_tx_selfgen_cmn_stats_tlv(tag_buf, stats_req); + break; + + case HTT_STATS_TX_SELFGEN_AC_STATS_TAG: + htt_print_tx_selfgen_ac_stats_tlv(tag_buf, stats_req); + break; + + case HTT_STATS_TX_SELFGEN_AX_STATS_TAG: + htt_print_tx_selfgen_ax_stats_tlv(tag_buf, stats_req); + break; + + case HTT_STATS_TX_SELFGEN_AX_ERR_STATS_TAG: + htt_print_tx_selfgen_ax_err_stats_tlv(tag_buf, stats_req); + break; + + case HTT_STATS_TX_HWQ_MUMIMO_SCH_STATS_TAG: + htt_print_tx_hwq_mu_mimo_sch_stats_tlv(tag_buf, stats_req); + break; + + case HTT_STATS_TX_HWQ_MUMIMO_MPDU_STATS_TAG: + htt_print_tx_hwq_mu_mimo_mpdu_stats_tlv(tag_buf, stats_req); + break; + + case HTT_STATS_TX_HWQ_MUMIMO_CMN_STATS_TAG: + htt_print_tx_hwq_mu_mimo_cmn_stats_tlv(tag_buf, stats_req); + break; + + case HTT_STATS_HW_INTR_MISC_TAG: + htt_print_hw_stats_intr_misc_tlv(tag_buf, stats_req); + break; + + case HTT_STATS_HW_WD_TIMEOUT_TAG: + htt_print_hw_stats_wd_timeout_tlv(tag_buf, stats_req); + break; + + case HTT_STATS_HW_PDEV_ERRS_TAG: + htt_print_hw_stats_pdev_errs_tlv(tag_buf, stats_req); + break; + + case HTT_STATS_COUNTER_NAME_TAG: + htt_print_counter_tlv(tag_buf, stats_req); + break; + + case HTT_STATS_TX_TID_DETAILS_TAG: + htt_print_tx_tid_stats_tlv(tag_buf, stats_req); + break; + + case HTT_STATS_TX_TID_DETAILS_V1_TAG: + htt_print_tx_tid_stats_v1_tlv(tag_buf, stats_req); + break; + + case HTT_STATS_RX_TID_DETAILS_TAG: + htt_print_rx_tid_stats_tlv(tag_buf, stats_req); + break; + + case HTT_STATS_PEER_STATS_CMN_TAG: + htt_print_peer_stats_cmn_tlv(tag_buf, stats_req); + break; + + case HTT_STATS_PEER_DETAILS_TAG: + htt_print_peer_details_tlv(tag_buf, stats_req); + break; + + case HTT_STATS_PEER_MSDU_FLOWQ_TAG: + htt_print_msdu_flow_stats_tlv(tag_buf, stats_req); + break; + + case HTT_STATS_PEER_TX_RATE_STATS_TAG: + htt_print_tx_peer_rate_stats_tlv(tag_buf, stats_req); + break; + + case HTT_STATS_PEER_RX_RATE_STATS_TAG: + htt_print_rx_peer_rate_stats_tlv(tag_buf, stats_req); + break; + + case HTT_STATS_TX_DE_COMPL_STATS_TAG: + htt_print_tx_de_compl_stats_tlv(tag_buf, stats_req); + break; + + case HTT_STATS_PDEV_CCA_1SEC_HIST_TAG: + case HTT_STATS_PDEV_CCA_100MSEC_HIST_TAG: + case HTT_STATS_PDEV_CCA_STAT_CUMULATIVE_TAG: + htt_print_pdev_cca_stats_hist_tlv(tag_buf, stats_req); + break; + + case HTT_STATS_PDEV_CCA_COUNTERS_TAG: + htt_print_pdev_stats_cca_counters_tlv(tag_buf, stats_req); + break; + + case HTT_STATS_WHAL_TX_TAG: + htt_print_hw_stats_whal_tx_tlv(tag_buf, stats_req); + break; + + case HTT_STATS_PDEV_TWT_SESSIONS_TAG: + htt_print_pdev_stats_twt_sessions_tlv(tag_buf, stats_req); + break; + + case HTT_STATS_PDEV_TWT_SESSION_TAG: + htt_print_pdev_stats_twt_session_tlv(tag_buf, stats_req); + break; + + case HTT_STATS_SCHED_TXQ_SCHED_ORDER_SU_TAG: + htt_print_sched_txq_sched_order_su_tlv_v(tag_buf, len, stats_req); + break; + + case HTT_STATS_SCHED_TXQ_SCHED_INELIGIBILITY_TAG: + htt_print_sched_txq_sched_ineligibility_tlv_v(tag_buf, len, stats_req); + break; + + case HTT_STATS_PDEV_OBSS_PD_TAG: + htt_print_pdev_obss_pd_stats_tlv_v(tag_buf, stats_req); + break; + default: + break; + } + + return 0; +} + +void ath11k_dbg_htt_ext_stats_handler(struct ath11k_base *ab, + struct sk_buff *skb) +{ + struct ath11k_htt_extd_stats_msg *msg; + struct debug_htt_stats_req *stats_req; + struct ath11k *ar; + u32 len; + u64 cookie; + int ret; + u8 pdev_id; + + msg = (struct ath11k_htt_extd_stats_msg *)skb->data; + cookie = msg->cookie; + + if (FIELD_GET(HTT_STATS_COOKIE_MSB, cookie) != HTT_STATS_MAGIC_VALUE) { + ath11k_warn(ab, "received invalid htt ext stats event\n"); + return; + } + + pdev_id = FIELD_GET(HTT_STATS_COOKIE_LSB, cookie); + rcu_read_lock(); + ar = ath11k_mac_get_ar_by_pdev_id(ab, pdev_id); + rcu_read_unlock(); + if (!ar) { + ath11k_warn(ab, "failed to get ar for pdev_id %d\n", pdev_id); + return; + } + + stats_req = ar->debug.htt_stats.stats_req; + if (!stats_req) + return; + + spin_lock_bh(&ar->debug.htt_stats.lock); + if (stats_req->done) { + spin_unlock_bh(&ar->debug.htt_stats.lock); + return; + } + stats_req->done = true; + spin_unlock_bh(&ar->debug.htt_stats.lock); + + len = FIELD_GET(HTT_T2H_EXT_STATS_INFO1_LENGTH, msg->info1); + ret = ath11k_dp_htt_tlv_iter(ab, msg->data, len, + ath11k_dbg_htt_ext_stats_parse, + stats_req); + if (ret) + ath11k_warn(ab, "Failed to parse tlv %d\n", ret); + + complete(&stats_req->cmpln); +} + +static ssize_t ath11k_read_htt_stats_type(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath11k *ar = file->private_data; + char buf[32]; + size_t len; + + len = scnprintf(buf, sizeof(buf), "%u\n", ar->debug.htt_stats.type); + + return simple_read_from_buffer(user_buf, count, ppos, buf, len); +} + +static ssize_t ath11k_write_htt_stats_type(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath11k *ar = file->private_data; + u8 type; + int ret; + + ret = kstrtou8_from_user(user_buf, count, 0, &type); + if (ret) + return ret; + + if (type >= ATH11K_DBG_HTT_NUM_EXT_STATS) + return -E2BIG; + + if (type == ATH11K_DBG_HTT_EXT_STATS_RESET || + type == ATH11K_DBG_HTT_EXT_STATS_PEER_INFO) + return -EPERM; + + ar->debug.htt_stats.type = type; + + ret = count; + + return ret; +} + +static const struct file_operations fops_htt_stats_type = { + .read = ath11k_read_htt_stats_type, + .write = ath11k_write_htt_stats_type, + .open = simple_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +static int ath11k_prep_htt_stats_cfg_params(struct ath11k *ar, u8 type, + const u8 *mac_addr, + struct htt_ext_stats_cfg_params *cfg_params) +{ + if (!cfg_params) + return -EINVAL; + + switch (type) { + case ATH11K_DBG_HTT_EXT_STATS_PDEV_TX_HWQ: + case ATH11K_DBG_HTT_EXT_STATS_TX_MU_HWQ: + cfg_params->cfg0 = HTT_STAT_DEFAULT_CFG0_ALL_HWQS; + break; + case ATH11K_DBG_HTT_EXT_STATS_PDEV_TX_SCHED: + cfg_params->cfg0 = HTT_STAT_DEFAULT_CFG0_ALL_TXQS; + break; + case ATH11K_DBG_HTT_EXT_STATS_TQM_CMDQ: + cfg_params->cfg0 = HTT_STAT_DEFAULT_CFG0_ALL_CMDQS; + break; + case ATH11K_DBG_HTT_EXT_STATS_PEER_INFO: + cfg_params->cfg0 = HTT_STAT_PEER_INFO_MAC_ADDR; + cfg_params->cfg0 |= FIELD_PREP(GENMASK(15, 1), + HTT_PEER_STATS_REQ_MODE_FLUSH_TQM); + cfg_params->cfg1 = HTT_STAT_DEFAULT_PEER_REQ_TYPE; + cfg_params->cfg2 |= FIELD_PREP(GENMASK(7, 0), mac_addr[0]); + cfg_params->cfg2 |= FIELD_PREP(GENMASK(15, 8), mac_addr[1]); + cfg_params->cfg2 |= FIELD_PREP(GENMASK(23, 16), mac_addr[2]); + cfg_params->cfg2 |= FIELD_PREP(GENMASK(31, 24), mac_addr[3]); + cfg_params->cfg3 |= FIELD_PREP(GENMASK(7, 0), mac_addr[4]); + cfg_params->cfg3 |= FIELD_PREP(GENMASK(15, 8), mac_addr[5]); + break; + case ATH11K_DBG_HTT_EXT_STATS_RING_IF_INFO: + case ATH11K_DBG_HTT_EXT_STATS_SRNG_INFO: + cfg_params->cfg0 = HTT_STAT_DEFAULT_CFG0_ALL_RINGS; + break; + case ATH11K_DBG_HTT_EXT_STATS_ACTIVE_PEERS_LIST: + cfg_params->cfg0 = HTT_STAT_DEFAULT_CFG0_ACTIVE_PEERS; + break; + case ATH11K_DBG_HTT_EXT_STATS_PDEV_CCA_STATS: + cfg_params->cfg0 = HTT_STAT_DEFAULT_CFG0_CCA_CUMULATIVE; + break; + case ATH11K_DBG_HTT_EXT_STATS_TX_SOUNDING_INFO: + cfg_params->cfg0 = HTT_STAT_DEFAULT_CFG0_ACTIVE_VDEVS; + break; + default: + break; + } + + return 0; +} + +int ath11k_dbg_htt_stats_req(struct ath11k *ar) +{ + struct debug_htt_stats_req *stats_req = ar->debug.htt_stats.stats_req; + u8 type = stats_req->type; + u64 cookie = 0; + int ret, pdev_id = ar->pdev->pdev_id; + struct htt_ext_stats_cfg_params cfg_params = { 0 }; + + init_completion(&stats_req->cmpln); + + stats_req->done = false; + stats_req->pdev_id = pdev_id; + + cookie = FIELD_PREP(HTT_STATS_COOKIE_MSB, HTT_STATS_MAGIC_VALUE) | + FIELD_PREP(HTT_STATS_COOKIE_LSB, pdev_id); + + ret = ath11k_prep_htt_stats_cfg_params(ar, type, stats_req->peer_addr, + &cfg_params); + if (ret) { + ath11k_warn(ar->ab, "failed to set htt stats cfg params: %d\n", ret); + return ret; + } + + ret = ath11k_dp_tx_htt_h2t_ext_stats_req(ar, type, &cfg_params, cookie); + if (ret) { + ath11k_warn(ar->ab, "failed to send htt stats request: %d\n", ret); + mutex_unlock(&ar->conf_mutex); + return ret; + } + + while (!wait_for_completion_timeout(&stats_req->cmpln, 3 * HZ)) { + spin_lock_bh(&ar->debug.htt_stats.lock); + if (!stats_req->done) { + stats_req->done = true; + spin_unlock_bh(&ar->debug.htt_stats.lock); + ath11k_warn(ar->ab, "stats request timed out\n"); + return -ETIMEDOUT; + } + spin_unlock_bh(&ar->debug.htt_stats.lock); + } + + return 0; +} + +static int ath11k_open_htt_stats(struct inode *inode, struct file *file) +{ + struct ath11k *ar = inode->i_private; + struct debug_htt_stats_req *stats_req; + u8 type = ar->debug.htt_stats.type; + int ret; + + if (type == ATH11K_DBG_HTT_EXT_STATS_RESET) + return -EPERM; + + stats_req = vzalloc(sizeof(*stats_req) + ATH11K_HTT_STATS_BUF_SIZE); + if (!stats_req) + return -ENOMEM; + + mutex_lock(&ar->conf_mutex); + ar->debug.htt_stats.stats_req = stats_req; + stats_req->type = type; + ret = ath11k_dbg_htt_stats_req(ar); + mutex_unlock(&ar->conf_mutex); + if (ret < 0) + goto out; + + file->private_data = stats_req; + return 0; +out: + vfree(stats_req); + return ret; +} + +static int ath11k_release_htt_stats(struct inode *inode, struct file *file) +{ + vfree(file->private_data); + return 0; +} + +static ssize_t ath11k_read_htt_stats(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct debug_htt_stats_req *stats_req = file->private_data; + char *buf; + u32 length = 0; + + buf = stats_req->buf; + length = min_t(u32, stats_req->buf_len, ATH11K_HTT_STATS_BUF_SIZE); + return simple_read_from_buffer(user_buf, count, ppos, buf, length); +} + +static const struct file_operations fops_dump_htt_stats = { + .open = ath11k_open_htt_stats, + .release = ath11k_release_htt_stats, + .read = ath11k_read_htt_stats, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +static ssize_t ath11k_read_htt_stats_reset(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath11k *ar = file->private_data; + char buf[32]; + size_t len; + + len = scnprintf(buf, sizeof(buf), "%u\n", ar->debug.htt_stats.reset); + + return simple_read_from_buffer(user_buf, count, ppos, buf, len); +} + +static ssize_t ath11k_write_htt_stats_reset(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath11k *ar = file->private_data; + u8 type; + struct htt_ext_stats_cfg_params cfg_params = { 0 }; + int ret; + + ret = kstrtou8_from_user(user_buf, count, 0, &type); + if (ret) + return ret; + + if (type >= ATH11K_DBG_HTT_NUM_EXT_STATS || + type == ATH11K_DBG_HTT_EXT_STATS_RESET) + return -E2BIG; + + mutex_lock(&ar->conf_mutex); + cfg_params.cfg0 = HTT_STAT_DEFAULT_RESET_START_OFFSET; + cfg_params.cfg1 = 1 << (cfg_params.cfg0 + type); + ret = ath11k_dp_tx_htt_h2t_ext_stats_req(ar, + ATH11K_DBG_HTT_EXT_STATS_RESET, + &cfg_params, + 0ULL); + if (ret) { + ath11k_warn(ar->ab, "failed to send htt stats request: %d\n", ret); + mutex_unlock(&ar->conf_mutex); + return ret; + } + + ar->debug.htt_stats.reset = type; + mutex_unlock(&ar->conf_mutex); + + ret = count; + + return ret; +} + +static const struct file_operations fops_htt_stats_reset = { + .read = ath11k_read_htt_stats_reset, + .write = ath11k_write_htt_stats_reset, + .open = simple_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +void ath11k_debug_htt_stats_init(struct ath11k *ar) +{ + spin_lock_init(&ar->debug.htt_stats.lock); + debugfs_create_file("htt_stats_type", 0600, ar->debug.debugfs_pdev, + ar, &fops_htt_stats_type); + debugfs_create_file("htt_stats", 0400, ar->debug.debugfs_pdev, + ar, &fops_dump_htt_stats); + debugfs_create_file("htt_stats_reset", 0600, ar->debug.debugfs_pdev, + ar, &fops_htt_stats_reset); +} diff --git a/drivers/net/wireless/ath/ath11k/debug_htt_stats.h b/drivers/net/wireless/ath/ath11k/debug_htt_stats.h new file mode 100644 index 0000000000000..618f1946bf499 --- /dev/null +++ b/drivers/net/wireless/ath/ath11k/debug_htt_stats.h @@ -0,0 +1,1620 @@ +/* SPDX-License-Identifier: BSD-3-Clause-Clear */ +/* + * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. + */ + +#ifndef DEBUG_HTT_STATS_H +#define DEBUG_HTT_STATS_H + +#define HTT_STATS_COOKIE_LSB GENMASK_ULL(31, 0) +#define HTT_STATS_COOKIE_MSB GENMASK_ULL(63, 32) +#define HTT_STATS_MAGIC_VALUE 0xF0F0F0F0 + +enum htt_tlv_tag_t { + HTT_STATS_TX_PDEV_CMN_TAG = 0, + HTT_STATS_TX_PDEV_UNDERRUN_TAG = 1, + HTT_STATS_TX_PDEV_SIFS_TAG = 2, + HTT_STATS_TX_PDEV_FLUSH_TAG = 3, + HTT_STATS_TX_PDEV_PHY_ERR_TAG = 4, + HTT_STATS_STRING_TAG = 5, + HTT_STATS_TX_HWQ_CMN_TAG = 6, + HTT_STATS_TX_HWQ_DIFS_LATENCY_TAG = 7, + HTT_STATS_TX_HWQ_CMD_RESULT_TAG = 8, + HTT_STATS_TX_HWQ_CMD_STALL_TAG = 9, + HTT_STATS_TX_HWQ_FES_STATUS_TAG = 10, + HTT_STATS_TX_TQM_GEN_MPDU_TAG = 11, + HTT_STATS_TX_TQM_LIST_MPDU_TAG = 12, + HTT_STATS_TX_TQM_LIST_MPDU_CNT_TAG = 13, + HTT_STATS_TX_TQM_CMN_TAG = 14, + HTT_STATS_TX_TQM_PDEV_TAG = 15, + HTT_STATS_TX_TQM_CMDQ_STATUS_TAG = 16, + HTT_STATS_TX_DE_EAPOL_PACKETS_TAG = 17, + HTT_STATS_TX_DE_CLASSIFY_FAILED_TAG = 18, + HTT_STATS_TX_DE_CLASSIFY_STATS_TAG = 19, + HTT_STATS_TX_DE_CLASSIFY_STATUS_TAG = 20, + HTT_STATS_TX_DE_ENQUEUE_PACKETS_TAG = 21, + HTT_STATS_TX_DE_ENQUEUE_DISCARD_TAG = 22, + HTT_STATS_TX_DE_CMN_TAG = 23, + HTT_STATS_RING_IF_TAG = 24, + HTT_STATS_TX_PDEV_MU_MIMO_STATS_TAG = 25, + HTT_STATS_SFM_CMN_TAG = 26, + HTT_STATS_SRING_STATS_TAG = 27, + HTT_STATS_RX_PDEV_FW_STATS_TAG = 28, + HTT_STATS_RX_PDEV_FW_RING_MPDU_ERR_TAG = 29, + HTT_STATS_RX_PDEV_FW_MPDU_DROP_TAG = 30, + HTT_STATS_RX_SOC_FW_STATS_TAG = 31, + HTT_STATS_RX_SOC_FW_REFILL_RING_EMPTY_TAG = 32, + HTT_STATS_RX_SOC_FW_REFILL_RING_NUM_REFILL_TAG = 33, + HTT_STATS_TX_PDEV_RATE_STATS_TAG = 34, + HTT_STATS_RX_PDEV_RATE_STATS_TAG = 35, + HTT_STATS_TX_PDEV_SCHEDULER_TXQ_STATS_TAG = 36, + HTT_STATS_TX_SCHED_CMN_TAG = 37, + HTT_STATS_TX_PDEV_MUMIMO_MPDU_STATS_TAG = 38, + HTT_STATS_SCHED_TXQ_CMD_POSTED_TAG = 39, + HTT_STATS_RING_IF_CMN_TAG = 40, + HTT_STATS_SFM_CLIENT_USER_TAG = 41, + HTT_STATS_SFM_CLIENT_TAG = 42, + HTT_STATS_TX_TQM_ERROR_STATS_TAG = 43, + HTT_STATS_SCHED_TXQ_CMD_REAPED_TAG = 44, + HTT_STATS_SRING_CMN_TAG = 45, + HTT_STATS_TX_SELFGEN_AC_ERR_STATS_TAG = 46, + HTT_STATS_TX_SELFGEN_CMN_STATS_TAG = 47, + HTT_STATS_TX_SELFGEN_AC_STATS_TAG = 48, + HTT_STATS_TX_SELFGEN_AX_STATS_TAG = 49, + HTT_STATS_TX_SELFGEN_AX_ERR_STATS_TAG = 50, + HTT_STATS_TX_HWQ_MUMIMO_SCH_STATS_TAG = 51, + HTT_STATS_TX_HWQ_MUMIMO_MPDU_STATS_TAG = 52, + HTT_STATS_TX_HWQ_MUMIMO_CMN_STATS_TAG = 53, + HTT_STATS_HW_INTR_MISC_TAG = 54, + HTT_STATS_HW_WD_TIMEOUT_TAG = 55, + HTT_STATS_HW_PDEV_ERRS_TAG = 56, + HTT_STATS_COUNTER_NAME_TAG = 57, + HTT_STATS_TX_TID_DETAILS_TAG = 58, + HTT_STATS_RX_TID_DETAILS_TAG = 59, + HTT_STATS_PEER_STATS_CMN_TAG = 60, + HTT_STATS_PEER_DETAILS_TAG = 61, + HTT_STATS_PEER_TX_RATE_STATS_TAG = 62, + HTT_STATS_PEER_RX_RATE_STATS_TAG = 63, + HTT_STATS_PEER_MSDU_FLOWQ_TAG = 64, + HTT_STATS_TX_DE_COMPL_STATS_TAG = 65, + HTT_STATS_WHAL_TX_TAG = 66, + HTT_STATS_TX_PDEV_SIFS_HIST_TAG = 67, + HTT_STATS_RX_PDEV_FW_STATS_PHY_ERR_TAG = 68, + HTT_STATS_TX_TID_DETAILS_V1_TAG = 69, + HTT_STATS_PDEV_CCA_1SEC_HIST_TAG = 70, + HTT_STATS_PDEV_CCA_100MSEC_HIST_TAG = 71, + HTT_STATS_PDEV_CCA_STAT_CUMULATIVE_TAG = 72, + HTT_STATS_PDEV_CCA_COUNTERS_TAG = 73, + HTT_STATS_TX_PDEV_MPDU_STATS_TAG = 74, + HTT_STATS_PDEV_TWT_SESSIONS_TAG = 75, + HTT_STATS_PDEV_TWT_SESSION_TAG = 76, + HTT_STATS_RX_REFILL_RXDMA_ERR_TAG = 77, + HTT_STATS_RX_REFILL_REO_ERR_TAG = 78, + HTT_STATS_RX_REO_RESOURCE_STATS_TAG = 79, + HTT_STATS_TX_SOUNDING_STATS_TAG = 80, + HTT_STATS_TX_PDEV_TX_PPDU_STATS_TAG = 81, + HTT_STATS_TX_PDEV_TRIED_MPDU_CNT_HIST_TAG = 82, + HTT_STATS_TX_HWQ_TRIED_MPDU_CNT_HIST_TAG = 83, + HTT_STATS_TX_HWQ_TXOP_USED_CNT_HIST_TAG = 84, + HTT_STATS_TX_DE_FW2WBM_RING_FULL_HIST_TAG = 85, + HTT_STATS_SCHED_TXQ_SCHED_ORDER_SU_TAG = 86, + HTT_STATS_SCHED_TXQ_SCHED_INELIGIBILITY_TAG = 87, + HTT_STATS_PDEV_OBSS_PD_TAG = 88, + + HTT_STATS_MAX_TAG, +}; + +#define HTT_STATS_MAX_STRING_SZ32 4 +#define HTT_STATS_MACID_INVALID 0xff +#define HTT_TX_HWQ_MAX_DIFS_LATENCY_BINS 10 +#define HTT_TX_HWQ_MAX_CMD_RESULT_STATS 13 +#define HTT_TX_HWQ_MAX_CMD_STALL_STATS 5 +#define HTT_TX_HWQ_MAX_FES_RESULT_STATS 10 + +enum htt_tx_pdev_underrun_enum { + HTT_STATS_TX_PDEV_NO_DATA_UNDERRUN = 0, + HTT_STATS_TX_PDEV_DATA_UNDERRUN_BETWEEN_MPDU = 1, + HTT_STATS_TX_PDEV_DATA_UNDERRUN_WITHIN_MPDU = 2, + HTT_TX_PDEV_MAX_URRN_STATS = 3, +}; + +#define HTT_TX_PDEV_MAX_FLUSH_REASON_STATS 71 +#define HTT_TX_PDEV_MAX_SIFS_BURST_STATS 9 +#define HTT_TX_PDEV_MAX_SIFS_BURST_HIST_STATS 10 +#define HTT_TX_PDEV_MAX_PHY_ERR_STATS 18 +#define HTT_TX_PDEV_SCHED_TX_MODE_MAX 4 +#define HTT_TX_PDEV_NUM_SCHED_ORDER_LOG 20 + +#define HTT_RX_STATS_REFILL_MAX_RING 4 +#define HTT_RX_STATS_RXDMA_MAX_ERR 16 +#define HTT_RX_STATS_FW_DROP_REASON_MAX 16 + +/* Bytes stored in little endian order */ +/* Length should be multiple of DWORD */ +struct htt_stats_string_tlv { + u32 data[0]; /* Can be variable length */ +} __packed; + +/* == TX PDEV STATS == */ +struct htt_tx_pdev_stats_cmn_tlv { + u32 mac_id__word; + u32 hw_queued; + u32 hw_reaped; + u32 underrun; + u32 hw_paused; + u32 hw_flush; + u32 hw_filt; + u32 tx_abort; + u32 mpdu_requed; + u32 tx_xretry; + u32 data_rc; + u32 mpdu_dropped_xretry; + u32 illgl_rate_phy_err; + u32 cont_xretry; + u32 tx_timeout; + u32 pdev_resets; + u32 phy_underrun; + u32 txop_ovf; + u32 seq_posted; + u32 seq_failed_queueing; + u32 seq_completed; + u32 seq_restarted; + u32 mu_seq_posted; + u32 seq_switch_hw_paused; + u32 next_seq_posted_dsr; + u32 seq_posted_isr; + u32 seq_ctrl_cached; + u32 mpdu_count_tqm; + u32 msdu_count_tqm; + u32 mpdu_removed_tqm; + u32 msdu_removed_tqm; + u32 mpdus_sw_flush; + u32 mpdus_hw_filter; + u32 mpdus_truncated; + u32 mpdus_ack_failed; + u32 mpdus_expired; + u32 mpdus_seq_hw_retry; + u32 ack_tlv_proc; + u32 coex_abort_mpdu_cnt_valid; + u32 coex_abort_mpdu_cnt; + u32 num_total_ppdus_tried_ota; + u32 num_data_ppdus_tried_ota; + u32 local_ctrl_mgmt_enqued; + u32 local_ctrl_mgmt_freed; + u32 local_data_enqued; + u32 local_data_freed; + u32 mpdu_tried; + u32 isr_wait_seq_posted; + + u32 tx_active_dur_us_low; + u32 tx_active_dur_us_high; +}; + +/* NOTE: Variable length TLV, use length spec to infer array size */ +struct htt_tx_pdev_stats_urrn_tlv_v { + u32 urrn_stats[0]; /* HTT_TX_PDEV_MAX_URRN_STATS */ +}; + +/* NOTE: Variable length TLV, use length spec to infer array size */ +struct htt_tx_pdev_stats_flush_tlv_v { + u32 flush_errs[0]; /* HTT_TX_PDEV_MAX_FLUSH_REASON_STATS */ +}; + +/* NOTE: Variable length TLV, use length spec to infer array size */ +struct htt_tx_pdev_stats_sifs_tlv_v { + u32 sifs_status[0]; /* HTT_TX_PDEV_MAX_SIFS_BURST_STATS */ +}; + +/* NOTE: Variable length TLV, use length spec to infer array size */ +struct htt_tx_pdev_stats_phy_err_tlv_v { + u32 phy_errs[0]; /* HTT_TX_PDEV_MAX_PHY_ERR_STATS */ +}; + +/* NOTE: Variable length TLV, use length spec to infer array size */ +struct htt_tx_pdev_stats_sifs_hist_tlv_v { + u32 sifs_hist_status[0]; /* HTT_TX_PDEV_SIFS_BURST_HIST_STATS */ +}; + +struct htt_tx_pdev_stats_tx_ppdu_stats_tlv_v { + u32 num_data_ppdus_legacy_su; + u32 num_data_ppdus_ac_su; + u32 num_data_ppdus_ax_su; + u32 num_data_ppdus_ac_su_txbf; + u32 num_data_ppdus_ax_su_txbf; +}; + +/* NOTE: Variable length TLV, use length spec to infer array size . + * + * Tried_mpdu_cnt_hist is the histogram of MPDUs tries per HWQ. + * The tries here is the count of the MPDUS within a PPDU that the + * HW had attempted to transmit on air, for the HWSCH Schedule + * command submitted by FW.It is not the retry attempts. + * The histogram bins are 0-29, 30-59, 60-89 and so on. The are + * 10 bins in this histogram. They are defined in FW using the + * following macros + * #define WAL_MAX_TRIED_MPDU_CNT_HISTOGRAM 9 + * #define WAL_TRIED_MPDU_CNT_HISTOGRAM_INTERVAL 30 + */ +struct htt_tx_pdev_stats_tried_mpdu_cnt_hist_tlv_v { + u32 hist_bin_size; + u32 tried_mpdu_cnt_hist[0]; /* HTT_TX_PDEV_TRIED_MPDU_CNT_HIST */ +}; + +/* == SOC ERROR STATS == */ + +/* =============== PDEV ERROR STATS ============== */ +#define HTT_STATS_MAX_HW_INTR_NAME_LEN 8 +struct htt_hw_stats_intr_misc_tlv { + /* Stored as little endian */ + u8 hw_intr_name[HTT_STATS_MAX_HW_INTR_NAME_LEN]; + u32 mask; + u32 count; +}; + +#define HTT_STATS_MAX_HW_MODULE_NAME_LEN 8 +struct htt_hw_stats_wd_timeout_tlv { + /* Stored as little endian */ + u8 hw_module_name[HTT_STATS_MAX_HW_MODULE_NAME_LEN]; + u32 count; +}; + +struct htt_hw_stats_pdev_errs_tlv { + u32 mac_id__word; /* BIT [ 7 : 0] : mac_id */ + u32 tx_abort; + u32 tx_abort_fail_count; + u32 rx_abort; + u32 rx_abort_fail_count; + u32 warm_reset; + u32 cold_reset; + u32 tx_flush; + u32 tx_glb_reset; + u32 tx_txq_reset; + u32 rx_timeout_reset; +}; + +struct htt_hw_stats_whal_tx_tlv { + u32 mac_id__word; + u32 last_unpause_ppdu_id; + u32 hwsch_unpause_wait_tqm_write; + u32 hwsch_dummy_tlv_skipped; + u32 hwsch_misaligned_offset_received; + u32 hwsch_reset_count; + u32 hwsch_dev_reset_war; + u32 hwsch_delayed_pause; + u32 hwsch_long_delayed_pause; + u32 sch_rx_ppdu_no_response; + u32 sch_selfgen_response; + u32 sch_rx_sifs_resp_trigger; +}; + +/* ============ PEER STATS ============ */ +struct htt_msdu_flow_stats_tlv { + u32 last_update_timestamp; + u32 last_add_timestamp; + u32 last_remove_timestamp; + u32 total_processed_msdu_count; + u32 cur_msdu_count_in_flowq; + u32 sw_peer_id; + u32 tx_flow_no__tid_num__drop_rule; + u32 last_cycle_enqueue_count; + u32 last_cycle_dequeue_count; + u32 last_cycle_drop_count; + u32 current_drop_th; +}; + +#define MAX_HTT_TID_NAME 8 + +/* Tidq stats */ +struct htt_tx_tid_stats_tlv { + /* Stored as little endian */ + u8 tid_name[MAX_HTT_TID_NAME]; + u32 sw_peer_id__tid_num; + u32 num_sched_pending__num_ppdu_in_hwq; + u32 tid_flags; + u32 hw_queued; + u32 hw_reaped; + u32 mpdus_hw_filter; + + u32 qdepth_bytes; + u32 qdepth_num_msdu; + u32 qdepth_num_mpdu; + u32 last_scheduled_tsmp; + u32 pause_module_id; + u32 block_module_id; + u32 tid_tx_airtime; +}; + +/* Tidq stats */ +struct htt_tx_tid_stats_v1_tlv { + /* Stored as little endian */ + u8 tid_name[MAX_HTT_TID_NAME]; + u32 sw_peer_id__tid_num; + u32 num_sched_pending__num_ppdu_in_hwq; + u32 tid_flags; + u32 max_qdepth_bytes; + u32 max_qdepth_n_msdus; + u32 rsvd; + + u32 qdepth_bytes; + u32 qdepth_num_msdu; + u32 qdepth_num_mpdu; + u32 last_scheduled_tsmp; + u32 pause_module_id; + u32 block_module_id; + u32 tid_tx_airtime; + u32 allow_n_flags; + u32 sendn_frms_allowed; +}; + +struct htt_rx_tid_stats_tlv { + u32 sw_peer_id__tid_num; + u8 tid_name[MAX_HTT_TID_NAME]; + u32 dup_in_reorder; + u32 dup_past_outside_window; + u32 dup_past_within_window; + u32 rxdesc_err_decrypt; + u32 tid_rx_airtime; +}; + +#define HTT_MAX_COUNTER_NAME 8 +struct htt_counter_tlv { + u8 counter_name[HTT_MAX_COUNTER_NAME]; + u32 count; +}; + +struct htt_peer_stats_cmn_tlv { + u32 ppdu_cnt; + u32 mpdu_cnt; + u32 msdu_cnt; + u32 pause_bitmap; + u32 block_bitmap; + u32 current_timestamp; + u32 peer_tx_airtime; + u32 peer_rx_airtime; + s32 rssi; + u32 peer_enqueued_count_low; + u32 peer_enqueued_count_high; + u32 peer_dequeued_count_low; + u32 peer_dequeued_count_high; + u32 peer_dropped_count_low; + u32 peer_dropped_count_high; + u32 ppdu_transmitted_bytes_low; + u32 ppdu_transmitted_bytes_high; + u32 peer_ttl_removed_count; + u32 inactive_time; +}; + +struct htt_peer_details_tlv { + u32 peer_type; + u32 sw_peer_id; + u32 vdev_pdev_ast_idx; + struct htt_mac_addr mac_addr; + u32 peer_flags; + u32 qpeer_flags; +}; + +enum htt_stats_param_type { + HTT_STATS_PREAM_OFDM, + HTT_STATS_PREAM_CCK, + HTT_STATS_PREAM_HT, + HTT_STATS_PREAM_VHT, + HTT_STATS_PREAM_HE, + HTT_STATS_PREAM_RSVD, + HTT_STATS_PREAM_RSVD1, + + HTT_STATS_PREAM_COUNT, +}; + +#define HTT_TX_PEER_STATS_NUM_MCS_COUNTERS 12 +#define HTT_TX_PEER_STATS_NUM_GI_COUNTERS 4 +#define HTT_TX_PEER_STATS_NUM_DCM_COUNTERS 5 +#define HTT_TX_PEER_STATS_NUM_BW_COUNTERS 4 +#define HTT_TX_PEER_STATS_NUM_SPATIAL_STREAMS 8 +#define HTT_TX_PEER_STATS_NUM_PREAMBLE_TYPES HTT_STATS_PREAM_COUNT + +struct htt_tx_peer_rate_stats_tlv { + u32 tx_ldpc; + u32 rts_cnt; + u32 ack_rssi; + + u32 tx_mcs[HTT_TX_PEER_STATS_NUM_MCS_COUNTERS]; + u32 tx_su_mcs[HTT_TX_PEER_STATS_NUM_MCS_COUNTERS]; + u32 tx_mu_mcs[HTT_TX_PEER_STATS_NUM_MCS_COUNTERS]; + /* element 0,1, ...7 -> NSS 1,2, ...8 */ + u32 tx_nss[HTT_TX_PEER_STATS_NUM_SPATIAL_STREAMS]; + /* element 0: 20 MHz, 1: 40 MHz, 2: 80 MHz, 3: 160 and 80+80 MHz */ + u32 tx_bw[HTT_TX_PEER_STATS_NUM_BW_COUNTERS]; + u32 tx_stbc[HTT_TX_PEER_STATS_NUM_MCS_COUNTERS]; + u32 tx_pream[HTT_TX_PEER_STATS_NUM_PREAMBLE_TYPES]; + + /* Counters to track number of tx packets in each GI + * (400us, 800us, 1600us & 3200us) in each mcs (0-11) + */ + u32 tx_gi[HTT_TX_PEER_STATS_NUM_GI_COUNTERS][HTT_TX_PEER_STATS_NUM_MCS_COUNTERS]; + + /* Counters to track packets in dcm mcs (MCS 0, 1, 3, 4) */ + u32 tx_dcm[HTT_TX_PEER_STATS_NUM_DCM_COUNTERS]; + +}; + +#define HTT_RX_PEER_STATS_NUM_MCS_COUNTERS 12 +#define HTT_RX_PEER_STATS_NUM_GI_COUNTERS 4 +#define HTT_RX_PEER_STATS_NUM_DCM_COUNTERS 5 +#define HTT_RX_PEER_STATS_NUM_BW_COUNTERS 4 +#define HTT_RX_PEER_STATS_NUM_SPATIAL_STREAMS 8 +#define HTT_RX_PEER_STATS_NUM_PREAMBLE_TYPES HTT_STATS_PREAM_COUNT + +struct htt_rx_peer_rate_stats_tlv { + u32 nsts; + + /* Number of rx ldpc packets */ + u32 rx_ldpc; + /* Number of rx rts packets */ + u32 rts_cnt; + + u32 rssi_mgmt; /* units = dB above noise floor */ + u32 rssi_data; /* units = dB above noise floor */ + u32 rssi_comb; /* units = dB above noise floor */ + u32 rx_mcs[HTT_RX_PEER_STATS_NUM_MCS_COUNTERS]; + /* element 0,1, ...7 -> NSS 1,2, ...8 */ + u32 rx_nss[HTT_RX_PEER_STATS_NUM_SPATIAL_STREAMS]; + u32 rx_dcm[HTT_RX_PEER_STATS_NUM_DCM_COUNTERS]; + u32 rx_stbc[HTT_RX_PEER_STATS_NUM_MCS_COUNTERS]; + /* element 0: 20 MHz, 1: 40 MHz, 2: 80 MHz, 3: 160 and 80+80 MHz */ + u32 rx_bw[HTT_RX_PEER_STATS_NUM_BW_COUNTERS]; + u32 rx_pream[HTT_RX_PEER_STATS_NUM_PREAMBLE_TYPES]; + /* units = dB above noise floor */ + u8 rssi_chain[HTT_RX_PEER_STATS_NUM_SPATIAL_STREAMS] + [HTT_RX_PEER_STATS_NUM_BW_COUNTERS]; + + /* Counters to track number of rx packets in each GI in each mcs (0-11) */ + u32 rx_gi[HTT_RX_PEER_STATS_NUM_GI_COUNTERS] + [HTT_RX_PEER_STATS_NUM_MCS_COUNTERS]; +}; + +enum htt_peer_stats_req_mode { + HTT_PEER_STATS_REQ_MODE_NO_QUERY, + HTT_PEER_STATS_REQ_MODE_QUERY_TQM, + HTT_PEER_STATS_REQ_MODE_FLUSH_TQM, +}; + +enum htt_peer_stats_tlv_enum { + HTT_PEER_STATS_CMN_TLV = 0, + HTT_PEER_DETAILS_TLV = 1, + HTT_TX_PEER_RATE_STATS_TLV = 2, + HTT_RX_PEER_RATE_STATS_TLV = 3, + HTT_TX_TID_STATS_TLV = 4, + HTT_RX_TID_STATS_TLV = 5, + HTT_MSDU_FLOW_STATS_TLV = 6, + + HTT_PEER_STATS_MAX_TLV = 31, +}; + +/* =========== MUMIMO HWQ stats =========== */ +/* MU MIMO stats per hwQ */ +struct htt_tx_hwq_mu_mimo_sch_stats_tlv { + u32 mu_mimo_sch_posted; + u32 mu_mimo_sch_failed; + u32 mu_mimo_ppdu_posted; +}; + +struct htt_tx_hwq_mu_mimo_mpdu_stats_tlv { + u32 mu_mimo_mpdus_queued_usr; + u32 mu_mimo_mpdus_tried_usr; + u32 mu_mimo_mpdus_failed_usr; + u32 mu_mimo_mpdus_requeued_usr; + u32 mu_mimo_err_no_ba_usr; + u32 mu_mimo_mpdu_underrun_usr; + u32 mu_mimo_ampdu_underrun_usr; +}; + +struct htt_tx_hwq_mu_mimo_cmn_stats_tlv { + u32 mac_id__hwq_id__word; +}; + +/* == TX HWQ STATS == */ +struct htt_tx_hwq_stats_cmn_tlv { + u32 mac_id__hwq_id__word; + + /* PPDU level stats */ + u32 xretry; + u32 underrun_cnt; + u32 flush_cnt; + u32 filt_cnt; + u32 null_mpdu_bmap; + u32 user_ack_failure; + u32 ack_tlv_proc; + u32 sched_id_proc; + u32 null_mpdu_tx_count; + u32 mpdu_bmap_not_recvd; + + /* Selfgen stats per hwQ */ + u32 num_bar; + u32 rts; + u32 cts2self; + u32 qos_null; + + /* MPDU level stats */ + u32 mpdu_tried_cnt; + u32 mpdu_queued_cnt; + u32 mpdu_ack_fail_cnt; + u32 mpdu_filt_cnt; + u32 false_mpdu_ack_count; + + u32 txq_timeout; +}; + +/* NOTE: Variable length TLV, use length spec to infer array size */ +struct htt_tx_hwq_difs_latency_stats_tlv_v { + u32 hist_intvl; + /* histogram of ppdu post to hwsch - > cmd status received */ + u32 difs_latency_hist[0]; /* HTT_TX_HWQ_MAX_DIFS_LATENCY_BINS */ +}; + +/* NOTE: Variable length TLV, use length spec to infer array size */ +struct htt_tx_hwq_cmd_result_stats_tlv_v { + /* Histogram of sched cmd result */ + u32 cmd_result[0]; /* HTT_TX_HWQ_MAX_CMD_RESULT_STATS */ +}; + +/* NOTE: Variable length TLV, use length spec to infer array size */ +struct htt_tx_hwq_cmd_stall_stats_tlv_v { + /* Histogram of various pause conitions */ + u32 cmd_stall_status[0]; /* HTT_TX_HWQ_MAX_CMD_STALL_STATS */ +}; + +/* NOTE: Variable length TLV, use length spec to infer array size */ +struct htt_tx_hwq_fes_result_stats_tlv_v { + /* Histogram of number of user fes result */ + u32 fes_result[0]; /* HTT_TX_HWQ_MAX_FES_RESULT_STATS */ +}; + +/* NOTE: Variable length TLV, use length spec to infer array size + * + * The hwq_tried_mpdu_cnt_hist is a histogram of MPDUs tries per HWQ. + * The tries here is the count of the MPDUS within a PPDU that the HW + * had attempted to transmit on air, for the HWSCH Schedule command + * submitted by FW in this HWQ .It is not the retry attempts. The + * histogram bins are 0-29, 30-59, 60-89 and so on. The are 10 bins + * in this histogram. + * they are defined in FW using the following macros + * #define WAL_MAX_TRIED_MPDU_CNT_HISTOGRAM 9 + * #define WAL_TRIED_MPDU_CNT_HISTOGRAM_INTERVAL 30 + */ +struct htt_tx_hwq_tried_mpdu_cnt_hist_tlv_v { + u32 hist_bin_size; + /* Histogram of number of mpdus on tried mpdu */ + u32 tried_mpdu_cnt_hist[0]; /* HTT_TX_HWQ_TRIED_MPDU_CNT_HIST */ +}; + +/* NOTE: Variable length TLV, use length spec to infer array size + * + * The txop_used_cnt_hist is the histogram of txop per burst. After + * completing the burst, we identify the txop used in the burst and + * incr the corresponding bin. + * Each bin represents 1ms & we have 10 bins in this histogram. + * they are deined in FW using the following macros + * #define WAL_MAX_TXOP_USED_CNT_HISTOGRAM 10 + * #define WAL_TXOP_USED_HISTOGRAM_INTERVAL 1000 ( 1 ms ) + */ +struct htt_tx_hwq_txop_used_cnt_hist_tlv_v { + /* Histogram of txop used cnt */ + u32 txop_used_cnt_hist[0]; /* HTT_TX_HWQ_TXOP_USED_CNT_HIST */ +}; + +/* == TX SELFGEN STATS == */ +struct htt_tx_selfgen_cmn_stats_tlv { + u32 mac_id__word; + u32 su_bar; + u32 rts; + u32 cts2self; + u32 qos_null; + u32 delayed_bar_1; /* MU user 1 */ + u32 delayed_bar_2; /* MU user 2 */ + u32 delayed_bar_3; /* MU user 3 */ + u32 delayed_bar_4; /* MU user 4 */ + u32 delayed_bar_5; /* MU user 5 */ + u32 delayed_bar_6; /* MU user 6 */ + u32 delayed_bar_7; /* MU user 7 */ +}; + +struct htt_tx_selfgen_ac_stats_tlv { + /* 11AC */ + u32 ac_su_ndpa; + u32 ac_su_ndp; + u32 ac_mu_mimo_ndpa; + u32 ac_mu_mimo_ndp; + u32 ac_mu_mimo_brpoll_1; /* MU user 1 */ + u32 ac_mu_mimo_brpoll_2; /* MU user 2 */ + u32 ac_mu_mimo_brpoll_3; /* MU user 3 */ +}; + +struct htt_tx_selfgen_ax_stats_tlv { + /* 11AX */ + u32 ax_su_ndpa; + u32 ax_su_ndp; + u32 ax_mu_mimo_ndpa; + u32 ax_mu_mimo_ndp; + u32 ax_mu_mimo_brpoll_1; /* MU user 1 */ + u32 ax_mu_mimo_brpoll_2; /* MU user 2 */ + u32 ax_mu_mimo_brpoll_3; /* MU user 3 */ + u32 ax_mu_mimo_brpoll_4; /* MU user 4 */ + u32 ax_mu_mimo_brpoll_5; /* MU user 5 */ + u32 ax_mu_mimo_brpoll_6; /* MU user 6 */ + u32 ax_mu_mimo_brpoll_7; /* MU user 7 */ + u32 ax_basic_trigger; + u32 ax_bsr_trigger; + u32 ax_mu_bar_trigger; + u32 ax_mu_rts_trigger; +}; + +struct htt_tx_selfgen_ac_err_stats_tlv { + /* 11AC error stats */ + u32 ac_su_ndp_err; + u32 ac_su_ndpa_err; + u32 ac_mu_mimo_ndpa_err; + u32 ac_mu_mimo_ndp_err; + u32 ac_mu_mimo_brp1_err; + u32 ac_mu_mimo_brp2_err; + u32 ac_mu_mimo_brp3_err; +}; + +struct htt_tx_selfgen_ax_err_stats_tlv { + /* 11AX error stats */ + u32 ax_su_ndp_err; + u32 ax_su_ndpa_err; + u32 ax_mu_mimo_ndpa_err; + u32 ax_mu_mimo_ndp_err; + u32 ax_mu_mimo_brp1_err; + u32 ax_mu_mimo_brp2_err; + u32 ax_mu_mimo_brp3_err; + u32 ax_mu_mimo_brp4_err; + u32 ax_mu_mimo_brp5_err; + u32 ax_mu_mimo_brp6_err; + u32 ax_mu_mimo_brp7_err; + u32 ax_basic_trigger_err; + u32 ax_bsr_trigger_err; + u32 ax_mu_bar_trigger_err; + u32 ax_mu_rts_trigger_err; +}; + +/* == TX MU STATS == */ +#define HTT_TX_PDEV_STATS_NUM_AC_MUMIMO_USER_STATS 4 +#define HTT_TX_PDEV_STATS_NUM_AX_MUMIMO_USER_STATS 8 +#define HTT_TX_PDEV_STATS_NUM_OFDMA_USER_STATS 74 + +struct htt_tx_pdev_mu_mimo_sch_stats_tlv { + /* mu-mimo sw sched cmd stats */ + u32 mu_mimo_sch_posted; + u32 mu_mimo_sch_failed; + /* MU PPDU stats per hwQ */ + u32 mu_mimo_ppdu_posted; + /* + * Counts the number of users in each transmission of + * the given TX mode. + * + * Index is the number of users - 1. + */ + u32 ac_mu_mimo_sch_nusers[HTT_TX_PDEV_STATS_NUM_AC_MUMIMO_USER_STATS]; + u32 ax_mu_mimo_sch_nusers[HTT_TX_PDEV_STATS_NUM_AX_MUMIMO_USER_STATS]; + u32 ax_ofdma_sch_nusers[HTT_TX_PDEV_STATS_NUM_OFDMA_USER_STATS]; +}; + +struct htt_tx_pdev_mu_mimo_mpdu_stats_tlv { + u32 mu_mimo_mpdus_queued_usr; + u32 mu_mimo_mpdus_tried_usr; + u32 mu_mimo_mpdus_failed_usr; + u32 mu_mimo_mpdus_requeued_usr; + u32 mu_mimo_err_no_ba_usr; + u32 mu_mimo_mpdu_underrun_usr; + u32 mu_mimo_ampdu_underrun_usr; + + u32 ax_mu_mimo_mpdus_queued_usr; + u32 ax_mu_mimo_mpdus_tried_usr; + u32 ax_mu_mimo_mpdus_failed_usr; + u32 ax_mu_mimo_mpdus_requeued_usr; + u32 ax_mu_mimo_err_no_ba_usr; + u32 ax_mu_mimo_mpdu_underrun_usr; + u32 ax_mu_mimo_ampdu_underrun_usr; + + u32 ax_ofdma_mpdus_queued_usr; + u32 ax_ofdma_mpdus_tried_usr; + u32 ax_ofdma_mpdus_failed_usr; + u32 ax_ofdma_mpdus_requeued_usr; + u32 ax_ofdma_err_no_ba_usr; + u32 ax_ofdma_mpdu_underrun_usr; + u32 ax_ofdma_ampdu_underrun_usr; +}; + +#define HTT_STATS_TX_SCHED_MODE_MU_MIMO_AC 1 +#define HTT_STATS_TX_SCHED_MODE_MU_MIMO_AX 2 +#define HTT_STATS_TX_SCHED_MODE_MU_OFDMA_AX 3 + +struct htt_tx_pdev_mpdu_stats_tlv { + /* mpdu level stats */ + u32 mpdus_queued_usr; + u32 mpdus_tried_usr; + u32 mpdus_failed_usr; + u32 mpdus_requeued_usr; + u32 err_no_ba_usr; + u32 mpdu_underrun_usr; + u32 ampdu_underrun_usr; + u32 user_index; + u32 tx_sched_mode; /* HTT_STATS_TX_SCHED_MODE_xxx */ +}; + +/* == TX SCHED STATS == */ +/* NOTE: Variable length TLV, use length spec to infer array size */ +struct htt_sched_txq_cmd_posted_tlv_v { + u32 sched_cmd_posted[0]; /* HTT_TX_PDEV_SCHED_TX_MODE_MAX */ +}; + +/* NOTE: Variable length TLV, use length spec to infer array size */ +struct htt_sched_txq_cmd_reaped_tlv_v { + u32 sched_cmd_reaped[0]; /* HTT_TX_PDEV_SCHED_TX_MODE_MAX */ +}; + +/* NOTE: Variable length TLV, use length spec to infer array size */ +struct htt_sched_txq_sched_order_su_tlv_v { + u32 sched_order_su[0]; /* HTT_TX_PDEV_NUM_SCHED_ORDER_LOG */ +}; + +enum htt_sched_txq_sched_ineligibility_tlv_enum { + HTT_SCHED_TID_SKIP_SCHED_MASK_DISABLED = 0, + HTT_SCHED_TID_SKIP_NOTIFY_MPDU, + HTT_SCHED_TID_SKIP_MPDU_STATE_INVALID, + HTT_SCHED_TID_SKIP_SCHED_DISABLED, + HTT_SCHED_TID_SKIP_TQM_BYPASS_CMD_PENDING, + HTT_SCHED_TID_SKIP_SECOND_SU_SCHEDULE, + + HTT_SCHED_TID_SKIP_CMD_SLOT_NOT_AVAIL, + HTT_SCHED_TID_SKIP_NO_ENQ, + HTT_SCHED_TID_SKIP_LOW_ENQ, + HTT_SCHED_TID_SKIP_PAUSED, + HTT_SCHED_TID_SKIP_UL, + HTT_SCHED_TID_REMOVE_PAUSED, + HTT_SCHED_TID_REMOVE_NO_ENQ, + HTT_SCHED_TID_REMOVE_UL, + HTT_SCHED_TID_QUERY, + HTT_SCHED_TID_SU_ONLY, + HTT_SCHED_TID_ELIGIBLE, + HTT_SCHED_INELIGIBILITY_MAX, +}; + +/* NOTE: Variable length TLV, use length spec to infer array size */ +struct htt_sched_txq_sched_ineligibility_tlv_v { + /* indexed by htt_sched_txq_sched_ineligibility_tlv_enum */ + u32 sched_ineligibility[0]; +}; + +struct htt_tx_pdev_stats_sched_per_txq_tlv { + u32 mac_id__txq_id__word; + u32 sched_policy; + u32 last_sched_cmd_posted_timestamp; + u32 last_sched_cmd_compl_timestamp; + u32 sched_2_tac_lwm_count; + u32 sched_2_tac_ring_full; + u32 sched_cmd_post_failure; + u32 num_active_tids; + u32 num_ps_schedules; + u32 sched_cmds_pending; + u32 num_tid_register; + u32 num_tid_unregister; + u32 num_qstats_queried; + u32 qstats_update_pending; + u32 last_qstats_query_timestamp; + u32 num_tqm_cmdq_full; + u32 num_de_sched_algo_trigger; + u32 num_rt_sched_algo_trigger; + u32 num_tqm_sched_algo_trigger; + u32 notify_sched; + u32 dur_based_sendn_term; +}; + +struct htt_stats_tx_sched_cmn_tlv { + /* BIT [ 7 : 0] :- mac_id + * BIT [31 : 8] :- reserved + */ + u32 mac_id__word; + /* Current timestamp */ + u32 current_timestamp; +}; + +/* == TQM STATS == */ +#define HTT_TX_TQM_MAX_GEN_MPDU_END_REASON 16 +#define HTT_TX_TQM_MAX_LIST_MPDU_END_REASON 16 +#define HTT_TX_TQM_MAX_LIST_MPDU_CNT_HISTOGRAM_BINS 16 + +/* NOTE: Variable length TLV, use length spec to infer array size */ +struct htt_tx_tqm_gen_mpdu_stats_tlv_v { + u32 gen_mpdu_end_reason[0]; /* HTT_TX_TQM_MAX_GEN_MPDU_END_REASON */ +}; + +/* NOTE: Variable length TLV, use length spec to infer array size */ +struct htt_tx_tqm_list_mpdu_stats_tlv_v { + u32 list_mpdu_end_reason[0]; /* HTT_TX_TQM_MAX_LIST_MPDU_END_REASON */ +}; + +/* NOTE: Variable length TLV, use length spec to infer array size */ +struct htt_tx_tqm_list_mpdu_cnt_tlv_v { + u32 list_mpdu_cnt_hist[0]; + /* HTT_TX_TQM_MAX_LIST_MPDU_CNT_HISTOGRAM_BINS */ +}; + +struct htt_tx_tqm_pdev_stats_tlv_v { + u32 msdu_count; + u32 mpdu_count; + u32 remove_msdu; + u32 remove_mpdu; + u32 remove_msdu_ttl; + u32 send_bar; + u32 bar_sync; + u32 notify_mpdu; + u32 sync_cmd; + u32 write_cmd; + u32 hwsch_trigger; + u32 ack_tlv_proc; + u32 gen_mpdu_cmd; + u32 gen_list_cmd; + u32 remove_mpdu_cmd; + u32 remove_mpdu_tried_cmd; + u32 mpdu_queue_stats_cmd; + u32 mpdu_head_info_cmd; + u32 msdu_flow_stats_cmd; + u32 remove_msdu_cmd; + u32 remove_msdu_ttl_cmd; + u32 flush_cache_cmd; + u32 update_mpduq_cmd; + u32 enqueue; + u32 enqueue_notify; + u32 notify_mpdu_at_head; + u32 notify_mpdu_state_valid; + /* + * On receiving TQM_FLOW_NOT_EMPTY_STATUS from TQM, (on MSDUs being enqueued + * the flow is non empty), if the number of MSDUs is greater than the threshold, + * notify is incremented. UDP_THRESH counters are for UDP MSDUs, and NONUDP are + * for non-UDP MSDUs. + * MSDUQ_SWNOTIFY_UDP_THRESH1 threshold - sched_udp_notify1 is incremented + * MSDUQ_SWNOTIFY_UDP_THRESH2 threshold - sched_udp_notify2 is incremented + * MSDUQ_SWNOTIFY_NONUDP_THRESH1 threshold - sched_nonudp_notify1 is incremented + * MSDUQ_SWNOTIFY_NONUDP_THRESH2 threshold - sched_nonudp_notify2 is incremented + * + * Notify signifies that we trigger the scheduler. + */ + u32 sched_udp_notify1; + u32 sched_udp_notify2; + u32 sched_nonudp_notify1; + u32 sched_nonudp_notify2; +}; + +struct htt_tx_tqm_cmn_stats_tlv { + u32 mac_id__word; + u32 max_cmdq_id; + u32 list_mpdu_cnt_hist_intvl; + + /* Global stats */ + u32 add_msdu; + u32 q_empty; + u32 q_not_empty; + u32 drop_notification; + u32 desc_threshold; +}; + +struct htt_tx_tqm_error_stats_tlv { + /* Error stats */ + u32 q_empty_failure; + u32 q_not_empty_failure; + u32 add_msdu_failure; +}; + +/* == TQM CMDQ stats == */ +struct htt_tx_tqm_cmdq_status_tlv { + u32 mac_id__cmdq_id__word; + u32 sync_cmd; + u32 write_cmd; + u32 gen_mpdu_cmd; + u32 mpdu_queue_stats_cmd; + u32 mpdu_head_info_cmd; + u32 msdu_flow_stats_cmd; + u32 remove_mpdu_cmd; + u32 remove_msdu_cmd; + u32 flush_cache_cmd; + u32 update_mpduq_cmd; + u32 update_msduq_cmd; +}; + +/* == TX-DE STATS == */ +/* Structures for tx de stats */ +struct htt_tx_de_eapol_packets_stats_tlv { + u32 m1_packets; + u32 m2_packets; + u32 m3_packets; + u32 m4_packets; + u32 g1_packets; + u32 g2_packets; +}; + +struct htt_tx_de_classify_failed_stats_tlv { + u32 ap_bss_peer_not_found; + u32 ap_bcast_mcast_no_peer; + u32 sta_delete_in_progress; + u32 ibss_no_bss_peer; + u32 invalid_vdev_type; + u32 invalid_ast_peer_entry; + u32 peer_entry_invalid; + u32 ethertype_not_ip; + u32 eapol_lookup_failed; + u32 qpeer_not_allow_data; + u32 fse_tid_override; + u32 ipv6_jumbogram_zero_length; + u32 qos_to_non_qos_in_prog; +}; + +struct htt_tx_de_classify_stats_tlv { + u32 arp_packets; + u32 igmp_packets; + u32 dhcp_packets; + u32 host_inspected; + u32 htt_included; + u32 htt_valid_mcs; + u32 htt_valid_nss; + u32 htt_valid_preamble_type; + u32 htt_valid_chainmask; + u32 htt_valid_guard_interval; + u32 htt_valid_retries; + u32 htt_valid_bw_info; + u32 htt_valid_power; + u32 htt_valid_key_flags; + u32 htt_valid_no_encryption; + u32 fse_entry_count; + u32 fse_priority_be; + u32 fse_priority_high; + u32 fse_priority_low; + u32 fse_traffic_ptrn_be; + u32 fse_traffic_ptrn_over_sub; + u32 fse_traffic_ptrn_bursty; + u32 fse_traffic_ptrn_interactive; + u32 fse_traffic_ptrn_periodic; + u32 fse_hwqueue_alloc; + u32 fse_hwqueue_created; + u32 fse_hwqueue_send_to_host; + u32 mcast_entry; + u32 bcast_entry; + u32 htt_update_peer_cache; + u32 htt_learning_frame; + u32 fse_invalid_peer; + /* + * mec_notify is HTT TX WBM multicast echo check notification + * from firmware to host. FW sends SA addresses to host for all + * multicast/broadcast packets received on STA side. + */ + u32 mec_notify; +}; + +struct htt_tx_de_classify_status_stats_tlv { + u32 eok; + u32 classify_done; + u32 lookup_failed; + u32 send_host_dhcp; + u32 send_host_mcast; + u32 send_host_unknown_dest; + u32 send_host; + u32 status_invalid; +}; + +struct htt_tx_de_enqueue_packets_stats_tlv { + u32 enqueued_pkts; + u32 to_tqm; + u32 to_tqm_bypass; +}; + +struct htt_tx_de_enqueue_discard_stats_tlv { + u32 discarded_pkts; + u32 local_frames; + u32 is_ext_msdu; +}; + +struct htt_tx_de_compl_stats_tlv { + u32 tcl_dummy_frame; + u32 tqm_dummy_frame; + u32 tqm_notify_frame; + u32 fw2wbm_enq; + u32 tqm_bypass_frame; +}; + +/* + * The htt_tx_de_fw2wbm_ring_full_hist_tlv is a histogram of time we waited + * for the fw2wbm ring buffer. we are requesting a buffer in FW2WBM release + * ring,which may fail, due to non availability of buffer. Hence we sleep for + * 200us & again request for it. This is a histogram of time we wait, with + * bin of 200ms & there are 10 bin (2 seconds max) + * They are defined by the following macros in FW + * #define ENTRIES_PER_BIN_COUNT 1000 // per bin 1000 * 200us = 200ms + * #define RING_FULL_BIN_ENTRIES (WAL_TX_DE_FW2WBM_ALLOC_TIMEOUT_COUNT / + * ENTRIES_PER_BIN_COUNT) + */ +struct htt_tx_de_fw2wbm_ring_full_hist_tlv { + u32 fw2wbm_ring_full_hist[0]; +}; + +struct htt_tx_de_cmn_stats_tlv { + u32 mac_id__word; + + /* Global Stats */ + u32 tcl2fw_entry_count; + u32 not_to_fw; + u32 invalid_pdev_vdev_peer; + u32 tcl_res_invalid_addrx; + u32 wbm2fw_entry_count; + u32 invalid_pdev; +}; + +/* == RING-IF STATS == */ +#define HTT_STATS_LOW_WM_BINS 5 +#define HTT_STATS_HIGH_WM_BINS 5 + +struct htt_ring_if_stats_tlv { + u32 base_addr; /* DWORD aligned base memory address of the ring */ + u32 elem_size; + u32 num_elems__prefetch_tail_idx; + u32 head_idx__tail_idx; + u32 shadow_head_idx__shadow_tail_idx; + u32 num_tail_incr; + u32 lwm_thresh__hwm_thresh; + u32 overrun_hit_count; + u32 underrun_hit_count; + u32 prod_blockwait_count; + u32 cons_blockwait_count; + u32 low_wm_hit_count[HTT_STATS_LOW_WM_BINS]; + u32 high_wm_hit_count[HTT_STATS_HIGH_WM_BINS]; +}; + +struct htt_ring_if_cmn_tlv { + u32 mac_id__word; + u32 num_records; +}; + +/* == SFM STATS == */ +/* NOTE: Variable length TLV, use length spec to infer array size */ +struct htt_sfm_client_user_tlv_v { + /* Number of DWORDS used per user and per client */ + u32 dwords_used_by_user_n[0]; +}; + +struct htt_sfm_client_tlv { + /* Client ID */ + u32 client_id; + /* Minimum number of buffers */ + u32 buf_min; + /* Maximum number of buffers */ + u32 buf_max; + /* Number of Busy buffers */ + u32 buf_busy; + /* Number of Allocated buffers */ + u32 buf_alloc; + /* Number of Available/Usable buffers */ + u32 buf_avail; + /* Number of users */ + u32 num_users; +}; + +struct htt_sfm_cmn_tlv { + u32 mac_id__word; + /* Indicates the total number of 128 byte buffers + * in the CMEM that are available for buffer sharing + */ + u32 buf_total; + /* Indicates for certain client or all the clients + * there is no dowrd saved in SFM, refer to SFM_R1_MEM_EMPTY + */ + u32 mem_empty; + /* DEALLOCATE_BUFFERS, refer to register SFM_R0_DEALLOCATE_BUFFERS */ + u32 deallocate_bufs; + /* Number of Records */ + u32 num_records; +}; + +/* == SRNG STATS == */ +struct htt_sring_stats_tlv { + u32 mac_id__ring_id__arena__ep; + u32 base_addr_lsb; /* DWORD aligned base memory address of the ring */ + u32 base_addr_msb; + u32 ring_size; + u32 elem_size; + + u32 num_avail_words__num_valid_words; + u32 head_ptr__tail_ptr; + u32 consumer_empty__producer_full; + u32 prefetch_count__internal_tail_ptr; +}; + +struct htt_sring_cmn_tlv { + u32 num_records; +}; + +/* == PDEV TX RATE CTRL STATS == */ +#define HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS 12 +#define HTT_TX_PDEV_STATS_NUM_GI_COUNTERS 4 +#define HTT_TX_PDEV_STATS_NUM_DCM_COUNTERS 5 +#define HTT_TX_PDEV_STATS_NUM_BW_COUNTERS 4 +#define HTT_TX_PDEV_STATS_NUM_SPATIAL_STREAMS 8 +#define HTT_TX_PDEV_STATS_NUM_PREAMBLE_TYPES HTT_STATS_PREAM_COUNT +#define HTT_TX_PDEV_STATS_NUM_LEGACY_CCK_STATS 4 +#define HTT_TX_PDEV_STATS_NUM_LEGACY_OFDM_STATS 8 +#define HTT_TX_PDEV_STATS_NUM_LTF 4 + +#define HTT_TX_NUM_OF_SOUNDING_STATS_WORDS \ + (HTT_TX_PDEV_STATS_NUM_BW_COUNTERS * \ + HTT_TX_PDEV_STATS_NUM_AX_MUMIMO_USER_STATS) + +struct htt_tx_pdev_rate_stats_tlv { + u32 mac_id__word; + u32 tx_ldpc; + u32 rts_cnt; + /* RSSI value of last ack packet (units = dB above noise floor) */ + u32 ack_rssi; + + u32 tx_mcs[HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS]; + + u32 tx_su_mcs[HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS]; + u32 tx_mu_mcs[HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS]; + + /* element 0,1, ...7 -> NSS 1,2, ...8 */ + u32 tx_nss[HTT_TX_PDEV_STATS_NUM_SPATIAL_STREAMS]; + /* element 0: 20 MHz, 1: 40 MHz, 2: 80 MHz, 3: 160 and 80+80 MHz */ + u32 tx_bw[HTT_TX_PDEV_STATS_NUM_BW_COUNTERS]; + u32 tx_stbc[HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS]; + u32 tx_pream[HTT_TX_PDEV_STATS_NUM_PREAMBLE_TYPES]; + + /* Counters to track number of tx packets + * in each GI (400us, 800us, 1600us & 3200us) in each mcs (0-11) + */ + u32 tx_gi[HTT_TX_PDEV_STATS_NUM_GI_COUNTERS][HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS]; + + /* Counters to track packets in dcm mcs (MCS 0, 1, 3, 4) */ + u32 tx_dcm[HTT_TX_PDEV_STATS_NUM_DCM_COUNTERS]; + /* Number of CTS-acknowledged RTS packets */ + u32 rts_success; + + /* + * Counters for legacy 11a and 11b transmissions. + * + * The index corresponds to: + * + * CCK: 0: 1 Mbps, 1: 2 Mbps, 2: 5.5 Mbps, 3: 11 Mbps + * + * OFDM: 0: 6 Mbps, 1: 9 Mbps, 2: 12 Mbps, 3: 18 Mbps, + * 4: 24 Mbps, 5: 36 Mbps, 6: 48 Mbps, 7: 54 Mbps + */ + u32 tx_legacy_cck_rate[HTT_TX_PDEV_STATS_NUM_LEGACY_CCK_STATS]; + u32 tx_legacy_ofdm_rate[HTT_TX_PDEV_STATS_NUM_LEGACY_OFDM_STATS]; + + u32 ac_mu_mimo_tx_ldpc; + u32 ax_mu_mimo_tx_ldpc; + u32 ofdma_tx_ldpc; + + /* + * Counters for 11ax HE LTF selection during TX. + * + * The index corresponds to: + * + * 0: unused, 1: 1x LTF, 2: 2x LTF, 3: 4x LTF + */ + u32 tx_he_ltf[HTT_TX_PDEV_STATS_NUM_LTF]; + + u32 ac_mu_mimo_tx_mcs[HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS]; + u32 ax_mu_mimo_tx_mcs[HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS]; + u32 ofdma_tx_mcs[HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS]; + + u32 ac_mu_mimo_tx_nss[HTT_TX_PDEV_STATS_NUM_SPATIAL_STREAMS]; + u32 ax_mu_mimo_tx_nss[HTT_TX_PDEV_STATS_NUM_SPATIAL_STREAMS]; + u32 ofdma_tx_nss[HTT_TX_PDEV_STATS_NUM_SPATIAL_STREAMS]; + + u32 ac_mu_mimo_tx_bw[HTT_TX_PDEV_STATS_NUM_BW_COUNTERS]; + u32 ax_mu_mimo_tx_bw[HTT_TX_PDEV_STATS_NUM_BW_COUNTERS]; + u32 ofdma_tx_bw[HTT_TX_PDEV_STATS_NUM_BW_COUNTERS]; + + u32 ac_mu_mimo_tx_gi[HTT_TX_PDEV_STATS_NUM_GI_COUNTERS] + [HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS]; + u32 ax_mu_mimo_tx_gi[HTT_TX_PDEV_STATS_NUM_GI_COUNTERS] + [HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS]; + u32 ofdma_tx_gi[HTT_TX_PDEV_STATS_NUM_GI_COUNTERS] + [HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS]; +}; + +/* == PDEV RX RATE CTRL STATS == */ +#define HTT_RX_PDEV_STATS_NUM_LEGACY_CCK_STATS 4 +#define HTT_RX_PDEV_STATS_NUM_LEGACY_OFDM_STATS 8 +#define HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS 12 +#define HTT_RX_PDEV_STATS_NUM_GI_COUNTERS 4 +#define HTT_RX_PDEV_STATS_NUM_DCM_COUNTERS 5 +#define HTT_RX_PDEV_STATS_NUM_BW_COUNTERS 4 +#define HTT_RX_PDEV_STATS_NUM_SPATIAL_STREAMS 8 +#define HTT_RX_PDEV_STATS_NUM_PREAMBLE_TYPES HTT_STATS_PREAM_COUNT + +struct htt_rx_pdev_rate_stats_tlv { + u32 mac_id__word; + u32 nsts; + + u32 rx_ldpc; + u32 rts_cnt; + + u32 rssi_mgmt; /* units = dB above noise floor */ + u32 rssi_data; /* units = dB above noise floor */ + u32 rssi_comb; /* units = dB above noise floor */ + u32 rx_mcs[HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS]; + /* element 0,1, ...7 -> NSS 1,2, ...8 */ + u32 rx_nss[HTT_RX_PDEV_STATS_NUM_SPATIAL_STREAMS]; + u32 rx_dcm[HTT_RX_PDEV_STATS_NUM_DCM_COUNTERS]; + u32 rx_stbc[HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS]; + /* element 0: 20 MHz, 1: 40 MHz, 2: 80 MHz, 3: 160 and 80+80 MHz */ + u32 rx_bw[HTT_RX_PDEV_STATS_NUM_BW_COUNTERS]; + u32 rx_pream[HTT_RX_PDEV_STATS_NUM_PREAMBLE_TYPES]; + u8 rssi_chain[HTT_RX_PDEV_STATS_NUM_SPATIAL_STREAMS] + [HTT_RX_PDEV_STATS_NUM_BW_COUNTERS]; + /* units = dB above noise floor */ + + /* Counters to track number of rx packets + * in each GI in each mcs (0-11) + */ + u32 rx_gi[HTT_RX_PDEV_STATS_NUM_GI_COUNTERS][HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS]; + s32 rssi_in_dbm; /* rx Signal Strength value in dBm unit */ + + u32 rx_11ax_su_ext; + u32 rx_11ac_mumimo; + u32 rx_11ax_mumimo; + u32 rx_11ax_ofdma; + u32 txbf; + u32 rx_legacy_cck_rate[HTT_RX_PDEV_STATS_NUM_LEGACY_CCK_STATS]; + u32 rx_legacy_ofdm_rate[HTT_RX_PDEV_STATS_NUM_LEGACY_OFDM_STATS]; + u32 rx_active_dur_us_low; + u32 rx_active_dur_us_high; +}; + +/* == RX PDEV/SOC STATS == */ +struct htt_rx_soc_fw_stats_tlv { + u32 fw_reo_ring_data_msdu; + u32 fw_to_host_data_msdu_bcmc; + u32 fw_to_host_data_msdu_uc; + u32 ofld_remote_data_buf_recycle_cnt; + u32 ofld_remote_free_buf_indication_cnt; + + u32 ofld_buf_to_host_data_msdu_uc; + u32 reo_fw_ring_to_host_data_msdu_uc; + + u32 wbm_sw_ring_reap; + u32 wbm_forward_to_host_cnt; + u32 wbm_target_recycle_cnt; + + u32 target_refill_ring_recycle_cnt; +}; + +/* NOTE: Variable length TLV, use length spec to infer array size */ +struct htt_rx_soc_fw_refill_ring_empty_tlv_v { + u32 refill_ring_empty_cnt[0]; /* HTT_RX_STATS_REFILL_MAX_RING */ +}; + +/* NOTE: Variable length TLV, use length spec to infer array size */ +struct htt_rx_soc_fw_refill_ring_num_refill_tlv_v { + u32 refill_ring_num_refill[0]; /* HTT_RX_STATS_REFILL_MAX_RING */ +}; + +/* RXDMA error code from WBM released packets */ +enum htt_rx_rxdma_error_code_enum { + HTT_RX_RXDMA_OVERFLOW_ERR = 0, + HTT_RX_RXDMA_MPDU_LENGTH_ERR = 1, + HTT_RX_RXDMA_FCS_ERR = 2, + HTT_RX_RXDMA_DECRYPT_ERR = 3, + HTT_RX_RXDMA_TKIP_MIC_ERR = 4, + HTT_RX_RXDMA_UNECRYPTED_ERR = 5, + HTT_RX_RXDMA_MSDU_LEN_ERR = 6, + HTT_RX_RXDMA_MSDU_LIMIT_ERR = 7, + HTT_RX_RXDMA_WIFI_PARSE_ERR = 8, + HTT_RX_RXDMA_AMSDU_PARSE_ERR = 9, + HTT_RX_RXDMA_SA_TIMEOUT_ERR = 10, + HTT_RX_RXDMA_DA_TIMEOUT_ERR = 11, + HTT_RX_RXDMA_FLOW_TIMEOUT_ERR = 12, + HTT_RX_RXDMA_FLUSH_REQUEST = 13, + HTT_RX_RXDMA_ERR_CODE_RVSD0 = 14, + HTT_RX_RXDMA_ERR_CODE_RVSD1 = 15, + + /* This MAX_ERR_CODE should not be used in any host/target messages, + * so that even though it is defined within a host/target interface + * definition header file, it isn't actually part of the host/target + * interface, and thus can be modified. + */ + HTT_RX_RXDMA_MAX_ERR_CODE +}; + +/* NOTE: Variable length TLV, use length spec to infer array size */ +struct htt_rx_soc_fw_refill_ring_num_rxdma_err_tlv_v { + u32 rxdma_err[0]; /* HTT_RX_RXDMA_MAX_ERR_CODE */ +}; + +/* REO error code from WBM released packets */ +enum htt_rx_reo_error_code_enum { + HTT_RX_REO_QUEUE_DESC_ADDR_ZERO = 0, + HTT_RX_REO_QUEUE_DESC_NOT_VALID = 1, + HTT_RX_AMPDU_IN_NON_BA = 2, + HTT_RX_NON_BA_DUPLICATE = 3, + HTT_RX_BA_DUPLICATE = 4, + HTT_RX_REGULAR_FRAME_2K_JUMP = 5, + HTT_RX_BAR_FRAME_2K_JUMP = 6, + HTT_RX_REGULAR_FRAME_OOR = 7, + HTT_RX_BAR_FRAME_OOR = 8, + HTT_RX_BAR_FRAME_NO_BA_SESSION = 9, + HTT_RX_BAR_FRAME_SN_EQUALS_SSN = 10, + HTT_RX_PN_CHECK_FAILED = 11, + HTT_RX_2K_ERROR_HANDLING_FLAG_SET = 12, + HTT_RX_PN_ERROR_HANDLING_FLAG_SET = 13, + HTT_RX_QUEUE_DESCRIPTOR_BLOCKED_SET = 14, + HTT_RX_REO_ERR_CODE_RVSD = 15, + + /* This MAX_ERR_CODE should not be used in any host/target messages, + * so that even though it is defined within a host/target interface + * definition header file, it isn't actually part of the host/target + * interface, and thus can be modified. + */ + HTT_RX_REO_MAX_ERR_CODE +}; + +/* NOTE: Variable length TLV, use length spec to infer array size */ +struct htt_rx_soc_fw_refill_ring_num_reo_err_tlv_v { + u32 reo_err[0]; /* HTT_RX_REO_MAX_ERR_CODE */ +}; + +/* == RX PDEV STATS == */ +#define HTT_STATS_SUBTYPE_MAX 16 + +struct htt_rx_pdev_fw_stats_tlv { + u32 mac_id__word; + u32 ppdu_recvd; + u32 mpdu_cnt_fcs_ok; + u32 mpdu_cnt_fcs_err; + u32 tcp_msdu_cnt; + u32 tcp_ack_msdu_cnt; + u32 udp_msdu_cnt; + u32 other_msdu_cnt; + u32 fw_ring_mpdu_ind; + u32 fw_ring_mgmt_subtype[HTT_STATS_SUBTYPE_MAX]; + u32 fw_ring_ctrl_subtype[HTT_STATS_SUBTYPE_MAX]; + u32 fw_ring_mcast_data_msdu; + u32 fw_ring_bcast_data_msdu; + u32 fw_ring_ucast_data_msdu; + u32 fw_ring_null_data_msdu; + u32 fw_ring_mpdu_drop; + u32 ofld_local_data_ind_cnt; + u32 ofld_local_data_buf_recycle_cnt; + u32 drx_local_data_ind_cnt; + u32 drx_local_data_buf_recycle_cnt; + u32 local_nondata_ind_cnt; + u32 local_nondata_buf_recycle_cnt; + + u32 fw_status_buf_ring_refill_cnt; + u32 fw_status_buf_ring_empty_cnt; + u32 fw_pkt_buf_ring_refill_cnt; + u32 fw_pkt_buf_ring_empty_cnt; + u32 fw_link_buf_ring_refill_cnt; + u32 fw_link_buf_ring_empty_cnt; + + u32 host_pkt_buf_ring_refill_cnt; + u32 host_pkt_buf_ring_empty_cnt; + u32 mon_pkt_buf_ring_refill_cnt; + u32 mon_pkt_buf_ring_empty_cnt; + u32 mon_status_buf_ring_refill_cnt; + u32 mon_status_buf_ring_empty_cnt; + u32 mon_desc_buf_ring_refill_cnt; + u32 mon_desc_buf_ring_empty_cnt; + u32 mon_dest_ring_update_cnt; + u32 mon_dest_ring_full_cnt; + + u32 rx_suspend_cnt; + u32 rx_suspend_fail_cnt; + u32 rx_resume_cnt; + u32 rx_resume_fail_cnt; + u32 rx_ring_switch_cnt; + u32 rx_ring_restore_cnt; + u32 rx_flush_cnt; + u32 rx_recovery_reset_cnt; +}; + +#define HTT_STATS_PHY_ERR_MAX 43 + +struct htt_rx_pdev_fw_stats_phy_err_tlv { + u32 mac_id__word; + u32 total_phy_err_cnt; + /* Counts of different types of phy errs + * The mapping of PHY error types to phy_err array elements is HW dependent. + * The only currently-supported mapping is shown below: + * + * 0 phyrx_err_phy_off Reception aborted due to receiving a PHY_OFF TLV + * 1 phyrx_err_synth_off + * 2 phyrx_err_ofdma_timing + * 3 phyrx_err_ofdma_signal_parity + * 4 phyrx_err_ofdma_rate_illegal + * 5 phyrx_err_ofdma_length_illegal + * 6 phyrx_err_ofdma_restart + * 7 phyrx_err_ofdma_service + * 8 phyrx_err_ppdu_ofdma_power_drop + * 9 phyrx_err_cck_blokker + * 10 phyrx_err_cck_timing + * 11 phyrx_err_cck_header_crc + * 12 phyrx_err_cck_rate_illegal + * 13 phyrx_err_cck_length_illegal + * 14 phyrx_err_cck_restart + * 15 phyrx_err_cck_service + * 16 phyrx_err_cck_power_drop + * 17 phyrx_err_ht_crc_err + * 18 phyrx_err_ht_length_illegal + * 19 phyrx_err_ht_rate_illegal + * 20 phyrx_err_ht_zlf + * 21 phyrx_err_false_radar_ext + * 22 phyrx_err_green_field + * 23 phyrx_err_bw_gt_dyn_bw + * 24 phyrx_err_leg_ht_mismatch + * 25 phyrx_err_vht_crc_error + * 26 phyrx_err_vht_siga_unsupported + * 27 phyrx_err_vht_lsig_len_invalid + * 28 phyrx_err_vht_ndp_or_zlf + * 29 phyrx_err_vht_nsym_lt_zero + * 30 phyrx_err_vht_rx_extra_symbol_mismatch + * 31 phyrx_err_vht_rx_skip_group_id0 + * 32 phyrx_err_vht_rx_skip_group_id1to62 + * 33 phyrx_err_vht_rx_skip_group_id63 + * 34 phyrx_err_ofdm_ldpc_decoder_disabled + * 35 phyrx_err_defer_nap + * 36 phyrx_err_fdomain_timeout + * 37 phyrx_err_lsig_rel_check + * 38 phyrx_err_bt_collision + * 39 phyrx_err_unsupported_mu_feedback + * 40 phyrx_err_ppdu_tx_interrupt_rx + * 41 phyrx_err_unsupported_cbf + * 42 phyrx_err_other + */ + u32 phy_err[HTT_STATS_PHY_ERR_MAX]; +}; + +/* NOTE: Variable length TLV, use length spec to infer array size */ +struct htt_rx_pdev_fw_ring_mpdu_err_tlv_v { + /* Num error MPDU for each RxDMA error type */ + u32 fw_ring_mpdu_err[0]; /* HTT_RX_STATS_RXDMA_MAX_ERR */ +}; + +/* NOTE: Variable length TLV, use length spec to infer array size */ +struct htt_rx_pdev_fw_mpdu_drop_tlv_v { + /* Num MPDU dropped */ + u32 fw_mpdu_drop[0]; /* HTT_RX_STATS_FW_DROP_REASON_MAX */ +}; + +#define HTT_PDEV_CCA_STATS_TX_FRAME_INFO_PRESENT (0x1) +#define HTT_PDEV_CCA_STATS_RX_FRAME_INFO_PRESENT (0x2) +#define HTT_PDEV_CCA_STATS_RX_CLEAR_INFO_PRESENT (0x4) +#define HTT_PDEV_CCA_STATS_MY_RX_FRAME_INFO_PRESENT (0x8) +#define HTT_PDEV_CCA_STATS_USEC_CNT_INFO_PRESENT (0x10) +#define HTT_PDEV_CCA_STATS_MED_RX_IDLE_INFO_PRESENT (0x20) +#define HTT_PDEV_CCA_STATS_MED_TX_IDLE_GLOBAL_INFO_PRESENT (0x40) +#define HTT_PDEV_CCA_STATS_CCA_OBBS_USEC_INFO_PRESENT (0x80) + +struct htt_pdev_stats_cca_counters_tlv { + /* Below values are obtained from the HW Cycles counter registers */ + u32 tx_frame_usec; + u32 rx_frame_usec; + u32 rx_clear_usec; + u32 my_rx_frame_usec; + u32 usec_cnt; + u32 med_rx_idle_usec; + u32 med_tx_idle_global_usec; + u32 cca_obss_usec; +}; + +struct htt_pdev_cca_stats_hist_v1_tlv { + u32 chan_num; + /* num of CCA records (Num of htt_pdev_stats_cca_counters_tlv)*/ + u32 num_records; + u32 valid_cca_counters_bitmap; + u32 collection_interval; + + /* This will be followed by an array which contains the CCA stats + * collected in the last N intervals, + * if the indication is for last N intervals CCA stats. + * Then the pdev_cca_stats[0] element contains the oldest CCA stats + * and pdev_cca_stats[N-1] will have the most recent CCA stats. + * htt_pdev_stats_cca_counters_tlv cca_hist_tlv[1]; + */ +}; + +struct htt_pdev_stats_twt_session_tlv { + u32 vdev_id; + struct htt_mac_addr peer_mac; + u32 flow_id_flags; + + /* TWT_DIALOG_ID_UNAVAILABLE is used + * when TWT session is not initiated by host + */ + u32 dialog_id; + u32 wake_dura_us; + u32 wake_intvl_us; + u32 sp_offset_us; +}; + +struct htt_pdev_stats_twt_sessions_tlv { + u32 pdev_id; + u32 num_sessions; + struct htt_pdev_stats_twt_session_tlv twt_session[0]; +}; + +enum htt_rx_reo_resource_sample_id_enum { + /* Global link descriptor queued in REO */ + HTT_RX_REO_RESOURCE_GLOBAL_LINK_DESC_COUNT_0 = 0, + HTT_RX_REO_RESOURCE_GLOBAL_LINK_DESC_COUNT_1 = 1, + HTT_RX_REO_RESOURCE_GLOBAL_LINK_DESC_COUNT_2 = 2, + /*Number of queue descriptors of this aging group */ + HTT_RX_REO_RESOURCE_BUFFERS_USED_AC0 = 3, + HTT_RX_REO_RESOURCE_BUFFERS_USED_AC1 = 4, + HTT_RX_REO_RESOURCE_BUFFERS_USED_AC2 = 5, + HTT_RX_REO_RESOURCE_BUFFERS_USED_AC3 = 6, + /* Total number of MSDUs buffered in AC */ + HTT_RX_REO_RESOURCE_AGING_NUM_QUEUES_AC0 = 7, + HTT_RX_REO_RESOURCE_AGING_NUM_QUEUES_AC1 = 8, + HTT_RX_REO_RESOURCE_AGING_NUM_QUEUES_AC2 = 9, + HTT_RX_REO_RESOURCE_AGING_NUM_QUEUES_AC3 = 10, + + HTT_RX_REO_RESOURCE_STATS_MAX = 16 +}; + +struct htt_rx_reo_resource_stats_tlv_v { + /* Variable based on the Number of records. HTT_RX_REO_RESOURCE_STATS_MAX */ + u32 sample_id; + u32 total_max; + u32 total_avg; + u32 total_sample; + u32 non_zeros_avg; + u32 non_zeros_sample; + u32 last_non_zeros_max; + u32 last_non_zeros_min; + u32 last_non_zeros_avg; + u32 last_non_zeros_sample; +}; + +/* == TX SOUNDING STATS == */ + +enum htt_txbf_sound_steer_modes { + HTT_IMPLICIT_TXBF_STEER_STATS = 0, + HTT_EXPLICIT_TXBF_SU_SIFS_STEER_STATS = 1, + HTT_EXPLICIT_TXBF_SU_RBO_STEER_STATS = 2, + HTT_EXPLICIT_TXBF_MU_SIFS_STEER_STATS = 3, + HTT_EXPLICIT_TXBF_MU_RBO_STEER_STATS = 4, + HTT_TXBF_MAX_NUM_OF_MODES = 5 +}; + +enum htt_stats_sounding_tx_mode { + HTT_TX_AC_SOUNDING_MODE = 0, + HTT_TX_AX_SOUNDING_MODE = 1, +}; + +struct htt_tx_sounding_stats_tlv { + u32 tx_sounding_mode; /* HTT_TX_XX_SOUNDING_MODE */ + /* Counts number of soundings for all steering modes in each bw */ + u32 cbf_20[HTT_TXBF_MAX_NUM_OF_MODES]; + u32 cbf_40[HTT_TXBF_MAX_NUM_OF_MODES]; + u32 cbf_80[HTT_TXBF_MAX_NUM_OF_MODES]; + u32 cbf_160[HTT_TXBF_MAX_NUM_OF_MODES]; + /* + * The sounding array is a 2-D array stored as an 1-D array of + * u32. The stats for a particular user/bw combination is + * referenced with the following: + * + * sounding[(user* max_bw) + bw] + * + * ... where max_bw == 4 for 160mhz + */ + u32 sounding[HTT_TX_NUM_OF_SOUNDING_STATS_WORDS]; +}; + +struct htt_pdev_obss_pd_stats_tlv { + u32 num_obss_tx_ppdu_success; + u32 num_obss_tx_ppdu_failure; +}; + +void ath11k_debug_htt_stats_init(struct ath11k *ar); +#endif diff --git a/drivers/net/wireless/ath/ath11k/debugfs_sta.c b/drivers/net/wireless/ath/ath11k/debugfs_sta.c new file mode 100644 index 0000000000000..b392117eff3ca --- /dev/null +++ b/drivers/net/wireless/ath/ath11k/debugfs_sta.c @@ -0,0 +1,538 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* + * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. + */ + +#include + +#include "core.h" +#include "peer.h" +#include "debug.h" + +void +ath11k_accumulate_per_peer_tx_stats(struct ath11k_sta *arsta, + struct ath11k_per_peer_tx_stats *peer_stats, + u8 legacy_rate_idx) +{ + struct rate_info *txrate = &arsta->txrate; + struct ath11k_htt_tx_stats *tx_stats; + int gi, mcs, bw, nss; + + if (!arsta->tx_stats) + return; + + tx_stats = arsta->tx_stats; + gi = FIELD_GET(RATE_INFO_FLAGS_SHORT_GI, arsta->txrate.flags); + mcs = txrate->mcs; + bw = txrate->bw; + nss = txrate->nss - 1; + +#define STATS_OP_FMT(name) tx_stats->stats[ATH11K_STATS_TYPE_##name] + + if (txrate->flags & RATE_INFO_FLAGS_HE_MCS) { + STATS_OP_FMT(SUCC).he[0][mcs] += peer_stats->succ_bytes; + STATS_OP_FMT(SUCC).he[1][mcs] += peer_stats->succ_pkts; + STATS_OP_FMT(FAIL).he[0][mcs] += peer_stats->failed_bytes; + STATS_OP_FMT(FAIL).he[1][mcs] += peer_stats->failed_pkts; + STATS_OP_FMT(RETRY).he[0][mcs] += peer_stats->retry_bytes; + STATS_OP_FMT(RETRY).he[1][mcs] += peer_stats->retry_pkts; + } else if (txrate->flags & RATE_INFO_FLAGS_VHT_MCS) { + STATS_OP_FMT(SUCC).vht[0][mcs] += peer_stats->succ_bytes; + STATS_OP_FMT(SUCC).vht[1][mcs] += peer_stats->succ_pkts; + STATS_OP_FMT(FAIL).vht[0][mcs] += peer_stats->failed_bytes; + STATS_OP_FMT(FAIL).vht[1][mcs] += peer_stats->failed_pkts; + STATS_OP_FMT(RETRY).vht[0][mcs] += peer_stats->retry_bytes; + STATS_OP_FMT(RETRY).vht[1][mcs] += peer_stats->retry_pkts; + } else if (txrate->flags & RATE_INFO_FLAGS_MCS) { + STATS_OP_FMT(SUCC).ht[0][mcs] += peer_stats->succ_bytes; + STATS_OP_FMT(SUCC).ht[1][mcs] += peer_stats->succ_pkts; + STATS_OP_FMT(FAIL).ht[0][mcs] += peer_stats->failed_bytes; + STATS_OP_FMT(FAIL).ht[1][mcs] += peer_stats->failed_pkts; + STATS_OP_FMT(RETRY).ht[0][mcs] += peer_stats->retry_bytes; + STATS_OP_FMT(RETRY).ht[1][mcs] += peer_stats->retry_pkts; + } else { + mcs = legacy_rate_idx; + + STATS_OP_FMT(SUCC).legacy[0][mcs] += peer_stats->succ_bytes; + STATS_OP_FMT(SUCC).legacy[1][mcs] += peer_stats->succ_pkts; + STATS_OP_FMT(FAIL).legacy[0][mcs] += peer_stats->failed_bytes; + STATS_OP_FMT(FAIL).legacy[1][mcs] += peer_stats->failed_pkts; + STATS_OP_FMT(RETRY).legacy[0][mcs] += peer_stats->retry_bytes; + STATS_OP_FMT(RETRY).legacy[1][mcs] += peer_stats->retry_pkts; + } + + if (peer_stats->is_ampdu) { + tx_stats->ba_fails += peer_stats->ba_fails; + + if (txrate->flags & RATE_INFO_FLAGS_HE_MCS) { + STATS_OP_FMT(AMPDU).he[0][mcs] += + peer_stats->succ_bytes + peer_stats->retry_bytes; + STATS_OP_FMT(AMPDU).he[1][mcs] += + peer_stats->succ_pkts + peer_stats->retry_pkts; + } else if (txrate->flags & RATE_INFO_FLAGS_MCS) { + STATS_OP_FMT(AMPDU).ht[0][mcs] += + peer_stats->succ_bytes + peer_stats->retry_bytes; + STATS_OP_FMT(AMPDU).ht[1][mcs] += + peer_stats->succ_pkts + peer_stats->retry_pkts; + } else { + STATS_OP_FMT(AMPDU).vht[0][mcs] += + peer_stats->succ_bytes + peer_stats->retry_bytes; + STATS_OP_FMT(AMPDU).vht[1][mcs] += + peer_stats->succ_pkts + peer_stats->retry_pkts; + } + STATS_OP_FMT(AMPDU).bw[0][bw] += + peer_stats->succ_bytes + peer_stats->retry_bytes; + STATS_OP_FMT(AMPDU).nss[0][nss] += + peer_stats->succ_bytes + peer_stats->retry_bytes; + STATS_OP_FMT(AMPDU).gi[0][gi] += + peer_stats->succ_bytes + peer_stats->retry_bytes; + STATS_OP_FMT(AMPDU).bw[1][bw] += + peer_stats->succ_pkts + peer_stats->retry_pkts; + STATS_OP_FMT(AMPDU).nss[1][nss] += + peer_stats->succ_pkts + peer_stats->retry_pkts; + STATS_OP_FMT(AMPDU).gi[1][gi] += + peer_stats->succ_pkts + peer_stats->retry_pkts; + } else { + tx_stats->ack_fails += peer_stats->ba_fails; + } + + STATS_OP_FMT(SUCC).bw[0][bw] += peer_stats->succ_bytes; + STATS_OP_FMT(SUCC).nss[0][nss] += peer_stats->succ_bytes; + STATS_OP_FMT(SUCC).gi[0][gi] += peer_stats->succ_bytes; + + STATS_OP_FMT(SUCC).bw[1][bw] += peer_stats->succ_pkts; + STATS_OP_FMT(SUCC).nss[1][nss] += peer_stats->succ_pkts; + STATS_OP_FMT(SUCC).gi[1][gi] += peer_stats->succ_pkts; + + STATS_OP_FMT(FAIL).bw[0][bw] += peer_stats->failed_bytes; + STATS_OP_FMT(FAIL).nss[0][nss] += peer_stats->failed_bytes; + STATS_OP_FMT(FAIL).gi[0][gi] += peer_stats->failed_bytes; + + STATS_OP_FMT(FAIL).bw[1][bw] += peer_stats->failed_pkts; + STATS_OP_FMT(FAIL).nss[1][nss] += peer_stats->failed_pkts; + STATS_OP_FMT(FAIL).gi[1][gi] += peer_stats->failed_pkts; + + STATS_OP_FMT(RETRY).bw[0][bw] += peer_stats->retry_bytes; + STATS_OP_FMT(RETRY).nss[0][nss] += peer_stats->retry_bytes; + STATS_OP_FMT(RETRY).gi[0][gi] += peer_stats->retry_bytes; + + STATS_OP_FMT(RETRY).bw[1][bw] += peer_stats->retry_pkts; + STATS_OP_FMT(RETRY).nss[1][nss] += peer_stats->retry_pkts; + STATS_OP_FMT(RETRY).gi[1][gi] += peer_stats->retry_pkts; + + tx_stats->tx_duration += peer_stats->duration; +} + +void ath11k_update_per_peer_stats_from_txcompl(struct ath11k *ar, + struct sk_buff *msdu, + struct hal_tx_status *ts) +{ + struct ath11k_base *ab = ar->ab; + struct ath11k_per_peer_tx_stats *peer_stats = &ar->cached_stats; + struct ath11k_peer *peer; + struct ath11k_sta *arsta; + struct ieee80211_sta *sta; + u16 rate; + u8 rate_idx; + int ret; + + rcu_read_lock(); + spin_lock_bh(&ab->base_lock); + peer = ath11k_peer_find_by_id(ab, ts->peer_id); + if (!peer || !peer->sta) { + ath11k_warn(ab, "failed to find the peer\n"); + spin_unlock_bh(&ab->base_lock); + rcu_read_unlock(); + return; + } + + sta = peer->sta; + arsta = (struct ath11k_sta *)sta->drv_priv; + + memset(&arsta->txrate, 0, sizeof(arsta->txrate)); + + if (ts->pkt_type == HAL_TX_RATE_STATS_PKT_TYPE_11A || + ts->pkt_type == HAL_TX_RATE_STATS_PKT_TYPE_11B) { + ret = ath11k_mac_hw_ratecode_to_legacy_rate(ts->mcs, + ts->pkt_type, + &rate_idx, + &rate); + if (ret < 0) { + spin_unlock_bh(&ab->base_lock); + rcu_read_unlock(); + return; + } + arsta->txrate.legacy = rate; + } else if (ts->pkt_type == HAL_TX_RATE_STATS_PKT_TYPE_11N) { + if (ts->mcs > 7) { + ath11k_warn(ab, "Invalid HT mcs index %d\n", ts->mcs); + spin_unlock_bh(&ab->base_lock); + rcu_read_unlock(); + return; + } + + arsta->txrate.mcs = ts->mcs + 8 * (arsta->last_txrate.nss - 1); + arsta->txrate.flags = RATE_INFO_FLAGS_MCS; + if (ts->sgi) + arsta->txrate.flags |= RATE_INFO_FLAGS_SHORT_GI; + } else if (ts->pkt_type == HAL_TX_RATE_STATS_PKT_TYPE_11AC) { + if (ts->mcs > 9) { + ath11k_warn(ab, "Invalid VHT mcs index %d\n", ts->mcs); + spin_unlock_bh(&ab->base_lock); + rcu_read_unlock(); + return; + } + + arsta->txrate.mcs = ts->mcs; + arsta->txrate.flags = RATE_INFO_FLAGS_VHT_MCS; + if (ts->sgi) + arsta->txrate.flags |= RATE_INFO_FLAGS_SHORT_GI; + } else { + /*TODO: update HE rates */ + } + + arsta->txrate.nss = arsta->last_txrate.nss; + arsta->txrate.bw = ts->bw; + + ath11k_accumulate_per_peer_tx_stats(arsta, peer_stats, rate_idx); + spin_unlock_bh(&ab->base_lock); + rcu_read_unlock(); +} + +static ssize_t ath11k_dbg_sta_dump_tx_stats(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ieee80211_sta *sta = file->private_data; + struct ath11k_sta *arsta = (struct ath11k_sta *)sta->drv_priv; + struct ath11k *ar = arsta->arvif->ar; + struct ath11k_htt_data_stats *stats; + static const char *str_name[ATH11K_STATS_TYPE_MAX] = {"succ", "fail", + "retry", "ampdu"}; + static const char *str[ATH11K_COUNTER_TYPE_MAX] = {"bytes", "packets"}; + int len = 0, i, j, k, retval = 0; + const int size = 2 * 4096; + char *buf; + + buf = kzalloc(size, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + mutex_lock(&ar->conf_mutex); + + spin_lock_bh(&ar->data_lock); + for (k = 0; k < ATH11K_STATS_TYPE_MAX; k++) { + for (j = 0; j < ATH11K_COUNTER_TYPE_MAX; j++) { + stats = &arsta->tx_stats->stats[k]; + len += scnprintf(buf + len, size - len, "%s_%s\n", + str_name[k], + str[j]); + len += scnprintf(buf + len, size - len, + " HE MCS %s\n", + str[j]); + for (i = 0; i < ATH11K_HE_MCS_NUM; i++) + len += scnprintf(buf + len, size - len, + " %llu ", + stats->he[j][i]); + len += scnprintf(buf + len, size - len, "\n"); + len += scnprintf(buf + len, size - len, + " VHT MCS %s\n", + str[j]); + for (i = 0; i < ATH11K_VHT_MCS_NUM; i++) + len += scnprintf(buf + len, size - len, + " %llu ", + stats->vht[j][i]); + len += scnprintf(buf + len, size - len, "\n"); + len += scnprintf(buf + len, size - len, " HT MCS %s\n", + str[j]); + for (i = 0; i < ATH11K_HT_MCS_NUM; i++) + len += scnprintf(buf + len, size - len, + " %llu ", stats->ht[j][i]); + len += scnprintf(buf + len, size - len, "\n"); + len += scnprintf(buf + len, size - len, + " BW %s (20,40,80,160 MHz)\n", str[j]); + len += scnprintf(buf + len, size - len, + " %llu %llu %llu %llu\n", + stats->bw[j][0], stats->bw[j][1], + stats->bw[j][2], stats->bw[j][3]); + len += scnprintf(buf + len, size - len, + " NSS %s (1x1,2x2,3x3,4x4)\n", str[j]); + len += scnprintf(buf + len, size - len, + " %llu %llu %llu %llu\n", + stats->nss[j][0], stats->nss[j][1], + stats->nss[j][2], stats->nss[j][3]); + len += scnprintf(buf + len, size - len, + " GI %s (0.4us,0.8us,1.6us,3.2us)\n", + str[j]); + len += scnprintf(buf + len, size - len, + " %llu %llu %llu %llu\n", + stats->gi[j][0], stats->gi[j][1], + stats->gi[j][2], stats->gi[j][3]); + len += scnprintf(buf + len, size - len, + " legacy rate %s (1,2 ... Mbps)\n ", + str[j]); + for (i = 0; i < ATH11K_LEGACY_NUM; i++) + len += scnprintf(buf + len, size - len, "%llu ", + stats->legacy[j][i]); + len += scnprintf(buf + len, size - len, "\n"); + } + } + + len += scnprintf(buf + len, size - len, + "\nTX duration\n %llu usecs\n", + arsta->tx_stats->tx_duration); + len += scnprintf(buf + len, size - len, + "BA fails\n %llu\n", arsta->tx_stats->ba_fails); + len += scnprintf(buf + len, size - len, + "ack fails\n %llu\n", arsta->tx_stats->ack_fails); + spin_unlock_bh(&ar->data_lock); + + if (len > size) + len = size; + retval = simple_read_from_buffer(user_buf, count, ppos, buf, len); + kfree(buf); + + mutex_unlock(&ar->conf_mutex); + return retval; +} + +static const struct file_operations fops_tx_stats = { + .read = ath11k_dbg_sta_dump_tx_stats, + .open = simple_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +static ssize_t ath11k_dbg_sta_dump_rx_stats(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ieee80211_sta *sta = file->private_data; + struct ath11k_sta *arsta = (struct ath11k_sta *)sta->drv_priv; + struct ath11k *ar = arsta->arvif->ar; + struct ath11k_rx_peer_stats *rx_stats = arsta->rx_stats; + int len = 0, i, retval = 0; + const int size = 4096; + char *buf; + + if (!rx_stats) + return -ENOENT; + + buf = kzalloc(size, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + mutex_lock(&ar->conf_mutex); + spin_lock_bh(&ar->ab->base_lock); + + len += scnprintf(buf + len, size - len, "RX peer stats:\n"); + len += scnprintf(buf + len, size - len, "Num of MSDUs: %llu\n", + rx_stats->num_msdu); + len += scnprintf(buf + len, size - len, "Num of MSDUs with TCP L4: %llu\n", + rx_stats->tcp_msdu_count); + len += scnprintf(buf + len, size - len, "Num of MSDUs with UDP L4: %llu\n", + rx_stats->udp_msdu_count); + len += scnprintf(buf + len, size - len, "Num of MSDUs part of AMPDU: %llu\n", + rx_stats->ampdu_msdu_count); + len += scnprintf(buf + len, size - len, "Num of MSDUs not part of AMPDU: %llu\n", + rx_stats->non_ampdu_msdu_count); + len += scnprintf(buf + len, size - len, "Num of MSDUs using STBC: %llu\n", + rx_stats->stbc_count); + len += scnprintf(buf + len, size - len, "Num of MSDUs beamformed: %llu\n", + rx_stats->beamformed_count); + len += scnprintf(buf + len, size - len, "Num of MPDUs with FCS ok: %llu\n", + rx_stats->num_mpdu_fcs_ok); + len += scnprintf(buf + len, size - len, "Num of MPDUs with FCS error: %llu\n", + rx_stats->num_mpdu_fcs_err); + len += scnprintf(buf + len, size - len, + "GI: 0.8us %llu 0.4us %llu 1.6us %llu 3.2us %llu\n", + rx_stats->gi_count[0], rx_stats->gi_count[1], + rx_stats->gi_count[2], rx_stats->gi_count[3]); + len += scnprintf(buf + len, size - len, + "BW: 20Mhz %llu 40Mhz %llu 80Mhz %llu 160Mhz %llu\n", + rx_stats->bw_count[0], rx_stats->bw_count[1], + rx_stats->bw_count[2], rx_stats->bw_count[3]); + len += scnprintf(buf + len, size - len, "BCC %llu LDPC %llu\n", + rx_stats->coding_count[0], rx_stats->coding_count[1]); + len += scnprintf(buf + len, size - len, + "preamble: 11A %llu 11B %llu 11N %llu 11AC %llu 11AX %llu\n", + rx_stats->pream_cnt[0], rx_stats->pream_cnt[1], + rx_stats->pream_cnt[2], rx_stats->pream_cnt[3], + rx_stats->pream_cnt[4]); + len += scnprintf(buf + len, size - len, + "reception type: SU %llu MU_MIMO %llu MU_OFDMA %llu MU_OFDMA_MIMO %llu\n", + rx_stats->reception_type[0], rx_stats->reception_type[1], + rx_stats->reception_type[2], rx_stats->reception_type[3]); + len += scnprintf(buf + len, size - len, "TID(0-15) Legacy TID(16):"); + for (i = 0; i <= IEEE80211_NUM_TIDS; i++) + len += scnprintf(buf + len, size - len, "%llu ", rx_stats->tid_count[i]); + len += scnprintf(buf + len, size - len, "\nMCS(0-11) Legacy MCS(12):"); + for (i = 0; i < HAL_RX_MAX_MCS + 1; i++) + len += scnprintf(buf + len, size - len, "%llu ", rx_stats->mcs_count[i]); + len += scnprintf(buf + len, size - len, "\nNSS(1-8):"); + for (i = 0; i < HAL_RX_MAX_NSS; i++) + len += scnprintf(buf + len, size - len, "%llu ", rx_stats->nss_count[i]); + len += scnprintf(buf + len, size - len, "\nRX Duration:%llu ", + rx_stats->rx_duration); + len += scnprintf(buf + len, size - len, "\n"); + + spin_unlock_bh(&ar->ab->base_lock); + + if (len > size) + len = size; + retval = simple_read_from_buffer(user_buf, count, ppos, buf, len); + kfree(buf); + + mutex_unlock(&ar->conf_mutex); + return retval; +} + +static const struct file_operations fops_rx_stats = { + .read = ath11k_dbg_sta_dump_rx_stats, + .open = simple_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +static int +ath11k_dbg_sta_open_htt_peer_stats(struct inode *inode, struct file *file) +{ + struct ieee80211_sta *sta = inode->i_private; + struct ath11k_sta *arsta = (struct ath11k_sta *)sta->drv_priv; + struct ath11k *ar = arsta->arvif->ar; + struct debug_htt_stats_req *stats_req; + int ret; + + stats_req = vzalloc(sizeof(*stats_req) + ATH11K_HTT_STATS_BUF_SIZE); + if (!stats_req) + return -ENOMEM; + + mutex_lock(&ar->conf_mutex); + ar->debug.htt_stats.stats_req = stats_req; + stats_req->type = ATH11K_DBG_HTT_EXT_STATS_PEER_INFO; + memcpy(stats_req->peer_addr, sta->addr, ETH_ALEN); + ret = ath11k_dbg_htt_stats_req(ar); + mutex_unlock(&ar->conf_mutex); + if (ret < 0) + goto out; + + file->private_data = stats_req; + return 0; +out: + vfree(stats_req); + return ret; +} + +static int +ath11k_dbg_sta_release_htt_peer_stats(struct inode *inode, struct file *file) +{ + vfree(file->private_data); + return 0; +} + +static ssize_t ath11k_dbg_sta_read_htt_peer_stats(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct debug_htt_stats_req *stats_req = file->private_data; + char *buf; + u32 length = 0; + + buf = stats_req->buf; + length = min_t(u32, stats_req->buf_len, ATH11K_HTT_STATS_BUF_SIZE); + return simple_read_from_buffer(user_buf, count, ppos, buf, length); +} + +static const struct file_operations fops_htt_peer_stats = { + .open = ath11k_dbg_sta_open_htt_peer_stats, + .release = ath11k_dbg_sta_release_htt_peer_stats, + .read = ath11k_dbg_sta_read_htt_peer_stats, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +static ssize_t ath11k_dbg_sta_write_peer_pktlog(struct file *file, + const char __user *buf, + size_t count, loff_t *ppos) +{ + struct ieee80211_sta *sta = file->private_data; + struct ath11k_sta *arsta = (struct ath11k_sta *)sta->drv_priv; + struct ath11k *ar = arsta->arvif->ar; + int ret, enable; + + mutex_lock(&ar->conf_mutex); + + if (ar->state != ATH11K_STATE_ON) { + ret = -ENETDOWN; + goto out; + } + + ret = kstrtoint_from_user(buf, count, 0, &enable); + if (ret) + goto out; + + ar->debug.pktlog_peer_valid = enable; + memcpy(ar->debug.pktlog_peer_addr, sta->addr, ETH_ALEN); + + /* Send peer based pktlog enable/disable */ + ret = ath11k_wmi_pdev_peer_pktlog_filter(ar, sta->addr, enable); + if (ret) { + ath11k_warn(ar->ab, "failed to set peer pktlog filter %pM: %d\n", + sta->addr, ret); + goto out; + } + + ath11k_dbg(ar->ab, ATH11K_DBG_WMI, "peer pktlog filter set to %d\n", + enable); + ret = count; + +out: + mutex_unlock(&ar->conf_mutex); + return ret; +} + +static ssize_t ath11k_dbg_sta_read_peer_pktlog(struct file *file, + char __user *ubuf, + size_t count, loff_t *ppos) +{ + struct ieee80211_sta *sta = file->private_data; + struct ath11k_sta *arsta = (struct ath11k_sta *)sta->drv_priv; + struct ath11k *ar = arsta->arvif->ar; + char buf[32] = {0}; + int len; + + mutex_lock(&ar->conf_mutex); + len = scnprintf(buf, sizeof(buf), "%08x %pM\n", + ar->debug.pktlog_peer_valid, + ar->debug.pktlog_peer_addr); + mutex_unlock(&ar->conf_mutex); + + return simple_read_from_buffer(ubuf, count, ppos, buf, len); +} + +static const struct file_operations fops_peer_pktlog = { + .write = ath11k_dbg_sta_write_peer_pktlog, + .read = ath11k_dbg_sta_read_peer_pktlog, + .open = simple_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +void ath11k_sta_add_debugfs(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_sta *sta, struct dentry *dir) +{ + struct ath11k *ar = hw->priv; + + if (ath11k_debug_is_extd_tx_stats_enabled(ar)) + debugfs_create_file("tx_stats", 0400, dir, sta, + &fops_tx_stats); + if (ath11k_debug_is_extd_rx_stats_enabled(ar)) + debugfs_create_file("rx_stats", 0400, dir, sta, + &fops_rx_stats); + + debugfs_create_file("htt_peer_stats", 0400, dir, sta, + &fops_htt_peer_stats); + + debugfs_create_file("peer_pktlog", 0644, dir, sta, + &fops_peer_pktlog); +} diff --git a/drivers/net/wireless/ath/ath11k/dp.c b/drivers/net/wireless/ath/ath11k/dp.c new file mode 100644 index 0000000000000..aa2b28189cda5 --- /dev/null +++ b/drivers/net/wireless/ath/ath11k/dp.c @@ -0,0 +1,898 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* + * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. + */ + +#include +#include "core.h" +#include "dp_tx.h" +#include "hal_tx.h" +#include "debug.h" +#include "dp_rx.h" +#include "peer.h" + +static void ath11k_dp_htt_htc_tx_complete(struct ath11k_base *ab, + struct sk_buff *skb) +{ + dev_kfree_skb_any(skb); +} + +void ath11k_dp_peer_cleanup(struct ath11k *ar, int vdev_id, const u8 *addr) +{ + struct ath11k_base *ab = ar->ab; + struct ath11k_peer *peer; + + /* TODO: Any other peer specific DP cleanup */ + + spin_lock_bh(&ab->base_lock); + peer = ath11k_peer_find(ab, vdev_id, addr); + if (!peer) { + ath11k_warn(ab, "failed to lookup peer %pM on vdev %d\n", + addr, vdev_id); + spin_unlock_bh(&ab->base_lock); + return; + } + + ath11k_peer_rx_tid_cleanup(ar, peer); + spin_unlock_bh(&ab->base_lock); +} + +int ath11k_dp_peer_setup(struct ath11k *ar, int vdev_id, const u8 *addr) +{ + struct ath11k_base *ab = ar->ab; + u32 reo_dest; + int ret; + + /* NOTE: reo_dest ring id starts from 1 unlike mac_id which starts from 0 */ + reo_dest = ar->dp.mac_id + 1; + ret = ath11k_wmi_set_peer_param(ar, addr, vdev_id, + WMI_PEER_SET_DEFAULT_ROUTING, + DP_RX_HASH_ENABLE | (reo_dest << 1)); + + if (ret) { + ath11k_warn(ab, "failed to set default routing %d peer :%pM vdev_id :%d\n", + ret, addr, vdev_id); + return ret; + } + + ret = ath11k_peer_rx_tid_setup(ar, addr, vdev_id, + HAL_DESC_REO_NON_QOS_TID, 1, 0); + if (ret) { + ath11k_warn(ab, "failed to setup rxd tid queue for non-qos tid %d\n", + ret); + return ret; + } + + ret = ath11k_peer_rx_tid_setup(ar, addr, vdev_id, 0, 1, 0); + if (ret) { + ath11k_warn(ab, "failed to setup rxd tid queue for tid 0 %d\n", + ret); + return ret; + } + + /* TODO: Setup other peer specific resource used in data path */ + + return 0; +} + +void ath11k_dp_srng_cleanup(struct ath11k_base *ab, struct dp_srng *ring) +{ + if (!ring->vaddr_unaligned) + return; + + dma_free_coherent(ab->dev, ring->size, ring->vaddr_unaligned, + ring->paddr_unaligned); + + ring->vaddr_unaligned = NULL; +} + +int ath11k_dp_srng_setup(struct ath11k_base *ab, struct dp_srng *ring, + enum hal_ring_type type, int ring_num, + int mac_id, int num_entries) +{ + struct hal_srng_params params = { 0 }; + int entry_sz = ath11k_hal_srng_get_entrysize(type); + int max_entries = ath11k_hal_srng_get_max_entries(type); + int ret; + + if (max_entries < 0 || entry_sz < 0) + return -EINVAL; + + if (num_entries > max_entries) + num_entries = max_entries; + + ring->size = (num_entries * entry_sz) + HAL_RING_BASE_ALIGN - 1; + ring->vaddr_unaligned = dma_alloc_coherent(ab->dev, ring->size, + &ring->paddr_unaligned, + GFP_KERNEL); + if (!ring->vaddr_unaligned) + return -ENOMEM; + + ring->vaddr = PTR_ALIGN(ring->vaddr_unaligned, HAL_RING_BASE_ALIGN); + ring->paddr = ring->paddr_unaligned + ((unsigned long)ring->vaddr - + (unsigned long)ring->vaddr_unaligned); + + params.ring_base_vaddr = ring->vaddr; + params.ring_base_paddr = ring->paddr; + params.num_entries = num_entries; + + switch (type) { + case HAL_REO_DST: + params.intr_batch_cntr_thres_entries = + HAL_SRNG_INT_BATCH_THRESHOLD_RX; + params.intr_timer_thres_us = HAL_SRNG_INT_TIMER_THRESHOLD_RX; + break; + case HAL_RXDMA_BUF: + case HAL_RXDMA_MONITOR_BUF: + case HAL_RXDMA_MONITOR_STATUS: + params.low_threshold = num_entries >> 3; + params.flags |= HAL_SRNG_FLAGS_LOW_THRESH_INTR_EN; + params.intr_batch_cntr_thres_entries = 0; + params.intr_timer_thres_us = HAL_SRNG_INT_TIMER_THRESHOLD_RX; + break; + case HAL_WBM2SW_RELEASE: + if (ring_num < 3) { + params.intr_batch_cntr_thres_entries = + HAL_SRNG_INT_BATCH_THRESHOLD_TX; + params.intr_timer_thres_us = + HAL_SRNG_INT_TIMER_THRESHOLD_TX; + break; + } + /* follow through when ring_num >= 3 */ + /* fall through */ + case HAL_REO_EXCEPTION: + case HAL_REO_REINJECT: + case HAL_REO_CMD: + case HAL_REO_STATUS: + case HAL_TCL_DATA: + case HAL_TCL_CMD: + case HAL_TCL_STATUS: + case HAL_WBM_IDLE_LINK: + case HAL_SW2WBM_RELEASE: + case HAL_RXDMA_DST: + case HAL_RXDMA_MONITOR_DST: + case HAL_RXDMA_MONITOR_DESC: + case HAL_RXDMA_DIR_BUF: + params.intr_batch_cntr_thres_entries = + HAL_SRNG_INT_BATCH_THRESHOLD_OTHER; + params.intr_timer_thres_us = HAL_SRNG_INT_TIMER_THRESHOLD_OTHER; + break; + default: + ath11k_warn(ab, "Not a valid ring type in dp :%d\n", type); + return -EINVAL; + } + + ret = ath11k_hal_srng_setup(ab, type, ring_num, mac_id, ¶ms); + if (ret < 0) { + ath11k_warn(ab, "failed to setup srng: %d ring_id %d\n", + ret, ring_num); + return ret; + } + + ring->ring_id = ret; + + return 0; +} + +static void ath11k_dp_srng_common_cleanup(struct ath11k_base *ab) +{ + struct ath11k_dp *dp = &ab->dp; + int i; + + ath11k_dp_srng_cleanup(ab, &dp->wbm_desc_rel_ring); + ath11k_dp_srng_cleanup(ab, &dp->tcl_cmd_ring); + ath11k_dp_srng_cleanup(ab, &dp->tcl_status_ring); + for (i = 0; i < DP_TCL_NUM_RING_MAX; i++) { + ath11k_dp_srng_cleanup(ab, &dp->tx_ring[i].tcl_data_ring); + ath11k_dp_srng_cleanup(ab, &dp->tx_ring[i].tcl_comp_ring); + } + ath11k_dp_srng_cleanup(ab, &dp->reo_reinject_ring); + ath11k_dp_srng_cleanup(ab, &dp->rx_rel_ring); + ath11k_dp_srng_cleanup(ab, &dp->reo_except_ring); + ath11k_dp_srng_cleanup(ab, &dp->reo_cmd_ring); + ath11k_dp_srng_cleanup(ab, &dp->reo_status_ring); +} + +static int ath11k_dp_srng_common_setup(struct ath11k_base *ab) +{ + struct ath11k_dp *dp = &ab->dp; + struct hal_srng *srng; + int i, ret; + + ret = ath11k_dp_srng_setup(ab, &dp->wbm_desc_rel_ring, + HAL_SW2WBM_RELEASE, 0, 0, + DP_WBM_RELEASE_RING_SIZE); + if (ret) { + ath11k_warn(ab, "failed to set up wbm2sw_release ring :%d\n", + ret); + goto err; + } + + ret = ath11k_dp_srng_setup(ab, &dp->tcl_cmd_ring, HAL_TCL_CMD, 0, 0, + DP_TCL_CMD_RING_SIZE); + if (ret) { + ath11k_warn(ab, "failed to set up tcl_cmd ring :%d\n", ret); + goto err; + } + + ret = ath11k_dp_srng_setup(ab, &dp->tcl_status_ring, HAL_TCL_STATUS, + 0, 0, DP_TCL_STATUS_RING_SIZE); + if (ret) { + ath11k_warn(ab, "failed to set up tcl_status ring :%d\n", ret); + goto err; + } + + for (i = 0; i < DP_TCL_NUM_RING_MAX; i++) { + ret = ath11k_dp_srng_setup(ab, &dp->tx_ring[i].tcl_data_ring, + HAL_TCL_DATA, i, 0, + DP_TCL_DATA_RING_SIZE); + if (ret) { + ath11k_warn(ab, "failed to set up tcl_data ring (%d) :%d\n", + i, ret); + goto err; + } + + ret = ath11k_dp_srng_setup(ab, &dp->tx_ring[i].tcl_comp_ring, + HAL_WBM2SW_RELEASE, i, 0, + DP_TX_COMP_RING_SIZE); + if (ret) { + ath11k_warn(ab, "failed to set up tcl_comp ring ring (%d) :%d\n", + i, ret); + goto err; + } + + srng = &ab->hal.srng_list[dp->tx_ring[i].tcl_data_ring.ring_id]; + ath11k_hal_tx_init_data_ring(ab, srng); + } + + ret = ath11k_dp_srng_setup(ab, &dp->reo_reinject_ring, HAL_REO_REINJECT, + 0, 0, DP_REO_REINJECT_RING_SIZE); + if (ret) { + ath11k_warn(ab, "failed to set up reo_reinject ring :%d\n", + ret); + goto err; + } + + ret = ath11k_dp_srng_setup(ab, &dp->rx_rel_ring, HAL_WBM2SW_RELEASE, + 3, 0, DP_RX_RELEASE_RING_SIZE); + if (ret) { + ath11k_warn(ab, "failed to set up rx_rel ring :%d\n", ret); + goto err; + } + + ret = ath11k_dp_srng_setup(ab, &dp->reo_except_ring, HAL_REO_EXCEPTION, + 0, 0, DP_REO_EXCEPTION_RING_SIZE); + if (ret) { + ath11k_warn(ab, "failed to set up reo_exception ring :%d\n", + ret); + goto err; + } + + ret = ath11k_dp_srng_setup(ab, &dp->reo_cmd_ring, HAL_REO_CMD, + 0, 0, DP_REO_CMD_RING_SIZE); + if (ret) { + ath11k_warn(ab, "failed to set up reo_cmd ring :%d\n", ret); + goto err; + } + + srng = &ab->hal.srng_list[dp->reo_cmd_ring.ring_id]; + ath11k_hal_reo_init_cmd_ring(ab, srng); + + ret = ath11k_dp_srng_setup(ab, &dp->reo_status_ring, HAL_REO_STATUS, + 0, 0, DP_REO_STATUS_RING_SIZE); + if (ret) { + ath11k_warn(ab, "failed to set up reo_status ring :%d\n", ret); + goto err; + } + + ath11k_hal_reo_hw_setup(ab); + + return 0; + +err: + ath11k_dp_srng_common_cleanup(ab); + + return ret; +} + +static void ath11k_dp_scatter_idle_link_desc_cleanup(struct ath11k_base *ab) +{ + struct ath11k_dp *dp = &ab->dp; + struct hal_wbm_idle_scatter_list *slist = dp->scatter_list; + int i; + + for (i = 0; i < DP_IDLE_SCATTER_BUFS_MAX; i++) { + if (!slist[i].vaddr) + continue; + + dma_free_coherent(ab->dev, HAL_WBM_IDLE_SCATTER_BUF_SIZE_MAX, + slist[i].vaddr, slist[i].paddr); + slist[i].vaddr = NULL; + } +} + +static int ath11k_dp_scatter_idle_link_desc_setup(struct ath11k_base *ab, + int size, + u32 n_link_desc_bank, + u32 n_link_desc, + u32 last_bank_sz) +{ + struct ath11k_dp *dp = &ab->dp; + struct dp_link_desc_bank *link_desc_banks = dp->link_desc_banks; + struct hal_wbm_idle_scatter_list *slist = dp->scatter_list; + u32 n_entries_per_buf; + int num_scatter_buf, scatter_idx; + struct hal_wbm_link_desc *scatter_buf; + int align_bytes, n_entries; + dma_addr_t paddr; + int rem_entries; + int i; + int ret = 0; + u32 end_offset; + + n_entries_per_buf = HAL_WBM_IDLE_SCATTER_BUF_SIZE / + ath11k_hal_srng_get_entrysize(HAL_WBM_IDLE_LINK); + num_scatter_buf = DIV_ROUND_UP(size, HAL_WBM_IDLE_SCATTER_BUF_SIZE); + + if (num_scatter_buf > DP_IDLE_SCATTER_BUFS_MAX) + return -EINVAL; + + for (i = 0; i < num_scatter_buf; i++) { + slist[i].vaddr = dma_alloc_coherent(ab->dev, + HAL_WBM_IDLE_SCATTER_BUF_SIZE_MAX, + &slist[i].paddr, GFP_KERNEL); + if (!slist[i].vaddr) { + ret = -ENOMEM; + goto err; + } + } + + scatter_idx = 0; + scatter_buf = slist[scatter_idx].vaddr; + rem_entries = n_entries_per_buf; + + for (i = 0; i < n_link_desc_bank; i++) { + align_bytes = link_desc_banks[i].vaddr - + link_desc_banks[i].vaddr_unaligned; + n_entries = (DP_LINK_DESC_ALLOC_SIZE_THRESH - align_bytes) / + HAL_LINK_DESC_SIZE; + paddr = link_desc_banks[i].paddr; + while (n_entries) { + ath11k_hal_set_link_desc_addr(scatter_buf, i, paddr); + n_entries--; + paddr += HAL_LINK_DESC_SIZE; + if (rem_entries) { + rem_entries--; + scatter_buf++; + continue; + } + + rem_entries = n_entries_per_buf; + scatter_idx++; + scatter_buf = slist[scatter_idx].vaddr; + } + } + + end_offset = (scatter_buf - slist[scatter_idx].vaddr) * + sizeof(struct hal_wbm_link_desc); + ath11k_hal_setup_link_idle_list(ab, slist, num_scatter_buf, + n_link_desc, end_offset); + + return 0; + +err: + ath11k_dp_scatter_idle_link_desc_cleanup(ab); + + return ret; +} + +static void +ath11k_dp_link_desc_bank_free(struct ath11k_base *ab, + struct dp_link_desc_bank *link_desc_banks) +{ + int i; + + for (i = 0; i < DP_LINK_DESC_BANKS_MAX; i++) { + if (link_desc_banks[i].vaddr_unaligned) { + dma_free_coherent(ab->dev, + link_desc_banks[i].size, + link_desc_banks[i].vaddr_unaligned, + link_desc_banks[i].paddr_unaligned); + link_desc_banks[i].vaddr_unaligned = NULL; + } + } +} + +static int ath11k_dp_link_desc_bank_alloc(struct ath11k_base *ab, + struct dp_link_desc_bank *desc_bank, + int n_link_desc_bank, + int last_bank_sz) +{ + struct ath11k_dp *dp = &ab->dp; + int i; + int ret = 0; + int desc_sz = DP_LINK_DESC_ALLOC_SIZE_THRESH; + + for (i = 0; i < n_link_desc_bank; i++) { + if (i == (n_link_desc_bank - 1) && last_bank_sz) + desc_sz = last_bank_sz; + + desc_bank[i].vaddr_unaligned = + dma_alloc_coherent(ab->dev, desc_sz, + &desc_bank[i].paddr_unaligned, + GFP_KERNEL); + if (!desc_bank[i].vaddr_unaligned) { + ret = -ENOMEM; + goto err; + } + + desc_bank[i].vaddr = PTR_ALIGN(desc_bank[i].vaddr_unaligned, + HAL_LINK_DESC_ALIGN); + desc_bank[i].paddr = desc_bank[i].paddr_unaligned + + ((unsigned long)desc_bank[i].vaddr - + (unsigned long)desc_bank[i].vaddr_unaligned); + desc_bank[i].size = desc_sz; + } + + return 0; + +err: + ath11k_dp_link_desc_bank_free(ab, dp->link_desc_banks); + + return ret; +} + +void ath11k_dp_link_desc_cleanup(struct ath11k_base *ab, + struct dp_link_desc_bank *desc_bank, + u32 ring_type, struct dp_srng *ring) +{ + ath11k_dp_link_desc_bank_free(ab, desc_bank); + + if (ring_type != HAL_RXDMA_MONITOR_DESC) { + ath11k_dp_srng_cleanup(ab, ring); + ath11k_dp_scatter_idle_link_desc_cleanup(ab); + } +} + +static int ath11k_wbm_idle_ring_setup(struct ath11k_base *ab, u32 *n_link_desc) +{ + struct ath11k_dp *dp = &ab->dp; + u32 n_mpdu_link_desc, n_mpdu_queue_desc; + u32 n_tx_msdu_link_desc, n_rx_msdu_link_desc; + int ret = 0; + + n_mpdu_link_desc = (DP_NUM_TIDS_MAX * DP_AVG_MPDUS_PER_TID_MAX) / + HAL_NUM_MPDUS_PER_LINK_DESC; + + n_mpdu_queue_desc = n_mpdu_link_desc / + HAL_NUM_MPDU_LINKS_PER_QUEUE_DESC; + + n_tx_msdu_link_desc = (DP_NUM_TIDS_MAX * DP_AVG_FLOWS_PER_TID * + DP_AVG_MSDUS_PER_FLOW) / + HAL_NUM_TX_MSDUS_PER_LINK_DESC; + + n_rx_msdu_link_desc = (DP_NUM_TIDS_MAX * DP_AVG_MPDUS_PER_TID_MAX * + DP_AVG_MSDUS_PER_MPDU) / + HAL_NUM_RX_MSDUS_PER_LINK_DESC; + + *n_link_desc = n_mpdu_link_desc + n_mpdu_queue_desc + + n_tx_msdu_link_desc + n_rx_msdu_link_desc; + + ret = ath11k_dp_srng_setup(ab, &dp->wbm_idle_ring, + HAL_WBM_IDLE_LINK, 0, 0, *n_link_desc); + if (ret) { + ath11k_warn(ab, "failed to setup wbm_idle_ring: %d\n", ret); + return ret; + } + return ret; +} + +int ath11k_dp_link_desc_setup(struct ath11k_base *ab, + struct dp_link_desc_bank *link_desc_banks, + u32 ring_type, struct hal_srng *srng, + u32 n_link_desc) +{ + u32 tot_mem_sz; + u32 n_link_desc_bank, last_bank_sz; + u32 entry_sz, align_bytes, n_entries; + u32 paddr; + u32 *desc; + int i, ret; + + if (n_link_desc & (n_link_desc - 1)) + n_link_desc = 1 << fls(n_link_desc); + + tot_mem_sz = n_link_desc * HAL_LINK_DESC_SIZE; + tot_mem_sz += HAL_LINK_DESC_ALIGN; + + if (tot_mem_sz <= DP_LINK_DESC_ALLOC_SIZE_THRESH) { + n_link_desc_bank = 1; + last_bank_sz = tot_mem_sz; + } else { + n_link_desc_bank = tot_mem_sz / + (DP_LINK_DESC_ALLOC_SIZE_THRESH - + HAL_LINK_DESC_ALIGN); + last_bank_sz = tot_mem_sz % + (DP_LINK_DESC_ALLOC_SIZE_THRESH - + HAL_LINK_DESC_ALIGN); + + if (last_bank_sz) + n_link_desc_bank += 1; + } + + if (n_link_desc_bank > DP_LINK_DESC_BANKS_MAX) + return -EINVAL; + + ret = ath11k_dp_link_desc_bank_alloc(ab, link_desc_banks, + n_link_desc_bank, last_bank_sz); + if (ret) + return ret; + + /* Setup link desc idle list for HW internal usage */ + entry_sz = ath11k_hal_srng_get_entrysize(ring_type); + tot_mem_sz = entry_sz * n_link_desc; + + /* Setup scatter desc list when the total memory requirement is more */ + if (tot_mem_sz > DP_LINK_DESC_ALLOC_SIZE_THRESH && + ring_type != HAL_RXDMA_MONITOR_DESC) { + ret = ath11k_dp_scatter_idle_link_desc_setup(ab, tot_mem_sz, + n_link_desc_bank, + n_link_desc, + last_bank_sz); + if (ret) { + ath11k_warn(ab, "failed to setup scatting idle list descriptor :%d\n", + ret); + goto fail_desc_bank_free; + } + + return 0; + } + + spin_lock_bh(&srng->lock); + + ath11k_hal_srng_access_begin(ab, srng); + + for (i = 0; i < n_link_desc_bank; i++) { + align_bytes = link_desc_banks[i].vaddr - + link_desc_banks[i].vaddr_unaligned; + n_entries = (link_desc_banks[i].size - align_bytes) / + HAL_LINK_DESC_SIZE; + paddr = link_desc_banks[i].paddr; + while (n_entries && + (desc = ath11k_hal_srng_src_get_next_entry(ab, srng))) { + ath11k_hal_set_link_desc_addr((struct hal_wbm_link_desc *)desc, + i, paddr); + n_entries--; + paddr += HAL_LINK_DESC_SIZE; + } + } + + ath11k_hal_srng_access_end(ab, srng); + + spin_unlock_bh(&srng->lock); + + return 0; + +fail_desc_bank_free: + ath11k_dp_link_desc_bank_free(ab, link_desc_banks); + + return ret; +} + +int ath11k_dp_service_srng(struct ath11k_base *ab, + struct ath11k_ext_irq_grp *irq_grp, + int budget) +{ + struct napi_struct *napi = &irq_grp->napi; + int grp_id = irq_grp->grp_id; + int work_done = 0; + int i = 0; + int tot_work_done = 0; + + while (ath11k_tx_ring_mask[grp_id] >> i) { + if (ath11k_tx_ring_mask[grp_id] & BIT(i)) + ath11k_dp_tx_completion_handler(ab, i); + i++; + } + + if (ath11k_rx_err_ring_mask[grp_id]) { + work_done = ath11k_dp_process_rx_err(ab, napi, budget); + budget -= work_done; + tot_work_done += work_done; + if (budget <= 0) + goto done; + } + + if (ath11k_rx_wbm_rel_ring_mask[grp_id]) { + work_done = ath11k_dp_rx_process_wbm_err(ab, + napi, + budget); + budget -= work_done; + tot_work_done += work_done; + + if (budget <= 0) + goto done; + } + + if (ath11k_rx_ring_mask[grp_id]) { + for (i = 0; i < ab->num_radios; i++) { + if (ath11k_rx_ring_mask[grp_id] & BIT(i)) { + work_done = ath11k_dp_process_rx(ab, i, napi, + &irq_grp->pending_q, + budget); + budget -= work_done; + tot_work_done += work_done; + } + if (budget <= 0) + goto done; + } + } + + if (rx_mon_status_ring_mask[grp_id]) { + for (i = 0; i < ab->num_radios; i++) { + if (rx_mon_status_ring_mask[grp_id] & BIT(i)) { + work_done = + ath11k_dp_rx_process_mon_rings(ab, + i, napi, + budget); + budget -= work_done; + tot_work_done += work_done; + } + if (budget <= 0) + goto done; + } + } + + if (ath11k_reo_status_ring_mask[grp_id]) + ath11k_dp_process_reo_status(ab); + + for (i = 0; i < ab->num_radios; i++) { + if (ath11k_rxdma2host_ring_mask[grp_id] & BIT(i)) { + work_done = ath11k_dp_process_rxdma_err(ab, i, budget); + budget -= work_done; + tot_work_done += work_done; + } + + if (budget <= 0) + goto done; + + if (ath11k_host2rxdma_ring_mask[grp_id] & BIT(i)) { + struct ath11k_pdev_dp *dp = &ab->pdevs[i].ar->dp; + struct dp_rxdma_ring *rx_ring = &dp->rx_refill_buf_ring; + + ath11k_dp_rxbufs_replenish(ab, i, rx_ring, 0, + HAL_RX_BUF_RBM_SW3_BM, + GFP_ATOMIC); + } + } + /* TODO: Implement handler for other interrupts */ + +done: + return tot_work_done; +} + +void ath11k_dp_pdev_free(struct ath11k_base *ab) +{ + struct ath11k *ar; + int i; + + for (i = 0; i < ab->num_radios; i++) { + ar = ab->pdevs[i].ar; + ath11k_dp_rx_pdev_free(ab, i); + ath11k_debug_unregister(ar); + ath11k_dp_rx_pdev_mon_detach(ar); + } +} + +int ath11k_dp_pdev_alloc(struct ath11k_base *ab) +{ + struct ath11k *ar; + struct ath11k_pdev_dp *dp; + int ret; + int i; + + for (i = 0; i < ab->num_radios; i++) { + ar = ab->pdevs[i].ar; + dp = &ar->dp; + dp->mac_id = i; + idr_init(&dp->rx_refill_buf_ring.bufs_idr); + spin_lock_init(&dp->rx_refill_buf_ring.idr_lock); + atomic_set(&dp->num_tx_pending, 0); + init_waitqueue_head(&dp->tx_empty_waitq); + idr_init(&dp->rx_mon_status_refill_ring.bufs_idr); + spin_lock_init(&dp->rx_mon_status_refill_ring.idr_lock); + idr_init(&dp->rxdma_mon_buf_ring.bufs_idr); + spin_lock_init(&dp->rxdma_mon_buf_ring.idr_lock); + } + + /* TODO:Per-pdev rx ring unlike tx ring which is mapped to different AC's */ + for (i = 0; i < ab->num_radios; i++) { + ar = ab->pdevs[i].ar; + ret = ath11k_dp_rx_pdev_alloc(ab, i); + if (ret) { + ath11k_warn(ab, "failed to allocate pdev rx for pdev_id :%d\n", + i); + goto err; + } + ret = ath11k_dp_rx_pdev_mon_attach(ar); + if (ret) { + ath11k_warn(ab, "failed to initialize mon pdev %d\n", + i); + goto err; + } + } + + return 0; + +err: + ath11k_dp_pdev_free(ab); + + return ret; +} + +int ath11k_dp_htt_connect(struct ath11k_dp *dp) +{ + struct ath11k_htc_svc_conn_req conn_req; + struct ath11k_htc_svc_conn_resp conn_resp; + int status; + + memset(&conn_req, 0, sizeof(conn_req)); + memset(&conn_resp, 0, sizeof(conn_resp)); + + conn_req.ep_ops.ep_tx_complete = ath11k_dp_htt_htc_tx_complete; + conn_req.ep_ops.ep_rx_complete = ath11k_dp_htt_htc_t2h_msg_handler; + + /* connect to control service */ + conn_req.service_id = ATH11K_HTC_SVC_ID_HTT_DATA_MSG; + + status = ath11k_htc_connect_service(&dp->ab->htc, &conn_req, + &conn_resp); + + if (status) + return status; + + dp->eid = conn_resp.eid; + + return 0; +} + +static void ath11k_dp_update_vdev_search(struct ath11k_vif *arvif) +{ + /* Enable AddrY (SA based search) for STA mode. All other modes it + * is going to be AddrX (DA based search). For STA mode, set search + * type based on AST value. + */ + switch (arvif->vdev_type) { + case WMI_VDEV_TYPE_STA: + arvif->hal_addr_search_flags = HAL_TX_ADDRY_EN; + arvif->search_type = HAL_TX_ADDR_SEARCH_INDEX; + break; + case WMI_VDEV_TYPE_AP: + case WMI_VDEV_TYPE_IBSS: + arvif->hal_addr_search_flags = HAL_TX_ADDRX_EN; + arvif->search_type = HAL_TX_ADDR_SEARCH_DEFAULT; + break; + case WMI_VDEV_TYPE_MONITOR: + default: + return; + } +} + +void ath11k_dp_vdev_tx_attach(struct ath11k *ar, struct ath11k_vif *arvif) +{ + arvif->tcl_metadata |= FIELD_PREP(HTT_TCL_META_DATA_TYPE, 1) | + FIELD_PREP(HTT_TCL_META_DATA_VDEV_ID, + arvif->vdev_id) | + FIELD_PREP(HTT_TCL_META_DATA_PDEV_ID, + ar->pdev->pdev_id); + + /* set HTT extension valid bit to 0 by default */ + arvif->tcl_metadata &= ~HTT_TCL_META_DATA_VALID_HTT; + + ath11k_dp_update_vdev_search(arvif); +} + +static int ath11k_dp_tx_pending_cleanup(int buf_id, void *skb, void *ctx) +{ + struct ath11k_base *ab = (struct ath11k_base *)ctx; + struct sk_buff *msdu = skb; + + dma_unmap_single(ab->dev, ATH11K_SKB_CB(msdu)->paddr, msdu->len, + DMA_TO_DEVICE); + + dev_kfree_skb_any(msdu); + + return 0; +} + +void ath11k_dp_free(struct ath11k_base *ab) +{ + struct ath11k_dp *dp = &ab->dp; + int i; + + ath11k_dp_link_desc_cleanup(ab, dp->link_desc_banks, + HAL_WBM_IDLE_LINK, &dp->wbm_idle_ring); + + ath11k_dp_srng_common_cleanup(ab); + + ath11k_dp_reo_cmd_list_cleanup(ab); + + for (i = 0; i < DP_TCL_NUM_RING_MAX; i++) { + spin_lock_bh(&dp->tx_ring[i].tx_idr_lock); + idr_for_each(&dp->tx_ring[i].txbuf_idr, + ath11k_dp_tx_pending_cleanup, ab); + idr_destroy(&dp->tx_ring[i].txbuf_idr); + spin_unlock_bh(&dp->tx_ring[i].tx_idr_lock); + + spin_lock_bh(&dp->tx_ring[i].tx_status_lock); + kfifo_free(&dp->tx_ring[i].tx_status_fifo); + spin_unlock_bh(&dp->tx_ring[i].tx_status_lock); + } + + /* Deinit any SOC level resource */ +} + +int ath11k_dp_alloc(struct ath11k_base *ab) +{ + struct ath11k_dp *dp = &ab->dp; + struct hal_srng *srng = NULL; + size_t size = 0; + u32 n_link_desc = 0; + int ret; + int i; + + dp->ab = ab; + + INIT_LIST_HEAD(&dp->reo_cmd_list); + INIT_LIST_HEAD(&dp->reo_cmd_cache_flush_list); + spin_lock_init(&dp->reo_cmd_lock); + + ret = ath11k_wbm_idle_ring_setup(ab, &n_link_desc); + if (ret) { + ath11k_warn(ab, "failed to setup wbm_idle_ring: %d\n", ret); + return ret; + } + + srng = &ab->hal.srng_list[dp->wbm_idle_ring.ring_id]; + + ret = ath11k_dp_link_desc_setup(ab, dp->link_desc_banks, + HAL_WBM_IDLE_LINK, srng, n_link_desc); + if (ret) { + ath11k_warn(ab, "failed to setup link desc: %d\n", ret); + return ret; + } + + ret = ath11k_dp_srng_common_setup(ab); + if (ret) + goto fail_link_desc_cleanup; + + size = roundup_pow_of_two(DP_TX_COMP_RING_SIZE); + + for (i = 0; i < DP_TCL_NUM_RING_MAX; i++) { + idr_init(&dp->tx_ring[i].txbuf_idr); + spin_lock_init(&dp->tx_ring[i].tx_idr_lock); + dp->tx_ring[i].tcl_data_ring_id = i; + + spin_lock_init(&dp->tx_ring[i].tx_status_lock); + ret = kfifo_alloc(&dp->tx_ring[i].tx_status_fifo, size, + GFP_KERNEL); + if (ret) + goto fail_cmn_srng_cleanup; + } + + for (i = 0; i < HAL_DSCP_TID_MAP_TBL_NUM_ENTRIES_MAX; i++) + ath11k_hal_tx_set_dscp_tid_map(ab, i); + + /* Init any SOC level resource for DP */ + + return 0; + +fail_cmn_srng_cleanup: + ath11k_dp_srng_common_cleanup(ab); + +fail_link_desc_cleanup: + ath11k_dp_link_desc_cleanup(ab, dp->link_desc_banks, + HAL_WBM_IDLE_LINK, &dp->wbm_idle_ring); + + return ret; +} diff --git a/drivers/net/wireless/ath/ath11k/dp.h b/drivers/net/wireless/ath/ath11k/dp.h new file mode 100644 index 0000000000000..4db71173db62c --- /dev/null +++ b/drivers/net/wireless/ath/ath11k/dp.h @@ -0,0 +1,1526 @@ +/* SPDX-License-Identifier: BSD-3-Clause-Clear */ +/* + * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. + */ + +#ifndef ATH11K_DP_H +#define ATH11K_DP_H + +#include +#include "hal_rx.h" + +struct ath11k_base; +struct ath11k_peer; +struct ath11k_dp; +struct ath11k_vif; +struct hal_tcl_status_ring; +struct ath11k_ext_irq_grp; + +struct dp_rx_tid { + u8 tid; + u32 *vaddr; + dma_addr_t paddr; + u32 size; + u32 ba_win_sz; + bool active; +}; + +#define DP_REO_DESC_FREE_TIMEOUT_MS 1000 + +struct dp_reo_cache_flush_elem { + struct list_head list; + struct dp_rx_tid data; + unsigned long ts; +}; + +struct dp_reo_cmd { + struct list_head list; + struct dp_rx_tid data; + int cmd_num; + void (*handler)(struct ath11k_dp *, void *, + enum hal_reo_cmd_status status); +}; + +struct dp_srng { + u32 *vaddr_unaligned; + u32 *vaddr; + dma_addr_t paddr_unaligned; + dma_addr_t paddr; + int size; + u32 ring_id; +}; + +struct dp_rxdma_ring { + struct dp_srng refill_buf_ring; + struct idr bufs_idr; + /* Protects bufs_idr */ + spinlock_t idr_lock; + int bufs_max; +}; + +struct dp_tx_ring { + u8 tcl_data_ring_id; + struct dp_srng tcl_data_ring; + struct dp_srng tcl_comp_ring; + struct idr txbuf_idr; + u32 num_tx_pending; + /* Protects txbuf_idr and num_pending */ + spinlock_t tx_idr_lock; + DECLARE_KFIFO_PTR(tx_status_fifo, struct hal_wbm_release_ring); + /* lock to protect tx_status_fifo because tx_status_fifo can be + * accessed concurrently. + */ + spinlock_t tx_status_lock; +}; + +struct ath11k_pdev_mon_stats { + u32 status_ppdu_state; + u32 status_ppdu_start; + u32 status_ppdu_end; + u32 status_ppdu_compl; + u32 status_ppdu_start_mis; + u32 status_ppdu_end_mis; + u32 status_ppdu_done; + u32 dest_ppdu_done; + u32 dest_mpdu_done; + u32 dest_mpdu_drop; + u32 dup_mon_linkdesc_cnt; + u32 dup_mon_buf_cnt; +}; + +struct dp_link_desc_bank { + void *vaddr_unaligned; + void *vaddr; + dma_addr_t paddr_unaligned; + dma_addr_t paddr; + u32 size; +}; + +/* Size to enforce scatter idle list mode */ +#define DP_LINK_DESC_ALLOC_SIZE_THRESH 0x200000 +#define DP_LINK_DESC_BANKS_MAX 8 + +#define DP_RX_DESC_COOKIE_INDEX_MAX 0x3ffff +#define DP_RX_DESC_COOKIE_POOL_ID_MAX 0x1c0000 +#define DP_RX_DESC_COOKIE_MAX \ + (DP_RX_DESC_COOKIE_INDEX_MAX | DP_RX_DESC_COOKIE_POOL_ID_MAX) +#define DP_NOT_PPDU_ID_WRAP_AROUND 20000 + +enum ath11k_dp_ppdu_state { + DP_PPDU_STATUS_START, + DP_PPDU_STATUS_DONE, +}; + +struct ath11k_mon_data { + struct dp_link_desc_bank link_desc_banks[DP_LINK_DESC_BANKS_MAX]; + struct hal_rx_mon_ppdu_info mon_ppdu_info; + + u32 mon_ppdu_status; + u32 mon_last_buf_cookie; + u64 mon_last_linkdesc_paddr; + u16 chan_noise_floor; + + struct ath11k_pdev_mon_stats rx_mon_stats; + /* lock for monitor data */ + spinlock_t mon_lock; + struct sk_buff_head rx_status_q; +}; + +struct ath11k_pdev_dp { + u32 mac_id; + atomic_t num_tx_pending; + wait_queue_head_t tx_empty_waitq; + struct dp_srng reo_dst_ring; + struct dp_rxdma_ring rx_refill_buf_ring; + struct dp_srng rxdma_err_dst_ring; + struct dp_srng rxdma_mon_dst_ring; + struct dp_srng rxdma_mon_desc_ring; + + struct dp_rxdma_ring rxdma_mon_buf_ring; + struct dp_rxdma_ring rx_mon_status_refill_ring; + struct ieee80211_rx_status rx_status; + struct ath11k_mon_data mon_data; +}; + +#define DP_NUM_CLIENTS_MAX 64 +#define DP_AVG_TIDS_PER_CLIENT 2 +#define DP_NUM_TIDS_MAX (DP_NUM_CLIENTS_MAX * DP_AVG_TIDS_PER_CLIENT) +#define DP_AVG_MSDUS_PER_FLOW 128 +#define DP_AVG_FLOWS_PER_TID 2 +#define DP_AVG_MPDUS_PER_TID_MAX 128 +#define DP_AVG_MSDUS_PER_MPDU 4 + +#define DP_RX_HASH_ENABLE 0 /* Disable hash based Rx steering */ + +#define DP_BA_WIN_SZ_MAX 256 + +#define DP_TCL_NUM_RING_MAX 3 + +#define DP_IDLE_SCATTER_BUFS_MAX 16 + +#define DP_WBM_RELEASE_RING_SIZE 64 +#define DP_TCL_DATA_RING_SIZE 512 +#define DP_TX_COMP_RING_SIZE 8192 +#define DP_TX_IDR_SIZE (DP_TX_COMP_RING_SIZE << 1) +#define DP_TCL_CMD_RING_SIZE 32 +#define DP_TCL_STATUS_RING_SIZE 32 +#define DP_REO_DST_RING_MAX 4 +#define DP_REO_DST_RING_SIZE 2048 +#define DP_REO_REINJECT_RING_SIZE 32 +#define DP_RX_RELEASE_RING_SIZE 1024 +#define DP_REO_EXCEPTION_RING_SIZE 128 +#define DP_REO_CMD_RING_SIZE 128 +#define DP_REO_STATUS_RING_SIZE 256 +#define DP_RXDMA_BUF_RING_SIZE 4096 +#define DP_RXDMA_REFILL_RING_SIZE 2048 +#define DP_RXDMA_ERR_DST_RING_SIZE 1024 +#define DP_RXDMA_MON_STATUS_RING_SIZE 1024 +#define DP_RXDMA_MONITOR_BUF_RING_SIZE 4096 +#define DP_RXDMA_MONITOR_DST_RING_SIZE 2048 +#define DP_RXDMA_MONITOR_DESC_RING_SIZE 4096 + +#define DP_RX_BUFFER_SIZE 2048 +#define DP_RX_BUFFER_ALIGN_SIZE 128 + +#define DP_RXDMA_BUF_COOKIE_BUF_ID GENMASK(17, 0) +#define DP_RXDMA_BUF_COOKIE_PDEV_ID GENMASK(20, 18) + +#define DP_HW2SW_MACID(mac_id) ((mac_id) ? ((mac_id) - 1) : 0) +#define DP_SW2HW_MACID(mac_id) ((mac_id) + 1) + +#define DP_TX_DESC_ID_MAC_ID GENMASK(1, 0) +#define DP_TX_DESC_ID_MSDU_ID GENMASK(18, 2) +#define DP_TX_DESC_ID_POOL_ID GENMASK(20, 19) + +struct ath11k_dp { + struct ath11k_base *ab; + enum ath11k_htc_ep_id eid; + struct completion htt_tgt_version_received; + u8 htt_tgt_ver_major; + u8 htt_tgt_ver_minor; + struct dp_link_desc_bank link_desc_banks[DP_LINK_DESC_BANKS_MAX]; + struct dp_srng wbm_idle_ring; + struct dp_srng wbm_desc_rel_ring; + struct dp_srng tcl_cmd_ring; + struct dp_srng tcl_status_ring; + struct dp_srng reo_reinject_ring; + struct dp_srng rx_rel_ring; + struct dp_srng reo_except_ring; + struct dp_srng reo_cmd_ring; + struct dp_srng reo_status_ring; + struct dp_tx_ring tx_ring[DP_TCL_NUM_RING_MAX]; + struct hal_wbm_idle_scatter_list scatter_list[DP_IDLE_SCATTER_BUFS_MAX]; + struct list_head reo_cmd_list; + struct list_head reo_cmd_cache_flush_list; + /* protects access to reo_cmd_list and reo_cmd_cache_flush_list */ + spinlock_t reo_cmd_lock; +}; + +/* HTT definitions */ + +#define HTT_TCL_META_DATA_TYPE BIT(0) +#define HTT_TCL_META_DATA_VALID_HTT BIT(1) + +/* vdev meta data */ +#define HTT_TCL_META_DATA_VDEV_ID GENMASK(9, 2) +#define HTT_TCL_META_DATA_PDEV_ID GENMASK(11, 10) +#define HTT_TCL_META_DATA_HOST_INSPECTED BIT(12) + +/* peer meta data */ +#define HTT_TCL_META_DATA_PEER_ID GENMASK(15, 2) + +#define HTT_TX_WBM_COMP_STATUS_OFFSET 8 + +/* HTT tx completion is overlayed in wbm_release_ring */ +#define HTT_TX_WBM_COMP_INFO0_STATUS GENMASK(12, 9) +#define HTT_TX_WBM_COMP_INFO0_REINJECT_REASON GENMASK(16, 13) +#define HTT_TX_WBM_COMP_INFO0_REINJECT_REASON GENMASK(16, 13) + +#define HTT_TX_WBM_COMP_INFO1_ACK_RSSI GENMASK(31, 24) + +struct htt_tx_wbm_completion { + u32 info0; + u32 info1; + u32 info2; + u32 info3; +} __packed; + +enum htt_h2t_msg_type { + HTT_H2T_MSG_TYPE_VERSION_REQ = 0, + HTT_H2T_MSG_TYPE_SRING_SETUP = 0xb, + HTT_H2T_MSG_TYPE_RX_RING_SELECTION_CFG = 0xc, + HTT_H2T_MSG_TYPE_EXT_STATS_CFG = 0x10, + HTT_H2T_MSG_TYPE_PPDU_STATS_CFG = 0x11, +}; + +#define HTT_VER_REQ_INFO_MSG_ID GENMASK(7, 0) + +struct htt_ver_req_cmd { + u32 ver_reg_info; +} __packed; + +enum htt_srng_ring_type { + HTT_HW_TO_SW_RING, + HTT_SW_TO_HW_RING, + HTT_SW_TO_SW_RING, +}; + +enum htt_srng_ring_id { + HTT_RXDMA_HOST_BUF_RING, + HTT_RXDMA_MONITOR_STATUS_RING, + HTT_RXDMA_MONITOR_BUF_RING, + HTT_RXDMA_MONITOR_DESC_RING, + HTT_RXDMA_MONITOR_DEST_RING, + HTT_HOST1_TO_FW_RXBUF_RING, + HTT_HOST2_TO_FW_RXBUF_RING, + HTT_RXDMA_NON_MONITOR_DEST_RING, +}; + +/* host -> target HTT_SRING_SETUP message + * + * After target is booted up, Host can send SRING setup message for + * each host facing LMAC SRING. Target setups up HW registers based + * on setup message and confirms back to Host if response_required is set. + * Host should wait for confirmation message before sending new SRING + * setup message + * + * The message would appear as follows: + * + * |31 24|23 20|19|18 16|15|14 8|7 0| + * |--------------- +-----------------+----------------+------------------| + * | ring_type | ring_id | pdev_id | msg_type | + * |----------------------------------------------------------------------| + * | ring_base_addr_lo | + * |----------------------------------------------------------------------| + * | ring_base_addr_hi | + * |----------------------------------------------------------------------| + * |ring_misc_cfg_flag|ring_entry_size| ring_size | + * |----------------------------------------------------------------------| + * | ring_head_offset32_remote_addr_lo | + * |----------------------------------------------------------------------| + * | ring_head_offset32_remote_addr_hi | + * |----------------------------------------------------------------------| + * | ring_tail_offset32_remote_addr_lo | + * |----------------------------------------------------------------------| + * | ring_tail_offset32_remote_addr_hi | + * |----------------------------------------------------------------------| + * | ring_msi_addr_lo | + * |----------------------------------------------------------------------| + * | ring_msi_addr_hi | + * |----------------------------------------------------------------------| + * | ring_msi_data | + * |----------------------------------------------------------------------| + * | intr_timer_th |IM| intr_batch_counter_th | + * |----------------------------------------------------------------------| + * | reserved |RR|PTCF| intr_low_threshold | + * |----------------------------------------------------------------------| + * Where + * IM = sw_intr_mode + * RR = response_required + * PTCF = prefetch_timer_cfg + * + * The message is interpreted as follows: + * dword0 - b'0:7 - msg_type: This will be set to + * HTT_H2T_MSG_TYPE_SRING_SETUP + * b'8:15 - pdev_id: + * 0 (for rings at SOC/UMAC level), + * 1/2/3 mac id (for rings at LMAC level) + * b'16:23 - ring_id: identify which ring is to setup, + * more details can be got from enum htt_srng_ring_id + * b'24:31 - ring_type: identify type of host rings, + * more details can be got from enum htt_srng_ring_type + * dword1 - b'0:31 - ring_base_addr_lo: Lower 32bits of ring base address + * dword2 - b'0:31 - ring_base_addr_hi: Upper 32bits of ring base address + * dword3 - b'0:15 - ring_size: size of the ring in unit of 4-bytes words + * b'16:23 - ring_entry_size: Size of each entry in 4-byte word units + * b'24:31 - ring_misc_cfg_flag: Valid only for HW_TO_SW_RING and + * SW_TO_HW_RING. + * Refer to HTT_SRING_SETUP_RING_MISC_CFG_RING defs. + * dword4 - b'0:31 - ring_head_off32_remote_addr_lo: + * Lower 32 bits of memory address of the remote variable + * storing the 4-byte word offset that identifies the head + * element within the ring. + * (The head offset variable has type u32.) + * Valid for HW_TO_SW and SW_TO_SW rings. + * dword5 - b'0:31 - ring_head_off32_remote_addr_hi: + * Upper 32 bits of memory address of the remote variable + * storing the 4-byte word offset that identifies the head + * element within the ring. + * (The head offset variable has type u32.) + * Valid for HW_TO_SW and SW_TO_SW rings. + * dword6 - b'0:31 - ring_tail_off32_remote_addr_lo: + * Lower 32 bits of memory address of the remote variable + * storing the 4-byte word offset that identifies the tail + * element within the ring. + * (The tail offset variable has type u32.) + * Valid for HW_TO_SW and SW_TO_SW rings. + * dword7 - b'0:31 - ring_tail_off32_remote_addr_hi: + * Upper 32 bits of memory address of the remote variable + * storing the 4-byte word offset that identifies the tail + * element within the ring. + * (The tail offset variable has type u32.) + * Valid for HW_TO_SW and SW_TO_SW rings. + * dword8 - b'0:31 - ring_msi_addr_lo: Lower 32bits of MSI cfg address + * valid only for HW_TO_SW_RING and SW_TO_HW_RING + * dword9 - b'0:31 - ring_msi_addr_hi: Upper 32bits of MSI cfg address + * valid only for HW_TO_SW_RING and SW_TO_HW_RING + * dword10 - b'0:31 - ring_msi_data: MSI data + * Refer to HTT_SRING_SETUP_RING_MSC_CFG_xxx defs + * valid only for HW_TO_SW_RING and SW_TO_HW_RING + * dword11 - b'0:14 - intr_batch_counter_th: + * batch counter threshold is in units of 4-byte words. + * HW internally maintains and increments batch count. + * (see SRING spec for detail description). + * When batch count reaches threshold value, an interrupt + * is generated by HW. + * b'15 - sw_intr_mode: + * This configuration shall be static. + * Only programmed at power up. + * 0: generate pulse style sw interrupts + * 1: generate level style sw interrupts + * b'16:31 - intr_timer_th: + * The timer init value when timer is idle or is + * initialized to start downcounting. + * In 8us units (to cover a range of 0 to 524 ms) + * dword12 - b'0:15 - intr_low_threshold: + * Used only by Consumer ring to generate ring_sw_int_p. + * Ring entries low threshold water mark, that is used + * in combination with the interrupt timer as well as + * the the clearing of the level interrupt. + * b'16:18 - prefetch_timer_cfg: + * Used only by Consumer ring to set timer mode to + * support Application prefetch handling. + * The external tail offset/pointer will be updated + * at following intervals: + * 3'b000: (Prefetch feature disabled; used only for debug) + * 3'b001: 1 usec + * 3'b010: 4 usec + * 3'b011: 8 usec (default) + * 3'b100: 16 usec + * Others: Reserverd + * b'19 - response_required: + * Host needs HTT_T2H_MSG_TYPE_SRING_SETUP_DONE as response + * b'20:31 - reserved: reserved for future use + */ + +#define HTT_SRNG_SETUP_CMD_INFO0_MSG_TYPE GENMASK(7, 0) +#define HTT_SRNG_SETUP_CMD_INFO0_PDEV_ID GENMASK(15, 8) +#define HTT_SRNG_SETUP_CMD_INFO0_RING_ID GENMASK(23, 16) +#define HTT_SRNG_SETUP_CMD_INFO0_RING_TYPE GENMASK(31, 24) + +#define HTT_SRNG_SETUP_CMD_INFO1_RING_SIZE GENMASK(15, 0) +#define HTT_SRNG_SETUP_CMD_INFO1_RING_ENTRY_SIZE GENMASK(23, 16) +#define HTT_SRNG_SETUP_CMD_INFO1_RING_LOOP_CNT_DIS BIT(25) +#define HTT_SRNG_SETUP_CMD_INFO1_RING_FLAGS_MSI_SWAP BIT(27) +#define HTT_SRNG_SETUP_CMD_INFO1_RING_FLAGS_HOST_FW_SWAP BIT(28) +#define HTT_SRNG_SETUP_CMD_INFO1_RING_FLAGS_TLV_SWAP BIT(29) + +#define HTT_SRNG_SETUP_CMD_INTR_INFO_BATCH_COUNTER_THRESH GENMASK(14, 0) +#define HTT_SRNG_SETUP_CMD_INTR_INFO_SW_INTR_MODE BIT(15) +#define HTT_SRNG_SETUP_CMD_INTR_INFO_INTR_TIMER_THRESH GENMASK(31, 16) + +#define HTT_SRNG_SETUP_CMD_INFO2_INTR_LOW_THRESH GENMASK(15, 0) +#define HTT_SRNG_SETUP_CMD_INFO2_PRE_FETCH_TIMER_CFG BIT(16) +#define HTT_SRNG_SETUP_CMD_INFO2_RESPONSE_REQUIRED BIT(19) + +struct htt_srng_setup_cmd { + u32 info0; + u32 ring_base_addr_lo; + u32 ring_base_addr_hi; + u32 info1; + u32 ring_head_off32_remote_addr_lo; + u32 ring_head_off32_remote_addr_hi; + u32 ring_tail_off32_remote_addr_lo; + u32 ring_tail_off32_remote_addr_hi; + u32 ring_msi_addr_lo; + u32 ring_msi_addr_hi; + u32 msi_data; + u32 intr_info; + u32 info2; +} __packed; + +/* host -> target FW PPDU_STATS config message + * + * @details + * The following field definitions describe the format of the HTT host + * to target FW for PPDU_STATS_CFG msg. + * The message allows the host to configure the PPDU_STATS_IND messages + * produced by the target. + * + * |31 24|23 16|15 8|7 0| + * |-----------------------------------------------------------| + * | REQ bit mask | pdev_mask | msg type | + * |-----------------------------------------------------------| + * Header fields: + * - MSG_TYPE + * Bits 7:0 + * Purpose: identifies this is a req to configure ppdu_stats_ind from target + * Value: 0x11 + * - PDEV_MASK + * Bits 8:15 + * Purpose: identifies which pdevs this PPDU stats configuration applies to + * Value: This is a overloaded field, refer to usage and interpretation of + * PDEV in interface document. + * Bit 8 : Reserved for SOC stats + * Bit 9 - 15 : Indicates PDEV_MASK in DBDC + * Indicates MACID_MASK in DBS + * - REQ_TLV_BIT_MASK + * Bits 16:31 + * Purpose: each set bit indicates the corresponding PPDU stats TLV type + * needs to be included in the target's PPDU_STATS_IND messages. + * Value: refer htt_ppdu_stats_tlv_tag_t << 30 bits + * Refer to PKT_TYPE_ENABLE_FLAG0_xxx_MGMT_xxx defs + * dword3 - b'0:31 - packet_type_enable_flags_1: + * Enable MGMT packet from 0b1010 to 0b1111 + * bits from low to high: FP, MD, MO - 3 bits + * Refer to PKT_TYPE_ENABLE_FLAG1_xxx_MGMT_xxx defs + * dword4 - b'0:31 - packet_type_enable_flags_2: + * Enable CTRL packet from 0b0000 to 0b1001 + * bits from low to high: FP, MD, MO - 3 bits + * Refer to PKT_TYPE_ENABLE_FLAG2_xxx_CTRL_xxx defs + * dword5 - b'0:31 - packet_type_enable_flags_3: + * Enable CTRL packet from 0b1010 to 0b1111, + * MCAST_DATA, UCAST_DATA, NULL_DATA + * bits from low to high: FP, MD, MO - 3 bits + * Refer to PKT_TYPE_ENABLE_FLAG3_xxx_CTRL_xxx defs + * dword6 - b'0:31 - tlv_filter_in_flags: + * Filter in Attention/MPDU/PPDU/Header/User tlvs + * Refer to CFG_TLV_FILTER_IN_FLAG defs + */ + +#define HTT_RX_RING_SELECTION_CFG_CMD_INFO0_MSG_TYPE GENMASK(7, 0) +#define HTT_RX_RING_SELECTION_CFG_CMD_INFO0_PDEV_ID GENMASK(15, 8) +#define HTT_RX_RING_SELECTION_CFG_CMD_INFO0_RING_ID GENMASK(23, 16) +#define HTT_RX_RING_SELECTION_CFG_CMD_INFO0_SS BIT(24) +#define HTT_RX_RING_SELECTION_CFG_CMD_INFO0_PS BIT(25) + +#define HTT_RX_RING_SELECTION_CFG_CMD_INFO1_BUF_SIZE GENMASK(15, 0) + +enum htt_rx_filter_tlv_flags { + HTT_RX_FILTER_TLV_FLAGS_MPDU_START = BIT(0), + HTT_RX_FILTER_TLV_FLAGS_MSDU_START = BIT(1), + HTT_RX_FILTER_TLV_FLAGS_RX_PACKET = BIT(2), + HTT_RX_FILTER_TLV_FLAGS_MSDU_END = BIT(3), + HTT_RX_FILTER_TLV_FLAGS_MPDU_END = BIT(4), + HTT_RX_FILTER_TLV_FLAGS_PACKET_HEADER = BIT(5), + HTT_RX_FILTER_TLV_FLAGS_PER_MSDU_HEADER = BIT(6), + HTT_RX_FILTER_TLV_FLAGS_ATTENTION = BIT(7), + HTT_RX_FILTER_TLV_FLAGS_PPDU_START = BIT(8), + HTT_RX_FILTER_TLV_FLAGS_PPDU_END = BIT(9), + HTT_RX_FILTER_TLV_FLAGS_PPDU_END_USER_STATS = BIT(10), + HTT_RX_FILTER_TLV_FLAGS_PPDU_END_USER_STATS_EXT = BIT(11), + HTT_RX_FILTER_TLV_FLAGS_PPDU_END_STATUS_DONE = BIT(12), +}; + +enum htt_rx_mgmt_pkt_filter_tlv_flags0 { + HTT_RX_FP_MGMT_PKT_FILTER_TLV_FLAGS0_ASSOC_REQ = BIT(0), + HTT_RX_MD_MGMT_PKT_FILTER_TLV_FLAGS0_ASSOC_REQ = BIT(1), + HTT_RX_MO_MGMT_PKT_FILTER_TLV_FLAGS0_ASSOC_REQ = BIT(2), + HTT_RX_FP_MGMT_PKT_FILTER_TLV_FLAGS0_ASSOC_RESP = BIT(3), + HTT_RX_MD_MGMT_PKT_FILTER_TLV_FLAGS0_ASSOC_RESP = BIT(4), + HTT_RX_MO_MGMT_PKT_FILTER_TLV_FLAGS0_ASSOC_RESP = BIT(5), + HTT_RX_FP_MGMT_PKT_FILTER_TLV_FLAGS0_REASSOC_REQ = BIT(6), + HTT_RX_MD_MGMT_PKT_FILTER_TLV_FLAGS0_REASSOC_REQ = BIT(7), + HTT_RX_MO_MGMT_PKT_FILTER_TLV_FLAGS0_REASSOC_REQ = BIT(8), + HTT_RX_FP_MGMT_PKT_FILTER_TLV_FLAGS0_REASSOC_RESP = BIT(9), + HTT_RX_MD_MGMT_PKT_FILTER_TLV_FLAGS0_REASSOC_RESP = BIT(10), + HTT_RX_MO_MGMT_PKT_FILTER_TLV_FLAGS0_REASSOC_RESP = BIT(11), + HTT_RX_FP_MGMT_PKT_FILTER_TLV_FLAGS0_PROBE_REQ = BIT(12), + HTT_RX_MD_MGMT_PKT_FILTER_TLV_FLAGS0_PROBE_REQ = BIT(13), + HTT_RX_MO_MGMT_PKT_FILTER_TLV_FLAGS0_PROBE_REQ = BIT(14), + HTT_RX_FP_MGMT_PKT_FILTER_TLV_FLAGS0_PROBE_RESP = BIT(15), + HTT_RX_MD_MGMT_PKT_FILTER_TLV_FLAGS0_PROBE_RESP = BIT(16), + HTT_RX_MO_MGMT_PKT_FILTER_TLV_FLAGS0_PROBE_RESP = BIT(17), + HTT_RX_FP_MGMT_PKT_FILTER_TLV_FLAGS0_PROBE_TIMING_ADV = BIT(18), + HTT_RX_MD_MGMT_PKT_FILTER_TLV_FLAGS0_PROBE_TIMING_ADV = BIT(19), + HTT_RX_MO_MGMT_PKT_FILTER_TLV_FLAGS0_PROBE_TIMING_ADV = BIT(20), + HTT_RX_FP_MGMT_PKT_FILTER_TLV_FLAGS0_RESERVED_7 = BIT(21), + HTT_RX_MD_MGMT_PKT_FILTER_TLV_FLAGS0_RESERVED_7 = BIT(22), + HTT_RX_MO_MGMT_PKT_FILTER_TLV_FLAGS0_RESERVED_7 = BIT(23), + HTT_RX_FP_MGMT_PKT_FILTER_TLV_FLAGS0_BEACON = BIT(24), + HTT_RX_MD_MGMT_PKT_FILTER_TLV_FLAGS0_BEACON = BIT(25), + HTT_RX_MO_MGMT_PKT_FILTER_TLV_FLAGS0_BEACON = BIT(26), + HTT_RX_FP_MGMT_PKT_FILTER_TLV_FLAGS0_ATIM = BIT(27), + HTT_RX_MD_MGMT_PKT_FILTER_TLV_FLAGS0_ATIM = BIT(28), + HTT_RX_MO_MGMT_PKT_FILTER_TLV_FLAGS0_ATIM = BIT(29), +}; + +enum htt_rx_mgmt_pkt_filter_tlv_flags1 { + HTT_RX_FP_MGMT_PKT_FILTER_TLV_FLAGS1_DISASSOC = BIT(0), + HTT_RX_MD_MGMT_PKT_FILTER_TLV_FLAGS1_DISASSOC = BIT(1), + HTT_RX_MO_MGMT_PKT_FILTER_TLV_FLAGS1_DISASSOC = BIT(2), + HTT_RX_FP_MGMT_PKT_FILTER_TLV_FLAGS1_AUTH = BIT(3), + HTT_RX_MD_MGMT_PKT_FILTER_TLV_FLAGS1_AUTH = BIT(4), + HTT_RX_MO_MGMT_PKT_FILTER_TLV_FLAGS1_AUTH = BIT(5), + HTT_RX_FP_MGMT_PKT_FILTER_TLV_FLAGS1_DEAUTH = BIT(6), + HTT_RX_MD_MGMT_PKT_FILTER_TLV_FLAGS1_DEAUTH = BIT(7), + HTT_RX_MO_MGMT_PKT_FILTER_TLV_FLAGS1_DEAUTH = BIT(8), + HTT_RX_FP_MGMT_PKT_FILTER_TLV_FLAGS1_ACTION = BIT(9), + HTT_RX_MD_MGMT_PKT_FILTER_TLV_FLAGS1_ACTION = BIT(10), + HTT_RX_MO_MGMT_PKT_FILTER_TLV_FLAGS1_ACTION = BIT(11), + HTT_RX_FP_MGMT_PKT_FILTER_TLV_FLAGS1_ACTION_NOACK = BIT(12), + HTT_RX_MD_MGMT_PKT_FILTER_TLV_FLAGS1_ACTION_NOACK = BIT(13), + HTT_RX_MO_MGMT_PKT_FILTER_TLV_FLAGS1_ACTION_NOACK = BIT(14), + HTT_RX_FP_MGMT_PKT_FILTER_TLV_FLAGS1_RESERVED_15 = BIT(15), + HTT_RX_MD_MGMT_PKT_FILTER_TLV_FLAGS1_RESERVED_15 = BIT(16), + HTT_RX_MO_MGMT_PKT_FILTER_TLV_FLAGS1_RESERVED_15 = BIT(17), +}; + +enum htt_rx_ctrl_pkt_filter_tlv_flags2 { + HTT_RX_FP_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_RESERVED_1 = BIT(0), + HTT_RX_MD_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_RESERVED_1 = BIT(1), + HTT_RX_MO_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_RESERVED_1 = BIT(2), + HTT_RX_FP_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_RESERVED_2 = BIT(3), + HTT_RX_MD_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_RESERVED_2 = BIT(4), + HTT_RX_MO_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_RESERVED_2 = BIT(5), + HTT_RX_FP_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_TRIGGER = BIT(6), + HTT_RX_MD_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_TRIGGER = BIT(7), + HTT_RX_MO_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_TRIGGER = BIT(8), + HTT_RX_FP_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_RESERVED_4 = BIT(9), + HTT_RX_MD_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_RESERVED_4 = BIT(10), + HTT_RX_MO_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_RESERVED_4 = BIT(11), + HTT_RX_FP_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_BF_REP_POLL = BIT(12), + HTT_RX_MD_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_BF_REP_POLL = BIT(13), + HTT_RX_MO_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_BF_REP_POLL = BIT(14), + HTT_RX_FP_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_VHT_NDP = BIT(15), + HTT_RX_MD_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_VHT_NDP = BIT(16), + HTT_RX_MO_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_VHT_NDP = BIT(17), + HTT_RX_FP_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_FRAME_EXT = BIT(18), + HTT_RX_MD_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_FRAME_EXT = BIT(19), + HTT_RX_MO_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_FRAME_EXT = BIT(20), + HTT_RX_FP_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_WRAPPER = BIT(21), + HTT_RX_MD_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_WRAPPER = BIT(22), + HTT_RX_MO_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_WRAPPER = BIT(23), + HTT_RX_FP_CTRL_PKT_FILTER_TLV_FLAGS2_BAR = BIT(24), + HTT_RX_MD_CTRL_PKT_FILTER_TLV_FLAGS2_BAR = BIT(25), + HTT_RX_MO_CTRL_PKT_FILTER_TLV_FLAGS2_BAR = BIT(26), + HTT_RX_FP_CTRL_PKT_FILTER_TLV_FLAGS2_BA = BIT(27), + HTT_RX_MD_CTRL_PKT_FILTER_TLV_FLAGS2_BA = BIT(28), + HTT_RX_MO_CTRL_PKT_FILTER_TLV_FLAGS2_BA = BIT(29), +}; + +enum htt_rx_ctrl_pkt_filter_tlv_flags3 { + HTT_RX_FP_CTRL_PKT_FILTER_TLV_FLAGS3_PSPOLL = BIT(0), + HTT_RX_MD_CTRL_PKT_FILTER_TLV_FLAGS3_PSPOLL = BIT(1), + HTT_RX_MO_CTRL_PKT_FILTER_TLV_FLAGS3_PSPOLL = BIT(2), + HTT_RX_FP_CTRL_PKT_FILTER_TLV_FLAGS3_RTS = BIT(3), + HTT_RX_MD_CTRL_PKT_FILTER_TLV_FLAGS3_RTS = BIT(4), + HTT_RX_MO_CTRL_PKT_FILTER_TLV_FLAGS3_RTS = BIT(5), + HTT_RX_FP_CTRL_PKT_FILTER_TLV_FLAGS3_CTS = BIT(6), + HTT_RX_MD_CTRL_PKT_FILTER_TLV_FLAGS3_CTS = BIT(7), + HTT_RX_MO_CTRL_PKT_FILTER_TLV_FLAGS3_CTS = BIT(8), + HTT_RX_FP_CTRL_PKT_FILTER_TLV_FLAGS3_ACK = BIT(9), + HTT_RX_MD_CTRL_PKT_FILTER_TLV_FLAGS3_ACK = BIT(10), + HTT_RX_MO_CTRL_PKT_FILTER_TLV_FLAGS3_ACK = BIT(11), + HTT_RX_FP_CTRL_PKT_FILTER_TLV_FLAGS3_CFEND = BIT(12), + HTT_RX_MD_CTRL_PKT_FILTER_TLV_FLAGS3_CFEND = BIT(13), + HTT_RX_MO_CTRL_PKT_FILTER_TLV_FLAGS3_CFEND = BIT(14), + HTT_RX_FP_CTRL_PKT_FILTER_TLV_FLAGS3_CFEND_ACK = BIT(15), + HTT_RX_MD_CTRL_PKT_FILTER_TLV_FLAGS3_CFEND_ACK = BIT(16), + HTT_RX_MO_CTRL_PKT_FILTER_TLV_FLAGS3_CFEND_ACK = BIT(17), +}; + +enum htt_rx_data_pkt_filter_tlv_flasg3 { + HTT_RX_FP_DATA_PKT_FILTER_TLV_FLASG3_MCAST = BIT(18), + HTT_RX_MD_DATA_PKT_FILTER_TLV_FLASG3_MCAST = BIT(19), + HTT_RX_MO_DATA_PKT_FILTER_TLV_FLASG3_MCAST = BIT(20), + HTT_RX_FP_DATA_PKT_FILTER_TLV_FLASG3_UCAST = BIT(21), + HTT_RX_MD_DATA_PKT_FILTER_TLV_FLASG3_UCAST = BIT(22), + HTT_RX_MO_DATA_PKT_FILTER_TLV_FLASG3_UCAST = BIT(23), + HTT_RX_FP_DATA_PKT_FILTER_TLV_FLASG3_NULL_DATA = BIT(24), + HTT_RX_MD_DATA_PKT_FILTER_TLV_FLASG3_NULL_DATA = BIT(25), + HTT_RX_MO_DATA_PKT_FILTER_TLV_FLASG3_NULL_DATA = BIT(26), +}; + +#define HTT_RX_FP_MGMT_FILTER_FLAGS0 \ + (HTT_RX_FP_MGMT_PKT_FILTER_TLV_FLAGS0_ASSOC_REQ \ + | HTT_RX_FP_MGMT_PKT_FILTER_TLV_FLAGS0_ASSOC_RESP \ + | HTT_RX_FP_MGMT_PKT_FILTER_TLV_FLAGS0_REASSOC_REQ \ + | HTT_RX_FP_MGMT_PKT_FILTER_TLV_FLAGS0_REASSOC_RESP \ + | HTT_RX_FP_MGMT_PKT_FILTER_TLV_FLAGS0_PROBE_REQ \ + | HTT_RX_FP_MGMT_PKT_FILTER_TLV_FLAGS0_PROBE_RESP \ + | HTT_RX_FP_MGMT_PKT_FILTER_TLV_FLAGS0_PROBE_TIMING_ADV \ + | HTT_RX_FP_MGMT_PKT_FILTER_TLV_FLAGS0_BEACON \ + | HTT_RX_FP_MGMT_PKT_FILTER_TLV_FLAGS0_ATIM) + +#define HTT_RX_MD_MGMT_FILTER_FLAGS0 \ + (HTT_RX_MD_MGMT_PKT_FILTER_TLV_FLAGS0_ASSOC_REQ \ + | HTT_RX_MD_MGMT_PKT_FILTER_TLV_FLAGS0_ASSOC_RESP \ + | HTT_RX_MD_MGMT_PKT_FILTER_TLV_FLAGS0_REASSOC_REQ \ + | HTT_RX_MD_MGMT_PKT_FILTER_TLV_FLAGS0_REASSOC_RESP \ + | HTT_RX_MD_MGMT_PKT_FILTER_TLV_FLAGS0_PROBE_REQ \ + | HTT_RX_MD_MGMT_PKT_FILTER_TLV_FLAGS0_PROBE_RESP \ + | HTT_RX_MD_MGMT_PKT_FILTER_TLV_FLAGS0_PROBE_TIMING_ADV \ + | HTT_RX_MD_MGMT_PKT_FILTER_TLV_FLAGS0_BEACON \ + | HTT_RX_MD_MGMT_PKT_FILTER_TLV_FLAGS0_ATIM) + +#define HTT_RX_MO_MGMT_FILTER_FLAGS0 \ + (HTT_RX_MO_MGMT_PKT_FILTER_TLV_FLAGS0_ASSOC_REQ \ + | HTT_RX_MO_MGMT_PKT_FILTER_TLV_FLAGS0_ASSOC_RESP \ + | HTT_RX_MO_MGMT_PKT_FILTER_TLV_FLAGS0_REASSOC_REQ \ + | HTT_RX_MO_MGMT_PKT_FILTER_TLV_FLAGS0_REASSOC_RESP \ + | HTT_RX_MO_MGMT_PKT_FILTER_TLV_FLAGS0_PROBE_REQ \ + | HTT_RX_MO_MGMT_PKT_FILTER_TLV_FLAGS0_PROBE_RESP \ + | HTT_RX_MO_MGMT_PKT_FILTER_TLV_FLAGS0_PROBE_TIMING_ADV \ + | HTT_RX_MO_MGMT_PKT_FILTER_TLV_FLAGS0_BEACON \ + | HTT_RX_MO_MGMT_PKT_FILTER_TLV_FLAGS0_ATIM) + +#define HTT_RX_FP_MGMT_FILTER_FLAGS1 (HTT_RX_FP_MGMT_PKT_FILTER_TLV_FLAGS1_DISASSOC \ + | HTT_RX_FP_MGMT_PKT_FILTER_TLV_FLAGS1_AUTH \ + | HTT_RX_FP_MGMT_PKT_FILTER_TLV_FLAGS1_DEAUTH \ + | HTT_RX_FP_MGMT_PKT_FILTER_TLV_FLAGS1_ACTION \ + | HTT_RX_FP_MGMT_PKT_FILTER_TLV_FLAGS1_ACTION_NOACK) + +#define HTT_RX_MD_MGMT_FILTER_FLAGS1 (HTT_RX_MD_MGMT_PKT_FILTER_TLV_FLAGS1_DISASSOC \ + | HTT_RX_MD_MGMT_PKT_FILTER_TLV_FLAGS1_AUTH \ + | HTT_RX_MD_MGMT_PKT_FILTER_TLV_FLAGS1_DEAUTH \ + | HTT_RX_MD_MGMT_PKT_FILTER_TLV_FLAGS1_ACTION \ + | HTT_RX_MD_MGMT_PKT_FILTER_TLV_FLAGS1_ACTION_NOACK) + +#define HTT_RX_MO_MGMT_FILTER_FLAGS1 (HTT_RX_MO_MGMT_PKT_FILTER_TLV_FLAGS1_DISASSOC \ + | HTT_RX_MO_MGMT_PKT_FILTER_TLV_FLAGS1_AUTH \ + | HTT_RX_MO_MGMT_PKT_FILTER_TLV_FLAGS1_DEAUTH \ + | HTT_RX_MO_MGMT_PKT_FILTER_TLV_FLAGS1_ACTION \ + | HTT_RX_MO_MGMT_PKT_FILTER_TLV_FLAGS1_ACTION_NOACK) + +#define HTT_RX_FP_CTRL_FILTER_FLASG2 (HTT_RX_FP_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_WRAPPER \ + | HTT_RX_FP_CTRL_PKT_FILTER_TLV_FLAGS2_BAR \ + | HTT_RX_FP_CTRL_PKT_FILTER_TLV_FLAGS2_BA) + +#define HTT_RX_MD_CTRL_FILTER_FLASG2 (HTT_RX_MD_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_WRAPPER \ + | HTT_RX_MD_CTRL_PKT_FILTER_TLV_FLAGS2_BAR \ + | HTT_RX_MD_CTRL_PKT_FILTER_TLV_FLAGS2_BA) + +#define HTT_RX_MO_CTRL_FILTER_FLASG2 (HTT_RX_MO_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_WRAPPER \ + | HTT_RX_MO_CTRL_PKT_FILTER_TLV_FLAGS2_BAR \ + | HTT_RX_MO_CTRL_PKT_FILTER_TLV_FLAGS2_BA) + +#define HTT_RX_FP_CTRL_FILTER_FLASG3 (HTT_RX_FP_CTRL_PKT_FILTER_TLV_FLAGS3_PSPOLL \ + | HTT_RX_FP_CTRL_PKT_FILTER_TLV_FLAGS3_RTS \ + | HTT_RX_FP_CTRL_PKT_FILTER_TLV_FLAGS3_CTS \ + | HTT_RX_FP_CTRL_PKT_FILTER_TLV_FLAGS3_ACK \ + | HTT_RX_FP_CTRL_PKT_FILTER_TLV_FLAGS3_CFEND \ + | HTT_RX_FP_CTRL_PKT_FILTER_TLV_FLAGS3_CFEND_ACK) + +#define HTT_RX_MD_CTRL_FILTER_FLASG3 (HTT_RX_MD_CTRL_PKT_FILTER_TLV_FLAGS3_PSPOLL \ + | HTT_RX_MD_CTRL_PKT_FILTER_TLV_FLAGS3_RTS \ + | HTT_RX_MD_CTRL_PKT_FILTER_TLV_FLAGS3_CTS \ + | HTT_RX_MD_CTRL_PKT_FILTER_TLV_FLAGS3_ACK \ + | HTT_RX_MD_CTRL_PKT_FILTER_TLV_FLAGS3_CFEND \ + | HTT_RX_MD_CTRL_PKT_FILTER_TLV_FLAGS3_CFEND_ACK) + +#define HTT_RX_MO_CTRL_FILTER_FLASG3 (HTT_RX_MO_CTRL_PKT_FILTER_TLV_FLAGS3_PSPOLL \ + | HTT_RX_MO_CTRL_PKT_FILTER_TLV_FLAGS3_RTS \ + | HTT_RX_MO_CTRL_PKT_FILTER_TLV_FLAGS3_CTS \ + | HTT_RX_MO_CTRL_PKT_FILTER_TLV_FLAGS3_ACK \ + | HTT_RX_MO_CTRL_PKT_FILTER_TLV_FLAGS3_CFEND \ + | HTT_RX_MO_CTRL_PKT_FILTER_TLV_FLAGS3_CFEND_ACK) + +#define HTT_RX_FP_DATA_FILTER_FLASG3 (HTT_RX_FP_DATA_PKT_FILTER_TLV_FLASG3_MCAST \ + | HTT_RX_FP_DATA_PKT_FILTER_TLV_FLASG3_UCAST \ + | HTT_RX_FP_DATA_PKT_FILTER_TLV_FLASG3_NULL_DATA) + +#define HTT_RX_MD_DATA_FILTER_FLASG3 (HTT_RX_MD_DATA_PKT_FILTER_TLV_FLASG3_MCAST \ + | HTT_RX_MD_DATA_PKT_FILTER_TLV_FLASG3_UCAST \ + | HTT_RX_MD_DATA_PKT_FILTER_TLV_FLASG3_NULL_DATA) + +#define HTT_RX_MO_DATA_FILTER_FLASG3 (HTT_RX_MO_DATA_PKT_FILTER_TLV_FLASG3_MCAST \ + | HTT_RX_MO_DATA_PKT_FILTER_TLV_FLASG3_UCAST \ + | HTT_RX_MO_DATA_PKT_FILTER_TLV_FLASG3_NULL_DATA) + +#define HTT_RX_MON_FP_MGMT_FILTER_FLAGS0 \ + (HTT_RX_FP_MGMT_FILTER_FLAGS0 | \ + HTT_RX_FP_MGMT_PKT_FILTER_TLV_FLAGS0_RESERVED_7) + +#define HTT_RX_MON_MO_MGMT_FILTER_FLAGS0 \ + (HTT_RX_MO_MGMT_FILTER_FLAGS0 | \ + HTT_RX_MO_MGMT_PKT_FILTER_TLV_FLAGS0_RESERVED_7) + +#define HTT_RX_MON_FP_MGMT_FILTER_FLAGS1 \ + (HTT_RX_FP_MGMT_FILTER_FLAGS1 | \ + HTT_RX_FP_MGMT_PKT_FILTER_TLV_FLAGS1_RESERVED_15) + +#define HTT_RX_MON_MO_MGMT_FILTER_FLAGS1 \ + (HTT_RX_MO_MGMT_FILTER_FLAGS1 | \ + HTT_RX_MO_MGMT_PKT_FILTER_TLV_FLAGS1_RESERVED_15) + +#define HTT_RX_MON_FP_CTRL_FILTER_FLASG2 \ + (HTT_RX_FP_CTRL_FILTER_FLASG2 | \ + HTT_RX_FP_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_RESERVED_1 | \ + HTT_RX_FP_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_RESERVED_2 | \ + HTT_RX_FP_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_TRIGGER | \ + HTT_RX_FP_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_RESERVED_4 | \ + HTT_RX_FP_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_BF_REP_POLL | \ + HTT_RX_FP_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_VHT_NDP | \ + HTT_RX_FP_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_FRAME_EXT) + +#define HTT_RX_MON_MO_CTRL_FILTER_FLASG2 \ + (HTT_RX_MO_CTRL_FILTER_FLASG2 | \ + HTT_RX_MO_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_RESERVED_1 | \ + HTT_RX_MO_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_RESERVED_2 | \ + HTT_RX_MO_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_TRIGGER | \ + HTT_RX_MO_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_RESERVED_4 | \ + HTT_RX_MO_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_BF_REP_POLL | \ + HTT_RX_MO_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_VHT_NDP | \ + HTT_RX_MO_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_FRAME_EXT) + +#define HTT_RX_MON_FP_CTRL_FILTER_FLASG3 HTT_RX_FP_CTRL_FILTER_FLASG3 + +#define HTT_RX_MON_MO_CTRL_FILTER_FLASG3 HTT_RX_MO_CTRL_FILTER_FLASG3 + +#define HTT_RX_MON_FP_DATA_FILTER_FLASG3 HTT_RX_FP_DATA_FILTER_FLASG3 + +#define HTT_RX_MON_MO_DATA_FILTER_FLASG3 HTT_RX_MO_DATA_FILTER_FLASG3 + +#define HTT_RX_MON_FILTER_TLV_FLAGS \ + (HTT_RX_FILTER_TLV_FLAGS_MPDU_START | \ + HTT_RX_FILTER_TLV_FLAGS_PPDU_START | \ + HTT_RX_FILTER_TLV_FLAGS_PPDU_END | \ + HTT_RX_FILTER_TLV_FLAGS_PPDU_END_USER_STATS | \ + HTT_RX_FILTER_TLV_FLAGS_PPDU_END_USER_STATS_EXT | \ + HTT_RX_FILTER_TLV_FLAGS_PPDU_END_STATUS_DONE) + +#define HTT_RX_MON_FILTER_TLV_FLAGS_MON_STATUS_RING \ + (HTT_RX_FILTER_TLV_FLAGS_MPDU_START | \ + HTT_RX_FILTER_TLV_FLAGS_PPDU_START | \ + HTT_RX_FILTER_TLV_FLAGS_PPDU_END | \ + HTT_RX_FILTER_TLV_FLAGS_PPDU_END_USER_STATS | \ + HTT_RX_FILTER_TLV_FLAGS_PPDU_END_USER_STATS_EXT | \ + HTT_RX_FILTER_TLV_FLAGS_PPDU_END_STATUS_DONE) + +#define HTT_RX_MON_FILTER_TLV_FLAGS_MON_BUF_RING \ + (HTT_RX_FILTER_TLV_FLAGS_MPDU_START | \ + HTT_RX_FILTER_TLV_FLAGS_MSDU_START | \ + HTT_RX_FILTER_TLV_FLAGS_RX_PACKET | \ + HTT_RX_FILTER_TLV_FLAGS_MSDU_END | \ + HTT_RX_FILTER_TLV_FLAGS_MPDU_END | \ + HTT_RX_FILTER_TLV_FLAGS_PACKET_HEADER | \ + HTT_RX_FILTER_TLV_FLAGS_PER_MSDU_HEADER | \ + HTT_RX_FILTER_TLV_FLAGS_ATTENTION) + +struct htt_rx_ring_selection_cfg_cmd { + u32 info0; + u32 info1; + u32 pkt_type_en_flags0; + u32 pkt_type_en_flags1; + u32 pkt_type_en_flags2; + u32 pkt_type_en_flags3; + u32 rx_filter_tlv; +} __packed; + +struct htt_rx_ring_tlv_filter { + u32 rx_filter; /* see htt_rx_filter_tlv_flags */ + u32 pkt_filter_flags0; /* MGMT */ + u32 pkt_filter_flags1; /* MGMT */ + u32 pkt_filter_flags2; /* CTRL */ + u32 pkt_filter_flags3; /* DATA */ +}; + +/* HTT message target->host */ + +enum htt_t2h_msg_type { + HTT_T2H_MSG_TYPE_VERSION_CONF, + HTT_T2H_MSG_TYPE_RX_ADDBA = 0x5, + HTT_T2H_MSG_TYPE_PKTLOG = 0x8, + HTT_T2H_MSG_TYPE_SEC_IND = 0xb, + HTT_T2H_MSG_TYPE_PEER_MAP = 0x1e, + HTT_T2H_MSG_TYPE_PEER_UNMAP = 0x1f, + HTT_T2H_MSG_TYPE_PPDU_STATS_IND = 0x1d, + HTT_T2H_MSG_TYPE_EXT_STATS_CONF = 0x1c, +}; + +#define HTT_TARGET_VERSION_MAJOR 3 + +#define HTT_T2H_MSG_TYPE GENMASK(7, 0) +#define HTT_T2H_VERSION_CONF_MINOR GENMASK(15, 8) +#define HTT_T2H_VERSION_CONF_MAJOR GENMASK(23, 16) + +struct htt_t2h_version_conf_msg { + u32 version; +} __packed; + +#define HTT_T2H_PEER_MAP_INFO_VDEV_ID GENMASK(15, 8) +#define HTT_T2H_PEER_MAP_INFO_PEER_ID GENMASK(31, 16) +#define HTT_T2H_PEER_MAP_INFO1_MAC_ADDR_H16 GENMASK(15, 0) +#define HTT_T2H_PEER_MAP_INFO1_HW_PEER_ID GENMASK(31, 16) +#define HTT_T2H_PEER_MAP_INFO2_AST_HASH_VAL GENMASK(15, 0) +#define HTT_T2H_PEER_MAP_INFO2_NEXT_HOP_M BIT(16) +#define HTT_T2H_PEER_MAP_INFO2_NEXT_HOP_S 16 + +struct htt_t2h_peer_map_event { + u32 info; + u32 mac_addr_l32; + u32 info1; + u32 info2; +} __packed; + +#define HTT_T2H_PEER_UNMAP_INFO_VDEV_ID HTT_T2H_PEER_MAP_INFO_VDEV_ID +#define HTT_T2H_PEER_UNMAP_INFO_PEER_ID HTT_T2H_PEER_MAP_INFO_PEER_ID +#define HTT_T2H_PEER_UNMAP_INFO1_MAC_ADDR_H16 \ + HTT_T2H_PEER_MAP_INFO1_MAC_ADDR_H16 +#define HTT_T2H_PEER_MAP_INFO1_NEXT_HOP_M HTT_T2H_PEER_MAP_INFO2_NEXT_HOP_M +#define HTT_T2H_PEER_MAP_INFO1_NEXT_HOP_S HTT_T2H_PEER_MAP_INFO2_NEXT_HOP_S + +struct htt_t2h_peer_unmap_event { + u32 info; + u32 mac_addr_l32; + u32 info1; +} __packed; + +struct htt_resp_msg { + union { + struct htt_t2h_version_conf_msg version_msg; + struct htt_t2h_peer_map_event peer_map_ev; + struct htt_t2h_peer_unmap_event peer_unmap_ev; + }; +} __packed; + +/* ppdu stats + * + * @details + * The following field definitions describe the format of the HTT target + * to host ppdu stats indication message. + * + * + * |31 16|15 12|11 10|9 8|7 0 | + * |----------------------------------------------------------------------| + * | payload_size | rsvd |pdev_id|mac_id | msg type | + * |----------------------------------------------------------------------| + * | ppdu_id | + * |----------------------------------------------------------------------| + * | Timestamp in us | + * |----------------------------------------------------------------------| + * | reserved | + * |----------------------------------------------------------------------| + * | type-specific stats info | + * | (see htt_ppdu_stats.h) | + * |----------------------------------------------------------------------| + * Header fields: + * - MSG_TYPE + * Bits 7:0 + * Purpose: Identifies this is a PPDU STATS indication + * message. + * Value: 0x1d + * - mac_id + * Bits 9:8 + * Purpose: mac_id of this ppdu_id + * Value: 0-3 + * - pdev_id + * Bits 11:10 + * Purpose: pdev_id of this ppdu_id + * Value: 0-3 + * 0 (for rings at SOC level), + * 1/2/3 PDEV -> 0/1/2 + * - payload_size + * Bits 31:16 + * Purpose: total tlv size + * Value: payload_size in bytes + */ + +#define HTT_T2H_PPDU_STATS_INFO_PDEV_ID GENMASK(11, 10) +#define HTT_T2H_PPDU_STATS_INFO_PAYLOAD_SIZE GENMASK(31, 16) + +struct ath11k_htt_ppdu_stats_msg { + u32 info; + u32 ppdu_id; + u32 timestamp; + u32 rsvd; + u8 data[0]; +} __packed; + +struct htt_tlv { + u32 header; + u8 value[0]; +} __packed; + +#define HTT_TLV_TAG GENMASK(11, 0) +#define HTT_TLV_LEN GENMASK(23, 12) + +enum HTT_PPDU_STATS_BW { + HTT_PPDU_STATS_BANDWIDTH_5MHZ = 0, + HTT_PPDU_STATS_BANDWIDTH_10MHZ = 1, + HTT_PPDU_STATS_BANDWIDTH_20MHZ = 2, + HTT_PPDU_STATS_BANDWIDTH_40MHZ = 3, + HTT_PPDU_STATS_BANDWIDTH_80MHZ = 4, + HTT_PPDU_STATS_BANDWIDTH_160MHZ = 5, /* includes 80+80 */ + HTT_PPDU_STATS_BANDWIDTH_DYN = 6, +}; + +#define HTT_PPDU_STATS_CMN_FLAGS_FRAME_TYPE_M GENMASK(7, 0) +#define HTT_PPDU_STATS_CMN_FLAGS_QUEUE_TYPE_M GENMASK(15, 8) +/* bw - HTT_PPDU_STATS_BW */ +#define HTT_PPDU_STATS_CMN_FLAGS_BW_M GENMASK(19, 16) + +struct htt_ppdu_stats_common { + u32 ppdu_id; + u16 sched_cmdid; + u8 ring_id; + u8 num_users; + u32 flags; /* %HTT_PPDU_STATS_COMMON_FLAGS_*/ + u32 chain_mask; + u32 fes_duration_us; /* frame exchange sequence */ + u32 ppdu_sch_eval_start_tstmp_us; + u32 ppdu_sch_end_tstmp_us; + u32 ppdu_start_tstmp_us; + /* BIT [15 : 0] - phy mode (WLAN_PHY_MODE) with which ppdu was transmitted + * BIT [31 : 16] - bandwidth (in MHz) with which ppdu was transmitted + */ + u16 phy_mode; + u16 bw_mhz; +} __packed; + +#define HTT_PPDU_STATS_USER_RATE_INFO0_USER_POS_M GENMASK(3, 0) +#define HTT_PPDU_STATS_USER_RATE_INFO0_MU_GROUP_ID_M GENMASK(11, 4) + +#define HTT_PPDU_STATS_USER_RATE_INFO1_RESP_TYPE_VALD_M BIT(0) +#define HTT_PPDU_STATS_USER_RATE_INFO1_PPDU_TYPE_M GENMASK(5, 1) + +#define HTT_PPDU_STATS_USER_RATE_FLAGS_LTF_SIZE_M GENMASK(1, 0) +#define HTT_PPDU_STATS_USER_RATE_FLAGS_STBC_M BIT(2) +#define HTT_PPDU_STATS_USER_RATE_FLAGS_HE_RE_M BIT(3) +#define HTT_PPDU_STATS_USER_RATE_FLAGS_TXBF_M GENMASK(7, 4) +#define HTT_PPDU_STATS_USER_RATE_FLAGS_BW_M GENMASK(11, 8) +#define HTT_PPDU_STATS_USER_RATE_FLAGS_NSS_M GENMASK(15, 12) +#define HTT_PPDU_STATS_USER_RATE_FLAGS_MCS_M GENMASK(19, 16) +#define HTT_PPDU_STATS_USER_RATE_FLAGS_PREAMBLE_M GENMASK(23, 20) +#define HTT_PPDU_STATS_USER_RATE_FLAGS_GI_M GENMASK(27, 24) +#define HTT_PPDU_STATS_USER_RATE_FLAGS_DCM_M BIT(28) +#define HTT_PPDU_STATS_USER_RATE_FLAGS_LDPC_M BIT(29) + +#define HTT_USR_RATE_PREAMBLE(_val) \ + FIELD_GET(HTT_PPDU_STATS_USER_RATE_FLAGS_PREAMBLE_M, _val) +#define HTT_USR_RATE_BW(_val) \ + FIELD_GET(HTT_PPDU_STATS_USER_RATE_FLAGS_BW_M, _val) +#define HTT_USR_RATE_NSS(_val) \ + FIELD_GET(HTT_PPDU_STATS_USER_RATE_FLAGS_NSS_M, _val) +#define HTT_USR_RATE_MCS(_val) \ + FIELD_GET(HTT_PPDU_STATS_USER_RATE_FLAGS_MCS_M, _val) +#define HTT_USR_RATE_GI(_val) \ + FIELD_GET(HTT_PPDU_STATS_USER_RATE_FLAGS_GI_M, _val) + +#define HTT_PPDU_STATS_USER_RATE_RESP_FLAGS_LTF_SIZE_M GENMASK(1, 0) +#define HTT_PPDU_STATS_USER_RATE_RESP_FLAGS_STBC_M BIT(2) +#define HTT_PPDU_STATS_USER_RATE_RESP_FLAGS_HE_RE_M BIT(3) +#define HTT_PPDU_STATS_USER_RATE_RESP_FLAGS_TXBF_M GENMASK(7, 4) +#define HTT_PPDU_STATS_USER_RATE_RESP_FLAGS_BW_M GENMASK(11, 8) +#define HTT_PPDU_STATS_USER_RATE_RESP_FLAGS_NSS_M GENMASK(15, 12) +#define HTT_PPDU_STATS_USER_RATE_RESP_FLAGS_MCS_M GENMASK(19, 16) +#define HTT_PPDU_STATS_USER_RATE_RESP_FLAGS_PREAMBLE_M GENMASK(23, 20) +#define HTT_PPDU_STATS_USER_RATE_RESP_FLAGS_GI_M GENMASK(27, 24) +#define HTT_PPDU_STATS_USER_RATE_RESP_FLAGS_DCM_M BIT(28) +#define HTT_PPDU_STATS_USER_RATE_RESP_FLAGS_LDPC_M BIT(29) + +struct htt_ppdu_stats_user_rate { + u8 tid_num; + u8 reserved0; + u16 sw_peer_id; + u32 info0; /* %HTT_PPDU_STATS_USER_RATE_INFO0_*/ + u16 ru_end; + u16 ru_start; + u16 resp_ru_end; + u16 resp_ru_start; + u32 info1; /* %HTT_PPDU_STATS_USER_RATE_INFO1_ */ + u32 rate_flags; /* %HTT_PPDU_STATS_USER_RATE_FLAGS_ */ + /* Note: resp_rate_info is only valid for if resp_type is UL */ + u32 resp_rate_flags; /* %HTT_PPDU_STATS_USER_RATE_RESP_FLAGS_ */ +} __packed; + +#define HTT_PPDU_STATS_TX_INFO_FLAGS_RATECODE_M GENMASK(7, 0) +#define HTT_PPDU_STATS_TX_INFO_FLAGS_IS_AMPDU_M BIT(8) +#define HTT_PPDU_STATS_TX_INFO_FLAGS_BA_ACK_FAILED_M GENMASK(10, 9) +#define HTT_PPDU_STATS_TX_INFO_FLAGS_BW_M GENMASK(13, 11) +#define HTT_PPDU_STATS_TX_INFO_FLAGS_SGI_M BIT(14) +#define HTT_PPDU_STATS_TX_INFO_FLAGS_PEERID_M GENMASK(31, 16) + +#define HTT_TX_INFO_IS_AMSDU(_flags) \ + FIELD_GET(HTT_PPDU_STATS_TX_INFO_FLAGS_IS_AMPDU_M, _flags) +#define HTT_TX_INFO_BA_ACK_FAILED(_flags) \ + FIELD_GET(HTT_PPDU_STATS_TX_INFO_FLAGS_BA_ACK_FAILED_M, _flags) +#define HTT_TX_INFO_RATECODE(_flags) \ + FIELD_GET(HTT_PPDU_STATS_TX_INFO_FLAGS_RATECODE_M, _flags) +#define HTT_TX_INFO_PEERID(_flags) \ + FIELD_GET(HTT_PPDU_STATS_TX_INFO_FLAGS_PEERID_M, _flags) + +struct htt_tx_ppdu_stats_info { + struct htt_tlv tlv_hdr; + u32 tx_success_bytes; + u32 tx_retry_bytes; + u32 tx_failed_bytes; + u32 flags; /* %HTT_PPDU_STATS_TX_INFO_FLAGS_ */ + u16 tx_success_msdus; + u16 tx_retry_msdus; + u16 tx_failed_msdus; + u16 tx_duration; /* united in us */ +} __packed; + +enum htt_ppdu_stats_usr_compln_status { + HTT_PPDU_STATS_USER_STATUS_OK, + HTT_PPDU_STATS_USER_STATUS_FILTERED, + HTT_PPDU_STATS_USER_STATUS_RESP_TIMEOUT, + HTT_PPDU_STATS_USER_STATUS_RESP_MISMATCH, + HTT_PPDU_STATS_USER_STATUS_ABORT, +}; + +#define HTT_PPDU_STATS_USR_CMPLTN_CMN_FLAGS_LONG_RETRY_M GENMASK(3, 0) +#define HTT_PPDU_STATS_USR_CMPLTN_CMN_FLAGS_SHORT_RETRY_M GENMASK(7, 4) +#define HTT_PPDU_STATS_USR_CMPLTN_CMN_FLAGS_IS_AMPDU_M BIT(8) +#define HTT_PPDU_STATS_USR_CMPLTN_CMN_FLAGS_RESP_TYPE_M GENMASK(12, 9) + +#define HTT_USR_CMPLTN_IS_AMPDU(_val) \ + FIELD_GET(HTT_PPDU_STATS_USR_CMPLTN_CMN_FLAGS_IS_AMPDU_M, _val) +#define HTT_USR_CMPLTN_LONG_RETRY(_val) \ + FIELD_GET(HTT_PPDU_STATS_USR_CMPLTN_CMN_FLAGS_LONG_RETRY_M, _val) +#define HTT_USR_CMPLTN_SHORT_RETRY(_val) \ + FIELD_GET(HTT_PPDU_STATS_USR_CMPLTN_CMN_FLAGS_SHORT_RETRY_M, _val) + +struct htt_ppdu_stats_usr_cmpltn_cmn { + u8 status; + u8 tid_num; + u16 sw_peer_id; + /* RSSI value of last ack packet (units = dB above noise floor) */ + u32 ack_rssi; + u16 mpdu_tried; + u16 mpdu_success; + u32 flags; /* %HTT_PPDU_STATS_USR_CMPLTN_CMN_FLAGS_LONG_RETRIES*/ +} __packed; + +#define HTT_PPDU_STATS_ACK_BA_INFO_NUM_MPDU_M GENMASK(8, 0) +#define HTT_PPDU_STATS_ACK_BA_INFO_NUM_MSDU_M GENMASK(24, 9) +#define HTT_PPDU_STATS_ACK_BA_INFO_TID_NUM GENMASK(3, 0) + +struct htt_ppdu_stats_usr_cmpltn_ack_ba_status { + u32 ppdu_id; + u16 sw_peer_id; + u16 reserved0; + u32 info; /* %HTT_PPDU_STATS_USR_CMPLTN_CMN_INFO_ */ + u16 current_seq; + u16 start_seq; + u32 success_bytes; +} __packed; + +struct htt_ppdu_stats_usr_cmn_array { + struct htt_tlv tlv_hdr; + u32 num_ppdu_stats; + /* tx_ppdu_stats_info is filled by multiple struct htt_tx_ppdu_stats_info + * elements. + * tx_ppdu_stats_info is variable length, with length = + * number_of_ppdu_stats * sizeof (struct htt_tx_ppdu_stats_info) + */ + struct htt_tx_ppdu_stats_info tx_ppdu_info[0]; +} __packed; + +struct htt_ppdu_user_stats { + u16 peer_id; + u32 tlv_flags; + bool is_valid_peer_id; + struct htt_ppdu_stats_user_rate rate; + struct htt_ppdu_stats_usr_cmpltn_cmn cmpltn_cmn; + struct htt_ppdu_stats_usr_cmpltn_ack_ba_status ack_ba; +}; + +#define HTT_PPDU_STATS_MAX_USERS 8 +#define HTT_PPDU_DESC_MAX_DEPTH 16 + +struct htt_ppdu_stats { + struct htt_ppdu_stats_common common; + struct htt_ppdu_user_stats user_stats[HTT_PPDU_STATS_MAX_USERS]; +}; + +struct htt_ppdu_stats_info { + u32 ppdu_id; + struct htt_ppdu_stats ppdu_stats; + struct list_head list; +}; + +/** + * @brief target -> host packet log message + * + * @details + * The following field definitions describe the format of the packet log + * message sent from the target to the host. + * The message consists of a 4-octet header,followed by a variable number + * of 32-bit character values. + * + * |31 16|15 12|11 10|9 8|7 0| + * |------------------------------------------------------------------| + * | payload_size | rsvd |pdev_id|mac_id| msg type | + * |------------------------------------------------------------------| + * | payload | + * |------------------------------------------------------------------| + * - MSG_TYPE + * Bits 7:0 + * Purpose: identifies this as a pktlog message + * Value: HTT_T2H_MSG_TYPE_PKTLOG + * - mac_id + * Bits 9:8 + * Purpose: identifies which MAC/PHY instance generated this pktlog info + * Value: 0-3 + * - pdev_id + * Bits 11:10 + * Purpose: pdev_id + * Value: 0-3 + * 0 (for rings at SOC level), + * 1/2/3 PDEV -> 0/1/2 + * - payload_size + * Bits 31:16 + * Purpose: explicitly specify the payload size + * Value: payload size in bytes (payload size is a multiple of 4 bytes) + */ +struct htt_pktlog_msg { + u32 hdr; + u8 payload[0]; +}; + +/** + * @brief host -> target FW extended statistics retrieve + * + * @details + * The following field definitions describe the format of the HTT host + * to target FW extended stats retrieve message. + * The message specifies the type of stats the host wants to retrieve. + * + * |31 24|23 16|15 8|7 0| + * |-----------------------------------------------------------| + * | reserved | stats type | pdev_mask | msg type | + * |-----------------------------------------------------------| + * | config param [0] | + * |-----------------------------------------------------------| + * | config param [1] | + * |-----------------------------------------------------------| + * | config param [2] | + * |-----------------------------------------------------------| + * | config param [3] | + * |-----------------------------------------------------------| + * | reserved | + * |-----------------------------------------------------------| + * | cookie LSBs | + * |-----------------------------------------------------------| + * | cookie MSBs | + * |-----------------------------------------------------------| + * Header fields: + * - MSG_TYPE + * Bits 7:0 + * Purpose: identifies this is a extended stats upload request message + * Value: 0x10 + * - PDEV_MASK + * Bits 8:15 + * Purpose: identifies the mask of PDEVs to retrieve stats from + * Value: This is a overloaded field, refer to usage and interpretation of + * PDEV in interface document. + * Bit 8 : Reserved for SOC stats + * Bit 9 - 15 : Indicates PDEV_MASK in DBDC + * Indicates MACID_MASK in DBS + * - STATS_TYPE + * Bits 23:16 + * Purpose: identifies which FW statistics to upload + * Value: Defined by htt_dbg_ext_stats_type (see htt_stats.h) + * - Reserved + * Bits 31:24 + * - CONFIG_PARAM [0] + * Bits 31:0 + * Purpose: give an opaque configuration value to the specified stats type + * Value: stats-type specific configuration value + * Refer to htt_stats.h for interpretation for each stats sub_type + * - CONFIG_PARAM [1] + * Bits 31:0 + * Purpose: give an opaque configuration value to the specified stats type + * Value: stats-type specific configuration value + * Refer to htt_stats.h for interpretation for each stats sub_type + * - CONFIG_PARAM [2] + * Bits 31:0 + * Purpose: give an opaque configuration value to the specified stats type + * Value: stats-type specific configuration value + * Refer to htt_stats.h for interpretation for each stats sub_type + * - CONFIG_PARAM [3] + * Bits 31:0 + * Purpose: give an opaque configuration value to the specified stats type + * Value: stats-type specific configuration value + * Refer to htt_stats.h for interpretation for each stats sub_type + * - Reserved [31:0] for future use. + * - COOKIE_LSBS + * Bits 31:0 + * Purpose: Provide a mechanism to match a target->host stats confirmation + * message with its preceding host->target stats request message. + * Value: LSBs of the opaque cookie specified by the host-side requestor + * - COOKIE_MSBS + * Bits 31:0 + * Purpose: Provide a mechanism to match a target->host stats confirmation + * message with its preceding host->target stats request message. + * Value: MSBs of the opaque cookie specified by the host-side requestor + */ + +struct htt_ext_stats_cfg_hdr { + u8 msg_type; + u8 pdev_mask; + u8 stats_type; + u8 reserved; +} __packed; + +struct htt_ext_stats_cfg_cmd { + struct htt_ext_stats_cfg_hdr hdr; + u32 cfg_param0; + u32 cfg_param1; + u32 cfg_param2; + u32 cfg_param3; + u32 reserved; + u32 cookie_lsb; + u32 cookie_msb; +} __packed; + +/* htt stats config default params */ +#define HTT_STAT_DEFAULT_RESET_START_OFFSET 0 +#define HTT_STAT_DEFAULT_CFG0_ALL_HWQS 0xffffffff +#define HTT_STAT_DEFAULT_CFG0_ALL_TXQS 0xffffffff +#define HTT_STAT_DEFAULT_CFG0_ALL_CMDQS 0xffff +#define HTT_STAT_DEFAULT_CFG0_ALL_RINGS 0xffff +#define HTT_STAT_DEFAULT_CFG0_ACTIVE_PEERS 0xff +#define HTT_STAT_DEFAULT_CFG0_CCA_CUMULATIVE 0x10 +#define HTT_STAT_DEFAULT_CFG0_ACTIVE_VDEVS 0xff + +/* HTT_DBG_EXT_STATS_PEER_INFO + * PARAMS: + * @config_param0: + * [Bit0] - [0] for sw_peer_id, [1] for mac_addr based request + * [Bit15 : Bit 1] htt_peer_stats_req_mode_t + * [Bit31 : Bit16] sw_peer_id + * @config_param1: + * peer_stats_req_type_mask:32 (enum htt_peer_stats_tlv_enum) + * 0 bit htt_peer_stats_cmn_tlv + * 1 bit htt_peer_details_tlv + * 2 bit htt_tx_peer_rate_stats_tlv + * 3 bit htt_rx_peer_rate_stats_tlv + * 4 bit htt_tx_tid_stats_tlv/htt_tx_tid_stats_v1_tlv + * 5 bit htt_rx_tid_stats_tlv + * 6 bit htt_msdu_flow_stats_tlv + * @config_param2: [Bit31 : Bit0] mac_addr31to0 + * @config_param3: [Bit15 : Bit0] mac_addr47to32 + * [Bit31 : Bit16] reserved + */ +#define HTT_STAT_PEER_INFO_MAC_ADDR BIT(0) +#define HTT_STAT_DEFAULT_PEER_REQ_TYPE 0x7f + +/* Used to set different configs to the specified stats type.*/ +struct htt_ext_stats_cfg_params { + u32 cfg0; + u32 cfg1; + u32 cfg2; + u32 cfg3; +}; + +/** + * @brief target -> host extended statistics upload + * + * @details + * The following field definitions describe the format of the HTT target + * to host stats upload confirmation message. + * The message contains a cookie echoed from the HTT host->target stats + * upload request, which identifies which request the confirmation is + * for, and a single stats can span over multiple HTT stats indication + * due to the HTT message size limitation so every HTT ext stats indication + * will have tag-length-value stats information elements. + * The tag-length header for each HTT stats IND message also includes a + * status field, to indicate whether the request for the stat type in + * question was fully met, partially met, unable to be met, or invalid + * (if the stat type in question is disabled in the target). + * A Done bit 1's indicate the end of the of stats info elements. + * + * + * |31 16|15 12|11|10 8|7 5|4 0| + * |--------------------------------------------------------------| + * | reserved | msg type | + * |--------------------------------------------------------------| + * | cookie LSBs | + * |--------------------------------------------------------------| + * | cookie MSBs | + * |--------------------------------------------------------------| + * | stats entry length | rsvd | D| S | stat type | + * |--------------------------------------------------------------| + * | type-specific stats info | + * | (see htt_stats.h) | + * |--------------------------------------------------------------| + * Header fields: + * - MSG_TYPE + * Bits 7:0 + * Purpose: Identifies this is a extended statistics upload confirmation + * message. + * Value: 0x1c + * - COOKIE_LSBS + * Bits 31:0 + * Purpose: Provide a mechanism to match a target->host stats confirmation + * message with its preceding host->target stats request message. + * Value: LSBs of the opaque cookie specified by the host-side requestor + * - COOKIE_MSBS + * Bits 31:0 + * Purpose: Provide a mechanism to match a target->host stats confirmation + * message with its preceding host->target stats request message. + * Value: MSBs of the opaque cookie specified by the host-side requestor + * + * Stats Information Element tag-length header fields: + * - STAT_TYPE + * Bits 7:0 + * Purpose: identifies the type of statistics info held in the + * following information element + * Value: htt_dbg_ext_stats_type + * - STATUS + * Bits 10:8 + * Purpose: indicate whether the requested stats are present + * Value: htt_dbg_ext_stats_status + * - DONE + * Bits 11 + * Purpose: + * Indicates the completion of the stats entry, this will be the last + * stats conf HTT segment for the requested stats type. + * Value: + * 0 -> the stats retrieval is ongoing + * 1 -> the stats retrieval is complete + * - LENGTH + * Bits 31:16 + * Purpose: indicate the stats information size + * Value: This field specifies the number of bytes of stats information + * that follows the element tag-length header. + * It is expected but not required that this length is a multiple of + * 4 bytes. + */ + +#define HTT_T2H_EXT_STATS_INFO1_LENGTH GENMASK(31, 16) + +struct ath11k_htt_extd_stats_msg { + u32 info0; + u64 cookie; + u32 info1; + u8 data[0]; +} __packed; + +struct htt_mac_addr { + u32 mac_addr_l32; + u32 mac_addr_h16; +}; + +static inline void ath11k_dp_get_mac_addr(u32 addr_l32, u16 addr_h16, u8 *addr) +{ + if (IS_ENABLED(CONFIG_CPU_BIG_ENDIAN)) { + addr_l32 = swab32(addr_l32); + addr_h16 = swab16(addr_h16); + } + + memcpy(addr, &addr_l32, 4); + memcpy(addr + 4, &addr_h16, ETH_ALEN - 4); +} + +int ath11k_dp_service_srng(struct ath11k_base *ab, + struct ath11k_ext_irq_grp *irq_grp, + int budget); +int ath11k_dp_htt_connect(struct ath11k_dp *dp); +void ath11k_dp_vdev_tx_attach(struct ath11k *ar, struct ath11k_vif *arvif); +void ath11k_dp_free(struct ath11k_base *ab); +int ath11k_dp_alloc(struct ath11k_base *ab); +int ath11k_dp_pdev_alloc(struct ath11k_base *ab); +void ath11k_dp_pdev_free(struct ath11k_base *ab); +int ath11k_dp_tx_htt_srng_setup(struct ath11k_base *ab, u32 ring_id, + int mac_id, enum hal_ring_type ring_type); +int ath11k_dp_peer_setup(struct ath11k *ar, int vdev_id, const u8 *addr); +void ath11k_dp_peer_cleanup(struct ath11k *ar, int vdev_id, const u8 *addr); +void ath11k_dp_srng_cleanup(struct ath11k_base *ab, struct dp_srng *ring); +int ath11k_dp_srng_setup(struct ath11k_base *ab, struct dp_srng *ring, + enum hal_ring_type type, int ring_num, + int mac_id, int num_entries); +void ath11k_dp_link_desc_cleanup(struct ath11k_base *ab, + struct dp_link_desc_bank *desc_bank, + u32 ring_type, struct dp_srng *ring); +int ath11k_dp_link_desc_setup(struct ath11k_base *ab, + struct dp_link_desc_bank *link_desc_banks, + u32 ring_type, struct hal_srng *srng, + u32 n_link_desc); + +#endif diff --git a/drivers/net/wireless/ath/ath11k/dp_rx.c b/drivers/net/wireless/ath/ath11k/dp_rx.c new file mode 100644 index 0000000000000..9491a479fab51 --- /dev/null +++ b/drivers/net/wireless/ath/ath11k/dp_rx.c @@ -0,0 +1,4143 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* + * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. + */ + +#include +#include "core.h" +#include "debug.h" +#include "hal_desc.h" +#include "hw.h" +#include "dp_rx.h" +#include "hal_rx.h" +#include "dp_tx.h" +#include "peer.h" + +static u8 *ath11k_dp_rx_h_80211_hdr(struct hal_rx_desc *desc) +{ + return desc->hdr_status; +} + +static enum hal_encrypt_type ath11k_dp_rx_h_mpdu_start_enctype(struct hal_rx_desc *desc) +{ + if (!(__le32_to_cpu(desc->mpdu_start.info1) & + RX_MPDU_START_INFO1_ENCRYPT_INFO_VALID)) + return HAL_ENCRYPT_TYPE_OPEN; + + return FIELD_GET(RX_MPDU_START_INFO2_ENC_TYPE, + __le32_to_cpu(desc->mpdu_start.info2)); +} + +static u8 ath11k_dp_rx_h_mpdu_start_decap_type(struct hal_rx_desc *desc) +{ + return FIELD_GET(RX_MPDU_START_INFO5_DECAP_TYPE, + __le32_to_cpu(desc->mpdu_start.info5)); +} + +static bool ath11k_dp_rx_h_attn_msdu_done(struct hal_rx_desc *desc) +{ + return !!FIELD_GET(RX_ATTENTION_INFO2_MSDU_DONE, + __le32_to_cpu(desc->attention.info2)); +} + +static bool ath11k_dp_rx_h_attn_first_mpdu(struct hal_rx_desc *desc) +{ + return !!FIELD_GET(RX_ATTENTION_INFO1_FIRST_MPDU, + __le32_to_cpu(desc->attention.info1)); +} + +static bool ath11k_dp_rx_h_attn_l4_cksum_fail(struct hal_rx_desc *desc) +{ + return !!FIELD_GET(RX_ATTENTION_INFO1_TCP_UDP_CKSUM_FAIL, + __le32_to_cpu(desc->attention.info1)); +} + +static bool ath11k_dp_rx_h_attn_ip_cksum_fail(struct hal_rx_desc *desc) +{ + return !!FIELD_GET(RX_ATTENTION_INFO1_IP_CKSUM_FAIL, + __le32_to_cpu(desc->attention.info1)); +} + +static bool ath11k_dp_rx_h_attn_is_decrypted(struct hal_rx_desc *desc) +{ + return (FIELD_GET(RX_ATTENTION_INFO2_DCRYPT_STATUS_CODE, + __le32_to_cpu(desc->attention.info2)) == + RX_DESC_DECRYPT_STATUS_CODE_OK); +} + +static u32 ath11k_dp_rx_h_attn_mpdu_err(struct hal_rx_desc *desc) +{ + u32 info = __le32_to_cpu(desc->attention.info1); + u32 errmap = 0; + + if (info & RX_ATTENTION_INFO1_FCS_ERR) + errmap |= DP_RX_MPDU_ERR_FCS; + + if (info & RX_ATTENTION_INFO1_DECRYPT_ERR) + errmap |= DP_RX_MPDU_ERR_DECRYPT; + + if (info & RX_ATTENTION_INFO1_TKIP_MIC_ERR) + errmap |= DP_RX_MPDU_ERR_TKIP_MIC; + + if (info & RX_ATTENTION_INFO1_A_MSDU_ERROR) + errmap |= DP_RX_MPDU_ERR_AMSDU_ERR; + + if (info & RX_ATTENTION_INFO1_OVERFLOW_ERR) + errmap |= DP_RX_MPDU_ERR_OVERFLOW; + + if (info & RX_ATTENTION_INFO1_MSDU_LEN_ERR) + errmap |= DP_RX_MPDU_ERR_MSDU_LEN; + + if (info & RX_ATTENTION_INFO1_MPDU_LEN_ERR) + errmap |= DP_RX_MPDU_ERR_MPDU_LEN; + + return errmap; +} + +static u16 ath11k_dp_rx_h_msdu_start_msdu_len(struct hal_rx_desc *desc) +{ + return FIELD_GET(RX_MSDU_START_INFO1_MSDU_LENGTH, + __le32_to_cpu(desc->msdu_start.info1)); +} + +static u8 ath11k_dp_rx_h_msdu_start_sgi(struct hal_rx_desc *desc) +{ + return FIELD_GET(RX_MSDU_START_INFO3_SGI, + __le32_to_cpu(desc->msdu_start.info3)); +} + +static u8 ath11k_dp_rx_h_msdu_start_rate_mcs(struct hal_rx_desc *desc) +{ + return FIELD_GET(RX_MSDU_START_INFO3_RATE_MCS, + __le32_to_cpu(desc->msdu_start.info3)); +} + +static u8 ath11k_dp_rx_h_msdu_start_rx_bw(struct hal_rx_desc *desc) +{ + return FIELD_GET(RX_MSDU_START_INFO3_RECV_BW, + __le32_to_cpu(desc->msdu_start.info3)); +} + +static u32 ath11k_dp_rx_h_msdu_start_freq(struct hal_rx_desc *desc) +{ + return __le32_to_cpu(desc->msdu_start.phy_meta_data); +} + +static u8 ath11k_dp_rx_h_msdu_start_pkt_type(struct hal_rx_desc *desc) +{ + return FIELD_GET(RX_MSDU_START_INFO3_PKT_TYPE, + __le32_to_cpu(desc->msdu_start.info3)); +} + +static u8 ath11k_dp_rx_h_msdu_start_nss(struct hal_rx_desc *desc) +{ + u8 mimo_ss_bitmap = FIELD_GET(RX_MSDU_START_INFO3_MIMO_SS_BITMAP, + __le32_to_cpu(desc->msdu_start.info3)); + + return hweight8(mimo_ss_bitmap); +} + +static u8 ath11k_dp_rx_h_msdu_end_l3pad(struct hal_rx_desc *desc) +{ + return FIELD_GET(RX_MSDU_END_INFO2_L3_HDR_PADDING, + __le32_to_cpu(desc->msdu_end.info2)); +} + +static bool ath11k_dp_rx_h_msdu_end_first_msdu(struct hal_rx_desc *desc) +{ + return !!FIELD_GET(RX_MSDU_END_INFO2_FIRST_MSDU, + __le32_to_cpu(desc->msdu_end.info2)); +} + +static bool ath11k_dp_rx_h_msdu_end_last_msdu(struct hal_rx_desc *desc) +{ + return !!FIELD_GET(RX_MSDU_END_INFO2_LAST_MSDU, + __le32_to_cpu(desc->msdu_end.info2)); +} + +static void ath11k_dp_rx_desc_end_tlv_copy(struct hal_rx_desc *fdesc, + struct hal_rx_desc *ldesc) +{ + memcpy((u8 *)&fdesc->msdu_end, (u8 *)&ldesc->msdu_end, + sizeof(struct rx_msdu_end)); + memcpy((u8 *)&fdesc->attention, (u8 *)&ldesc->attention, + sizeof(struct rx_attention)); + memcpy((u8 *)&fdesc->mpdu_end, (u8 *)&ldesc->mpdu_end, + sizeof(struct rx_mpdu_end)); +} + +static u32 ath11k_dp_rxdesc_get_mpdulen_err(struct hal_rx_desc *rx_desc) +{ + struct rx_attention *rx_attn; + + rx_attn = &rx_desc->attention; + + return FIELD_GET(RX_ATTENTION_INFO1_MPDU_LEN_ERR, + __le32_to_cpu(rx_attn->info1)); +} + +static u32 ath11k_dp_rxdesc_get_decap_format(struct hal_rx_desc *rx_desc) +{ + struct rx_msdu_start *rx_msdu_start; + + rx_msdu_start = &rx_desc->msdu_start; + + return FIELD_GET(RX_MSDU_START_INFO2_DECAP_FORMAT, + __le32_to_cpu(rx_msdu_start->info2)); +} + +static u8 *ath11k_dp_rxdesc_get_80211hdr(struct hal_rx_desc *rx_desc) +{ + u8 *rx_pkt_hdr; + + rx_pkt_hdr = &rx_desc->msdu_payload[0]; + + return rx_pkt_hdr; +} + +static bool ath11k_dp_rxdesc_mpdu_valid(struct hal_rx_desc *rx_desc) +{ + u32 tlv_tag; + + tlv_tag = FIELD_GET(HAL_TLV_HDR_TAG, + __le32_to_cpu(rx_desc->mpdu_start_tag)); + + return tlv_tag == HAL_RX_MPDU_START ? true : false; +} + +static u32 ath11k_dp_rxdesc_get_ppduid(struct hal_rx_desc *rx_desc) +{ + return __le16_to_cpu(rx_desc->mpdu_start.phy_ppdu_id); +} + +/* Returns number of Rx buffers replenished */ +int ath11k_dp_rxbufs_replenish(struct ath11k_base *ab, int mac_id, + struct dp_rxdma_ring *rx_ring, + int req_entries, + enum hal_rx_buf_return_buf_manager mgr, + gfp_t gfp) +{ + struct hal_srng *srng; + u32 *desc; + struct sk_buff *skb; + int num_free; + int num_remain; + int buf_id; + u32 cookie; + dma_addr_t paddr; + + req_entries = min(req_entries, rx_ring->bufs_max); + + srng = &ab->hal.srng_list[rx_ring->refill_buf_ring.ring_id]; + + spin_lock_bh(&srng->lock); + + ath11k_hal_srng_access_begin(ab, srng); + + num_free = ath11k_hal_srng_src_num_free(ab, srng, true); + if (!req_entries && (num_free > (rx_ring->bufs_max * 3) / 4)) + req_entries = num_free; + + req_entries = min(num_free, req_entries); + num_remain = req_entries; + + while (num_remain > 0) { + skb = dev_alloc_skb(DP_RX_BUFFER_SIZE + + DP_RX_BUFFER_ALIGN_SIZE); + if (!skb) + break; + + if (!IS_ALIGNED((unsigned long)skb->data, + DP_RX_BUFFER_ALIGN_SIZE)) { + skb_pull(skb, + PTR_ALIGN(skb->data, DP_RX_BUFFER_ALIGN_SIZE) - + skb->data); + } + + paddr = dma_map_single(ab->dev, skb->data, + skb->len + skb_tailroom(skb), + DMA_FROM_DEVICE); + if (dma_mapping_error(ab->dev, paddr)) + goto fail_free_skb; + + spin_lock_bh(&rx_ring->idr_lock); + buf_id = idr_alloc(&rx_ring->bufs_idr, skb, 0, + rx_ring->bufs_max * 3, gfp); + spin_unlock_bh(&rx_ring->idr_lock); + if (buf_id < 0) + goto fail_dma_unmap; + + desc = ath11k_hal_srng_src_get_next_entry(ab, srng); + if (!desc) + goto fail_idr_remove; + + ATH11K_SKB_RXCB(skb)->paddr = paddr; + + cookie = FIELD_PREP(DP_RXDMA_BUF_COOKIE_PDEV_ID, mac_id) | + FIELD_PREP(DP_RXDMA_BUF_COOKIE_BUF_ID, buf_id); + + num_remain--; + + ath11k_hal_rx_buf_addr_info_set(desc, paddr, cookie, mgr); + } + + ath11k_hal_srng_access_end(ab, srng); + + spin_unlock_bh(&srng->lock); + + return req_entries - num_remain; + +fail_idr_remove: + spin_lock_bh(&rx_ring->idr_lock); + idr_remove(&rx_ring->bufs_idr, buf_id); + spin_unlock_bh(&rx_ring->idr_lock); +fail_dma_unmap: + dma_unmap_single(ab->dev, paddr, skb->len + skb_tailroom(skb), + DMA_FROM_DEVICE); +fail_free_skb: + dev_kfree_skb_any(skb); + + ath11k_hal_srng_access_end(ab, srng); + + spin_unlock_bh(&srng->lock); + + return req_entries - num_remain; +} + +static int ath11k_dp_rxdma_buf_ring_free(struct ath11k *ar, + struct dp_rxdma_ring *rx_ring) +{ + struct ath11k_pdev_dp *dp = &ar->dp; + struct sk_buff *skb; + int buf_id; + + spin_lock_bh(&rx_ring->idr_lock); + idr_for_each_entry(&rx_ring->bufs_idr, skb, buf_id) { + idr_remove(&rx_ring->bufs_idr, buf_id); + /* TODO: Understand where internal driver does this dma_unmap of + * of rxdma_buffer. + */ + dma_unmap_single(ar->ab->dev, ATH11K_SKB_RXCB(skb)->paddr, + skb->len + skb_tailroom(skb), DMA_FROM_DEVICE); + dev_kfree_skb_any(skb); + } + + idr_destroy(&rx_ring->bufs_idr); + spin_unlock_bh(&rx_ring->idr_lock); + + rx_ring = &dp->rx_mon_status_refill_ring; + + spin_lock_bh(&rx_ring->idr_lock); + idr_for_each_entry(&rx_ring->bufs_idr, skb, buf_id) { + idr_remove(&rx_ring->bufs_idr, buf_id); + /* XXX: Understand where internal driver does this dma_unmap of + * of rxdma_buffer. + */ + dma_unmap_single(ar->ab->dev, ATH11K_SKB_RXCB(skb)->paddr, + skb->len + skb_tailroom(skb), DMA_BIDIRECTIONAL); + dev_kfree_skb_any(skb); + } + + idr_destroy(&rx_ring->bufs_idr); + spin_unlock_bh(&rx_ring->idr_lock); + return 0; +} + +static int ath11k_dp_rxdma_pdev_buf_free(struct ath11k *ar) +{ + struct ath11k_pdev_dp *dp = &ar->dp; + struct dp_rxdma_ring *rx_ring = &dp->rx_refill_buf_ring; + + ath11k_dp_rxdma_buf_ring_free(ar, rx_ring); + + rx_ring = &dp->rxdma_mon_buf_ring; + ath11k_dp_rxdma_buf_ring_free(ar, rx_ring); + + rx_ring = &dp->rx_mon_status_refill_ring; + ath11k_dp_rxdma_buf_ring_free(ar, rx_ring); + return 0; +} + +static int ath11k_dp_rxdma_ring_buf_setup(struct ath11k *ar, + struct dp_rxdma_ring *rx_ring, + u32 ringtype) +{ + struct ath11k_pdev_dp *dp = &ar->dp; + int num_entries; + + num_entries = rx_ring->refill_buf_ring.size / + ath11k_hal_srng_get_entrysize(ringtype); + + rx_ring->bufs_max = num_entries; + ath11k_dp_rxbufs_replenish(ar->ab, dp->mac_id, rx_ring, num_entries, + HAL_RX_BUF_RBM_SW3_BM, GFP_KERNEL); + return 0; +} + +static int ath11k_dp_rxdma_pdev_buf_setup(struct ath11k *ar) +{ + struct ath11k_pdev_dp *dp = &ar->dp; + struct dp_rxdma_ring *rx_ring = &dp->rx_refill_buf_ring; + + ath11k_dp_rxdma_ring_buf_setup(ar, rx_ring, HAL_RXDMA_BUF); + + rx_ring = &dp->rxdma_mon_buf_ring; + ath11k_dp_rxdma_ring_buf_setup(ar, rx_ring, HAL_RXDMA_MONITOR_BUF); + + rx_ring = &dp->rx_mon_status_refill_ring; + ath11k_dp_rxdma_ring_buf_setup(ar, rx_ring, HAL_RXDMA_MONITOR_STATUS); + + return 0; +} + +static void ath11k_dp_rx_pdev_srng_free(struct ath11k *ar) +{ + struct ath11k_pdev_dp *dp = &ar->dp; + + ath11k_dp_srng_cleanup(ar->ab, &dp->rx_refill_buf_ring.refill_buf_ring); + ath11k_dp_srng_cleanup(ar->ab, &dp->reo_dst_ring); + ath11k_dp_srng_cleanup(ar->ab, &dp->rxdma_err_dst_ring); + ath11k_dp_srng_cleanup(ar->ab, &dp->rx_mon_status_refill_ring.refill_buf_ring); + ath11k_dp_srng_cleanup(ar->ab, &dp->rxdma_mon_buf_ring.refill_buf_ring); +} + +static int ath11k_dp_rx_pdev_srng_alloc(struct ath11k *ar) +{ + struct ath11k_pdev_dp *dp = &ar->dp; + struct dp_srng *srng = NULL; + int ret; + + ret = ath11k_dp_srng_setup(ar->ab, + &dp->rx_refill_buf_ring.refill_buf_ring, + HAL_RXDMA_BUF, 0, + dp->mac_id, DP_RXDMA_BUF_RING_SIZE); + if (ret) { + ath11k_warn(ar->ab, "failed to setup rx_refill_buf_ring\n"); + return ret; + } + + ret = ath11k_dp_srng_setup(ar->ab, &dp->reo_dst_ring, HAL_REO_DST, + dp->mac_id, dp->mac_id, + DP_REO_DST_RING_SIZE); + if (ret) { + ath11k_warn(ar->ab, "failed to setup reo_dst_ring\n"); + return ret; + } + + ret = ath11k_dp_srng_setup(ar->ab, &dp->rxdma_err_dst_ring, + HAL_RXDMA_DST, 0, dp->mac_id, + DP_RXDMA_ERR_DST_RING_SIZE); + if (ret) { + ath11k_warn(ar->ab, "failed to setup rxdma_err_dst_ring\n"); + return ret; + } + + srng = &dp->rx_mon_status_refill_ring.refill_buf_ring; + ret = ath11k_dp_srng_setup(ar->ab, + srng, + HAL_RXDMA_MONITOR_STATUS, 0, dp->mac_id, + DP_RXDMA_MON_STATUS_RING_SIZE); + if (ret) { + ath11k_warn(ar->ab, + "failed to setup rx_mon_status_refill_ring\n"); + return ret; + } + ret = ath11k_dp_srng_setup(ar->ab, + &dp->rxdma_mon_buf_ring.refill_buf_ring, + HAL_RXDMA_MONITOR_BUF, 0, dp->mac_id, + DP_RXDMA_MONITOR_BUF_RING_SIZE); + if (ret) { + ath11k_warn(ar->ab, + "failed to setup HAL_RXDMA_MONITOR_BUF\n"); + return ret; + } + + ret = ath11k_dp_srng_setup(ar->ab, &dp->rxdma_mon_dst_ring, + HAL_RXDMA_MONITOR_DST, 0, dp->mac_id, + DP_RXDMA_MONITOR_DST_RING_SIZE); + if (ret) { + ath11k_warn(ar->ab, + "failed to setup HAL_RXDMA_MONITOR_DST\n"); + return ret; + } + + ret = ath11k_dp_srng_setup(ar->ab, &dp->rxdma_mon_desc_ring, + HAL_RXDMA_MONITOR_DESC, 0, dp->mac_id, + DP_RXDMA_MONITOR_DESC_RING_SIZE); + if (ret) { + ath11k_warn(ar->ab, + "failed to setup HAL_RXDMA_MONITOR_DESC\n"); + return ret; + } + + return 0; +} + +void ath11k_dp_reo_cmd_list_cleanup(struct ath11k_base *ab) +{ + struct ath11k_dp *dp = &ab->dp; + struct dp_reo_cmd *cmd, *tmp; + struct dp_reo_cache_flush_elem *cmd_cache, *tmp_cache; + + spin_lock_bh(&dp->reo_cmd_lock); + list_for_each_entry_safe(cmd, tmp, &dp->reo_cmd_list, list) { + list_del(&cmd->list); + dma_unmap_single(ab->dev, cmd->data.paddr, + cmd->data.size, DMA_BIDIRECTIONAL); + kfree(cmd->data.vaddr); + kfree(cmd); + } + + list_for_each_entry_safe(cmd_cache, tmp_cache, + &dp->reo_cmd_cache_flush_list, list) { + list_del(&cmd_cache->list); + dma_unmap_single(ab->dev, cmd_cache->data.paddr, + cmd_cache->data.size, DMA_BIDIRECTIONAL); + kfree(cmd_cache->data.vaddr); + kfree(cmd_cache); + } + spin_unlock_bh(&dp->reo_cmd_lock); +} + +static void ath11k_dp_reo_cmd_free(struct ath11k_dp *dp, void *ctx, + enum hal_reo_cmd_status status) +{ + struct dp_rx_tid *rx_tid = ctx; + + if (status != HAL_REO_CMD_SUCCESS) + ath11k_warn(dp->ab, "failed to flush rx tid hw desc, tid %d status %d\n", + rx_tid->tid, status); + + dma_unmap_single(dp->ab->dev, rx_tid->paddr, rx_tid->size, + DMA_BIDIRECTIONAL); + kfree(rx_tid->vaddr); +} + +static void ath11k_dp_reo_cache_flush(struct ath11k_base *ab, + struct dp_rx_tid *rx_tid) +{ + struct ath11k_hal_reo_cmd cmd = {0}; + unsigned long tot_desc_sz, desc_sz; + int ret; + + tot_desc_sz = rx_tid->size; + desc_sz = ath11k_hal_reo_qdesc_size(0, HAL_DESC_REO_NON_QOS_TID); + + while (tot_desc_sz > desc_sz) { + tot_desc_sz -= desc_sz; + cmd.addr_lo = lower_32_bits(rx_tid->paddr + tot_desc_sz); + cmd.addr_hi = upper_32_bits(rx_tid->paddr); + ret = ath11k_dp_tx_send_reo_cmd(ab, rx_tid, + HAL_REO_CMD_FLUSH_CACHE, &cmd, + NULL); + if (ret) + ath11k_warn(ab, + "failed to send HAL_REO_CMD_FLUSH_CACHE, tid %d (%d)\n", + rx_tid->tid, ret); + } + + memset(&cmd, 0, sizeof(cmd)); + cmd.addr_lo = lower_32_bits(rx_tid->paddr); + cmd.addr_hi = upper_32_bits(rx_tid->paddr); + cmd.flag |= HAL_REO_CMD_FLG_NEED_STATUS; + ret = ath11k_dp_tx_send_reo_cmd(ab, rx_tid, + HAL_REO_CMD_FLUSH_CACHE, + &cmd, ath11k_dp_reo_cmd_free); + if (ret) { + ath11k_err(ab, "failed to send HAL_REO_CMD_FLUSH_CACHE cmd, tid %d (%d)\n", + rx_tid->tid, ret); + dma_unmap_single(ab->dev, rx_tid->paddr, rx_tid->size, + DMA_BIDIRECTIONAL); + kfree(rx_tid->vaddr); + } +} + +static void ath11k_dp_rx_tid_del_func(struct ath11k_dp *dp, void *ctx, + enum hal_reo_cmd_status status) +{ + struct ath11k_base *ab = dp->ab; + struct dp_rx_tid *rx_tid = ctx; + struct dp_reo_cache_flush_elem *elem, *tmp; + + if (status == HAL_REO_CMD_DRAIN) { + goto free_desc; + } else if (status != HAL_REO_CMD_SUCCESS) { + /* Shouldn't happen! Cleanup in case of other failure? */ + ath11k_warn(ab, "failed to delete rx tid %d hw descriptor %d\n", + rx_tid->tid, status); + return; + } + + elem = kzalloc(sizeof(*elem), GFP_ATOMIC); + if (!elem) + goto free_desc; + + elem->ts = jiffies; + memcpy(&elem->data, rx_tid, sizeof(*rx_tid)); + + spin_lock_bh(&dp->reo_cmd_lock); + list_add_tail(&elem->list, &dp->reo_cmd_cache_flush_list); + spin_unlock_bh(&dp->reo_cmd_lock); + + /* Flush and invalidate aged REO desc from HW cache */ + spin_lock_bh(&dp->reo_cmd_lock); + list_for_each_entry_safe(elem, tmp, &dp->reo_cmd_cache_flush_list, + list) { + if (time_after(jiffies, elem->ts + + msecs_to_jiffies(DP_REO_DESC_FREE_TIMEOUT_MS))) { + list_del(&elem->list); + spin_unlock_bh(&dp->reo_cmd_lock); + + ath11k_dp_reo_cache_flush(ab, &elem->data); + kfree(elem); + spin_lock_bh(&dp->reo_cmd_lock); + } + } + spin_unlock_bh(&dp->reo_cmd_lock); + + return; +free_desc: + dma_unmap_single(ab->dev, rx_tid->paddr, rx_tid->size, + DMA_BIDIRECTIONAL); + kfree(rx_tid->vaddr); +} + +static void ath11k_peer_rx_tid_delete(struct ath11k *ar, + struct ath11k_peer *peer, u8 tid) +{ + struct ath11k_hal_reo_cmd cmd = {0}; + struct dp_rx_tid *rx_tid = &peer->rx_tid[tid]; + int ret; + + if (!rx_tid->active) + return; + + cmd.flag = HAL_REO_CMD_FLG_NEED_STATUS; + cmd.addr_lo = lower_32_bits(rx_tid->paddr); + cmd.addr_hi = upper_32_bits(rx_tid->paddr); + cmd.upd0 |= HAL_REO_CMD_UPD0_VLD; + ret = ath11k_dp_tx_send_reo_cmd(ar->ab, rx_tid, + HAL_REO_CMD_UPDATE_RX_QUEUE, &cmd, + ath11k_dp_rx_tid_del_func); + if (ret) { + ath11k_err(ar->ab, "failed to send HAL_REO_CMD_UPDATE_RX_QUEUE cmd, tid %d (%d)\n", + tid, ret); + dma_unmap_single(ar->ab->dev, rx_tid->paddr, rx_tid->size, + DMA_BIDIRECTIONAL); + kfree(rx_tid->vaddr); + } + + rx_tid->active = false; +} + +void ath11k_peer_rx_tid_cleanup(struct ath11k *ar, struct ath11k_peer *peer) +{ + int i; + + for (i = 0; i <= IEEE80211_NUM_TIDS; i++) + ath11k_peer_rx_tid_delete(ar, peer, i); +} + +static int ath11k_peer_rx_tid_reo_update(struct ath11k *ar, + struct ath11k_peer *peer, + struct dp_rx_tid *rx_tid, + u32 ba_win_sz, u16 ssn) +{ + struct ath11k_hal_reo_cmd cmd = {0}; + int ret; + + cmd.addr_lo = lower_32_bits(rx_tid->paddr); + cmd.addr_hi = upper_32_bits(rx_tid->paddr); + cmd.flag = HAL_REO_CMD_FLG_NEED_STATUS; + cmd.upd0 = HAL_REO_CMD_UPD0_BA_WINDOW_SIZE | + HAL_REO_CMD_UPD0_SSN; + cmd.ba_window_size = ba_win_sz; + cmd.upd2 = FIELD_PREP(HAL_REO_CMD_UPD2_SSN, ssn); + + ret = ath11k_dp_tx_send_reo_cmd(ar->ab, rx_tid, + HAL_REO_CMD_UPDATE_RX_QUEUE, &cmd, + NULL); + if (ret) { + ath11k_warn(ar->ab, "failed to update rx tid queue, tid %d (%d)\n", + rx_tid->tid, ret); + return ret; + } + + rx_tid->ba_win_sz = ba_win_sz; + + return 0; +} + +static void ath11k_dp_rx_tid_mem_free(struct ath11k_base *ab, + const u8 *peer_mac, int vdev_id, u8 tid) +{ + struct ath11k_peer *peer; + struct dp_rx_tid *rx_tid; + + spin_lock_bh(&ab->base_lock); + + peer = ath11k_peer_find(ab, vdev_id, peer_mac); + if (!peer) { + ath11k_warn(ab, "failed to find the peer to free up rx tid mem\n"); + goto unlock_exit; + } + + rx_tid = &peer->rx_tid[tid]; + if (!rx_tid->active) + goto unlock_exit; + + dma_unmap_single(ab->dev, rx_tid->paddr, rx_tid->size, + DMA_BIDIRECTIONAL); + kfree(rx_tid->vaddr); + + rx_tid->active = false; + +unlock_exit: + spin_unlock_bh(&ab->base_lock); +} + +int ath11k_peer_rx_tid_setup(struct ath11k *ar, const u8 *peer_mac, int vdev_id, + u8 tid, u32 ba_win_sz, u16 ssn) +{ + struct ath11k_base *ab = ar->ab; + struct ath11k_peer *peer; + struct dp_rx_tid *rx_tid; + u32 hw_desc_sz; + u32 *addr_aligned; + void *vaddr; + dma_addr_t paddr; + int ret; + + spin_lock_bh(&ab->base_lock); + + peer = ath11k_peer_find(ab, vdev_id, peer_mac); + if (!peer) { + ath11k_warn(ab, "failed to find the peer to set up rx tid\n"); + spin_unlock_bh(&ab->base_lock); + return -ENOENT; + } + + rx_tid = &peer->rx_tid[tid]; + /* Update the tid queue if it is already setup */ + if (rx_tid->active) { + paddr = rx_tid->paddr; + ret = ath11k_peer_rx_tid_reo_update(ar, peer, rx_tid, + ba_win_sz, ssn); + spin_unlock_bh(&ab->base_lock); + if (ret) { + ath11k_warn(ab, "failed to update reo for rx tid %d\n", tid); + return ret; + } + + ret = ath11k_wmi_peer_rx_reorder_queue_setup(ar, vdev_id, + peer_mac, paddr, + tid, 1, ba_win_sz); + if (ret) + ath11k_warn(ab, "failed to send wmi command to update rx reorder queue, tid :%d (%d)\n", + tid, ret); + return ret; + } + + rx_tid->tid = tid; + + rx_tid->ba_win_sz = ba_win_sz; + + /* TODO: Optimize the memory allocation for qos tid based on the + * the actual BA window size in REO tid update path. + */ + if (tid == HAL_DESC_REO_NON_QOS_TID) + hw_desc_sz = ath11k_hal_reo_qdesc_size(ba_win_sz, tid); + else + hw_desc_sz = ath11k_hal_reo_qdesc_size(DP_BA_WIN_SZ_MAX, tid); + + vaddr = kzalloc(hw_desc_sz + HAL_LINK_DESC_ALIGN - 1, GFP_KERNEL); + if (!vaddr) { + spin_unlock_bh(&ab->base_lock); + return -ENOMEM; + } + + addr_aligned = PTR_ALIGN(vaddr, HAL_LINK_DESC_ALIGN); + + ath11k_hal_reo_qdesc_setup(addr_aligned, tid, ba_win_sz, ssn); + + paddr = dma_map_single(ab->dev, addr_aligned, hw_desc_sz, + DMA_BIDIRECTIONAL); + + ret = dma_mapping_error(ab->dev, paddr); + if (ret) { + spin_unlock_bh(&ab->base_lock); + goto err_mem_free; + } + + rx_tid->vaddr = vaddr; + rx_tid->paddr = paddr; + rx_tid->size = hw_desc_sz; + rx_tid->active = true; + + spin_unlock_bh(&ab->base_lock); + + ret = ath11k_wmi_peer_rx_reorder_queue_setup(ar, vdev_id, peer_mac, + paddr, tid, 1, ba_win_sz); + if (ret) { + ath11k_warn(ar->ab, "failed to setup rx reorder queue, tid :%d (%d)\n", + tid, ret); + ath11k_dp_rx_tid_mem_free(ab, peer_mac, vdev_id, tid); + } + + return ret; + +err_mem_free: + kfree(vaddr); + + return ret; +} + +int ath11k_dp_rx_ampdu_start(struct ath11k *ar, + struct ieee80211_ampdu_params *params) +{ + struct ath11k_base *ab = ar->ab; + struct ath11k_sta *arsta = (void *)params->sta->drv_priv; + int vdev_id = arsta->arvif->vdev_id; + int ret; + + ret = ath11k_peer_rx_tid_setup(ar, params->sta->addr, vdev_id, + params->tid, params->buf_size, + params->ssn); + if (ret) + ath11k_warn(ab, "failed to setup rx tid %d\n", ret); + + return ret; +} + +int ath11k_dp_rx_ampdu_stop(struct ath11k *ar, + struct ieee80211_ampdu_params *params) +{ + struct ath11k_base *ab = ar->ab; + struct ath11k_peer *peer; + struct ath11k_sta *arsta = (void *)params->sta->drv_priv; + int vdev_id = arsta->arvif->vdev_id; + dma_addr_t paddr; + bool active; + int ret; + + spin_lock_bh(&ab->base_lock); + + peer = ath11k_peer_find(ab, vdev_id, params->sta->addr); + if (!peer) { + ath11k_warn(ab, "failed to find the peer to stop rx aggregation\n"); + spin_unlock_bh(&ab->base_lock); + return -ENOENT; + } + + paddr = peer->rx_tid[params->tid].paddr; + active = peer->rx_tid[params->tid].active; + + ath11k_peer_rx_tid_delete(ar, peer, params->tid); + + spin_unlock_bh(&ab->base_lock); + + if (!active) + return 0; + + ret = ath11k_wmi_peer_rx_reorder_queue_setup(ar, vdev_id, + params->sta->addr, paddr, + params->tid, 1, 1); + if (ret) + ath11k_warn(ab, "failed to send wmi to delete rx tid %d\n", + ret); + + return ret; +} + +static int ath11k_get_ppdu_user_index(struct htt_ppdu_stats *ppdu_stats, + u16 peer_id) +{ + int i; + + for (i = 0; i < HTT_PPDU_STATS_MAX_USERS - 1; i++) { + if (ppdu_stats->user_stats[i].is_valid_peer_id) { + if (peer_id == ppdu_stats->user_stats[i].peer_id) + return i; + } else { + return i; + } + } + + return -EINVAL; +} + +static int ath11k_htt_tlv_ppdu_stats_parse(struct ath11k_base *ab, + u16 tag, u16 len, const void *ptr, + void *data) +{ + struct htt_ppdu_stats_info *ppdu_info; + struct htt_ppdu_user_stats *user_stats; + int cur_user; + u16 peer_id; + + ppdu_info = (struct htt_ppdu_stats_info *)data; + + switch (tag) { + case HTT_PPDU_STATS_TAG_COMMON: + if (len < sizeof(struct htt_ppdu_stats_common)) { + ath11k_warn(ab, "Invalid len %d for the tag 0x%x\n", + len, tag); + return -EINVAL; + } + memcpy((void *)&ppdu_info->ppdu_stats.common, ptr, + sizeof(struct htt_ppdu_stats_common)); + break; + case HTT_PPDU_STATS_TAG_USR_RATE: + if (len < sizeof(struct htt_ppdu_stats_user_rate)) { + ath11k_warn(ab, "Invalid len %d for the tag 0x%x\n", + len, tag); + return -EINVAL; + } + + peer_id = ((struct htt_ppdu_stats_user_rate *)ptr)->sw_peer_id; + cur_user = ath11k_get_ppdu_user_index(&ppdu_info->ppdu_stats, + peer_id); + if (cur_user < 0) + return -EINVAL; + user_stats = &ppdu_info->ppdu_stats.user_stats[cur_user]; + user_stats->peer_id = peer_id; + user_stats->is_valid_peer_id = true; + memcpy((void *)&user_stats->rate, ptr, + sizeof(struct htt_ppdu_stats_user_rate)); + user_stats->tlv_flags |= BIT(tag); + break; + case HTT_PPDU_STATS_TAG_USR_COMPLTN_COMMON: + if (len < sizeof(struct htt_ppdu_stats_usr_cmpltn_cmn)) { + ath11k_warn(ab, "Invalid len %d for the tag 0x%x\n", + len, tag); + return -EINVAL; + } + + peer_id = ((struct htt_ppdu_stats_usr_cmpltn_cmn *)ptr)->sw_peer_id; + cur_user = ath11k_get_ppdu_user_index(&ppdu_info->ppdu_stats, + peer_id); + if (cur_user < 0) + return -EINVAL; + user_stats = &ppdu_info->ppdu_stats.user_stats[cur_user]; + user_stats->peer_id = peer_id; + user_stats->is_valid_peer_id = true; + memcpy((void *)&user_stats->cmpltn_cmn, ptr, + sizeof(struct htt_ppdu_stats_usr_cmpltn_cmn)); + user_stats->tlv_flags |= BIT(tag); + break; + case HTT_PPDU_STATS_TAG_USR_COMPLTN_ACK_BA_STATUS: + if (len < + sizeof(struct htt_ppdu_stats_usr_cmpltn_ack_ba_status)) { + ath11k_warn(ab, "Invalid len %d for the tag 0x%x\n", + len, tag); + return -EINVAL; + } + + peer_id = + ((struct htt_ppdu_stats_usr_cmpltn_ack_ba_status *)ptr)->sw_peer_id; + cur_user = ath11k_get_ppdu_user_index(&ppdu_info->ppdu_stats, + peer_id); + if (cur_user < 0) + return -EINVAL; + user_stats = &ppdu_info->ppdu_stats.user_stats[cur_user]; + user_stats->peer_id = peer_id; + user_stats->is_valid_peer_id = true; + memcpy((void *)&user_stats->ack_ba, ptr, + sizeof(struct htt_ppdu_stats_usr_cmpltn_ack_ba_status)); + user_stats->tlv_flags |= BIT(tag); + break; + } + return 0; +} + +int ath11k_dp_htt_tlv_iter(struct ath11k_base *ab, const void *ptr, size_t len, + int (*iter)(struct ath11k_base *ar, u16 tag, u16 len, + const void *ptr, void *data), + void *data) +{ + const struct htt_tlv *tlv; + const void *begin = ptr; + u16 tlv_tag, tlv_len; + int ret = -EINVAL; + + while (len > 0) { + if (len < sizeof(*tlv)) { + ath11k_err(ab, "htt tlv parse failure at byte %zd (%zu bytes left, %zu expected)\n", + ptr - begin, len, sizeof(*tlv)); + return -EINVAL; + } + tlv = (struct htt_tlv *)ptr; + tlv_tag = FIELD_GET(HTT_TLV_TAG, tlv->header); + tlv_len = FIELD_GET(HTT_TLV_LEN, tlv->header); + ptr += sizeof(*tlv); + len -= sizeof(*tlv); + + if (tlv_len > len) { + ath11k_err(ab, "htt tlv parse failure of tag %hhu at byte %zd (%zu bytes left, %hhu expected)\n", + tlv_tag, ptr - begin, len, tlv_len); + return -EINVAL; + } + ret = iter(ab, tlv_tag, tlv_len, ptr, data); + if (ret == -ENOMEM) + return ret; + + ptr += tlv_len; + len -= tlv_len; + } + return 0; +} + +static u8 ath11k_bw_to_mac80211_bw(u8 bw) +{ + u8 ret = 0; + + switch (bw) { + case ATH11K_BW_20: + ret = RATE_INFO_BW_20; + break; + case ATH11K_BW_40: + ret = RATE_INFO_BW_40; + break; + case ATH11K_BW_80: + ret = RATE_INFO_BW_80; + break; + case ATH11K_BW_160: + ret = RATE_INFO_BW_160; + break; + } + + return ret; +} + +static u32 ath11k_bw_to_mac80211_bwflags(u8 bw) +{ + u32 bwflags = 0; + + switch (bw) { + case ATH11K_BW_40: + bwflags = IEEE80211_TX_RC_40_MHZ_WIDTH; + break; + case ATH11K_BW_80: + bwflags = IEEE80211_TX_RC_80_MHZ_WIDTH; + break; + case ATH11K_BW_160: + bwflags = IEEE80211_TX_RC_160_MHZ_WIDTH; + break; + } + + return bwflags; +} + +static void +ath11k_update_per_peer_tx_stats(struct ath11k *ar, + struct htt_ppdu_stats *ppdu_stats, u8 user) +{ + struct ath11k_base *ab = ar->ab; + struct ath11k_peer *peer; + struct ieee80211_sta *sta; + struct ath11k_sta *arsta; + struct htt_ppdu_stats_user_rate *user_rate; + struct ieee80211_chanctx_conf *conf = NULL; + struct ath11k_per_peer_tx_stats *peer_stats = &ar->peer_tx_stats; + struct htt_ppdu_user_stats *usr_stats = &ppdu_stats->user_stats[user]; + struct htt_ppdu_stats_common *common = &ppdu_stats->common; + int ret; + u8 flags, mcs, nss, bw, sgi, rate_idx = 0; + u32 succ_bytes = 0; + u16 rate = 0, succ_pkts = 0; + u32 tx_duration = 0; + bool is_ampdu = false; + + if (!usr_stats) + return; + + if (!(usr_stats->tlv_flags & BIT(HTT_PPDU_STATS_TAG_USR_RATE))) + return; + + if (usr_stats->tlv_flags & BIT(HTT_PPDU_STATS_TAG_USR_COMPLTN_COMMON)) + is_ampdu = + HTT_USR_CMPLTN_IS_AMPDU(usr_stats->cmpltn_cmn.flags); + + if (usr_stats->tlv_flags & + BIT(HTT_PPDU_STATS_TAG_USR_COMPLTN_ACK_BA_STATUS)) { + succ_bytes = usr_stats->ack_ba.success_bytes; + succ_pkts = FIELD_GET(HTT_PPDU_STATS_ACK_BA_INFO_NUM_MSDU_M, + usr_stats->ack_ba.info); + } + + if (common->fes_duration_us) + tx_duration = common->fes_duration_us; + + user_rate = &usr_stats->rate; + flags = HTT_USR_RATE_PREAMBLE(user_rate->rate_flags); + bw = HTT_USR_RATE_BW(user_rate->rate_flags) - 2; + nss = HTT_USR_RATE_NSS(user_rate->rate_flags) + 1; + mcs = HTT_USR_RATE_MCS(user_rate->rate_flags); + sgi = HTT_USR_RATE_GI(user_rate->rate_flags); + + /* Note: If host configured fixed rates and in some other special + * cases, the broadcast/management frames are sent in different rates. + * Firmware rate's control to be skipped for this? + */ + + if (flags == WMI_RATE_PREAMBLE_VHT && mcs > 9) { + ath11k_warn(ab, "Invalid VHT mcs %hhd peer stats", mcs); + return; + } + + if (flags == WMI_RATE_PREAMBLE_HT && (mcs > 7 || nss < 1)) { + ath11k_warn(ab, "Invalid HT mcs %hhd nss %hhd peer stats", + mcs, nss); + return; + } + + if (flags == WMI_RATE_PREAMBLE_CCK || flags == WMI_RATE_PREAMBLE_OFDM) { + ret = ath11k_mac_hw_ratecode_to_legacy_rate(mcs, + flags, + &rate_idx, + &rate); + if (ret < 0) + return; + } + + rcu_read_lock(); + spin_lock_bh(&ab->base_lock); + peer = ath11k_peer_find_by_id(ab, usr_stats->peer_id); + + if (!peer || !peer->sta) { + spin_unlock_bh(&ab->base_lock); + rcu_read_unlock(); + return; + } + + sta = peer->sta; + arsta = (struct ath11k_sta *)sta->drv_priv; + + memset(&arsta->txrate, 0, sizeof(arsta->txrate)); + memset(&arsta->tx_info.status, 0, sizeof(arsta->tx_info.status)); + + switch (flags) { + case WMI_RATE_PREAMBLE_OFDM: + arsta->txrate.legacy = rate; + if (arsta->arvif && arsta->arvif->vif) + conf = rcu_dereference(arsta->arvif->vif->chanctx_conf); + if (conf && conf->def.chan->band == NL80211_BAND_5GHZ) + arsta->tx_info.status.rates[0].idx = rate_idx - 4; + break; + case WMI_RATE_PREAMBLE_CCK: + arsta->txrate.legacy = rate; + arsta->tx_info.status.rates[0].idx = rate_idx; + if (mcs > ATH11K_HW_RATE_CCK_LP_1M && + mcs <= ATH11K_HW_RATE_CCK_SP_2M) + arsta->tx_info.status.rates[0].flags |= + IEEE80211_TX_RC_USE_SHORT_PREAMBLE; + break; + case WMI_RATE_PREAMBLE_HT: + arsta->txrate.mcs = mcs + 8 * (nss - 1); + arsta->tx_info.status.rates[0].idx = arsta->txrate.mcs; + arsta->txrate.flags = RATE_INFO_FLAGS_MCS; + arsta->tx_info.status.rates[0].flags |= IEEE80211_TX_RC_MCS; + if (sgi) { + arsta->txrate.flags |= RATE_INFO_FLAGS_SHORT_GI; + arsta->tx_info.status.rates[0].flags |= + IEEE80211_TX_RC_SHORT_GI; + } + break; + case WMI_RATE_PREAMBLE_VHT: + arsta->txrate.mcs = mcs; + ieee80211_rate_set_vht(&arsta->tx_info.status.rates[0], mcs, nss); + arsta->txrate.flags = RATE_INFO_FLAGS_VHT_MCS; + arsta->tx_info.status.rates[0].flags |= IEEE80211_TX_RC_VHT_MCS; + if (sgi) { + arsta->txrate.flags |= RATE_INFO_FLAGS_SHORT_GI; + arsta->tx_info.status.rates[0].flags |= + IEEE80211_TX_RC_SHORT_GI; + } + break; + } + + arsta->txrate.nss = nss; + arsta->txrate.bw = ath11k_bw_to_mac80211_bw(bw); + arsta->tx_info.status.rates[0].flags |= ath11k_bw_to_mac80211_bwflags(bw); + + memcpy(&arsta->last_txrate, &arsta->txrate, sizeof(struct rate_info)); + + if (succ_pkts) { + arsta->tx_info.flags = IEEE80211_TX_STAT_ACK; + arsta->tx_info.status.rates[0].count = 1; + ieee80211_tx_rate_update(ar->hw, sta, &arsta->tx_info); + } + + memset(peer_stats, 0, sizeof(*peer_stats)); + + peer_stats->succ_pkts = succ_pkts; + peer_stats->succ_bytes = succ_bytes; + peer_stats->is_ampdu = is_ampdu; + peer_stats->duration = tx_duration; + peer_stats->ba_fails = + HTT_USR_CMPLTN_LONG_RETRY(usr_stats->cmpltn_cmn.flags) + + HTT_USR_CMPLTN_SHORT_RETRY(usr_stats->cmpltn_cmn.flags); + + if (ath11k_debug_is_extd_tx_stats_enabled(ar)) + ath11k_accumulate_per_peer_tx_stats(arsta, + peer_stats, rate_idx); + + spin_unlock_bh(&ab->base_lock); + rcu_read_unlock(); +} + +static void ath11k_htt_update_ppdu_stats(struct ath11k *ar, + struct htt_ppdu_stats *ppdu_stats) +{ + u8 user; + + for (user = 0; user < HTT_PPDU_STATS_MAX_USERS - 1; user++) + ath11k_update_per_peer_tx_stats(ar, ppdu_stats, user); +} + +static +struct htt_ppdu_stats_info *ath11k_dp_htt_get_ppdu_desc(struct ath11k *ar, + u32 ppdu_id) +{ + struct htt_ppdu_stats_info *ppdu_info = NULL; + + spin_lock_bh(&ar->data_lock); + if (!list_empty(&ar->ppdu_stats_info)) { + list_for_each_entry(ppdu_info, &ar->ppdu_stats_info, list) { + if (ppdu_info && ppdu_info->ppdu_id == ppdu_id) { + spin_unlock_bh(&ar->data_lock); + return ppdu_info; + } + } + + if (ar->ppdu_stat_list_depth > HTT_PPDU_DESC_MAX_DEPTH) { + ppdu_info = list_first_entry(&ar->ppdu_stats_info, + typeof(*ppdu_info), list); + list_del(&ppdu_info->list); + ar->ppdu_stat_list_depth--; + ath11k_htt_update_ppdu_stats(ar, &ppdu_info->ppdu_stats); + kfree(ppdu_info); + } + } + spin_unlock_bh(&ar->data_lock); + + ppdu_info = kzalloc(sizeof(*ppdu_info), GFP_KERNEL); + if (!ppdu_info) + return NULL; + + spin_lock_bh(&ar->data_lock); + list_add_tail(&ppdu_info->list, &ar->ppdu_stats_info); + ar->ppdu_stat_list_depth++; + spin_unlock_bh(&ar->data_lock); + + return ppdu_info; +} + +static int ath11k_htt_pull_ppdu_stats(struct ath11k_base *ab, + struct sk_buff *skb) +{ + struct ath11k_htt_ppdu_stats_msg *msg; + struct htt_ppdu_stats_info *ppdu_info; + struct ath11k *ar; + int ret; + u8 pdev_id; + u32 ppdu_id, len; + + msg = (struct ath11k_htt_ppdu_stats_msg *)skb->data; + len = FIELD_GET(HTT_T2H_PPDU_STATS_INFO_PAYLOAD_SIZE, msg->info); + pdev_id = FIELD_GET(HTT_T2H_PPDU_STATS_INFO_PDEV_ID, msg->info); + ppdu_id = msg->ppdu_id; + + rcu_read_lock(); + ar = ath11k_mac_get_ar_by_pdev_id(ab, pdev_id); + if (!ar) { + ret = -EINVAL; + goto exit; + } + + if (ath11k_debug_is_pktlog_lite_mode_enabled(ar)) + trace_ath11k_htt_ppdu_stats(ar, skb->data, len); + + ppdu_info = ath11k_dp_htt_get_ppdu_desc(ar, ppdu_id); + if (!ppdu_info) { + ret = -EINVAL; + goto exit; + } + + ppdu_info->ppdu_id = ppdu_id; + ret = ath11k_dp_htt_tlv_iter(ab, msg->data, len, + ath11k_htt_tlv_ppdu_stats_parse, + (void *)ppdu_info); + if (ret) { + ath11k_warn(ab, "Failed to parse tlv %d\n", ret); + goto exit; + } + +exit: + rcu_read_unlock(); + + return ret; +} + +static void ath11k_htt_pktlog(struct ath11k_base *ab, struct sk_buff *skb) +{ + struct htt_pktlog_msg *data = (struct htt_pktlog_msg *)skb->data; + struct ath11k *ar; + u32 len; + u8 pdev_id; + + len = FIELD_GET(HTT_T2H_PPDU_STATS_INFO_PAYLOAD_SIZE, data->hdr); + + if (len > ATH11K_HTT_PKTLOG_MAX_SIZE) { + ath11k_warn(ab, "htt pktlog buffer size %d, expected < %d\n", + len, + ATH11K_HTT_PKTLOG_MAX_SIZE); + return; + } + + pdev_id = FIELD_GET(HTT_T2H_PPDU_STATS_INFO_PDEV_ID, data->hdr); + pdev_id = DP_HW2SW_MACID(pdev_id); + ar = ab->pdevs[pdev_id].ar; + + trace_ath11k_htt_pktlog(ar, data->payload, len); +} + +void ath11k_dp_htt_htc_t2h_msg_handler(struct ath11k_base *ab, + struct sk_buff *skb) +{ + struct ath11k_dp *dp = &ab->dp; + struct htt_resp_msg *resp = (struct htt_resp_msg *)skb->data; + enum htt_t2h_msg_type type = FIELD_GET(HTT_T2H_MSG_TYPE, *(u32 *)resp); + u16 peer_id; + u8 vdev_id; + u8 mac_addr[ETH_ALEN]; + u16 peer_mac_h16; + u16 ast_hash; + + ath11k_dbg(ab, ATH11K_DBG_DP_HTT, "dp_htt rx msg type :0x%0x\n", type); + + switch (type) { + case HTT_T2H_MSG_TYPE_VERSION_CONF: + dp->htt_tgt_ver_major = FIELD_GET(HTT_T2H_VERSION_CONF_MAJOR, + resp->version_msg.version); + dp->htt_tgt_ver_minor = FIELD_GET(HTT_T2H_VERSION_CONF_MINOR, + resp->version_msg.version); + complete(&dp->htt_tgt_version_received); + break; + case HTT_T2H_MSG_TYPE_PEER_MAP: + vdev_id = FIELD_GET(HTT_T2H_PEER_MAP_INFO_VDEV_ID, + resp->peer_map_ev.info); + peer_id = FIELD_GET(HTT_T2H_PEER_MAP_INFO_PEER_ID, + resp->peer_map_ev.info); + peer_mac_h16 = FIELD_GET(HTT_T2H_PEER_MAP_INFO1_MAC_ADDR_H16, + resp->peer_map_ev.info1); + ath11k_dp_get_mac_addr(resp->peer_map_ev.mac_addr_l32, + peer_mac_h16, mac_addr); + ast_hash = FIELD_GET(HTT_T2H_PEER_MAP_INFO2_AST_HASH_VAL, + resp->peer_map_ev.info1); + ath11k_peer_map_event(ab, vdev_id, peer_id, mac_addr, ast_hash); + break; + case HTT_T2H_MSG_TYPE_PEER_UNMAP: + peer_id = FIELD_GET(HTT_T2H_PEER_UNMAP_INFO_PEER_ID, + resp->peer_unmap_ev.info); + ath11k_peer_unmap_event(ab, peer_id); + break; + case HTT_T2H_MSG_TYPE_PPDU_STATS_IND: + ath11k_htt_pull_ppdu_stats(ab, skb); + break; + case HTT_T2H_MSG_TYPE_EXT_STATS_CONF: + ath11k_dbg_htt_ext_stats_handler(ab, skb); + break; + case HTT_T2H_MSG_TYPE_PKTLOG: + ath11k_htt_pktlog(ab, skb); + break; + default: + ath11k_warn(ab, "htt event %d not handled\n", type); + break; + } + + dev_kfree_skb_any(skb); +} + +static int ath11k_dp_rx_msdu_coalesce(struct ath11k *ar, + struct sk_buff_head *msdu_list, + struct sk_buff *first, struct sk_buff *last, + u8 l3pad_bytes, int msdu_len) +{ + struct sk_buff *skb; + struct ath11k_skb_rxcb *rxcb = ATH11K_SKB_RXCB(first); + struct hal_rx_desc *ldesc; + int space_extra; + int rem_len; + int buf_len; + + if (!rxcb->is_continuation) { + skb_put(first, HAL_RX_DESC_SIZE + l3pad_bytes + msdu_len); + skb_pull(first, HAL_RX_DESC_SIZE + l3pad_bytes); + return 0; + } + + if (WARN_ON_ONCE(msdu_len <= (DP_RX_BUFFER_SIZE - + (HAL_RX_DESC_SIZE + l3pad_bytes)))) { + skb_put(first, HAL_RX_DESC_SIZE + l3pad_bytes + msdu_len); + skb_pull(first, HAL_RX_DESC_SIZE + l3pad_bytes); + return 0; + } + + ldesc = (struct hal_rx_desc *)last->data; + rxcb->is_first_msdu = ath11k_dp_rx_h_msdu_end_first_msdu(ldesc); + rxcb->is_last_msdu = ath11k_dp_rx_h_msdu_end_last_msdu(ldesc); + + /* MSDU spans over multiple buffers because the length of the MSDU + * exceeds DP_RX_BUFFER_SIZE - HAL_RX_DESC_SIZE. So assume the data + * in the first buf is of length DP_RX_BUFFER_SIZE - HAL_RX_DESC_SIZE. + */ + skb_put(first, DP_RX_BUFFER_SIZE); + skb_pull(first, HAL_RX_DESC_SIZE + l3pad_bytes); + + space_extra = msdu_len - (DP_RX_BUFFER_SIZE + skb_tailroom(first)); + if (space_extra > 0 && + (pskb_expand_head(first, 0, space_extra, GFP_ATOMIC) < 0)) { + /* Free up all buffers of the MSDU */ + while ((skb = __skb_dequeue(msdu_list)) != NULL) { + rxcb = ATH11K_SKB_RXCB(skb); + if (!rxcb->is_continuation) { + dev_kfree_skb_any(skb); + break; + } + dev_kfree_skb_any(skb); + } + return -ENOMEM; + } + + /* When an MSDU spread over multiple buffers attention, MSDU_END and + * MPDU_END tlvs are valid only in the last buffer. Copy those tlvs. + */ + ath11k_dp_rx_desc_end_tlv_copy(rxcb->rx_desc, ldesc); + + rem_len = msdu_len - + (DP_RX_BUFFER_SIZE - HAL_RX_DESC_SIZE - l3pad_bytes); + while ((skb = __skb_dequeue(msdu_list)) != NULL && rem_len > 0) { + rxcb = ATH11K_SKB_RXCB(skb); + if (rxcb->is_continuation) + buf_len = DP_RX_BUFFER_SIZE - HAL_RX_DESC_SIZE; + else + buf_len = rem_len; + + if (buf_len > (DP_RX_BUFFER_SIZE - HAL_RX_DESC_SIZE)) { + WARN_ON_ONCE(1); + dev_kfree_skb_any(skb); + return -EINVAL; + } + + skb_put(skb, buf_len + HAL_RX_DESC_SIZE); + skb_pull(skb, HAL_RX_DESC_SIZE); + skb_copy_from_linear_data(skb, skb_put(first, buf_len), + buf_len); + dev_kfree_skb_any(skb); + + rem_len -= buf_len; + if (!rxcb->is_continuation) + break; + } + + return 0; +} + +static struct sk_buff *ath11k_dp_rx_get_msdu_last_buf(struct sk_buff_head *msdu_list, + struct sk_buff *first) +{ + struct sk_buff *skb; + struct ath11k_skb_rxcb *rxcb = ATH11K_SKB_RXCB(first); + + if (!rxcb->is_continuation) + return first; + + skb_queue_walk(msdu_list, skb) { + rxcb = ATH11K_SKB_RXCB(skb); + if (!rxcb->is_continuation) + return skb; + } + + return NULL; +} + +static int ath11k_dp_rx_retrieve_amsdu(struct ath11k *ar, + struct sk_buff_head *msdu_list, + struct sk_buff_head *amsdu_list) +{ + struct sk_buff *msdu = skb_peek(msdu_list); + struct sk_buff *last_buf; + struct ath11k_skb_rxcb *rxcb; + struct ieee80211_hdr *hdr; + struct hal_rx_desc *rx_desc, *lrx_desc; + u16 msdu_len; + u8 l3_pad_bytes; + u8 *hdr_status; + int ret; + + if (!msdu) + return -ENOENT; + + rx_desc = (struct hal_rx_desc *)msdu->data; + hdr_status = ath11k_dp_rx_h_80211_hdr(rx_desc); + hdr = (struct ieee80211_hdr *)hdr_status; + /* Process only data frames */ + if (!ieee80211_is_data(hdr->frame_control)) { + __skb_unlink(msdu, msdu_list); + dev_kfree_skb_any(msdu); + return -EINVAL; + } + + do { + __skb_unlink(msdu, msdu_list); + last_buf = ath11k_dp_rx_get_msdu_last_buf(msdu_list, msdu); + if (!last_buf) { + ath11k_warn(ar->ab, + "No valid Rx buffer to access Atten/MSDU_END/MPDU_END tlvs\n"); + ret = -EIO; + goto free_out; + } + + rx_desc = (struct hal_rx_desc *)msdu->data; + lrx_desc = (struct hal_rx_desc *)last_buf->data; + + if (!ath11k_dp_rx_h_attn_msdu_done(lrx_desc)) { + ath11k_warn(ar->ab, "msdu_done bit in attention is not set\n"); + ret = -EIO; + goto free_out; + } + + rxcb = ATH11K_SKB_RXCB(msdu); + rxcb->rx_desc = rx_desc; + msdu_len = ath11k_dp_rx_h_msdu_start_msdu_len(rx_desc); + l3_pad_bytes = ath11k_dp_rx_h_msdu_end_l3pad(lrx_desc); + + if (!rxcb->is_continuation) { + skb_put(msdu, HAL_RX_DESC_SIZE + l3_pad_bytes + msdu_len); + skb_pull(msdu, HAL_RX_DESC_SIZE + l3_pad_bytes); + } else { + ret = ath11k_dp_rx_msdu_coalesce(ar, msdu_list, + msdu, last_buf, + l3_pad_bytes, msdu_len); + if (ret) { + ath11k_warn(ar->ab, + "failed to coalesce msdu rx buffer%d\n", ret); + goto free_out; + } + } + __skb_queue_tail(amsdu_list, msdu); + + /* Should we also consider msdu_cnt from mpdu_meta while + * preparing amsdu list? + */ + if (rxcb->is_last_msdu) + break; + } while ((msdu = skb_peek(msdu_list)) != NULL); + + return 0; + +free_out: + dev_kfree_skb_any(msdu); + __skb_queue_purge(amsdu_list); + + return ret; +} + +static void ath11k_dp_rx_h_csum_offload(struct sk_buff *msdu) +{ + struct ath11k_skb_rxcb *rxcb = ATH11K_SKB_RXCB(msdu); + bool ip_csum_fail, l4_csum_fail; + + ip_csum_fail = ath11k_dp_rx_h_attn_ip_cksum_fail(rxcb->rx_desc); + l4_csum_fail = ath11k_dp_rx_h_attn_l4_cksum_fail(rxcb->rx_desc); + + msdu->ip_summed = (ip_csum_fail || l4_csum_fail) ? + CHECKSUM_NONE : CHECKSUM_UNNECESSARY; +} + +static int ath11k_dp_rx_crypto_mic_len(struct ath11k *ar, + enum hal_encrypt_type enctype) +{ + switch (enctype) { + case HAL_ENCRYPT_TYPE_OPEN: + case HAL_ENCRYPT_TYPE_TKIP_NO_MIC: + case HAL_ENCRYPT_TYPE_TKIP_MIC: + return 0; + case HAL_ENCRYPT_TYPE_CCMP_128: + return IEEE80211_CCMP_MIC_LEN; + case HAL_ENCRYPT_TYPE_CCMP_256: + return IEEE80211_CCMP_256_MIC_LEN; + case HAL_ENCRYPT_TYPE_GCMP_128: + case HAL_ENCRYPT_TYPE_AES_GCMP_256: + return IEEE80211_GCMP_MIC_LEN; + case HAL_ENCRYPT_TYPE_WEP_40: + case HAL_ENCRYPT_TYPE_WEP_104: + case HAL_ENCRYPT_TYPE_WEP_128: + case HAL_ENCRYPT_TYPE_WAPI_GCM_SM4: + case HAL_ENCRYPT_TYPE_WAPI: + break; + } + + ath11k_warn(ar->ab, "unsupported encryption type %d for mic len\n", enctype); + return 0; +} + +static int ath11k_dp_rx_crypto_param_len(struct ath11k *ar, + enum hal_encrypt_type enctype) +{ + switch (enctype) { + case HAL_ENCRYPT_TYPE_OPEN: + return 0; + case HAL_ENCRYPT_TYPE_TKIP_NO_MIC: + case HAL_ENCRYPT_TYPE_TKIP_MIC: + return IEEE80211_TKIP_IV_LEN; + case HAL_ENCRYPT_TYPE_CCMP_128: + return IEEE80211_CCMP_HDR_LEN; + case HAL_ENCRYPT_TYPE_CCMP_256: + return IEEE80211_CCMP_256_HDR_LEN; + case HAL_ENCRYPT_TYPE_GCMP_128: + case HAL_ENCRYPT_TYPE_AES_GCMP_256: + return IEEE80211_GCMP_HDR_LEN; + case HAL_ENCRYPT_TYPE_WEP_40: + case HAL_ENCRYPT_TYPE_WEP_104: + case HAL_ENCRYPT_TYPE_WEP_128: + case HAL_ENCRYPT_TYPE_WAPI_GCM_SM4: + case HAL_ENCRYPT_TYPE_WAPI: + break; + } + + ath11k_warn(ar->ab, "unsupported encryption type %d\n", enctype); + return 0; +} + +static int ath11k_dp_rx_crypto_icv_len(struct ath11k *ar, + enum hal_encrypt_type enctype) +{ + switch (enctype) { + case HAL_ENCRYPT_TYPE_OPEN: + case HAL_ENCRYPT_TYPE_CCMP_128: + case HAL_ENCRYPT_TYPE_CCMP_256: + case HAL_ENCRYPT_TYPE_GCMP_128: + case HAL_ENCRYPT_TYPE_AES_GCMP_256: + return 0; + case HAL_ENCRYPT_TYPE_TKIP_NO_MIC: + case HAL_ENCRYPT_TYPE_TKIP_MIC: + return IEEE80211_TKIP_ICV_LEN; + case HAL_ENCRYPT_TYPE_WEP_40: + case HAL_ENCRYPT_TYPE_WEP_104: + case HAL_ENCRYPT_TYPE_WEP_128: + case HAL_ENCRYPT_TYPE_WAPI_GCM_SM4: + case HAL_ENCRYPT_TYPE_WAPI: + break; + } + + ath11k_warn(ar->ab, "unsupported encryption type %d\n", enctype); + return 0; +} + +static void ath11k_dp_rx_h_undecap_nwifi(struct ath11k *ar, + struct sk_buff *msdu, + u8 *first_hdr, + enum hal_encrypt_type enctype, + struct ieee80211_rx_status *status) +{ + struct ieee80211_hdr *hdr; + size_t hdr_len; + u8 da[ETH_ALEN]; + u8 sa[ETH_ALEN]; + + /* pull decapped header and copy SA & DA */ + hdr = (struct ieee80211_hdr *)msdu->data; + ether_addr_copy(da, ieee80211_get_DA(hdr)); + ether_addr_copy(sa, ieee80211_get_SA(hdr)); + skb_pull(msdu, ieee80211_hdrlen(hdr->frame_control)); + + /* push original 802.11 header */ + hdr = (struct ieee80211_hdr *)first_hdr; + hdr_len = ieee80211_hdrlen(hdr->frame_control); + + if (!(status->flag & RX_FLAG_IV_STRIPPED)) { + memcpy(skb_push(msdu, + ath11k_dp_rx_crypto_param_len(ar, enctype)), + (void *)hdr + hdr_len, + ath11k_dp_rx_crypto_param_len(ar, enctype)); + } + + memcpy(skb_push(msdu, hdr_len), hdr, hdr_len); + + /* original 802.11 header has a different DA and in + * case of 4addr it may also have different SA + */ + hdr = (struct ieee80211_hdr *)msdu->data; + ether_addr_copy(ieee80211_get_DA(hdr), da); + ether_addr_copy(ieee80211_get_SA(hdr), sa); +} + +static void ath11k_dp_rx_h_undecap_raw(struct ath11k *ar, struct sk_buff *msdu, + enum hal_encrypt_type enctype, + struct ieee80211_rx_status *status, + bool decrypted) +{ + struct ath11k_skb_rxcb *rxcb = ATH11K_SKB_RXCB(msdu); + struct ieee80211_hdr *hdr; + size_t hdr_len; + size_t crypto_len; + + if (!rxcb->is_first_msdu || + !(rxcb->is_first_msdu && rxcb->is_last_msdu)) { + WARN_ON_ONCE(1); + return; + } + + skb_trim(msdu, msdu->len - FCS_LEN); + + if (!decrypted) + return; + + hdr = (void *)msdu->data; + + /* Tail */ + if (status->flag & RX_FLAG_IV_STRIPPED) { + skb_trim(msdu, msdu->len - + ath11k_dp_rx_crypto_mic_len(ar, enctype)); + + skb_trim(msdu, msdu->len - + ath11k_dp_rx_crypto_icv_len(ar, enctype)); + } else { + /* MIC */ + if (status->flag & RX_FLAG_MIC_STRIPPED) + skb_trim(msdu, msdu->len - + ath11k_dp_rx_crypto_mic_len(ar, enctype)); + + /* ICV */ + if (status->flag & RX_FLAG_ICV_STRIPPED) + skb_trim(msdu, msdu->len - + ath11k_dp_rx_crypto_icv_len(ar, enctype)); + } + + /* MMIC */ + if ((status->flag & RX_FLAG_MMIC_STRIPPED) && + !ieee80211_has_morefrags(hdr->frame_control) && + enctype == HAL_ENCRYPT_TYPE_TKIP_MIC) + skb_trim(msdu, msdu->len - IEEE80211_CCMP_MIC_LEN); + + /* Head */ + if (status->flag & RX_FLAG_IV_STRIPPED) { + hdr_len = ieee80211_hdrlen(hdr->frame_control); + crypto_len = ath11k_dp_rx_crypto_param_len(ar, enctype); + + memmove((void *)msdu->data + crypto_len, + (void *)msdu->data, hdr_len); + skb_pull(msdu, crypto_len); + } +} + +static void *ath11k_dp_rx_h_find_rfc1042(struct ath11k *ar, + struct sk_buff *msdu, + enum hal_encrypt_type enctype) +{ + struct ath11k_skb_rxcb *rxcb = ATH11K_SKB_RXCB(msdu); + struct ieee80211_hdr *hdr; + size_t hdr_len, crypto_len; + void *rfc1042; + bool is_amsdu; + + is_amsdu = !(rxcb->is_first_msdu && rxcb->is_last_msdu); + hdr = (struct ieee80211_hdr *)ath11k_dp_rx_h_80211_hdr(rxcb->rx_desc); + rfc1042 = hdr; + + if (rxcb->is_first_msdu) { + hdr_len = ieee80211_hdrlen(hdr->frame_control); + crypto_len = ath11k_dp_rx_crypto_param_len(ar, enctype); + + rfc1042 += hdr_len + crypto_len; + } + + if (is_amsdu) + rfc1042 += sizeof(struct ath11k_dp_amsdu_subframe_hdr); + + return rfc1042; +} + +static void ath11k_dp_rx_h_undecap_eth(struct ath11k *ar, + struct sk_buff *msdu, + u8 *first_hdr, + enum hal_encrypt_type enctype, + struct ieee80211_rx_status *status) +{ + struct ieee80211_hdr *hdr; + struct ethhdr *eth; + size_t hdr_len; + u8 da[ETH_ALEN]; + u8 sa[ETH_ALEN]; + void *rfc1042; + + rfc1042 = ath11k_dp_rx_h_find_rfc1042(ar, msdu, enctype); + if (WARN_ON_ONCE(!rfc1042)) + return; + + /* pull decapped header and copy SA & DA */ + eth = (struct ethhdr *)msdu->data; + ether_addr_copy(da, eth->h_dest); + ether_addr_copy(sa, eth->h_source); + skb_pull(msdu, sizeof(struct ethhdr)); + + /* push rfc1042/llc/snap */ + memcpy(skb_push(msdu, sizeof(struct ath11k_dp_rfc1042_hdr)), rfc1042, + sizeof(struct ath11k_dp_rfc1042_hdr)); + + /* push original 802.11 header */ + hdr = (struct ieee80211_hdr *)first_hdr; + hdr_len = ieee80211_hdrlen(hdr->frame_control); + + if (!(status->flag & RX_FLAG_IV_STRIPPED)) { + memcpy(skb_push(msdu, + ath11k_dp_rx_crypto_param_len(ar, enctype)), + (void *)hdr + hdr_len, + ath11k_dp_rx_crypto_param_len(ar, enctype)); + } + + memcpy(skb_push(msdu, hdr_len), hdr, hdr_len); + + /* original 802.11 header has a different DA and in + * case of 4addr it may also have different SA + */ + hdr = (struct ieee80211_hdr *)msdu->data; + ether_addr_copy(ieee80211_get_DA(hdr), da); + ether_addr_copy(ieee80211_get_SA(hdr), sa); +} + +static void ath11k_dp_rx_h_undecap(struct ath11k *ar, struct sk_buff *msdu, + struct hal_rx_desc *rx_desc, + enum hal_encrypt_type enctype, + struct ieee80211_rx_status *status, + bool decrypted) +{ + u8 *first_hdr; + u8 decap; + + first_hdr = ath11k_dp_rx_h_80211_hdr(rx_desc); + decap = ath11k_dp_rx_h_mpdu_start_decap_type(rx_desc); + + switch (decap) { + case DP_RX_DECAP_TYPE_NATIVE_WIFI: + ath11k_dp_rx_h_undecap_nwifi(ar, msdu, first_hdr, + enctype, status); + break; + case DP_RX_DECAP_TYPE_RAW: + ath11k_dp_rx_h_undecap_raw(ar, msdu, enctype, status, + decrypted); + break; + case DP_RX_DECAP_TYPE_ETHERNET2_DIX: + ath11k_dp_rx_h_undecap_eth(ar, msdu, first_hdr, + enctype, status); + break; + case DP_RX_DECAP_TYPE_8023: + /* TODO: Handle undecap for these formats */ + break; + } +} + +static void ath11k_dp_rx_h_mpdu(struct ath11k *ar, + struct sk_buff_head *amsdu_list, + struct hal_rx_desc *rx_desc, + struct ieee80211_rx_status *rx_status) +{ + struct ieee80211_hdr *hdr; + enum hal_encrypt_type enctype; + struct sk_buff *last_msdu; + struct sk_buff *msdu; + struct ath11k_skb_rxcb *last_rxcb; + bool is_decrypted; + u32 err_bitmap; + u8 *qos; + + if (skb_queue_empty(amsdu_list)) + return; + + hdr = (struct ieee80211_hdr *)ath11k_dp_rx_h_80211_hdr(rx_desc); + + /* Each A-MSDU subframe will use the original header as the base and be + * reported as a separate MSDU so strip the A-MSDU bit from QoS Ctl. + */ + if (ieee80211_is_data_qos(hdr->frame_control)) { + qos = ieee80211_get_qos_ctl(hdr); + qos[0] &= ~IEEE80211_QOS_CTL_A_MSDU_PRESENT; + } + + is_decrypted = ath11k_dp_rx_h_attn_is_decrypted(rx_desc); + enctype = ath11k_dp_rx_h_mpdu_start_enctype(rx_desc); + + /* Some attention flags are valid only in the last MSDU. */ + last_msdu = skb_peek_tail(amsdu_list); + last_rxcb = ATH11K_SKB_RXCB(last_msdu); + + err_bitmap = ath11k_dp_rx_h_attn_mpdu_err(last_rxcb->rx_desc); + + /* Clear per-MPDU flags while leaving per-PPDU flags intact. */ + rx_status->flag &= ~(RX_FLAG_FAILED_FCS_CRC | + RX_FLAG_MMIC_ERROR | + RX_FLAG_DECRYPTED | + RX_FLAG_IV_STRIPPED | + RX_FLAG_MMIC_STRIPPED); + + if (err_bitmap & DP_RX_MPDU_ERR_FCS) + rx_status->flag |= RX_FLAG_FAILED_FCS_CRC; + + if (err_bitmap & DP_RX_MPDU_ERR_TKIP_MIC) + rx_status->flag |= RX_FLAG_MMIC_ERROR; + + if (is_decrypted) + rx_status->flag |= RX_FLAG_DECRYPTED | RX_FLAG_MMIC_STRIPPED | + RX_FLAG_MIC_STRIPPED | RX_FLAG_ICV_STRIPPED; + + skb_queue_walk(amsdu_list, msdu) { + ath11k_dp_rx_h_csum_offload(msdu); + ath11k_dp_rx_h_undecap(ar, msdu, rx_desc, + enctype, rx_status, is_decrypted); + } +} + +static void ath11k_dp_rx_h_rate(struct ath11k *ar, struct hal_rx_desc *rx_desc, + struct ieee80211_rx_status *rx_status) +{ + struct ieee80211_supported_band *sband; + enum rx_msdu_start_pkt_type pkt_type; + u8 bw; + u8 rate_mcs, nss; + u8 sgi; + bool is_cck; + + pkt_type = ath11k_dp_rx_h_msdu_start_pkt_type(rx_desc); + bw = ath11k_dp_rx_h_msdu_start_rx_bw(rx_desc); + rate_mcs = ath11k_dp_rx_h_msdu_start_rate_mcs(rx_desc); + nss = ath11k_dp_rx_h_msdu_start_nss(rx_desc); + sgi = ath11k_dp_rx_h_msdu_start_sgi(rx_desc); + + switch (pkt_type) { + case RX_MSDU_START_PKT_TYPE_11A: + case RX_MSDU_START_PKT_TYPE_11B: + is_cck = (pkt_type == RX_MSDU_START_PKT_TYPE_11B); + sband = &ar->mac.sbands[rx_status->band]; + rx_status->rate_idx = ath11k_mac_hw_rate_to_idx(sband, rate_mcs, + is_cck); + break; + case RX_MSDU_START_PKT_TYPE_11N: + rx_status->encoding = RX_ENC_HT; + if (rate_mcs > ATH11K_HT_MCS_MAX) { + ath11k_warn(ar->ab, + "Received with invalid mcs in HT mode %d\n", + rate_mcs); + break; + } + rx_status->rate_idx = rate_mcs + (8 * (nss - 1)); + if (sgi) + rx_status->enc_flags |= RX_ENC_FLAG_SHORT_GI; + rx_status->bw = ath11k_bw_to_mac80211_bw(bw); + break; + case RX_MSDU_START_PKT_TYPE_11AC: + rx_status->encoding = RX_ENC_VHT; + rx_status->rate_idx = rate_mcs; + if (rate_mcs > ATH11K_VHT_MCS_MAX) { + ath11k_warn(ar->ab, + "Received with invalid mcs in VHT mode %d\n", + rate_mcs); + break; + } + rx_status->nss = nss; + if (sgi) + rx_status->enc_flags |= RX_ENC_FLAG_SHORT_GI; + rx_status->bw = ath11k_bw_to_mac80211_bw(bw); + break; + case RX_MSDU_START_PKT_TYPE_11AX: + rx_status->rate_idx = rate_mcs; + if (rate_mcs > ATH11K_HE_MCS_MAX) { + ath11k_warn(ar->ab, + "Received with invalid mcs in HE mode %d\n", + rate_mcs); + break; + } + rx_status->encoding = RX_ENC_HE; + rx_status->nss = nss; + rx_status->bw = ath11k_bw_to_mac80211_bw(bw); + break; + } +} + +static void ath11k_dp_rx_h_ppdu(struct ath11k *ar, struct hal_rx_desc *rx_desc, + struct ieee80211_rx_status *rx_status) +{ + u8 channel_num; + + rx_status->freq = 0; + rx_status->rate_idx = 0; + rx_status->nss = 0; + rx_status->encoding = RX_ENC_LEGACY; + rx_status->bw = RATE_INFO_BW_20; + + rx_status->flag |= RX_FLAG_NO_SIGNAL_VAL; + + channel_num = ath11k_dp_rx_h_msdu_start_freq(rx_desc); + + if (channel_num >= 1 && channel_num <= 14) { + rx_status->band = NL80211_BAND_2GHZ; + } else if (channel_num >= 36 && channel_num <= 173) { + rx_status->band = NL80211_BAND_5GHZ; + } else { + ath11k_warn(ar->ab, "Unsupported Channel info received %d\n", + channel_num); + return; + } + + rx_status->freq = ieee80211_channel_to_frequency(channel_num, + rx_status->band); + + ath11k_dp_rx_h_rate(ar, rx_desc, rx_status); +} + +static void ath11k_dp_rx_process_amsdu(struct ath11k *ar, + struct sk_buff_head *amsdu_list, + struct ieee80211_rx_status *rx_status) +{ + struct sk_buff *first; + struct ath11k_skb_rxcb *rxcb; + struct hal_rx_desc *rx_desc; + bool first_mpdu; + + if (skb_queue_empty(amsdu_list)) + return; + + first = skb_peek(amsdu_list); + rxcb = ATH11K_SKB_RXCB(first); + rx_desc = rxcb->rx_desc; + + first_mpdu = ath11k_dp_rx_h_attn_first_mpdu(rx_desc); + if (first_mpdu) + ath11k_dp_rx_h_ppdu(ar, rx_desc, rx_status); + + ath11k_dp_rx_h_mpdu(ar, amsdu_list, rx_desc, rx_status); +} + +static char *ath11k_print_get_tid(struct ieee80211_hdr *hdr, char *out, + size_t size) +{ + u8 *qc; + int tid; + + if (!ieee80211_is_data_qos(hdr->frame_control)) + return ""; + + qc = ieee80211_get_qos_ctl(hdr); + tid = *qc & IEEE80211_QOS_CTL_TID_MASK; + snprintf(out, size, "tid %d", tid); + + return out; +} + +static void ath11k_dp_rx_deliver_msdu(struct ath11k *ar, struct napi_struct *napi, + struct sk_buff *msdu) +{ + struct ieee80211_rx_status *status; + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)msdu->data; + char tid[32]; + + status = IEEE80211_SKB_RXCB(msdu); + + ath11k_dbg(ar->ab, ATH11K_DBG_DATA, + "rx skb %pK len %u peer %pM %s %s sn %u %s%s%s%s%s%s%s %srate_idx %u vht_nss %u freq %u band %u flag 0x%x fcs-err %i mic-err %i amsdu-more %i\n", + msdu, + msdu->len, + ieee80211_get_SA(hdr), + ath11k_print_get_tid(hdr, tid, sizeof(tid)), + is_multicast_ether_addr(ieee80211_get_DA(hdr)) ? + "mcast" : "ucast", + (__le16_to_cpu(hdr->seq_ctrl) & IEEE80211_SCTL_SEQ) >> 4, + (status->encoding == RX_ENC_LEGACY) ? "legacy" : "", + (status->encoding == RX_ENC_HT) ? "ht" : "", + (status->encoding == RX_ENC_VHT) ? "vht" : "", + (status->encoding == RX_ENC_HE) ? "he" : "", + (status->bw == RATE_INFO_BW_40) ? "40" : "", + (status->bw == RATE_INFO_BW_80) ? "80" : "", + (status->bw == RATE_INFO_BW_160) ? "160" : "", + status->enc_flags & RX_ENC_FLAG_SHORT_GI ? "sgi " : "", + status->rate_idx, + status->nss, + status->freq, + status->band, status->flag, + !!(status->flag & RX_FLAG_FAILED_FCS_CRC), + !!(status->flag & RX_FLAG_MMIC_ERROR), + !!(status->flag & RX_FLAG_AMSDU_MORE)); + + /* TODO: trace rx packet */ + + ieee80211_rx_napi(ar->hw, NULL, msdu, napi); +} + +static void ath11k_dp_rx_pre_deliver_amsdu(struct ath11k *ar, + struct sk_buff_head *amsdu_list, + struct ieee80211_rx_status *rxs) +{ + struct sk_buff *msdu; + struct sk_buff *first_subframe; + struct ieee80211_rx_status *status; + + first_subframe = skb_peek(amsdu_list); + + skb_queue_walk(amsdu_list, msdu) { + /* Setup per-MSDU flags */ + if (skb_queue_empty(amsdu_list)) + rxs->flag &= ~RX_FLAG_AMSDU_MORE; + else + rxs->flag |= RX_FLAG_AMSDU_MORE; + + if (msdu == first_subframe) { + first_subframe = NULL; + rxs->flag &= ~RX_FLAG_ALLOW_SAME_PN; + } else { + rxs->flag |= RX_FLAG_ALLOW_SAME_PN; + } + rxs->flag |= RX_FLAG_SKIP_MONITOR; + + status = IEEE80211_SKB_RXCB(msdu); + *status = *rxs; + } +} + +static void ath11k_dp_rx_process_pending_packets(struct ath11k_base *ab, + struct napi_struct *napi, + struct sk_buff_head *pending_q, + int *quota, u8 mac_id) +{ + struct ath11k *ar; + struct sk_buff *msdu; + struct ath11k_pdev *pdev; + + if (skb_queue_empty(pending_q)) + return; + + ar = ab->pdevs[mac_id].ar; + + rcu_read_lock(); + pdev = rcu_dereference(ab->pdevs_active[mac_id]); + + while (*quota && (msdu = __skb_dequeue(pending_q))) { + if (!pdev) { + dev_kfree_skb_any(msdu); + continue; + } + + ath11k_dp_rx_deliver_msdu(ar, napi, msdu); + (*quota)--; + } + rcu_read_unlock(); +} + +int ath11k_dp_process_rx(struct ath11k_base *ab, int mac_id, + struct napi_struct *napi, struct sk_buff_head *pending_q, + int budget) +{ + struct ath11k *ar = ab->pdevs[mac_id].ar; + struct ath11k_pdev_dp *dp = &ar->dp; + struct ieee80211_rx_status *rx_status = &dp->rx_status; + struct dp_rxdma_ring *rx_ring = &dp->rx_refill_buf_ring; + struct hal_srng *srng; + struct hal_rx_meta_info meta_info; + struct sk_buff *msdu; + struct sk_buff_head msdu_list; + struct sk_buff_head amsdu_list; + struct ath11k_skb_rxcb *rxcb; + u32 *rx_desc; + int buf_id; + int num_buffs_reaped = 0; + int quota = budget; + int ret; + bool done = false; + + /* Process any pending packets from the previous napi poll. + * Note: All msdu's in this pending_q corresponds to the same mac id + * due to pdev based reo dest mapping and also since each irq group id + * maps to specific reo dest ring. + */ + ath11k_dp_rx_process_pending_packets(ab, napi, pending_q, "a, + mac_id); + + /* If all quota is exhausted by processing the pending_q, + * Wait for the next napi poll to reap the new info + */ + if (!quota) + goto exit; + + __skb_queue_head_init(&msdu_list); + + srng = &ab->hal.srng_list[dp->reo_dst_ring.ring_id]; + + spin_lock_bh(&srng->lock); + + ath11k_hal_srng_access_begin(ab, srng); + +try_again: + while ((rx_desc = ath11k_hal_srng_dst_get_next_entry(ab, srng))) { + memset(&meta_info, 0, sizeof(meta_info)); + ath11k_hal_rx_parse_dst_ring_desc(ab, rx_desc, &meta_info); + + buf_id = FIELD_GET(DP_RXDMA_BUF_COOKIE_BUF_ID, + meta_info.msdu_meta.cookie); + spin_lock_bh(&rx_ring->idr_lock); + msdu = idr_find(&rx_ring->bufs_idr, buf_id); + if (!msdu) { + ath11k_warn(ab, "frame rx with invalid buf_id %d\n", + buf_id); + spin_unlock_bh(&rx_ring->idr_lock); + continue; + } + + idr_remove(&rx_ring->bufs_idr, buf_id); + spin_unlock_bh(&rx_ring->idr_lock); + + rxcb = ATH11K_SKB_RXCB(msdu); + dma_unmap_single(ab->dev, rxcb->paddr, + msdu->len + skb_tailroom(msdu), + DMA_FROM_DEVICE); + + num_buffs_reaped++; + + if (meta_info.push_reason != + HAL_REO_DEST_RING_PUSH_REASON_ROUTING_INSTRUCTION) { + /* TODO: Check if the msdu can be sent up for processing */ + dev_kfree_skb_any(msdu); + ab->soc_stats.hal_reo_error[dp->reo_dst_ring.ring_id]++; + continue; + } + + rxcb->is_first_msdu = meta_info.msdu_meta.first; + rxcb->is_last_msdu = meta_info.msdu_meta.last; + rxcb->is_continuation = meta_info.msdu_meta.continuation; + rxcb->mac_id = mac_id; + __skb_queue_tail(&msdu_list, msdu); + + /* Stop reaping from the ring once quota is exhausted + * and we've received all msdu's in the the AMSDU. The + * additional msdu's reaped in excess of quota here would + * be pushed into the pending queue to be processed during + * the next napi poll. + * Note: More profiling can be done to see the impact on + * pending_q and throughput during various traffic & density + * and how use of budget instead of remaining quota affects it. + */ + if (num_buffs_reaped >= quota && rxcb->is_last_msdu && + !rxcb->is_continuation) { + done = true; + break; + } + } + + /* Hw might have updated the head pointer after we cached it. + * In this case, even though there are entries in the ring we'll + * get rx_desc NULL. Give the read another try with updated cached + * head pointer so that we can reap complete MPDU in the current + * rx processing. + */ + if (!done && ath11k_hal_srng_dst_num_free(ab, srng, true)) { + ath11k_hal_srng_access_end(ab, srng); + goto try_again; + } + + ath11k_hal_srng_access_end(ab, srng); + + spin_unlock_bh(&srng->lock); + + if (!num_buffs_reaped) + goto exit; + + /* Should we reschedule it later if we are not able to replenish all + * the buffers? + */ + ath11k_dp_rxbufs_replenish(ab, mac_id, rx_ring, num_buffs_reaped, + HAL_RX_BUF_RBM_SW3_BM, GFP_ATOMIC); + + rcu_read_lock(); + if (!rcu_dereference(ab->pdevs_active[mac_id])) { + __skb_queue_purge(&msdu_list); + goto rcu_unlock; + } + + if (test_bit(ATH11K_CAC_RUNNING, &ar->dev_flags)) { + __skb_queue_purge(&msdu_list); + goto rcu_unlock; + } + + while (!skb_queue_empty(&msdu_list)) { + __skb_queue_head_init(&amsdu_list); + ret = ath11k_dp_rx_retrieve_amsdu(ar, &msdu_list, &amsdu_list); + if (ret) { + if (ret == -EIO) { + ath11k_err(ab, "rx ring got corrupted %d\n", ret); + __skb_queue_purge(&msdu_list); + /* Should stop processing any more rx in + * future from this ring? + */ + goto rcu_unlock; + } + + /* A-MSDU retrieval got failed due to non-fatal condition, + * continue processing with the next msdu. + */ + continue; + } + + ath11k_dp_rx_process_amsdu(ar, &amsdu_list, rx_status); + + ath11k_dp_rx_pre_deliver_amsdu(ar, &amsdu_list, rx_status); + skb_queue_splice_tail(&amsdu_list, pending_q); + } + + while (quota && (msdu = __skb_dequeue(pending_q))) { + ath11k_dp_rx_deliver_msdu(ar, napi, msdu); + quota--; + } + +rcu_unlock: + rcu_read_unlock(); +exit: + return budget - quota; +} + +static void ath11k_dp_rx_update_peer_stats(struct ath11k_sta *arsta, + struct hal_rx_mon_ppdu_info *ppdu_info) +{ + struct ath11k_rx_peer_stats *rx_stats = arsta->rx_stats; + u32 num_msdu; + + if (!rx_stats) + return; + + num_msdu = ppdu_info->tcp_msdu_count + ppdu_info->tcp_ack_msdu_count + + ppdu_info->udp_msdu_count + ppdu_info->other_msdu_count; + + rx_stats->num_msdu += num_msdu; + rx_stats->tcp_msdu_count += ppdu_info->tcp_msdu_count + + ppdu_info->tcp_ack_msdu_count; + rx_stats->udp_msdu_count += ppdu_info->udp_msdu_count; + rx_stats->other_msdu_count += ppdu_info->other_msdu_count; + + if (ppdu_info->preamble_type == HAL_RX_PREAMBLE_11A || + ppdu_info->preamble_type == HAL_RX_PREAMBLE_11B) { + ppdu_info->nss = 1; + ppdu_info->mcs = HAL_RX_MAX_MCS; + ppdu_info->tid = IEEE80211_NUM_TIDS; + } + + if (ppdu_info->nss > 0 && ppdu_info->nss <= HAL_RX_MAX_NSS) + rx_stats->nss_count[ppdu_info->nss - 1] += num_msdu; + + if (ppdu_info->mcs <= HAL_RX_MAX_MCS) + rx_stats->mcs_count[ppdu_info->mcs] += num_msdu; + + if (ppdu_info->gi < HAL_RX_GI_MAX) + rx_stats->gi_count[ppdu_info->gi] += num_msdu; + + if (ppdu_info->bw < HAL_RX_BW_MAX) + rx_stats->bw_count[ppdu_info->bw] += num_msdu; + + if (ppdu_info->ldpc < HAL_RX_SU_MU_CODING_MAX) + rx_stats->coding_count[ppdu_info->ldpc] += num_msdu; + + if (ppdu_info->tid <= IEEE80211_NUM_TIDS) + rx_stats->tid_count[ppdu_info->tid] += num_msdu; + + if (ppdu_info->preamble_type < HAL_RX_PREAMBLE_MAX) + rx_stats->pream_cnt[ppdu_info->preamble_type] += num_msdu; + + if (ppdu_info->reception_type < HAL_RX_RECEPTION_TYPE_MAX) + rx_stats->reception_type[ppdu_info->reception_type] += num_msdu; + + if (ppdu_info->is_stbc) + rx_stats->stbc_count += num_msdu; + + if (ppdu_info->beamformed) + rx_stats->beamformed_count += num_msdu; + + if (ppdu_info->num_mpdu_fcs_ok > 1) + rx_stats->ampdu_msdu_count += num_msdu; + else + rx_stats->non_ampdu_msdu_count += num_msdu; + + rx_stats->num_mpdu_fcs_ok += ppdu_info->num_mpdu_fcs_ok; + rx_stats->num_mpdu_fcs_err += ppdu_info->num_mpdu_fcs_err; + + arsta->rssi_comb = ppdu_info->rssi_comb; + rx_stats->rx_duration += ppdu_info->rx_duration; + arsta->rx_duration = rx_stats->rx_duration; +} + +static struct sk_buff *ath11k_dp_rx_alloc_mon_status_buf(struct ath11k_base *ab, + struct dp_rxdma_ring *rx_ring, + int *buf_id, gfp_t gfp) +{ + struct sk_buff *skb; + dma_addr_t paddr; + + skb = dev_alloc_skb(DP_RX_BUFFER_SIZE + + DP_RX_BUFFER_ALIGN_SIZE); + + if (!skb) + goto fail_alloc_skb; + + if (!IS_ALIGNED((unsigned long)skb->data, + DP_RX_BUFFER_ALIGN_SIZE)) { + skb_pull(skb, PTR_ALIGN(skb->data, DP_RX_BUFFER_ALIGN_SIZE) - + skb->data); + } + + paddr = dma_map_single(ab->dev, skb->data, + skb->len + skb_tailroom(skb), + DMA_BIDIRECTIONAL); + if (unlikely(dma_mapping_error(ab->dev, paddr))) + goto fail_free_skb; + + spin_lock_bh(&rx_ring->idr_lock); + *buf_id = idr_alloc(&rx_ring->bufs_idr, skb, 0, + rx_ring->bufs_max, gfp); + spin_unlock_bh(&rx_ring->idr_lock); + if (*buf_id < 0) + goto fail_dma_unmap; + + ATH11K_SKB_RXCB(skb)->paddr = paddr; + return skb; + +fail_dma_unmap: + dma_unmap_single(ab->dev, paddr, skb->len + skb_tailroom(skb), + DMA_BIDIRECTIONAL); +fail_free_skb: + dev_kfree_skb_any(skb); +fail_alloc_skb: + return NULL; +} + +int ath11k_dp_rx_mon_status_bufs_replenish(struct ath11k_base *ab, int mac_id, + struct dp_rxdma_ring *rx_ring, + int req_entries, + enum hal_rx_buf_return_buf_manager mgr, + gfp_t gfp) +{ + struct hal_srng *srng; + u32 *desc; + struct sk_buff *skb; + int num_free; + int num_remain; + int buf_id; + u32 cookie; + dma_addr_t paddr; + + req_entries = min(req_entries, rx_ring->bufs_max); + + srng = &ab->hal.srng_list[rx_ring->refill_buf_ring.ring_id]; + + spin_lock_bh(&srng->lock); + + ath11k_hal_srng_access_begin(ab, srng); + + num_free = ath11k_hal_srng_src_num_free(ab, srng, true); + + req_entries = min(num_free, req_entries); + num_remain = req_entries; + + while (num_remain > 0) { + skb = ath11k_dp_rx_alloc_mon_status_buf(ab, rx_ring, + &buf_id, gfp); + if (!skb) + break; + paddr = ATH11K_SKB_RXCB(skb)->paddr; + + desc = ath11k_hal_srng_src_get_next_entry(ab, srng); + if (!desc) + goto fail_desc_get; + + cookie = FIELD_PREP(DP_RXDMA_BUF_COOKIE_PDEV_ID, mac_id) | + FIELD_PREP(DP_RXDMA_BUF_COOKIE_BUF_ID, buf_id); + + num_remain--; + + ath11k_hal_rx_buf_addr_info_set(desc, paddr, cookie, mgr); + } + + ath11k_hal_srng_access_end(ab, srng); + + spin_unlock_bh(&srng->lock); + + return req_entries - num_remain; + +fail_desc_get: + spin_lock_bh(&rx_ring->idr_lock); + idr_remove(&rx_ring->bufs_idr, buf_id); + spin_unlock_bh(&rx_ring->idr_lock); + dma_unmap_single(ab->dev, paddr, skb->len + skb_tailroom(skb), + DMA_BIDIRECTIONAL); + dev_kfree_skb_any(skb); + ath11k_hal_srng_access_end(ab, srng); + spin_unlock_bh(&srng->lock); + + return req_entries - num_remain; +} + +static int ath11k_dp_rx_reap_mon_status_ring(struct ath11k_base *ab, int mac_id, + int *budget, struct sk_buff_head *skb_list) +{ + struct ath11k *ar = ab->pdevs[mac_id].ar; + struct ath11k_pdev_dp *dp = &ar->dp; + struct dp_rxdma_ring *rx_ring = &dp->rx_mon_status_refill_ring; + struct hal_srng *srng; + void *rx_mon_status_desc; + struct sk_buff *skb; + struct ath11k_skb_rxcb *rxcb; + struct hal_tlv_hdr *tlv; + u32 cookie; + int buf_id; + dma_addr_t paddr; + u8 rbm; + int num_buffs_reaped = 0; + + srng = &ab->hal.srng_list[rx_ring->refill_buf_ring.ring_id]; + + spin_lock_bh(&srng->lock); + + ath11k_hal_srng_access_begin(ab, srng); + while (*budget) { + *budget -= 1; + rx_mon_status_desc = + ath11k_hal_srng_src_peek(ab, srng); + if (!rx_mon_status_desc) + break; + + ath11k_hal_rx_buf_addr_info_get(rx_mon_status_desc, &paddr, + &cookie, &rbm); + if (paddr) { + buf_id = FIELD_GET(DP_RXDMA_BUF_COOKIE_BUF_ID, cookie); + + spin_lock_bh(&rx_ring->idr_lock); + skb = idr_find(&rx_ring->bufs_idr, buf_id); + if (!skb) { + ath11k_warn(ab, "rx monitor status with invalid buf_id %d\n", + buf_id); + spin_unlock_bh(&rx_ring->idr_lock); + continue; + } + + idr_remove(&rx_ring->bufs_idr, buf_id); + spin_unlock_bh(&rx_ring->idr_lock); + + rxcb = ATH11K_SKB_RXCB(skb); + + dma_sync_single_for_cpu(ab->dev, rxcb->paddr, + skb->len + skb_tailroom(skb), + DMA_FROM_DEVICE); + + dma_unmap_single(ab->dev, rxcb->paddr, + skb->len + skb_tailroom(skb), + DMA_BIDIRECTIONAL); + + tlv = (struct hal_tlv_hdr *)skb->data; + if (FIELD_GET(HAL_TLV_HDR_TAG, tlv->tl) != + HAL_RX_STATUS_BUFFER_DONE) { + ath11k_hal_srng_src_get_next_entry(ab, srng); + continue; + } + + __skb_queue_tail(skb_list, skb); + } + + skb = ath11k_dp_rx_alloc_mon_status_buf(ab, rx_ring, + &buf_id, GFP_ATOMIC); + + if (!skb) { + ath11k_hal_rx_buf_addr_info_set(rx_mon_status_desc, 0, 0, + HAL_RX_BUF_RBM_SW3_BM); + num_buffs_reaped++; + break; + } + rxcb = ATH11K_SKB_RXCB(skb); + + cookie = FIELD_PREP(DP_RXDMA_BUF_COOKIE_PDEV_ID, mac_id) | + FIELD_PREP(DP_RXDMA_BUF_COOKIE_BUF_ID, buf_id); + + ath11k_hal_rx_buf_addr_info_set(rx_mon_status_desc, rxcb->paddr, + cookie, HAL_RX_BUF_RBM_SW3_BM); + ath11k_hal_srng_src_get_next_entry(ab, srng); + num_buffs_reaped++; + } + ath11k_hal_srng_access_end(ab, srng); + spin_unlock_bh(&srng->lock); + + return num_buffs_reaped; +} + +int ath11k_dp_rx_process_mon_status(struct ath11k_base *ab, int mac_id, + struct napi_struct *napi, int budget) +{ + struct ath11k *ar = ab->pdevs[mac_id].ar; + enum hal_rx_mon_status hal_status; + struct sk_buff *skb; + struct sk_buff_head skb_list; + struct hal_rx_mon_ppdu_info ppdu_info; + struct ath11k_peer *peer; + struct ath11k_sta *arsta; + int num_buffs_reaped = 0; + + __skb_queue_head_init(&skb_list); + + num_buffs_reaped = ath11k_dp_rx_reap_mon_status_ring(ab, mac_id, &budget, + &skb_list); + if (!num_buffs_reaped) + goto exit; + + while ((skb = __skb_dequeue(&skb_list))) { + memset(&ppdu_info, 0, sizeof(ppdu_info)); + ppdu_info.peer_id = HAL_INVALID_PEERID; + + if (ath11k_debug_is_pktlog_rx_stats_enabled(ar)) + trace_ath11k_htt_rxdesc(ar, skb->data, DP_RX_BUFFER_SIZE); + + hal_status = ath11k_hal_rx_parse_mon_status(ab, &ppdu_info, skb); + + if (ppdu_info.peer_id == HAL_INVALID_PEERID || + hal_status != HAL_RX_MON_STATUS_PPDU_DONE) { + dev_kfree_skb_any(skb); + continue; + } + + rcu_read_lock(); + spin_lock_bh(&ab->base_lock); + peer = ath11k_peer_find_by_id(ab, ppdu_info.peer_id); + + if (!peer || !peer->sta) { + ath11k_warn(ab, "failed to find the peer with peer_id %d\n", + ppdu_info.peer_id); + spin_unlock_bh(&ab->base_lock); + rcu_read_unlock(); + dev_kfree_skb_any(skb); + continue; + } + + arsta = (struct ath11k_sta *)peer->sta->drv_priv; + ath11k_dp_rx_update_peer_stats(arsta, &ppdu_info); + + if (ath11k_debug_is_pktlog_peer_valid(ar, peer->addr)) + trace_ath11k_htt_rxdesc(ar, skb->data, DP_RX_BUFFER_SIZE); + + spin_unlock_bh(&ab->base_lock); + rcu_read_unlock(); + + dev_kfree_skb_any(skb); + } +exit: + return num_buffs_reaped; +} + +static int ath11k_dp_rx_link_desc_return(struct ath11k_base *ab, + u32 *link_desc, + enum hal_wbm_rel_bm_act action) +{ + struct ath11k_dp *dp = &ab->dp; + struct hal_srng *srng; + u32 *desc; + int ret = 0; + + srng = &ab->hal.srng_list[dp->wbm_desc_rel_ring.ring_id]; + + spin_lock_bh(&srng->lock); + + ath11k_hal_srng_access_begin(ab, srng); + + desc = ath11k_hal_srng_src_get_next_entry(ab, srng); + if (!desc) { + ret = -ENOBUFS; + goto exit; + } + + ath11k_hal_rx_msdu_link_desc_set(ab, (void *)desc, (void *)link_desc, + action); + +exit: + ath11k_hal_srng_access_end(ab, srng); + + spin_unlock_bh(&srng->lock); + + return ret; +} + +static void ath11k_dp_rx_frag_h_mpdu(struct ath11k *ar, + struct sk_buff *msdu, + struct hal_rx_desc *rx_desc, + struct ieee80211_rx_status *rx_status) +{ + u8 rx_channel; + enum hal_encrypt_type enctype; + bool is_decrypted; + u32 err_bitmap; + + is_decrypted = ath11k_dp_rx_h_attn_is_decrypted(rx_desc); + enctype = ath11k_dp_rx_h_mpdu_start_enctype(rx_desc); + err_bitmap = ath11k_dp_rx_h_attn_mpdu_err(rx_desc); + + if (err_bitmap & DP_RX_MPDU_ERR_FCS) + rx_status->flag |= RX_FLAG_FAILED_FCS_CRC; + + if (err_bitmap & DP_RX_MPDU_ERR_TKIP_MIC) + rx_status->flag |= RX_FLAG_MMIC_ERROR; + + rx_status->encoding = RX_ENC_LEGACY; + rx_status->bw = RATE_INFO_BW_20; + + rx_status->flag |= RX_FLAG_NO_SIGNAL_VAL; + + rx_channel = ath11k_dp_rx_h_msdu_start_freq(rx_desc); + + if (rx_channel >= 1 && rx_channel <= 14) { + rx_status->band = NL80211_BAND_2GHZ; + } else if (rx_channel >= 36 && rx_channel <= 173) { + rx_status->band = NL80211_BAND_5GHZ; + } else { + ath11k_warn(ar->ab, "Unsupported Channel info received %d\n", + rx_channel); + return; + } + + rx_status->freq = ieee80211_channel_to_frequency(rx_channel, + rx_status->band); + ath11k_dp_rx_h_rate(ar, rx_desc, rx_status); + + /* Rx fragments are received in raw mode */ + skb_trim(msdu, msdu->len - FCS_LEN); + + if (is_decrypted) { + rx_status->flag |= RX_FLAG_DECRYPTED | RX_FLAG_MIC_STRIPPED; + skb_trim(msdu, msdu->len - + ath11k_dp_rx_crypto_mic_len(ar, enctype)); + } +} + +static int +ath11k_dp_process_rx_err_buf(struct ath11k *ar, struct napi_struct *napi, + int buf_id, bool frag) +{ + struct ath11k_pdev_dp *dp = &ar->dp; + struct dp_rxdma_ring *rx_ring = &dp->rx_refill_buf_ring; + struct ieee80211_rx_status rx_status = {0}; + struct sk_buff *msdu; + struct ath11k_skb_rxcb *rxcb; + struct ieee80211_rx_status *status; + struct hal_rx_desc *rx_desc; + u16 msdu_len; + + spin_lock_bh(&rx_ring->idr_lock); + msdu = idr_find(&rx_ring->bufs_idr, buf_id); + if (!msdu) { + ath11k_warn(ar->ab, "rx err buf with invalid buf_id %d\n", + buf_id); + spin_unlock_bh(&rx_ring->idr_lock); + return -EINVAL; + } + + idr_remove(&rx_ring->bufs_idr, buf_id); + spin_unlock_bh(&rx_ring->idr_lock); + + rxcb = ATH11K_SKB_RXCB(msdu); + dma_unmap_single(ar->ab->dev, rxcb->paddr, + msdu->len + skb_tailroom(msdu), + DMA_FROM_DEVICE); + + if (!frag) { + /* Process only rx fragments below, and drop + * msdu's indicated due to error reasons. + */ + dev_kfree_skb_any(msdu); + return 0; + } + + rcu_read_lock(); + if (!rcu_dereference(ar->ab->pdevs_active[ar->pdev_idx])) { + dev_kfree_skb_any(msdu); + goto exit; + } + + if (test_bit(ATH11K_CAC_RUNNING, &ar->dev_flags)) { + dev_kfree_skb_any(msdu); + goto exit; + } + + rx_desc = (struct hal_rx_desc *)msdu->data; + msdu_len = ath11k_dp_rx_h_msdu_start_msdu_len(rx_desc); + skb_put(msdu, HAL_RX_DESC_SIZE + msdu_len); + skb_pull(msdu, HAL_RX_DESC_SIZE); + + ath11k_dp_rx_frag_h_mpdu(ar, msdu, rx_desc, &rx_status); + + status = IEEE80211_SKB_RXCB(msdu); + + *status = rx_status; + + ath11k_dp_rx_deliver_msdu(ar, napi, msdu); + +exit: + rcu_read_unlock(); + return 0; +} + +int ath11k_dp_process_rx_err(struct ath11k_base *ab, struct napi_struct *napi, + int budget) +{ + struct hal_rx_msdu_meta meta[HAL_NUM_RX_MSDUS_PER_LINK_DESC]; + struct dp_link_desc_bank *link_desc_banks; + enum hal_rx_buf_return_buf_manager rbm; + int tot_n_bufs_reaped, quota, ret, i; + int n_bufs_reaped[MAX_RADIOS] = {0}; + struct hal_rx_meta_info meta_info; + struct dp_rxdma_ring *rx_ring; + struct dp_srng *reo_except; + u32 desc_bank, num_msdus; + struct hal_srng *srng; + struct ath11k_dp *dp; + void *link_desc_va; + int buf_id, mac_id; + struct ath11k *ar; + dma_addr_t paddr; + u32 *desc; + bool is_frag; + + tot_n_bufs_reaped = 0; + quota = budget; + + dp = &ab->dp; + reo_except = &dp->reo_except_ring; + link_desc_banks = dp->link_desc_banks; + + srng = &ab->hal.srng_list[reo_except->ring_id]; + + spin_lock_bh(&srng->lock); + + ath11k_hal_srng_access_begin(ab, srng); + + while (budget && + (desc = ath11k_hal_srng_dst_get_next_entry(ab, srng))) { + ab->soc_stats.err_ring_pkts++; + ret = ath11k_hal_desc_reo_parse_err(ab, desc, &paddr, + &desc_bank); + if (ret) { + ath11k_warn(ab, "failed to parse error reo desc %d\n", + ret); + continue; + } + link_desc_va = link_desc_banks[desc_bank].vaddr + + (paddr - link_desc_banks[desc_bank].paddr); + ath11k_hal_rx_msdu_link_info_get(link_desc_va, &num_msdus, meta, + &rbm); + if (rbm != HAL_RX_BUF_RBM_WBM_IDLE_DESC_LIST && + rbm != HAL_RX_BUF_RBM_SW3_BM) { + ab->soc_stats.invalid_rbm++; + ath11k_warn(ab, "invalid return buffer manager %d\n", rbm); + ath11k_dp_rx_link_desc_return(ab, desc, + HAL_WBM_REL_BM_ACT_REL_MSDU); + continue; + } + + memset(&meta_info, 0, sizeof(meta_info)); + ath11k_hal_rx_parse_dst_ring_desc(ab, desc, &meta_info); + + is_frag = meta_info.mpdu_meta.frag; + + /* Return the link desc back to wbm idle list */ + ath11k_dp_rx_link_desc_return(ab, desc, + HAL_WBM_REL_BM_ACT_PUT_IN_IDLE); + + for (i = 0; i < num_msdus; i++) { + buf_id = FIELD_GET(DP_RXDMA_BUF_COOKIE_BUF_ID, + meta[i].cookie); + + mac_id = FIELD_GET(DP_RXDMA_BUF_COOKIE_PDEV_ID, + meta[i].cookie); + + ar = ab->pdevs[mac_id].ar; + + if (!ath11k_dp_process_rx_err_buf(ar, napi, buf_id, + is_frag)) { + n_bufs_reaped[mac_id]++; + tot_n_bufs_reaped++; + } + } + + if (tot_n_bufs_reaped >= quota) { + tot_n_bufs_reaped = quota; + goto exit; + } + + budget = quota - tot_n_bufs_reaped; + } + +exit: + ath11k_hal_srng_access_end(ab, srng); + + spin_unlock_bh(&srng->lock); + + for (i = 0; i < ab->num_radios; i++) { + if (!n_bufs_reaped[i]) + continue; + + ar = ab->pdevs[i].ar; + rx_ring = &ar->dp.rx_refill_buf_ring; + + ath11k_dp_rxbufs_replenish(ab, i, rx_ring, n_bufs_reaped[i], + HAL_RX_BUF_RBM_SW3_BM, GFP_ATOMIC); + } + + return tot_n_bufs_reaped; +} + +static void ath11k_dp_rx_null_q_desc_sg_drop(struct ath11k *ar, + int msdu_len, + struct sk_buff_head *msdu_list) +{ + struct sk_buff *skb, *tmp; + struct ath11k_skb_rxcb *rxcb; + int n_buffs; + + n_buffs = DIV_ROUND_UP(msdu_len, + (DP_RX_BUFFER_SIZE - HAL_RX_DESC_SIZE)); + + skb_queue_walk_safe(msdu_list, skb, tmp) { + rxcb = ATH11K_SKB_RXCB(skb); + if (rxcb->err_rel_src == HAL_WBM_REL_SRC_MODULE_REO && + rxcb->err_code == HAL_REO_DEST_RING_ERROR_CODE_DESC_ADDR_ZERO) { + if (!n_buffs) + break; + __skb_unlink(skb, msdu_list); + dev_kfree_skb_any(skb); + n_buffs--; + } + } +} + +static int ath11k_dp_rx_h_null_q_desc(struct ath11k *ar, struct sk_buff *msdu, + struct ieee80211_rx_status *status, + struct sk_buff_head *msdu_list) +{ + struct sk_buff_head amsdu_list; + u16 msdu_len; + struct hal_rx_desc *desc = (struct hal_rx_desc *)msdu->data; + u8 l3pad_bytes; + struct ath11k_skb_rxcb *rxcb = ATH11K_SKB_RXCB(msdu); + + msdu_len = ath11k_dp_rx_h_msdu_start_msdu_len(desc); + + if ((msdu_len + HAL_RX_DESC_SIZE) > DP_RX_BUFFER_SIZE) { + /* First buffer will be freed by the caller, so deduct it's length */ + msdu_len = msdu_len - (DP_RX_BUFFER_SIZE - HAL_RX_DESC_SIZE); + ath11k_dp_rx_null_q_desc_sg_drop(ar, msdu_len, msdu_list); + return -EINVAL; + } + + if (!ath11k_dp_rx_h_attn_msdu_done(desc)) { + ath11k_warn(ar->ab, + "msdu_done bit not set in null_q_des processing\n"); + __skb_queue_purge(msdu_list); + return -EIO; + } + + /* Handle NULL queue descriptor violations arising out a missing + * REO queue for a given peer or a given TID. This typically + * may happen if a packet is received on a QOS enabled TID before the + * ADDBA negotiation for that TID, when the TID queue is setup. Or + * it may also happen for MC/BC frames if they are not routed to the + * non-QOS TID queue, in the absence of any other default TID queue. + * This error can show up both in a REO destination or WBM release ring. + */ + + __skb_queue_head_init(&amsdu_list); + + rxcb->is_first_msdu = ath11k_dp_rx_h_msdu_end_first_msdu(desc); + rxcb->is_last_msdu = ath11k_dp_rx_h_msdu_end_last_msdu(desc); + + l3pad_bytes = ath11k_dp_rx_h_msdu_end_l3pad(desc); + + if ((HAL_RX_DESC_SIZE + l3pad_bytes + msdu_len) > DP_RX_BUFFER_SIZE) + return -EINVAL; + + skb_put(msdu, HAL_RX_DESC_SIZE + l3pad_bytes + msdu_len); + skb_pull(msdu, HAL_RX_DESC_SIZE + l3pad_bytes); + + ath11k_dp_rx_h_ppdu(ar, desc, status); + + __skb_queue_tail(&amsdu_list, msdu); + + ath11k_dp_rx_h_mpdu(ar, &amsdu_list, desc, status); + + /* Please note that caller will having the access to msdu and completing + * rx with mac80211. Need not worry about cleaning up amsdu_list. + */ + + return 0; +} + +static bool ath11k_dp_rx_h_reo_err(struct ath11k *ar, struct sk_buff *msdu, + struct ieee80211_rx_status *status, + struct sk_buff_head *msdu_list) +{ + struct ath11k_skb_rxcb *rxcb = ATH11K_SKB_RXCB(msdu); + bool drop = false; + + ar->ab->soc_stats.reo_error[rxcb->err_code]++; + + switch (rxcb->err_code) { + case HAL_REO_DEST_RING_ERROR_CODE_DESC_ADDR_ZERO: + if (ath11k_dp_rx_h_null_q_desc(ar, msdu, status, msdu_list)) + drop = true; + break; + default: + /* TODO: Review other errors and process them to mac80211 + * as appropriate. + */ + drop = true; + break; + } + + return drop; +} + +static void ath11k_dp_rx_h_tkip_mic_err(struct ath11k *ar, struct sk_buff *msdu, + struct ieee80211_rx_status *status) +{ + u16 msdu_len; + struct hal_rx_desc *desc = (struct hal_rx_desc *)msdu->data; + u8 l3pad_bytes; + struct ath11k_skb_rxcb *rxcb = ATH11K_SKB_RXCB(msdu); + + rxcb->is_first_msdu = ath11k_dp_rx_h_msdu_end_first_msdu(desc); + rxcb->is_last_msdu = ath11k_dp_rx_h_msdu_end_last_msdu(desc); + + l3pad_bytes = ath11k_dp_rx_h_msdu_end_l3pad(desc); + msdu_len = ath11k_dp_rx_h_msdu_start_msdu_len(desc); + skb_put(msdu, HAL_RX_DESC_SIZE + l3pad_bytes + msdu_len); + skb_pull(msdu, HAL_RX_DESC_SIZE + l3pad_bytes); + + ath11k_dp_rx_h_ppdu(ar, desc, status); + + status->flag |= (RX_FLAG_MMIC_STRIPPED | RX_FLAG_MMIC_ERROR | + RX_FLAG_DECRYPTED); + + ath11k_dp_rx_h_undecap(ar, msdu, desc, + HAL_ENCRYPT_TYPE_TKIP_MIC, status, false); +} + +static bool ath11k_dp_rx_h_rxdma_err(struct ath11k *ar, struct sk_buff *msdu, + struct ieee80211_rx_status *status) +{ + struct ath11k_skb_rxcb *rxcb = ATH11K_SKB_RXCB(msdu); + bool drop = false; + + ar->ab->soc_stats.rxdma_error[rxcb->err_code]++; + + switch (rxcb->err_code) { + case HAL_REO_ENTR_RING_RXDMA_ECODE_TKIP_MIC_ERR: + ath11k_dp_rx_h_tkip_mic_err(ar, msdu, status); + break; + default: + /* TODO: Review other rxdma error code to check if anything is + * worth reporting to mac80211 + */ + drop = true; + break; + } + + return drop; +} + +static void ath11k_dp_rx_wbm_err(struct ath11k *ar, + struct napi_struct *napi, + struct sk_buff *msdu, + struct sk_buff_head *msdu_list) +{ + struct ath11k_skb_rxcb *rxcb = ATH11K_SKB_RXCB(msdu); + struct ieee80211_rx_status rxs = {0}; + struct ieee80211_rx_status *status; + bool drop = true; + + switch (rxcb->err_rel_src) { + case HAL_WBM_REL_SRC_MODULE_REO: + drop = ath11k_dp_rx_h_reo_err(ar, msdu, &rxs, msdu_list); + break; + case HAL_WBM_REL_SRC_MODULE_RXDMA: + drop = ath11k_dp_rx_h_rxdma_err(ar, msdu, &rxs); + break; + default: + /* msdu will get freed */ + break; + } + + if (drop) { + dev_kfree_skb_any(msdu); + return; + } + + status = IEEE80211_SKB_RXCB(msdu); + *status = rxs; + + ath11k_dp_rx_deliver_msdu(ar, napi, msdu); +} + +int ath11k_dp_rx_process_wbm_err(struct ath11k_base *ab, + struct napi_struct *napi, int budget) +{ + struct ath11k *ar; + struct ath11k_dp *dp = &ab->dp; + struct dp_rxdma_ring *rx_ring; + struct hal_rx_wbm_rel_info err_info; + struct hal_srng *srng; + struct sk_buff *msdu; + struct sk_buff_head msdu_list[MAX_RADIOS]; + struct ath11k_skb_rxcb *rxcb; + u32 *rx_desc; + int buf_id, mac_id; + int num_buffs_reaped[MAX_RADIOS] = {0}; + int total_num_buffs_reaped = 0; + int ret, i; + + for (i = 0; i < MAX_RADIOS; i++) + __skb_queue_head_init(&msdu_list[i]); + + srng = &ab->hal.srng_list[dp->rx_rel_ring.ring_id]; + + spin_lock_bh(&srng->lock); + + ath11k_hal_srng_access_begin(ab, srng); + + while (budget) { + rx_desc = ath11k_hal_srng_dst_get_next_entry(ab, srng); + if (!rx_desc) + break; + + ret = ath11k_hal_wbm_desc_parse_err(ab, rx_desc, &err_info); + if (ret) { + ath11k_warn(ab, + "failed to parse rx error in wbm_rel ring desc %d\n", + ret); + continue; + } + + buf_id = FIELD_GET(DP_RXDMA_BUF_COOKIE_BUF_ID, err_info.cookie); + mac_id = FIELD_GET(DP_RXDMA_BUF_COOKIE_PDEV_ID, err_info.cookie); + + ar = ab->pdevs[mac_id].ar; + rx_ring = &ar->dp.rx_refill_buf_ring; + + spin_lock_bh(&rx_ring->idr_lock); + msdu = idr_find(&rx_ring->bufs_idr, buf_id); + if (!msdu) { + ath11k_warn(ab, "frame rx with invalid buf_id %d pdev %d\n", + buf_id, mac_id); + spin_unlock_bh(&rx_ring->idr_lock); + continue; + } + + idr_remove(&rx_ring->bufs_idr, buf_id); + spin_unlock_bh(&rx_ring->idr_lock); + + rxcb = ATH11K_SKB_RXCB(msdu); + dma_unmap_single(ab->dev, rxcb->paddr, + msdu->len + skb_tailroom(msdu), + DMA_FROM_DEVICE); + + num_buffs_reaped[mac_id]++; + total_num_buffs_reaped++; + budget--; + + if (err_info.push_reason != + HAL_REO_DEST_RING_PUSH_REASON_ERR_DETECTED) { + dev_kfree_skb_any(msdu); + continue; + } + + rxcb->err_rel_src = err_info.err_rel_src; + rxcb->err_code = err_info.err_code; + rxcb->rx_desc = (struct hal_rx_desc *)msdu->data; + __skb_queue_tail(&msdu_list[mac_id], msdu); + } + + ath11k_hal_srng_access_end(ab, srng); + + spin_unlock_bh(&srng->lock); + + if (!total_num_buffs_reaped) + goto done; + + for (i = 0; i < ab->num_radios; i++) { + if (!num_buffs_reaped[i]) + continue; + + ar = ab->pdevs[i].ar; + rx_ring = &ar->dp.rx_refill_buf_ring; + + ath11k_dp_rxbufs_replenish(ab, i, rx_ring, num_buffs_reaped[i], + HAL_RX_BUF_RBM_SW3_BM, GFP_ATOMIC); + } + + rcu_read_lock(); + for (i = 0; i < ab->num_radios; i++) { + if (!rcu_dereference(ab->pdevs_active[i])) { + __skb_queue_purge(&msdu_list[i]); + continue; + } + + ar = ab->pdevs[i].ar; + + if (test_bit(ATH11K_CAC_RUNNING, &ar->dev_flags)) { + __skb_queue_purge(&msdu_list[i]); + continue; + } + + while ((msdu = __skb_dequeue(&msdu_list[i])) != NULL) + ath11k_dp_rx_wbm_err(ar, napi, msdu, &msdu_list[i]); + } + rcu_read_unlock(); +done: + return total_num_buffs_reaped; +} + +int ath11k_dp_process_rxdma_err(struct ath11k_base *ab, int mac_id, int budget) +{ + struct ath11k *ar = ab->pdevs[mac_id].ar; + struct dp_srng *err_ring = &ar->dp.rxdma_err_dst_ring; + struct dp_rxdma_ring *rx_ring = &ar->dp.rx_refill_buf_ring; + struct dp_link_desc_bank *link_desc_banks = ab->dp.link_desc_banks; + struct hal_srng *srng; + struct hal_rx_msdu_meta meta[HAL_NUM_RX_MSDUS_PER_LINK_DESC]; + enum hal_rx_buf_return_buf_manager rbm; + enum hal_reo_entr_rxdma_ecode rxdma_err_code; + struct ath11k_skb_rxcb *rxcb; + struct sk_buff *skb; + struct hal_reo_entrance_ring *entr_ring; + void *desc; + int num_buf_freed = 0; + int quota = budget; + dma_addr_t paddr; + u32 desc_bank; + void *link_desc_va; + int num_msdus; + int i; + int buf_id; + + srng = &ab->hal.srng_list[err_ring->ring_id]; + + spin_lock_bh(&srng->lock); + + ath11k_hal_srng_access_begin(ab, srng); + + while (quota-- && + (desc = ath11k_hal_srng_dst_get_next_entry(ab, srng))) { + ath11k_hal_rx_reo_ent_paddr_get(ab, desc, &paddr, &desc_bank); + + entr_ring = (struct hal_reo_entrance_ring *)desc; + rxdma_err_code = + FIELD_GET(HAL_REO_ENTR_RING_INFO1_RXDMA_ERROR_CODE, + entr_ring->info1); + ab->soc_stats.rxdma_error[rxdma_err_code]++; + + link_desc_va = link_desc_banks[desc_bank].vaddr + + (paddr - link_desc_banks[desc_bank].paddr); + ath11k_hal_rx_msdu_link_info_get(link_desc_va, &num_msdus, meta, + &rbm); + + for (i = 0; i < num_msdus; i++) { + buf_id = FIELD_GET(DP_RXDMA_BUF_COOKIE_BUF_ID, + meta[i].cookie); + + spin_lock_bh(&rx_ring->idr_lock); + skb = idr_find(&rx_ring->bufs_idr, buf_id); + if (!skb) { + ath11k_warn(ab, "rxdma error with invalid buf_id %d\n", + buf_id); + spin_unlock_bh(&rx_ring->idr_lock); + continue; + } + + idr_remove(&rx_ring->bufs_idr, buf_id); + spin_unlock_bh(&rx_ring->idr_lock); + + rxcb = ATH11K_SKB_RXCB(skb); + dma_unmap_single(ab->dev, rxcb->paddr, + skb->len + skb_tailroom(skb), + DMA_FROM_DEVICE); + dev_kfree_skb_any(skb); + + num_buf_freed++; + } + + ath11k_dp_rx_link_desc_return(ab, desc, + HAL_WBM_REL_BM_ACT_PUT_IN_IDLE); + } + + ath11k_hal_srng_access_end(ab, srng); + + spin_unlock_bh(&srng->lock); + + if (num_buf_freed) + ath11k_dp_rxbufs_replenish(ab, mac_id, rx_ring, num_buf_freed, + HAL_RX_BUF_RBM_SW3_BM, GFP_ATOMIC); + + return budget - quota; +} + +void ath11k_dp_process_reo_status(struct ath11k_base *ab) +{ + struct ath11k_dp *dp = &ab->dp; + struct hal_srng *srng; + struct dp_reo_cmd *cmd, *tmp; + bool found = false; + u32 *reo_desc; + u16 tag; + struct hal_reo_status reo_status; + + srng = &ab->hal.srng_list[dp->reo_status_ring.ring_id]; + + memset(&reo_status, 0, sizeof(reo_status)); + + spin_lock_bh(&srng->lock); + + ath11k_hal_srng_access_begin(ab, srng); + + while ((reo_desc = ath11k_hal_srng_dst_get_next_entry(ab, srng))) { + tag = FIELD_GET(HAL_SRNG_TLV_HDR_TAG, *reo_desc); + + switch (tag) { + case HAL_REO_GET_QUEUE_STATS_STATUS: + ath11k_hal_reo_status_queue_stats(ab, reo_desc, + &reo_status); + break; + case HAL_REO_FLUSH_QUEUE_STATUS: + ath11k_hal_reo_flush_queue_status(ab, reo_desc, + &reo_status); + break; + case HAL_REO_FLUSH_CACHE_STATUS: + ath11k_hal_reo_flush_cache_status(ab, reo_desc, + &reo_status); + break; + case HAL_REO_UNBLOCK_CACHE_STATUS: + ath11k_hal_reo_unblk_cache_status(ab, reo_desc, + &reo_status); + break; + case HAL_REO_FLUSH_TIMEOUT_LIST_STATUS: + ath11k_hal_reo_flush_timeout_list_status(ab, reo_desc, + &reo_status); + break; + case HAL_REO_DESCRIPTOR_THRESHOLD_REACHED_STATUS: + ath11k_hal_reo_desc_thresh_reached_status(ab, reo_desc, + &reo_status); + break; + case HAL_REO_UPDATE_RX_REO_QUEUE_STATUS: + ath11k_hal_reo_update_rx_reo_queue_status(ab, reo_desc, + &reo_status); + break; + default: + ath11k_warn(ab, "Unknown reo status type %d\n", tag); + continue; + } + + spin_lock_bh(&dp->reo_cmd_lock); + list_for_each_entry_safe(cmd, tmp, &dp->reo_cmd_list, list) { + if (reo_status.uniform_hdr.cmd_num == cmd->cmd_num) { + found = true; + list_del(&cmd->list); + break; + } + } + spin_unlock_bh(&dp->reo_cmd_lock); + + if (found) { + cmd->handler(dp, (void *)&cmd->data, + reo_status.uniform_hdr.cmd_status); + kfree(cmd); + } + + found = false; + } + + ath11k_hal_srng_access_end(ab, srng); + + spin_unlock_bh(&srng->lock); +} + +void ath11k_dp_rx_pdev_free(struct ath11k_base *ab, int mac_id) +{ + struct ath11k *ar = ab->pdevs[mac_id].ar; + + ath11k_dp_rx_pdev_srng_free(ar); + ath11k_dp_rxdma_pdev_buf_free(ar); +} + +int ath11k_dp_rx_pdev_alloc(struct ath11k_base *ab, int mac_id) +{ + struct ath11k *ar = ab->pdevs[mac_id].ar; + struct ath11k_pdev_dp *dp = &ar->dp; + u32 ring_id; + int ret; + + ret = ath11k_dp_rx_pdev_srng_alloc(ar); + if (ret) { + ath11k_warn(ab, "failed to setup rx srngs\n"); + return ret; + } + + ret = ath11k_dp_rxdma_pdev_buf_setup(ar); + if (ret) { + ath11k_warn(ab, "failed to setup rxdma ring\n"); + return ret; + } + + ring_id = dp->rx_refill_buf_ring.refill_buf_ring.ring_id; + ret = ath11k_dp_tx_htt_srng_setup(ab, ring_id, mac_id, HAL_RXDMA_BUF); + if (ret) { + ath11k_warn(ab, "failed to configure rx_refill_buf_ring %d\n", + ret); + return ret; + } + + ring_id = dp->rxdma_err_dst_ring.ring_id; + ret = ath11k_dp_tx_htt_srng_setup(ab, ring_id, mac_id, HAL_RXDMA_DST); + if (ret) { + ath11k_warn(ab, "failed to configure rxdma_err_dest_ring %d\n", + ret); + return ret; + } + + ring_id = dp->rxdma_mon_buf_ring.refill_buf_ring.ring_id; + ret = ath11k_dp_tx_htt_srng_setup(ab, ring_id, + mac_id, HAL_RXDMA_MONITOR_BUF); + if (ret) { + ath11k_warn(ab, "failed to configure rxdma_mon_buf_ring %d\n", + ret); + return ret; + } + ret = ath11k_dp_tx_htt_srng_setup(ab, + dp->rxdma_mon_dst_ring.ring_id, + mac_id, HAL_RXDMA_MONITOR_DST); + if (ret) { + ath11k_warn(ab, "failed to configure rxdma_mon_dst_ring %d\n", + ret); + return ret; + } + ret = ath11k_dp_tx_htt_srng_setup(ab, + dp->rxdma_mon_desc_ring.ring_id, + mac_id, HAL_RXDMA_MONITOR_DESC); + if (ret) { + ath11k_warn(ab, "failed to configure rxdma_mon_dst_ring %d\n", + ret); + return ret; + } + ring_id = dp->rx_mon_status_refill_ring.refill_buf_ring.ring_id; + ret = ath11k_dp_tx_htt_srng_setup(ab, ring_id, mac_id, + HAL_RXDMA_MONITOR_STATUS); + if (ret) { + ath11k_warn(ab, + "failed to configure mon_status_refill_ring %d\n", + ret); + return ret; + } + return 0; +} + +static void ath11k_dp_mon_set_frag_len(u32 *total_len, u32 *frag_len) +{ + if (*total_len >= (DP_RX_BUFFER_SIZE - sizeof(struct hal_rx_desc))) { + *frag_len = DP_RX_BUFFER_SIZE - sizeof(struct hal_rx_desc); + *total_len -= *frag_len; + } else { + *frag_len = *total_len; + *total_len = 0; + } +} + +static +int ath11k_dp_rx_monitor_link_desc_return(struct ath11k *ar, + void *p_last_buf_addr_info, + u8 mac_id) +{ + struct ath11k_pdev_dp *dp = &ar->dp; + struct dp_srng *dp_srng; + void *hal_srng; + void *src_srng_desc; + int ret = 0; + + dp_srng = &dp->rxdma_mon_desc_ring; + hal_srng = &ar->ab->hal.srng_list[dp_srng->ring_id]; + + ath11k_hal_srng_access_begin(ar->ab, hal_srng); + + src_srng_desc = ath11k_hal_srng_src_get_next_entry(ar->ab, hal_srng); + + if (src_srng_desc) { + struct ath11k_buffer_addr *src_desc = + (struct ath11k_buffer_addr *)src_srng_desc; + + *src_desc = *((struct ath11k_buffer_addr *)p_last_buf_addr_info); + } else { + ath11k_dbg(ar->ab, ATH11K_DBG_DATA, + "Monitor Link Desc Ring %d Full", mac_id); + ret = -ENOMEM; + } + + ath11k_hal_srng_access_end(ar->ab, hal_srng); + return ret; +} + +static +void ath11k_dp_rx_mon_next_link_desc_get(void *rx_msdu_link_desc, + dma_addr_t *paddr, u32 *sw_cookie, + void **pp_buf_addr_info) +{ + struct hal_rx_msdu_link *msdu_link = + (struct hal_rx_msdu_link *)rx_msdu_link_desc; + struct ath11k_buffer_addr *buf_addr_info; + u8 rbm = 0; + + buf_addr_info = (struct ath11k_buffer_addr *)&msdu_link->buf_addr_info; + + ath11k_hal_rx_buf_addr_info_get(buf_addr_info, paddr, sw_cookie, &rbm); + + *pp_buf_addr_info = (void *)buf_addr_info; +} + +static int ath11k_dp_pkt_set_pktlen(struct sk_buff *skb, u32 len) +{ + if (skb->len > len) { + skb_trim(skb, len); + } else { + if (skb_tailroom(skb) < len - skb->len) { + if ((pskb_expand_head(skb, 0, + len - skb->len - skb_tailroom(skb), + GFP_ATOMIC))) { + dev_kfree_skb_any(skb); + return -ENOMEM; + } + } + skb_put(skb, (len - skb->len)); + } + return 0; +} + +static void ath11k_hal_rx_msdu_list_get(struct ath11k *ar, + void *msdu_link_desc, + struct hal_rx_msdu_list *msdu_list, + u16 *num_msdus) +{ + struct hal_rx_msdu_details *msdu_details = NULL; + struct rx_msdu_desc *msdu_desc_info = NULL; + struct hal_rx_msdu_link *msdu_link = NULL; + int i; + u32 last = FIELD_PREP(RX_MSDU_DESC_INFO0_LAST_MSDU_IN_MPDU, 1); + u32 first = FIELD_PREP(RX_MSDU_DESC_INFO0_FIRST_MSDU_IN_MPDU, 1); + u8 tmp = 0; + + msdu_link = (struct hal_rx_msdu_link *)msdu_link_desc; + msdu_details = &msdu_link->msdu_link[0]; + + for (i = 0; i < HAL_RX_NUM_MSDU_DESC; i++) { + if (FIELD_GET(BUFFER_ADDR_INFO0_ADDR, + msdu_details[i].buf_addr_info.info0) == 0) { + msdu_desc_info = &msdu_details[i - 1].rx_msdu_info; + msdu_desc_info->info0 |= last; + ; + break; + } + msdu_desc_info = &msdu_details[i].rx_msdu_info; + + if (!i) + msdu_desc_info->info0 |= first; + else if (i == (HAL_RX_NUM_MSDU_DESC - 1)) + msdu_desc_info->info0 |= last; + msdu_list->msdu_info[i].msdu_flags = msdu_desc_info->info0; + msdu_list->msdu_info[i].msdu_len = + HAL_RX_MSDU_PKT_LENGTH_GET(msdu_desc_info->info0); + msdu_list->sw_cookie[i] = + FIELD_GET(BUFFER_ADDR_INFO1_SW_COOKIE, + msdu_details[i].buf_addr_info.info1); + tmp = FIELD_GET(BUFFER_ADDR_INFO1_RET_BUF_MGR, + msdu_details[i].buf_addr_info.info1); + msdu_list->rbm[i] = tmp; + } + *num_msdus = i; +} + +static u32 ath11k_dp_rx_mon_comp_ppduid(u32 msdu_ppdu_id, u32 *ppdu_id, + u32 *rx_bufs_used) +{ + u32 ret = 0; + + if ((*ppdu_id < msdu_ppdu_id) && + ((msdu_ppdu_id - *ppdu_id) < DP_NOT_PPDU_ID_WRAP_AROUND)) { + *ppdu_id = msdu_ppdu_id; + ret = msdu_ppdu_id; + } else if ((*ppdu_id > msdu_ppdu_id) && + ((*ppdu_id - msdu_ppdu_id) > DP_NOT_PPDU_ID_WRAP_AROUND)) { + /* mon_dst is behind than mon_status + * skip dst_ring and free it + */ + *rx_bufs_used += 1; + *ppdu_id = msdu_ppdu_id; + ret = msdu_ppdu_id; + } + return ret; +} + +static void ath11k_dp_mon_get_buf_len(struct hal_rx_msdu_desc_info *info, + bool *is_frag, u32 *total_len, + u32 *frag_len, u32 *msdu_cnt) +{ + if (info->msdu_flags & RX_MSDU_DESC_INFO0_MSDU_CONTINUATION) { + if (!*is_frag) { + *total_len = info->msdu_len; + *is_frag = true; + } + ath11k_dp_mon_set_frag_len(total_len, + frag_len); + } else { + if (*is_frag) { + ath11k_dp_mon_set_frag_len(total_len, + frag_len); + } else { + *frag_len = info->msdu_len; + } + *is_frag = false; + *msdu_cnt -= 1; + } +} + +static u32 +ath11k_dp_rx_mon_mpdu_pop(struct ath11k *ar, + void *ring_entry, struct sk_buff **head_msdu, + struct sk_buff **tail_msdu, u32 *npackets, + u32 *ppdu_id) +{ + struct ath11k_pdev_dp *dp = &ar->dp; + struct ath11k_mon_data *pmon = (struct ath11k_mon_data *)&dp->mon_data; + struct dp_rxdma_ring *rx_ring = &dp->rxdma_mon_buf_ring; + struct sk_buff *msdu = NULL, *last = NULL; + struct hal_rx_msdu_list msdu_list; + void *p_buf_addr_info, *p_last_buf_addr_info; + struct hal_rx_desc *rx_desc; + void *rx_msdu_link_desc; + dma_addr_t paddr; + u16 num_msdus = 0; + u32 rx_buf_size, rx_pkt_offset, sw_cookie; + u32 rx_bufs_used = 0, i = 0; + u32 msdu_ppdu_id = 0, msdu_cnt = 0; + u32 total_len = 0, frag_len = 0; + bool is_frag, is_first_msdu; + bool drop_mpdu = false; + struct ath11k_skb_rxcb *rxcb; + struct hal_reo_entrance_ring *ent_desc = + (struct hal_reo_entrance_ring *)ring_entry; + int buf_id; + + ath11k_hal_rx_reo_ent_buf_paddr_get(ring_entry, &paddr, + &sw_cookie, &p_last_buf_addr_info, + &msdu_cnt); + + if (FIELD_GET(HAL_REO_ENTR_RING_INFO1_RXDMA_PUSH_REASON, + ent_desc->info1) == + HAL_REO_DEST_RING_PUSH_REASON_ERR_DETECTED) { + u8 rxdma_err = + FIELD_GET(HAL_REO_ENTR_RING_INFO1_RXDMA_ERROR_CODE, + ent_desc->info1); + if (rxdma_err == HAL_REO_ENTR_RING_RXDMA_ECODE_FLUSH_REQUEST_ERR || + rxdma_err == HAL_REO_ENTR_RING_RXDMA_ECODE_MPDU_LEN_ERR || + rxdma_err == HAL_REO_ENTR_RING_RXDMA_ECODE_OVERFLOW_ERR) { + drop_mpdu = true; + pmon->rx_mon_stats.dest_mpdu_drop++; + } + } + + is_frag = false; + is_first_msdu = true; + + do { + if (pmon->mon_last_linkdesc_paddr == paddr) { + pmon->rx_mon_stats.dup_mon_linkdesc_cnt++; + return rx_bufs_used; + } + + rx_msdu_link_desc = + (void *)pmon->link_desc_banks[sw_cookie].vaddr + + (paddr - pmon->link_desc_banks[sw_cookie].paddr); + + ath11k_hal_rx_msdu_list_get(ar, rx_msdu_link_desc, &msdu_list, + &num_msdus); + + for (i = 0; i < num_msdus; i++) { + u32 l2_hdr_offset; + + if (pmon->mon_last_buf_cookie == msdu_list.sw_cookie[i]) { + ath11k_dbg(ar->ab, ATH11K_DBG_DATA, + "i %d last_cookie %d is same\n", + i, pmon->mon_last_buf_cookie); + drop_mpdu = true; + pmon->rx_mon_stats.dup_mon_buf_cnt++; + continue; + } + buf_id = FIELD_GET(DP_RXDMA_BUF_COOKIE_BUF_ID, + msdu_list.sw_cookie[i]); + + spin_lock_bh(&rx_ring->idr_lock); + msdu = idr_find(&rx_ring->bufs_idr, buf_id); + spin_unlock_bh(&rx_ring->idr_lock); + if (!msdu) { + ath11k_dbg(ar->ab, ATH11K_DBG_DATA, + "msdu_pop: invalid buf_id %d\n", buf_id); + break; + } + rxcb = ATH11K_SKB_RXCB(msdu); + if (!rxcb->unmapped) { + dma_unmap_single(ar->ab->dev, rxcb->paddr, + msdu->len + + skb_tailroom(msdu), + DMA_FROM_DEVICE); + rxcb->unmapped = 1; + } + if (drop_mpdu) { + ath11k_dbg(ar->ab, ATH11K_DBG_DATA, + "i %d drop msdu %p *ppdu_id %x\n", + i, msdu, *ppdu_id); + dev_kfree_skb_any(msdu); + msdu = NULL; + goto next_msdu; + } + + rx_desc = (struct hal_rx_desc *)msdu->data; + + rx_pkt_offset = sizeof(struct hal_rx_desc); + l2_hdr_offset = ath11k_dp_rx_h_msdu_end_l3pad(rx_desc); + + if (is_first_msdu) { + if (!ath11k_dp_rxdesc_mpdu_valid(rx_desc)) { + drop_mpdu = true; + dev_kfree_skb_any(msdu); + msdu = NULL; + pmon->mon_last_linkdesc_paddr = paddr; + goto next_msdu; + } + + msdu_ppdu_id = + ath11k_dp_rxdesc_get_ppduid(rx_desc); + + if (ath11k_dp_rx_mon_comp_ppduid(msdu_ppdu_id, + ppdu_id, + &rx_bufs_used)) + return rx_bufs_used; + pmon->mon_last_linkdesc_paddr = paddr; + is_first_msdu = false; + } + ath11k_dp_mon_get_buf_len(&msdu_list.msdu_info[i], + &is_frag, &total_len, + &frag_len, &msdu_cnt); + rx_buf_size = rx_pkt_offset + l2_hdr_offset + frag_len; + + ath11k_dp_pkt_set_pktlen(msdu, rx_buf_size); + + if (!(*head_msdu)) + *head_msdu = msdu; + else if (last) + last->next = msdu; + + last = msdu; +next_msdu: + pmon->mon_last_buf_cookie = msdu_list.sw_cookie[i]; + rx_bufs_used++; + spin_lock_bh(&rx_ring->idr_lock); + idr_remove(&rx_ring->bufs_idr, buf_id); + spin_unlock_bh(&rx_ring->idr_lock); + } + + ath11k_dp_rx_mon_next_link_desc_get(rx_msdu_link_desc, &paddr, + &sw_cookie, + &p_buf_addr_info); + + if (ath11k_dp_rx_monitor_link_desc_return(ar, + p_last_buf_addr_info, + dp->mac_id)) + ath11k_dbg(ar->ab, ATH11K_DBG_DATA, + "dp_rx_monitor_link_desc_return failed"); + + p_last_buf_addr_info = p_buf_addr_info; + + } while (paddr && msdu_cnt); + + if (last) + last->next = NULL; + + *tail_msdu = msdu; + + if (msdu_cnt == 0) + *npackets = 1; + + return rx_bufs_used; +} + +static void ath11k_dp_rx_msdus_set_payload(struct sk_buff *msdu) +{ + u32 rx_pkt_offset, l2_hdr_offset; + + rx_pkt_offset = sizeof(struct hal_rx_desc); + l2_hdr_offset = ath11k_dp_rx_h_msdu_end_l3pad((struct hal_rx_desc *)msdu->data); + skb_pull(msdu, rx_pkt_offset + l2_hdr_offset); +} + +static struct sk_buff * +ath11k_dp_rx_mon_merg_msdus(struct ath11k *ar, + u32 mac_id, struct sk_buff *head_msdu, + struct sk_buff *last_msdu, + struct ieee80211_rx_status *rxs) +{ + struct sk_buff *msdu, *mpdu_buf, *prev_buf; + u32 decap_format, wifi_hdr_len; + struct hal_rx_desc *rx_desc; + char *hdr_desc; + u8 *dest; + struct ieee80211_hdr_3addr *wh; + + mpdu_buf = NULL; + + if (!head_msdu) + goto err_merge_fail; + + rx_desc = (struct hal_rx_desc *)head_msdu->data; + + if (ath11k_dp_rxdesc_get_mpdulen_err(rx_desc)) + return NULL; + + decap_format = ath11k_dp_rxdesc_get_decap_format(rx_desc); + + ath11k_dp_rx_h_ppdu(ar, rx_desc, rxs); + + if (decap_format == DP_RX_DECAP_TYPE_RAW) { + ath11k_dp_rx_msdus_set_payload(head_msdu); + + prev_buf = head_msdu; + msdu = head_msdu->next; + + while (msdu) { + ath11k_dp_rx_msdus_set_payload(msdu); + + prev_buf = msdu; + msdu = msdu->next; + } + + prev_buf->next = NULL; + + skb_trim(prev_buf, prev_buf->len - HAL_RX_FCS_LEN); + } else if (decap_format == DP_RX_DECAP_TYPE_NATIVE_WIFI) { + __le16 qos_field; + u8 qos_pkt = 0; + + rx_desc = (struct hal_rx_desc *)head_msdu->data; + hdr_desc = ath11k_dp_rxdesc_get_80211hdr(rx_desc); + + /* Base size */ + wifi_hdr_len = sizeof(struct ieee80211_hdr_3addr); + wh = (struct ieee80211_hdr_3addr *)hdr_desc; + + if (ieee80211_is_data_qos(wh->frame_control)) { + struct ieee80211_qos_hdr *qwh = + (struct ieee80211_qos_hdr *)hdr_desc; + + qos_field = qwh->qos_ctrl; + qos_pkt = 1; + } + msdu = head_msdu; + + while (msdu) { + rx_desc = (struct hal_rx_desc *)msdu->data; + hdr_desc = ath11k_dp_rxdesc_get_80211hdr(rx_desc); + + if (qos_pkt) { + dest = skb_push(msdu, sizeof(__le16)); + if (!dest) + goto err_merge_fail; + memcpy(dest, hdr_desc, wifi_hdr_len); + memcpy(dest + wifi_hdr_len, + (u8 *)&qos_field, sizeof(__le16)); + } + ath11k_dp_rx_msdus_set_payload(msdu); + prev_buf = msdu; + msdu = msdu->next; + } + dest = skb_put(prev_buf, HAL_RX_FCS_LEN); + if (!dest) + goto err_merge_fail; + + ath11k_dbg(ar->ab, ATH11K_DBG_DATA, + "mpdu_buf %pK mpdu_buf->len %u", + prev_buf, prev_buf->len); + } else { + ath11k_dbg(ar->ab, ATH11K_DBG_DATA, + "decap format %d is not supported!\n", + decap_format); + goto err_merge_fail; + } + + return head_msdu; + +err_merge_fail: + if (mpdu_buf && decap_format != DP_RX_DECAP_TYPE_RAW) { + ath11k_dbg(ar->ab, ATH11K_DBG_DATA, + "err_merge_fail mpdu_buf %pK", mpdu_buf); + /* Free the head buffer */ + dev_kfree_skb_any(mpdu_buf); + } + return NULL; +} + +static int ath11k_dp_rx_mon_deliver(struct ath11k *ar, u32 mac_id, + struct sk_buff *head_msdu, + struct sk_buff *tail_msdu, + struct napi_struct *napi) +{ + struct ath11k_pdev_dp *dp = &ar->dp; + struct sk_buff *mon_skb, *skb_next, *header; + struct ieee80211_rx_status *rxs = &dp->rx_status, *status; + + mon_skb = ath11k_dp_rx_mon_merg_msdus(ar, mac_id, head_msdu, + tail_msdu, rxs); + + if (!mon_skb) + goto mon_deliver_fail; + + header = mon_skb; + + rxs->flag = 0; + do { + skb_next = mon_skb->next; + if (!skb_next) + rxs->flag &= ~RX_FLAG_AMSDU_MORE; + else + rxs->flag |= RX_FLAG_AMSDU_MORE; + + if (mon_skb == header) { + header = NULL; + rxs->flag &= ~RX_FLAG_ALLOW_SAME_PN; + } else { + rxs->flag |= RX_FLAG_ALLOW_SAME_PN; + } + rxs->flag |= RX_FLAG_ONLY_MONITOR; + + status = IEEE80211_SKB_RXCB(mon_skb); + *status = *rxs; + + ath11k_dp_rx_deliver_msdu(ar, napi, mon_skb); + mon_skb = skb_next; + } while (mon_skb && (mon_skb != tail_msdu)); + rxs->flag = 0; + + return 0; + +mon_deliver_fail: + mon_skb = head_msdu; + while (mon_skb) { + skb_next = mon_skb->next; + dev_kfree_skb_any(mon_skb); + mon_skb = skb_next; + } + return -EINVAL; +} + +static void ath11k_dp_rx_mon_dest_process(struct ath11k *ar, u32 quota, + struct napi_struct *napi) +{ + struct ath11k_pdev_dp *dp = &ar->dp; + struct ath11k_mon_data *pmon = (struct ath11k_mon_data *)&dp->mon_data; + void *ring_entry; + void *mon_dst_srng; + u32 ppdu_id; + u32 rx_bufs_used; + struct ath11k_pdev_mon_stats *rx_mon_stats; + u32 npackets = 0; + + mon_dst_srng = &ar->ab->hal.srng_list[dp->rxdma_mon_dst_ring.ring_id]; + + if (!mon_dst_srng) { + ath11k_warn(ar->ab, + "HAL Monitor Destination Ring Init Failed -- %pK", + mon_dst_srng); + return; + } + + spin_lock_bh(&pmon->mon_lock); + + ath11k_hal_srng_access_begin(ar->ab, mon_dst_srng); + + ppdu_id = pmon->mon_ppdu_info.ppdu_id; + rx_bufs_used = 0; + rx_mon_stats = &pmon->rx_mon_stats; + + while ((ring_entry = ath11k_hal_srng_dst_peek(ar->ab, mon_dst_srng))) { + struct sk_buff *head_msdu, *tail_msdu; + + head_msdu = NULL; + tail_msdu = NULL; + + rx_bufs_used += ath11k_dp_rx_mon_mpdu_pop(ar, ring_entry, + &head_msdu, + &tail_msdu, + &npackets, &ppdu_id); + + if (ppdu_id != pmon->mon_ppdu_info.ppdu_id) { + pmon->mon_ppdu_status = DP_PPDU_STATUS_START; + ath11k_dbg(ar->ab, ATH11K_DBG_DATA, + "dest_rx: new ppdu_id %x != status ppdu_id %x", + ppdu_id, pmon->mon_ppdu_info.ppdu_id); + break; + } + if (head_msdu && tail_msdu) { + ath11k_dp_rx_mon_deliver(ar, dp->mac_id, head_msdu, + tail_msdu, napi); + rx_mon_stats->dest_mpdu_done++; + } + + ring_entry = ath11k_hal_srng_dst_get_next_entry(ar->ab, + mon_dst_srng); + } + ath11k_hal_srng_access_end(ar->ab, mon_dst_srng); + + spin_unlock_bh(&pmon->mon_lock); + + if (rx_bufs_used) { + rx_mon_stats->dest_ppdu_done++; + ath11k_dp_rxbufs_replenish(ar->ab, dp->mac_id, + &dp->rxdma_mon_buf_ring, + rx_bufs_used, + HAL_RX_BUF_RBM_SW3_BM, GFP_ATOMIC); + } +} + +static void ath11k_dp_rx_mon_status_process_tlv(struct ath11k *ar, + u32 quota, + struct napi_struct *napi) +{ + struct ath11k_pdev_dp *dp = &ar->dp; + struct ath11k_mon_data *pmon = (struct ath11k_mon_data *)&dp->mon_data; + struct hal_rx_mon_ppdu_info *ppdu_info; + struct sk_buff *status_skb; + u32 tlv_status = HAL_TLV_STATUS_BUF_DONE; + struct ath11k_pdev_mon_stats *rx_mon_stats; + + ppdu_info = &pmon->mon_ppdu_info; + rx_mon_stats = &pmon->rx_mon_stats; + + if (pmon->mon_ppdu_status != DP_PPDU_STATUS_START) + return; + + while (!skb_queue_empty(&pmon->rx_status_q)) { + status_skb = skb_dequeue(&pmon->rx_status_q); + + tlv_status = ath11k_hal_rx_parse_mon_status(ar->ab, ppdu_info, + status_skb); + if (tlv_status == HAL_TLV_STATUS_PPDU_DONE) { + rx_mon_stats->status_ppdu_done++; + pmon->mon_ppdu_status = DP_PPDU_STATUS_DONE; + ath11k_dp_rx_mon_dest_process(ar, quota, napi); + pmon->mon_ppdu_status = DP_PPDU_STATUS_START; + } + dev_kfree_skb_any(status_skb); + } +} + +static int ath11k_dp_mon_process_rx(struct ath11k_base *ab, int mac_id, + struct napi_struct *napi, int budget) +{ + struct ath11k *ar = ab->pdevs[mac_id].ar; + struct ath11k_pdev_dp *dp = &ar->dp; + struct ath11k_mon_data *pmon = (struct ath11k_mon_data *)&dp->mon_data; + int num_buffs_reaped = 0; + + num_buffs_reaped = ath11k_dp_rx_reap_mon_status_ring(ar->ab, dp->mac_id, &budget, + &pmon->rx_status_q); + if (num_buffs_reaped) + ath11k_dp_rx_mon_status_process_tlv(ar, budget, napi); + + return num_buffs_reaped; +} + +int ath11k_dp_rx_process_mon_rings(struct ath11k_base *ab, int mac_id, + struct napi_struct *napi, int budget) +{ + struct ath11k *ar = ab->pdevs[mac_id].ar; + int ret = 0; + + if (test_bit(ATH11K_FLAG_MONITOR_ENABLED, &ar->monitor_flags)) + ret = ath11k_dp_mon_process_rx(ab, mac_id, napi, budget); + else + ret = ath11k_dp_rx_process_mon_status(ab, mac_id, napi, budget); + return ret; +} + +static int ath11k_dp_rx_pdev_mon_status_attach(struct ath11k *ar) +{ + struct ath11k_pdev_dp *dp = &ar->dp; + struct ath11k_mon_data *pmon = (struct ath11k_mon_data *)&dp->mon_data; + + skb_queue_head_init(&pmon->rx_status_q); + + pmon->mon_ppdu_status = DP_PPDU_STATUS_START; + + memset(&pmon->rx_mon_stats, 0, + sizeof(pmon->rx_mon_stats)); + return 0; +} + +int ath11k_dp_rx_pdev_mon_attach(struct ath11k *ar) +{ + struct ath11k_pdev_dp *dp = &ar->dp; + struct ath11k_mon_data *pmon = &dp->mon_data; + struct hal_srng *mon_desc_srng = NULL; + struct dp_srng *dp_srng; + int ret = 0; + u32 n_link_desc = 0; + + ret = ath11k_dp_rx_pdev_mon_status_attach(ar); + if (ret) { + ath11k_warn(ar->ab, "pdev_mon_status_attach() failed"); + return ret; + } + + dp_srng = &dp->rxdma_mon_desc_ring; + n_link_desc = dp_srng->size / + ath11k_hal_srng_get_entrysize(HAL_RXDMA_MONITOR_DESC); + mon_desc_srng = + &ar->ab->hal.srng_list[dp->rxdma_mon_desc_ring.ring_id]; + + ret = ath11k_dp_link_desc_setup(ar->ab, pmon->link_desc_banks, + HAL_RXDMA_MONITOR_DESC, mon_desc_srng, + n_link_desc); + if (ret) { + ath11k_warn(ar->ab, "mon_link_desc_pool_setup() failed"); + return ret; + } + pmon->mon_last_linkdesc_paddr = 0; + pmon->mon_last_buf_cookie = DP_RX_DESC_COOKIE_MAX + 1; + spin_lock_init(&pmon->mon_lock); + return 0; +} + +static int ath11k_dp_mon_link_free(struct ath11k *ar) +{ + struct ath11k_pdev_dp *dp = &ar->dp; + struct ath11k_mon_data *pmon = &dp->mon_data; + + ath11k_dp_link_desc_cleanup(ar->ab, pmon->link_desc_banks, + HAL_RXDMA_MONITOR_DESC, + &dp->rxdma_mon_desc_ring); + return 0; +} + +int ath11k_dp_rx_pdev_mon_detach(struct ath11k *ar) +{ + ath11k_dp_mon_link_free(ar); + return 0; +} diff --git a/drivers/net/wireless/ath/ath11k/dp_rx.h b/drivers/net/wireless/ath/ath11k/dp_rx.h new file mode 100644 index 0000000000000..026820fda5689 --- /dev/null +++ b/drivers/net/wireless/ath/ath11k/dp_rx.h @@ -0,0 +1,84 @@ +/* SPDX-License-Identifier: BSD-3-Clause-Clear */ +/* + * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. + */ +#ifndef ATH11K_DP_RX_H +#define ATH11K_DP_RX_H + +#include "core.h" +#include "rx_desc.h" +#include "debug.h" + +#define DP_RX_MPDU_ERR_FCS BIT(0) +#define DP_RX_MPDU_ERR_DECRYPT BIT(1) +#define DP_RX_MPDU_ERR_TKIP_MIC BIT(2) +#define DP_RX_MPDU_ERR_AMSDU_ERR BIT(3) +#define DP_RX_MPDU_ERR_OVERFLOW BIT(4) +#define DP_RX_MPDU_ERR_MSDU_LEN BIT(5) +#define DP_RX_MPDU_ERR_MPDU_LEN BIT(6) +#define DP_RX_MPDU_ERR_UNENCRYPTED_FRAME BIT(7) + +enum dp_rx_decap_type { + DP_RX_DECAP_TYPE_RAW, + DP_RX_DECAP_TYPE_NATIVE_WIFI, + DP_RX_DECAP_TYPE_ETHERNET2_DIX, + DP_RX_DECAP_TYPE_8023, +}; + +struct ath11k_dp_amsdu_subframe_hdr { + u8 dst[ETH_ALEN]; + u8 src[ETH_ALEN]; + __be16 len; +} __packed; + +struct ath11k_dp_rfc1042_hdr { + u8 llc_dsap; + u8 llc_ssap; + u8 llc_ctrl; + u8 snap_oui[3]; + __be16 snap_type; +} __packed; + +int ath11k_dp_rx_ampdu_start(struct ath11k *ar, + struct ieee80211_ampdu_params *params); +int ath11k_dp_rx_ampdu_stop(struct ath11k *ar, + struct ieee80211_ampdu_params *params); +void ath11k_peer_rx_tid_cleanup(struct ath11k *ar, struct ath11k_peer *peer); +int ath11k_peer_rx_tid_setup(struct ath11k *ar, const u8 *peer_mac, int vdev_id, + u8 tid, u32 ba_win_sz, u16 ssn); +void ath11k_dp_htt_htc_t2h_msg_handler(struct ath11k_base *ab, + struct sk_buff *skb); +int ath11k_dp_rx_pdev_alloc(struct ath11k_base *ab, int pdev_idx); +void ath11k_dp_rx_pdev_free(struct ath11k_base *ab, int pdev_idx); +void ath11k_dp_reo_cmd_list_cleanup(struct ath11k_base *ab); +void ath11k_dp_process_reo_status(struct ath11k_base *ab); +int ath11k_dp_process_rxdma_err(struct ath11k_base *ab, int mac_id, int budget); +int ath11k_dp_rx_process_wbm_err(struct ath11k_base *ab, + struct napi_struct *napi, int budget); +int ath11k_dp_process_rx_err(struct ath11k_base *ab, struct napi_struct *napi, + int budget); +int ath11k_dp_process_rx(struct ath11k_base *ab, int mac_id, + struct napi_struct *napi, struct sk_buff_head *pending_q, + int budget); +int ath11k_dp_rxbufs_replenish(struct ath11k_base *ab, int mac_id, + struct dp_rxdma_ring *rx_ring, + int req_entries, + enum hal_rx_buf_return_buf_manager mgr, + gfp_t gfp); +int ath11k_dp_htt_tlv_iter(struct ath11k_base *ab, const void *ptr, size_t len, + int (*iter)(struct ath11k_base *ar, u16 tag, u16 len, + const void *ptr, void *data), + void *data); +int ath11k_dp_rx_process_mon_rings(struct ath11k_base *ab, int mac_id, + struct napi_struct *napi, int budget); +int ath11k_dp_rx_process_mon_status(struct ath11k_base *ab, int mac_id, + struct napi_struct *napi, int budget); +int ath11k_dp_rx_mon_status_bufs_replenish(struct ath11k_base *ab, int mac_id, + struct dp_rxdma_ring *rx_ring, + int req_entries, + enum hal_rx_buf_return_buf_manager mgr, + gfp_t gfp); +int ath11k_dp_rx_pdev_mon_detach(struct ath11k *ar); +int ath11k_dp_rx_pdev_mon_attach(struct ath11k *ar); + +#endif /* ATH11K_DP_RX_H */ diff --git a/drivers/net/wireless/ath/ath11k/dp_tx.c b/drivers/net/wireless/ath/ath11k/dp_tx.c new file mode 100644 index 0000000000000..51a02b8e66ccf --- /dev/null +++ b/drivers/net/wireless/ath/ath11k/dp_tx.c @@ -0,0 +1,943 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* + * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. + */ + +#include "core.h" +#include "dp_tx.h" +#include "debug.h" +#include "hw.h" + +/* NOTE: Any of the mapped ring id value must not exceed DP_TCL_NUM_RING_MAX */ +static const u8 +ath11k_txq_tcl_ring_map[ATH11K_HW_MAX_QUEUES] = { 0x0, 0x1, 0x2, 0x2 }; + +static enum hal_tcl_encap_type +ath11k_dp_tx_get_encap_type(struct ath11k_vif *arvif, struct sk_buff *skb) +{ + /* TODO: Determine encap type based on vif_type and configuration */ + return HAL_TCL_ENCAP_TYPE_NATIVE_WIFI; +} + +static void ath11k_dp_tx_encap_nwifi(struct sk_buff *skb) +{ + struct ieee80211_hdr *hdr = (void *)skb->data; + u8 *qos_ctl; + + if (!ieee80211_is_data_qos(hdr->frame_control)) + return; + + qos_ctl = ieee80211_get_qos_ctl(hdr); + memmove(skb->data + IEEE80211_QOS_CTL_LEN, + skb->data, (void *)qos_ctl - (void *)skb->data); + skb_pull(skb, IEEE80211_QOS_CTL_LEN); + + hdr = (void *)skb->data; + hdr->frame_control &= ~__cpu_to_le16(IEEE80211_STYPE_QOS_DATA); +} + +static u8 ath11k_dp_tx_get_tid(struct sk_buff *skb) +{ + struct ieee80211_hdr *hdr = (void *)skb->data; + + if (!ieee80211_is_data_qos(hdr->frame_control)) + return HAL_DESC_REO_NON_QOS_TID; + else + return skb->priority & IEEE80211_QOS_CTL_TID_MASK; +} + +static enum hal_encrypt_type ath11k_dp_tx_get_encrypt_type(u32 cipher) +{ + switch (cipher) { + case WLAN_CIPHER_SUITE_WEP40: + return HAL_ENCRYPT_TYPE_WEP_40; + case WLAN_CIPHER_SUITE_WEP104: + return HAL_ENCRYPT_TYPE_WEP_104; + case WLAN_CIPHER_SUITE_TKIP: + return HAL_ENCRYPT_TYPE_TKIP_MIC; + case WLAN_CIPHER_SUITE_CCMP: + return HAL_ENCRYPT_TYPE_CCMP_128; + case WLAN_CIPHER_SUITE_CCMP_256: + return HAL_ENCRYPT_TYPE_CCMP_256; + case WLAN_CIPHER_SUITE_GCMP: + return HAL_ENCRYPT_TYPE_GCMP_128; + case WLAN_CIPHER_SUITE_GCMP_256: + return HAL_ENCRYPT_TYPE_AES_GCMP_256; + default: + return HAL_ENCRYPT_TYPE_OPEN; + } +} + +int ath11k_dp_tx(struct ath11k *ar, struct ath11k_vif *arvif, + struct sk_buff *skb) +{ + struct ath11k_base *ab = ar->ab; + struct ath11k_dp *dp = &ab->dp; + struct hal_tx_info ti = {0}; + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct ath11k_skb_cb *skb_cb = ATH11K_SKB_CB(skb); + struct hal_srng *tcl_ring; + struct ieee80211_hdr *hdr = (void *)skb->data; + struct dp_tx_ring *tx_ring; + u8 cached_desc[HAL_TCL_DESC_LEN]; + void *hal_tcl_desc; + u8 pool_id; + u8 hal_ring_id; + int ret; + + if (test_bit(ATH11K_FLAG_CRASH_FLUSH, &ar->ab->dev_flags)) + return -ESHUTDOWN; + + if (!ieee80211_is_data(hdr->frame_control)) + return -ENOTSUPP; + + pool_id = skb_get_queue_mapping(skb) & (ATH11K_HW_MAX_QUEUES - 1); + ti.ring_id = ath11k_txq_tcl_ring_map[pool_id]; + + tx_ring = &dp->tx_ring[ti.ring_id]; + + spin_lock_bh(&tx_ring->tx_idr_lock); + ret = idr_alloc(&tx_ring->txbuf_idr, skb, 0, + DP_TX_IDR_SIZE - 1, GFP_ATOMIC); + spin_unlock_bh(&tx_ring->tx_idr_lock); + + if (ret < 0) + return -ENOSPC; + + ti.desc_id = FIELD_PREP(DP_TX_DESC_ID_MAC_ID, ar->pdev_idx) | + FIELD_PREP(DP_TX_DESC_ID_MSDU_ID, ret) | + FIELD_PREP(DP_TX_DESC_ID_POOL_ID, pool_id); + ti.encap_type = ath11k_dp_tx_get_encap_type(arvif, skb); + ti.meta_data_flags = arvif->tcl_metadata; + + if (info->control.hw_key) + ti.encrypt_type = + ath11k_dp_tx_get_encrypt_type(info->control.hw_key->cipher); + else + ti.encrypt_type = HAL_ENCRYPT_TYPE_OPEN; + + ti.addr_search_flags = arvif->hal_addr_search_flags; + ti.search_type = arvif->search_type; + ti.type = HAL_TCL_DESC_TYPE_BUFFER; + ti.pkt_offset = 0; + ti.lmac_id = ar->lmac_id; + ti.bss_ast_hash = arvif->ast_hash; + ti.dscp_tid_tbl_idx = 0; + + if (skb->ip_summed == CHECKSUM_PARTIAL) { + ti.flags0 |= FIELD_PREP(HAL_TCL_DATA_CMD_INFO1_IP4_CKSUM_EN, 1) | + FIELD_PREP(HAL_TCL_DATA_CMD_INFO1_UDP4_CKSUM_EN, 1) | + FIELD_PREP(HAL_TCL_DATA_CMD_INFO1_UDP6_CKSUM_EN, 1) | + FIELD_PREP(HAL_TCL_DATA_CMD_INFO1_TCP4_CKSUM_EN, 1) | + FIELD_PREP(HAL_TCL_DATA_CMD_INFO1_TCP6_CKSUM_EN, 1); + } + + if (ieee80211_vif_is_mesh(arvif->vif)) + ti.flags1 |= FIELD_PREP(HAL_TCL_DATA_CMD_INFO2_MESH_ENABLE, 1); + + ti.flags1 |= FIELD_PREP(HAL_TCL_DATA_CMD_INFO2_TID_OVERWRITE, 1); + + ti.tid = ath11k_dp_tx_get_tid(skb); + + switch (ti.encap_type) { + case HAL_TCL_ENCAP_TYPE_NATIVE_WIFI: + ath11k_dp_tx_encap_nwifi(skb); + break; + case HAL_TCL_ENCAP_TYPE_RAW: + /* TODO: for CHECKSUM_PARTIAL case in raw mode, HW checksum offload + * is not applicable, hence manual checksum calculation using + * skb_checksum_help() is needed + */ + case HAL_TCL_ENCAP_TYPE_ETHERNET: + case HAL_TCL_ENCAP_TYPE_802_3: + /* TODO: Take care of other encap modes as well */ + ret = -EINVAL; + goto fail_remove_idr; + } + + ti.paddr = dma_map_single(ab->dev, skb->data, skb->len, DMA_TO_DEVICE); + if (dma_mapping_error(ab->dev, ti.paddr)) { + ath11k_warn(ab, "failed to DMA map data Tx buffer\n"); + ret = -ENOMEM; + goto fail_remove_idr; + } + + ti.data_len = skb->len; + skb_cb->paddr = ti.paddr; + skb_cb->vif = arvif->vif; + skb_cb->ar = ar; + + memset(cached_desc, 0, HAL_TCL_DESC_LEN); + + ath11k_hal_tx_cmd_desc_setup(ab, cached_desc, &ti); + + hal_ring_id = tx_ring->tcl_data_ring.ring_id; + tcl_ring = &ab->hal.srng_list[hal_ring_id]; + + spin_lock_bh(&tcl_ring->lock); + + ath11k_hal_srng_access_begin(ab, tcl_ring); + + hal_tcl_desc = (void *)ath11k_hal_srng_src_get_next_entry(ab, tcl_ring); + if (!hal_tcl_desc) { + /* NOTE: It is highly unlikely we'll be running out of tcl_ring + * desc because the desc is directly enqueued onto hw queue. + * So add tx packet throttling logic in future if required. + */ + ath11k_hal_srng_access_end(ab, tcl_ring); + spin_unlock_bh(&tcl_ring->lock); + ret = -ENOMEM; + goto fail_unmap_dma; + } + + ath11k_hal_tx_desc_sync(cached_desc, hal_tcl_desc); + + ath11k_hal_srng_access_end(ab, tcl_ring); + + spin_unlock_bh(&tcl_ring->lock); + + spin_lock_bh(&tx_ring->tx_idr_lock); + tx_ring->num_tx_pending++; + spin_unlock_bh(&tx_ring->tx_idr_lock); + + atomic_inc(&ar->dp.num_tx_pending); + + return 0; + +fail_unmap_dma: + dma_unmap_single(ab->dev, ti.paddr, ti.data_len, DMA_TO_DEVICE); + +fail_remove_idr: + spin_lock_bh(&tx_ring->tx_idr_lock); + idr_remove(&tx_ring->txbuf_idr, + FIELD_GET(DP_TX_DESC_ID_MSDU_ID, ti.desc_id)); + spin_unlock_bh(&tx_ring->tx_idr_lock); + + return ret; +} + +static void ath11k_dp_tx_free_txbuf(struct ath11k_base *ab, u8 mac_id, + int msdu_id, + struct dp_tx_ring *tx_ring) +{ + struct ath11k *ar; + struct sk_buff *msdu; + struct ath11k_skb_cb *skb_cb; + + spin_lock_bh(&tx_ring->tx_idr_lock); + msdu = idr_find(&tx_ring->txbuf_idr, msdu_id); + if (!msdu) { + ath11k_warn(ab, "tx completion for unknown msdu_id %d\n", + msdu_id); + spin_unlock_bh(&tx_ring->tx_idr_lock); + return; + } + + skb_cb = ATH11K_SKB_CB(msdu); + + idr_remove(&tx_ring->txbuf_idr, msdu_id); + tx_ring->num_tx_pending--; + spin_unlock_bh(&tx_ring->tx_idr_lock); + + dma_unmap_single(ab->dev, skb_cb->paddr, msdu->len, DMA_TO_DEVICE); + dev_kfree_skb_any(msdu); + + ar = ab->pdevs[mac_id].ar; + if (atomic_dec_and_test(&ar->dp.num_tx_pending)) + wake_up(&ar->dp.tx_empty_waitq); +} + +static void +ath11k_dp_tx_htt_tx_complete_buf(struct ath11k_base *ab, + struct dp_tx_ring *tx_ring, + struct ath11k_dp_htt_wbm_tx_status *ts) +{ + struct sk_buff *msdu; + struct ieee80211_tx_info *info; + struct ath11k_skb_cb *skb_cb; + struct ath11k *ar; + + spin_lock_bh(&tx_ring->tx_idr_lock); + msdu = idr_find(&tx_ring->txbuf_idr, ts->msdu_id); + if (!msdu) { + ath11k_warn(ab, "htt tx completion for unknown msdu_id %d\n", + ts->msdu_id); + spin_unlock_bh(&tx_ring->tx_idr_lock); + return; + } + + skb_cb = ATH11K_SKB_CB(msdu); + info = IEEE80211_SKB_CB(msdu); + + ar = skb_cb->ar; + + idr_remove(&tx_ring->txbuf_idr, ts->msdu_id); + tx_ring->num_tx_pending--; + spin_unlock_bh(&tx_ring->tx_idr_lock); + + if (atomic_dec_and_test(&ar->dp.num_tx_pending)) + wake_up(&ar->dp.tx_empty_waitq); + + dma_unmap_single(ab->dev, skb_cb->paddr, msdu->len, DMA_TO_DEVICE); + + memset(&info->status, 0, sizeof(info->status)); + + if (ts->acked) { + if (!(info->flags & IEEE80211_TX_CTL_NO_ACK)) { + info->flags |= IEEE80211_TX_STAT_ACK; + info->status.ack_signal = ATH11K_DEFAULT_NOISE_FLOOR + + ts->ack_rssi; + info->status.is_valid_ack_signal = true; + } else { + info->flags |= IEEE80211_TX_STAT_NOACK_TRANSMITTED; + } + } + + ieee80211_tx_status(ar->hw, msdu); +} + +static void +ath11k_dp_tx_process_htt_tx_complete(struct ath11k_base *ab, + void *desc, u8 mac_id, + u32 msdu_id, struct dp_tx_ring *tx_ring) +{ + struct htt_tx_wbm_completion *status_desc; + struct ath11k_dp_htt_wbm_tx_status ts = {0}; + enum hal_wbm_htt_tx_comp_status wbm_status; + + status_desc = desc + HTT_TX_WBM_COMP_STATUS_OFFSET; + + wbm_status = FIELD_GET(HTT_TX_WBM_COMP_INFO0_STATUS, + status_desc->info0); + + switch (wbm_status) { + case HAL_WBM_REL_HTT_TX_COMP_STATUS_OK: + case HAL_WBM_REL_HTT_TX_COMP_STATUS_DROP: + case HAL_WBM_REL_HTT_TX_COMP_STATUS_TTL: + ts.acked = (wbm_status == HAL_WBM_REL_HTT_TX_COMP_STATUS_OK); + ts.msdu_id = msdu_id; + ts.ack_rssi = FIELD_GET(HTT_TX_WBM_COMP_INFO1_ACK_RSSI, + status_desc->info1); + ath11k_dp_tx_htt_tx_complete_buf(ab, tx_ring, &ts); + break; + case HAL_WBM_REL_HTT_TX_COMP_STATUS_REINJ: + case HAL_WBM_REL_HTT_TX_COMP_STATUS_INSPECT: + ath11k_dp_tx_free_txbuf(ab, mac_id, msdu_id, tx_ring); + break; + case HAL_WBM_REL_HTT_TX_COMP_STATUS_MEC_NOTIFY: + /* This event is to be handled only when the driver decides to + * use WDS offload functionality. + */ + break; + default: + ath11k_warn(ab, "Unknown htt tx status %d\n", wbm_status); + break; + } +} + +static void ath11k_dp_tx_cache_peer_stats(struct ath11k *ar, + struct sk_buff *msdu, + struct hal_tx_status *ts) +{ + struct ath11k_per_peer_tx_stats *peer_stats = &ar->cached_stats; + + if (ts->try_cnt > 1) { + peer_stats->retry_pkts += ts->try_cnt - 1; + peer_stats->retry_bytes += (ts->try_cnt - 1) * msdu->len; + + if (ts->status != HAL_WBM_TQM_REL_REASON_FRAME_ACKED) { + peer_stats->failed_pkts += 1; + peer_stats->failed_bytes += msdu->len; + } + } +} + +static void ath11k_dp_tx_complete_msdu(struct ath11k *ar, + struct sk_buff *msdu, + struct hal_tx_status *ts) +{ + struct ath11k_base *ab = ar->ab; + struct ieee80211_tx_info *info; + struct ath11k_skb_cb *skb_cb; + + if (WARN_ON_ONCE(ts->buf_rel_source != HAL_WBM_REL_SRC_MODULE_TQM)) { + /* Must not happen */ + return; + } + + skb_cb = ATH11K_SKB_CB(msdu); + + dma_unmap_single(ab->dev, skb_cb->paddr, msdu->len, DMA_TO_DEVICE); + + rcu_read_lock(); + + if (!rcu_dereference(ab->pdevs_active[ar->pdev_idx])) { + dev_kfree_skb_any(msdu); + goto exit; + } + + if (!skb_cb->vif) { + dev_kfree_skb_any(msdu); + goto exit; + } + + info = IEEE80211_SKB_CB(msdu); + memset(&info->status, 0, sizeof(info->status)); + + /* skip tx rate update from ieee80211_status*/ + info->status.rates[0].idx = -1; + + if (ts->status == HAL_WBM_TQM_REL_REASON_FRAME_ACKED && + !(info->flags & IEEE80211_TX_CTL_NO_ACK)) { + info->flags |= IEEE80211_TX_STAT_ACK; + info->status.ack_signal = ATH11K_DEFAULT_NOISE_FLOOR + + ts->ack_rssi; + info->status.is_valid_ack_signal = true; + } + + if (ts->status == HAL_WBM_TQM_REL_REASON_CMD_REMOVE_TX && + (info->flags & IEEE80211_TX_CTL_NO_ACK)) + info->flags |= IEEE80211_TX_STAT_NOACK_TRANSMITTED; + + if (ath11k_debug_is_extd_tx_stats_enabled(ar)) { + if (ts->flags & HAL_TX_STATUS_FLAGS_FIRST_MSDU) { + if (ar->last_ppdu_id == 0) { + ar->last_ppdu_id = ts->ppdu_id; + } else if (ar->last_ppdu_id == ts->ppdu_id || + ar->cached_ppdu_id == ar->last_ppdu_id) { + ar->cached_ppdu_id = ar->last_ppdu_id; + ar->cached_stats.is_ampdu = true; + ath11k_update_per_peer_stats_from_txcompl(ar, msdu, ts); + memset(&ar->cached_stats, 0, + sizeof(struct ath11k_per_peer_tx_stats)); + } else { + ar->cached_stats.is_ampdu = false; + ath11k_update_per_peer_stats_from_txcompl(ar, msdu, ts); + memset(&ar->cached_stats, 0, + sizeof(struct ath11k_per_peer_tx_stats)); + } + ar->last_ppdu_id = ts->ppdu_id; + } + + ath11k_dp_tx_cache_peer_stats(ar, msdu, ts); + } + + /* NOTE: Tx rate status reporting. Tx completion status does not have + * necessary information (for example nss) to build the tx rate. + * Might end up reporting it out-of-band from HTT stats. + */ + + ieee80211_tx_status(ar->hw, msdu); + +exit: + rcu_read_unlock(); +} + +void ath11k_dp_tx_completion_handler(struct ath11k_base *ab, int ring_id) +{ + struct ath11k *ar; + struct ath11k_dp *dp = &ab->dp; + int hal_ring_id = dp->tx_ring[ring_id].tcl_comp_ring.ring_id; + struct hal_srng *status_ring = &ab->hal.srng_list[hal_ring_id]; + struct sk_buff *msdu; + struct hal_wbm_release_ring tx_status; + struct hal_tx_status ts; + struct dp_tx_ring *tx_ring = &dp->tx_ring[ring_id]; + u32 *desc; + u32 msdu_id; + u8 mac_id; + + spin_lock_bh(&status_ring->lock); + + ath11k_hal_srng_access_begin(ab, status_ring); + + spin_lock_bh(&tx_ring->tx_status_lock); + while (!kfifo_is_full(&tx_ring->tx_status_fifo) && + (desc = ath11k_hal_srng_dst_get_next_entry(ab, status_ring))) { + ath11k_hal_tx_status_desc_sync((void *)desc, + (void *)&tx_status); + kfifo_put(&tx_ring->tx_status_fifo, tx_status); + } + + if ((ath11k_hal_srng_dst_peek(ab, status_ring) != NULL) && + kfifo_is_full(&tx_ring->tx_status_fifo)) { + /* TODO: Process pending tx_status messages when kfifo_is_full() */ + ath11k_warn(ab, "Unable to process some of the tx_status ring desc because status_fifo is full\n"); + } + + spin_unlock_bh(&tx_ring->tx_status_lock); + + ath11k_hal_srng_access_end(ab, status_ring); + spin_unlock_bh(&status_ring->lock); + + spin_lock_bh(&tx_ring->tx_status_lock); + while (kfifo_get(&tx_ring->tx_status_fifo, &tx_status)) { + memset(&ts, 0, sizeof(ts)); + ath11k_hal_tx_status_parse(ab, &tx_status, &ts); + + mac_id = FIELD_GET(DP_TX_DESC_ID_MAC_ID, ts.desc_id); + msdu_id = FIELD_GET(DP_TX_DESC_ID_MSDU_ID, ts.desc_id); + + if (ts.buf_rel_source == HAL_WBM_REL_SRC_MODULE_FW) { + ath11k_dp_tx_process_htt_tx_complete(ab, + (void *)&tx_status, + mac_id, msdu_id, + tx_ring); + continue; + } + + spin_lock_bh(&tx_ring->tx_idr_lock); + msdu = idr_find(&tx_ring->txbuf_idr, msdu_id); + if (!msdu) { + ath11k_warn(ab, "tx completion for unknown msdu_id %d\n", + msdu_id); + spin_unlock_bh(&tx_ring->tx_idr_lock); + continue; + } + idr_remove(&tx_ring->txbuf_idr, msdu_id); + tx_ring->num_tx_pending--; + spin_unlock_bh(&tx_ring->tx_idr_lock); + + ar = ab->pdevs[mac_id].ar; + + if (atomic_dec_and_test(&ar->dp.num_tx_pending)) + wake_up(&ar->dp.tx_empty_waitq); + + /* TODO: Locking optimization so that tx_completion for an msdu + * is not called with tx_status_lock acquired + */ + ath11k_dp_tx_complete_msdu(ar, msdu, &ts); + } + spin_unlock_bh(&tx_ring->tx_status_lock); +} + +int ath11k_dp_tx_send_reo_cmd(struct ath11k_base *ab, struct dp_rx_tid *rx_tid, + enum hal_reo_cmd_type type, + struct ath11k_hal_reo_cmd *cmd, + void (*cb)(struct ath11k_dp *, void *, + enum hal_reo_cmd_status)) +{ + struct ath11k_dp *dp = &ab->dp; + struct dp_reo_cmd *dp_cmd; + struct hal_srng *cmd_ring; + int cmd_num; + + cmd_ring = &ab->hal.srng_list[dp->reo_cmd_ring.ring_id]; + cmd_num = ath11k_hal_reo_cmd_send(ab, cmd_ring, type, cmd); + + /* reo cmd ring descriptors has cmd_num starting from 1 */ + if (cmd_num <= 0) + return -EINVAL; + + if (!cb) + return 0; + + /* Can this be optimized so that we keep the pending command list only + * for tid delete command to free up the resoruce on the command status + * indication? + */ + dp_cmd = kzalloc(sizeof(*dp_cmd), GFP_ATOMIC); + + if (!dp_cmd) + return -ENOMEM; + + memcpy(&dp_cmd->data, rx_tid, sizeof(struct dp_rx_tid)); + dp_cmd->cmd_num = cmd_num; + dp_cmd->handler = cb; + + spin_lock_bh(&dp->reo_cmd_lock); + list_add_tail(&dp_cmd->list, &dp->reo_cmd_list); + spin_unlock_bh(&dp->reo_cmd_lock); + + return 0; +} + +static int +ath11k_dp_tx_get_ring_id_type(struct ath11k_base *ab, + int mac_id, u32 ring_id, + enum hal_ring_type ring_type, + enum htt_srng_ring_type *htt_ring_type, + enum htt_srng_ring_id *htt_ring_id) +{ + int lmac_ring_id_offset = 0; + int ret = 0; + + switch (ring_type) { + case HAL_RXDMA_BUF: + lmac_ring_id_offset = mac_id * HAL_SRNG_RINGS_PER_LMAC; + if (!(ring_id == (HAL_SRNG_RING_ID_WMAC1_SW2RXDMA0_BUF + + lmac_ring_id_offset) || + ring_id == (HAL_SRNG_RING_ID_WMAC1_SW2RXDMA1_BUF + + lmac_ring_id_offset))) { + ret = -EINVAL; + } + *htt_ring_id = HTT_RXDMA_HOST_BUF_RING; + *htt_ring_type = HTT_SW_TO_HW_RING; + break; + case HAL_RXDMA_DST: + *htt_ring_id = HTT_RXDMA_NON_MONITOR_DEST_RING; + *htt_ring_type = HTT_HW_TO_SW_RING; + break; + case HAL_RXDMA_MONITOR_BUF: + *htt_ring_id = HTT_RXDMA_MONITOR_BUF_RING; + *htt_ring_type = HTT_SW_TO_HW_RING; + break; + case HAL_RXDMA_MONITOR_STATUS: + *htt_ring_id = HTT_RXDMA_MONITOR_STATUS_RING; + *htt_ring_type = HTT_SW_TO_HW_RING; + break; + case HAL_RXDMA_MONITOR_DST: + *htt_ring_id = HTT_RXDMA_MONITOR_DEST_RING; + *htt_ring_type = HTT_HW_TO_SW_RING; + break; + case HAL_RXDMA_MONITOR_DESC: + *htt_ring_id = HTT_RXDMA_MONITOR_DESC_RING; + *htt_ring_type = HTT_SW_TO_HW_RING; + break; + default: + ath11k_warn(ab, "Unsupported ring type in DP :%d\n", ring_type); + ret = -EINVAL; + } + return ret; +} + +int ath11k_dp_tx_htt_srng_setup(struct ath11k_base *ab, u32 ring_id, + int mac_id, enum hal_ring_type ring_type) +{ + struct htt_srng_setup_cmd *cmd; + struct hal_srng *srng = &ab->hal.srng_list[ring_id]; + struct hal_srng_params params; + struct sk_buff *skb; + u32 ring_entry_sz; + int len = sizeof(*cmd); + dma_addr_t hp_addr, tp_addr; + enum htt_srng_ring_type htt_ring_type; + enum htt_srng_ring_id htt_ring_id; + int ret = 0; + + skb = ath11k_htc_alloc_skb(ab, len); + if (!skb) + return -ENOMEM; + + memset(¶ms, 0, sizeof(params)); + ath11k_hal_srng_get_params(ab, srng, ¶ms); + + hp_addr = ath11k_hal_srng_get_hp_addr(ab, srng); + tp_addr = ath11k_hal_srng_get_tp_addr(ab, srng); + + if (ath11k_dp_tx_get_ring_id_type(ab, mac_id, ring_id, + ring_type, &htt_ring_type, + &htt_ring_id)) + goto err_free; + + skb_put(skb, len); + cmd = (struct htt_srng_setup_cmd *)skb->data; + cmd->info0 = FIELD_PREP(HTT_SRNG_SETUP_CMD_INFO0_MSG_TYPE, + HTT_H2T_MSG_TYPE_SRING_SETUP); + if (htt_ring_type == HTT_SW_TO_HW_RING || + htt_ring_type == HTT_HW_TO_SW_RING) + cmd->info0 |= FIELD_PREP(HTT_SRNG_SETUP_CMD_INFO0_PDEV_ID, + DP_SW2HW_MACID(mac_id)); + else + cmd->info0 |= FIELD_PREP(HTT_SRNG_SETUP_CMD_INFO0_PDEV_ID, + mac_id); + cmd->info0 |= FIELD_PREP(HTT_SRNG_SETUP_CMD_INFO0_RING_TYPE, + htt_ring_type); + cmd->info0 |= FIELD_PREP(HTT_SRNG_SETUP_CMD_INFO0_RING_ID, htt_ring_id); + + cmd->ring_base_addr_lo = params.ring_base_paddr & + HAL_ADDR_LSB_REG_MASK; + + cmd->ring_base_addr_hi = (u64)params.ring_base_paddr >> + HAL_ADDR_MSB_REG_SHIFT; + + ret = ath11k_hal_srng_get_entrysize(ring_type); + if (ret < 0) + return -EINVAL; + + ring_entry_sz = ret; + + ring_entry_sz >>= 2; + cmd->info1 = FIELD_PREP(HTT_SRNG_SETUP_CMD_INFO1_RING_ENTRY_SIZE, + ring_entry_sz); + cmd->info1 |= FIELD_PREP(HTT_SRNG_SETUP_CMD_INFO1_RING_SIZE, + params.num_entries * ring_entry_sz); + cmd->info1 |= FIELD_PREP(HTT_SRNG_SETUP_CMD_INFO1_RING_FLAGS_MSI_SWAP, + !!(params.flags & HAL_SRNG_FLAGS_MSI_SWAP)); + cmd->info1 |= FIELD_PREP( + HTT_SRNG_SETUP_CMD_INFO1_RING_FLAGS_TLV_SWAP, + !!(params.flags & HAL_SRNG_FLAGS_DATA_TLV_SWAP)); + cmd->info1 |= FIELD_PREP( + HTT_SRNG_SETUP_CMD_INFO1_RING_FLAGS_HOST_FW_SWAP, + !!(params.flags & HAL_SRNG_FLAGS_RING_PTR_SWAP)); + if (htt_ring_type == HTT_SW_TO_HW_RING) + cmd->info1 |= HTT_SRNG_SETUP_CMD_INFO1_RING_LOOP_CNT_DIS; + + cmd->ring_head_off32_remote_addr_lo = hp_addr & HAL_ADDR_LSB_REG_MASK; + cmd->ring_head_off32_remote_addr_hi = (u64)hp_addr >> + HAL_ADDR_MSB_REG_SHIFT; + + cmd->ring_tail_off32_remote_addr_lo = tp_addr & HAL_ADDR_LSB_REG_MASK; + cmd->ring_tail_off32_remote_addr_hi = (u64)tp_addr >> + HAL_ADDR_MSB_REG_SHIFT; + + cmd->ring_msi_addr_lo = 0; + cmd->ring_msi_addr_hi = 0; + cmd->msi_data = 0; + + cmd->intr_info = FIELD_PREP( + HTT_SRNG_SETUP_CMD_INTR_INFO_BATCH_COUNTER_THRESH, + params.intr_batch_cntr_thres_entries * ring_entry_sz); + cmd->intr_info |= FIELD_PREP( + HTT_SRNG_SETUP_CMD_INTR_INFO_INTR_TIMER_THRESH, + params.intr_timer_thres_us >> 3); + + cmd->info2 = 0; + if (params.flags & HAL_SRNG_FLAGS_LOW_THRESH_INTR_EN) { + cmd->info2 = FIELD_PREP( + HTT_SRNG_SETUP_CMD_INFO2_INTR_LOW_THRESH, + params.low_threshold); + } + + ret = ath11k_htc_send(&ab->htc, ab->dp.eid, skb); + if (ret) + goto err_free; + + return 0; + +err_free: + dev_kfree_skb_any(skb); + + return ret; +} + +#define HTT_TARGET_VERSION_TIMEOUT_HZ (3 * HZ) + +int ath11k_dp_tx_htt_h2t_ver_req_msg(struct ath11k_base *ab) +{ + struct ath11k_dp *dp = &ab->dp; + struct sk_buff *skb; + struct htt_ver_req_cmd *cmd; + int len = sizeof(*cmd); + int ret; + + init_completion(&dp->htt_tgt_version_received); + + skb = ath11k_htc_alloc_skb(ab, len); + if (!skb) + return -ENOMEM; + + skb_put(skb, len); + cmd = (struct htt_ver_req_cmd *)skb->data; + cmd->ver_reg_info = FIELD_PREP(HTT_VER_REQ_INFO_MSG_ID, + HTT_H2T_MSG_TYPE_VERSION_REQ); + + ret = ath11k_htc_send(&ab->htc, dp->eid, skb); + if (ret) { + dev_kfree_skb_any(skb); + return ret; + } + + ret = wait_for_completion_timeout(&dp->htt_tgt_version_received, + HTT_TARGET_VERSION_TIMEOUT_HZ); + if (ret == 0) { + ath11k_warn(ab, "htt target version request timed out\n"); + return -ETIMEDOUT; + } + + if (dp->htt_tgt_ver_major != HTT_TARGET_VERSION_MAJOR) { + ath11k_err(ab, "unsupported htt major version %d supported version is %d\n", + dp->htt_tgt_ver_major, HTT_TARGET_VERSION_MAJOR); + return -ENOTSUPP; + } + + return 0; +} + +int ath11k_dp_tx_htt_h2t_ppdu_stats_req(struct ath11k *ar, u32 mask) +{ + struct ath11k_base *ab = ar->ab; + struct ath11k_dp *dp = &ab->dp; + struct sk_buff *skb; + struct htt_ppdu_stats_cfg_cmd *cmd; + int len = sizeof(*cmd); + u8 pdev_mask; + int ret; + + skb = ath11k_htc_alloc_skb(ab, len); + if (!skb) + return -ENOMEM; + + skb_put(skb, len); + cmd = (struct htt_ppdu_stats_cfg_cmd *)skb->data; + cmd->msg = FIELD_PREP(HTT_PPDU_STATS_CFG_MSG_TYPE, + HTT_H2T_MSG_TYPE_PPDU_STATS_CFG); + + pdev_mask = 1 << (ar->pdev_idx); + cmd->msg |= FIELD_PREP(HTT_PPDU_STATS_CFG_PDEV_ID, pdev_mask); + cmd->msg |= FIELD_PREP(HTT_PPDU_STATS_CFG_TLV_TYPE_BITMASK, mask); + + ret = ath11k_htc_send(&ab->htc, dp->eid, skb); + if (ret) { + dev_kfree_skb_any(skb); + return ret; + } + + return 0; +} + +int ath11k_dp_tx_htt_rx_filter_setup(struct ath11k_base *ab, u32 ring_id, + int mac_id, enum hal_ring_type ring_type, + int rx_buf_size, + struct htt_rx_ring_tlv_filter *tlv_filter) +{ + struct htt_rx_ring_selection_cfg_cmd *cmd; + struct hal_srng *srng = &ab->hal.srng_list[ring_id]; + struct hal_srng_params params; + struct sk_buff *skb; + int len = sizeof(*cmd); + enum htt_srng_ring_type htt_ring_type; + enum htt_srng_ring_id htt_ring_id; + int ret = 0; + + skb = ath11k_htc_alloc_skb(ab, len); + if (!skb) + return -ENOMEM; + + memset(¶ms, 0, sizeof(params)); + ath11k_hal_srng_get_params(ab, srng, ¶ms); + + if (ath11k_dp_tx_get_ring_id_type(ab, mac_id, ring_id, + ring_type, &htt_ring_type, + &htt_ring_id)) + goto err_free; + + skb_put(skb, len); + cmd = (struct htt_rx_ring_selection_cfg_cmd *)skb->data; + cmd->info0 = FIELD_PREP(HTT_RX_RING_SELECTION_CFG_CMD_INFO0_MSG_TYPE, + HTT_H2T_MSG_TYPE_RX_RING_SELECTION_CFG); + if (htt_ring_type == HTT_SW_TO_HW_RING || + htt_ring_type == HTT_HW_TO_SW_RING) + cmd->info0 |= + FIELD_PREP(HTT_RX_RING_SELECTION_CFG_CMD_INFO0_PDEV_ID, + DP_SW2HW_MACID(mac_id)); + else + cmd->info0 |= + FIELD_PREP(HTT_RX_RING_SELECTION_CFG_CMD_INFO0_PDEV_ID, + mac_id); + cmd->info0 |= FIELD_PREP(HTT_RX_RING_SELECTION_CFG_CMD_INFO0_RING_ID, + htt_ring_id); + cmd->info0 |= FIELD_PREP(HTT_RX_RING_SELECTION_CFG_CMD_INFO0_SS, + !!(params.flags & HAL_SRNG_FLAGS_MSI_SWAP)); + cmd->info0 |= FIELD_PREP(HTT_RX_RING_SELECTION_CFG_CMD_INFO0_PS, + !!(params.flags & HAL_SRNG_FLAGS_DATA_TLV_SWAP)); + + cmd->info1 = FIELD_PREP(HTT_RX_RING_SELECTION_CFG_CMD_INFO1_BUF_SIZE, + rx_buf_size); + cmd->pkt_type_en_flags0 = tlv_filter->pkt_filter_flags0; + cmd->pkt_type_en_flags1 = tlv_filter->pkt_filter_flags1; + cmd->pkt_type_en_flags2 = tlv_filter->pkt_filter_flags2; + cmd->pkt_type_en_flags3 = tlv_filter->pkt_filter_flags3; + cmd->rx_filter_tlv = tlv_filter->rx_filter; + + ret = ath11k_htc_send(&ab->htc, ab->dp.eid, skb); + if (ret) + goto err_free; + + return 0; + +err_free: + dev_kfree_skb_any(skb); + + return ret; +} + +int +ath11k_dp_tx_htt_h2t_ext_stats_req(struct ath11k *ar, u8 type, + struct htt_ext_stats_cfg_params *cfg_params, + u64 cookie) +{ + struct ath11k_base *ab = ar->ab; + struct ath11k_dp *dp = &ab->dp; + struct sk_buff *skb; + struct htt_ext_stats_cfg_cmd *cmd; + int len = sizeof(*cmd); + int ret; + + skb = ath11k_htc_alloc_skb(ab, len); + if (!skb) + return -ENOMEM; + + skb_put(skb, len); + + cmd = (struct htt_ext_stats_cfg_cmd *)skb->data; + memset(cmd, 0, sizeof(*cmd)); + cmd->hdr.msg_type = HTT_H2T_MSG_TYPE_EXT_STATS_CFG; + + cmd->hdr.pdev_mask = 1 << ar->pdev->pdev_id; + + cmd->hdr.stats_type = type; + cmd->cfg_param0 = cfg_params->cfg0; + cmd->cfg_param1 = cfg_params->cfg1; + cmd->cfg_param2 = cfg_params->cfg2; + cmd->cfg_param3 = cfg_params->cfg3; + cmd->cookie_lsb = lower_32_bits(cookie); + cmd->cookie_msb = upper_32_bits(cookie); + + ret = ath11k_htc_send(&ab->htc, dp->eid, skb); + if (ret) { + ath11k_warn(ab, "failed to send htt type stats request: %d", + ret); + dev_kfree_skb_any(skb); + return ret; + } + + return 0; +} + +int ath11k_dp_tx_htt_monitor_mode_ring_config(struct ath11k *ar, bool reset) +{ + struct ath11k_pdev_dp *dp = &ar->dp; + struct htt_rx_ring_tlv_filter tlv_filter = {0}; + int ret = 0, ring_id = 0; + + ring_id = dp->rxdma_mon_buf_ring.refill_buf_ring.ring_id; + + if (!reset) { + tlv_filter.rx_filter = HTT_RX_MON_FILTER_TLV_FLAGS_MON_BUF_RING; + tlv_filter.pkt_filter_flags0 = + HTT_RX_MON_FP_MGMT_FILTER_FLAGS0 | + HTT_RX_MON_MO_MGMT_FILTER_FLAGS0; + tlv_filter.pkt_filter_flags1 = + HTT_RX_MON_FP_MGMT_FILTER_FLAGS1 | + HTT_RX_MON_MO_MGMT_FILTER_FLAGS1; + tlv_filter.pkt_filter_flags2 = + HTT_RX_MON_FP_CTRL_FILTER_FLASG2 | + HTT_RX_MON_MO_CTRL_FILTER_FLASG2; + tlv_filter.pkt_filter_flags3 = + HTT_RX_MON_FP_CTRL_FILTER_FLASG3 | + HTT_RX_MON_MO_CTRL_FILTER_FLASG3 | + HTT_RX_MON_FP_DATA_FILTER_FLASG3 | + HTT_RX_MON_MO_DATA_FILTER_FLASG3; + } + + ret = ath11k_dp_tx_htt_rx_filter_setup(ar->ab, ring_id, dp->mac_id, + HAL_RXDMA_MONITOR_BUF, + DP_RXDMA_REFILL_RING_SIZE, + &tlv_filter); + if (ret) + return ret; + + ring_id = dp->rx_mon_status_refill_ring.refill_buf_ring.ring_id; + if (!reset) + tlv_filter.rx_filter = + HTT_RX_MON_FILTER_TLV_FLAGS_MON_STATUS_RING; + else + tlv_filter = ath11k_mac_mon_status_filter_default; + + ret = ath11k_dp_tx_htt_rx_filter_setup(ar->ab, ring_id, dp->mac_id, + HAL_RXDMA_MONITOR_STATUS, + DP_RXDMA_REFILL_RING_SIZE, + &tlv_filter); + return ret; +} diff --git a/drivers/net/wireless/ath/ath11k/dp_tx.h b/drivers/net/wireless/ath/ath11k/dp_tx.h new file mode 100644 index 0000000000000..f8a9f9c8e444e --- /dev/null +++ b/drivers/net/wireless/ath/ath11k/dp_tx.h @@ -0,0 +1,40 @@ +/* SPDX-License-Identifier: BSD-3-Clause-Clear */ +/* + * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. + */ + +#ifndef ATH11K_DP_TX_H +#define ATH11K_DP_TX_H + +#include "core.h" +#include "hal_tx.h" + +struct ath11k_dp_htt_wbm_tx_status { + u32 msdu_id; + bool acked; + int ack_rssi; +}; + +int ath11k_dp_tx_htt_h2t_ver_req_msg(struct ath11k_base *ab); +int ath11k_dp_tx(struct ath11k *ar, struct ath11k_vif *arvif, + struct sk_buff *skb); +void ath11k_dp_tx_completion_handler(struct ath11k_base *ab, int ring_id); +int ath11k_dp_tx_send_reo_cmd(struct ath11k_base *ab, struct dp_rx_tid *rx_tid, + enum hal_reo_cmd_type type, + struct ath11k_hal_reo_cmd *cmd, + void (*func)(struct ath11k_dp *, void *, + enum hal_reo_cmd_status)); + +int ath11k_dp_tx_htt_h2t_ppdu_stats_req(struct ath11k *ar, u32 mask); +int +ath11k_dp_tx_htt_h2t_ext_stats_req(struct ath11k *ar, u8 type, + struct htt_ext_stats_cfg_params *cfg_params, + u64 cookie); +int ath11k_dp_tx_htt_monitor_mode_ring_config(struct ath11k *ar, bool reset); + +int ath11k_dp_tx_htt_rx_filter_setup(struct ath11k_base *ab, u32 ring_id, + int mac_id, enum hal_ring_type ring_type, + int rx_buf_size, + struct htt_rx_ring_tlv_filter *tlv_filter); + +#endif diff --git a/drivers/net/wireless/ath/ath11k/hal.c b/drivers/net/wireless/ath/ath11k/hal.c new file mode 100644 index 0000000000000..b58ac11c2747e --- /dev/null +++ b/drivers/net/wireless/ath/ath11k/hal.c @@ -0,0 +1,1124 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* + * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. + */ +#include +#include "ahb.h" +#include "hal_tx.h" +#include "debug.h" +#include "hal_desc.h" + +static const struct hal_srng_config hw_srng_config[] = { + /* TODO: max_rings can populated by querying HW capabilities */ + { /* REO_DST */ + .start_ring_id = HAL_SRNG_RING_ID_REO2SW1, + .max_rings = 4, + .entry_size = sizeof(struct hal_reo_dest_ring) >> 2, + .lmac_ring = false, + .ring_dir = HAL_SRNG_DIR_DST, + .reg_start = { + HAL_SEQ_WCSS_UMAC_REO_REG + HAL_REO1_RING_BASE_LSB, + HAL_SEQ_WCSS_UMAC_REO_REG + HAL_REO1_RING_HP, + }, + .reg_size = { + HAL_REO2_RING_BASE_LSB - HAL_REO1_RING_BASE_LSB, + HAL_REO2_RING_HP - HAL_REO1_RING_HP, + }, + .max_size = HAL_REO_REO2SW1_RING_BASE_MSB_RING_SIZE, + }, + { /* REO_EXCEPTION */ + /* Designating REO2TCL ring as exception ring. This ring is + * similar to other REO2SW rings though it is named as REO2TCL. + * Any of theREO2SW rings can be used as exception ring. + */ + .start_ring_id = HAL_SRNG_RING_ID_REO2TCL, + .max_rings = 1, + .entry_size = sizeof(struct hal_reo_dest_ring) >> 2, + .lmac_ring = false, + .ring_dir = HAL_SRNG_DIR_DST, + .reg_start = { + HAL_SEQ_WCSS_UMAC_REO_REG + HAL_REO_TCL_RING_BASE_LSB, + HAL_SEQ_WCSS_UMAC_REO_REG + HAL_REO_TCL_RING_HP, + }, + .max_size = HAL_REO_REO2TCL_RING_BASE_MSB_RING_SIZE, + }, + { /* REO_REINJECT */ + .start_ring_id = HAL_SRNG_RING_ID_SW2REO, + .max_rings = 1, + .entry_size = sizeof(struct hal_reo_entrance_ring) >> 2, + .lmac_ring = false, + .ring_dir = HAL_SRNG_DIR_SRC, + .reg_start = { + HAL_SEQ_WCSS_UMAC_REO_REG + HAL_SW2REO_RING_BASE_LSB, + HAL_SEQ_WCSS_UMAC_REO_REG + HAL_SW2REO_RING_HP, + }, + .max_size = HAL_REO_SW2REO_RING_BASE_MSB_RING_SIZE, + }, + { /* REO_CMD */ + .start_ring_id = HAL_SRNG_RING_ID_REO_CMD, + .max_rings = 1, + .entry_size = (sizeof(struct hal_tlv_hdr) + + sizeof(struct hal_reo_get_queue_stats)) >> 2, + .lmac_ring = false, + .ring_dir = HAL_SRNG_DIR_SRC, + .reg_start = { + HAL_SEQ_WCSS_UMAC_REO_REG + HAL_REO_CMD_RING_BASE_LSB, + HAL_SEQ_WCSS_UMAC_REO_REG + HAL_REO_CMD_HP, + }, + .max_size = HAL_REO_CMD_RING_BASE_MSB_RING_SIZE, + }, + { /* REO_STATUS */ + .start_ring_id = HAL_SRNG_RING_ID_REO_STATUS, + .max_rings = 1, + .entry_size = (sizeof(struct hal_tlv_hdr) + + sizeof(struct hal_reo_get_queue_stats_status)) >> 2, + .lmac_ring = false, + .ring_dir = HAL_SRNG_DIR_DST, + .reg_start = { + HAL_SEQ_WCSS_UMAC_REO_REG + + HAL_REO_STATUS_RING_BASE_LSB, + HAL_SEQ_WCSS_UMAC_REO_REG + HAL_REO_STATUS_HP, + }, + .max_size = HAL_REO_STATUS_RING_BASE_MSB_RING_SIZE, + }, + { /* TCL_DATA */ + .start_ring_id = HAL_SRNG_RING_ID_SW2TCL1, + .max_rings = 3, + .entry_size = (sizeof(struct hal_tlv_hdr) + + sizeof(struct hal_tcl_data_cmd)) >> 2, + .lmac_ring = false, + .ring_dir = HAL_SRNG_DIR_SRC, + .reg_start = { + HAL_SEQ_WCSS_UMAC_TCL_REG + HAL_TCL1_RING_BASE_LSB, + HAL_SEQ_WCSS_UMAC_TCL_REG + HAL_TCL1_RING_HP, + }, + .reg_size = { + HAL_TCL2_RING_BASE_LSB - HAL_TCL1_RING_BASE_LSB, + HAL_TCL2_RING_HP - HAL_TCL1_RING_HP, + }, + .max_size = HAL_SW2TCL1_RING_BASE_MSB_RING_SIZE, + }, + { /* TCL_CMD */ + .start_ring_id = HAL_SRNG_RING_ID_SW2TCL_CMD, + .max_rings = 1, + .entry_size = (sizeof(struct hal_tlv_hdr) + + sizeof(struct hal_tcl_gse_cmd)) >> 2, + .lmac_ring = false, + .ring_dir = HAL_SRNG_DIR_SRC, + .reg_start = { + HAL_SEQ_WCSS_UMAC_TCL_REG + HAL_TCL_RING_BASE_LSB, + HAL_SEQ_WCSS_UMAC_TCL_REG + HAL_TCL_RING_HP, + }, + .max_size = HAL_SW2TCL1_CMD_RING_BASE_MSB_RING_SIZE, + }, + { /* TCL_STATUS */ + .start_ring_id = HAL_SRNG_RING_ID_TCL_STATUS, + .max_rings = 1, + .entry_size = (sizeof(struct hal_tlv_hdr) + + sizeof(struct hal_tcl_status_ring)) >> 2, + .lmac_ring = false, + .ring_dir = HAL_SRNG_DIR_DST, + .reg_start = { + HAL_SEQ_WCSS_UMAC_TCL_REG + + HAL_TCL_STATUS_RING_BASE_LSB, + HAL_SEQ_WCSS_UMAC_TCL_REG + HAL_TCL_STATUS_RING_HP, + }, + .max_size = HAL_TCL_STATUS_RING_BASE_MSB_RING_SIZE, + }, + { /* CE_SRC */ + .start_ring_id = HAL_SRNG_RING_ID_CE0_SRC, + .max_rings = 12, + .entry_size = sizeof(struct hal_ce_srng_src_desc) >> 2, + .lmac_ring = false, + .ring_dir = HAL_SRNG_DIR_SRC, + .reg_start = { + (HAL_SEQ_WCSS_UMAC_CE0_SRC_REG + + HAL_CE_DST_RING_BASE_LSB), + HAL_SEQ_WCSS_UMAC_CE0_SRC_REG + HAL_CE_DST_RING_HP, + }, + .reg_size = { + (HAL_SEQ_WCSS_UMAC_CE1_SRC_REG - + HAL_SEQ_WCSS_UMAC_CE0_SRC_REG), + (HAL_SEQ_WCSS_UMAC_CE1_SRC_REG - + HAL_SEQ_WCSS_UMAC_CE0_SRC_REG), + }, + .max_size = HAL_CE_SRC_RING_BASE_MSB_RING_SIZE, + }, + { /* CE_DST */ + .start_ring_id = HAL_SRNG_RING_ID_CE0_DST, + .max_rings = 12, + .entry_size = sizeof(struct hal_ce_srng_dest_desc) >> 2, + .lmac_ring = false, + .ring_dir = HAL_SRNG_DIR_SRC, + .reg_start = { + (HAL_SEQ_WCSS_UMAC_CE0_DST_REG + + HAL_CE_DST_RING_BASE_LSB), + HAL_SEQ_WCSS_UMAC_CE0_DST_REG + HAL_CE_DST_RING_HP, + }, + .reg_size = { + (HAL_SEQ_WCSS_UMAC_CE1_DST_REG - + HAL_SEQ_WCSS_UMAC_CE0_DST_REG), + (HAL_SEQ_WCSS_UMAC_CE1_DST_REG - + HAL_SEQ_WCSS_UMAC_CE0_DST_REG), + }, + .max_size = HAL_CE_DST_RING_BASE_MSB_RING_SIZE, + }, + { /* CE_DST_STATUS */ + .start_ring_id = HAL_SRNG_RING_ID_CE0_DST_STATUS, + .max_rings = 12, + .entry_size = sizeof(struct hal_ce_srng_dst_status_desc) >> 2, + .lmac_ring = false, + .ring_dir = HAL_SRNG_DIR_DST, + .reg_start = { + (HAL_SEQ_WCSS_UMAC_CE0_DST_REG + + HAL_CE_DST_STATUS_RING_BASE_LSB), + (HAL_SEQ_WCSS_UMAC_CE0_DST_REG + + HAL_CE_DST_STATUS_RING_HP), + }, + .reg_size = { + (HAL_SEQ_WCSS_UMAC_CE1_DST_REG - + HAL_SEQ_WCSS_UMAC_CE0_DST_REG), + (HAL_SEQ_WCSS_UMAC_CE1_DST_REG - + HAL_SEQ_WCSS_UMAC_CE0_DST_REG), + }, + .max_size = HAL_CE_DST_STATUS_RING_BASE_MSB_RING_SIZE, + }, + { /* WBM_IDLE_LINK */ + .start_ring_id = HAL_SRNG_RING_ID_WBM_IDLE_LINK, + .max_rings = 1, + .entry_size = sizeof(struct hal_wbm_link_desc) >> 2, + .lmac_ring = false, + .ring_dir = HAL_SRNG_DIR_SRC, + .reg_start = { + (HAL_SEQ_WCSS_UMAC_WBM_REG + + HAL_WBM_IDLE_LINK_RING_BASE_LSB), + (HAL_SEQ_WCSS_UMAC_WBM_REG + HAL_WBM_IDLE_LINK_RING_HP), + }, + .max_size = HAL_WBM_IDLE_LINK_RING_BASE_MSB_RING_SIZE, + }, + { /* SW2WBM_RELEASE */ + .start_ring_id = HAL_SRNG_RING_ID_WBM_SW_RELEASE, + .max_rings = 1, + .entry_size = sizeof(struct hal_wbm_release_ring) >> 2, + .lmac_ring = false, + .ring_dir = HAL_SRNG_DIR_SRC, + .reg_start = { + (HAL_SEQ_WCSS_UMAC_WBM_REG + + HAL_WBM_RELEASE_RING_BASE_LSB), + (HAL_SEQ_WCSS_UMAC_WBM_REG + HAL_WBM_RELEASE_RING_HP), + }, + .max_size = HAL_SW2WBM_RELEASE_RING_BASE_MSB_RING_SIZE, + }, + { /* WBM2SW_RELEASE */ + .start_ring_id = HAL_SRNG_RING_ID_WBM2SW0_RELEASE, + .max_rings = 4, + .entry_size = sizeof(struct hal_wbm_release_ring) >> 2, + .lmac_ring = false, + .ring_dir = HAL_SRNG_DIR_DST, + .reg_start = { + (HAL_SEQ_WCSS_UMAC_WBM_REG + + HAL_WBM0_RELEASE_RING_BASE_LSB), + (HAL_SEQ_WCSS_UMAC_WBM_REG + HAL_WBM0_RELEASE_RING_HP), + }, + .reg_size = { + (HAL_WBM1_RELEASE_RING_BASE_LSB - + HAL_WBM0_RELEASE_RING_BASE_LSB), + (HAL_WBM1_RELEASE_RING_HP - HAL_WBM0_RELEASE_RING_HP), + }, + .max_size = HAL_WBM2SW_RELEASE_RING_BASE_MSB_RING_SIZE, + }, + { /* RXDMA_BUF */ + .start_ring_id = HAL_SRNG_RING_ID_WMAC1_SW2RXDMA0_BUF, + .max_rings = 2, + .entry_size = sizeof(struct hal_wbm_buffer_ring) >> 2, + .lmac_ring = true, + .ring_dir = HAL_SRNG_DIR_SRC, + .max_size = HAL_RXDMA_RING_MAX_SIZE, + }, + { /* RXDMA_DST */ + .start_ring_id = HAL_SRNG_RING_ID_WMAC1_RXDMA2SW0, + .max_rings = 1, + .entry_size = sizeof(struct hal_reo_entrance_ring) >> 2, + .lmac_ring = true, + .ring_dir = HAL_SRNG_DIR_DST, + .max_size = HAL_RXDMA_RING_MAX_SIZE, + }, + { /* RXDMA_MONITOR_BUF */ + .start_ring_id = HAL_SRNG_RING_ID_WMAC1_SW2RXDMA2_BUF, + .max_rings = 1, + .entry_size = sizeof(struct hal_wbm_buffer_ring) >> 2, + .lmac_ring = true, + .ring_dir = HAL_SRNG_DIR_SRC, + .max_size = HAL_RXDMA_RING_MAX_SIZE, + }, + { /* RXDMA_MONITOR_STATUS */ + .start_ring_id = HAL_SRNG_RING_ID_WMAC1_SW2RXDMA1_STATBUF, + .max_rings = 1, + .entry_size = sizeof(struct hal_wbm_buffer_ring) >> 2, + .lmac_ring = true, + .ring_dir = HAL_SRNG_DIR_SRC, + .max_size = HAL_RXDMA_RING_MAX_SIZE, + }, + { /* RXDMA_MONITOR_DST */ + .start_ring_id = HAL_SRNG_RING_ID_WMAC1_RXDMA2SW1, + .max_rings = 1, + .entry_size = sizeof(struct hal_reo_entrance_ring) >> 2, + .lmac_ring = true, + .ring_dir = HAL_SRNG_DIR_DST, + .max_size = HAL_RXDMA_RING_MAX_SIZE, + }, + { /* RXDMA_MONITOR_DESC */ + .start_ring_id = HAL_SRNG_RING_ID_WMAC1_SW2RXDMA1_DESC, + .max_rings = 1, + .entry_size = sizeof(struct hal_wbm_buffer_ring) >> 2, + .lmac_ring = true, + .ring_dir = HAL_SRNG_DIR_SRC, + .max_size = HAL_RXDMA_RING_MAX_SIZE, + }, + { /* RXDMA DIR BUF */ + .start_ring_id = HAL_SRNG_RING_ID_RXDMA_DIR_BUF, + .max_rings = 1, + .entry_size = 8 >> 2, /* TODO: Define the struct */ + .lmac_ring = true, + .ring_dir = HAL_SRNG_DIR_SRC, + .max_size = HAL_RXDMA_RING_MAX_SIZE, + }, +}; + +static int ath11k_hal_alloc_cont_rdp(struct ath11k_base *ab) +{ + struct ath11k_hal *hal = &ab->hal; + size_t size; + + size = sizeof(u32) * HAL_SRNG_RING_ID_MAX; + hal->rdp.vaddr = dma_alloc_coherent(ab->dev, size, &hal->rdp.paddr, + GFP_KERNEL); + if (!hal->rdp.vaddr) + return -ENOMEM; + + return 0; +} + +static void ath11k_hal_free_cont_rdp(struct ath11k_base *ab) +{ + struct ath11k_hal *hal = &ab->hal; + size_t size; + + if (!hal->rdp.vaddr) + return; + + size = sizeof(u32) * HAL_SRNG_RING_ID_MAX; + dma_free_coherent(ab->dev, size, + hal->rdp.vaddr, hal->rdp.paddr); + hal->rdp.vaddr = NULL; +} + +static int ath11k_hal_alloc_cont_wrp(struct ath11k_base *ab) +{ + struct ath11k_hal *hal = &ab->hal; + size_t size; + + size = sizeof(u32) * HAL_SRNG_NUM_LMAC_RINGS; + hal->wrp.vaddr = dma_alloc_coherent(ab->dev, size, &hal->wrp.paddr, + GFP_KERNEL); + if (!hal->wrp.vaddr) + return -ENOMEM; + + return 0; +} + +static void ath11k_hal_free_cont_wrp(struct ath11k_base *ab) +{ + struct ath11k_hal *hal = &ab->hal; + size_t size; + + if (!hal->wrp.vaddr) + return; + + size = sizeof(u32) * HAL_SRNG_NUM_LMAC_RINGS; + dma_free_coherent(ab->dev, size, + hal->wrp.vaddr, hal->wrp.paddr); + hal->wrp.vaddr = NULL; +} + +static void ath11k_hal_ce_dst_setup(struct ath11k_base *ab, + struct hal_srng *srng, int ring_num) +{ + const struct hal_srng_config *srng_config = &hw_srng_config[HAL_CE_DST]; + u32 addr; + u32 val; + + addr = HAL_CE_DST_RING_CTRL + + srng_config->reg_start[HAL_SRNG_REG_GRP_R0] + + ring_num * srng_config->reg_size[HAL_SRNG_REG_GRP_R0]; + val = ath11k_ahb_read32(ab, addr); + val &= ~HAL_CE_DST_R0_DEST_CTRL_MAX_LEN; + val |= FIELD_PREP(HAL_CE_DST_R0_DEST_CTRL_MAX_LEN, + srng->u.dst_ring.max_buffer_length); + ath11k_ahb_write32(ab, addr, val); +} + +static void ath11k_hal_srng_dst_hw_init(struct ath11k_base *ab, + struct hal_srng *srng) +{ + struct ath11k_hal *hal = &ab->hal; + u32 val; + u64 hp_addr; + u32 reg_base; + + reg_base = srng->hwreg_base[HAL_SRNG_REG_GRP_R0]; + + if (srng->flags & HAL_SRNG_FLAGS_MSI_INTR) { + ath11k_ahb_write32(ab, reg_base + + HAL_REO1_RING_MSI1_BASE_LSB_OFFSET, + (u32)srng->msi_addr); + + val = FIELD_PREP(HAL_REO1_RING_MSI1_BASE_MSB_ADDR, + ((u64)srng->msi_addr >> + HAL_ADDR_MSB_REG_SHIFT)) | + HAL_REO1_RING_MSI1_BASE_MSB_MSI1_ENABLE; + ath11k_ahb_write32(ab, reg_base + + HAL_REO1_RING_MSI1_BASE_MSB_OFFSET, val); + + ath11k_ahb_write32(ab, + reg_base + HAL_REO1_RING_MSI1_DATA_OFFSET, + srng->msi_data); + } + + ath11k_ahb_write32(ab, reg_base, (u32)srng->ring_base_paddr); + + val = FIELD_PREP(HAL_REO1_RING_BASE_MSB_RING_BASE_ADDR_MSB, + ((u64)srng->ring_base_paddr >> + HAL_ADDR_MSB_REG_SHIFT)) | + FIELD_PREP(HAL_REO1_RING_BASE_MSB_RING_SIZE, + (srng->entry_size * srng->num_entries)); + ath11k_ahb_write32(ab, reg_base + HAL_REO1_RING_BASE_MSB_OFFSET, val); + + val = FIELD_PREP(HAL_REO1_RING_ID_RING_ID, srng->ring_id) | + FIELD_PREP(HAL_REO1_RING_ID_ENTRY_SIZE, srng->entry_size); + ath11k_ahb_write32(ab, reg_base + HAL_REO1_RING_ID_OFFSET, val); + + /* interrupt setup */ + val = FIELD_PREP(HAL_REO1_RING_PRDR_INT_SETUP_INTR_TMR_THOLD, + (srng->intr_timer_thres_us >> 3)); + + val |= FIELD_PREP(HAL_REO1_RING_PRDR_INT_SETUP_BATCH_COUNTER_THOLD, + (srng->intr_batch_cntr_thres_entries * + srng->entry_size)); + + ath11k_ahb_write32(ab, + reg_base + HAL_REO1_RING_PRODUCER_INT_SETUP_OFFSET, + val); + + hp_addr = hal->rdp.paddr + + ((unsigned long)srng->u.dst_ring.hp_addr - + (unsigned long)hal->rdp.vaddr); + ath11k_ahb_write32(ab, reg_base + HAL_REO1_RING_HP_ADDR_LSB_OFFSET, + hp_addr & HAL_ADDR_LSB_REG_MASK); + ath11k_ahb_write32(ab, reg_base + HAL_REO1_RING_HP_ADDR_MSB_OFFSET, + hp_addr >> HAL_ADDR_MSB_REG_SHIFT); + + /* Initialize head and tail pointers to indicate ring is empty */ + reg_base = srng->hwreg_base[HAL_SRNG_REG_GRP_R2]; + ath11k_ahb_write32(ab, reg_base, 0); + ath11k_ahb_write32(ab, reg_base + HAL_REO1_RING_TP_OFFSET, 0); + *srng->u.dst_ring.hp_addr = 0; + + reg_base = srng->hwreg_base[HAL_SRNG_REG_GRP_R0]; + val = 0; + if (srng->flags & HAL_SRNG_FLAGS_DATA_TLV_SWAP) + val |= HAL_REO1_RING_MISC_DATA_TLV_SWAP; + if (srng->flags & HAL_SRNG_FLAGS_RING_PTR_SWAP) + val |= HAL_REO1_RING_MISC_HOST_FW_SWAP; + if (srng->flags & HAL_SRNG_FLAGS_MSI_SWAP) + val |= HAL_REO1_RING_MISC_MSI_SWAP; + val |= HAL_REO1_RING_MISC_SRNG_ENABLE; + + ath11k_ahb_write32(ab, reg_base + HAL_REO1_RING_MISC_OFFSET, val); +} + +static void ath11k_hal_srng_src_hw_init(struct ath11k_base *ab, + struct hal_srng *srng) +{ + struct ath11k_hal *hal = &ab->hal; + u32 val; + u64 tp_addr; + u32 reg_base; + + reg_base = srng->hwreg_base[HAL_SRNG_REG_GRP_R0]; + + if (srng->flags & HAL_SRNG_FLAGS_MSI_INTR) { + ath11k_ahb_write32(ab, reg_base + + HAL_TCL1_RING_MSI1_BASE_LSB_OFFSET, + (u32)srng->msi_addr); + + val = FIELD_PREP(HAL_TCL1_RING_MSI1_BASE_MSB_ADDR, + ((u64)srng->msi_addr >> + HAL_ADDR_MSB_REG_SHIFT)) | + HAL_TCL1_RING_MSI1_BASE_MSB_MSI1_ENABLE; + ath11k_ahb_write32(ab, reg_base + + HAL_TCL1_RING_MSI1_BASE_MSB_OFFSET, + val); + + ath11k_ahb_write32(ab, reg_base + + HAL_TCL1_RING_MSI1_DATA_OFFSET, + srng->msi_data); + } + + ath11k_ahb_write32(ab, reg_base, (u32)srng->ring_base_paddr); + + val = FIELD_PREP(HAL_TCL1_RING_BASE_MSB_RING_BASE_ADDR_MSB, + ((u64)srng->ring_base_paddr >> + HAL_ADDR_MSB_REG_SHIFT)) | + FIELD_PREP(HAL_TCL1_RING_BASE_MSB_RING_SIZE, + (srng->entry_size * srng->num_entries)); + ath11k_ahb_write32(ab, reg_base + HAL_TCL1_RING_BASE_MSB_OFFSET, val); + + val = FIELD_PREP(HAL_REO1_RING_ID_ENTRY_SIZE, srng->entry_size); + ath11k_ahb_write32(ab, reg_base + HAL_TCL1_RING_ID_OFFSET, val); + + /* interrupt setup */ + /* NOTE: IPQ8074 v2 requires the interrupt timer threshold in the + * unit of 8 usecs instead of 1 usec (as required by v1). + */ + val = FIELD_PREP(HAL_TCL1_RING_CONSR_INT_SETUP_IX0_INTR_TMR_THOLD, + srng->intr_timer_thres_us); + + val |= FIELD_PREP(HAL_TCL1_RING_CONSR_INT_SETUP_IX0_BATCH_COUNTER_THOLD, + (srng->intr_batch_cntr_thres_entries * + srng->entry_size)); + + ath11k_ahb_write32(ab, + reg_base + HAL_TCL1_RING_CONSR_INT_SETUP_IX0_OFFSET, + val); + + val = 0; + if (srng->flags & HAL_SRNG_FLAGS_LOW_THRESH_INTR_EN) { + val |= FIELD_PREP(HAL_TCL1_RING_CONSR_INT_SETUP_IX1_LOW_THOLD, + srng->u.src_ring.low_threshold); + } + ath11k_ahb_write32(ab, + reg_base + HAL_TCL1_RING_CONSR_INT_SETUP_IX1_OFFSET, + val); + + if (srng->ring_id != HAL_SRNG_RING_ID_WBM_IDLE_LINK) { + tp_addr = hal->rdp.paddr + + ((unsigned long)srng->u.src_ring.tp_addr - + (unsigned long)hal->rdp.vaddr); + ath11k_ahb_write32(ab, + reg_base + HAL_TCL1_RING_TP_ADDR_LSB_OFFSET, + tp_addr & HAL_ADDR_LSB_REG_MASK); + ath11k_ahb_write32(ab, + reg_base + HAL_TCL1_RING_TP_ADDR_MSB_OFFSET, + tp_addr >> HAL_ADDR_MSB_REG_SHIFT); + } + + /* Initialize head and tail pointers to indicate ring is empty */ + reg_base = srng->hwreg_base[HAL_SRNG_REG_GRP_R2]; + ath11k_ahb_write32(ab, reg_base, 0); + ath11k_ahb_write32(ab, reg_base + HAL_TCL1_RING_TP_OFFSET, 0); + *srng->u.src_ring.tp_addr = 0; + + reg_base = srng->hwreg_base[HAL_SRNG_REG_GRP_R0]; + val = 0; + if (srng->flags & HAL_SRNG_FLAGS_DATA_TLV_SWAP) + val |= HAL_TCL1_RING_MISC_DATA_TLV_SWAP; + if (srng->flags & HAL_SRNG_FLAGS_RING_PTR_SWAP) + val |= HAL_TCL1_RING_MISC_HOST_FW_SWAP; + if (srng->flags & HAL_SRNG_FLAGS_MSI_SWAP) + val |= HAL_TCL1_RING_MISC_MSI_SWAP; + + /* Loop count is not used for SRC rings */ + val |= HAL_TCL1_RING_MISC_MSI_LOOPCNT_DISABLE; + + val |= HAL_TCL1_RING_MISC_SRNG_ENABLE; + + ath11k_ahb_write32(ab, reg_base + HAL_TCL1_RING_MISC_OFFSET, val); +} + +static void ath11k_hal_srng_hw_init(struct ath11k_base *ab, + struct hal_srng *srng) +{ + if (srng->ring_dir == HAL_SRNG_DIR_SRC) + ath11k_hal_srng_src_hw_init(ab, srng); + else + ath11k_hal_srng_dst_hw_init(ab, srng); +} + +static int ath11k_hal_srng_get_ring_id(struct ath11k_base *ab, + enum hal_ring_type type, + int ring_num, int mac_id) +{ + const struct hal_srng_config *srng_config = &hw_srng_config[type]; + int ring_id; + + if (ring_num >= srng_config->max_rings) { + ath11k_warn(ab, "invalid ring number :%d\n", ring_num); + return -EINVAL; + } + + ring_id = srng_config->start_ring_id + ring_num; + if (srng_config->lmac_ring) + ring_id += mac_id * HAL_SRNG_RINGS_PER_LMAC; + + if (WARN_ON(ring_id >= HAL_SRNG_RING_ID_MAX)) + return -EINVAL; + + return ring_id; +} + +int ath11k_hal_srng_get_entrysize(u32 ring_type) +{ + const struct hal_srng_config *srng_config; + + if (WARN_ON(ring_type >= HAL_MAX_RING_TYPES)) + return -EINVAL; + + srng_config = &hw_srng_config[ring_type]; + + return (srng_config->entry_size << 2); +} + +int ath11k_hal_srng_get_max_entries(u32 ring_type) +{ + const struct hal_srng_config *srng_config; + + if (WARN_ON(ring_type >= HAL_MAX_RING_TYPES)) + return -EINVAL; + + srng_config = &hw_srng_config[ring_type]; + + return (srng_config->max_size / srng_config->entry_size); +} + +void ath11k_hal_srng_get_params(struct ath11k_base *ab, struct hal_srng *srng, + struct hal_srng_params *params) +{ + params->ring_base_paddr = srng->ring_base_paddr; + params->ring_base_vaddr = srng->ring_base_vaddr; + params->num_entries = srng->num_entries; + params->intr_timer_thres_us = srng->intr_timer_thres_us; + params->intr_batch_cntr_thres_entries = + srng->intr_batch_cntr_thres_entries; + params->low_threshold = srng->u.src_ring.low_threshold; + params->flags = srng->flags; +} + +dma_addr_t ath11k_hal_srng_get_hp_addr(struct ath11k_base *ab, + struct hal_srng *srng) +{ + if (!(srng->flags & HAL_SRNG_FLAGS_LMAC_RING)) + return 0; + + if (srng->ring_dir == HAL_SRNG_DIR_SRC) + return ab->hal.wrp.paddr + + ((unsigned long)srng->u.src_ring.hp_addr - + (unsigned long)ab->hal.wrp.vaddr); + else + return ab->hal.rdp.paddr + + ((unsigned long)srng->u.dst_ring.hp_addr - + (unsigned long)ab->hal.rdp.vaddr); +} + +dma_addr_t ath11k_hal_srng_get_tp_addr(struct ath11k_base *ab, + struct hal_srng *srng) +{ + if (!(srng->flags & HAL_SRNG_FLAGS_LMAC_RING)) + return 0; + + if (srng->ring_dir == HAL_SRNG_DIR_SRC) + return ab->hal.rdp.paddr + + ((unsigned long)srng->u.src_ring.tp_addr - + (unsigned long)ab->hal.rdp.vaddr); + else + return ab->hal.wrp.paddr + + ((unsigned long)srng->u.dst_ring.tp_addr - + (unsigned long)ab->hal.wrp.vaddr); +} + +u32 ath11k_hal_ce_get_desc_size(enum hal_ce_desc type) +{ + switch (type) { + case HAL_CE_DESC_SRC: + return sizeof(struct hal_ce_srng_src_desc); + case HAL_CE_DESC_DST: + return sizeof(struct hal_ce_srng_dest_desc); + case HAL_CE_DESC_DST_STATUS: + return sizeof(struct hal_ce_srng_dst_status_desc); + } + + return 0; +} + +void ath11k_hal_ce_src_set_desc(void *buf, dma_addr_t paddr, u32 len, u32 id, + u8 byte_swap_data) +{ + struct hal_ce_srng_src_desc *desc = (struct hal_ce_srng_src_desc *)buf; + + desc->buffer_addr_low = paddr & HAL_ADDR_LSB_REG_MASK; + desc->buffer_addr_info = + FIELD_PREP(HAL_CE_SRC_DESC_ADDR_INFO_ADDR_HI, + ((u64)paddr >> HAL_ADDR_MSB_REG_SHIFT)) | + FIELD_PREP(HAL_CE_SRC_DESC_ADDR_INFO_BYTE_SWAP, + byte_swap_data) | + FIELD_PREP(HAL_CE_SRC_DESC_ADDR_INFO_GATHER, 0) | + FIELD_PREP(HAL_CE_SRC_DESC_ADDR_INFO_LEN, len); + desc->meta_info = FIELD_PREP(HAL_CE_SRC_DESC_META_INFO_DATA, id); +} + +void ath11k_hal_ce_dst_set_desc(void *buf, dma_addr_t paddr) +{ + struct hal_ce_srng_dest_desc *desc = + (struct hal_ce_srng_dest_desc *)buf; + + desc->buffer_addr_low = paddr & HAL_ADDR_LSB_REG_MASK; + desc->buffer_addr_info = + FIELD_PREP(HAL_CE_DEST_DESC_ADDR_INFO_ADDR_HI, + ((u64)paddr >> HAL_ADDR_MSB_REG_SHIFT)); +} + +u32 ath11k_hal_ce_dst_status_get_length(void *buf) +{ + struct hal_ce_srng_dst_status_desc *desc = + (struct hal_ce_srng_dst_status_desc *)buf; + u32 len; + + len = FIELD_GET(HAL_CE_DST_STATUS_DESC_FLAGS_LEN, desc->flags); + desc->flags &= ~HAL_CE_DST_STATUS_DESC_FLAGS_LEN; + + return len; +} + +void ath11k_hal_set_link_desc_addr(struct hal_wbm_link_desc *desc, u32 cookie, + dma_addr_t paddr) +{ + desc->buf_addr_info.info0 = FIELD_PREP(BUFFER_ADDR_INFO0_ADDR, + (paddr & HAL_ADDR_LSB_REG_MASK)); + desc->buf_addr_info.info1 = FIELD_PREP(BUFFER_ADDR_INFO1_ADDR, + ((u64)paddr >> HAL_ADDR_MSB_REG_SHIFT)) | + FIELD_PREP(BUFFER_ADDR_INFO1_RET_BUF_MGR, 1) | + FIELD_PREP(BUFFER_ADDR_INFO1_SW_COOKIE, cookie); +} + +u32 *ath11k_hal_srng_dst_peek(struct ath11k_base *ab, struct hal_srng *srng) +{ + lockdep_assert_held(&srng->lock); + + if (srng->u.dst_ring.tp != srng->u.dst_ring.cached_hp) + return (srng->ring_base_vaddr + srng->u.dst_ring.tp); + + return NULL; +} + +u32 *ath11k_hal_srng_dst_get_next_entry(struct ath11k_base *ab, + struct hal_srng *srng) +{ + u32 *desc; + + lockdep_assert_held(&srng->lock); + + if (srng->u.dst_ring.tp == srng->u.dst_ring.cached_hp) + return NULL; + + desc = srng->ring_base_vaddr + srng->u.dst_ring.tp; + + srng->u.dst_ring.tp = (srng->u.dst_ring.tp + srng->entry_size) % + srng->ring_size; + + return desc; +} + +int ath11k_hal_srng_dst_num_free(struct ath11k_base *ab, struct hal_srng *srng, + bool sync_hw_ptr) +{ + u32 tp, hp; + + lockdep_assert_held(&srng->lock); + + tp = srng->u.dst_ring.tp; + + if (sync_hw_ptr) { + hp = *srng->u.dst_ring.hp_addr; + srng->u.dst_ring.cached_hp = hp; + } else { + hp = srng->u.dst_ring.cached_hp; + } + + if (hp >= tp) + return (hp - tp) / srng->entry_size; + else + return (srng->ring_size - tp + hp) / srng->entry_size; +} + +/* Returns number of available entries in src ring */ +int ath11k_hal_srng_src_num_free(struct ath11k_base *ab, struct hal_srng *srng, + bool sync_hw_ptr) +{ + u32 tp, hp; + + lockdep_assert_held(&srng->lock); + + hp = srng->u.src_ring.hp; + + if (sync_hw_ptr) { + tp = *srng->u.src_ring.tp_addr; + srng->u.src_ring.cached_tp = tp; + } else { + tp = srng->u.src_ring.cached_tp; + } + + if (tp > hp) + return ((tp - hp) / srng->entry_size) - 1; + else + return ((srng->ring_size - hp + tp) / srng->entry_size) - 1; +} + +u32 *ath11k_hal_srng_src_get_next_entry(struct ath11k_base *ab, + struct hal_srng *srng) +{ + u32 *desc; + u32 next_hp; + + lockdep_assert_held(&srng->lock); + + /* TODO: Using % is expensive, but we have to do this since size of some + * SRNG rings is not power of 2 (due to descriptor sizes). Need to see + * if separate function is defined for rings having power of 2 ring size + * (TCL2SW, REO2SW, SW2RXDMA and CE rings) so that we can avoid the + * overhead of % by using mask (with &). + */ + next_hp = (srng->u.src_ring.hp + srng->entry_size) % srng->ring_size; + + if (next_hp == srng->u.src_ring.cached_tp) + return NULL; + + desc = srng->ring_base_vaddr + srng->u.src_ring.hp; + srng->u.src_ring.hp = next_hp; + + /* TODO: Reap functionality is not used by all rings. If particular + * ring does not use reap functionality, we need not update reap_hp + * with next_hp pointer. Need to make sure a separate function is used + * before doing any optimization by removing below code updating + * reap_hp. + */ + srng->u.src_ring.reap_hp = next_hp; + + return desc; +} + +u32 *ath11k_hal_srng_src_reap_next(struct ath11k_base *ab, + struct hal_srng *srng) +{ + u32 *desc; + u32 next_reap_hp; + + lockdep_assert_held(&srng->lock); + + next_reap_hp = (srng->u.src_ring.reap_hp + srng->entry_size) % + srng->ring_size; + + if (next_reap_hp == srng->u.src_ring.cached_tp) + return NULL; + + desc = srng->ring_base_vaddr + next_reap_hp; + srng->u.src_ring.reap_hp = next_reap_hp; + + return desc; +} + +u32 *ath11k_hal_srng_src_get_next_reaped(struct ath11k_base *ab, + struct hal_srng *srng) +{ + u32 *desc; + + lockdep_assert_held(&srng->lock); + + if (srng->u.src_ring.hp == srng->u.src_ring.reap_hp) + return NULL; + + desc = srng->ring_base_vaddr + srng->u.src_ring.hp; + srng->u.src_ring.hp = (srng->u.src_ring.hp + srng->entry_size) % + srng->ring_size; + + return desc; +} + +u32 *ath11k_hal_srng_src_peek(struct ath11k_base *ab, struct hal_srng *srng) +{ + lockdep_assert_held(&srng->lock); + + if (((srng->u.src_ring.hp + srng->entry_size) % srng->ring_size) == + srng->u.src_ring.cached_tp) + return NULL; + + return srng->ring_base_vaddr + srng->u.src_ring.hp; +} + +void ath11k_hal_srng_access_begin(struct ath11k_base *ab, struct hal_srng *srng) +{ + lockdep_assert_held(&srng->lock); + + if (srng->ring_dir == HAL_SRNG_DIR_SRC) + srng->u.src_ring.cached_tp = + *(volatile u32 *)srng->u.src_ring.tp_addr; + else + srng->u.dst_ring.cached_hp = *srng->u.dst_ring.hp_addr; +} + +/* Update cached ring head/tail pointers to HW. ath11k_hal_srng_access_begin() + * should have been called before this. + */ +void ath11k_hal_srng_access_end(struct ath11k_base *ab, struct hal_srng *srng) +{ + lockdep_assert_held(&srng->lock); + + /* TODO: See if we need a write memory barrier here */ + if (srng->flags & HAL_SRNG_FLAGS_LMAC_RING) { + /* For LMAC rings, ring pointer updates are done through FW and + * hence written to a shared memory location that is read by FW + */ + if (srng->ring_dir == HAL_SRNG_DIR_SRC) + *srng->u.src_ring.hp_addr = srng->u.src_ring.hp; + else + *srng->u.dst_ring.tp_addr = srng->u.dst_ring.tp; + } else { + if (srng->ring_dir == HAL_SRNG_DIR_SRC) { + ath11k_ahb_write32(ab, + (unsigned long)srng->u.src_ring.hp_addr - + (unsigned long)ab->mem, + srng->u.src_ring.hp); + } else { + ath11k_ahb_write32(ab, + (unsigned long)srng->u.dst_ring.tp_addr - + (unsigned long)ab->mem, + srng->u.dst_ring.tp); + } + } +} + +void ath11k_hal_setup_link_idle_list(struct ath11k_base *ab, + struct hal_wbm_idle_scatter_list *sbuf, + u32 nsbufs, u32 tot_link_desc, + u32 end_offset) +{ + struct ath11k_buffer_addr *link_addr; + int i; + u32 reg_scatter_buf_sz = HAL_WBM_IDLE_SCATTER_BUF_SIZE / 64; + + link_addr = (void *)sbuf[0].vaddr + HAL_WBM_IDLE_SCATTER_BUF_SIZE; + + for (i = 1; i < nsbufs; i++) { + link_addr->info0 = sbuf[i].paddr & HAL_ADDR_LSB_REG_MASK; + link_addr->info1 = FIELD_PREP( + HAL_WBM_SCATTERED_DESC_MSB_BASE_ADDR_39_32, + (u64)sbuf[i].paddr >> HAL_ADDR_MSB_REG_SHIFT) | + FIELD_PREP( + HAL_WBM_SCATTERED_DESC_MSB_BASE_ADDR_MATCH_TAG, + BASE_ADDR_MATCH_TAG_VAL); + + link_addr = (void *)sbuf[i].vaddr + + HAL_WBM_IDLE_SCATTER_BUF_SIZE; + } + + ath11k_ahb_write32(ab, + HAL_SEQ_WCSS_UMAC_WBM_REG + HAL_WBM_R0_IDLE_LIST_CONTROL_ADDR, + FIELD_PREP(HAL_WBM_SCATTER_BUFFER_SIZE, reg_scatter_buf_sz) | + FIELD_PREP(HAL_WBM_LINK_DESC_IDLE_LIST_MODE, 0x1)); + ath11k_ahb_write32(ab, + HAL_SEQ_WCSS_UMAC_WBM_REG + HAL_WBM_R0_IDLE_LIST_SIZE_ADDR, + FIELD_PREP(HAL_WBM_SCATTER_RING_SIZE_OF_IDLE_LINK_DESC_LIST, + reg_scatter_buf_sz * nsbufs)); + ath11k_ahb_write32(ab, + HAL_SEQ_WCSS_UMAC_WBM_REG + + HAL_WBM_SCATTERED_RING_BASE_LSB, + FIELD_PREP(BUFFER_ADDR_INFO0_ADDR, + sbuf[0].paddr & HAL_ADDR_LSB_REG_MASK)); + ath11k_ahb_write32(ab, + HAL_SEQ_WCSS_UMAC_WBM_REG + + HAL_WBM_SCATTERED_RING_BASE_MSB, + FIELD_PREP( + HAL_WBM_SCATTERED_DESC_MSB_BASE_ADDR_39_32, + (u64)sbuf[0].paddr >> HAL_ADDR_MSB_REG_SHIFT) | + FIELD_PREP( + HAL_WBM_SCATTERED_DESC_MSB_BASE_ADDR_MATCH_TAG, + BASE_ADDR_MATCH_TAG_VAL)); + + /* Setup head and tail pointers for the idle list */ + ath11k_ahb_write32(ab, + HAL_SEQ_WCSS_UMAC_WBM_REG + + HAL_WBM_SCATTERED_DESC_PTR_HEAD_INFO_IX0, + FIELD_PREP(BUFFER_ADDR_INFO0_ADDR, + sbuf[nsbufs - 1].paddr)); + ath11k_ahb_write32(ab, + HAL_SEQ_WCSS_UMAC_WBM_REG + + HAL_WBM_SCATTERED_DESC_PTR_HEAD_INFO_IX1, + FIELD_PREP( + HAL_WBM_SCATTERED_DESC_MSB_BASE_ADDR_39_32, + ((u64)sbuf[nsbufs - 1].paddr >> + HAL_ADDR_MSB_REG_SHIFT)) | + FIELD_PREP(HAL_WBM_SCATTERED_DESC_HEAD_P_OFFSET_IX1, + (end_offset >> 2))); + ath11k_ahb_write32(ab, + HAL_SEQ_WCSS_UMAC_WBM_REG + + HAL_WBM_SCATTERED_DESC_PTR_HEAD_INFO_IX0, + FIELD_PREP(BUFFER_ADDR_INFO0_ADDR, + sbuf[0].paddr)); + + ath11k_ahb_write32(ab, + HAL_SEQ_WCSS_UMAC_WBM_REG + + HAL_WBM_SCATTERED_DESC_PTR_TAIL_INFO_IX0, + FIELD_PREP(BUFFER_ADDR_INFO0_ADDR, + sbuf[0].paddr)); + ath11k_ahb_write32(ab, + HAL_SEQ_WCSS_UMAC_WBM_REG + + HAL_WBM_SCATTERED_DESC_PTR_TAIL_INFO_IX1, + FIELD_PREP( + HAL_WBM_SCATTERED_DESC_MSB_BASE_ADDR_39_32, + ((u64)sbuf[0].paddr >> HAL_ADDR_MSB_REG_SHIFT)) | + FIELD_PREP(HAL_WBM_SCATTERED_DESC_TAIL_P_OFFSET_IX1, + 0)); + ath11k_ahb_write32(ab, + HAL_SEQ_WCSS_UMAC_WBM_REG + + HAL_WBM_SCATTERED_DESC_PTR_HP_ADDR, + 2 * tot_link_desc); + + /* Enable the SRNG */ + ath11k_ahb_write32(ab, + HAL_SEQ_WCSS_UMAC_WBM_REG + + HAL_WBM_IDLE_LINK_RING_MISC_ADDR, 0x40); +} + +int ath11k_hal_srng_setup(struct ath11k_base *ab, enum hal_ring_type type, + int ring_num, int mac_id, + struct hal_srng_params *params) +{ + struct ath11k_hal *hal = &ab->hal; + const struct hal_srng_config *srng_config = &hw_srng_config[type]; + struct hal_srng *srng; + int ring_id; + u32 lmac_idx; + int i; + u32 reg_base; + + ring_id = ath11k_hal_srng_get_ring_id(ab, type, ring_num, mac_id); + if (ring_id < 0) + return ring_id; + + srng = &hal->srng_list[ring_id]; + + srng->ring_id = ring_id; + srng->ring_dir = srng_config->ring_dir; + srng->ring_base_paddr = params->ring_base_paddr; + srng->ring_base_vaddr = params->ring_base_vaddr; + srng->entry_size = srng_config->entry_size; + srng->num_entries = params->num_entries; + srng->ring_size = srng->entry_size * srng->num_entries; + srng->intr_batch_cntr_thres_entries = + params->intr_batch_cntr_thres_entries; + srng->intr_timer_thres_us = params->intr_timer_thres_us; + srng->flags = params->flags; + spin_lock_init(&srng->lock); + + for (i = 0; i < HAL_SRNG_NUM_REG_GRP; i++) { + srng->hwreg_base[i] = srng_config->reg_start[i] + + (ring_num * srng_config->reg_size[i]); + } + + memset(srng->ring_base_vaddr, 0, + (srng->entry_size * srng->num_entries) << 2); + + /* TODO: Add comments on these swap configurations */ + if (IS_ENABLED(CONFIG_CPU_BIG_ENDIAN)) + srng->flags |= HAL_SRNG_FLAGS_MSI_SWAP | HAL_SRNG_FLAGS_DATA_TLV_SWAP | + HAL_SRNG_FLAGS_RING_PTR_SWAP; + + reg_base = srng->hwreg_base[HAL_SRNG_REG_GRP_R2]; + + if (srng->ring_dir == HAL_SRNG_DIR_SRC) { + srng->u.src_ring.hp = 0; + srng->u.src_ring.cached_tp = 0; + srng->u.src_ring.reap_hp = srng->ring_size - srng->entry_size; + srng->u.src_ring.tp_addr = (void *)(hal->rdp.vaddr + ring_id); + srng->u.src_ring.low_threshold = params->low_threshold * + srng->entry_size; + if (srng_config->lmac_ring) { + lmac_idx = ring_id - HAL_SRNG_RING_ID_LMAC1_ID_START; + srng->u.src_ring.hp_addr = (void *)(hal->wrp.vaddr + + lmac_idx); + srng->flags |= HAL_SRNG_FLAGS_LMAC_RING; + } else { + srng->u.src_ring.hp_addr = + (u32 *)((unsigned long)ab->mem + reg_base); + } + } else { + /* During initialization loop count in all the descriptors + * will be set to zero, and HW will set it to 1 on completing + * descriptor update in first loop, and increments it by 1 on + * subsequent loops (loop count wraps around after reaching + * 0xffff). The 'loop_cnt' in SW ring state is the expected + * loop count in descriptors updated by HW (to be processed + * by SW). + */ + srng->u.dst_ring.loop_cnt = 1; + srng->u.dst_ring.tp = 0; + srng->u.dst_ring.cached_hp = 0; + srng->u.dst_ring.hp_addr = (void *)(hal->rdp.vaddr + ring_id); + if (srng_config->lmac_ring) { + /* For LMAC rings, tail pointer updates will be done + * through FW by writing to a shared memory location + */ + lmac_idx = ring_id - HAL_SRNG_RING_ID_LMAC1_ID_START; + srng->u.dst_ring.tp_addr = (void *)(hal->wrp.vaddr + + lmac_idx); + srng->flags |= HAL_SRNG_FLAGS_LMAC_RING; + } else { + srng->u.dst_ring.tp_addr = + (u32 *)((unsigned long)ab->mem + reg_base + + (HAL_REO1_RING_TP - HAL_REO1_RING_HP)); + } + } + + if (srng_config->lmac_ring) + return ring_id; + + ath11k_hal_srng_hw_init(ab, srng); + + if (type == HAL_CE_DST) { + srng->u.dst_ring.max_buffer_length = params->max_buffer_len; + ath11k_hal_ce_dst_setup(ab, srng, ring_num); + } + + return ring_id; +} + +int ath11k_hal_srng_init(struct ath11k_base *ab) +{ + struct ath11k_hal *hal = &ab->hal; + int ret; + + memset(hal, 0, sizeof(*hal)); + + hal->srng_config = hw_srng_config; + + ret = ath11k_hal_alloc_cont_rdp(ab); + if (ret) + goto err_hal; + + ret = ath11k_hal_alloc_cont_wrp(ab); + if (ret) + goto err_free_cont_rdp; + + return 0; + +err_free_cont_rdp: + ath11k_hal_free_cont_rdp(ab); + +err_hal: + return ret; +} + +void ath11k_hal_srng_deinit(struct ath11k_base *ab) +{ + ath11k_hal_free_cont_rdp(ab); + ath11k_hal_free_cont_wrp(ab); +} diff --git a/drivers/net/wireless/ath/ath11k/hal.h b/drivers/net/wireless/ath/ath11k/hal.h new file mode 100644 index 0000000000000..5b13ccdf39e4f --- /dev/null +++ b/drivers/net/wireless/ath/ath11k/hal.h @@ -0,0 +1,897 @@ +/* SPDX-License-Identifier: BSD-3-Clause-Clear */ +/* + * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. + */ + +#ifndef ATH11K_HAL_H +#define ATH11K_HAL_H + +#include "hal_desc.h" +#include "rx_desc.h" + +struct ath11k_base; + +#define HAL_LINK_DESC_SIZE (32 << 2) +#define HAL_LINK_DESC_ALIGN 128 +#define HAL_NUM_MPDUS_PER_LINK_DESC 6 +#define HAL_NUM_TX_MSDUS_PER_LINK_DESC 7 +#define HAL_NUM_RX_MSDUS_PER_LINK_DESC 6 +#define HAL_NUM_MPDU_LINKS_PER_QUEUE_DESC 12 +#define HAL_MAX_AVAIL_BLK_RES 3 + +#define HAL_RING_BASE_ALIGN 8 + +#define HAL_WBM_IDLE_SCATTER_BUF_SIZE_MAX 32704 +/* TODO: Check with hw team on the supported scatter buf size */ +#define HAL_WBM_IDLE_SCATTER_NEXT_PTR_SIZE 8 +#define HAL_WBM_IDLE_SCATTER_BUF_SIZE (HAL_WBM_IDLE_SCATTER_BUF_SIZE_MAX - \ + HAL_WBM_IDLE_SCATTER_NEXT_PTR_SIZE) + +#define HAL_DSCP_TID_MAP_TBL_NUM_ENTRIES_MAX 48 +#define HAL_DSCP_TID_TBL_SIZE 24 + +/* calculate the register address from bar0 of shadow register x */ +#define SHADOW_BASE_ADDRESS 0x00003024 +#define SHADOW_NUM_REGISTERS 36 + +/* WCSS Relative address */ +#define HAL_SEQ_WCSS_UMAC_REO_REG 0x00a38000 +#define HAL_SEQ_WCSS_UMAC_TCL_REG 0x00a44000 +#define HAL_SEQ_WCSS_UMAC_CE0_SRC_REG 0x00a00000 +#define HAL_SEQ_WCSS_UMAC_CE0_DST_REG 0x00a01000 +#define HAL_SEQ_WCSS_UMAC_CE1_SRC_REG 0x00a02000 +#define HAL_SEQ_WCSS_UMAC_CE1_DST_REG 0x00a03000 +#define HAL_SEQ_WCSS_UMAC_WBM_REG 0x00a34000 + +/* SW2TCL(x) R0 ring configuration address */ +#define HAL_TCL1_RING_CMN_CTRL_REG 0x00000014 +#define HAL_TCL1_RING_DSCP_TID_MAP 0x0000002c +#define HAL_TCL1_RING_BASE_LSB 0x00000510 +#define HAL_TCL1_RING_BASE_MSB 0x00000514 +#define HAL_TCL1_RING_ID 0x00000518 +#define HAL_TCL1_RING_MISC 0x00000520 +#define HAL_TCL1_RING_TP_ADDR_LSB 0x0000052c +#define HAL_TCL1_RING_TP_ADDR_MSB 0x00000530 +#define HAL_TCL1_RING_CONSUMER_INT_SETUP_IX0 0x00000540 +#define HAL_TCL1_RING_CONSUMER_INT_SETUP_IX1 0x00000544 +#define HAL_TCL1_RING_MSI1_BASE_LSB 0x00000558 +#define HAL_TCL1_RING_MSI1_BASE_MSB 0x0000055c +#define HAL_TCL1_RING_MSI1_DATA 0x00000560 +#define HAL_TCL2_RING_BASE_LSB 0x00000568 +#define HAL_TCL_RING_BASE_LSB 0x00000618 + +#define HAL_TCL1_RING_MSI1_BASE_LSB_OFFSET \ + (HAL_TCL1_RING_MSI1_BASE_LSB - HAL_TCL1_RING_BASE_LSB) +#define HAL_TCL1_RING_MSI1_BASE_MSB_OFFSET \ + (HAL_TCL1_RING_MSI1_BASE_MSB - HAL_TCL1_RING_BASE_LSB) +#define HAL_TCL1_RING_MSI1_DATA_OFFSET \ + (HAL_TCL1_RING_MSI1_DATA - HAL_TCL1_RING_BASE_LSB) +#define HAL_TCL1_RING_BASE_MSB_OFFSET \ + (HAL_TCL1_RING_BASE_MSB - HAL_TCL1_RING_BASE_LSB) +#define HAL_TCL1_RING_ID_OFFSET \ + (HAL_TCL1_RING_ID - HAL_TCL1_RING_BASE_LSB) +#define HAL_TCL1_RING_CONSR_INT_SETUP_IX0_OFFSET \ + (HAL_TCL1_RING_CONSUMER_INT_SETUP_IX0 - HAL_TCL1_RING_BASE_LSB) +#define HAL_TCL1_RING_CONSR_INT_SETUP_IX1_OFFSET \ + (HAL_TCL1_RING_CONSUMER_INT_SETUP_IX1 - HAL_TCL1_RING_BASE_LSB) +#define HAL_TCL1_RING_TP_ADDR_LSB_OFFSET \ + (HAL_TCL1_RING_TP_ADDR_LSB - HAL_TCL1_RING_BASE_LSB) +#define HAL_TCL1_RING_TP_ADDR_MSB_OFFSET \ + (HAL_TCL1_RING_TP_ADDR_MSB - HAL_TCL1_RING_BASE_LSB) +#define HAL_TCL1_RING_MISC_OFFSET \ + (HAL_TCL1_RING_MISC - HAL_TCL1_RING_BASE_LSB) + +/* SW2TCL(x) R2 ring pointers (head/tail) address */ +#define HAL_TCL1_RING_HP 0x00002000 +#define HAL_TCL1_RING_TP 0x00002004 +#define HAL_TCL2_RING_HP 0x00002008 +#define HAL_TCL_RING_HP 0x00002018 + +#define HAL_TCL1_RING_TP_OFFSET \ + (HAL_TCL1_RING_TP - HAL_TCL1_RING_HP) + +/* TCL STATUS ring address */ +#define HAL_TCL_STATUS_RING_BASE_LSB 0x00000720 +#define HAL_TCL_STATUS_RING_HP 0x00002030 + +/* REO2SW(x) R0 ring configuration address */ +#define HAL_REO1_GEN_ENABLE 0x00000000 +#define HAL_REO1_DEST_RING_CTRL_IX_2 0x0000000c +#define HAL_REO1_DEST_RING_CTRL_IX_3 0x00000010 +#define HAL_REO1_RING_BASE_LSB 0x0000029c +#define HAL_REO1_RING_BASE_MSB 0x000002a0 +#define HAL_REO1_RING_ID 0x000002a4 +#define HAL_REO1_RING_MISC 0x000002ac +#define HAL_REO1_RING_HP_ADDR_LSB 0x000002b0 +#define HAL_REO1_RING_HP_ADDR_MSB 0x000002b4 +#define HAL_REO1_RING_PRODUCER_INT_SETUP 0x000002c0 +#define HAL_REO1_RING_MSI1_BASE_LSB 0x000002e4 +#define HAL_REO1_RING_MSI1_BASE_MSB 0x000002e8 +#define HAL_REO1_RING_MSI1_DATA 0x000002ec +#define HAL_REO2_RING_BASE_LSB 0x000002f4 +#define HAL_REO1_AGING_THRESH_IX_0 0x00000564 +#define HAL_REO1_AGING_THRESH_IX_1 0x00000568 +#define HAL_REO1_AGING_THRESH_IX_2 0x0000056c +#define HAL_REO1_AGING_THRESH_IX_3 0x00000570 + +#define HAL_REO1_RING_MSI1_BASE_LSB_OFFSET \ + (HAL_REO1_RING_MSI1_BASE_LSB - HAL_REO1_RING_BASE_LSB) +#define HAL_REO1_RING_MSI1_BASE_MSB_OFFSET \ + (HAL_REO1_RING_MSI1_BASE_MSB - HAL_REO1_RING_BASE_LSB) +#define HAL_REO1_RING_MSI1_DATA_OFFSET \ + (HAL_REO1_RING_MSI1_DATA - HAL_REO1_RING_BASE_LSB) +#define HAL_REO1_RING_BASE_MSB_OFFSET \ + (HAL_REO1_RING_BASE_MSB - HAL_REO1_RING_BASE_LSB) +#define HAL_REO1_RING_ID_OFFSET (HAL_REO1_RING_ID - HAL_REO1_RING_BASE_LSB) +#define HAL_REO1_RING_PRODUCER_INT_SETUP_OFFSET \ + (HAL_REO1_RING_PRODUCER_INT_SETUP - HAL_REO1_RING_BASE_LSB) +#define HAL_REO1_RING_HP_ADDR_LSB_OFFSET \ + (HAL_REO1_RING_HP_ADDR_LSB - HAL_REO1_RING_BASE_LSB) +#define HAL_REO1_RING_HP_ADDR_MSB_OFFSET \ + (HAL_REO1_RING_HP_ADDR_MSB - HAL_REO1_RING_BASE_LSB) +#define HAL_REO1_RING_MISC_OFFSET (HAL_REO1_RING_MISC - HAL_REO1_RING_BASE_LSB) + +/* REO2SW(x) R2 ring pointers (head/tail) address */ +#define HAL_REO1_RING_HP 0x00003038 +#define HAL_REO1_RING_TP 0x0000303c +#define HAL_REO2_RING_HP 0x00003040 + +#define HAL_REO1_RING_TP_OFFSET (HAL_REO1_RING_TP - HAL_REO1_RING_HP) + +/* REO2TCL R0 ring configuration address */ +#define HAL_REO_TCL_RING_BASE_LSB 0x000003fc + +/* REO2TCL R2 ring pointer (head/tail) address */ +#define HAL_REO_TCL_RING_HP 0x00003058 + +/* REO CMD R0 address */ +#define HAL_REO_CMD_RING_BASE_LSB 0x00000194 + +/* REO CMD R2 address */ +#define HAL_REO_CMD_HP 0x00003020 + +/* SW2REO R0 address */ +#define HAL_SW2REO_RING_BASE_LSB 0x000001ec + +/* SW2REO R2 address */ +#define HAL_SW2REO_RING_HP 0x00003028 + +/* CE ring R0 address */ +#define HAL_CE_DST_RING_BASE_LSB 0x00000000 +#define HAL_CE_DST_STATUS_RING_BASE_LSB 0x00000058 +#define HAL_CE_DST_RING_CTRL 0x000000b0 + +/* CE ring R2 address */ +#define HAL_CE_DST_RING_HP 0x00000400 +#define HAL_CE_DST_STATUS_RING_HP 0x00000408 + +/* REO status address */ +#define HAL_REO_STATUS_RING_BASE_LSB 0x00000504 +#define HAL_REO_STATUS_HP 0x00003070 + +/* WBM Idle R0 address */ +#define HAL_WBM_IDLE_LINK_RING_BASE_LSB 0x00000860 +#define HAL_WBM_IDLE_LINK_RING_MISC_ADDR 0x00000870 +#define HAL_WBM_R0_IDLE_LIST_CONTROL_ADDR 0x00000048 +#define HAL_WBM_R0_IDLE_LIST_SIZE_ADDR 0x0000004c +#define HAL_WBM_SCATTERED_RING_BASE_LSB 0x00000058 +#define HAL_WBM_SCATTERED_RING_BASE_MSB 0x0000005c +#define HAL_WBM_SCATTERED_DESC_PTR_HEAD_INFO_IX0 0x00000068 +#define HAL_WBM_SCATTERED_DESC_PTR_HEAD_INFO_IX1 0x0000006c +#define HAL_WBM_SCATTERED_DESC_PTR_TAIL_INFO_IX0 0x00000078 +#define HAL_WBM_SCATTERED_DESC_PTR_TAIL_INFO_IX1 0x0000007c +#define HAL_WBM_SCATTERED_DESC_PTR_HP_ADDR 0x00000084 + +/* WBM Idle R2 address */ +#define HAL_WBM_IDLE_LINK_RING_HP 0x000030b0 + +/* SW2WBM R0 release address */ +#define HAL_WBM_RELEASE_RING_BASE_LSB 0x000001d8 + +/* SW2WBM R2 release address */ +#define HAL_WBM_RELEASE_RING_HP 0x00003018 + +/* WBM2SW R0 release address */ +#define HAL_WBM0_RELEASE_RING_BASE_LSB 0x00000910 +#define HAL_WBM1_RELEASE_RING_BASE_LSB 0x00000968 + +/* WBM2SW R2 release address */ +#define HAL_WBM0_RELEASE_RING_HP 0x000030c0 +#define HAL_WBM1_RELEASE_RING_HP 0x000030c8 + +/* TCL ring feild mask and offset */ +#define HAL_TCL1_RING_BASE_MSB_RING_SIZE GENMASK(27, 8) +#define HAL_TCL1_RING_BASE_MSB_RING_BASE_ADDR_MSB GENMASK(7, 0) +#define HAL_TCL1_RING_ID_ENTRY_SIZE GENMASK(7, 0) +#define HAL_TCL1_RING_MISC_MSI_LOOPCNT_DISABLE BIT(1) +#define HAL_TCL1_RING_MISC_MSI_SWAP BIT(3) +#define HAL_TCL1_RING_MISC_HOST_FW_SWAP BIT(4) +#define HAL_TCL1_RING_MISC_DATA_TLV_SWAP BIT(5) +#define HAL_TCL1_RING_MISC_SRNG_ENABLE BIT(6) +#define HAL_TCL1_RING_CONSR_INT_SETUP_IX0_INTR_TMR_THOLD GENMASK(31, 16) +#define HAL_TCL1_RING_CONSR_INT_SETUP_IX0_BATCH_COUNTER_THOLD GENMASK(14, 0) +#define HAL_TCL1_RING_CONSR_INT_SETUP_IX1_LOW_THOLD GENMASK(15, 0) +#define HAL_TCL1_RING_MSI1_BASE_MSB_MSI1_ENABLE BIT(8) +#define HAL_TCL1_RING_MSI1_BASE_MSB_ADDR GENMASK(7, 0) +#define HAL_TCL1_RING_CMN_CTRL_DSCP_TID_MAP_PROG_EN BIT(17) +#define HAL_TCL1_RING_FIELD_DSCP_TID_MAP GENMASK(31, 0) +#define HAL_TCL1_RING_FIELD_DSCP_TID_MAP0 GENMASK(2, 0) +#define HAL_TCL1_RING_FIELD_DSCP_TID_MAP1 GENMASK(5, 3) +#define HAL_TCL1_RING_FIELD_DSCP_TID_MAP2 GENMASK(8, 6) +#define HAL_TCL1_RING_FIELD_DSCP_TID_MAP3 GENMASK(11, 9) +#define HAL_TCL1_RING_FIELD_DSCP_TID_MAP4 GENMASK(14, 12) +#define HAL_TCL1_RING_FIELD_DSCP_TID_MAP5 GENMASK(17, 15) +#define HAL_TCL1_RING_FIELD_DSCP_TID_MAP6 GENMASK(20, 18) +#define HAL_TCL1_RING_FIELD_DSCP_TID_MAP7 GENMASK(23, 21) + +/* REO ring feild mask and offset */ +#define HAL_REO1_RING_BASE_MSB_RING_SIZE GENMASK(27, 8) +#define HAL_REO1_RING_BASE_MSB_RING_BASE_ADDR_MSB GENMASK(7, 0) +#define HAL_REO1_RING_ID_RING_ID GENMASK(15, 8) +#define HAL_REO1_RING_ID_ENTRY_SIZE GENMASK(7, 0) +#define HAL_REO1_RING_MISC_MSI_SWAP BIT(3) +#define HAL_REO1_RING_MISC_HOST_FW_SWAP BIT(4) +#define HAL_REO1_RING_MISC_DATA_TLV_SWAP BIT(5) +#define HAL_REO1_RING_MISC_SRNG_ENABLE BIT(6) +#define HAL_REO1_RING_PRDR_INT_SETUP_INTR_TMR_THOLD GENMASK(31, 16) +#define HAL_REO1_RING_PRDR_INT_SETUP_BATCH_COUNTER_THOLD GENMASK(14, 0) +#define HAL_REO1_RING_MSI1_BASE_MSB_MSI1_ENABLE BIT(8) +#define HAL_REO1_RING_MSI1_BASE_MSB_ADDR GENMASK(7, 0) +#define HAL_REO1_GEN_ENABLE_FRAG_DST_RING GENMASK(25, 23) +#define HAL_REO1_GEN_ENABLE_AGING_LIST_ENABLE BIT(2) +#define HAL_REO1_GEN_ENABLE_AGING_FLUSH_ENABLE BIT(3) + +/* CE ring bit field mask and shift */ +#define HAL_CE_DST_R0_DEST_CTRL_MAX_LEN GENMASK(15, 0) + +#define HAL_ADDR_LSB_REG_MASK 0xffffffff + +#define HAL_ADDR_MSB_REG_SHIFT 32 + +/* WBM ring bit field mask and shift */ +#define HAL_WBM_LINK_DESC_IDLE_LIST_MODE BIT(1) +#define HAL_WBM_SCATTER_BUFFER_SIZE GENMASK(10, 2) +#define HAL_WBM_SCATTER_RING_SIZE_OF_IDLE_LINK_DESC_LIST GENMASK(31, 16) +#define HAL_WBM_SCATTERED_DESC_MSB_BASE_ADDR_39_32 GENMASK(7, 0) +#define HAL_WBM_SCATTERED_DESC_MSB_BASE_ADDR_MATCH_TAG GENMASK(31, 8) + +#define HAL_WBM_SCATTERED_DESC_HEAD_P_OFFSET_IX1 GENMASK(20, 8) +#define HAL_WBM_SCATTERED_DESC_TAIL_P_OFFSET_IX1 GENMASK(20, 8) + +#define BASE_ADDR_MATCH_TAG_VAL 0x5 + +#define HAL_REO_REO2SW1_RING_BASE_MSB_RING_SIZE 0x000fffff +#define HAL_REO_REO2TCL_RING_BASE_MSB_RING_SIZE 0x000fffff +#define HAL_REO_SW2REO_RING_BASE_MSB_RING_SIZE 0x0000ffff +#define HAL_REO_CMD_RING_BASE_MSB_RING_SIZE 0x0000ffff +#define HAL_REO_STATUS_RING_BASE_MSB_RING_SIZE 0x0000ffff +#define HAL_SW2TCL1_RING_BASE_MSB_RING_SIZE 0x000fffff +#define HAL_SW2TCL1_CMD_RING_BASE_MSB_RING_SIZE 0x000fffff +#define HAL_TCL_STATUS_RING_BASE_MSB_RING_SIZE 0x0000ffff +#define HAL_CE_SRC_RING_BASE_MSB_RING_SIZE 0x0000ffff +#define HAL_CE_DST_RING_BASE_MSB_RING_SIZE 0x0000ffff +#define HAL_CE_DST_STATUS_RING_BASE_MSB_RING_SIZE 0x0000ffff +#define HAL_WBM_IDLE_LINK_RING_BASE_MSB_RING_SIZE 0x0000ffff +#define HAL_SW2WBM_RELEASE_RING_BASE_MSB_RING_SIZE 0x0000ffff +#define HAL_WBM2SW_RELEASE_RING_BASE_MSB_RING_SIZE 0x000fffff +#define HAL_RXDMA_RING_MAX_SIZE 0x0000ffff + +#define HAL_RX_DESC_SIZE (sizeof(struct hal_rx_desc)) + +/* Add any other errors here and return them in + * ath11k_hal_rx_desc_get_err(). + */ + +enum hal_srng_ring_id { + HAL_SRNG_RING_ID_REO2SW1 = 0, + HAL_SRNG_RING_ID_REO2SW2, + HAL_SRNG_RING_ID_REO2SW3, + HAL_SRNG_RING_ID_REO2SW4, + HAL_SRNG_RING_ID_REO2TCL, + HAL_SRNG_RING_ID_SW2REO, + + HAL_SRNG_RING_ID_REO_CMD = 8, + HAL_SRNG_RING_ID_REO_STATUS, + + HAL_SRNG_RING_ID_SW2TCL1 = 16, + HAL_SRNG_RING_ID_SW2TCL2, + HAL_SRNG_RING_ID_SW2TCL3, + HAL_SRNG_RING_ID_SW2TCL4, + + HAL_SRNG_RING_ID_SW2TCL_CMD = 24, + HAL_SRNG_RING_ID_TCL_STATUS, + + HAL_SRNG_RING_ID_CE0_SRC = 32, + HAL_SRNG_RING_ID_CE1_SRC, + HAL_SRNG_RING_ID_CE2_SRC, + HAL_SRNG_RING_ID_CE3_SRC, + HAL_SRNG_RING_ID_CE4_SRC, + HAL_SRNG_RING_ID_CE5_SRC, + HAL_SRNG_RING_ID_CE6_SRC, + HAL_SRNG_RING_ID_CE7_SRC, + HAL_SRNG_RING_ID_CE8_SRC, + HAL_SRNG_RING_ID_CE9_SRC, + HAL_SRNG_RING_ID_CE10_SRC, + HAL_SRNG_RING_ID_CE11_SRC, + + HAL_SRNG_RING_ID_CE0_DST = 56, + HAL_SRNG_RING_ID_CE1_DST, + HAL_SRNG_RING_ID_CE2_DST, + HAL_SRNG_RING_ID_CE3_DST, + HAL_SRNG_RING_ID_CE4_DST, + HAL_SRNG_RING_ID_CE5_DST, + HAL_SRNG_RING_ID_CE6_DST, + HAL_SRNG_RING_ID_CE7_DST, + HAL_SRNG_RING_ID_CE8_DST, + HAL_SRNG_RING_ID_CE9_DST, + HAL_SRNG_RING_ID_CE10_DST, + HAL_SRNG_RING_ID_CE11_DST, + + HAL_SRNG_RING_ID_CE0_DST_STATUS = 80, + HAL_SRNG_RING_ID_CE1_DST_STATUS, + HAL_SRNG_RING_ID_CE2_DST_STATUS, + HAL_SRNG_RING_ID_CE3_DST_STATUS, + HAL_SRNG_RING_ID_CE4_DST_STATUS, + HAL_SRNG_RING_ID_CE5_DST_STATUS, + HAL_SRNG_RING_ID_CE6_DST_STATUS, + HAL_SRNG_RING_ID_CE7_DST_STATUS, + HAL_SRNG_RING_ID_CE8_DST_STATUS, + HAL_SRNG_RING_ID_CE9_DST_STATUS, + HAL_SRNG_RING_ID_CE10_DST_STATUS, + HAL_SRNG_RING_ID_CE11_DST_STATUS, + + HAL_SRNG_RING_ID_WBM_IDLE_LINK = 104, + HAL_SRNG_RING_ID_WBM_SW_RELEASE, + HAL_SRNG_RING_ID_WBM2SW0_RELEASE, + HAL_SRNG_RING_ID_WBM2SW1_RELEASE, + HAL_SRNG_RING_ID_WBM2SW2_RELEASE, + HAL_SRNG_RING_ID_WBM2SW3_RELEASE, + + HAL_SRNG_RING_ID_UMAC_ID_END = 127, + HAL_SRNG_RING_ID_LMAC1_ID_START, + + HAL_SRNG_RING_ID_WMAC1_SW2RXDMA0_BUF = HAL_SRNG_RING_ID_LMAC1_ID_START, + HAL_SRNG_RING_ID_WMAC1_SW2RXDMA1_BUF, + HAL_SRNG_RING_ID_WMAC1_SW2RXDMA2_BUF, + HAL_SRNG_RING_ID_WMAC1_SW2RXDMA0_STATBUF, + HAL_SRNG_RING_ID_WMAC1_SW2RXDMA1_STATBUF, + HAL_SRNG_RING_ID_WMAC1_RXDMA2SW0, + HAL_SRNG_RING_ID_WMAC1_RXDMA2SW1, + HAL_SRNG_RING_ID_WMAC1_SW2RXDMA1_DESC, + HAL_SRNG_RING_ID_RXDMA_DIR_BUF, + + HAL_SRNG_RING_ID_LMAC1_ID_END = 143 +}; + +/* SRNG registers are split into two groups R0 and R2 */ +#define HAL_SRNG_REG_GRP_R0 0 +#define HAL_SRNG_REG_GRP_R2 1 +#define HAL_SRNG_NUM_REG_GRP 2 + +#define HAL_SRNG_NUM_LMACS 3 +#define HAL_SRNG_REO_EXCEPTION HAL_SRNG_RING_ID_REO2SW1 +#define HAL_SRNG_RINGS_PER_LMAC (HAL_SRNG_RING_ID_LMAC1_ID_END - \ + HAL_SRNG_RING_ID_LMAC1_ID_START) +#define HAL_SRNG_NUM_LMAC_RINGS (HAL_SRNG_NUM_LMACS * HAL_SRNG_RINGS_PER_LMAC) +#define HAL_SRNG_RING_ID_MAX (HAL_SRNG_RING_ID_UMAC_ID_END + \ + HAL_SRNG_NUM_LMAC_RINGS) + +enum hal_ring_type { + HAL_REO_DST, + HAL_REO_EXCEPTION, + HAL_REO_REINJECT, + HAL_REO_CMD, + HAL_REO_STATUS, + HAL_TCL_DATA, + HAL_TCL_CMD, + HAL_TCL_STATUS, + HAL_CE_SRC, + HAL_CE_DST, + HAL_CE_DST_STATUS, + HAL_WBM_IDLE_LINK, + HAL_SW2WBM_RELEASE, + HAL_WBM2SW_RELEASE, + HAL_RXDMA_BUF, + HAL_RXDMA_DST, + HAL_RXDMA_MONITOR_BUF, + HAL_RXDMA_MONITOR_STATUS, + HAL_RXDMA_MONITOR_DST, + HAL_RXDMA_MONITOR_DESC, + HAL_RXDMA_DIR_BUF, + HAL_MAX_RING_TYPES, +}; + +#define HAL_RX_MAX_BA_WINDOW 256 + +#define HAL_DEFAULT_REO_TIMEOUT_USEC (40 * 1000) + +/** + * enum hal_reo_cmd_type: Enum for REO command type + * @CMD_GET_QUEUE_STATS: Get REO queue status/stats + * @CMD_FLUSH_QUEUE: Flush all frames in REO queue + * @CMD_FLUSH_CACHE: Flush descriptor entries in the cache + * @CMD_UNBLOCK_CACHE: Unblock a descriptor's address that was blocked + * earlier with a 'REO_FLUSH_CACHE' command + * @CMD_FLUSH_TIMEOUT_LIST: Flush buffers/descriptors from timeout list + * @CMD_UPDATE_RX_REO_QUEUE: Update REO queue settings + */ +enum hal_reo_cmd_type { + HAL_REO_CMD_GET_QUEUE_STATS = 0, + HAL_REO_CMD_FLUSH_QUEUE = 1, + HAL_REO_CMD_FLUSH_CACHE = 2, + HAL_REO_CMD_UNBLOCK_CACHE = 3, + HAL_REO_CMD_FLUSH_TIMEOUT_LIST = 4, + HAL_REO_CMD_UPDATE_RX_QUEUE = 5, +}; + +/** + * enum hal_reo_cmd_status: Enum for execution status of REO command + * @HAL_REO_CMD_SUCCESS: Command has successfully executed + * @HAL_REO_CMD_BLOCKED: Command could not be executed as the queue + * or cache was blocked + * @HAL_REO_CMD_FAILED: Command execution failed, could be due to + * invalid queue desc + * @HAL_REO_CMD_RESOURCE_BLOCKED: + * @HAL_REO_CMD_DRAIN: + */ +enum hal_reo_cmd_status { + HAL_REO_CMD_SUCCESS = 0, + HAL_REO_CMD_BLOCKED = 1, + HAL_REO_CMD_FAILED = 2, + HAL_REO_CMD_RESOURCE_BLOCKED = 3, + HAL_REO_CMD_DRAIN = 0xff, +}; + +struct hal_wbm_idle_scatter_list { + dma_addr_t paddr; + struct hal_wbm_link_desc *vaddr; +}; + +struct hal_srng_params { + dma_addr_t ring_base_paddr; + u32 *ring_base_vaddr; + int num_entries; + u32 intr_batch_cntr_thres_entries; + u32 intr_timer_thres_us; + u32 flags; + u32 max_buffer_len; + u32 low_threshold; + + /* Add more params as needed */ +}; + +enum hal_srng_dir { + HAL_SRNG_DIR_SRC, + HAL_SRNG_DIR_DST +}; + +/* srng flags */ +#define HAL_SRNG_FLAGS_MSI_SWAP 0x00000008 +#define HAL_SRNG_FLAGS_RING_PTR_SWAP 0x00000010 +#define HAL_SRNG_FLAGS_DATA_TLV_SWAP 0x00000020 +#define HAL_SRNG_FLAGS_LOW_THRESH_INTR_EN 0x00010000 +#define HAL_SRNG_FLAGS_MSI_INTR 0x00020000 +#define HAL_SRNG_FLAGS_LMAC_RING 0x80000000 + +#define HAL_SRNG_TLV_HDR_TAG GENMASK(9, 1) +#define HAL_SRNG_TLV_HDR_LEN GENMASK(25, 10) + +/* Common SRNG ring structure for source and destination rings */ +struct hal_srng { + /* Unique SRNG ring ID */ + u8 ring_id; + + /* Ring initialization done */ + u8 initialized; + + /* Interrupt/MSI value assigned to this ring */ + int irq; + + /* Physical base address of the ring */ + dma_addr_t ring_base_paddr; + + /* Virtual base address of the ring */ + u32 *ring_base_vaddr; + + /* Number of entries in ring */ + u32 num_entries; + + /* Ring size */ + u32 ring_size; + + /* Ring size mask */ + u32 ring_size_mask; + + /* Size of ring entry */ + u32 entry_size; + + /* Interrupt timer threshold - in micro seconds */ + u32 intr_timer_thres_us; + + /* Interrupt batch counter threshold - in number of ring entries */ + u32 intr_batch_cntr_thres_entries; + + /* MSI Address */ + dma_addr_t msi_addr; + + /* MSI data */ + u32 msi_data; + + /* Misc flags */ + u32 flags; + + /* Lock for serializing ring index updates */ + spinlock_t lock; + + /* Start offset of SRNG register groups for this ring + * TBD: See if this is required - register address can be derived + * from ring ID + */ + u32 hwreg_base[HAL_SRNG_NUM_REG_GRP]; + + /* Source or Destination ring */ + enum hal_srng_dir ring_dir; + + union { + struct { + /* SW tail pointer */ + u32 tp; + + /* Shadow head pointer location to be updated by HW */ + volatile u32 *hp_addr; + + /* Cached head pointer */ + u32 cached_hp; + + /* Tail pointer location to be updated by SW - This + * will be a register address and need not be + * accessed through SW structure + */ + u32 *tp_addr; + + /* Current SW loop cnt */ + u32 loop_cnt; + + /* max transfer size */ + u16 max_buffer_length; + } dst_ring; + + struct { + /* SW head pointer */ + u32 hp; + + /* SW reap head pointer */ + u32 reap_hp; + + /* Shadow tail pointer location to be updated by HW */ + u32 *tp_addr; + + /* Cached tail pointer */ + u32 cached_tp; + + /* Head pointer location to be updated by SW - This + * will be a register address and need not be accessed + * through SW structure + */ + u32 *hp_addr; + + /* Low threshold - in number of ring entries */ + u32 low_threshold; + } src_ring; + } u; +}; + +/* Interrupt mitigation - Batch threshold in terms of numer of frames */ +#define HAL_SRNG_INT_BATCH_THRESHOLD_TX 256 +#define HAL_SRNG_INT_BATCH_THRESHOLD_RX 128 +#define HAL_SRNG_INT_BATCH_THRESHOLD_OTHER 1 + +/* Interrupt mitigation - timer threshold in us */ +#define HAL_SRNG_INT_TIMER_THRESHOLD_TX 1000 +#define HAL_SRNG_INT_TIMER_THRESHOLD_RX 500 +#define HAL_SRNG_INT_TIMER_THRESHOLD_OTHER 1000 + +/* HW SRNG configuration table */ +struct hal_srng_config { + int start_ring_id; + u16 max_rings; + u16 entry_size; + u32 reg_start[HAL_SRNG_NUM_REG_GRP]; + u16 reg_size[HAL_SRNG_NUM_REG_GRP]; + u8 lmac_ring; + enum hal_srng_dir ring_dir; + u32 max_size; +}; + +/** + * enum hal_rx_buf_return_buf_manager + * + * @HAL_RX_BUF_RBM_WBM_IDLE_BUF_LIST: Buffer returned to WBM idle buffer list + * @HAL_RX_BUF_RBM_WBM_IDLE_DESC_LIST: Descriptor returned to WBM idle + * descriptor list. + * @HAL_RX_BUF_RBM_FW_BM: Buffer returned to FW + * @HAL_RX_BUF_RBM_SW0_BM: For Tx completion -- returned to host + * @HAL_RX_BUF_RBM_SW1_BM: For Tx completion -- returned to host + * @HAL_RX_BUF_RBM_SW2_BM: For Tx completion -- returned to host + * @HAL_RX_BUF_RBM_SW3_BM: For Rx release -- returned to host + */ + +enum hal_rx_buf_return_buf_manager { + HAL_RX_BUF_RBM_WBM_IDLE_BUF_LIST, + HAL_RX_BUF_RBM_WBM_IDLE_DESC_LIST, + HAL_RX_BUF_RBM_FW_BM, + HAL_RX_BUF_RBM_SW0_BM, + HAL_RX_BUF_RBM_SW1_BM, + HAL_RX_BUF_RBM_SW2_BM, + HAL_RX_BUF_RBM_SW3_BM, +}; + +#define HAL_SRNG_DESC_LOOP_CNT 0xf0000000 + +#define HAL_REO_CMD_FLG_NEED_STATUS BIT(0) +#define HAL_REO_CMD_FLG_STATS_CLEAR BIT(1) +#define HAL_REO_CMD_FLG_FLUSH_BLOCK_LATER BIT(2) +#define HAL_REO_CMD_FLG_FLUSH_RELEASE_BLOCKING BIT(3) +#define HAL_REO_CMD_FLG_FLUSH_NO_INVAL BIT(4) +#define HAL_REO_CMD_FLG_FLUSH_FWD_ALL_MPDUS BIT(5) +#define HAL_REO_CMD_FLG_FLUSH_ALL BIT(6) +#define HAL_REO_CMD_FLG_UNBLK_RESOURCE BIT(7) +#define HAL_REO_CMD_FLG_UNBLK_CACHE BIT(8) + +/* Should be matching with HAL_REO_UPD_RX_QUEUE_INFO0_UPD_* feilds */ +#define HAL_REO_CMD_UPD0_RX_QUEUE_NUM BIT(8) +#define HAL_REO_CMD_UPD0_VLD BIT(9) +#define HAL_REO_CMD_UPD0_ALDC BIT(10) +#define HAL_REO_CMD_UPD0_DIS_DUP_DETECTION BIT(11) +#define HAL_REO_CMD_UPD0_SOFT_REORDER_EN BIT(12) +#define HAL_REO_CMD_UPD0_AC BIT(13) +#define HAL_REO_CMD_UPD0_BAR BIT(14) +#define HAL_REO_CMD_UPD0_RETRY BIT(15) +#define HAL_REO_CMD_UPD0_CHECK_2K_MODE BIT(16) +#define HAL_REO_CMD_UPD0_OOR_MODE BIT(17) +#define HAL_REO_CMD_UPD0_BA_WINDOW_SIZE BIT(18) +#define HAL_REO_CMD_UPD0_PN_CHECK BIT(19) +#define HAL_REO_CMD_UPD0_EVEN_PN BIT(20) +#define HAL_REO_CMD_UPD0_UNEVEN_PN BIT(21) +#define HAL_REO_CMD_UPD0_PN_HANDLE_ENABLE BIT(22) +#define HAL_REO_CMD_UPD0_PN_SIZE BIT(23) +#define HAL_REO_CMD_UPD0_IGNORE_AMPDU_FLG BIT(24) +#define HAL_REO_CMD_UPD0_SVLD BIT(25) +#define HAL_REO_CMD_UPD0_SSN BIT(26) +#define HAL_REO_CMD_UPD0_SEQ_2K_ERR BIT(27) +#define HAL_REO_CMD_UPD0_PN_ERR BIT(28) +#define HAL_REO_CMD_UPD0_PN_VALID BIT(29) +#define HAL_REO_CMD_UPD0_PN BIT(30) + +/* Should be matching with HAL_REO_UPD_RX_QUEUE_INFO1_* feilds */ +#define HAL_REO_CMD_UPD1_VLD BIT(16) +#define HAL_REO_CMD_UPD1_ALDC GENMASK(18, 17) +#define HAL_REO_CMD_UPD1_DIS_DUP_DETECTION BIT(19) +#define HAL_REO_CMD_UPD1_SOFT_REORDER_EN BIT(20) +#define HAL_REO_CMD_UPD1_AC GENMASK(22, 21) +#define HAL_REO_CMD_UPD1_BAR BIT(23) +#define HAL_REO_CMD_UPD1_RETRY BIT(24) +#define HAL_REO_CMD_UPD1_CHECK_2K_MODE BIT(25) +#define HAL_REO_CMD_UPD1_OOR_MODE BIT(26) +#define HAL_REO_CMD_UPD1_PN_CHECK BIT(27) +#define HAL_REO_CMD_UPD1_EVEN_PN BIT(28) +#define HAL_REO_CMD_UPD1_UNEVEN_PN BIT(29) +#define HAL_REO_CMD_UPD1_PN_HANDLE_ENABLE BIT(30) +#define HAL_REO_CMD_UPD1_IGNORE_AMPDU_FLG BIT(31) + +/* Should be matching with HAL_REO_UPD_RX_QUEUE_INFO2_* feilds */ +#define HAL_REO_CMD_UPD2_SVLD BIT(10) +#define HAL_REO_CMD_UPD2_SSN GENMASK(22, 11) +#define HAL_REO_CMD_UPD2_SEQ_2K_ERR BIT(23) +#define HAL_REO_CMD_UPD2_PN_ERR BIT(24) + +#define HAL_REO_DEST_RING_CTRL_HASH_RING_MAP GENMASK(31, 8) + +struct ath11k_hal_reo_cmd { + u32 addr_lo; + u32 flag; + u32 upd0; + u32 upd1; + u32 upd2; + u32 pn[4]; + u16 rx_queue_num; + u16 min_rel; + u16 min_fwd; + u8 addr_hi; + u8 ac_list; + u8 blocking_idx; + u16 ba_window_size; + u8 pn_size; +}; + +enum hal_pn_type { + HAL_PN_TYPE_NONE, + HAL_PN_TYPE_WPA, + HAL_PN_TYPE_WAPI_EVEN, + HAL_PN_TYPE_WAPI_UNEVEN, +}; + +enum hal_ce_desc { + HAL_CE_DESC_SRC, + HAL_CE_DESC_DST, + HAL_CE_DESC_DST_STATUS, +}; + +struct hal_reo_status_header { + u16 cmd_num; + enum hal_reo_cmd_status cmd_status; + u16 cmd_exe_time; + u32 timestamp; +}; + +struct hal_reo_status_queue_stats { + u16 ssn; + u16 curr_idx; + u32 pn[4]; + u32 last_rx_queue_ts; + u32 last_rx_dequeue_ts; + u32 rx_bitmap[8]; /* Bitmap from 0-255 */ + u32 curr_mpdu_cnt; + u32 curr_msdu_cnt; + u16 fwd_due_to_bar_cnt; + u16 dup_cnt; + u32 frames_in_order_cnt; + u32 num_mpdu_processed_cnt; + u32 num_msdu_processed_cnt; + u32 total_num_processed_byte_cnt; + u32 late_rx_mpdu_cnt; + u32 reorder_hole_cnt; + u8 timeout_cnt; + u8 bar_rx_cnt; + u8 num_window_2k_jump_cnt; +}; + +struct hal_reo_status_flush_queue { + bool err_detected; +}; + +enum hal_reo_status_flush_cache_err_code { + HAL_REO_STATUS_FLUSH_CACHE_ERR_CODE_SUCCESS, + HAL_REO_STATUS_FLUSH_CACHE_ERR_CODE_IN_USE, + HAL_REO_STATUS_FLUSH_CACHE_ERR_CODE_NOT_FOUND, +}; + +struct hal_reo_status_flush_cache { + bool err_detected; + enum hal_reo_status_flush_cache_err_code err_code; + bool cache_controller_flush_status_hit; + u8 cache_controller_flush_status_desc_type; + u8 cache_controller_flush_status_client_id; + u8 cache_controller_flush_status_err; + u8 cache_controller_flush_status_cnt; +}; + +enum hal_reo_status_unblock_cache_type { + HAL_REO_STATUS_UNBLOCK_BLOCKING_RESOURCE, + HAL_REO_STATUS_UNBLOCK_ENTIRE_CACHE_USAGE, +}; + +struct hal_reo_status_unblock_cache { + bool err_detected; + enum hal_reo_status_unblock_cache_type unblock_type; +}; + +struct hal_reo_status_flush_timeout_list { + bool err_detected; + bool list_empty; + u16 release_desc_cnt; + u16 fwd_buf_cnt; +}; + +enum hal_reo_threshold_idx { + HAL_REO_THRESHOLD_IDX_DESC_COUNTER0, + HAL_REO_THRESHOLD_IDX_DESC_COUNTER1, + HAL_REO_THRESHOLD_IDX_DESC_COUNTER2, + HAL_REO_THRESHOLD_IDX_DESC_COUNTER_SUM, +}; + +struct hal_reo_status_desc_thresh_reached { + enum hal_reo_threshold_idx threshold_idx; + u32 link_desc_counter0; + u32 link_desc_counter1; + u32 link_desc_counter2; + u32 link_desc_counter_sum; +}; + +struct hal_reo_status { + struct hal_reo_status_header uniform_hdr; + u8 loop_cnt; + union { + struct hal_reo_status_queue_stats queue_stats; + struct hal_reo_status_flush_queue flush_queue; + struct hal_reo_status_flush_cache flush_cache; + struct hal_reo_status_unblock_cache unblock_cache; + struct hal_reo_status_flush_timeout_list timeout_list; + struct hal_reo_status_desc_thresh_reached desc_thresh_reached; + } u; +}; + +/** + * HAL context to be used to access SRNG APIs (currently used by data path + * and transport (CE) modules) + */ +struct ath11k_hal { + /* HAL internal state for all SRNG rings. + */ + struct hal_srng srng_list[HAL_SRNG_RING_ID_MAX]; + + /* SRNG configuration table */ + const struct hal_srng_config *srng_config; + + /* Remote pointer memory for HW/FW updates */ + struct { + u32 *vaddr; + dma_addr_t paddr; + } rdp; + + /* Shared memory for ring pointer updates from host to FW */ + struct { + u32 *vaddr; + dma_addr_t paddr; + } wrp; + + /* Available REO blocking resources bitmap */ + u8 avail_blk_resource; + + u8 current_blk_index; + + /* shadow register configuration */ + u32 shadow_reg_addr[SHADOW_NUM_REGISTERS]; + int num_shadow_reg_configured; +}; + +u32 ath11k_hal_reo_qdesc_size(u32 ba_window_size, u8 tid); +void ath11k_hal_reo_qdesc_setup(void *vaddr, int tid, u32 ba_window_size, + u32 start_seqtype); +void ath11k_hal_reo_init_cmd_ring(struct ath11k_base *ab, + struct hal_srng *srng); +void ath11k_hal_reo_hw_setup(struct ath11k_base *ab); +void ath11k_hal_setup_link_idle_list(struct ath11k_base *ab, + struct hal_wbm_idle_scatter_list *sbuf, + u32 nsbufs, u32 tot_link_desc, + u32 end_offset); + +dma_addr_t ath11k_hal_srng_get_tp_addr(struct ath11k_base *ab, + struct hal_srng *srng); +dma_addr_t ath11k_hal_srng_get_hp_addr(struct ath11k_base *ab, + struct hal_srng *srng); +void ath11k_hal_set_link_desc_addr(struct hal_wbm_link_desc *desc, u32 cookie, + dma_addr_t paddr); +u32 ath11k_hal_ce_get_desc_size(enum hal_ce_desc type); +void ath11k_hal_ce_src_set_desc(void *buf, dma_addr_t paddr, u32 len, u32 id, + u8 byte_swap_data); +void ath11k_hal_ce_dst_set_desc(void *buf, dma_addr_t paddr); +u32 ath11k_hal_ce_dst_status_get_length(void *buf); +int ath11k_hal_srng_get_entrysize(u32 ring_type); +int ath11k_hal_srng_get_max_entries(u32 ring_type); +void ath11k_hal_srng_get_params(struct ath11k_base *ab, struct hal_srng *srng, + struct hal_srng_params *params); +u32 *ath11k_hal_srng_dst_get_next_entry(struct ath11k_base *ab, + struct hal_srng *srng); +u32 *ath11k_hal_srng_dst_peek(struct ath11k_base *ab, struct hal_srng *srng); +int ath11k_hal_srng_dst_num_free(struct ath11k_base *ab, struct hal_srng *srng, + bool sync_hw_ptr); +u32 *ath11k_hal_srng_src_peek(struct ath11k_base *ab, struct hal_srng *srng); +u32 *ath11k_hal_srng_src_get_next_reaped(struct ath11k_base *ab, + struct hal_srng *srng); +u32 *ath11k_hal_srng_src_reap_next(struct ath11k_base *ab, + struct hal_srng *srng); +u32 *ath11k_hal_srng_src_get_next_entry(struct ath11k_base *ab, + struct hal_srng *srng); +int ath11k_hal_srng_src_num_free(struct ath11k_base *ab, struct hal_srng *srng, + bool sync_hw_ptr); +void ath11k_hal_srng_access_begin(struct ath11k_base *ab, + struct hal_srng *srng); +void ath11k_hal_srng_access_end(struct ath11k_base *ab, struct hal_srng *srng); +int ath11k_hal_srng_setup(struct ath11k_base *ab, enum hal_ring_type type, + int ring_num, int mac_id, + struct hal_srng_params *params); +int ath11k_hal_srng_init(struct ath11k_base *ath11k); +void ath11k_hal_srng_deinit(struct ath11k_base *ath11k); + +#endif diff --git a/drivers/net/wireless/ath/ath11k/hal_desc.h b/drivers/net/wireless/ath/ath11k/hal_desc.h new file mode 100644 index 0000000000000..35e191b772718 --- /dev/null +++ b/drivers/net/wireless/ath/ath11k/hal_desc.h @@ -0,0 +1,2469 @@ +/* SPDX-License-Identifier: BSD-3-Clause-Clear */ +/* + * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. + */ +#ifndef ATH11K_HAL_DESC_H +#define ATH11K_HAL_DESC_H + +#define BUFFER_ADDR_INFO0_ADDR GENMASK(31, 0) + +#define BUFFER_ADDR_INFO1_ADDR GENMASK(7, 0) +#define BUFFER_ADDR_INFO1_RET_BUF_MGR GENMASK(10, 8) +#define BUFFER_ADDR_INFO1_SW_COOKIE GENMASK(31, 11) + +struct ath11k_buffer_addr { + u32 info0; + u32 info1; +} __packed; + +/* ath11k_buffer_addr + * + * info0 + * Address (lower 32 bits) of the msdu buffer or msdu extension + * descriptor or Link descriptor + * + * addr + * Address (upper 8 bits) of the msdu buffer or msdu extension + * descriptor or Link descriptor + * + * return_buffer_manager (RBM) + * Consumer: WBM + * Producer: SW/FW + * Indicates to which buffer manager the buffer or MSDU_EXTENSION + * descriptor or link descriptor that is being pointed to shall be + * returned after the frame has been processed. It is used by WBM + * for routing purposes. + * + * Values are defined in enum %HAL_RX_BUF_RBM_ + * + * sw_buffer_cookie + * Cookie field exclusively used by SW. HW ignores the contents, + * accept that it passes the programmed value on to other + * descriptors together with the physical address. + * + * Field can be used by SW to for example associate the buffers + * physical address with the virtual address. + */ + +enum hal_tlv_tag { + HAL_MACTX_CBF_START = 0 /* 0x0 */, + HAL_PHYRX_DATA = 1 /* 0x1 */, + HAL_PHYRX_CBF_DATA_RESP = 2 /* 0x2 */, + HAL_PHYRX_ABORT_REQUEST = 3 /* 0x3 */, + HAL_PHYRX_USER_ABORT_NOTIFICATION = 4 /* 0x4 */, + HAL_MACTX_DATA_RESP = 5 /* 0x5 */, + HAL_MACTX_CBF_DATA = 6 /* 0x6 */, + HAL_MACTX_CBF_DONE = 7 /* 0x7 */, + HAL_MACRX_CBF_READ_REQUEST = 8 /* 0x8 */, + HAL_MACRX_CBF_DATA_REQUEST = 9 /* 0x9 */, + HAL_MACRX_EXPECT_NDP_RECEPTION = 10 /* 0xa */, + HAL_MACRX_FREEZE_CAPTURE_CHANNEL = 11 /* 0xb */, + HAL_MACRX_NDP_TIMEOUT = 12 /* 0xc */, + HAL_MACRX_ABORT_ACK = 13 /* 0xd */, + HAL_MACRX_REQ_IMPLICIT_FB = 14 /* 0xe */, + HAL_MACRX_CHAIN_MASK = 15 /* 0xf */, + HAL_MACRX_NAP_USER = 16 /* 0x10 */, + HAL_MACRX_ABORT_REQUEST = 17 /* 0x11 */, + HAL_PHYTX_OTHER_TRANSMIT_INFO16 = 18 /* 0x12 */, + HAL_PHYTX_ABORT_ACK = 19 /* 0x13 */, + HAL_PHYTX_ABORT_REQUEST = 20 /* 0x14 */, + HAL_PHYTX_PKT_END = 21 /* 0x15 */, + HAL_PHYTX_PPDU_HEADER_INFO_REQUEST = 22 /* 0x16 */, + HAL_PHYTX_REQUEST_CTRL_INFO = 23 /* 0x17 */, + HAL_PHYTX_DATA_REQUEST = 24 /* 0x18 */, + HAL_PHYTX_BF_CV_LOADING_DONE = 25 /* 0x19 */, + HAL_PHYTX_NAP_ACK = 26 /* 0x1a */, + HAL_PHYTX_NAP_DONE = 27 /* 0x1b */, + HAL_PHYTX_OFF_ACK = 28 /* 0x1c */, + HAL_PHYTX_ON_ACK = 29 /* 0x1d */, + HAL_PHYTX_SYNTH_OFF_ACK = 30 /* 0x1e */, + HAL_PHYTX_DEBUG16 = 31 /* 0x1f */, + HAL_MACTX_ABORT_REQUEST = 32 /* 0x20 */, + HAL_MACTX_ABORT_ACK = 33 /* 0x21 */, + HAL_MACTX_PKT_END = 34 /* 0x22 */, + HAL_MACTX_PRE_PHY_DESC = 35 /* 0x23 */, + HAL_MACTX_BF_PARAMS_COMMON = 36 /* 0x24 */, + HAL_MACTX_BF_PARAMS_PER_USER = 37 /* 0x25 */, + HAL_MACTX_PREFETCH_CV = 38 /* 0x26 */, + HAL_MACTX_USER_DESC_COMMON = 39 /* 0x27 */, + HAL_MACTX_USER_DESC_PER_USER = 40 /* 0x28 */, + HAL_EXAMPLE_USER_TLV_16 = 41 /* 0x29 */, + HAL_EXAMPLE_TLV_16 = 42 /* 0x2a */, + HAL_MACTX_PHY_OFF = 43 /* 0x2b */, + HAL_MACTX_PHY_ON = 44 /* 0x2c */, + HAL_MACTX_SYNTH_OFF = 45 /* 0x2d */, + HAL_MACTX_EXPECT_CBF_COMMON = 46 /* 0x2e */, + HAL_MACTX_EXPECT_CBF_PER_USER = 47 /* 0x2f */, + HAL_MACTX_PHY_DESC = 48 /* 0x30 */, + HAL_MACTX_L_SIG_A = 49 /* 0x31 */, + HAL_MACTX_L_SIG_B = 50 /* 0x32 */, + HAL_MACTX_HT_SIG = 51 /* 0x33 */, + HAL_MACTX_VHT_SIG_A = 52 /* 0x34 */, + HAL_MACTX_VHT_SIG_B_SU20 = 53 /* 0x35 */, + HAL_MACTX_VHT_SIG_B_SU40 = 54 /* 0x36 */, + HAL_MACTX_VHT_SIG_B_SU80 = 55 /* 0x37 */, + HAL_MACTX_VHT_SIG_B_SU160 = 56 /* 0x38 */, + HAL_MACTX_VHT_SIG_B_MU20 = 57 /* 0x39 */, + HAL_MACTX_VHT_SIG_B_MU40 = 58 /* 0x3a */, + HAL_MACTX_VHT_SIG_B_MU80 = 59 /* 0x3b */, + HAL_MACTX_VHT_SIG_B_MU160 = 60 /* 0x3c */, + HAL_MACTX_SERVICE = 61 /* 0x3d */, + HAL_MACTX_HE_SIG_A_SU = 62 /* 0x3e */, + HAL_MACTX_HE_SIG_A_MU_DL = 63 /* 0x3f */, + HAL_MACTX_HE_SIG_A_MU_UL = 64 /* 0x40 */, + HAL_MACTX_HE_SIG_B1_MU = 65 /* 0x41 */, + HAL_MACTX_HE_SIG_B2_MU = 66 /* 0x42 */, + HAL_MACTX_HE_SIG_B2_OFDMA = 67 /* 0x43 */, + HAL_MACTX_DELETE_CV = 68 /* 0x44 */, + HAL_MACTX_MU_UPLINK_COMMON = 69 /* 0x45 */, + HAL_MACTX_MU_UPLINK_USER_SETUP = 70 /* 0x46 */, + HAL_MACTX_OTHER_TRANSMIT_INFO = 71 /* 0x47 */, + HAL_MACTX_PHY_NAP = 72 /* 0x48 */, + HAL_MACTX_DEBUG = 73 /* 0x49 */, + HAL_PHYRX_ABORT_ACK = 74 /* 0x4a */, + HAL_PHYRX_GENERATED_CBF_DETAILS = 75 /* 0x4b */, + HAL_PHYRX_RSSI_LEGACY = 76 /* 0x4c */, + HAL_PHYRX_RSSI_HT = 77 /* 0x4d */, + HAL_PHYRX_USER_INFO = 78 /* 0x4e */, + HAL_PHYRX_PKT_END = 79 /* 0x4f */, + HAL_PHYRX_DEBUG = 80 /* 0x50 */, + HAL_PHYRX_CBF_TRANSFER_DONE = 81 /* 0x51 */, + HAL_PHYRX_CBF_TRANSFER_ABORT = 82 /* 0x52 */, + HAL_PHYRX_L_SIG_A = 83 /* 0x53 */, + HAL_PHYRX_L_SIG_B = 84 /* 0x54 */, + HAL_PHYRX_HT_SIG = 85 /* 0x55 */, + HAL_PHYRX_VHT_SIG_A = 86 /* 0x56 */, + HAL_PHYRX_VHT_SIG_B_SU20 = 87 /* 0x57 */, + HAL_PHYRX_VHT_SIG_B_SU40 = 88 /* 0x58 */, + HAL_PHYRX_VHT_SIG_B_SU80 = 89 /* 0x59 */, + HAL_PHYRX_VHT_SIG_B_SU160 = 90 /* 0x5a */, + HAL_PHYRX_VHT_SIG_B_MU20 = 91 /* 0x5b */, + HAL_PHYRX_VHT_SIG_B_MU40 = 92 /* 0x5c */, + HAL_PHYRX_VHT_SIG_B_MU80 = 93 /* 0x5d */, + HAL_PHYRX_VHT_SIG_B_MU160 = 94 /* 0x5e */, + HAL_PHYRX_HE_SIG_A_SU = 95 /* 0x5f */, + HAL_PHYRX_HE_SIG_A_MU_DL = 96 /* 0x60 */, + HAL_PHYRX_HE_SIG_A_MU_UL = 97 /* 0x61 */, + HAL_PHYRX_HE_SIG_B1_MU = 98 /* 0x62 */, + HAL_PHYRX_HE_SIG_B2_MU = 99 /* 0x63 */, + HAL_PHYRX_HE_SIG_B2_OFDMA = 100 /* 0x64 */, + HAL_PHYRX_OTHER_RECEIVE_INFO = 101 /* 0x65 */, + HAL_PHYRX_COMMON_USER_INFO = 102 /* 0x66 */, + HAL_PHYRX_DATA_DONE = 103 /* 0x67 */, + HAL_RECEIVE_RSSI_INFO = 104 /* 0x68 */, + HAL_RECEIVE_USER_INFO = 105 /* 0x69 */, + HAL_MIMO_CONTROL_INFO = 106 /* 0x6a */, + HAL_RX_LOCATION_INFO = 107 /* 0x6b */, + HAL_COEX_TX_REQ = 108 /* 0x6c */, + HAL_DUMMY = 109 /* 0x6d */, + HAL_RX_TIMING_OFFSET_INFO = 110 /* 0x6e */, + HAL_EXAMPLE_TLV_32_NAME = 111 /* 0x6f */, + HAL_MPDU_LIMIT = 112 /* 0x70 */, + HAL_NA_LENGTH_END = 113 /* 0x71 */, + HAL_OLE_BUF_STATUS = 114 /* 0x72 */, + HAL_PCU_PPDU_SETUP_DONE = 115 /* 0x73 */, + HAL_PCU_PPDU_SETUP_END = 116 /* 0x74 */, + HAL_PCU_PPDU_SETUP_INIT = 117 /* 0x75 */, + HAL_PCU_PPDU_SETUP_START = 118 /* 0x76 */, + HAL_PDG_FES_SETUP = 119 /* 0x77 */, + HAL_PDG_RESPONSE = 120 /* 0x78 */, + HAL_PDG_TX_REQ = 121 /* 0x79 */, + HAL_SCH_WAIT_INSTR = 122 /* 0x7a */, + HAL_SCHEDULER_TLV = 123 /* 0x7b */, + HAL_TQM_FLOW_EMPTY_STATUS = 124 /* 0x7c */, + HAL_TQM_FLOW_NOT_EMPTY_STATUS = 125 /* 0x7d */, + HAL_TQM_GEN_MPDU_LENGTH_LIST = 126 /* 0x7e */, + HAL_TQM_GEN_MPDU_LENGTH_LIST_STATUS = 127 /* 0x7f */, + HAL_TQM_GEN_MPDUS = 128 /* 0x80 */, + HAL_TQM_GEN_MPDUS_STATUS = 129 /* 0x81 */, + HAL_TQM_REMOVE_MPDU = 130 /* 0x82 */, + HAL_TQM_REMOVE_MPDU_STATUS = 131 /* 0x83 */, + HAL_TQM_REMOVE_MSDU = 132 /* 0x84 */, + HAL_TQM_REMOVE_MSDU_STATUS = 133 /* 0x85 */, + HAL_TQM_UPDATE_TX_MPDU_COUNT = 134 /* 0x86 */, + HAL_TQM_WRITE_CMD = 135 /* 0x87 */, + HAL_OFDMA_TRIGGER_DETAILS = 136 /* 0x88 */, + HAL_TX_DATA = 137 /* 0x89 */, + HAL_TX_FES_SETUP = 138 /* 0x8a */, + HAL_RX_PACKET = 139 /* 0x8b */, + HAL_EXPECTED_RESPONSE = 140 /* 0x8c */, + HAL_TX_MPDU_END = 141 /* 0x8d */, + HAL_TX_MPDU_START = 142 /* 0x8e */, + HAL_TX_MSDU_END = 143 /* 0x8f */, + HAL_TX_MSDU_START = 144 /* 0x90 */, + HAL_TX_SW_MODE_SETUP = 145 /* 0x91 */, + HAL_TXPCU_BUFFER_STATUS = 146 /* 0x92 */, + HAL_TXPCU_USER_BUFFER_STATUS = 147 /* 0x93 */, + HAL_DATA_TO_TIME_CONFIG = 148 /* 0x94 */, + HAL_EXAMPLE_USER_TLV_32 = 149 /* 0x95 */, + HAL_MPDU_INFO = 150 /* 0x96 */, + HAL_PDG_USER_SETUP = 151 /* 0x97 */, + HAL_TX_11AH_SETUP = 152 /* 0x98 */, + HAL_REO_UPDATE_RX_REO_QUEUE_STATUS = 153 /* 0x99 */, + HAL_TX_PEER_ENTRY = 154 /* 0x9a */, + HAL_TX_RAW_OR_NATIVE_FRAME_SETUP = 155 /* 0x9b */, + HAL_EXAMPLE_STRUCT_NAME = 156 /* 0x9c */, + HAL_PCU_PPDU_SETUP_END_INFO = 157 /* 0x9d */, + HAL_PPDU_RATE_SETTING = 158 /* 0x9e */, + HAL_PROT_RATE_SETTING = 159 /* 0x9f */, + HAL_RX_MPDU_DETAILS = 160 /* 0xa0 */, + HAL_EXAMPLE_USER_TLV_42 = 161 /* 0xa1 */, + HAL_RX_MSDU_LINK = 162 /* 0xa2 */, + HAL_RX_REO_QUEUE = 163 /* 0xa3 */, + HAL_ADDR_SEARCH_ENTRY = 164 /* 0xa4 */, + HAL_SCHEDULER_CMD = 165 /* 0xa5 */, + HAL_TX_FLUSH = 166 /* 0xa6 */, + HAL_TQM_ENTRANCE_RING = 167 /* 0xa7 */, + HAL_TX_DATA_WORD = 168 /* 0xa8 */, + HAL_TX_MPDU_DETAILS = 169 /* 0xa9 */, + HAL_TX_MPDU_LINK = 170 /* 0xaa */, + HAL_TX_MPDU_LINK_PTR = 171 /* 0xab */, + HAL_TX_MPDU_QUEUE_HEAD = 172 /* 0xac */, + HAL_TX_MPDU_QUEUE_EXT = 173 /* 0xad */, + HAL_TX_MPDU_QUEUE_EXT_PTR = 174 /* 0xae */, + HAL_TX_MSDU_DETAILS = 175 /* 0xaf */, + HAL_TX_MSDU_EXTENSION = 176 /* 0xb0 */, + HAL_TX_MSDU_FLOW = 177 /* 0xb1 */, + HAL_TX_MSDU_LINK = 178 /* 0xb2 */, + HAL_TX_MSDU_LINK_ENTRY_PTR = 179 /* 0xb3 */, + HAL_RESPONSE_RATE_SETTING = 180 /* 0xb4 */, + HAL_TXPCU_BUFFER_BASICS = 181 /* 0xb5 */, + HAL_UNIFORM_DESCRIPTOR_HEADER = 182 /* 0xb6 */, + HAL_UNIFORM_TQM_CMD_HEADER = 183 /* 0xb7 */, + HAL_UNIFORM_TQM_STATUS_HEADER = 184 /* 0xb8 */, + HAL_USER_RATE_SETTING = 185 /* 0xb9 */, + HAL_WBM_BUFFER_RING = 186 /* 0xba */, + HAL_WBM_LINK_DESCRIPTOR_RING = 187 /* 0xbb */, + HAL_WBM_RELEASE_RING = 188 /* 0xbc */, + HAL_TX_FLUSH_REQ = 189 /* 0xbd */, + HAL_RX_MSDU_DETAILS = 190 /* 0xbe */, + HAL_TQM_WRITE_CMD_STATUS = 191 /* 0xbf */, + HAL_TQM_GET_MPDU_QUEUE_STATS = 192 /* 0xc0 */, + HAL_TQM_GET_MSDU_FLOW_STATS = 193 /* 0xc1 */, + HAL_EXAMPLE_USER_CTLV_32 = 194 /* 0xc2 */, + HAL_TX_FES_STATUS_START = 195 /* 0xc3 */, + HAL_TX_FES_STATUS_USER_PPDU = 196 /* 0xc4 */, + HAL_TX_FES_STATUS_USER_RESPONSE = 197 /* 0xc5 */, + HAL_TX_FES_STATUS_END = 198 /* 0xc6 */, + HAL_RX_TRIG_INFO = 199 /* 0xc7 */, + HAL_RXPCU_TX_SETUP_CLEAR = 200 /* 0xc8 */, + HAL_RX_FRAME_BITMAP_REQ = 201 /* 0xc9 */, + HAL_RX_FRAME_BITMAP_ACK = 202 /* 0xca */, + HAL_COEX_RX_STATUS = 203 /* 0xcb */, + HAL_RX_START_PARAM = 204 /* 0xcc */, + HAL_RX_PPDU_START = 205 /* 0xcd */, + HAL_RX_PPDU_END = 206 /* 0xce */, + HAL_RX_MPDU_START = 207 /* 0xcf */, + HAL_RX_MPDU_END = 208 /* 0xd0 */, + HAL_RX_MSDU_START = 209 /* 0xd1 */, + HAL_RX_MSDU_END = 210 /* 0xd2 */, + HAL_RX_ATTENTION = 211 /* 0xd3 */, + HAL_RECEIVED_RESPONSE_INFO = 212 /* 0xd4 */, + HAL_RX_PHY_SLEEP = 213 /* 0xd5 */, + HAL_RX_HEADER = 214 /* 0xd6 */, + HAL_RX_PEER_ENTRY = 215 /* 0xd7 */, + HAL_RX_FLUSH = 216 /* 0xd8 */, + HAL_RX_RESPONSE_REQUIRED_INFO = 217 /* 0xd9 */, + HAL_RX_FRAMELESS_BAR_DETAILS = 218 /* 0xda */, + HAL_TQM_GET_MPDU_QUEUE_STATS_STATUS = 219 /* 0xdb */, + HAL_TQM_GET_MSDU_FLOW_STATS_STATUS = 220 /* 0xdc */, + HAL_TX_CBF_INFO = 221 /* 0xdd */, + HAL_PCU_PPDU_SETUP_USER = 222 /* 0xde */, + HAL_RX_MPDU_PCU_START = 223 /* 0xdf */, + HAL_RX_PM_INFO = 224 /* 0xe0 */, + HAL_RX_USER_PPDU_END = 225 /* 0xe1 */, + HAL_RX_PRE_PPDU_START = 226 /* 0xe2 */, + HAL_RX_PREAMBLE = 227 /* 0xe3 */, + HAL_TX_FES_SETUP_COMPLETE = 228 /* 0xe4 */, + HAL_TX_LAST_MPDU_FETCHED = 229 /* 0xe5 */, + HAL_TXDMA_STOP_REQUEST = 230 /* 0xe6 */, + HAL_RXPCU_SETUP = 231 /* 0xe7 */, + HAL_RXPCU_USER_SETUP = 232 /* 0xe8 */, + HAL_TX_FES_STATUS_ACK_OR_BA = 233 /* 0xe9 */, + HAL_TQM_ACKED_MPDU = 234 /* 0xea */, + HAL_COEX_TX_RESP = 235 /* 0xeb */, + HAL_COEX_TX_STATUS = 236 /* 0xec */, + HAL_MACTX_COEX_PHY_CTRL = 237 /* 0xed */, + HAL_COEX_STATUS_BROADCAST = 238 /* 0xee */, + HAL_RESPONSE_START_STATUS = 239 /* 0xef */, + HAL_RESPONSE_END_STATUS = 240 /* 0xf0 */, + HAL_CRYPTO_STATUS = 241 /* 0xf1 */, + HAL_RECEIVED_TRIGGER_INFO = 242 /* 0xf2 */, + HAL_REO_ENTRANCE_RING = 243 /* 0xf3 */, + HAL_RX_MPDU_LINK = 244 /* 0xf4 */, + HAL_COEX_TX_STOP_CTRL = 245 /* 0xf5 */, + HAL_RX_PPDU_ACK_REPORT = 246 /* 0xf6 */, + HAL_RX_PPDU_NO_ACK_REPORT = 247 /* 0xf7 */, + HAL_SCH_COEX_STATUS = 248 /* 0xf8 */, + HAL_SCHEDULER_COMMAND_STATUS = 249 /* 0xf9 */, + HAL_SCHEDULER_RX_PPDU_NO_RESPONSE_STATUS = 250 /* 0xfa */, + HAL_TX_FES_STATUS_PROT = 251 /* 0xfb */, + HAL_TX_FES_STATUS_START_PPDU = 252 /* 0xfc */, + HAL_TX_FES_STATUS_START_PROT = 253 /* 0xfd */, + HAL_TXPCU_PHYTX_DEBUG32 = 254 /* 0xfe */, + HAL_TXPCU_PHYTX_OTHER_TRANSMIT_INFO32 = 255 /* 0xff */, + HAL_TX_MPDU_COUNT_TRANSFER_END = 256 /* 0x100 */, + HAL_WHO_ANCHOR_OFFSET = 257 /* 0x101 */, + HAL_WHO_ANCHOR_VALUE = 258 /* 0x102 */, + HAL_WHO_CCE_INFO = 259 /* 0x103 */, + HAL_WHO_COMMIT = 260 /* 0x104 */, + HAL_WHO_COMMIT_DONE = 261 /* 0x105 */, + HAL_WHO_FLUSH = 262 /* 0x106 */, + HAL_WHO_L2_LLC = 263 /* 0x107 */, + HAL_WHO_L2_PAYLOAD = 264 /* 0x108 */, + HAL_WHO_L3_CHECKSUM = 265 /* 0x109 */, + HAL_WHO_L3_INFO = 266 /* 0x10a */, + HAL_WHO_L4_CHECKSUM = 267 /* 0x10b */, + HAL_WHO_L4_INFO = 268 /* 0x10c */, + HAL_WHO_MSDU = 269 /* 0x10d */, + HAL_WHO_MSDU_MISC = 270 /* 0x10e */, + HAL_WHO_PACKET_DATA = 271 /* 0x10f */, + HAL_WHO_PACKET_HDR = 272 /* 0x110 */, + HAL_WHO_PPDU_END = 273 /* 0x111 */, + HAL_WHO_PPDU_START = 274 /* 0x112 */, + HAL_WHO_TSO = 275 /* 0x113 */, + HAL_WHO_WMAC_HEADER_PV0 = 276 /* 0x114 */, + HAL_WHO_WMAC_HEADER_PV1 = 277 /* 0x115 */, + HAL_WHO_WMAC_IV = 278 /* 0x116 */, + HAL_MPDU_INFO_END = 279 /* 0x117 */, + HAL_MPDU_INFO_BITMAP = 280 /* 0x118 */, + HAL_TX_QUEUE_EXTENSION = 281 /* 0x119 */, + HAL_RX_PEER_ENTRY_DETAILS = 282 /* 0x11a */, + HAL_RX_REO_QUEUE_REFERENCE = 283 /* 0x11b */, + HAL_RX_REO_QUEUE_EXT = 284 /* 0x11c */, + HAL_SCHEDULER_SELFGEN_RESPONSE_STATUS = 285 /* 0x11d */, + HAL_TQM_UPDATE_TX_MPDU_COUNT_STATUS = 286 /* 0x11e */, + HAL_TQM_ACKED_MPDU_STATUS = 287 /* 0x11f */, + HAL_TQM_ADD_MSDU_STATUS = 288 /* 0x120 */, + HAL_RX_MPDU_LINK_PTR = 289 /* 0x121 */, + HAL_REO_DESTINATION_RING = 290 /* 0x122 */, + HAL_TQM_LIST_GEN_DONE = 291 /* 0x123 */, + HAL_WHO_TERMINATE = 292 /* 0x124 */, + HAL_TX_LAST_MPDU_END = 293 /* 0x125 */, + HAL_TX_CV_DATA = 294 /* 0x126 */, + HAL_TCL_ENTRANCE_FROM_PPE_RING = 295 /* 0x127 */, + HAL_PPDU_TX_END = 296 /* 0x128 */, + HAL_PROT_TX_END = 297 /* 0x129 */, + HAL_PDG_RESPONSE_RATE_SETTING = 298 /* 0x12a */, + HAL_MPDU_INFO_GLOBAL_END = 299 /* 0x12b */, + HAL_TQM_SCH_INSTR_GLOBAL_END = 300 /* 0x12c */, + HAL_RX_PPDU_END_USER_STATS = 301 /* 0x12d */, + HAL_RX_PPDU_END_USER_STATS_EXT = 302 /* 0x12e */, + HAL_NO_ACK_REPORT = 303 /* 0x12f */, + HAL_ACK_REPORT = 304 /* 0x130 */, + HAL_UNIFORM_REO_CMD_HEADER = 305 /* 0x131 */, + HAL_REO_GET_QUEUE_STATS = 306 /* 0x132 */, + HAL_REO_FLUSH_QUEUE = 307 /* 0x133 */, + HAL_REO_FLUSH_CACHE = 308 /* 0x134 */, + HAL_REO_UNBLOCK_CACHE = 309 /* 0x135 */, + HAL_UNIFORM_REO_STATUS_HEADER = 310 /* 0x136 */, + HAL_REO_GET_QUEUE_STATS_STATUS = 311 /* 0x137 */, + HAL_REO_FLUSH_QUEUE_STATUS = 312 /* 0x138 */, + HAL_REO_FLUSH_CACHE_STATUS = 313 /* 0x139 */, + HAL_REO_UNBLOCK_CACHE_STATUS = 314 /* 0x13a */, + HAL_TQM_FLUSH_CACHE = 315 /* 0x13b */, + HAL_TQM_UNBLOCK_CACHE = 316 /* 0x13c */, + HAL_TQM_FLUSH_CACHE_STATUS = 317 /* 0x13d */, + HAL_TQM_UNBLOCK_CACHE_STATUS = 318 /* 0x13e */, + HAL_RX_PPDU_END_STATUS_DONE = 319 /* 0x13f */, + HAL_RX_STATUS_BUFFER_DONE = 320 /* 0x140 */, + HAL_BUFFER_ADDR_INFO = 321 /* 0x141 */, + HAL_RX_MSDU_DESC_INFO = 322 /* 0x142 */, + HAL_RX_MPDU_DESC_INFO = 323 /* 0x143 */, + HAL_TCL_DATA_CMD = 324 /* 0x144 */, + HAL_TCL_GSE_CMD = 325 /* 0x145 */, + HAL_TCL_EXIT_BASE = 326 /* 0x146 */, + HAL_TCL_COMPACT_EXIT_RING = 327 /* 0x147 */, + HAL_TCL_REGULAR_EXIT_RING = 328 /* 0x148 */, + HAL_TCL_EXTENDED_EXIT_RING = 329 /* 0x149 */, + HAL_UPLINK_COMMON_INFO = 330 /* 0x14a */, + HAL_UPLINK_USER_SETUP_INFO = 331 /* 0x14b */, + HAL_TX_DATA_SYNC = 332 /* 0x14c */, + HAL_PHYRX_CBF_READ_REQUEST_ACK = 333 /* 0x14d */, + HAL_TCL_STATUS_RING = 334 /* 0x14e */, + HAL_TQM_GET_MPDU_HEAD_INFO = 335 /* 0x14f */, + HAL_TQM_SYNC_CMD = 336 /* 0x150 */, + HAL_TQM_GET_MPDU_HEAD_INFO_STATUS = 337 /* 0x151 */, + HAL_TQM_SYNC_CMD_STATUS = 338 /* 0x152 */, + HAL_TQM_THRESHOLD_DROP_NOTIFICATION_STATUS = 339 /* 0x153 */, + HAL_TQM_DESCRIPTOR_THRESHOLD_REACHED_STATUS = 340 /* 0x154 */, + HAL_REO_FLUSH_TIMEOUT_LIST = 341 /* 0x155 */, + HAL_REO_FLUSH_TIMEOUT_LIST_STATUS = 342 /* 0x156 */, + HAL_REO_TO_PPE_RING = 343 /* 0x157 */, + HAL_RX_MPDU_INFO = 344 /* 0x158 */, + HAL_REO_DESCRIPTOR_THRESHOLD_REACHED_STATUS = 345 /* 0x159 */, + HAL_SCHEDULER_RX_SIFS_RESPONSE_TRIGGER_STATUS = 346 /* 0x15a */, + HAL_EXAMPLE_USER_TLV_32_NAME = 347 /* 0x15b */, + HAL_RX_PPDU_START_USER_INFO = 348 /* 0x15c */, + HAL_RX_RXPCU_CLASSIFICATION_OVERVIEW = 349 /* 0x15d */, + HAL_RX_RING_MASK = 350 /* 0x15e */, + HAL_WHO_CLASSIFY_INFO = 351 /* 0x15f */, + HAL_TXPT_CLASSIFY_INFO = 352 /* 0x160 */, + HAL_RXPT_CLASSIFY_INFO = 353 /* 0x161 */, + HAL_TX_FLOW_SEARCH_ENTRY = 354 /* 0x162 */, + HAL_RX_FLOW_SEARCH_ENTRY = 355 /* 0x163 */, + HAL_RECEIVED_TRIGGER_INFO_DETAILS = 356 /* 0x164 */, + HAL_COEX_MAC_NAP = 357 /* 0x165 */, + HAL_MACRX_ABORT_REQUEST_INFO = 358 /* 0x166 */, + HAL_MACTX_ABORT_REQUEST_INFO = 359 /* 0x167 */, + HAL_PHYRX_ABORT_REQUEST_INFO = 360 /* 0x168 */, + HAL_PHYTX_ABORT_REQUEST_INFO = 361 /* 0x169 */, + HAL_RXPCU_PPDU_END_INFO = 362 /* 0x16a */, + HAL_WHO_MESH_CONTROL = 363 /* 0x16b */, + HAL_L_SIG_A_INFO = 364 /* 0x16c */, + HAL_L_SIG_B_INFO = 365 /* 0x16d */, + HAL_HT_SIG_INFO = 366 /* 0x16e */, + HAL_VHT_SIG_A_INFO = 367 /* 0x16f */, + HAL_VHT_SIG_B_SU20_INFO = 368 /* 0x170 */, + HAL_VHT_SIG_B_SU40_INFO = 369 /* 0x171 */, + HAL_VHT_SIG_B_SU80_INFO = 370 /* 0x172 */, + HAL_VHT_SIG_B_SU160_INFO = 371 /* 0x173 */, + HAL_VHT_SIG_B_MU20_INFO = 372 /* 0x174 */, + HAL_VHT_SIG_B_MU40_INFO = 373 /* 0x175 */, + HAL_VHT_SIG_B_MU80_INFO = 374 /* 0x176 */, + HAL_VHT_SIG_B_MU160_INFO = 375 /* 0x177 */, + HAL_SERVICE_INFO = 376 /* 0x178 */, + HAL_HE_SIG_A_SU_INFO = 377 /* 0x179 */, + HAL_HE_SIG_A_MU_DL_INFO = 378 /* 0x17a */, + HAL_HE_SIG_A_MU_UL_INFO = 379 /* 0x17b */, + HAL_HE_SIG_B1_MU_INFO = 380 /* 0x17c */, + HAL_HE_SIG_B2_MU_INFO = 381 /* 0x17d */, + HAL_HE_SIG_B2_OFDMA_INFO = 382 /* 0x17e */, + HAL_PDG_SW_MODE_BW_START = 383 /* 0x17f */, + HAL_PDG_SW_MODE_BW_END = 384 /* 0x180 */, + HAL_PDG_WAIT_FOR_MAC_REQUEST = 385 /* 0x181 */, + HAL_PDG_WAIT_FOR_PHY_REQUEST = 386 /* 0x182 */, + HAL_SCHEDULER_END = 387 /* 0x183 */, + HAL_PEER_TABLE_ENTRY = 388 /* 0x184 */, + HAL_SW_PEER_INFO = 389 /* 0x185 */, + HAL_RXOLE_CCE_CLASSIFY_INFO = 390 /* 0x186 */, + HAL_TCL_CCE_CLASSIFY_INFO = 391 /* 0x187 */, + HAL_RXOLE_CCE_INFO = 392 /* 0x188 */, + HAL_TCL_CCE_INFO = 393 /* 0x189 */, + HAL_TCL_CCE_SUPERRULE = 394 /* 0x18a */, + HAL_CCE_RULE = 395 /* 0x18b */, + HAL_RX_PPDU_START_DROPPED = 396 /* 0x18c */, + HAL_RX_PPDU_END_DROPPED = 397 /* 0x18d */, + HAL_RX_PPDU_END_STATUS_DONE_DROPPED = 398 /* 0x18e */, + HAL_RX_MPDU_START_DROPPED = 399 /* 0x18f */, + HAL_RX_MSDU_START_DROPPED = 400 /* 0x190 */, + HAL_RX_MSDU_END_DROPPED = 401 /* 0x191 */, + HAL_RX_MPDU_END_DROPPED = 402 /* 0x192 */, + HAL_RX_ATTENTION_DROPPED = 403 /* 0x193 */, + HAL_TXPCU_USER_SETUP = 404 /* 0x194 */, + HAL_RXPCU_USER_SETUP_EXT = 405 /* 0x195 */, + HAL_CE_SRC_DESC = 406 /* 0x196 */, + HAL_CE_STAT_DESC = 407 /* 0x197 */, + HAL_RXOLE_CCE_SUPERRULE = 408 /* 0x198 */, + HAL_TX_RATE_STATS_INFO = 409 /* 0x199 */, + HAL_CMD_PART_0_END = 410 /* 0x19a */, + HAL_MACTX_SYNTH_ON = 411 /* 0x19b */, + HAL_SCH_CRITICAL_TLV_REFERENCE = 412 /* 0x19c */, + HAL_TQM_MPDU_GLOBAL_START = 413 /* 0x19d */, + HAL_EXAMPLE_TLV_32 = 414 /* 0x19e */, + HAL_TQM_UPDATE_TX_MSDU_FLOW = 415 /* 0x19f */, + HAL_TQM_UPDATE_TX_MPDU_QUEUE_HEAD = 416 /* 0x1a0 */, + HAL_TQM_UPDATE_TX_MSDU_FLOW_STATUS = 417 /* 0x1a1 */, + HAL_TQM_UPDATE_TX_MPDU_QUEUE_HEAD_STATUS = 418 /* 0x1a2 */, + HAL_REO_UPDATE_RX_REO_QUEUE = 419 /* 0x1a3 */, + HAL_CE_DST_DESC = 420 /* 0x1a4 */, + HAL_TLV_BASE = 511 /* 0x1ff */, +}; + +#define HAL_TLV_HDR_TAG GENMASK(9, 1) +#define HAL_TLV_HDR_LEN GENMASK(25, 10) + +#define HAL_TLV_ALIGN 4 + +struct hal_tlv_hdr { + u32 tl; + u8 value[0]; +} __packed; + +#define RX_MPDU_DESC_INFO0_MSDU_COUNT GENMASK(7, 0) +#define RX_MPDU_DESC_INFO0_SEQ_NUM GENMASK(19, 8) +#define RX_MPDU_DESC_INFO0_FRAG_FLAG BIT(20) +#define RX_MPDU_DESC_INFO0_MPDU_RETRY BIT(21) +#define RX_MPDU_DESC_INFO0_AMPDU_FLAG BIT(22) +#define RX_MPDU_DESC_INFO0_BAR_FRAME BIT(23) +#define RX_MPDU_DESC_INFO0_VALID_PN BIT(24) +#define RX_MPDU_DESC_INFO0_VALID_SA BIT(25) +#define RX_MPDU_DESC_INFO0_SA_IDX_TIMEOUT BIT(26) +#define RX_MPDU_DESC_INFO0_VALID_DA BIT(27) +#define RX_MPDU_DESC_INFO0_DA_MCBC BIT(28) +#define RX_MPDU_DESC_INFO0_DA_IDX_TIMEOUT BIT(29) +#define RX_MPDU_DESC_INFO0_RAW_MPDU BIT(30) + +struct rx_mpdu_desc { + u32 info0; /* %RX_MPDU_DESC_INFO */ + u32 meta_data; +} __packed; + +/* rx_mpdu_desc + * Producer: RXDMA + * Consumer: REO/SW/FW + * + * msdu_count + * The number of MSDUs within the MPDU + * + * mpdu_sequence_number + * The field can have two different meanings based on the setting + * of field 'bar_frame'. If 'bar_frame' is set, it means the MPDU + * start sequence number from the BAR frame otherwise it means + * the MPDU sequence number of the received frame. + * + * fragment_flag + * When set, this MPDU is a fragment and REO should forward this + * fragment MPDU to the REO destination ring without any reorder + * checks, pn checks or bitmap update. This implies that REO is + * forwarding the pointer to the MSDU link descriptor. + * + * mpdu_retry_bit + * The retry bit setting from the MPDU header of the received frame + * + * ampdu_flag + * Indicates the MPDU was received as part of an A-MPDU. + * + * bar_frame + * Indicates the received frame is a BAR frame. After processing, + * this frame shall be pushed to SW or deleted. + * + * valid_pn + * When not set, REO will not perform a PN sequence number check. + * + * valid_sa + * Indicates OLE found a valid SA entry for all MSDUs in this MPDU. + * + * sa_idx_timeout + * Indicates, at least 1 MSDU within the MPDU has an unsuccessful + * MAC source address search due to the expiration of search timer. + * + * valid_da + * When set, OLE found a valid DA entry for all MSDUs in this MPDU. + * + * da_mcbc + * Field Only valid if valid_da is set. Indicates at least one of + * the DA addresses is a Multicast or Broadcast address. + * + * da_idx_timeout + * Indicates, at least 1 MSDU within the MPDU has an unsuccessful + * MAC destination address search due to the expiration of search + * timer. + * + * raw_mpdu + * Field only valid when first_msdu_in_mpdu_flag is set. Indicates + * the contents in the MSDU buffer contains a 'RAW' MPDU. + */ + +enum hal_rx_msdu_desc_reo_dest_ind { + HAL_RX_MSDU_DESC_REO_DEST_IND_TCL, + HAL_RX_MSDU_DESC_REO_DEST_IND_SW1, + HAL_RX_MSDU_DESC_REO_DEST_IND_SW2, + HAL_RX_MSDU_DESC_REO_DEST_IND_SW3, + HAL_RX_MSDU_DESC_REO_DEST_IND_SW4, + HAL_RX_MSDU_DESC_REO_DEST_IND_RELEASE, + HAL_RX_MSDU_DESC_REO_DEST_IND_FW, +}; + +#define RX_MSDU_DESC_INFO0_FIRST_MSDU_IN_MPDU BIT(0) +#define RX_MSDU_DESC_INFO0_LAST_MSDU_IN_MPDU BIT(1) +#define RX_MSDU_DESC_INFO0_MSDU_CONTINUATION BIT(2) +#define RX_MSDU_DESC_INFO0_MSDU_LENGTH GENMASK(16, 3) +#define RX_MSDU_DESC_INFO0_REO_DEST_IND GENMASK(21, 17) +#define RX_MSDU_DESC_INFO0_MSDU_DROP BIT(22) +#define RX_MSDU_DESC_INFO0_VALID_SA BIT(23) +#define RX_MSDU_DESC_INFO0_SA_IDX_TIMEOUT BIT(24) +#define RX_MSDU_DESC_INFO0_VALID_DA BIT(25) +#define RX_MSDU_DESC_INFO0_DA_MCBC BIT(26) +#define RX_MSDU_DESC_INFO0_DA_IDX_TIMEOUT BIT(27) + +#define HAL_RX_MSDU_PKT_LENGTH_GET(val) \ + (FIELD_GET(RX_MSDU_DESC_INFO0_MSDU_LENGTH, (val))) + +struct rx_msdu_desc { + u32 info0; + u32 rsvd0; +} __packed; + +/* rx_msdu_desc + * + * first_msdu_in_mpdu + * Indicates first msdu in mpdu. + * + * last_msdu_in_mpdu + * Indicates last msdu in mpdu. This flag can be true only when + * 'Msdu_continuation' set to 0. This implies that when an msdu + * is spread out over multiple buffers and thus msdu_continuation + * is set, only for the very last buffer of the msdu, can the + * 'last_msdu_in_mpdu' be set. + * + * When both first_msdu_in_mpdu and last_msdu_in_mpdu are set, + * the MPDU that this MSDU belongs to only contains a single MSDU. + * + * msdu_continuation + * When set, this MSDU buffer was not able to hold the entire MSDU. + * The next buffer will therefor contain additional information + * related to this MSDU. + * + * msdu_length + * Field is only valid in combination with the 'first_msdu_in_mpdu' + * being set. Full MSDU length in bytes after decapsulation. This + * field is still valid for MPDU frames without A-MSDU. It still + * represents MSDU length after decapsulation Or in case of RAW + * MPDUs, it indicates the length of the entire MPDU (without FCS + * field). + * + * reo_destination_indication + * The id of the reo exit ring where the msdu frame shall push + * after (MPDU level) reordering has finished. Values are defined + * in enum %HAL_RX_MSDU_DESC_REO_DEST_IND_. + * + * msdu_drop + * Indicates that REO shall drop this MSDU and not forward it to + * any other ring. + * + * valid_sa + * Indicates OLE found a valid SA entry for this MSDU. + * + * sa_idx_timeout + * Indicates, an unsuccessful MAC source address search due to + * the expiration of search timer for this MSDU. + * + * valid_da + * When set, OLE found a valid DA entry for this MSDU. + * + * da_mcbc + * Field Only valid if valid_da is set. Indicates the DA address + * is a Multicast or Broadcast address for this MSDU. + * + * da_idx_timeout + * Indicates, an unsuccessful MAC destination address search due + * to the expiration of search timer fot this MSDU. + */ + +enum hal_reo_dest_ring_buffer_type { + HAL_REO_DEST_RING_BUFFER_TYPE_MSDU, + HAL_REO_DEST_RING_BUFFER_TYPE_LINK_DESC, +}; + +enum hal_reo_dest_ring_push_reason { + HAL_REO_DEST_RING_PUSH_REASON_ERR_DETECTED, + HAL_REO_DEST_RING_PUSH_REASON_ROUTING_INSTRUCTION, +}; + +enum hal_reo_dest_ring_error_code { + HAL_REO_DEST_RING_ERROR_CODE_DESC_ADDR_ZERO, + HAL_REO_DEST_RING_ERROR_CODE_DESC_INVALID, + HAL_REO_DEST_RING_ERROR_CODE_AMPDU_IN_NON_BA, + HAL_REO_DEST_RING_ERROR_CODE_NON_BA_DUPLICATE, + HAL_REO_DEST_RING_ERROR_CODE_BA_DUPLICATE, + HAL_REO_DEST_RING_ERROR_CODE_FRAME_2K_JUMP, + HAL_REO_DEST_RING_ERROR_CODE_BAR_2K_JUMP, + HAL_REO_DEST_RING_ERROR_CODE_FRAME_OOR, + HAL_REO_DEST_RING_ERROR_CODE_BAR_OOR, + HAL_REO_DEST_RING_ERROR_CODE_NO_BA_SESSION, + HAL_REO_DEST_RING_ERROR_CODE_FRAME_SN_EQUALS_SSN, + HAL_REO_DEST_RING_ERROR_CODE_PN_CHECK_FAILED, + HAL_REO_DEST_RING_ERROR_CODE_2K_ERR_FLAG_SET, + HAL_REO_DEST_RING_ERROR_CODE_PN_ERR_FLAG_SET, + HAL_REO_DEST_RING_ERROR_CODE_DESC_BLOCKED, + HAL_REO_DEST_RING_ERROR_CODE_MAX, +}; + +#define HAL_REO_DEST_RING_INFO0_QUEUE_ADDR_HI GENMASK(7, 0) +#define HAL_REO_DEST_RING_INFO0_BUFFER_TYPE BIT(8) +#define HAL_REO_DEST_RING_INFO0_PUSH_REASON GENMASK(10, 9) +#define HAL_REO_DEST_RING_INFO0_ERROR_CODE GENMASK(15, 11) +#define HAL_REO_DEST_RING_INFO0_RX_QUEUE_NUM GENMASK(31, 16) + +#define HAL_REO_DEST_RING_INFO1_REORDER_INFO_VALID BIT(0) +#define HAL_REO_DEST_RING_INFO1_REORDER_OPCODE GENMASK(4, 1) +#define HAL_REO_DEST_RING_INFO1_REORDER_SLOT_IDX GENMASK(12, 5) + +#define HAL_REO_DEST_RING_INFO2_RING_ID GENMASK(27, 20) +#define HAL_REO_DEST_RING_INFO2_LOOPING_COUNT GENMASK(31, 28) + +struct hal_reo_dest_ring { + struct ath11k_buffer_addr buf_addr_info; + struct rx_mpdu_desc rx_mpdu_info; + struct rx_msdu_desc rx_msdu_info; + u32 queue_addr_lo; + u32 info0; /* %HAL_REO_DEST_RING_INFO0_ */ + u32 info1; /* %HAL_REO_DEST_RING_INFO1_ */ + u32 rsvd0; + u32 rsvd1; + u32 rsvd2; + u32 rsvd3; + u32 rsvd4; + u32 rsvd5; + u32 info2; /* %HAL_REO_DEST_RING_INFO2_ */ +} __packed; + +/* hal_reo_dest_ring + * + * Producer: RXDMA + * Consumer: REO/SW/FW + * + * buf_addr_info + * Details of the physical address of a buffer or MSDU + * link descriptor. + * + * rx_mpdu_info + * General information related to the MPDU that is passed + * on from REO entrance ring to the REO destination ring. + * + * rx_msdu_info + * General information related to the MSDU that is passed + * on from RXDMA all the way to to the REO destination ring. + * + * queue_addr_lo + * Address (lower 32 bits) of the REO queue descriptor. + * + * queue_addr_hi + * Address (upper 8 bits) of the REO queue descriptor. + * + * buffer_type + * Indicates the type of address provided in the buf_addr_info. + * Values are defined in enum %HAL_REO_DEST_RING_BUFFER_TYPE_. + * + * push_reason + * Reason for pushing this frame to this exit ring. Values are + * defined in enum %HAL_REO_DEST_RING_PUSH_REASON_. + * + * error_code + * Valid only when 'push_reason' is set. All error codes are + * defined in enum %HAL_REO_DEST_RING_ERROR_CODE_. + * + * rx_queue_num + * Indicates the REO MPDU reorder queue id from which this frame + * originated. + * + * reorder_info_valid + * When set, REO has been instructed to not perform the actual + * re-ordering of frames for this queue, but just to insert + * the reorder opcodes. + * + * reorder_opcode + * Field is valid when 'reorder_info_valid' is set. This field is + * always valid for debug purpose as well. + * + * reorder_slot_idx + * Valid only when 'reorder_info_valid' is set. + * + * ring_id + * The buffer pointer ring id. + * 0 - Idle ring + * 1 - N refers to other rings. + * + * looping_count + * Indicates the number of times the producer of entries into + * this ring has looped around the ring. + */ + +enum hal_reo_entr_rxdma_ecode { + HAL_REO_ENTR_RING_RXDMA_ECODE_OVERFLOW_ERR, + HAL_REO_ENTR_RING_RXDMA_ECODE_MPDU_LEN_ERR, + HAL_REO_ENTR_RING_RXDMA_ECODE_FCS_ERR, + HAL_REO_ENTR_RING_RXDMA_ECODE_DECRYPT_ERR, + HAL_REO_ENTR_RING_RXDMA_ECODE_TKIP_MIC_ERR, + HAL_REO_ENTR_RING_RXDMA_ECODE_UNECRYPTED_ERR, + HAL_REO_ENTR_RING_RXDMA_ECODE_MSDU_LEN_ERR, + HAL_REO_ENTR_RING_RXDMA_ECODE_MSDU_LIMIT_ERR, + HAL_REO_ENTR_RING_RXDMA_ECODE_WIFI_PARSE_ERR, + HAL_REO_ENTR_RING_RXDMA_ECODE_AMSDU_PARSE_ERR, + HAL_REO_ENTR_RING_RXDMA_ECODE_SA_TIMEOUT_ERR, + HAL_REO_ENTR_RING_RXDMA_ECODE_DA_TIMEOUT_ERR, + HAL_REO_ENTR_RING_RXDMA_ECODE_FLOW_TIMEOUT_ERR, + HAL_REO_ENTR_RING_RXDMA_ECODE_FLUSH_REQUEST_ERR, + HAL_REO_ENTR_RING_RXDMA_ECODE_MAX, +}; + +#define HAL_REO_ENTR_RING_INFO0_QUEUE_ADDR_HI GENMASK(7, 0) +#define HAL_REO_ENTR_RING_INFO0_MPDU_BYTE_COUNT GENMASK(21, 8) +#define HAL_REO_ENTR_RING_INFO0_DEST_IND GENMASK(26, 22) +#define HAL_REO_ENTR_RING_INFO0_FRAMELESS_BAR BIT(27) + +#define HAL_REO_ENTR_RING_INFO1_RXDMA_PUSH_REASON GENMASK(1, 0) +#define HAL_REO_ENTR_RING_INFO1_RXDMA_ERROR_CODE GENMASK(6, 2) + +struct hal_reo_entrance_ring { + struct ath11k_buffer_addr buf_addr_info; + struct rx_mpdu_desc rx_mpdu_info; + u32 queue_addr_lo; + u32 info0; /* %HAL_REO_ENTR_RING_INFO0_ */ + u32 info1; /* %HAL_REO_ENTR_RING_INFO1_ */ + u32 info2; /* %HAL_REO_DEST_RING_INFO2_ */ + +} __packed; + +/* hal_reo_entrance_ring + * + * Producer: RXDMA + * Consumer: REO + * + * buf_addr_info + * Details of the physical address of a buffer or MSDU + * link descriptor. + * + * rx_mpdu_info + * General information related to the MPDU that is passed + * on from REO entrance ring to the REO destination ring. + * + * queue_addr_lo + * Address (lower 32 bits) of the REO queue descriptor. + * + * queue_addr_hi + * Address (upper 8 bits) of the REO queue descriptor. + * + * mpdu_byte_count + * An approximation of the number of bytes received in this MPDU. + * Used to keeps stats on the amount of data flowing + * through a queue. + * + * reo_destination_indication + * The id of the reo exit ring where the msdu frame shall push + * after (MPDU level) reordering has finished. Values are defined + * in enum %HAL_RX_MSDU_DESC_REO_DEST_IND_. + * + * frameless_bar + * Indicates that this REO entrance ring struct contains BAR info + * from a multi TID BAR frame. The original multi TID BAR frame + * itself contained all the REO info for the first TID, but all + * the subsequent TID info and their linkage to the REO descriptors + * is passed down as 'frameless' BAR info. + * + * The only fields valid in this descriptor when this bit is set + * are queue_addr_lo, queue_addr_hi, mpdu_sequence_number, + * bar_frame and peer_meta_data. + * + * rxdma_push_reason + * Reason for pushing this frame to this exit ring. Values are + * defined in enum %HAL_REO_DEST_RING_PUSH_REASON_. + * + * rxdma_error_code + * Valid only when 'push_reason' is set. All error codes are + * defined in enum %HAL_REO_ENTR_RING_RXDMA_ECODE_. + * + * ring_id + * The buffer pointer ring id. + * 0 - Idle ring + * 1 - N refers to other rings. + * + * looping_count + * Indicates the number of times the producer of entries into + * this ring has looped around the ring. + */ + +#define HAL_REO_CMD_HDR_INFO0_CMD_NUMBER GENMASK(15, 0) +#define HAL_REO_CMD_HDR_INFO0_STATUS_REQUIRED BIT(16) + +struct hal_reo_cmd_hdr { + u32 info0; +} __packed; + +#define HAL_REO_GET_QUEUE_STATS_INFO0_QUEUE_ADDR_HI GENMASK(7, 0) +#define HAL_REO_GET_QUEUE_STATS_INFO0_CLEAR_STATS BIT(8) + +struct hal_reo_get_queue_stats { + struct hal_reo_cmd_hdr cmd; + u32 queue_addr_lo; + u32 info0; + u32 rsvd0[6]; +} __packed; + +/* hal_reo_get_queue_stats + * Producer: SW + * Consumer: REO + * + * cmd + * Details for command execution tracking purposes. + * + * queue_addr_lo + * Address (lower 32 bits) of the REO queue descriptor. + * + * queue_addr_hi + * Address (upper 8 bits) of the REO queue descriptor. + * + * clear_stats + * Clear stats settings. When set, Clear the stats after + * generating the status. + * + * Following stats will be cleared. + * Timeout_count + * Forward_due_to_bar_count + * Duplicate_count + * Frames_in_order_count + * BAR_received_count + * MPDU_Frames_processed_count + * MSDU_Frames_processed_count + * Total_processed_byte_count + * Late_receive_MPDU_count + * window_jump_2k + * Hole_count + */ + +#define HAL_REO_FLUSH_QUEUE_INFO0_DESC_ADDR_HI GENMASK(7, 0) +#define HAL_REO_FLUSH_QUEUE_INFO0_BLOCK_DESC_ADDR BIT(8) +#define HAL_REO_FLUSH_QUEUE_INFO0_BLOCK_RESRC_IDX GENMASK(10, 9) + +struct hal_reo_flush_queue { + struct hal_reo_cmd_hdr cmd; + u32 desc_addr_lo; + u32 info0; + u32 rsvd0[6]; +} __packed; + +#define HAL_REO_FLUSH_CACHE_INFO0_CACHE_ADDR_HI GENMASK(7, 0) +#define HAL_REO_FLUSH_CACHE_INFO0_FWD_ALL_MPDUS BIT(8) +#define HAL_REO_FLUSH_CACHE_INFO0_RELEASE_BLOCK_IDX BIT(9) +#define HAL_REO_FLUSH_CACHE_INFO0_BLOCK_RESRC_IDX GENMASK(11, 10) +#define HAL_REO_FLUSH_CACHE_INFO0_FLUSH_WO_INVALIDATE BIT(12) +#define HAL_REO_FLUSH_CACHE_INFO0_BLOCK_CACHE_USAGE BIT(13) +#define HAL_REO_FLUSH_CACHE_INFO0_FLUSH_ALL BIT(14) + +struct hal_reo_flush_cache { + struct hal_reo_cmd_hdr cmd; + u32 cache_addr_lo; + u32 info0; + u32 rsvd0[6]; +} __packed; + +#define HAL_TCL_DATA_CMD_INFO0_DESC_TYPE BIT(0) +#define HAL_TCL_DATA_CMD_INFO0_EPD BIT(1) +#define HAL_TCL_DATA_CMD_INFO0_ENCAP_TYPE GENMASK(3, 2) +#define HAL_TCL_DATA_CMD_INFO0_ENCRYPT_TYPE GENMASK(7, 4) +#define HAL_TCL_DATA_CMD_INFO0_SRC_BUF_SWAP BIT(8) +#define HAL_TCL_DATA_CMD_INFO0_LNK_META_SWAP BIT(9) +#define HAL_TCL_DATA_CMD_INFO0_SEARCH_TYPE BIT(12) +#define HAL_TCL_DATA_CMD_INFO0_ADDRX_EN BIT(14) +#define HAL_TCL_DATA_CMD_INFO0_ADDRY_EN BIT(15) +#define HAL_TCL_DATA_CMD_INFO0_CMD_NUM GENMASK(31, 16) + +#define HAL_TCL_DATA_CMD_INFO1_DATA_LEN GENMASK(15, 0) +#define HAL_TCL_DATA_CMD_INFO1_IP4_CKSUM_EN BIT(16) +#define HAL_TCL_DATA_CMD_INFO1_UDP4_CKSUM_EN BIT(17) +#define HAL_TCL_DATA_CMD_INFO1_UDP6_CKSUM_EN BIT(18) +#define HAL_TCL_DATA_CMD_INFO1_TCP4_CKSUM_EN BIT(19) +#define HAL_TCL_DATA_CMD_INFO1_TCP6_CKSUM_EN BIT(20) +#define HAL_TCL_DATA_CMD_INFO1_TO_FW BIT(21) +#define HAL_TCL_DATA_CMD_INFO1_PKT_OFFSET GENMASK(31, 23) + +#define HAL_TCL_DATA_CMD_INFO2_BUF_TIMESTAMP GENMASK(18, 0) +#define HAL_TCL_DATA_CMD_INFO2_BUF_T_VALID BIT(19) +#define HAL_TCL_DATA_CMD_INFO2_MESH_ENABLE BIT(20) +#define HAL_TCL_DATA_CMD_INFO2_TID_OVERWRITE BIT(21) +#define HAL_TCL_DATA_CMD_INFO2_TID GENMASK(25, 22) +#define HAL_TCL_DATA_CMD_INFO2_LMAC_ID GENMASK(27, 26) + +#define HAL_TCL_DATA_CMD_INFO3_DSCP_TID_TABLE_IDX GENMASK(5, 0) +#define HAL_TCL_DATA_CMD_INFO3_SEARCH_INDEX GENMASK(25, 6) +#define HAL_TCL_DATA_CMD_INFO3_CACHE_SET_NUM GENMASK(29, 26) + +#define HAL_TCL_DATA_CMD_INFO4_RING_ID GENMASK(27, 20) +#define HAL_TCL_DATA_CMD_INFO4_LOOPING_COUNT GENMASK(31, 28) + +enum hal_encrypt_type { + HAL_ENCRYPT_TYPE_WEP_40, + HAL_ENCRYPT_TYPE_WEP_104, + HAL_ENCRYPT_TYPE_TKIP_NO_MIC, + HAL_ENCRYPT_TYPE_WEP_128, + HAL_ENCRYPT_TYPE_TKIP_MIC, + HAL_ENCRYPT_TYPE_WAPI, + HAL_ENCRYPT_TYPE_CCMP_128, + HAL_ENCRYPT_TYPE_OPEN, + HAL_ENCRYPT_TYPE_CCMP_256, + HAL_ENCRYPT_TYPE_GCMP_128, + HAL_ENCRYPT_TYPE_AES_GCMP_256, + HAL_ENCRYPT_TYPE_WAPI_GCM_SM4, +}; + +enum hal_tcl_encap_type { + HAL_TCL_ENCAP_TYPE_RAW, + HAL_TCL_ENCAP_TYPE_NATIVE_WIFI, + HAL_TCL_ENCAP_TYPE_ETHERNET, + HAL_TCL_ENCAP_TYPE_802_3 = 3, +}; + +enum hal_tcl_desc_type { + HAL_TCL_DESC_TYPE_BUFFER, + HAL_TCL_DESC_TYPE_EXT_DESC, +}; + +enum hal_wbm_htt_tx_comp_status { + HAL_WBM_REL_HTT_TX_COMP_STATUS_OK, + HAL_WBM_REL_HTT_TX_COMP_STATUS_DROP, + HAL_WBM_REL_HTT_TX_COMP_STATUS_TTL, + HAL_WBM_REL_HTT_TX_COMP_STATUS_REINJ, + HAL_WBM_REL_HTT_TX_COMP_STATUS_INSPECT, + HAL_WBM_REL_HTT_TX_COMP_STATUS_MEC_NOTIFY, +}; + +struct hal_tcl_data_cmd { + struct ath11k_buffer_addr buf_addr_info; + u32 info0; + u32 info1; + u32 info2; + u32 info3; + u32 info4; +} __packed; + +/* hal_tcl_data_cmd + * + * buf_addr_info + * Details of the physical address of a buffer or MSDU + * link descriptor. + * + * desc_type + * Indicates the type of address provided in the buf_addr_info. + * Values are defined in enum %HAL_REO_DEST_RING_BUFFER_TYPE_. + * + * epd + * When this bit is set then input packet is an EPD type. + * + * encap_type + * Indicates the encapsulation that HW will perform. Values are + * defined in enum %HAL_TCL_ENCAP_TYPE_. + * + * encrypt_type + * Field only valid for encap_type: RAW + * Values are defined in enum %HAL_ENCRYPT_TYPE_. + * + * src_buffer_swap + * Treats source memory (packet buffer) organization as big-endian. + * 1'b0: Source memory is little endian + * 1'b1: Source memory is big endian + * + * link_meta_swap + * Treats link descriptor and Metadata as big-endian. + * 1'b0: memory is little endian + * 1'b1: memory is big endian + * + * search_type + * Search type select + * 0 - Normal search, 1 - Index based address search, + * 2 - Index based flow search + * + * addrx_en + * addry_en + * Address X/Y search enable in ASE correspondingly. + * 1'b0: Search disable + * 1'b1: Search Enable + * + * cmd_num + * This number can be used to match against status. + * + * data_length + * MSDU length in case of direct descriptor. Length of link + * extension descriptor in case of Link extension descriptor. + * + * *_checksum_en + * Enable checksum replacement for ipv4, udp_over_ipv4, ipv6, + * udp_over_ipv6, tcp_over_ipv4 and tcp_over_ipv6. + * + * to_fw + * Forward packet to FW along with classification result. The + * packet will not be forward to TQM when this bit is set. + * 1'b0: Use classification result to forward the packet. + * 1'b1: Override classification result & forward packet only to fw + * + * packet_offset + * Packet offset from Metadata in case of direct buffer descriptor. + * + * buffer_timestamp + * buffer_timestamp_valid + * Frame system entrance timestamp. It shall be filled by first + * module (SW, TCL or TQM) that sees the frames first. + * + * mesh_enable + * For raw WiFi frames, this indicates transmission to a mesh STA, + * enabling the interpretation of the 'Mesh Control Present' bit + * (bit 8) of QoS Control. + * For native WiFi frames, this indicates that a 'Mesh Control' + * field is present between the header and the LLC. + * + * hlos_tid_overwrite + * + * When set, TCL shall ignore the IP DSCP and VLAN PCP + * fields and use HLOS_TID as the final TID. Otherwise TCL + * shall consider the DSCP and PCP fields as well as HLOS_TID + * and choose a final TID based on the configured priority + * + * hlos_tid + * HLOS MSDU priority + * Field is used when HLOS_TID_overwrite is set. + * + * lmac_id + * TCL uses this LMAC_ID in address search, i.e, while + * finding matching entry for the packet in AST corresponding + * to given LMAC_ID + * + * If LMAC ID is all 1s (=> value 3), it indicates wildcard + * match for any MAC + * + * dscp_tid_table_num + * DSCP to TID mapping table number that need to be used + * for the MSDU. + * + * search_index + * The index that will be used for index based address or + * flow search. The field is valid when 'search_type' is 1 or 2. + * + * cache_set_num + * + * Cache set number that should be used to cache the index + * based search results, for address and flow search. This + * value should be equal to LSB four bits of the hash value of + * match data, in case of search index points to an entry which + * may be used in content based search also. The value can be + * anything when the entry pointed by search index will not be + * used for content based search. + * + * ring_id + * The buffer pointer ring ID. + * 0 refers to the IDLE ring + * 1 - N refers to other rings + * + * looping_count + * + * A count value that indicates the number of times the + * producer of entries into the Ring has looped around the + * ring. + * + * At initialization time, this value is set to 0. On the + * first loop, this value is set to 1. After the max value is + * reached allowed by the number of bits for this field, the + * count value continues with 0 again. + * + * In case SW is the consumer of the ring entries, it can + * use this field to figure out up to where the producer of + * entries has created new entries. This eliminates the need to + * check where the head pointer' of the ring is located once + * the SW starts processing an interrupt indicating that new + * entries have been put into this ring... + * + * Also note that SW if it wants only needs to look at the + * LSB bit of this count value. + */ + +#define HAL_TCL_DESC_LEN sizeof(struct hal_tcl_data_cmd) + +enum hal_tcl_gse_ctrl { + HAL_TCL_GSE_CTRL_RD_STAT, + HAL_TCL_GSE_CTRL_SRCH_DIS, + HAL_TCL_GSE_CTRL_WR_BK_SINGLE, + HAL_TCL_GSE_CTRL_WR_BK_ALL, + HAL_TCL_GSE_CTRL_INVAL_SINGLE, + HAL_TCL_GSE_CTRL_INVAL_ALL, + HAL_TCL_GSE_CTRL_WR_BK_INVAL_SINGLE, + HAL_TCL_GSE_CTRL_WR_BK_INVAL_ALL, + HAL_TCL_GSE_CTRL_CLR_STAT_SINGLE, +}; + +/* hal_tcl_gse_ctrl + * + * rd_stat + * Report or Read statistics + * srch_dis + * Search disable. Report only Hash. + * wr_bk_single + * Write Back single entry + * wr_bk_all + * Write Back entire cache entry + * inval_single + * Invalidate single cache entry + * inval_all + * Invalidate entire cache + * wr_bk_inval_single + * Write back and invalidate single entry in cache + * wr_bk_inval_all + * Write back and invalidate entire cache + * clr_stat_single + * Clear statistics for single entry + */ + +#define HAL_TCL_GSE_CMD_INFO0_CTRL_BUF_ADDR_HI GENMASK(7, 0) +#define HAL_TCL_GSE_CMD_INFO0_GSE_CTRL GENMASK(11, 8) +#define HAL_TCL_GSE_CMD_INFO0_GSE_SEL BIT(12) +#define HAL_TCL_GSE_CMD_INFO0_STATUS_DEST_RING_ID BIT(13) +#define HAL_TCL_GSE_CMD_INFO0_SWAP BIT(14) + +#define HAL_TCL_GSE_CMD_INFO1_RING_ID GENMASK(27, 20) +#define HAL_TCL_GSE_CMD_INFO1_LOOPING_COUNT GENMASK(31, 28) + +struct hal_tcl_gse_cmd { + u32 ctrl_buf_addr_lo; + u32 info0; + u32 meta_data[2]; + u32 rsvd0[2]; + u32 info1; +} __packed; + +/* hal_tcl_gse_cmd + * + * ctrl_buf_addr_lo, ctrl_buf_addr_hi + * Address of a control buffer containing additional info needed + * for this command execution. + * + * gse_ctrl + * GSE control operations. This includes cache operations and table + * entry statistics read/clear operation. Values are defined in + * enum %HAL_TCL_GSE_CTRL. + * + * gse_sel + * To select the ASE/FSE to do the operation mention by GSE_ctrl. + * 0: FSE select 1: ASE select + * + * status_destination_ring_id + * TCL status ring to which the GSE status needs to be send. + * + * swap + * Bit to enable byte swapping of contents of buffer. + * + * meta_data + * Meta data to be returned in the status descriptor + */ + +enum hal_tcl_cache_op_res { + HAL_TCL_CACHE_OP_RES_DONE, + HAL_TCL_CACHE_OP_RES_NOT_FOUND, + HAL_TCL_CACHE_OP_RES_TIMEOUT, +}; + +#define HAL_TCL_STATUS_RING_INFO0_GSE_CTRL GENMASK(3, 0) +#define HAL_TCL_STATUS_RING_INFO0_GSE_SEL BIT(4) +#define HAL_TCL_STATUS_RING_INFO0_CACHE_OP_RES GENMASK(6, 5) +#define HAL_TCL_STATUS_RING_INFO0_MSDU_CNT GENMASK(31, 8) + +#define HAL_TCL_STATUS_RING_INFO1_HASH_IDX GENMASK(19, 0) + +#define HAL_TCL_STATUS_RING_INFO2_RING_ID GENMASK(27, 20) +#define HAL_TCL_STATUS_RING_INFO2_LOOPING_COUNT GENMASK(31, 28) + +struct hal_tcl_status_ring { + u32 info0; + u32 msdu_byte_count; + u32 msdu_timestamp; + u32 meta_data[2]; + u32 info1; + u32 rsvd0; + u32 info2; +} __packed; + +/* hal_tcl_status_ring + * + * gse_ctrl + * GSE control operations. This includes cache operations and table + * entry statistics read/clear operation. Values are defined in + * enum %HAL_TCL_GSE_CTRL. + * + * gse_sel + * To select the ASE/FSE to do the operation mention by GSE_ctrl. + * 0: FSE select 1: ASE select + * + * cache_op_res + * Cache operation result. Values are defined in enum + * %HAL_TCL_CACHE_OP_RES_. + * + * msdu_cnt + * msdu_byte_count + * MSDU count of Entry and MSDU byte count for entry 1. + * + * hash_indx + * Hash value of the entry in case of search failed or disabled. + */ + +#define HAL_CE_SRC_DESC_ADDR_INFO_ADDR_HI GENMASK(7, 0) +#define HAL_CE_SRC_DESC_ADDR_INFO_HASH_EN BIT(8) +#define HAL_CE_SRC_DESC_ADDR_INFO_BYTE_SWAP BIT(9) +#define HAL_CE_SRC_DESC_ADDR_INFO_DEST_SWAP BIT(10) +#define HAL_CE_SRC_DESC_ADDR_INFO_GATHER BIT(11) +#define HAL_CE_SRC_DESC_ADDR_INFO_LEN GENMASK(31, 16) + +#define HAL_CE_SRC_DESC_META_INFO_DATA GENMASK(15, 0) + +#define HAL_CE_SRC_DESC_FLAGS_RING_ID GENMASK(27, 20) +#define HAL_CE_SRC_DESC_FLAGS_LOOP_CNT HAL_SRNG_DESC_LOOP_CNT + +struct hal_ce_srng_src_desc { + u32 buffer_addr_low; + u32 buffer_addr_info; /* %HAL_CE_SRC_DESC_ADDR_INFO_ */ + u32 meta_info; /* %HAL_CE_SRC_DESC_META_INFO_ */ + u32 flags; /* %HAL_CE_SRC_DESC_FLAGS_ */ +} __packed; + +/* + * hal_ce_srng_src_desc + * + * buffer_addr_lo + * LSB 32 bits of the 40 Bit Pointer to the source buffer + * + * buffer_addr_hi + * MSB 8 bits of the 40 Bit Pointer to the source buffer + * + * toeplitz_en + * Enable generation of 32-bit Toeplitz-LFSR hash for + * data transfer. In case of gather field in first source + * ring entry of the gather copy cycle in taken into account. + * + * src_swap + * Treats source memory organization as big-endian. For + * each dword read (4 bytes), the byte 0 is swapped with byte 3 + * and byte 1 is swapped with byte 2. + * In case of gather field in first source ring entry of + * the gather copy cycle in taken into account. + * + * dest_swap + * Treats destination memory organization as big-endian. + * For each dword write (4 bytes), the byte 0 is swapped with + * byte 3 and byte 1 is swapped with byte 2. + * In case of gather field in first source ring entry of + * the gather copy cycle in taken into account. + * + * gather + * Enables gather of multiple copy engine source + * descriptors to one destination. + * + * ce_res_0 + * Reserved + * + * + * length + * Length of the buffer in units of octets of the current + * descriptor + * + * fw_metadata + * Meta data used by FW. + * In case of gather field in first source ring entry of + * the gather copy cycle in taken into account. + * + * ce_res_1 + * Reserved + * + * ce_res_2 + * Reserved + * + * ring_id + * The buffer pointer ring ID. + * 0 refers to the IDLE ring + * 1 - N refers to other rings + * Helps with debugging when dumping ring contents. + * + * looping_count + * A count value that indicates the number of times the + * producer of entries into the Ring has looped around the + * ring. + * + * At initialization time, this value is set to 0. On the + * first loop, this value is set to 1. After the max value is + * reached allowed by the number of bits for this field, the + * count value continues with 0 again. + * + * In case SW is the consumer of the ring entries, it can + * use this field to figure out up to where the producer of + * entries has created new entries. This eliminates the need to + * check where the head pointer' of the ring is located once + * the SW starts processing an interrupt indicating that new + * entries have been put into this ring... + * + * Also note that SW if it wants only needs to look at the + * LSB bit of this count value. + */ + +#define HAL_CE_DEST_DESC_ADDR_INFO_ADDR_HI GENMASK(7, 0) +#define HAL_CE_DEST_DESC_ADDR_INFO_RING_ID GENMASK(27, 20) +#define HAL_CE_DEST_DESC_ADDR_INFO_LOOP_CNT HAL_SRNG_DESC_LOOP_CNT + +struct hal_ce_srng_dest_desc { + u32 buffer_addr_low; + u32 buffer_addr_info; /* %HAL_CE_DEST_DESC_ADDR_INFO_ */ +} __packed; + +/* hal_ce_srng_dest_desc + * + * dst_buffer_low + * LSB 32 bits of the 40 Bit Pointer to the Destination + * buffer + * + * dst_buffer_high + * MSB 8 bits of the 40 Bit Pointer to the Destination + * buffer + * + * ce_res_4 + * Reserved + * + * ring_id + * The buffer pointer ring ID. + * 0 refers to the IDLE ring + * 1 - N refers to other rings + * Helps with debugging when dumping ring contents. + * + * looping_count + * A count value that indicates the number of times the + * producer of entries into the Ring has looped around the + * ring. + * + * At initialization time, this value is set to 0. On the + * first loop, this value is set to 1. After the max value is + * reached allowed by the number of bits for this field, the + * count value continues with 0 again. + * + * In case SW is the consumer of the ring entries, it can + * use this field to figure out up to where the producer of + * entries has created new entries. This eliminates the need to + * check where the head pointer' of the ring is located once + * the SW starts processing an interrupt indicating that new + * entries have been put into this ring... + * + * Also note that SW if it wants only needs to look at the + * LSB bit of this count value. + */ + +#define HAL_CE_DST_STATUS_DESC_FLAGS_HASH_EN BIT(8) +#define HAL_CE_DST_STATUS_DESC_FLAGS_BYTE_SWAP BIT(9) +#define HAL_CE_DST_STATUS_DESC_FLAGS_DEST_SWAP BIT(10) +#define HAL_CE_DST_STATUS_DESC_FLAGS_GATHER BIT(11) +#define HAL_CE_DST_STATUS_DESC_FLAGS_LEN GENMASK(31, 16) + +#define HAL_CE_DST_STATUS_DESC_META_INFO_DATA GENMASK(7, 0) +#define HAL_CE_DST_STATUS_DESC_META_INFO_RING_ID GENMASK(27, 20) +#define HAL_CE_DST_STATUS_DESC_META_INFO_LOOP_CNT HAL_SRNG_DESC_LOOP_CNT + +struct hal_ce_srng_dst_status_desc { + u32 flags; /* %HAL_CE_DST_STATUS_DESC_FLAGS_ */ + u32 toeplitz_hash0; + u32 toeplitz_hash1; + u32 meta_info; /* HAL_CE_DST_STATUS_DESC_META_INFO_ */ +} __packed; + +/* hal_ce_srng_dst_status_desc + * + * ce_res_5 + * Reserved + * + * toeplitz_en + * + * src_swap + * Source memory buffer swapped + * + * dest_swap + * Destination memory buffer swapped + * + * gather + * Gather of multiple copy engine source descriptors to one + * destination enabled + * + * ce_res_6 + * Reserved + * + * length + * Sum of all the Lengths of the source descriptor in the + * gather chain + * + * toeplitz_hash_0 + * 32 LS bits of 64 bit Toeplitz LFSR hash result + * + * toeplitz_hash_1 + * 32 MS bits of 64 bit Toeplitz LFSR hash result + * + * fw_metadata + * Meta data used by FW + * In case of gather field in first source ring entry of + * the gather copy cycle in taken into account. + * + * ce_res_7 + * Reserved + * + * ring_id + * The buffer pointer ring ID. + * 0 refers to the IDLE ring + * 1 - N refers to other rings + * Helps with debugging when dumping ring contents. + * + * looping_count + * A count value that indicates the number of times the + * producer of entries into the Ring has looped around the + * ring. + * + * At initialization time, this value is set to 0. On the + * first loop, this value is set to 1. After the max value is + * reached allowed by the number of bits for this field, the + * count value continues with 0 again. + * + * In case SW is the consumer of the ring entries, it can + * use this field to figure out up to where the producer of + * entries has created new entries. This eliminates the need to + * check where the head pointer' of the ring is located once + * the SW starts processing an interrupt indicating that new + * entries have been put into this ring... + * + * Also note that SW if it wants only needs to look at the + * LSB bit of this count value. + */ + +#define HAL_TX_RATE_STATS_INFO0_VALID BIT(0) +#define HAL_TX_RATE_STATS_INFO0_BW GENMASK(2, 1) +#define HAL_TX_RATE_STATS_INFO0_PKT_TYPE GENMASK(6, 3) +#define HAL_TX_RATE_STATS_INFO0_STBC BIT(7) +#define HAL_TX_RATE_STATS_INFO0_LDPC BIT(8) +#define HAL_TX_RATE_STATS_INFO0_SGI GENMASK(10, 9) +#define HAL_TX_RATE_STATS_INFO0_MCS GENMASK(14, 11) +#define HAL_TX_RATE_STATS_INFO0_OFDMA_TX BIT(15) +#define HAL_TX_RATE_STATS_INFO0_TONES_IN_RU GENMASK(27, 16) + +enum hal_tx_rate_stats_bw { + HAL_TX_RATE_STATS_BW_20, + HAL_TX_RATE_STATS_BW_40, + HAL_TX_RATE_STATS_BW_80, + HAL_TX_RATE_STATS_BW_160, +}; + +enum hal_tx_rate_stats_pkt_type { + HAL_TX_RATE_STATS_PKT_TYPE_11A, + HAL_TX_RATE_STATS_PKT_TYPE_11B, + HAL_TX_RATE_STATS_PKT_TYPE_11N, + HAL_TX_RATE_STATS_PKT_TYPE_11AC, + HAL_TX_RATE_STATS_PKT_TYPE_11AX, +}; + +enum hal_tx_rate_stats_sgi { + HAL_TX_RATE_STATS_SGI_08US, + HAL_TX_RATE_STATS_SGI_04US, + HAL_TX_RATE_STATS_SGI_16US, + HAL_TX_RATE_STATS_SGI_32US, +}; + +struct hal_tx_rate_stats { + u32 info0; + u32 tsf; +} __packed; + +struct hal_wbm_link_desc { + struct ath11k_buffer_addr buf_addr_info; +} __packed; + +/* hal_wbm_link_desc + * + * Producer: WBM + * Consumer: WBM + * + * buf_addr_info + * Details of the physical address of a buffer or MSDU + * link descriptor. + */ + +enum hal_wbm_rel_src_module { + HAL_WBM_REL_SRC_MODULE_TQM, + HAL_WBM_REL_SRC_MODULE_RXDMA, + HAL_WBM_REL_SRC_MODULE_REO, + HAL_WBM_REL_SRC_MODULE_FW, + HAL_WBM_REL_SRC_MODULE_SW, +}; + +enum hal_wbm_rel_desc_type { + HAL_WBM_REL_DESC_TYPE_REL_MSDU, + HAL_WBM_REL_DESC_TYPE_MSDU_LINK, + HAL_WBM_REL_DESC_TYPE_MPDU_LINK, + HAL_WBM_REL_DESC_TYPE_MSDU_EXT, + HAL_WBM_REL_DESC_TYPE_QUEUE_EXT, +}; + +/* hal_wbm_rel_desc_type + * + * msdu_buffer + * The address points to an MSDU buffer + * + * msdu_link_descriptor + * The address points to an Tx MSDU link descriptor + * + * mpdu_link_descriptor + * The address points to an MPDU link descriptor + * + * msdu_ext_descriptor + * The address points to an MSDU extension descriptor + * + * queue_ext_descriptor + * The address points to an TQM queue extension descriptor. WBM should + * treat this is the same way as a link descriptor. + */ + +enum hal_wbm_rel_bm_act { + HAL_WBM_REL_BM_ACT_PUT_IN_IDLE, + HAL_WBM_REL_BM_ACT_REL_MSDU, +}; + +/* hal_wbm_rel_bm_act + * + * put_in_idle_list + * Put the buffer or descriptor back in the idle list. In case of MSDU or + * MDPU link descriptor, BM does not need to check to release any + * individual MSDU buffers. + * + * release_msdu_list + * This BM action can only be used in combination with desc_type being + * msdu_link_descriptor. Field first_msdu_index points out which MSDU + * pointer in the MSDU link descriptor is the first of an MPDU that is + * released. BM shall release all the MSDU buffers linked to this first + * MSDU buffer pointer. All related MSDU buffer pointer entries shall be + * set to value 0, which represents the 'NULL' pointer. When all MSDU + * buffer pointers in the MSDU link descriptor are 'NULL', the MSDU link + * descriptor itself shall also be released. + */ + +#define HAL_WBM_RELEASE_INFO0_REL_SRC_MODULE GENMASK(2, 0) +#define HAL_WBM_RELEASE_INFO0_BM_ACTION GENMASK(5, 3) +#define HAL_WBM_RELEASE_INFO0_DESC_TYPE GENMASK(8, 6) +#define HAL_WBM_RELEASE_INFO0_FIRST_MSDU_IDX GENMASK(12, 9) +#define HAL_WBM_RELEASE_INFO0_TQM_RELEASE_REASON GENMASK(16, 13) +#define HAL_WBM_RELEASE_INFO0_RXDMA_PUSH_REASON GENMASK(18, 17) +#define HAL_WBM_RELEASE_INFO0_RXDMA_ERROR_CODE GENMASK(23, 19) +#define HAL_WBM_RELEASE_INFO0_REO_PUSH_REASON GENMASK(25, 24) +#define HAL_WBM_RELEASE_INFO0_REO_ERROR_CODE GENMASK(30, 26) +#define HAL_WBM_RELEASE_INFO0_WBM_INTERNAL_ERROR BIT(31) + +#define HAL_WBM_RELEASE_INFO1_TQM_STATUS_NUMBER GENMASK(23, 0) +#define HAL_WBM_RELEASE_INFO1_TRANSMIT_COUNT GENMASK(30, 24) + +#define HAL_WBM_RELEASE_INFO2_ACK_FRAME_RSSI GENMASK(7, 0) +#define HAL_WBM_RELEASE_INFO2_SW_REL_DETAILS_VALID BIT(8) +#define HAL_WBM_RELEASE_INFO2_FIRST_MSDU BIT(9) +#define HAL_WBM_RELEASE_INFO2_LAST_MSDU BIT(10) +#define HAL_WBM_RELEASE_INFO2_MSDU_IN_AMSDU BIT(11) +#define HAL_WBM_RELEASE_INFO2_FW_TX_NOTIF_FRAME BIT(12) +#define HAL_WBM_RELEASE_INFO2_BUFFER_TIMESTAMP GENMASK(31, 13) + +#define HAL_WBM_RELEASE_INFO3_PEER_ID GENMASK(15, 0) +#define HAL_WBM_RELEASE_INFO3_TID GENMASK(19, 16) +#define HAL_WBM_RELEASE_INFO3_RING_ID GENMASK(27, 20) +#define HAL_WBM_RELEASE_INFO3_LOOPING_COUNT GENMASK(31, 28) + +#define HAL_WBM_REL_HTT_TX_COMP_INFO0_STATUS GENMASK(12, 9) +#define HAL_WBM_REL_HTT_TX_COMP_INFO0_REINJ_REASON GENMASK(16, 13) +#define HAL_WBM_REL_HTT_TX_COMP_INFO0_EXP_FRAME BIT(17) + +struct hal_wbm_release_ring { + struct ath11k_buffer_addr buf_addr_info; + u32 info0; + u32 info1; + u32 info2; + struct hal_tx_rate_stats rate_stats; + u32 info3; +} __packed; + +/* hal_wbm_release_ring + * + * Producer: SW/TQM/RXDMA/REO/SWITCH + * Consumer: WBM/SW/FW + * + * HTT tx status is overlayed on wbm_release ring on 4-byte words 2, 3, 4 and 5 + * for software based completions. + * + * buf_addr_info + * Details of the physical address of the buffer or link descriptor. + * + * release_source_module + * Indicates which module initiated the release of this buffer/descriptor. + * Values are defined in enum %HAL_WBM_REL_SRC_MODULE_. + * + * bm_action + * Field only valid when the field return_buffer_manager in + * Released_buff_or_desc_addr_info indicates: + * WBM_IDLE_BUF_LIST / WBM_IDLE_DESC_LIST + * Values are defined in enum %HAL_WBM_REL_BM_ACT_. + * + * buffer_or_desc_type + * Field only valid when WBM is marked as the return_buffer_manager in + * the Released_Buffer_address_info. Indicates that type of buffer or + * descriptor is being released. Values are in enum %HAL_WBM_REL_DESC_TYPE. + * + * first_msdu_index + * Field only valid for the bm_action release_msdu_list. The index of the + * first MSDU in an MSDU link descriptor all belonging to the same MPDU. + * + * tqm_release_reason + * Field only valid when Release_source_module is set to release_source_TQM + * Release reasons are defined in enum %HAL_WBM_TQM_REL_REASON_. + * + * rxdma_push_reason + * reo_push_reason + * Indicates why rxdma/reo pushed the frame to this ring and values are + * defined in enum %HAL_REO_DEST_RING_PUSH_REASON_. + * + * rxdma_error_code + * Field only valid when 'rxdma_push_reason' set to 'error_detected'. + * Values are defined in enum %HAL_REO_ENTR_RING_RXDMA_ECODE_. + * + * reo_error_code + * Field only valid when 'reo_push_reason' set to 'error_detected'. Values + * are defined in enum %HAL_REO_DEST_RING_ERROR_CODE_. + * + * wbm_internal_error + * Is set when WBM got a buffer pointer but the action was to push it to + * the idle link descriptor ring or do link related activity OR + * Is set when WBM got a link buffer pointer but the action was to push it + * to the buffer descriptor ring. + * + * tqm_status_number + * The value in this field is equal to tqm_cmd_number in TQM command. It is + * used to correlate the statu with TQM commands. Only valid when + * release_source_module is TQM. + * + * transmit_count + * The number of times the frame has been transmitted, valid only when + * release source in TQM. + * + * ack_frame_rssi + * This field is only valid when the source is TQM. If this frame is + * removed as the result of the reception of an ACK or BA, this field + * indicates the RSSI of the received ACK or BA frame. + * + * sw_release_details_valid + * This is set when WMB got a 'release_msdu_list' command from TQM and + * return buffer manager is not WMB. WBM will then de-aggregate all MSDUs + * and pass them one at a time on to the 'buffer owner'. + * + * first_msdu + * Field only valid when SW_release_details_valid is set. + * When set, this MSDU is the first MSDU pointed to in the + * 'release_msdu_list' command. + * + * last_msdu + * Field only valid when SW_release_details_valid is set. + * When set, this MSDU is the last MSDU pointed to in the + * 'release_msdu_list' command. + * + * msdu_part_of_amsdu + * Field only valid when SW_release_details_valid is set. + * When set, this MSDU was part of an A-MSDU in MPDU + * + * fw_tx_notify_frame + * Field only valid when SW_release_details_valid is set. + * + * buffer_timestamp + * Field only valid when SW_release_details_valid is set. + * This is the Buffer_timestamp field from the + * Timestamp in units of 1024 us + * + * struct hal_tx_rate_stats rate_stats + * Details for command execution tracking purposes. + * + * sw_peer_id + * tid + * Field only valid when Release_source_module is set to + * release_source_TQM + * + * 1) Release of msdu buffer due to drop_frame = 1. Flow is + * not fetched and hence sw_peer_id and tid = 0 + * + * buffer_or_desc_type = e_num 0 + * MSDU_rel_buffertqm_release_reason = e_num 1 + * tqm_rr_rem_cmd_rem + * + * 2) Release of msdu buffer due to Flow is not fetched and + * hence sw_peer_id and tid = 0 + * + * buffer_or_desc_type = e_num 0 + * MSDU_rel_buffertqm_release_reason = e_num 1 + * tqm_rr_rem_cmd_rem + * + * 3) Release of msdu link due to remove_mpdu or acked_mpdu + * command. + * + * buffer_or_desc_type = e_num1 + * msdu_link_descriptortqm_release_reason can be:e_num 1 + * tqm_rr_rem_cmd_reme_num 2 tqm_rr_rem_cmd_tx + * e_num 3 tqm_rr_rem_cmd_notxe_num 4 tqm_rr_rem_cmd_aged + * + * This field represents the TID from the TX_MSDU_FLOW + * descriptor or TX_MPDU_QUEUE descriptor + * + * rind_id + * For debugging. + * This field is filled in by the SRNG module. + * It help to identify the ring that is being looked + * + * looping_count + * A count value that indicates the number of times the + * producer of entries into the Buffer Manager Ring has looped + * around the ring. + * + * At initialization time, this value is set to 0. On the + * first loop, this value is set to 1. After the max value is + * reached allowed by the number of bits for this field, the + * count value continues with 0 again. + * + * In case SW is the consumer of the ring entries, it can + * use this field to figure out up to where the producer of + * entries has created new entries. This eliminates the need to + * check where the head pointer' of the ring is located once + * the SW starts processing an interrupt indicating that new + * entries have been put into this ring... + * + * Also note that SW if it wants only needs to look at the + * LSB bit of this count value. + */ + +/** + * enum hal_wbm_tqm_rel_reason - TQM release reason code + * @HAL_WBM_TQM_REL_REASON_FRAME_ACKED: ACK or BACK received for the frame + * @HAL_WBM_TQM_REL_REASON_CMD_REMOVE_MPDU: Command remove_mpdus initiated by SW + * @HAL_WBM_TQM_REL_REASON_CMD_REMOVE_TX: Command remove transmitted_mpdus + * initiated by sw. + * @HAL_WBM_TQM_REL_REASON_CMD_REMOVE_NOTX: Command remove untransmitted_mpdus + * initiated by sw. + * @HAL_WBM_TQM_REL_REASON_CMD_REMOVE_AGED_FRAMES: Command remove aged msdus or + * mpdus. + * @HAL_WBM_TQM_REL_REASON_CMD_REMOVE_RESEAON1: Remove command initiated by + * fw with fw_reason1. + * @HAL_WBM_TQM_REL_REASON_CMD_REMOVE_RESEAON2: Remove command initiated by + * fw with fw_reason2. + * @HAL_WBM_TQM_REL_REASON_CMD_REMOVE_RESEAON3: Remove command initiated by + * fw with fw_reason3. + */ +enum hal_wbm_tqm_rel_reason { + HAL_WBM_TQM_REL_REASON_FRAME_ACKED, + HAL_WBM_TQM_REL_REASON_CMD_REMOVE_MPDU, + HAL_WBM_TQM_REL_REASON_CMD_REMOVE_TX, + HAL_WBM_TQM_REL_REASON_CMD_REMOVE_NOTX, + HAL_WBM_TQM_REL_REASON_CMD_REMOVE_AGED_FRAMES, + HAL_WBM_TQM_REL_REASON_CMD_REMOVE_RESEAON1, + HAL_WBM_TQM_REL_REASON_CMD_REMOVE_RESEAON2, + HAL_WBM_TQM_REL_REASON_CMD_REMOVE_RESEAON3, +}; + +struct hal_wbm_buffer_ring { + struct ath11k_buffer_addr buf_addr_info; +}; + +enum hal_desc_owner { + HAL_DESC_OWNER_WBM, + HAL_DESC_OWNER_SW, + HAL_DESC_OWNER_TQM, + HAL_DESC_OWNER_RXDMA, + HAL_DESC_OWNER_REO, + HAL_DESC_OWNER_SWITCH, +}; + +enum hal_desc_buf_type { + HAL_DESC_BUF_TYPE_TX_MSDU_LINK, + HAL_DESC_BUF_TYPE_TX_MPDU_LINK, + HAL_DESC_BUF_TYPE_TX_MPDU_QUEUE_HEAD, + HAL_DESC_BUF_TYPE_TX_MPDU_QUEUE_EXT, + HAL_DESC_BUF_TYPE_TX_FLOW, + HAL_DESC_BUF_TYPE_TX_BUFFER, + HAL_DESC_BUF_TYPE_RX_MSDU_LINK, + HAL_DESC_BUF_TYPE_RX_MPDU_LINK, + HAL_DESC_BUF_TYPE_RX_REO_QUEUE, + HAL_DESC_BUF_TYPE_RX_REO_QUEUE_EXT, + HAL_DESC_BUF_TYPE_RX_BUFFER, + HAL_DESC_BUF_TYPE_IDLE_LINK, +}; + +#define HAL_DESC_REO_OWNED 4 +#define HAL_DESC_REO_QUEUE_DESC 8 +#define HAL_DESC_REO_QUEUE_EXT_DESC 9 +#define HAL_DESC_REO_NON_QOS_TID 16 + +#define HAL_DESC_HDR_INFO0_OWNER GENMASK(3, 0) +#define HAL_DESC_HDR_INFO0_BUF_TYPE GENMASK(7, 4) +#define HAL_DESC_HDR_INFO0_DBG_RESERVED GENMASK(31, 8) + +struct hal_desc_header { + u32 info0; +} __packed; + +struct hal_rx_mpdu_link_ptr { + struct ath11k_buffer_addr addr_info; +} __packed; + +struct hal_rx_msdu_details { + struct ath11k_buffer_addr buf_addr_info; + struct rx_msdu_desc rx_msdu_info; +} __packed; + +#define HAL_RX_MSDU_LNK_INFO0_RX_QUEUE_NUMBER GENMASK(15, 0) +#define HAL_RX_MSDU_LNK_INFO0_FIRST_MSDU_LNK BIT(16) + +struct hal_rx_msdu_link { + struct hal_desc_header desc_hdr; + struct ath11k_buffer_addr buf_addr_info; + u32 info0; + u32 pn[4]; + struct hal_rx_msdu_details msdu_link[6]; +} __packed; + +struct hal_rx_reo_queue_ext { + struct hal_desc_header desc_hdr; + u32 rsvd; + struct hal_rx_mpdu_link_ptr mpdu_link[15]; +} __packed; + +/* hal_rx_reo_queue_ext + * Consumer: REO + * Producer: REO + * + * descriptor_header + * Details about which module owns this struct. + * + * mpdu_link + * Pointer to the next MPDU_link descriptor in the MPDU queue. + */ + +enum hal_rx_reo_queue_pn_size { + HAL_RX_REO_QUEUE_PN_SIZE_24, + HAL_RX_REO_QUEUE_PN_SIZE_48, + HAL_RX_REO_QUEUE_PN_SIZE_128, +}; + +#define HAL_RX_REO_QUEUE_RX_QUEUE_NUMBER GENMASK(15, 0) + +#define HAL_RX_REO_QUEUE_INFO0_VLD BIT(0) +#define HAL_RX_REO_QUEUE_INFO0_ASSOC_LNK_DESC_COUNTER GENMASK(2, 1) +#define HAL_RX_REO_QUEUE_INFO0_DIS_DUP_DETECTION BIT(3) +#define HAL_RX_REO_QUEUE_INFO0_SOFT_REORDER_EN BIT(4) +#define HAL_RX_REO_QUEUE_INFO0_AC GENMASK(6, 5) +#define HAL_RX_REO_QUEUE_INFO0_BAR BIT(7) +#define HAL_RX_REO_QUEUE_INFO0_RETRY BIT(8) +#define HAL_RX_REO_QUEUE_INFO0_CHECK_2K_MODE BIT(9) +#define HAL_RX_REO_QUEUE_INFO0_OOR_MODE BIT(10) +#define HAL_RX_REO_QUEUE_INFO0_BA_WINDOW_SIZE GENMASK(18, 11) +#define HAL_RX_REO_QUEUE_INFO0_PN_CHECK BIT(19) +#define HAL_RX_REO_QUEUE_INFO0_EVEN_PN BIT(20) +#define HAL_RX_REO_QUEUE_INFO0_UNEVEN_PN BIT(21) +#define HAL_RX_REO_QUEUE_INFO0_PN_HANDLE_ENABLE BIT(22) +#define HAL_RX_REO_QUEUE_INFO0_PN_SIZE GENMASK(24, 23) +#define HAL_RX_REO_QUEUE_INFO0_IGNORE_AMPDU_FLG BIT(25) + +#define HAL_RX_REO_QUEUE_INFO1_SVLD BIT(0) +#define HAL_RX_REO_QUEUE_INFO1_SSN GENMASK(12, 1) +#define HAL_RX_REO_QUEUE_INFO1_CURRENT_IDX GENMASK(20, 13) +#define HAL_RX_REO_QUEUE_INFO1_SEQ_2K_ERR BIT(21) +#define HAL_RX_REO_QUEUE_INFO1_PN_ERR BIT(22) +#define HAL_RX_REO_QUEUE_INFO1_PN_VALID BIT(31) + +#define HAL_RX_REO_QUEUE_INFO2_MPDU_COUNT GENMASK(6, 0) +#define HAL_RX_REO_QUEUE_INFO2_MSDU_COUNT (31, 7) + +#define HAL_RX_REO_QUEUE_INFO3_TIMEOUT_COUNT GENMASK(9, 4) +#define HAL_RX_REO_QUEUE_INFO3_FWD_DUE_TO_BAR_CNT GENMASK(15, 10) +#define HAL_RX_REO_QUEUE_INFO3_DUPLICATE_COUNT GENMASK(31, 10) + +#define HAL_RX_REO_QUEUE_INFO4_FRAME_IN_ORD_COUNT GENMASK(23, 0) +#define HAL_RX_REO_QUEUE_INFO4_BAR_RECVD_COUNT GENMASK(31, 24) + +#define HAL_RX_REO_QUEUE_INFO5_LATE_RX_MPDU_COUNT GENMASK(11, 0) +#define HAL_RX_REO_QUEUE_INFO5_WINDOW_JUMP_2K GENMASK(15, 12) +#define HAL_RX_REO_QUEUE_INFO5_HOLE_COUNT GENMASK(31, 16) + +struct hal_rx_reo_queue { + struct hal_desc_header desc_hdr; + u32 rx_queue_num; + u32 info0; + u32 info1; + u32 pn[4]; + u32 last_rx_enqueue_timestamp; + u32 last_rx_dequeue_timestamp; + u32 next_aging_queue[2]; + u32 prev_aging_queue[2]; + u32 rx_bitmap[8]; + u32 info2; + u32 info3; + u32 info4; + u32 processed_mpdus; + u32 processed_msdus; + u32 processed_total_bytes; + u32 info5; + u32 rsvd[3]; + struct hal_rx_reo_queue_ext ext_desc[0]; +} __packed; + +/* hal_rx_reo_queue + * + * descriptor_header + * Details about which module owns this struct. Note that sub field + * Buffer_type shall be set to receive_reo_queue_descriptor. + * + * receive_queue_number + * Indicates the MPDU queue ID to which this MPDU link descriptor belongs. + * + * vld + * Valid bit indicating a session is established and the queue descriptor + * is valid. + * associated_link_descriptor_counter + * Indicates which of the 3 link descriptor counters shall be incremented + * or decremented when link descriptors are added or removed from this + * flow queue. + * disable_duplicate_detection + * When set, do not perform any duplicate detection. + * soft_reorder_enable + * When set, REO has been instructed to not perform the actual re-ordering + * of frames for this queue, but just to insert the reorder opcodes. + * ac + * Indicates the access category of the queue descriptor. + * bar + * Indicates if BAR has been received. + * retry + * Retry bit is checked if this bit is set. + * chk_2k_mode + * Indicates what type of operation is expected from Reo when the received + * frame SN falls within the 2K window. + * oor_mode + * Indicates what type of operation is expected when the received frame + * falls within the OOR window. + * ba_window_size + * Indicates the negotiated (window size + 1). Max of 256 bits. + * + * A value 255 means 256 bitmap, 63 means 64 bitmap, 0 (means non-BA + * session, with window size of 0). The 3 values here are the main values + * validated, but other values should work as well. + * + * A BA window size of 0 (=> one frame entry bitmat), means that there is + * no additional rx_reo_queue_ext desc. following rx_reo_queue in memory. + * A BA window size of 1 - 105, means that there is 1 rx_reo_queue_ext. + * A BA window size of 106 - 210, means that there are 2 rx_reo_queue_ext. + * A BA window size of 211 - 256, means that there are 3 rx_reo_queue_ext. + * pn_check_needed, pn_shall_be_even, pn_shall_be_uneven, pn_handling_enable, + * pn_size + * REO shall perform the PN increment check, even number check, uneven + * number check, PN error check and size of the PN field check. + * ignore_ampdu_flag + * REO shall ignore the ampdu_flag on entrance descriptor for this queue. + * + * svld + * Sequence number in next field is valid one. + * ssn + * Starting Sequence number of the session. + * current_index + * Points to last forwarded packet + * seq_2k_error_detected_flag + * REO has detected a 2k error jump in the sequence number and from that + * moment forward, all new frames are forwarded directly to FW, without + * duplicate detect, reordering, etc. + * pn_error_detected_flag + * REO has detected a PN error. + */ + +#define HAL_REO_UPD_RX_QUEUE_INFO0_QUEUE_ADDR_HI GENMASK(7, 0) +#define HAL_REO_UPD_RX_QUEUE_INFO0_UPD_RX_QUEUE_NUM BIT(8) +#define HAL_REO_UPD_RX_QUEUE_INFO0_UPD_VLD BIT(9) +#define HAL_REO_UPD_RX_QUEUE_INFO0_UPD_ASSOC_LNK_DESC_CNT BIT(10) +#define HAL_REO_UPD_RX_QUEUE_INFO0_UPD_DIS_DUP_DETECTION BIT(11) +#define HAL_REO_UPD_RX_QUEUE_INFO0_UPD_SOFT_REORDER_EN BIT(12) +#define HAL_REO_UPD_RX_QUEUE_INFO0_UPD_AC BIT(13) +#define HAL_REO_UPD_RX_QUEUE_INFO0_UPD_BAR BIT(14) +#define HAL_REO_UPD_RX_QUEUE_INFO0_UPD_RETRY BIT(15) +#define HAL_REO_UPD_RX_QUEUE_INFO0_UPD_CHECK_2K_MODE BIT(16) +#define HAL_REO_UPD_RX_QUEUE_INFO0_UPD_OOR_MODE BIT(17) +#define HAL_REO_UPD_RX_QUEUE_INFO0_UPD_BA_WINDOW_SIZE BIT(18) +#define HAL_REO_UPD_RX_QUEUE_INFO0_UPD_PN_CHECK BIT(19) +#define HAL_REO_UPD_RX_QUEUE_INFO0_UPD_EVEN_PN BIT(20) +#define HAL_REO_UPD_RX_QUEUE_INFO0_UPD_UNEVEN_PN BIT(21) +#define HAL_REO_UPD_RX_QUEUE_INFO0_UPD_PN_HANDLE_ENABLE BIT(22) +#define HAL_REO_UPD_RX_QUEUE_INFO0_UPD_PN_SIZE BIT(23) +#define HAL_REO_UPD_RX_QUEUE_INFO0_UPD_IGNORE_AMPDU_FLG BIT(24) +#define HAL_REO_UPD_RX_QUEUE_INFO0_UPD_SVLD BIT(25) +#define HAL_REO_UPD_RX_QUEUE_INFO0_UPD_SSN BIT(26) +#define HAL_REO_UPD_RX_QUEUE_INFO0_UPD_SEQ_2K_ERR BIT(27) +#define HAL_REO_UPD_RX_QUEUE_INFO0_UPD_PN_ERR BIT(28) +#define HAL_REO_UPD_RX_QUEUE_INFO0_UPD_PN_VALID BIT(29) +#define HAL_REO_UPD_RX_QUEUE_INFO0_UPD_PN BIT(30) + +#define HAL_REO_UPD_RX_QUEUE_INFO1_RX_QUEUE_NUMBER GENMASK(15, 0) +#define HAL_REO_UPD_RX_QUEUE_INFO1_VLD BIT(16) +#define HAL_REO_UPD_RX_QUEUE_INFO1_ASSOC_LNK_DESC_COUNTER GENMASK(18, 17) +#define HAL_REO_UPD_RX_QUEUE_INFO1_DIS_DUP_DETECTION BIT(19) +#define HAL_REO_UPD_RX_QUEUE_INFO1_SOFT_REORDER_EN BIT(20) +#define HAL_REO_UPD_RX_QUEUE_INFO1_AC GENMASK(22, 21) +#define HAL_REO_UPD_RX_QUEUE_INFO1_BAR BIT(23) +#define HAL_REO_UPD_RX_QUEUE_INFO1_RETRY BIT(24) +#define HAL_REO_UPD_RX_QUEUE_INFO1_CHECK_2K_MODE BIT(25) +#define HAL_REO_UPD_RX_QUEUE_INFO1_OOR_MODE BIT(26) +#define HAL_REO_UPD_RX_QUEUE_INFO1_PN_CHECK BIT(27) +#define HAL_REO_UPD_RX_QUEUE_INFO1_EVEN_PN BIT(28) +#define HAL_REO_UPD_RX_QUEUE_INFO1_UNEVEN_PN BIT(29) +#define HAL_REO_UPD_RX_QUEUE_INFO1_PN_HANDLE_ENABLE BIT(30) +#define HAL_REO_UPD_RX_QUEUE_INFO1_IGNORE_AMPDU_FLG BIT(31) + +#define HAL_REO_UPD_RX_QUEUE_INFO2_BA_WINDOW_SIZE GENMASK(7, 0) +#define HAL_REO_UPD_RX_QUEUE_INFO2_PN_SIZE GENMASK(9, 8) +#define HAL_REO_UPD_RX_QUEUE_INFO2_SVLD BIT(10) +#define HAL_REO_UPD_RX_QUEUE_INFO2_SSN GENMASK(22, 11) +#define HAL_REO_UPD_RX_QUEUE_INFO2_SEQ_2K_ERR BIT(23) +#define HAL_REO_UPD_RX_QUEUE_INFO2_PN_ERR BIT(24) +#define HAL_REO_UPD_RX_QUEUE_INFO2_PN_VALID BIT(25) + +struct hal_reo_update_rx_queue { + struct hal_reo_cmd_hdr cmd; + u32 queue_addr_lo; + u32 info0; + u32 info1; + u32 info2; + u32 pn[4]; +} __packed; + +#define HAL_REO_UNBLOCK_CACHE_INFO0_UNBLK_CACHE BIT(0) +#define HAL_REO_UNBLOCK_CACHE_INFO0_RESOURCE_IDX GENMASK(2, 1) + +struct hal_reo_unblock_cache { + struct hal_reo_cmd_hdr cmd; + u32 info0; + u32 rsvd[7]; +} __packed; + +enum hal_reo_exec_status { + HAL_REO_EXEC_STATUS_SUCCESS, + HAL_REO_EXEC_STATUS_BLOCKED, + HAL_REO_EXEC_STATUS_FAILED, + HAL_REO_EXEC_STATUS_RESOURCE_BLOCKED, +}; + +#define HAL_REO_STATUS_HDR_INFO0_STATUS_NUM GENMASK(15, 0) +#define HAL_REO_STATUS_HDR_INFO0_EXEC_TIME GENMASK(25, 16) +#define HAL_REO_STATUS_HDR_INFO0_EXEC_STATUS GENMASK(27, 26) + +struct hal_reo_status_hdr { + u32 info0; + u32 timestamp; +} __packed; + +/* hal_reo_status_hdr + * Producer: REO + * Consumer: SW + * + * status_num + * The value in this field is equal to value of the reo command + * number. This field helps to correlate the statuses with the REO + * commands. + * + * execution_time (in us) + * The amount of time REO took to excecute the command. Note that + * this time does not include the duration of the command waiting + * in the command ring, before the execution started. + * + * execution_status + * Execution status of the command. Values are defined in + * enum %HAL_REO_EXEC_STATUS_. + */ +#define HAL_REO_GET_QUEUE_STATS_STATUS_INFO0_SSN GENMASK(11, 0) +#define HAL_REO_GET_QUEUE_STATS_STATUS_INFO0_CUR_IDX GENMASK(19, 12) + +#define HAL_REO_GET_QUEUE_STATS_STATUS_INFO1_MPDU_COUNT GENMASK(6, 0) +#define HAL_REO_GET_QUEUE_STATS_STATUS_INFO1_MSDU_COUNT GENMASK(31, 7) + +#define HAL_REO_GET_QUEUE_STATS_STATUS_INFO2_TIMEOUT_COUNT GENMASK(9, 4) +#define HAL_REO_GET_QUEUE_STATS_STATUS_INFO2_FDTB_COUNT GENMASK(15, 10) +#define HAL_REO_GET_QUEUE_STATS_STATUS_INFO2_DUPLICATE_COUNT GENMASK(31, 16) + +#define HAL_REO_GET_QUEUE_STATS_STATUS_INFO3_FIO_COUNT GENMASK(23, 0) +#define HAL_REO_GET_QUEUE_STATS_STATUS_INFO3_BAR_RCVD_CNT GENMASK(31, 24) + +#define HAL_REO_GET_QUEUE_STATS_STATUS_INFO4_LATE_RX_MPDU GENMASK(11, 0) +#define HAL_REO_GET_QUEUE_STATS_STATUS_INFO4_WINDOW_JMP2K GENMASK(15, 12) +#define HAL_REO_GET_QUEUE_STATS_STATUS_INFO4_HOLE_COUNT GENMASK(31, 16) + +#define HAL_REO_GET_QUEUE_STATS_STATUS_INFO5_LOOPING_CNT GENMASK(31, 28) + +struct hal_reo_get_queue_stats_status { + struct hal_reo_status_hdr hdr; + u32 info0; + u32 pn[4]; + u32 last_rx_enqueue_timestamp; + u32 last_rx_dequeue_timestamp; + u32 rx_bitmap[8]; + u32 info1; + u32 info2; + u32 info3; + u32 num_mpdu_frames; + u32 num_msdu_frames; + u32 total_bytes; + u32 info4; + u32 info5; +} __packed; + +/* hal_reo_get_queue_stats_status + * Producer: REO + * Consumer: SW + * + * status_hdr + * Details that can link this status with the original command. It + * also contains info on how long REO took to execute this command. + * + * ssn + * Starting Sequence number of the session, this changes whenever + * window moves (can be filled by SW then maintained by REO). + * + * current_index + * Points to last forwarded packet. + * + * pn + * Bits of the PN number. + * + * last_rx_enqueue_timestamp + * last_rx_dequeue_timestamp + * Timestamp of arrival of the last MPDU for this queue and + * Timestamp of forwarding an MPDU accordingly. + * + * rx_bitmap + * When a bit is set, the corresponding frame is currently held + * in the re-order queue. The bitmap is Fully managed by HW. + * + * current_mpdu_count + * current_msdu_count + * The number of MPDUs and MSDUs in the queue. + * + * timeout_count + * The number of times REO started forwarding frames even though + * there is a hole in the bitmap. Forwarding reason is timeout. + * + * forward_due_to_bar_count + * The number of times REO started forwarding frames even though + * there is a hole in the bitmap. Fwd reason is reception of BAR. + * + * duplicate_count + * The number of duplicate frames that have been detected. + * + * frames_in_order_count + * The number of frames that have been received in order (without + * a hole that prevented them from being forwarded immediately). + * + * bar_received_count + * The number of times a BAR frame is received. + * + * mpdu_frames_processed_count + * msdu_frames_processed_count + * The total number of MPDU/MSDU frames that have been processed. + * + * total_bytes + * An approximation of the number of bytes received for this queue. + * + * late_receive_mpdu_count + * The number of MPDUs received after the window had already moved + * on. The 'late' sequence window is defined as + * (Window SSN - 256) - (Window SSN - 1). + * + * window_jump_2k + * The number of times the window moved more than 2K + * + * hole_count + * The number of times a hole was created in the receive bitmap. + * + * looping_count + * A count value that indicates the number of times the producer of + * entries into this Ring has looped around the ring. + */ + +#define HAL_REO_STATUS_LOOP_CNT GENMASK(31, 28) + +#define HAL_REO_FLUSH_QUEUE_INFO0_ERR_DETECTED BIT(0) +#define HAL_REO_FLUSH_QUEUE_INFO0_RSVD GENMASK(31, 1) +#define HAL_REO_FLUSH_QUEUE_INFO1_RSVD GENMASK(27, 0) + +struct hal_reo_flush_queue_status { + struct hal_reo_status_hdr hdr; + u32 info0; + u32 rsvd0[21]; + u32 info1; +} __packed; + +/* hal_reo_flush_queue_status + * Producer: REO + * Consumer: SW + * + * status_hdr + * Details that can link this status with the original command. It + * also contains info on how long REO took to execute this command. + * + * error_detected + * Status of blocking resource + * + * 0 - No error has been detected while executing this command + * 1 - Error detected. The resource to be used for blocking was + * already in use. + * + * looping_count + * A count value that indicates the number of times the producer of + * entries into this Ring has looped around the ring. + */ + +#define HAL_REO_FLUSH_CACHE_STATUS_INFO0_IS_ERR BIT(0) +#define HAL_REO_FLUSH_CACHE_STATUS_INFO0_BLOCK_ERR_CODE GENMASK(2, 1) +#define HAL_REO_FLUSH_CACHE_STATUS_INFO0_FLUSH_STATUS_HIT BIT(8) +#define HAL_REO_FLUSH_CACHE_STATUS_INFO0_FLUSH_DESC_TYPE GENMASK(11, 9) +#define HAL_REO_FLUSH_CACHE_STATUS_INFO0_FLUSH_CLIENT_ID GENMASK(15, 12) +#define HAL_REO_FLUSH_CACHE_STATUS_INFO0_FLUSH_ERR GENMASK(17, 16) +#define HAL_REO_FLUSH_CACHE_STATUS_INFO0_FLUSH_COUNT GENMASK(25, 18) + +struct hal_reo_flush_cache_status { + struct hal_reo_status_hdr hdr; + u32 info0; + u32 rsvd0[21]; + u32 info1; +} __packed; + +/* hal_reo_flush_cache_status + * Producer: REO + * Consumer: SW + * + * status_hdr + * Details that can link this status with the original command. It + * also contains info on how long REO took to execute this command. + * + * error_detected + * Status for blocking resource handling + * + * 0 - No error has been detected while executing this command + * 1 - An error in the blocking resource management was detected + * + * block_error_details + * only valid when error_detected is set + * + * 0 - No blocking related errors found + * 1 - Blocking resource is already in use + * 2 - Resource requested to be unblocked, was not blocked + * + * cache_controller_flush_status_hit + * The status that the cache controller returned on executing the + * flush command. + * + * 0 - miss; 1 - hit + * + * cache_controller_flush_status_desc_type + * Flush descriptor type + * + * cache_controller_flush_status_client_id + * Module who made the flush request + * + * In REO, this is always 0 + * + * cache_controller_flush_status_error + * Error condition + * + * 0 - No error found + * 1 - HW interface is still busy + * 2 - Line currently locked. Used for one line flush command + * 3 - At least one line is still locked. + * Used for cache flush command. + * + * cache_controller_flush_count + * The number of lines that were actually flushed out + * + * looping_count + * A count value that indicates the number of times the producer of + * entries into this Ring has looped around the ring. + */ + +#define HAL_REO_UNBLOCK_CACHE_STATUS_INFO0_IS_ERR BIT(0) +#define HAL_REO_UNBLOCK_CACHE_STATUS_INFO0_TYPE BIT(1) + +struct hal_reo_unblock_cache_status { + struct hal_reo_status_hdr hdr; + u32 info0; + u32 rsvd0[21]; + u32 info1; +} __packed; + +/* hal_reo_unblock_cache_status + * Producer: REO + * Consumer: SW + * + * status_hdr + * Details that can link this status with the original command. It + * also contains info on how long REO took to execute this command. + * + * error_detected + * 0 - No error has been detected while executing this command + * 1 - The blocking resource was not in use, and therefore it could + * not be unblocked. + * + * unblock_type + * Reference to the type of unblock command + * 0 - Unblock a blocking resource + * 1 - The entire cache usage is unblock + * + * looping_count + * A count value that indicates the number of times the producer of + * entries into this Ring has looped around the ring. + */ + +#define HAL_REO_FLUSH_TIMEOUT_STATUS_INFO0_IS_ERR BIT(0) +#define HAL_REO_FLUSH_TIMEOUT_STATUS_INFO0_LIST_EMPTY BIT(1) + +#define HAL_REO_FLUSH_TIMEOUT_STATUS_INFO1_REL_DESC_COUNT GENMASK(15, 0) +#define HAL_REO_FLUSH_TIMEOUT_STATUS_INFO1_FWD_BUF_COUNT GENMASK(31, 16) + +struct hal_reo_flush_timeout_list_status { + struct hal_reo_status_hdr hdr; + u32 info0; + u32 info1; + u32 rsvd0[20]; + u32 info2; +} __packed; + +/* hal_reo_flush_timeout_list_status + * Producer: REO + * Consumer: SW + * + * status_hdr + * Details that can link this status with the original command. It + * also contains info on how long REO took to execute this command. + * + * error_detected + * 0 - No error has been detected while executing this command + * 1 - Command not properly executed and returned with error + * + * timeout_list_empty + * When set, REO has depleted the timeout list and all entries are + * gone. + * + * release_desc_count + * Producer: SW; Consumer: REO + * The number of link descriptor released + * + * forward_buf_count + * Producer: SW; Consumer: REO + * The number of buffers forwarded to the REO destination rings + * + * looping_count + * A count value that indicates the number of times the producer of + * entries into this Ring has looped around the ring. + */ + +#define HAL_REO_DESC_THRESH_STATUS_INFO0_THRESH_INDEX GENMASK(1, 0) +#define HAL_REO_DESC_THRESH_STATUS_INFO1_LINK_DESC_COUNTER0 GENMASK(23, 0) +#define HAL_REO_DESC_THRESH_STATUS_INFO2_LINK_DESC_COUNTER1 GENMASK(23, 0) +#define HAL_REO_DESC_THRESH_STATUS_INFO3_LINK_DESC_COUNTER2 GENMASK(23, 0) +#define HAL_REO_DESC_THRESH_STATUS_INFO4_LINK_DESC_COUNTER_SUM GENMASK(23, 0) + +struct hal_reo_desc_thresh_reached_status { + struct hal_reo_status_hdr hdr; + u32 info0; + u32 info1; + u32 info2; + u32 info3; + u32 info4; + u32 rsvd0[17]; + u32 info5; +} __packed; + +/* hal_reo_desc_thresh_reached_status + * Producer: REO + * Consumer: SW + * + * status_hdr + * Details that can link this status with the original command. It + * also contains info on how long REO took to execute this command. + * + * threshold_index + * The index of the threshold register whose value got reached + * + * link_descriptor_counter0 + * link_descriptor_counter1 + * link_descriptor_counter2 + * link_descriptor_counter_sum + * Value of the respective counters at generation of this message + * + * looping_count + * A count value that indicates the number of times the producer of + * entries into this Ring has looped around the ring. + */ + +#endif /* ATH11K_HAL_DESC_H */ diff --git a/drivers/net/wireless/ath/ath11k/hal_rx.c b/drivers/net/wireless/ath/ath11k/hal_rx.c new file mode 100644 index 0000000000000..2de4b388db20c --- /dev/null +++ b/drivers/net/wireless/ath/ath11k/hal_rx.c @@ -0,0 +1,1221 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* + * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. + */ + +#include "ahb.h" +#include "debug.h" +#include "hal.h" +#include "hal_tx.h" +#include "hal_rx.h" +#include "hal_desc.h" + +static void ath11k_hal_reo_set_desc_hdr(struct hal_desc_header *hdr, + u8 owner, u8 buffer_type, u32 magic) +{ + hdr->info0 = FIELD_PREP(HAL_DESC_HDR_INFO0_OWNER, owner) | + FIELD_PREP(HAL_DESC_HDR_INFO0_BUF_TYPE, buffer_type); + + /* Magic pattern in reserved bits for debugging */ + hdr->info0 |= FIELD_PREP(HAL_DESC_HDR_INFO0_DBG_RESERVED, magic); +} + +static int ath11k_hal_reo_cmd_queue_stats(struct hal_tlv_hdr *tlv, + struct ath11k_hal_reo_cmd *cmd) +{ + struct hal_reo_get_queue_stats *desc; + + tlv->tl = FIELD_PREP(HAL_TLV_HDR_TAG, HAL_REO_GET_QUEUE_STATS) | + FIELD_PREP(HAL_TLV_HDR_LEN, sizeof(*desc)); + + desc = (struct hal_reo_get_queue_stats *)tlv->value; + memset(&desc->queue_addr_lo, 0, + (sizeof(*desc) - sizeof(struct hal_reo_cmd_hdr))); + + desc->cmd.info0 &= ~HAL_REO_CMD_HDR_INFO0_STATUS_REQUIRED; + if (cmd->flag & HAL_REO_CMD_FLG_NEED_STATUS) + desc->cmd.info0 |= HAL_REO_CMD_HDR_INFO0_STATUS_REQUIRED; + + desc->queue_addr_lo = cmd->addr_lo; + desc->info0 = FIELD_PREP(HAL_REO_GET_QUEUE_STATS_INFO0_QUEUE_ADDR_HI, + cmd->addr_hi); + if (cmd->flag & HAL_REO_CMD_FLG_STATS_CLEAR) + desc->info0 |= HAL_REO_GET_QUEUE_STATS_INFO0_CLEAR_STATS; + + return FIELD_GET(HAL_REO_CMD_HDR_INFO0_CMD_NUMBER, desc->cmd.info0); +} + +static int ath11k_hal_reo_cmd_flush_cache(struct ath11k_hal *hal, struct hal_tlv_hdr *tlv, + struct ath11k_hal_reo_cmd *cmd) +{ + struct hal_reo_flush_cache *desc; + u8 avail_slot = ffz(hal->avail_blk_resource); + + if (cmd->flag & HAL_REO_CMD_FLG_FLUSH_BLOCK_LATER) { + if (avail_slot >= HAL_MAX_AVAIL_BLK_RES) + return -ENOSPC; + + hal->current_blk_index = avail_slot; + } + + tlv->tl = FIELD_PREP(HAL_TLV_HDR_TAG, HAL_REO_FLUSH_CACHE) | + FIELD_PREP(HAL_TLV_HDR_LEN, sizeof(*desc)); + + desc = (struct hal_reo_flush_cache *)tlv->value; + memset(&desc->cache_addr_lo, 0, + (sizeof(*desc) - sizeof(struct hal_reo_cmd_hdr))); + + desc->cmd.info0 &= ~HAL_REO_CMD_HDR_INFO0_STATUS_REQUIRED; + if (cmd->flag & HAL_REO_CMD_FLG_NEED_STATUS) + desc->cmd.info0 |= HAL_REO_CMD_HDR_INFO0_STATUS_REQUIRED; + + desc->cache_addr_lo = cmd->addr_lo; + desc->info0 = FIELD_PREP(HAL_REO_FLUSH_CACHE_INFO0_CACHE_ADDR_HI, + cmd->addr_hi); + + if (cmd->flag & HAL_REO_CMD_FLG_FLUSH_FWD_ALL_MPDUS) + desc->info0 |= HAL_REO_FLUSH_CACHE_INFO0_FWD_ALL_MPDUS; + + if (cmd->flag & HAL_REO_CMD_FLG_FLUSH_BLOCK_LATER) { + desc->info0 |= HAL_REO_FLUSH_CACHE_INFO0_BLOCK_CACHE_USAGE; + desc->info0 |= + FIELD_PREP(HAL_REO_FLUSH_CACHE_INFO0_BLOCK_RESRC_IDX, + avail_slot); + } + + if (cmd->flag & HAL_REO_CMD_FLG_FLUSH_NO_INVAL) + desc->info0 |= HAL_REO_FLUSH_CACHE_INFO0_FLUSH_WO_INVALIDATE; + + if (cmd->flag & HAL_REO_CMD_FLG_FLUSH_ALL) + desc->info0 |= HAL_REO_FLUSH_CACHE_INFO0_FLUSH_ALL; + + return FIELD_GET(HAL_REO_CMD_HDR_INFO0_CMD_NUMBER, desc->cmd.info0); +} + +static int ath11k_hal_reo_cmd_update_rx_queue(struct hal_tlv_hdr *tlv, + struct ath11k_hal_reo_cmd *cmd) +{ + struct hal_reo_update_rx_queue *desc; + + tlv->tl = FIELD_PREP(HAL_TLV_HDR_TAG, HAL_REO_UPDATE_RX_REO_QUEUE) | + FIELD_PREP(HAL_TLV_HDR_LEN, sizeof(*desc)); + + desc = (struct hal_reo_update_rx_queue *)tlv->value; + memset(&desc->queue_addr_lo, 0, + (sizeof(*desc) - sizeof(struct hal_reo_cmd_hdr))); + + desc->cmd.info0 &= ~HAL_REO_CMD_HDR_INFO0_STATUS_REQUIRED; + if (cmd->flag & HAL_REO_CMD_FLG_NEED_STATUS) + desc->cmd.info0 |= HAL_REO_CMD_HDR_INFO0_STATUS_REQUIRED; + + desc->queue_addr_lo = cmd->addr_lo; + desc->info0 = + FIELD_PREP(HAL_REO_UPD_RX_QUEUE_INFO0_QUEUE_ADDR_HI, + cmd->addr_hi) | + FIELD_PREP(HAL_REO_UPD_RX_QUEUE_INFO0_UPD_RX_QUEUE_NUM, + !!(cmd->upd0 & HAL_REO_CMD_UPD0_RX_QUEUE_NUM)) | + FIELD_PREP(HAL_REO_UPD_RX_QUEUE_INFO0_UPD_VLD, + !!(cmd->upd0 & HAL_REO_CMD_UPD0_VLD)) | + FIELD_PREP(HAL_REO_UPD_RX_QUEUE_INFO0_UPD_ASSOC_LNK_DESC_CNT, + !!(cmd->upd0 & HAL_REO_CMD_UPD0_ALDC)) | + FIELD_PREP(HAL_REO_UPD_RX_QUEUE_INFO0_UPD_DIS_DUP_DETECTION, + !!(cmd->upd0 & HAL_REO_CMD_UPD0_DIS_DUP_DETECTION)) | + FIELD_PREP(HAL_REO_UPD_RX_QUEUE_INFO0_UPD_SOFT_REORDER_EN, + !!(cmd->upd0 & HAL_REO_CMD_UPD0_SOFT_REORDER_EN)) | + FIELD_PREP(HAL_REO_UPD_RX_QUEUE_INFO0_UPD_AC, + !!(cmd->upd0 & HAL_REO_CMD_UPD0_AC)) | + FIELD_PREP(HAL_REO_UPD_RX_QUEUE_INFO0_UPD_BAR, + !!(cmd->upd0 & HAL_REO_CMD_UPD0_BAR)) | + FIELD_PREP(HAL_REO_UPD_RX_QUEUE_INFO0_UPD_RETRY, + !!(cmd->upd0 & HAL_REO_CMD_UPD0_RETRY)) | + FIELD_PREP(HAL_REO_UPD_RX_QUEUE_INFO0_UPD_CHECK_2K_MODE, + !!(cmd->upd0 & HAL_REO_CMD_UPD0_CHECK_2K_MODE)) | + FIELD_PREP(HAL_REO_UPD_RX_QUEUE_INFO0_UPD_OOR_MODE, + !!(cmd->upd0 & HAL_REO_CMD_UPD0_OOR_MODE)) | + FIELD_PREP(HAL_REO_UPD_RX_QUEUE_INFO0_UPD_BA_WINDOW_SIZE, + !!(cmd->upd0 & HAL_REO_CMD_UPD0_BA_WINDOW_SIZE)) | + FIELD_PREP(HAL_REO_UPD_RX_QUEUE_INFO0_UPD_PN_CHECK, + !!(cmd->upd0 & HAL_REO_CMD_UPD0_PN_CHECK)) | + FIELD_PREP(HAL_REO_UPD_RX_QUEUE_INFO0_UPD_EVEN_PN, + !!(cmd->upd0 & HAL_REO_CMD_UPD0_EVEN_PN)) | + FIELD_PREP(HAL_REO_UPD_RX_QUEUE_INFO0_UPD_UNEVEN_PN, + !!(cmd->upd0 & HAL_REO_CMD_UPD0_UNEVEN_PN)) | + FIELD_PREP(HAL_REO_UPD_RX_QUEUE_INFO0_UPD_PN_HANDLE_ENABLE, + !!(cmd->upd0 & HAL_REO_CMD_UPD0_PN_HANDLE_ENABLE)) | + FIELD_PREP(HAL_REO_UPD_RX_QUEUE_INFO0_UPD_PN_SIZE, + !!(cmd->upd0 & HAL_REO_CMD_UPD0_PN_SIZE)) | + FIELD_PREP(HAL_REO_UPD_RX_QUEUE_INFO0_UPD_IGNORE_AMPDU_FLG, + !!(cmd->upd0 & HAL_REO_CMD_UPD0_IGNORE_AMPDU_FLG)) | + FIELD_PREP(HAL_REO_UPD_RX_QUEUE_INFO0_UPD_SVLD, + !!(cmd->upd0 & HAL_REO_CMD_UPD0_SVLD)) | + FIELD_PREP(HAL_REO_UPD_RX_QUEUE_INFO0_UPD_SSN, + !!(cmd->upd0 & HAL_REO_CMD_UPD0_SSN)) | + FIELD_PREP(HAL_REO_UPD_RX_QUEUE_INFO0_UPD_SEQ_2K_ERR, + !!(cmd->upd0 & HAL_REO_CMD_UPD0_SEQ_2K_ERR)) | + FIELD_PREP(HAL_REO_UPD_RX_QUEUE_INFO0_UPD_PN_VALID, + !!(cmd->upd0 & HAL_REO_CMD_UPD0_PN_VALID)) | + FIELD_PREP(HAL_REO_UPD_RX_QUEUE_INFO0_UPD_PN, + !!(cmd->upd0 & HAL_REO_CMD_UPD0_PN)); + + desc->info1 = + FIELD_PREP(HAL_REO_UPD_RX_QUEUE_INFO1_RX_QUEUE_NUMBER, + cmd->rx_queue_num) | + FIELD_PREP(HAL_REO_UPD_RX_QUEUE_INFO1_VLD, + !!(cmd->upd1 & HAL_REO_CMD_UPD1_VLD)) | + FIELD_PREP(HAL_REO_UPD_RX_QUEUE_INFO1_ASSOC_LNK_DESC_COUNTER, + FIELD_GET(HAL_REO_CMD_UPD1_ALDC, cmd->upd1)) | + FIELD_PREP(HAL_REO_UPD_RX_QUEUE_INFO1_DIS_DUP_DETECTION, + !!(cmd->upd1 & HAL_REO_CMD_UPD1_DIS_DUP_DETECTION)) | + FIELD_PREP(HAL_REO_UPD_RX_QUEUE_INFO1_SOFT_REORDER_EN, + !!(cmd->upd1 & HAL_REO_CMD_UPD1_SOFT_REORDER_EN)) | + FIELD_PREP(HAL_REO_UPD_RX_QUEUE_INFO1_AC, + FIELD_GET(HAL_REO_CMD_UPD1_AC, cmd->upd1)) | + FIELD_PREP(HAL_REO_UPD_RX_QUEUE_INFO1_BAR, + !!(cmd->upd1 & HAL_REO_CMD_UPD1_BAR)) | + FIELD_PREP(HAL_REO_UPD_RX_QUEUE_INFO1_CHECK_2K_MODE, + !!(cmd->upd1 & HAL_REO_CMD_UPD1_CHECK_2K_MODE)) | + FIELD_PREP(HAL_REO_UPD_RX_QUEUE_INFO1_RETRY, + !!(cmd->upd1 & HAL_REO_CMD_UPD1_RETRY)) | + FIELD_PREP(HAL_REO_UPD_RX_QUEUE_INFO1_OOR_MODE, + !!(cmd->upd1 & HAL_REO_CMD_UPD1_OOR_MODE)) | + FIELD_PREP(HAL_REO_UPD_RX_QUEUE_INFO1_PN_CHECK, + !!(cmd->upd1 & HAL_REO_CMD_UPD1_PN_CHECK)) | + FIELD_PREP(HAL_REO_UPD_RX_QUEUE_INFO1_EVEN_PN, + !!(cmd->upd1 & HAL_REO_CMD_UPD1_EVEN_PN)) | + FIELD_PREP(HAL_REO_UPD_RX_QUEUE_INFO1_UNEVEN_PN, + !!(cmd->upd1 & HAL_REO_CMD_UPD1_UNEVEN_PN)) | + FIELD_PREP(HAL_REO_UPD_RX_QUEUE_INFO1_PN_HANDLE_ENABLE, + !!(cmd->upd1 & HAL_REO_CMD_UPD1_PN_HANDLE_ENABLE)) | + FIELD_PREP(HAL_REO_UPD_RX_QUEUE_INFO1_IGNORE_AMPDU_FLG, + !!(cmd->upd1 & HAL_REO_CMD_UPD1_IGNORE_AMPDU_FLG)); + + if (cmd->pn_size == 24) + cmd->pn_size = HAL_RX_REO_QUEUE_PN_SIZE_24; + else if (cmd->pn_size == 48) + cmd->pn_size = HAL_RX_REO_QUEUE_PN_SIZE_48; + else if (cmd->pn_size == 128) + cmd->pn_size = HAL_RX_REO_QUEUE_PN_SIZE_128; + + if (cmd->ba_window_size < 1) + cmd->ba_window_size = 1; + + if (cmd->ba_window_size == 1) + cmd->ba_window_size++; + + desc->info2 = + FIELD_PREP(HAL_REO_UPD_RX_QUEUE_INFO2_BA_WINDOW_SIZE, + cmd->ba_window_size - 1) | + FIELD_PREP(HAL_REO_UPD_RX_QUEUE_INFO2_PN_SIZE, cmd->pn_size) | + FIELD_PREP(HAL_REO_UPD_RX_QUEUE_INFO2_SVLD, + !!(cmd->upd2 & HAL_REO_CMD_UPD2_SVLD)) | + FIELD_PREP(HAL_REO_UPD_RX_QUEUE_INFO2_SSN, + FIELD_GET(HAL_REO_CMD_UPD2_SSN, cmd->upd2)) | + FIELD_PREP(HAL_REO_UPD_RX_QUEUE_INFO2_SEQ_2K_ERR, + !!(cmd->upd2 & HAL_REO_CMD_UPD2_SEQ_2K_ERR)) | + FIELD_PREP(HAL_REO_UPD_RX_QUEUE_INFO2_PN_ERR, + !!(cmd->upd2 & HAL_REO_CMD_UPD2_PN_ERR)); + + return FIELD_GET(HAL_REO_CMD_HDR_INFO0_CMD_NUMBER, desc->cmd.info0); +} + +int ath11k_hal_reo_cmd_send(struct ath11k_base *ab, struct hal_srng *srng, + enum hal_reo_cmd_type type, + struct ath11k_hal_reo_cmd *cmd) +{ + struct hal_tlv_hdr *reo_desc; + int ret; + + spin_lock_bh(&srng->lock); + + ath11k_hal_srng_access_begin(ab, srng); + reo_desc = (struct hal_tlv_hdr *)ath11k_hal_srng_src_get_next_entry(ab, srng); + if (!reo_desc) { + ret = -ENOBUFS; + goto out; + } + + switch (type) { + case HAL_REO_CMD_GET_QUEUE_STATS: + ret = ath11k_hal_reo_cmd_queue_stats(reo_desc, cmd); + break; + case HAL_REO_CMD_FLUSH_CACHE: + ret = ath11k_hal_reo_cmd_flush_cache(&ab->hal, reo_desc, cmd); + break; + case HAL_REO_CMD_UPDATE_RX_QUEUE: + ret = ath11k_hal_reo_cmd_update_rx_queue(reo_desc, cmd); + break; + case HAL_REO_CMD_FLUSH_QUEUE: + case HAL_REO_CMD_UNBLOCK_CACHE: + case HAL_REO_CMD_FLUSH_TIMEOUT_LIST: + ath11k_warn(ab, "Unsupported reo command %d\n", type); + ret = -ENOTSUPP; + break; + default: + ath11k_warn(ab, "Unknown reo command %d\n", type); + ret = -EINVAL; + break; + } + +out: + ath11k_hal_srng_access_end(ab, srng); + spin_unlock_bh(&srng->lock); + + return ret; +} + +void ath11k_hal_rx_buf_addr_info_set(void *desc, dma_addr_t paddr, + u32 cookie, u8 manager) +{ + struct ath11k_buffer_addr *binfo = (struct ath11k_buffer_addr *)desc; + u32 paddr_lo, paddr_hi; + + paddr_lo = lower_32_bits(paddr); + paddr_hi = upper_32_bits(paddr); + binfo->info0 = FIELD_PREP(BUFFER_ADDR_INFO0_ADDR, paddr_lo); + binfo->info1 = FIELD_PREP(BUFFER_ADDR_INFO1_ADDR, paddr_hi) | + FIELD_PREP(BUFFER_ADDR_INFO1_SW_COOKIE, cookie) | + FIELD_PREP(BUFFER_ADDR_INFO1_RET_BUF_MGR, manager); +} + +void ath11k_hal_rx_buf_addr_info_get(void *desc, dma_addr_t *paddr, + u32 *cookie, u8 *rbm) +{ + struct ath11k_buffer_addr *binfo = (struct ath11k_buffer_addr *)desc; + + *paddr = + (((u64)FIELD_GET(BUFFER_ADDR_INFO1_ADDR, binfo->info1)) << 32) | + FIELD_GET(BUFFER_ADDR_INFO0_ADDR, binfo->info0); + *cookie = FIELD_GET(BUFFER_ADDR_INFO1_SW_COOKIE, binfo->info1); + *rbm = FIELD_GET(BUFFER_ADDR_INFO1_RET_BUF_MGR, binfo->info1); +} + +void ath11k_hal_rx_msdu_link_info_get(void *link_desc, u32 *num_msdus, + struct hal_rx_msdu_meta *meta, + enum hal_rx_buf_return_buf_manager *rbm) +{ + struct hal_rx_msdu_link *link = (struct hal_rx_msdu_link *)link_desc; + struct hal_rx_msdu_details *msdu; + int i; + + *num_msdus = HAL_NUM_RX_MSDUS_PER_LINK_DESC; + + msdu = &link->msdu_link[0]; + *rbm = FIELD_GET(BUFFER_ADDR_INFO1_RET_BUF_MGR, + msdu->buf_addr_info.info1); + + for (i = 0; i < *num_msdus; i++) { + msdu = &link->msdu_link[i]; + + if (!FIELD_GET(BUFFER_ADDR_INFO0_ADDR, + msdu->buf_addr_info.info0)) { + *num_msdus = i; + break; + } + meta->msdu_len = FIELD_GET(RX_MSDU_DESC_INFO0_MSDU_LENGTH, + msdu->rx_msdu_info.info0); + meta->first = !!(msdu->rx_msdu_info.info0 & + RX_MSDU_DESC_INFO0_FIRST_MSDU_IN_MPDU); + meta->last = !!(msdu->rx_msdu_info.info0 & + RX_MSDU_DESC_INFO0_LAST_MSDU_IN_MPDU); + meta->continuation = !!(msdu->rx_msdu_info.info0 & + RX_MSDU_DESC_INFO0_MSDU_CONTINUATION); + meta->cookie = FIELD_GET(BUFFER_ADDR_INFO1_SW_COOKIE, + msdu->buf_addr_info.info1); + meta++; + } +} + +int ath11k_hal_desc_reo_parse_err(struct ath11k_base *ab, u32 *rx_desc, + dma_addr_t *paddr, u32 *desc_bank) +{ + struct hal_reo_dest_ring *desc = (struct hal_reo_dest_ring *)rx_desc; + enum hal_reo_dest_ring_push_reason push_reason; + enum hal_reo_dest_ring_error_code err_code; + + push_reason = FIELD_GET(HAL_REO_DEST_RING_INFO0_PUSH_REASON, + desc->info0); + err_code = FIELD_GET(HAL_REO_DEST_RING_INFO0_ERROR_CODE, + desc->info0); + ab->soc_stats.reo_error[err_code]++; + + if (push_reason != HAL_REO_DEST_RING_PUSH_REASON_ERR_DETECTED && + push_reason != HAL_REO_DEST_RING_PUSH_REASON_ROUTING_INSTRUCTION) { + ath11k_warn(ab, "expected error push reason code, received %d\n", + push_reason); + return -EINVAL; + } + + if (FIELD_GET(HAL_REO_DEST_RING_INFO0_BUFFER_TYPE, desc->info0) != + HAL_REO_DEST_RING_BUFFER_TYPE_LINK_DESC) { + ath11k_warn(ab, "expected buffer type link_desc"); + return -EINVAL; + } + + ath11k_hal_rx_reo_ent_paddr_get(ab, rx_desc, paddr, desc_bank); + + return 0; +} + +void ath11k_hal_rx_parse_dst_ring_desc(struct ath11k_base *ab, u32 *rx_desc, + struct hal_rx_meta_info *meta_info) +{ + struct hal_reo_dest_ring *desc = (struct hal_reo_dest_ring *)rx_desc; + struct rx_mpdu_desc *mpdu = &desc->rx_mpdu_info; + struct rx_msdu_desc *msdu = &desc->rx_msdu_info; + struct hal_rx_mpdu_meta *meta_mpdu = &meta_info->mpdu_meta; + struct hal_rx_msdu_meta *meta_msdu = &meta_info->msdu_meta; + + meta_info->push_reason = FIELD_GET(HAL_REO_DEST_RING_INFO0_PUSH_REASON, + desc->info0); + + meta_mpdu->msdu_cnt = FIELD_GET(RX_MPDU_DESC_INFO0_MSDU_COUNT, + mpdu->info0); + meta_mpdu->seq_num = FIELD_GET(RX_MPDU_DESC_INFO0_SEQ_NUM, mpdu->info0); + meta_mpdu->frag = !!(mpdu->info0 & RX_MPDU_DESC_INFO0_FRAG_FLAG); + meta_mpdu->retry = !!(mpdu->info0 & RX_MPDU_DESC_INFO0_MPDU_RETRY); + meta_mpdu->ampdu = !!(mpdu->info0 & RX_MPDU_DESC_INFO0_AMPDU_FLAG); + meta_mpdu->raw = !!(mpdu->info0 & RX_MPDU_DESC_INFO0_RAW_MPDU); + meta_mpdu->peer_meta = mpdu->meta_data; + + meta_msdu->cookie = FIELD_GET(BUFFER_ADDR_INFO1_SW_COOKIE, + desc->buf_addr_info.info1); + meta_msdu->msdu_len = FIELD_GET(RX_MSDU_DESC_INFO0_MSDU_LENGTH, + msdu->info0); + meta_msdu->first = + !!(msdu->info0 & RX_MSDU_DESC_INFO0_FIRST_MSDU_IN_MPDU); + meta_msdu->last = + !!(msdu->info0 & RX_MSDU_DESC_INFO0_LAST_MSDU_IN_MPDU); + meta_msdu->continuation = + !!(msdu->info0 & RX_MSDU_DESC_INFO0_MSDU_CONTINUATION); +} + +int ath11k_hal_wbm_desc_parse_err(struct ath11k_base *ab, void *desc, + struct hal_rx_wbm_rel_info *rel_info) +{ + struct hal_wbm_release_ring *wbm_desc = desc; + enum hal_wbm_rel_desc_type type; + enum hal_wbm_rel_src_module rel_src; + + type = FIELD_GET(HAL_WBM_RELEASE_INFO0_DESC_TYPE, + wbm_desc->info0); + /* We expect only WBM_REL buffer type */ + if (type != HAL_WBM_REL_DESC_TYPE_REL_MSDU) { + WARN_ON(1); + return -EINVAL; + } + + rel_src = FIELD_GET(HAL_WBM_RELEASE_INFO0_REL_SRC_MODULE, + wbm_desc->info0); + if (rel_src != HAL_WBM_REL_SRC_MODULE_RXDMA && + rel_src != HAL_WBM_REL_SRC_MODULE_REO) + return -EINVAL; + + if (FIELD_GET(BUFFER_ADDR_INFO1_RET_BUF_MGR, + wbm_desc->buf_addr_info.info1) != HAL_RX_BUF_RBM_SW3_BM) { + ab->soc_stats.invalid_rbm++; + return -EINVAL; + } + + rel_info->cookie = FIELD_GET(BUFFER_ADDR_INFO1_SW_COOKIE, + wbm_desc->buf_addr_info.info1); + rel_info->err_rel_src = rel_src; + if (rel_src == HAL_WBM_REL_SRC_MODULE_REO) { + rel_info->push_reason = + FIELD_GET(HAL_WBM_RELEASE_INFO0_REO_PUSH_REASON, + wbm_desc->info0); + rel_info->err_code = + FIELD_GET(HAL_WBM_RELEASE_INFO0_REO_ERROR_CODE, + wbm_desc->info0); + } else { + rel_info->push_reason = + FIELD_GET(HAL_WBM_RELEASE_INFO0_RXDMA_PUSH_REASON, + wbm_desc->info0); + rel_info->err_code = + FIELD_GET(HAL_WBM_RELEASE_INFO0_RXDMA_ERROR_CODE, + wbm_desc->info0); + } + + rel_info->first_msdu = FIELD_GET(HAL_WBM_RELEASE_INFO2_FIRST_MSDU, + wbm_desc->info2); + rel_info->last_msdu = FIELD_GET(HAL_WBM_RELEASE_INFO2_LAST_MSDU, + wbm_desc->info2); + return 0; +} + +void ath11k_hal_rx_reo_ent_paddr_get(struct ath11k_base *ab, void *desc, + dma_addr_t *paddr, u32 *desc_bank) +{ + struct ath11k_buffer_addr *buff_addr = desc; + + *paddr = ((u64)(FIELD_GET(BUFFER_ADDR_INFO1_ADDR, buff_addr->info1)) << 32) | + FIELD_GET(BUFFER_ADDR_INFO0_ADDR, buff_addr->info0); + + *desc_bank = FIELD_GET(BUFFER_ADDR_INFO1_SW_COOKIE, buff_addr->info1); +} + +void ath11k_hal_rx_msdu_link_desc_set(struct ath11k_base *ab, void *desc, + void *link_desc, + enum hal_wbm_rel_bm_act action) +{ + struct hal_wbm_release_ring *dst_desc = desc; + struct hal_wbm_release_ring *src_desc = link_desc; + + dst_desc->buf_addr_info = src_desc->buf_addr_info; + dst_desc->info0 |= FIELD_PREP(HAL_WBM_RELEASE_INFO0_REL_SRC_MODULE, + HAL_WBM_REL_SRC_MODULE_SW) | + FIELD_PREP(HAL_WBM_RELEASE_INFO0_BM_ACTION, action) | + FIELD_PREP(HAL_WBM_RELEASE_INFO0_DESC_TYPE, + HAL_WBM_REL_DESC_TYPE_MSDU_LINK); +} + +void ath11k_hal_reo_status_queue_stats(struct ath11k_base *ab, u32 *reo_desc, + struct hal_reo_status *status) +{ + struct hal_tlv_hdr *tlv = (struct hal_tlv_hdr *)reo_desc; + struct hal_reo_get_queue_stats_status *desc = + (struct hal_reo_get_queue_stats_status *)tlv->value; + + status->uniform_hdr.cmd_num = + FIELD_GET(HAL_REO_STATUS_HDR_INFO0_STATUS_NUM, + desc->hdr.info0); + status->uniform_hdr.cmd_status = + FIELD_GET(HAL_REO_STATUS_HDR_INFO0_EXEC_STATUS, + desc->hdr.info0); + + ath11k_dbg(ab, ATH11k_DBG_HAL, "Queue stats status:\n"); + ath11k_dbg(ab, ATH11k_DBG_HAL, "header: cmd_num %d status %d\n", + status->uniform_hdr.cmd_num, + status->uniform_hdr.cmd_status); + ath11k_dbg(ab, ATH11k_DBG_HAL, "ssn %ld cur_idx %ld\n", + FIELD_GET(HAL_REO_GET_QUEUE_STATS_STATUS_INFO0_SSN, + desc->info0), + FIELD_GET(HAL_REO_GET_QUEUE_STATS_STATUS_INFO0_CUR_IDX, + desc->info0)); + ath11k_dbg(ab, ATH11k_DBG_HAL, "pn = [%08x, %08x, %08x, %08x]\n", + desc->pn[0], desc->pn[1], desc->pn[2], desc->pn[3]); + ath11k_dbg(ab, ATH11k_DBG_HAL, "last_rx: enqueue_tstamp %08x dequeue_tstamp %08x\n", + desc->last_rx_enqueue_timestamp, + desc->last_rx_dequeue_timestamp); + ath11k_dbg(ab, ATH11k_DBG_HAL, "rx_bitmap [%08x %08x %08x %08x %08x %08x %08x %08x]\n", + desc->rx_bitmap[0], desc->rx_bitmap[1], desc->rx_bitmap[2], + desc->rx_bitmap[3], desc->rx_bitmap[4], desc->rx_bitmap[5], + desc->rx_bitmap[6], desc->rx_bitmap[7]); + ath11k_dbg(ab, ATH11k_DBG_HAL, "count: cur_mpdu %ld cur_msdu %ld\n", + FIELD_GET(HAL_REO_GET_QUEUE_STATS_STATUS_INFO1_MPDU_COUNT, + desc->info1), + FIELD_GET(HAL_REO_GET_QUEUE_STATS_STATUS_INFO1_MSDU_COUNT, + desc->info1)); + ath11k_dbg(ab, ATH11k_DBG_HAL, "fwd_timeout %ld fwd_bar %ld dup_count %ld\n", + FIELD_GET(HAL_REO_GET_QUEUE_STATS_STATUS_INFO2_TIMEOUT_COUNT, + desc->info2), + FIELD_GET(HAL_REO_GET_QUEUE_STATS_STATUS_INFO2_FDTB_COUNT, + desc->info2), + FIELD_GET(HAL_REO_GET_QUEUE_STATS_STATUS_INFO2_DUPLICATE_COUNT, + desc->info2)); + ath11k_dbg(ab, ATH11k_DBG_HAL, "frames_in_order %ld bar_rcvd %ld\n", + FIELD_GET(HAL_REO_GET_QUEUE_STATS_STATUS_INFO3_FIO_COUNT, + desc->info3), + FIELD_GET(HAL_REO_GET_QUEUE_STATS_STATUS_INFO3_BAR_RCVD_CNT, + desc->info3)); + ath11k_dbg(ab, ATH11k_DBG_HAL, "num_mpdus %d num_msdus %d total_bytes %d\n", + desc->num_mpdu_frames, desc->num_msdu_frames, + desc->total_bytes); + ath11k_dbg(ab, ATH11k_DBG_HAL, "late_rcvd %ld win_jump_2k %ld hole_cnt %ld\n", + FIELD_GET(HAL_REO_GET_QUEUE_STATS_STATUS_INFO4_LATE_RX_MPDU, + desc->info4), + FIELD_GET(HAL_REO_GET_QUEUE_STATS_STATUS_INFO4_WINDOW_JMP2K, + desc->info4), + FIELD_GET(HAL_REO_GET_QUEUE_STATS_STATUS_INFO4_HOLE_COUNT, + desc->info4)); + ath11k_dbg(ab, ATH11k_DBG_HAL, "looping count %ld\n", + FIELD_GET(HAL_REO_GET_QUEUE_STATS_STATUS_INFO5_LOOPING_CNT, + desc->info5)); +} + +int ath11k_hal_reo_process_status(u8 *reo_desc, u8 *status) +{ + struct hal_tlv_hdr *tlv = (struct hal_tlv_hdr *)reo_desc; + struct hal_reo_status_hdr *hdr; + + hdr = (struct hal_reo_status_hdr *)tlv->value; + *status = FIELD_GET(HAL_REO_STATUS_HDR_INFO0_EXEC_STATUS, hdr->info0); + + return FIELD_GET(HAL_REO_STATUS_HDR_INFO0_STATUS_NUM, hdr->info0); +} + +void ath11k_hal_reo_flush_queue_status(struct ath11k_base *ab, u32 *reo_desc, + struct hal_reo_status *status) +{ + struct hal_tlv_hdr *tlv = (struct hal_tlv_hdr *)reo_desc; + struct hal_reo_flush_queue_status *desc = + (struct hal_reo_flush_queue_status *)tlv->value; + + status->uniform_hdr.cmd_num = + FIELD_GET(HAL_REO_STATUS_HDR_INFO0_STATUS_NUM, + desc->hdr.info0); + status->uniform_hdr.cmd_status = + FIELD_GET(HAL_REO_STATUS_HDR_INFO0_EXEC_STATUS, + desc->hdr.info0); + status->u.flush_queue.err_detected = + FIELD_GET(HAL_REO_FLUSH_QUEUE_INFO0_ERR_DETECTED, + desc->info0); +} + +void ath11k_hal_reo_flush_cache_status(struct ath11k_base *ab, u32 *reo_desc, + struct hal_reo_status *status) +{ + struct ath11k_hal *hal = &ab->hal; + struct hal_tlv_hdr *tlv = (struct hal_tlv_hdr *)reo_desc; + struct hal_reo_flush_cache_status *desc = + (struct hal_reo_flush_cache_status *)tlv->value; + + status->uniform_hdr.cmd_num = + FIELD_GET(HAL_REO_STATUS_HDR_INFO0_STATUS_NUM, + desc->hdr.info0); + status->uniform_hdr.cmd_status = + FIELD_GET(HAL_REO_STATUS_HDR_INFO0_EXEC_STATUS, + desc->hdr.info0); + + status->u.flush_cache.err_detected = + FIELD_GET(HAL_REO_FLUSH_CACHE_STATUS_INFO0_IS_ERR, + desc->info0); + status->u.flush_cache.err_code = + FIELD_GET(HAL_REO_FLUSH_CACHE_STATUS_INFO0_BLOCK_ERR_CODE, + desc->info0); + if (!status->u.flush_cache.err_code) + hal->avail_blk_resource |= BIT(hal->current_blk_index); + + status->u.flush_cache.cache_controller_flush_status_hit = + FIELD_GET(HAL_REO_FLUSH_CACHE_STATUS_INFO0_FLUSH_STATUS_HIT, + desc->info0); + + status->u.flush_cache.cache_controller_flush_status_desc_type = + FIELD_GET(HAL_REO_FLUSH_CACHE_STATUS_INFO0_FLUSH_DESC_TYPE, + desc->info0); + status->u.flush_cache.cache_controller_flush_status_client_id = + FIELD_GET(HAL_REO_FLUSH_CACHE_STATUS_INFO0_FLUSH_CLIENT_ID, + desc->info0); + status->u.flush_cache.cache_controller_flush_status_err = + FIELD_GET(HAL_REO_FLUSH_CACHE_STATUS_INFO0_FLUSH_ERR, + desc->info0); + status->u.flush_cache.cache_controller_flush_status_cnt = + FIELD_GET(HAL_REO_FLUSH_CACHE_STATUS_INFO0_FLUSH_COUNT, + desc->info0); +} + +void ath11k_hal_reo_unblk_cache_status(struct ath11k_base *ab, u32 *reo_desc, + struct hal_reo_status *status) +{ + struct ath11k_hal *hal = &ab->hal; + struct hal_tlv_hdr *tlv = (struct hal_tlv_hdr *)reo_desc; + struct hal_reo_unblock_cache_status *desc = + (struct hal_reo_unblock_cache_status *)tlv->value; + + status->uniform_hdr.cmd_num = + FIELD_GET(HAL_REO_STATUS_HDR_INFO0_STATUS_NUM, + desc->hdr.info0); + status->uniform_hdr.cmd_status = + FIELD_GET(HAL_REO_STATUS_HDR_INFO0_EXEC_STATUS, + desc->hdr.info0); + + status->u.unblock_cache.err_detected = + FIELD_GET(HAL_REO_UNBLOCK_CACHE_STATUS_INFO0_IS_ERR, + desc->info0); + status->u.unblock_cache.unblock_type = + FIELD_GET(HAL_REO_UNBLOCK_CACHE_STATUS_INFO0_TYPE, + desc->info0); + + if (!status->u.unblock_cache.err_detected && + status->u.unblock_cache.unblock_type == + HAL_REO_STATUS_UNBLOCK_BLOCKING_RESOURCE) + hal->avail_blk_resource &= ~BIT(hal->current_blk_index); +} + +void ath11k_hal_reo_flush_timeout_list_status(struct ath11k_base *ab, + u32 *reo_desc, + struct hal_reo_status *status) +{ + struct hal_tlv_hdr *tlv = (struct hal_tlv_hdr *)reo_desc; + struct hal_reo_flush_timeout_list_status *desc = + (struct hal_reo_flush_timeout_list_status *)tlv->value; + + status->uniform_hdr.cmd_num = + FIELD_GET(HAL_REO_STATUS_HDR_INFO0_STATUS_NUM, + desc->hdr.info0); + status->uniform_hdr.cmd_status = + FIELD_GET(HAL_REO_STATUS_HDR_INFO0_EXEC_STATUS, + desc->hdr.info0); + + status->u.timeout_list.err_detected = + FIELD_GET(HAL_REO_FLUSH_TIMEOUT_STATUS_INFO0_IS_ERR, + desc->info0); + status->u.timeout_list.list_empty = + FIELD_GET(HAL_REO_FLUSH_TIMEOUT_STATUS_INFO0_LIST_EMPTY, + desc->info0); + + status->u.timeout_list.release_desc_cnt = + FIELD_GET(HAL_REO_FLUSH_TIMEOUT_STATUS_INFO1_REL_DESC_COUNT, + desc->info1); + status->u.timeout_list.fwd_buf_cnt = + FIELD_GET(HAL_REO_FLUSH_TIMEOUT_STATUS_INFO1_FWD_BUF_COUNT, + desc->info1); +} + +void ath11k_hal_reo_desc_thresh_reached_status(struct ath11k_base *ab, + u32 *reo_desc, + struct hal_reo_status *status) +{ + struct hal_tlv_hdr *tlv = (struct hal_tlv_hdr *)reo_desc; + struct hal_reo_desc_thresh_reached_status *desc = + (struct hal_reo_desc_thresh_reached_status *)tlv->value; + + status->uniform_hdr.cmd_num = + FIELD_GET(HAL_REO_STATUS_HDR_INFO0_STATUS_NUM, + desc->hdr.info0); + status->uniform_hdr.cmd_status = + FIELD_GET(HAL_REO_STATUS_HDR_INFO0_EXEC_STATUS, + desc->hdr.info0); + + status->u.desc_thresh_reached.threshold_idx = + FIELD_GET(HAL_REO_DESC_THRESH_STATUS_INFO0_THRESH_INDEX, + desc->info0); + + status->u.desc_thresh_reached.link_desc_counter0 = + FIELD_GET(HAL_REO_DESC_THRESH_STATUS_INFO1_LINK_DESC_COUNTER0, + desc->info1); + + status->u.desc_thresh_reached.link_desc_counter1 = + FIELD_GET(HAL_REO_DESC_THRESH_STATUS_INFO2_LINK_DESC_COUNTER1, + desc->info2); + + status->u.desc_thresh_reached.link_desc_counter2 = + FIELD_GET(HAL_REO_DESC_THRESH_STATUS_INFO3_LINK_DESC_COUNTER2, + desc->info3); + + status->u.desc_thresh_reached.link_desc_counter_sum = + FIELD_GET(HAL_REO_DESC_THRESH_STATUS_INFO4_LINK_DESC_COUNTER_SUM, + desc->info4); +} + +void ath11k_hal_reo_update_rx_reo_queue_status(struct ath11k_base *ab, + u32 *reo_desc, + struct hal_reo_status *status) +{ + struct hal_tlv_hdr *tlv = (struct hal_tlv_hdr *)reo_desc; + struct hal_reo_status_hdr *desc = + (struct hal_reo_status_hdr *)tlv->value; + + status->uniform_hdr.cmd_num = + FIELD_GET(HAL_REO_STATUS_HDR_INFO0_STATUS_NUM, + desc->info0); + status->uniform_hdr.cmd_status = + FIELD_GET(HAL_REO_STATUS_HDR_INFO0_EXEC_STATUS, + desc->info0); +} + +u32 ath11k_hal_reo_qdesc_size(u32 ba_window_size, u8 tid) +{ + u32 num_ext_desc; + + if (ba_window_size <= 1) { + if (tid != HAL_DESC_REO_NON_QOS_TID) + num_ext_desc = 1; + else + num_ext_desc = 0; + } else if (ba_window_size <= 105) { + num_ext_desc = 1; + } else if (ba_window_size <= 210) { + num_ext_desc = 2; + } else { + num_ext_desc = 3; + } + + return sizeof(struct hal_rx_reo_queue) + + (num_ext_desc * sizeof(struct hal_rx_reo_queue_ext)); +} + +void ath11k_hal_reo_qdesc_setup(void *vaddr, int tid, u32 ba_window_size, + u32 start_seq) +{ + struct hal_rx_reo_queue *qdesc = (struct hal_rx_reo_queue *)vaddr; + struct hal_rx_reo_queue_ext *ext_desc; + + memset(qdesc, 0, sizeof(*qdesc)); + + ath11k_hal_reo_set_desc_hdr(&qdesc->desc_hdr, HAL_DESC_REO_OWNED, + HAL_DESC_REO_QUEUE_DESC, + REO_QUEUE_DESC_MAGIC_DEBUG_PATTERN_0); + + qdesc->rx_queue_num = FIELD_PREP(HAL_RX_REO_QUEUE_RX_QUEUE_NUMBER, tid); + + qdesc->info0 = + FIELD_PREP(HAL_RX_REO_QUEUE_INFO0_VLD, 1) | + FIELD_PREP(HAL_RX_REO_QUEUE_INFO0_ASSOC_LNK_DESC_COUNTER, 1) | + FIELD_PREP(HAL_RX_REO_QUEUE_INFO0_AC, ath11k_tid_to_ac(tid)); + + if (ba_window_size < 1) + ba_window_size = 1; + + if (ba_window_size == 1 && tid != HAL_DESC_REO_NON_QOS_TID) + ba_window_size++; + + if (ba_window_size == 1) + qdesc->info0 |= FIELD_PREP(HAL_RX_REO_QUEUE_INFO0_RETRY, 1); + + qdesc->info0 |= FIELD_PREP(HAL_RX_REO_QUEUE_INFO0_BA_WINDOW_SIZE, + ba_window_size - 1); + + /* TODO: Set Ignore ampdu flags based on BA window size and/or + * AMPDU capabilities + */ + qdesc->info0 |= FIELD_PREP(HAL_RX_REO_QUEUE_INFO0_IGNORE_AMPDU_FLG, 1); + + qdesc->info1 |= FIELD_PREP(HAL_RX_REO_QUEUE_INFO1_SVLD, 0); + + if (start_seq <= 0xfff) + qdesc->info1 = FIELD_PREP(HAL_RX_REO_QUEUE_INFO1_SSN, + start_seq); + + if (tid == HAL_DESC_REO_NON_QOS_TID) + return; + + ext_desc = qdesc->ext_desc; + + /* TODO: HW queue descriptors are currently allocated for max BA + * window size for all QOS TIDs so that same descriptor can be used + * later when ADDBA request is recevied. This should be changed to + * allocate HW queue descriptors based on BA window size being + * negotiated (0 for non BA cases), and reallocate when BA window + * size changes and also send WMI message to FW to change the REO + * queue descriptor in Rx peer entry as part of dp_rx_tid_update. + */ + memset(ext_desc, 0, 3 * sizeof(*ext_desc)); + ath11k_hal_reo_set_desc_hdr(&ext_desc->desc_hdr, HAL_DESC_REO_OWNED, + HAL_DESC_REO_QUEUE_EXT_DESC, + REO_QUEUE_DESC_MAGIC_DEBUG_PATTERN_1); + ext_desc++; + ath11k_hal_reo_set_desc_hdr(&ext_desc->desc_hdr, HAL_DESC_REO_OWNED, + HAL_DESC_REO_QUEUE_EXT_DESC, + REO_QUEUE_DESC_MAGIC_DEBUG_PATTERN_2); + ext_desc++; + ath11k_hal_reo_set_desc_hdr(&ext_desc->desc_hdr, HAL_DESC_REO_OWNED, + HAL_DESC_REO_QUEUE_EXT_DESC, + REO_QUEUE_DESC_MAGIC_DEBUG_PATTERN_3); +} + +void ath11k_hal_reo_init_cmd_ring(struct ath11k_base *ab, + struct hal_srng *srng) +{ + struct hal_srng_params params; + struct hal_tlv_hdr *tlv; + struct hal_reo_get_queue_stats *desc; + int i, cmd_num = 1; + int entry_size; + u8 *entry; + + memset(¶ms, 0, sizeof(params)); + + entry_size = ath11k_hal_srng_get_entrysize(HAL_REO_CMD); + ath11k_hal_srng_get_params(ab, srng, ¶ms); + entry = (u8 *)params.ring_base_vaddr; + + for (i = 0; i < params.num_entries; i++) { + tlv = (struct hal_tlv_hdr *)entry; + desc = (struct hal_reo_get_queue_stats *)tlv->value; + desc->cmd.info0 = + FIELD_PREP(HAL_REO_CMD_HDR_INFO0_CMD_NUMBER, cmd_num++); + entry += entry_size; + } +} + +void ath11k_hal_reo_hw_setup(struct ath11k_base *ab) +{ + u32 reo_base = HAL_SEQ_WCSS_UMAC_REO_REG; + u32 val; + + val = ath11k_ahb_read32(ab, reo_base + HAL_REO1_GEN_ENABLE); + + val &= ~HAL_REO1_GEN_ENABLE_FRAG_DST_RING; + val |= FIELD_PREP(HAL_REO1_GEN_ENABLE_FRAG_DST_RING, + HAL_SRNG_RING_ID_REO2SW1) | + FIELD_PREP(HAL_REO1_GEN_ENABLE_AGING_LIST_ENABLE, 1) | + FIELD_PREP(HAL_REO1_GEN_ENABLE_AGING_FLUSH_ENABLE, 1); + ath11k_ahb_write32(ab, reo_base + HAL_REO1_GEN_ENABLE, val); + + ath11k_ahb_write32(ab, reo_base + HAL_REO1_AGING_THRESH_IX_0, + HAL_DEFAULT_REO_TIMEOUT_USEC); + ath11k_ahb_write32(ab, reo_base + HAL_REO1_AGING_THRESH_IX_1, + HAL_DEFAULT_REO_TIMEOUT_USEC); + ath11k_ahb_write32(ab, reo_base + HAL_REO1_AGING_THRESH_IX_2, + HAL_DEFAULT_REO_TIMEOUT_USEC); + ath11k_ahb_write32(ab, reo_base + HAL_REO1_AGING_THRESH_IX_3, + HAL_DEFAULT_REO_TIMEOUT_USEC); +} + +static enum hal_rx_mon_status +ath11k_hal_rx_parse_mon_status_tlv(struct ath11k_base *ab, + struct hal_rx_mon_ppdu_info *ppdu_info, + u32 tlv_tag, u8 *tlv_data) +{ + u32 info0, info1; + + switch (tlv_tag) { + case HAL_RX_PPDU_START: { + struct hal_rx_ppdu_start *ppdu_start = + (struct hal_rx_ppdu_start *)tlv_data; + + ppdu_info->ppdu_id = + FIELD_GET(HAL_RX_PPDU_START_INFO0_PPDU_ID, + __le32_to_cpu(ppdu_start->info0)); + ppdu_info->chan_num = __le32_to_cpu(ppdu_start->chan_num); + ppdu_info->ppdu_ts = __le32_to_cpu(ppdu_start->ppdu_start_ts); + break; + } + case HAL_RX_PPDU_END_USER_STATS: { + struct hal_rx_ppdu_end_user_stats *eu_stats = + (struct hal_rx_ppdu_end_user_stats *)tlv_data; + + info0 = __le32_to_cpu(eu_stats->info0); + info1 = __le32_to_cpu(eu_stats->info1); + + ppdu_info->tid = + ffs(FIELD_GET(HAL_RX_PPDU_END_USER_STATS_INFO6_TID_BITMAP, + __le32_to_cpu(eu_stats->info6))) - 1; + ppdu_info->tcp_msdu_count = + FIELD_GET(HAL_RX_PPDU_END_USER_STATS_INFO4_TCP_MSDU_CNT, + __le32_to_cpu(eu_stats->info4)); + ppdu_info->udp_msdu_count = + FIELD_GET(HAL_RX_PPDU_END_USER_STATS_INFO4_UDP_MSDU_CNT, + __le32_to_cpu(eu_stats->info4)); + ppdu_info->other_msdu_count = + FIELD_GET(HAL_RX_PPDU_END_USER_STATS_INFO5_OTHER_MSDU_CNT, + __le32_to_cpu(eu_stats->info5)); + ppdu_info->tcp_ack_msdu_count = + FIELD_GET(HAL_RX_PPDU_END_USER_STATS_INFO5_TCP_ACK_MSDU_CNT, + __le32_to_cpu(eu_stats->info5)); + ppdu_info->preamble_type = + FIELD_GET(HAL_RX_PPDU_END_USER_STATS_INFO1_PKT_TYPE, info1); + ppdu_info->num_mpdu_fcs_ok = + FIELD_GET(HAL_RX_PPDU_END_USER_STATS_INFO1_MPDU_CNT_FCS_OK, + info1); + ppdu_info->num_mpdu_fcs_err = + FIELD_GET(HAL_RX_PPDU_END_USER_STATS_INFO0_MPDU_CNT_FCS_ERR, + info0); + break; + } + case HAL_PHYRX_HT_SIG: { + struct hal_rx_ht_sig_info *ht_sig = + (struct hal_rx_ht_sig_info *)tlv_data; + + info0 = __le32_to_cpu(ht_sig->info0); + info1 = __le32_to_cpu(ht_sig->info1); + + ppdu_info->mcs = FIELD_GET(HAL_RX_HT_SIG_INFO_INFO0_MCS, info0); + ppdu_info->bw = FIELD_GET(HAL_RX_HT_SIG_INFO_INFO0_BW, info0); + ppdu_info->is_stbc = FIELD_GET(HAL_RX_HT_SIG_INFO_INFO1_STBC, + info1); + ppdu_info->ldpc = FIELD_GET(HAL_RX_HT_SIG_INFO_INFO1_FEC_CODING, info1); + ppdu_info->gi = info1 & HAL_RX_HT_SIG_INFO_INFO1_GI; + + switch (ppdu_info->mcs) { + case 0 ... 7: + ppdu_info->nss = 1; + break; + case 8 ... 15: + ppdu_info->nss = 2; + break; + case 16 ... 23: + ppdu_info->nss = 3; + break; + case 24 ... 31: + ppdu_info->nss = 4; + break; + } + + if (ppdu_info->nss > 1) + ppdu_info->mcs = ppdu_info->mcs % 8; + + ppdu_info->reception_type = HAL_RX_RECEPTION_TYPE_SU; + break; + } + case HAL_PHYRX_L_SIG_B: { + struct hal_rx_lsig_b_info *lsigb = + (struct hal_rx_lsig_b_info *)tlv_data; + + ppdu_info->rate = FIELD_GET(HAL_RX_LSIG_B_INFO_INFO0_RATE, + __le32_to_cpu(lsigb->info0)); + ppdu_info->reception_type = HAL_RX_RECEPTION_TYPE_SU; + break; + } + case HAL_PHYRX_L_SIG_A: { + struct hal_rx_lsig_a_info *lsiga = + (struct hal_rx_lsig_a_info *)tlv_data; + + ppdu_info->rate = FIELD_GET(HAL_RX_LSIG_A_INFO_INFO0_RATE, + __le32_to_cpu(lsiga->info0)); + ppdu_info->reception_type = HAL_RX_RECEPTION_TYPE_SU; + break; + } + case HAL_PHYRX_VHT_SIG_A: { + struct hal_rx_vht_sig_a_info *vht_sig = + (struct hal_rx_vht_sig_a_info *)tlv_data; + u32 nsts; + u32 group_id; + + info0 = __le32_to_cpu(vht_sig->info0); + info1 = __le32_to_cpu(vht_sig->info1); + + ppdu_info->ldpc = FIELD_GET(HAL_RX_VHT_SIG_A_INFO_INFO1_SU_MU_CODING, + info0); + ppdu_info->mcs = FIELD_GET(HAL_RX_VHT_SIG_A_INFO_INFO1_MCS, + info1); + ppdu_info->gi = + FIELD_GET(HAL_RX_VHT_SIG_A_INFO_INFO1_GI_SETTING, + info1); + ppdu_info->is_stbc = info0 & HAL_RX_VHT_SIG_A_INFO_INFO0_STBC; + nsts = FIELD_GET(HAL_RX_VHT_SIG_A_INFO_INFO0_NSTS, info0); + if (ppdu_info->is_stbc && nsts > 0) + nsts = ((nsts + 1) >> 1) - 1; + + ppdu_info->nss = (nsts & VHT_SIG_SU_NSS_MASK) + 1; + ppdu_info->bw = FIELD_GET(HAL_RX_VHT_SIG_A_INFO_INFO0_BW, + info0); + ppdu_info->beamformed = info1 & + HAL_RX_VHT_SIG_A_INFO_INFO1_BEAMFORMED; + group_id = FIELD_GET(HAL_RX_VHT_SIG_A_INFO_INFO0_GROUP_ID, + info0); + if (group_id == 0 || group_id == 63) + ppdu_info->reception_type = HAL_RX_RECEPTION_TYPE_SU; + else + ppdu_info->reception_type = + HAL_RX_RECEPTION_TYPE_MU_MIMO; + break; + } + case HAL_PHYRX_HE_SIG_A_SU: { + struct hal_rx_he_sig_a_su_info *he_sig_a = + (struct hal_rx_he_sig_a_su_info *)tlv_data; + u32 nsts, cp_ltf, dcm; + + info0 = __le32_to_cpu(he_sig_a->info0); + info1 = __le32_to_cpu(he_sig_a->info1); + + ppdu_info->mcs = + FIELD_GET(HAL_RX_HE_SIG_A_SU_INFO_INFO0_TRANSMIT_MCS, + info0); + ppdu_info->bw = + FIELD_GET(HAL_RX_HE_SIG_A_SU_INFO_INFO0_TRANSMIT_BW, + info0); + ppdu_info->ldpc = FIELD_GET(HAL_RX_HE_SIG_A_SU_INFO_INFO1_CODING, info0); + ppdu_info->is_stbc = info1 & + HAL_RX_HE_SIG_A_SU_INFO_INFO1_STBC; + ppdu_info->beamformed = info1 & + HAL_RX_HE_SIG_A_SU_INFO_INFO1_TXBF; + dcm = info0 & HAL_RX_HE_SIG_A_SU_INFO_INFO0_DCM; + cp_ltf = FIELD_GET(HAL_RX_HE_SIG_A_SU_INFO_INFO0_CP_LTF_SIZE, + info0); + nsts = FIELD_GET(HAL_RX_HE_SIG_A_SU_INFO_INFO0_NSTS, info0); + + switch (cp_ltf) { + case 0: + case 1: + ppdu_info->gi = HAL_RX_GI_0_8_US; + break; + case 2: + ppdu_info->gi = HAL_RX_GI_1_6_US; + break; + case 3: + if (dcm && ppdu_info->is_stbc) + ppdu_info->gi = HAL_RX_GI_0_8_US; + else + ppdu_info->gi = HAL_RX_GI_3_2_US; + break; + } + + ppdu_info->nss = nsts + 1; + ppdu_info->reception_type = HAL_RX_RECEPTION_TYPE_SU; + break; + } + case HAL_PHYRX_HE_SIG_A_MU_DL: { + struct hal_rx_he_sig_a_mu_dl_info *he_sig_a_mu_dl = + (struct hal_rx_he_sig_a_mu_dl_info *)tlv_data; + + u32 cp_ltf; + + info0 = __le32_to_cpu(he_sig_a_mu_dl->info0); + info1 = __le32_to_cpu(he_sig_a_mu_dl->info1); + + ppdu_info->bw = + FIELD_GET(HAL_RX_HE_SIG_A_MU_DL_INFO_INFO0_TRANSMIT_BW, + info0); + cp_ltf = FIELD_GET(HAL_RX_HE_SIG_A_MU_DL_INFO_INFO0_CP_LTF_SIZE, + info0); + + switch (cp_ltf) { + case 0: + case 1: + ppdu_info->gi = HAL_RX_GI_0_8_US; + break; + case 2: + ppdu_info->gi = HAL_RX_GI_1_6_US; + break; + case 3: + ppdu_info->gi = HAL_RX_GI_3_2_US; + break; + } + + ppdu_info->is_stbc = info1 & + HAL_RX_HE_SIG_A_MU_DL_INFO_INFO1_STBC; + ppdu_info->reception_type = HAL_RX_RECEPTION_TYPE_MU_MIMO; + break; + } + case HAL_PHYRX_HE_SIG_B1_MU: { + /* TODO: Check if resource unit(RU) allocation stats + * are required + */ + ppdu_info->reception_type = HAL_RX_RECEPTION_TYPE_MU_MIMO; + break; + } + case HAL_PHYRX_HE_SIG_B2_MU: { + struct hal_rx_he_sig_b2_mu_info *he_sig_b2_mu = + (struct hal_rx_he_sig_b2_mu_info *)tlv_data; + + info0 = __le32_to_cpu(he_sig_b2_mu->info0); + + ppdu_info->mcs = + FIELD_GET(HAL_RX_HE_SIG_B2_MU_INFO_INFO0_STA_MCS, + info0); + ppdu_info->nss = + FIELD_GET(HAL_RX_HE_SIG_B2_MU_INFO_INFO0_STA_NSTS, + info0) + 1; + ppdu_info->ldpc = FIELD_GET(HAL_RX_HE_SIG_B2_MU_INFO_INFO0_STA_CODING, + info0); + break; + } + case HAL_PHYRX_HE_SIG_B2_OFDMA: { + struct hal_rx_he_sig_b2_ofdma_info *he_sig_b2_ofdma = + (struct hal_rx_he_sig_b2_ofdma_info *)tlv_data; + + info0 = __le32_to_cpu(he_sig_b2_ofdma->info0); + + ppdu_info->mcs = + FIELD_GET(HAL_RX_HE_SIG_B2_OFDMA_INFO_INFO0_STA_MCS, + info0); + ppdu_info->nss = + FIELD_GET(HAL_RX_HE_SIG_B2_OFDMA_INFO_INFO0_STA_NSTS, + info0) + 1; + ppdu_info->beamformed = + info0 & + HAL_RX_HE_SIG_B2_OFDMA_INFO_INFO0_STA_TXBF; + ppdu_info->ldpc = FIELD_GET(HAL_RX_HE_SIG_B2_OFDMA_INFO_INFO0_STA_CODING, + info0); + ppdu_info->reception_type = HAL_RX_RECEPTION_TYPE_MU_OFDMA; + break; + } + case HAL_PHYRX_RSSI_LEGACY: { + struct hal_rx_phyrx_rssi_legacy_info *rssi = + (struct hal_rx_phyrx_rssi_legacy_info *)tlv_data; + + /* TODO: Please note that the combined rssi will not be accurate + * in MU case. Rssi in MU needs to be retrieved from + * PHYRX_OTHER_RECEIVE_INFO TLV. + */ + ppdu_info->rssi_comb = + FIELD_GET(HAL_RX_PHYRX_RSSI_LEGACY_INFO_INFO1_RSSI_COMB, + __le32_to_cpu(rssi->info0)); + break; + } + case HAL_RX_MPDU_START: { + struct hal_rx_mpdu_info *mpdu_info = + (struct hal_rx_mpdu_info *)tlv_data; + u16 peer_id; + + peer_id = FIELD_GET(HAL_RX_MPDU_INFO_INFO0_PEERID, + __le32_to_cpu(mpdu_info->info0)); + if (peer_id) + ppdu_info->peer_id = peer_id; + break; + } + case HAL_RXPCU_PPDU_END_INFO: { + struct hal_rx_ppdu_end_duration *ppdu_rx_duration = + (struct hal_rx_ppdu_end_duration *)tlv_data; + ppdu_info->rx_duration = + FIELD_GET(HAL_RX_PPDU_END_DURATION, + __le32_to_cpu(ppdu_rx_duration->info0)); + break; + } + case HAL_DUMMY: + return HAL_RX_MON_STATUS_BUF_DONE; + case HAL_RX_PPDU_END_STATUS_DONE: + case 0: + return HAL_RX_MON_STATUS_PPDU_DONE; + default: + break; + } + + return HAL_RX_MON_STATUS_PPDU_NOT_DONE; +} + +enum hal_rx_mon_status +ath11k_hal_rx_parse_mon_status(struct ath11k_base *ab, + struct hal_rx_mon_ppdu_info *ppdu_info, + struct sk_buff *skb) +{ + struct hal_tlv_hdr *tlv; + enum hal_rx_mon_status hal_status = HAL_RX_MON_STATUS_BUF_DONE; + u16 tlv_tag; + u16 tlv_len; + u8 *ptr = skb->data; + + do { + tlv = (struct hal_tlv_hdr *)ptr; + tlv_tag = FIELD_GET(HAL_TLV_HDR_TAG, tlv->tl); + tlv_len = FIELD_GET(HAL_TLV_HDR_LEN, tlv->tl); + ptr += sizeof(*tlv); + + /* The actual length of PPDU_END is the combined length of many PHY + * TLVs that follow. Skip the TLV header and + * rx_rxpcu_classification_overview that follows the header to get to + * next TLV. + */ + if (tlv_tag == HAL_RX_PPDU_END) + tlv_len = sizeof(struct hal_rx_rxpcu_classification_overview); + + hal_status = ath11k_hal_rx_parse_mon_status_tlv(ab, ppdu_info, + tlv_tag, ptr); + ptr += tlv_len; + ptr = PTR_ALIGN(ptr, HAL_TLV_ALIGN); + + if ((ptr - skb->data) >= DP_RX_BUFFER_SIZE) + break; + } while (hal_status == HAL_RX_MON_STATUS_PPDU_NOT_DONE); + + return hal_status; +} + +void ath11k_hal_rx_reo_ent_buf_paddr_get(void *rx_desc, dma_addr_t *paddr, + u32 *sw_cookie, void **pp_buf_addr, + u32 *msdu_cnt) +{ + struct hal_reo_entrance_ring *reo_ent_ring = + (struct hal_reo_entrance_ring *)rx_desc; + struct ath11k_buffer_addr *buf_addr_info; + struct rx_mpdu_desc *rx_mpdu_desc_info_details; + + rx_mpdu_desc_info_details = + (struct rx_mpdu_desc *)&reo_ent_ring->rx_mpdu_info; + + *msdu_cnt = FIELD_GET(RX_MPDU_DESC_INFO0_MSDU_COUNT, + rx_mpdu_desc_info_details->info0); + + buf_addr_info = (struct ath11k_buffer_addr *)&reo_ent_ring->buf_addr_info; + + *paddr = (((u64)FIELD_GET(BUFFER_ADDR_INFO1_ADDR, + buf_addr_info->info1)) << 32) | + FIELD_GET(BUFFER_ADDR_INFO0_ADDR, + buf_addr_info->info0); + + *sw_cookie = FIELD_GET(BUFFER_ADDR_INFO1_SW_COOKIE, + buf_addr_info->info1); + + *pp_buf_addr = (void *)buf_addr_info; +} diff --git a/drivers/net/wireless/ath/ath11k/hal_rx.h b/drivers/net/wireless/ath/ath11k/hal_rx.h new file mode 100644 index 0000000000000..96555e4f4ef31 --- /dev/null +++ b/drivers/net/wireless/ath/ath11k/hal_rx.h @@ -0,0 +1,352 @@ +/* SPDX-License-Identifier: BSD-3-Clause-Clear */ +/* + * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. + */ + +#ifndef ATH11K_HAL_RX_H +#define ATH11K_HAL_RX_H + +struct hal_rx_mpdu_meta { + u32 peer_meta; + u16 msdu_cnt; + u16 seq_num; + bool frag; + bool retry; + bool ampdu; + bool raw; +}; + +struct hal_rx_msdu_meta { + u32 cookie; + u32 msdu_len; + bool first; + bool last; + bool continuation; +}; + +struct hal_rx_meta_info { + enum hal_reo_dest_ring_push_reason push_reason; + struct hal_rx_mpdu_meta mpdu_meta; + struct hal_rx_msdu_meta msdu_meta; +}; + +struct hal_rx_wbm_rel_info { + u32 cookie; + enum hal_wbm_rel_src_module err_rel_src; + enum hal_reo_dest_ring_push_reason push_reason; + u32 err_code; + bool first_msdu; + bool last_msdu; +}; + +#define HAL_INVALID_PEERID 0xffff +#define VHT_SIG_SU_NSS_MASK 0x7 + +#define HAL_RX_MAX_MCS 12 +#define HAL_RX_MAX_NSS 8 + +struct hal_rx_mon_status_tlv_hdr { + u32 hdr; + u8 value[0]; +}; + +enum hal_rx_su_mu_coding { + HAL_RX_SU_MU_CODING_BCC, + HAL_RX_SU_MU_CODING_LDPC, + HAL_RX_SU_MU_CODING_MAX, +}; + +enum hal_rx_gi { + HAL_RX_GI_0_8_US, + HAL_RX_GI_0_4_US, + HAL_RX_GI_1_6_US, + HAL_RX_GI_3_2_US, + HAL_RX_GI_MAX, +}; + +enum hal_rx_bw { + HAL_RX_BW_20MHZ, + HAL_RX_BW_40MHZ, + HAL_RX_BW_80MHZ, + HAL_RX_BW_160MHZ, + HAL_RX_BW_MAX, +}; + +enum hal_rx_preamble { + HAL_RX_PREAMBLE_11A, + HAL_RX_PREAMBLE_11B, + HAL_RX_PREAMBLE_11N, + HAL_RX_PREAMBLE_11AC, + HAL_RX_PREAMBLE_11AX, + HAL_RX_PREAMBLE_MAX, +}; + +enum hal_rx_reception_type { + HAL_RX_RECEPTION_TYPE_SU, + HAL_RX_RECEPTION_TYPE_MU_MIMO, + HAL_RX_RECEPTION_TYPE_MU_OFDMA, + HAL_RX_RECEPTION_TYPE_MU_OFDMA_MIMO, + HAL_RX_RECEPTION_TYPE_MAX, +}; + +#define HAL_TLV_STATUS_PPDU_NOT_DONE 0 +#define HAL_TLV_STATUS_PPDU_DONE 1 +#define HAL_TLV_STATUS_BUF_DONE 2 +#define HAL_TLV_STATUS_PPDU_NON_STD_DONE 3 +#define HAL_RX_FCS_LEN 4 + +enum hal_rx_mon_status { + HAL_RX_MON_STATUS_PPDU_NOT_DONE, + HAL_RX_MON_STATUS_PPDU_DONE, + HAL_RX_MON_STATUS_BUF_DONE, +}; + +struct hal_rx_mon_ppdu_info { + u32 ppdu_id; + u32 ppdu_ts; + u32 num_mpdu_fcs_ok; + u32 num_mpdu_fcs_err; + u32 preamble_type; + u16 chan_num; + u16 tcp_msdu_count; + u16 tcp_ack_msdu_count; + u16 udp_msdu_count; + u16 other_msdu_count; + u16 peer_id; + u8 rate; + u8 mcs; + u8 nss; + u8 bw; + u8 is_stbc; + u8 gi; + u8 ldpc; + u8 beamformed; + u8 rssi_comb; + u8 tid; + u8 reception_type; + u64 rx_duration; +}; + +#define HAL_RX_PPDU_START_INFO0_PPDU_ID GENMASK(15, 0) + +struct hal_rx_ppdu_start { + __le32 info0; + __le32 chan_num; + __le32 ppdu_start_ts; +} __packed; + +#define HAL_RX_PPDU_END_USER_STATS_INFO0_MPDU_CNT_FCS_ERR GENMASK(25, 16) + +#define HAL_RX_PPDU_END_USER_STATS_INFO1_MPDU_CNT_FCS_OK GENMASK(8, 0) +#define HAL_RX_PPDU_END_USER_STATS_INFO1_FC_VALID BIT(9) +#define HAL_RX_PPDU_END_USER_STATS_INFO1_QOS_CTRL_VALID BIT(10) +#define HAL_RX_PPDU_END_USER_STATS_INFO1_HT_CTRL_VALID BIT(11) +#define HAL_RX_PPDU_END_USER_STATS_INFO1_PKT_TYPE GENMASK(23, 20) + +#define HAL_RX_PPDU_END_USER_STATS_INFO2_AST_INDEX GENMASK(15, 0) +#define HAL_RX_PPDU_END_USER_STATS_INFO2_FRAME_CTRL GENMASK(31, 16) + +#define HAL_RX_PPDU_END_USER_STATS_INFO3_QOS_CTRL GENMASK(31, 16) + +#define HAL_RX_PPDU_END_USER_STATS_INFO4_UDP_MSDU_CNT GENMASK(15, 0) +#define HAL_RX_PPDU_END_USER_STATS_INFO4_TCP_MSDU_CNT GENMASK(31, 16) + +#define HAL_RX_PPDU_END_USER_STATS_INFO5_OTHER_MSDU_CNT GENMASK(15, 0) +#define HAL_RX_PPDU_END_USER_STATS_INFO5_TCP_ACK_MSDU_CNT GENMASK(31, 16) + +#define HAL_RX_PPDU_END_USER_STATS_INFO6_TID_BITMAP GENMASK(15, 0) +#define HAL_RX_PPDU_END_USER_STATS_INFO6_TID_EOSP_BITMAP GENMASK(31, 16) + +struct hal_rx_ppdu_end_user_stats { + __le32 rsvd0[2]; + __le32 info0; + __le32 info1; + __le32 info2; + __le32 info3; + __le32 ht_ctrl; + __le32 rsvd1[2]; + __le32 info4; + __le32 info5; + __le32 info6; + __le32 rsvd2[11]; +} __packed; + +#define HAL_RX_HT_SIG_INFO_INFO0_MCS GENMASK(6, 0) +#define HAL_RX_HT_SIG_INFO_INFO0_BW BIT(7) + +#define HAL_RX_HT_SIG_INFO_INFO1_STBC GENMASK(5, 4) +#define HAL_RX_HT_SIG_INFO_INFO1_FEC_CODING BIT(6) +#define HAL_RX_HT_SIG_INFO_INFO1_GI BIT(7) + +struct hal_rx_ht_sig_info { + __le32 info0; + __le32 info1; +} __packed; + +#define HAL_RX_LSIG_B_INFO_INFO0_RATE GENMASK(3, 0) +#define HAL_RX_LSIG_B_INFO_INFO0_LEN GENMASK(15, 4) + +struct hal_rx_lsig_b_info { + __le32 info0; +} __packed; + +#define HAL_RX_LSIG_A_INFO_INFO0_RATE GENMASK(3, 0) +#define HAL_RX_LSIG_A_INFO_INFO0_LEN GENMASK(16, 5) +#define HAL_RX_LSIG_A_INFO_INFO0_PKT_TYPE GENMASK(27, 24) + +struct hal_rx_lsig_a_info { + __le32 info0; +} __packed; + +#define HAL_RX_VHT_SIG_A_INFO_INFO0_BW GENMASK(1, 0) +#define HAL_RX_VHT_SIG_A_INFO_INFO0_STBC BIT(3) +#define HAL_RX_VHT_SIG_A_INFO_INFO0_GROUP_ID GENMASK(9, 4) +#define HAL_RX_VHT_SIG_A_INFO_INFO0_NSTS GENMASK(21, 10) + +#define HAL_RX_VHT_SIG_A_INFO_INFO1_GI_SETTING GENMASK(1, 0) +#define HAL_RX_VHT_SIG_A_INFO_INFO1_SU_MU_CODING BIT(2) +#define HAL_RX_VHT_SIG_A_INFO_INFO1_MCS GENMASK(7, 4) +#define HAL_RX_VHT_SIG_A_INFO_INFO1_BEAMFORMED BIT(8) + +struct hal_rx_vht_sig_a_info { + __le32 info0; + __le32 info1; +} __packed; + +#define HAL_RX_HE_SIG_A_SU_INFO_INFO0_TRANSMIT_MCS GENMASK(6, 3) +#define HAL_RX_HE_SIG_A_SU_INFO_INFO0_DCM BIT(7) +#define HAL_RX_HE_SIG_A_SU_INFO_INFO0_TRANSMIT_BW GENMASK(20, 19) +#define HAL_RX_HE_SIG_A_SU_INFO_INFO0_CP_LTF_SIZE GENMASK(22, 21) +#define HAL_RX_HE_SIG_A_SU_INFO_INFO0_NSTS GENMASK(25, 23) + +#define HAL_RX_HE_SIG_A_SU_INFO_INFO1_CODING BIT(7) +#define HAL_RX_HE_SIG_A_SU_INFO_INFO1_STBC BIT(9) +#define HAL_RX_HE_SIG_A_SU_INFO_INFO1_TXBF BIT(10) + +struct hal_rx_he_sig_a_su_info { + __le32 info0; + __le32 info1; +} __packed; + +#define HAL_RX_HE_SIG_A_MU_DL_INFO_INFO0_TRANSMIT_BW GENMASK(17, 15) +#define HAL_RX_HE_SIG_A_MU_DL_INFO_INFO0_CP_LTF_SIZE GENMASK(24, 23) + +#define HAL_RX_HE_SIG_A_MU_DL_INFO_INFO1_STBC BIT(12) + +struct hal_rx_he_sig_a_mu_dl_info { + __le32 info0; + __le32 info1; +} __packed; + +#define HAL_RX_HE_SIG_B1_MU_INFO_INFO0_RU_ALLOCATION GENMASK(7, 0) + +struct hal_rx_he_sig_b1_mu_info { + __le32 info0; +} __packed; + +#define HAL_RX_HE_SIG_B2_MU_INFO_INFO0_STA_MCS GENMASK(18, 15) +#define HAL_RX_HE_SIG_B2_MU_INFO_INFO0_STA_CODING BIT(20) +#define HAL_RX_HE_SIG_B2_MU_INFO_INFO0_STA_NSTS GENMASK(31, 29) + +struct hal_rx_he_sig_b2_mu_info { + __le32 info0; +} __packed; + +#define HAL_RX_HE_SIG_B2_OFDMA_INFO_INFO0_STA_NSTS GENMASK(13, 11) +#define HAL_RX_HE_SIG_B2_OFDMA_INFO_INFO0_STA_TXBF BIT(19) +#define HAL_RX_HE_SIG_B2_OFDMA_INFO_INFO0_STA_MCS GENMASK(18, 15) +#define HAL_RX_HE_SIG_B2_OFDMA_INFO_INFO0_STA_DCM BIT(19) +#define HAL_RX_HE_SIG_B2_OFDMA_INFO_INFO0_STA_CODING BIT(20) + +struct hal_rx_he_sig_b2_ofdma_info { + __le32 info0; +} __packed; + +#define HAL_RX_PHYRX_RSSI_LEGACY_INFO_INFO1_RSSI_COMB GENMASK(15, 8) + +struct hal_rx_phyrx_rssi_legacy_info { + __le32 rsvd[35]; + __le32 info0; +} __packed; + +#define HAL_RX_MPDU_INFO_INFO0_PEERID GENMASK(31, 16) +struct hal_rx_mpdu_info { + __le32 rsvd0; + __le32 info0; + __le32 rsvd1[21]; +} __packed; + +#define HAL_RX_PPDU_END_DURATION GENMASK(23, 0) +struct hal_rx_ppdu_end_duration { + __le32 rsvd0[9]; + __le32 info0; + __le32 rsvd1[4]; +} __packed; + +struct hal_rx_rxpcu_classification_overview { + u32 rsvd0; +} __packed; + +struct hal_rx_msdu_desc_info { + u32 msdu_flags; + u16 msdu_len; /* 14 bits for length */ +}; + +#define HAL_RX_NUM_MSDU_DESC 6 +struct hal_rx_msdu_list { + struct hal_rx_msdu_desc_info msdu_info[HAL_RX_NUM_MSDU_DESC]; + u32 sw_cookie[HAL_RX_NUM_MSDU_DESC]; + u8 rbm[HAL_RX_NUM_MSDU_DESC]; +}; + +void ath11k_hal_reo_status_queue_stats(struct ath11k_base *ab, u32 *reo_desc, + struct hal_reo_status *status); +void ath11k_hal_reo_flush_queue_status(struct ath11k_base *ab, u32 *reo_desc, + struct hal_reo_status *status); +void ath11k_hal_reo_flush_cache_status(struct ath11k_base *ab, u32 *reo_desc, + struct hal_reo_status *status); +void ath11k_hal_reo_flush_cache_status(struct ath11k_base *ab, u32 *reo_desc, + struct hal_reo_status *status); +void ath11k_hal_reo_unblk_cache_status(struct ath11k_base *ab, u32 *reo_desc, + struct hal_reo_status *status); +void ath11k_hal_reo_flush_timeout_list_status(struct ath11k_base *ab, + u32 *reo_desc, + struct hal_reo_status *status); +void ath11k_hal_reo_desc_thresh_reached_status(struct ath11k_base *ab, + u32 *reo_desc, + struct hal_reo_status *status); +void ath11k_hal_reo_update_rx_reo_queue_status(struct ath11k_base *ab, + u32 *reo_desc, + struct hal_reo_status *status); +int ath11k_hal_reo_process_status(u8 *reo_desc, u8 *status); +void ath11k_hal_rx_msdu_link_info_get(void *link_desc, u32 *num_msdus, + struct hal_rx_msdu_meta *meta, + enum hal_rx_buf_return_buf_manager *rbm); +void ath11k_hal_rx_msdu_link_desc_set(struct ath11k_base *ab, void *desc, + void *link_desc, + enum hal_wbm_rel_bm_act action); +void ath11k_hal_rx_buf_addr_info_set(void *desc, dma_addr_t paddr, + u32 cookie, u8 manager); +void ath11k_hal_rx_buf_addr_info_get(void *desc, dma_addr_t *paddr, + u32 *cookie, u8 *rbm); +int ath11k_hal_desc_reo_parse_err(struct ath11k_base *ab, u32 *rx_desc, + dma_addr_t *paddr, u32 *desc_bank); +void ath11k_hal_rx_parse_dst_ring_desc(struct ath11k_base *ab, u32 *rx_desc, + struct hal_rx_meta_info *meta_info); +int ath11k_hal_wbm_desc_parse_err(struct ath11k_base *ab, void *desc, + struct hal_rx_wbm_rel_info *rel_info); +void ath11k_hal_rx_reo_ent_paddr_get(struct ath11k_base *ab, void *desc, + dma_addr_t *paddr, u32 *desc_bank); +void ath11k_hal_rx_reo_ent_buf_paddr_get(void *rx_desc, + dma_addr_t *paddr, u32 *sw_cookie, + void **pp_buf_addr_info, + u32 *msdu_cnt); +enum hal_rx_mon_status +ath11k_hal_rx_parse_mon_status(struct ath11k_base *ab, + struct hal_rx_mon_ppdu_info *ppdu_info, + struct sk_buff *skb); +#define REO_QUEUE_DESC_MAGIC_DEBUG_PATTERN_0 0xDDBEEF +#define REO_QUEUE_DESC_MAGIC_DEBUG_PATTERN_1 0xADBEEF +#define REO_QUEUE_DESC_MAGIC_DEBUG_PATTERN_2 0xBDBEEF +#define REO_QUEUE_DESC_MAGIC_DEBUG_PATTERN_3 0xCDBEEF +#endif diff --git a/drivers/net/wireless/ath/ath11k/hal_tx.c b/drivers/net/wireless/ath/ath11k/hal_tx.c new file mode 100644 index 0000000000000..bb27591023fe7 --- /dev/null +++ b/drivers/net/wireless/ath/ath11k/hal_tx.c @@ -0,0 +1,226 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* + * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. + */ + +#include "ahb.h" +#include "hal.h" +#include "hal_tx.h" + +#define DSCP_TID_MAP_TBL_ENTRY_SIZE 64 + +/* dscp_tid_map - Default DSCP-TID mapping + * + * DSCP TID + * 000000 0 + * 001000 1 + * 010000 2 + * 011000 3 + * 100000 4 + * 101000 5 + * 110000 6 + * 111000 7 + */ +static const u8 dscp_tid_map[DSCP_TID_MAP_TBL_ENTRY_SIZE] = { + 0, 0, 0, 0, 0, 0, 0, 0, + 1, 1, 1, 1, 1, 1, 1, 1, + 2, 2, 2, 2, 2, 2, 2, 2, + 3, 3, 3, 3, 3, 3, 3, 3, + 4, 4, 4, 4, 4, 4, 4, 4, + 5, 5, 5, 5, 5, 5, 5, 5, + 6, 6, 6, 6, 6, 6, 6, 6, + 7, 7, 7, 7, 7, 7, 7, 7, +}; + +void ath11k_hal_tx_cmd_desc_setup(struct ath11k_base *ab, void *cmd, + struct hal_tx_info *ti) +{ + struct hal_tcl_data_cmd *tcl_cmd = (struct hal_tcl_data_cmd *)cmd; + + tcl_cmd->buf_addr_info.info0 = + FIELD_PREP(BUFFER_ADDR_INFO0_ADDR, ti->paddr); + tcl_cmd->buf_addr_info.info1 = + FIELD_PREP(BUFFER_ADDR_INFO1_ADDR, + ((uint64_t)ti->paddr >> HAL_ADDR_MSB_REG_SHIFT)); + tcl_cmd->buf_addr_info.info1 |= + FIELD_PREP(BUFFER_ADDR_INFO1_RET_BUF_MGR, + (ti->ring_id + HAL_RX_BUF_RBM_SW0_BM)) | + FIELD_PREP(BUFFER_ADDR_INFO1_SW_COOKIE, ti->desc_id); + + tcl_cmd->info0 = + FIELD_PREP(HAL_TCL_DATA_CMD_INFO0_DESC_TYPE, ti->type) | + FIELD_PREP(HAL_TCL_DATA_CMD_INFO0_ENCAP_TYPE, ti->encap_type) | + FIELD_PREP(HAL_TCL_DATA_CMD_INFO0_ENCRYPT_TYPE, + ti->encrypt_type) | + FIELD_PREP(HAL_TCL_DATA_CMD_INFO0_ADDRX_EN, + ti->addr_search_flags) | + FIELD_PREP(HAL_TCL_DATA_CMD_INFO0_ADDRY_EN, + ti->addr_search_flags) | + FIELD_PREP(HAL_TCL_DATA_CMD_INFO0_CMD_NUM, + ti->meta_data_flags); + + tcl_cmd->info1 = ti->flags0 | + FIELD_PREP(HAL_TCL_DATA_CMD_INFO1_DATA_LEN, ti->data_len) | + FIELD_PREP(HAL_TCL_DATA_CMD_INFO1_PKT_OFFSET, ti->pkt_offset); + + tcl_cmd->info2 = ti->flags1 | + FIELD_PREP(HAL_TCL_DATA_CMD_INFO2_TID, ti->tid) | + FIELD_PREP(HAL_TCL_DATA_CMD_INFO2_LMAC_ID, ti->lmac_id); + + tcl_cmd->info3 = FIELD_PREP(HAL_TCL_DATA_CMD_INFO3_DSCP_TID_TABLE_IDX, + ti->dscp_tid_tbl_idx) | + FIELD_PREP(HAL_TCL_DATA_CMD_INFO3_SEARCH_INDEX, + ti->bss_ast_hash); +} + +/* Commit the descriptor to hardware */ +void ath11k_hal_tx_desc_sync(void *tx_desc_cached, void *hw_desc) +{ + memcpy(hw_desc + sizeof(struct hal_tlv_hdr), tx_desc_cached, + sizeof(struct hal_tcl_data_cmd)); +} + +/* Get the descriptor status from hardware */ +void ath11k_hal_tx_status_desc_sync(void *hw_desc, void *local_desc) +{ + memcpy(local_desc, hw_desc, HAL_TX_STATUS_DESC_LEN); +} + +void ath11k_hal_tx_status_parse(struct ath11k_base *ab, + struct hal_wbm_release_ring *desc, + struct hal_tx_status *ts) +{ + ts->buf_rel_source = + FIELD_GET(HAL_WBM_RELEASE_INFO0_REL_SRC_MODULE, desc->info0); + if (ts->buf_rel_source != HAL_WBM_REL_SRC_MODULE_FW && + ts->buf_rel_source != HAL_WBM_REL_SRC_MODULE_TQM) + return; + + ts->desc_id = FIELD_GET(BUFFER_ADDR_INFO1_SW_COOKIE, + desc->buf_addr_info.info1); + + if (ts->buf_rel_source == HAL_WBM_REL_SRC_MODULE_FW) + return; + + ts->status = FIELD_GET(HAL_WBM_RELEASE_INFO0_TQM_RELEASE_REASON, + desc->info0); + ts->ppdu_id = FIELD_GET(HAL_WBM_RELEASE_INFO1_TQM_STATUS_NUMBER, + desc->info1); + ts->try_cnt = FIELD_GET(HAL_WBM_RELEASE_INFO1_TRANSMIT_COUNT, + desc->info1); + + ts->ack_rssi = FIELD_GET(HAL_WBM_RELEASE_INFO2_ACK_FRAME_RSSI, + desc->info2); + if (desc->info2 & HAL_WBM_RELEASE_INFO2_FIRST_MSDU) + ts->flags |= HAL_TX_STATUS_FLAGS_FIRST_MSDU; + + if (desc->info2 & HAL_WBM_RELEASE_INFO2_LAST_MSDU) + ts->flags |= HAL_TX_STATUS_FLAGS_LAST_MSDU; + + if (desc->info2 & HAL_WBM_RELEASE_INFO2_MSDU_IN_AMSDU) + ts->flags |= HAL_TX_STATUS_FLAGS_MSDU_IN_AMSDU; + + ts->peer_id = FIELD_GET(HAL_WBM_RELEASE_INFO3_PEER_ID, desc->info3); + ts->tid = FIELD_GET(HAL_WBM_RELEASE_INFO3_TID, desc->info3); + + if (!(desc->rate_stats.info0 & HAL_TX_RATE_STATS_INFO0_VALID)) + return; + + ts->flags |= HAL_TX_STATUS_FLAGS_RATE_STATS_VALID; + ts->tsf = desc->rate_stats.tsf; + ts->bw = FIELD_GET(HAL_TX_RATE_STATS_INFO0_BW, desc->rate_stats.info0); + ts->pkt_type = FIELD_GET(HAL_TX_RATE_STATS_INFO0_PKT_TYPE, + desc->rate_stats.info0); + if (desc->rate_stats.info0 & HAL_TX_RATE_STATS_INFO0_STBC) + ts->flags |= HAL_TX_STATUS_FLAGS_RATE_STBC; + if (desc->rate_stats.info0 & HAL_TX_RATE_STATS_INFO0_LDPC) + ts->flags |= HAL_TX_STATUS_FLAGS_RATE_LDPC; + if (desc->rate_stats.info0 & HAL_TX_RATE_STATS_INFO0_OFDMA_TX) + ts->flags |= HAL_TX_STATUS_FLAGS_OFDMA; + + ts->sgi = FIELD_GET(HAL_TX_RATE_STATS_INFO0_SGI, + desc->rate_stats.info0); + ts->mcs = FIELD_GET(HAL_TX_RATE_STATS_INFO0_MCS, + desc->rate_stats.info0); + ts->num_tones_in_ru = FIELD_GET(HAL_TX_RATE_STATS_INFO0_TONES_IN_RU, + desc->rate_stats.info0); +} + +void ath11k_hal_tx_set_dscp_tid_map(struct ath11k_base *ab, int id) +{ + u32 ctrl_reg_val; + u32 addr; + u8 hw_map_val[HAL_DSCP_TID_TBL_SIZE]; + int i; + u32 value; + int cnt = 0; + + ctrl_reg_val = ath11k_ahb_read32(ab, HAL_SEQ_WCSS_UMAC_TCL_REG + + HAL_TCL1_RING_CMN_CTRL_REG); + /* Enable read/write access */ + ctrl_reg_val |= HAL_TCL1_RING_CMN_CTRL_DSCP_TID_MAP_PROG_EN; + ath11k_ahb_write32(ab, HAL_SEQ_WCSS_UMAC_TCL_REG + + HAL_TCL1_RING_CMN_CTRL_REG, ctrl_reg_val); + + addr = HAL_SEQ_WCSS_UMAC_TCL_REG + HAL_TCL1_RING_DSCP_TID_MAP + + (4 * id * (HAL_DSCP_TID_TBL_SIZE / 4)); + + /* Configure each DSCP-TID mapping in three bits there by configure + * three bytes in an iteration. + */ + for (i = 0; i < DSCP_TID_MAP_TBL_ENTRY_SIZE; i += 8) { + value = FIELD_PREP(HAL_TCL1_RING_FIELD_DSCP_TID_MAP0, + dscp_tid_map[i]) | + FIELD_PREP(HAL_TCL1_RING_FIELD_DSCP_TID_MAP1, + dscp_tid_map[i + 1]) | + FIELD_PREP(HAL_TCL1_RING_FIELD_DSCP_TID_MAP2, + dscp_tid_map[i + 2]) | + FIELD_PREP(HAL_TCL1_RING_FIELD_DSCP_TID_MAP3, + dscp_tid_map[i + 3]) | + FIELD_PREP(HAL_TCL1_RING_FIELD_DSCP_TID_MAP4, + dscp_tid_map[i + 4]) | + FIELD_PREP(HAL_TCL1_RING_FIELD_DSCP_TID_MAP5, + dscp_tid_map[i + 5]) | + FIELD_PREP(HAL_TCL1_RING_FIELD_DSCP_TID_MAP6, + dscp_tid_map[i + 6]) | + FIELD_PREP(HAL_TCL1_RING_FIELD_DSCP_TID_MAP7, + dscp_tid_map[i + 7]); + memcpy(&hw_map_val[cnt], (u8 *)&value, 3); + cnt += 3; + } + + for (i = 0; i < HAL_DSCP_TID_TBL_SIZE; i += 4) { + ath11k_ahb_write32(ab, addr, *(u32 *)&hw_map_val[i]); + addr += 4; + } + + /* Disable read/write access */ + ctrl_reg_val = ath11k_ahb_read32(ab, HAL_SEQ_WCSS_UMAC_TCL_REG + + HAL_TCL1_RING_CMN_CTRL_REG); + ctrl_reg_val &= ~HAL_TCL1_RING_CMN_CTRL_DSCP_TID_MAP_PROG_EN; + ath11k_ahb_write32(ab, HAL_SEQ_WCSS_UMAC_TCL_REG + + HAL_TCL1_RING_CMN_CTRL_REG, + ctrl_reg_val); +} + +void ath11k_hal_tx_init_data_ring(struct ath11k_base *ab, struct hal_srng *srng) +{ + struct hal_srng_params params; + struct hal_tlv_hdr *tlv; + int i, entry_size; + u8 *desc; + + memset(¶ms, 0, sizeof(params)); + + entry_size = ath11k_hal_srng_get_entrysize(HAL_TCL_DATA); + ath11k_hal_srng_get_params(ab, srng, ¶ms); + desc = (u8 *)params.ring_base_vaddr; + + for (i = 0; i < params.num_entries; i++) { + tlv = (struct hal_tlv_hdr *)desc; + tlv->tl = FIELD_PREP(HAL_TLV_HDR_TAG, HAL_TCL_DATA_CMD) | + FIELD_PREP(HAL_TLV_HDR_LEN, + sizeof(struct hal_tcl_data_cmd)); + desc += entry_size; + } +} diff --git a/drivers/net/wireless/ath/ath11k/hal_tx.h b/drivers/net/wireless/ath/ath11k/hal_tx.h new file mode 100644 index 0000000000000..f955070bcf1e4 --- /dev/null +++ b/drivers/net/wireless/ath/ath11k/hal_tx.h @@ -0,0 +1,80 @@ +/* SPDX-License-Identifier: BSD-3-Clause-Clear */ +/* + * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. + */ + +#ifndef ATH11K_HAL_TX_H +#define ATH11K_HAL_TX_H + +#include "hal_desc.h" + +#define HAL_TX_ADDRX_EN 1 +#define HAL_TX_ADDRY_EN 2 + +#define HAL_TX_ADDR_SEARCH_INDEX 0 +#define HAL_TX_ADDR_SEARCH_DEFAULT 1 + +struct hal_tx_info { + u16 meta_data_flags; /* %HAL_TCL_DATA_CMD_INFO0_META_ */ + u8 ring_id; + u32 desc_id; + enum hal_tcl_desc_type type; + enum hal_tcl_encap_type encap_type; + dma_addr_t paddr; + u32 data_len; + u32 pkt_offset; + enum hal_encrypt_type encrypt_type; + u32 flags0; /* %HAL_TCL_DATA_CMD_INFO1_ */ + u32 flags1; /* %HAL_TCL_DATA_CMD_INFO2_ */ + u16 addr_search_flags; /* %HAL_TCL_DATA_CMD_INFO0_ADDR(X/Y)_ */ + u16 bss_ast_hash; + u8 tid; + u8 search_type; /* %HAL_TX_ADDR_SEARCH_ */ + u8 lmac_id; + u8 dscp_tid_tbl_idx; +}; + +/* TODO: Check if the actual desc macros can be used instead */ +#define HAL_TX_STATUS_FLAGS_FIRST_MSDU BIT(0) +#define HAL_TX_STATUS_FLAGS_LAST_MSDU BIT(1) +#define HAL_TX_STATUS_FLAGS_MSDU_IN_AMSDU BIT(2) +#define HAL_TX_STATUS_FLAGS_RATE_STATS_VALID BIT(3) +#define HAL_TX_STATUS_FLAGS_RATE_LDPC BIT(4) +#define HAL_TX_STATUS_FLAGS_RATE_STBC BIT(5) +#define HAL_TX_STATUS_FLAGS_OFDMA BIT(6) + +#define HAL_TX_STATUS_DESC_LEN sizeof(struct hal_wbm_release_ring) + +/* Tx status parsed from srng desc */ +struct hal_tx_status { + enum hal_wbm_rel_src_module buf_rel_source; + u32 desc_id; + enum hal_wbm_tqm_rel_reason status; + u8 ack_rssi; + enum hal_tx_rate_stats_bw bw; + enum hal_tx_rate_stats_pkt_type pkt_type; + enum hal_tx_rate_stats_sgi sgi; + u8 mcs; + u16 num_tones_in_ru; + u32 flags; /* %HAL_TX_STATUS_FLAGS_ */ + u32 tsf; + u32 ppdu_id; + u8 try_cnt; + u8 tid; + u16 peer_id; +}; + +void ath11k_hal_tx_cmd_desc_setup(struct ath11k_base *ab, void *cmd, + struct hal_tx_info *ti); +void ath11k_hal_tx_desc_sync(void *tx_desc_cached, void *hw_desc); +void ath11k_hal_tx_status_parse(struct ath11k_base *ab, + struct hal_wbm_release_ring *desc, + struct hal_tx_status *ts); +void ath11k_hal_tx_status_desc_sync(void *hw_desc, void *local_desc); +void ath11k_hal_tx_set_dscp_tid_map(struct ath11k_base *ab, int id); +int ath11k_hal_reo_cmd_send(struct ath11k_base *ab, struct hal_srng *srng, + enum hal_reo_cmd_type type, + struct ath11k_hal_reo_cmd *cmd); +void ath11k_hal_tx_init_data_ring(struct ath11k_base *ab, + struct hal_srng *srng); +#endif diff --git a/drivers/net/wireless/ath/ath11k/htc.c b/drivers/net/wireless/ath/ath11k/htc.c new file mode 100644 index 0000000000000..9d4115f25ceb4 --- /dev/null +++ b/drivers/net/wireless/ath/ath11k/htc.c @@ -0,0 +1,773 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* + * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. + */ +#include +#include + +#include "ahb.h" +#include "debug.h" + +struct sk_buff *ath11k_htc_alloc_skb(struct ath11k_base *ab, int size) +{ + struct sk_buff *skb; + + skb = dev_alloc_skb(size + sizeof(struct ath11k_htc_hdr)); + if (!skb) + return NULL; + + skb_reserve(skb, sizeof(struct ath11k_htc_hdr)); + + /* FW/HTC requires 4-byte aligned streams */ + if (!IS_ALIGNED((unsigned long)skb->data, 4)) + ath11k_warn(ab, "Unaligned HTC tx skb\n"); + + return skb; +} + +static void ath11k_htc_control_tx_complete(struct ath11k_base *ab, + struct sk_buff *skb) +{ + kfree_skb(skb); +} + +static struct sk_buff *ath11k_htc_build_tx_ctrl_skb(void *ab) +{ + struct sk_buff *skb; + struct ath11k_skb_cb *skb_cb; + + skb = dev_alloc_skb(ATH11K_HTC_CONTROL_BUFFER_SIZE); + if (!skb) + return NULL; + + skb_reserve(skb, sizeof(struct ath11k_htc_hdr)); + WARN_ON_ONCE(!IS_ALIGNED((unsigned long)skb->data, 4)); + + skb_cb = ATH11K_SKB_CB(skb); + memset(skb_cb, 0, sizeof(*skb_cb)); + + ath11k_dbg(ab, ATH11K_DBG_HTC, "%s: skb %pK\n", __func__, skb); + return skb; +} + +static inline void ath11k_htc_restore_tx_skb(struct ath11k_htc *htc, + struct sk_buff *skb) +{ + struct ath11k_skb_cb *skb_cb = ATH11K_SKB_CB(skb); + + dma_unmap_single(htc->ab->dev, skb_cb->paddr, skb->len, DMA_TO_DEVICE); + skb_pull(skb, sizeof(struct ath11k_htc_hdr)); +} + +static void ath11k_htc_prepare_tx_skb(struct ath11k_htc_ep *ep, + struct sk_buff *skb) +{ + struct ath11k_htc_hdr *hdr; + + hdr = (struct ath11k_htc_hdr *)skb->data; + + memset(hdr, 0, sizeof(*hdr)); + hdr->htc_info = FIELD_PREP(HTC_HDR_ENDPOINTID, ep->eid) | + FIELD_PREP(HTC_HDR_PAYLOADLEN, + (skb->len - sizeof(*hdr))) | + FIELD_PREP(HTC_HDR_FLAGS, + ATH11K_HTC_FLAG_NEED_CREDIT_UPDATE); + + spin_lock_bh(&ep->htc->tx_lock); + hdr->ctrl_info = FIELD_PREP(HTC_HDR_CONTROLBYTES1, ep->seq_no++); + spin_unlock_bh(&ep->htc->tx_lock); +} + +int ath11k_htc_send(struct ath11k_htc *htc, + enum ath11k_htc_ep_id eid, + struct sk_buff *skb) +{ + struct ath11k_htc_ep *ep = &htc->endpoint[eid]; + struct ath11k_skb_cb *skb_cb = ATH11K_SKB_CB(skb); + struct device *dev = htc->ab->dev; + struct ath11k_base *ab = htc->ab; + int credits = 0; + int ret; + + if (eid >= ATH11K_HTC_EP_COUNT) { + ath11k_warn(ab, "Invalid endpoint id: %d\n", eid); + return -ENOENT; + } + + skb_push(skb, sizeof(struct ath11k_htc_hdr)); + + if (ep->tx_credit_flow_enabled) { + credits = DIV_ROUND_UP(skb->len, htc->target_credit_size); + spin_lock_bh(&htc->tx_lock); + if (ep->tx_credits < credits) { + ath11k_dbg(ab, ATH11K_DBG_HTC, + "htc insufficient credits ep %d required %d available %d\n", + eid, credits, ep->tx_credits); + spin_unlock_bh(&htc->tx_lock); + ret = -EAGAIN; + goto err_pull; + } + ep->tx_credits -= credits; + ath11k_dbg(ab, ATH11K_DBG_HTC, + "htc ep %d consumed %d credits (total %d)\n", + eid, credits, ep->tx_credits); + spin_unlock_bh(&htc->tx_lock); + } + + ath11k_htc_prepare_tx_skb(ep, skb); + + skb_cb->eid = eid; + skb_cb->paddr = dma_map_single(dev, skb->data, skb->len, DMA_TO_DEVICE); + ret = dma_mapping_error(dev, skb_cb->paddr); + if (ret) { + ret = -EIO; + goto err_credits; + } + + ret = ath11k_ce_send(htc->ab, skb, ep->ul_pipe_id, ep->eid); + if (ret) + goto err_unmap; + + return 0; + +err_unmap: + dma_unmap_single(dev, skb_cb->paddr, skb->len, DMA_TO_DEVICE); +err_credits: + if (ep->tx_credit_flow_enabled) { + spin_lock_bh(&htc->tx_lock); + ep->tx_credits += credits; + ath11k_dbg(ab, ATH11K_DBG_HTC, + "htc ep %d reverted %d credits back (total %d)\n", + eid, credits, ep->tx_credits); + spin_unlock_bh(&htc->tx_lock); + + if (ep->ep_ops.ep_tx_credits) + ep->ep_ops.ep_tx_credits(htc->ab); + } +err_pull: + skb_pull(skb, sizeof(struct ath11k_htc_hdr)); + return ret; +} + +static void +ath11k_htc_process_credit_report(struct ath11k_htc *htc, + const struct ath11k_htc_credit_report *report, + int len, + enum ath11k_htc_ep_id eid) +{ + struct ath11k_base *ab = htc->ab; + struct ath11k_htc_ep *ep; + int i, n_reports; + + if (len % sizeof(*report)) + ath11k_warn(ab, "Uneven credit report len %d", len); + + n_reports = len / sizeof(*report); + + spin_lock_bh(&htc->tx_lock); + for (i = 0; i < n_reports; i++, report++) { + if (report->eid >= ATH11K_HTC_EP_COUNT) + break; + + ep = &htc->endpoint[report->eid]; + ep->tx_credits += report->credits; + + ath11k_dbg(ab, ATH11K_DBG_HTC, "htc ep %d got %d credits (total %d)\n", + report->eid, report->credits, ep->tx_credits); + + if (ep->ep_ops.ep_tx_credits) { + spin_unlock_bh(&htc->tx_lock); + ep->ep_ops.ep_tx_credits(htc->ab); + spin_lock_bh(&htc->tx_lock); + } + } + spin_unlock_bh(&htc->tx_lock); +} + +static int ath11k_htc_process_trailer(struct ath11k_htc *htc, + u8 *buffer, + int length, + enum ath11k_htc_ep_id src_eid) +{ + struct ath11k_base *ab = htc->ab; + int status = 0; + struct ath11k_htc_record *record; + size_t len; + + while (length > 0) { + record = (struct ath11k_htc_record *)buffer; + + if (length < sizeof(record->hdr)) { + status = -EINVAL; + break; + } + + if (record->hdr.len > length) { + /* no room left in buffer for record */ + ath11k_warn(ab, "Invalid record length: %d\n", + record->hdr.len); + status = -EINVAL; + break; + } + + switch (record->hdr.id) { + case ATH11K_HTC_RECORD_CREDITS: + len = sizeof(struct ath11k_htc_credit_report); + if (record->hdr.len < len) { + ath11k_warn(ab, "Credit report too long\n"); + status = -EINVAL; + break; + } + ath11k_htc_process_credit_report(htc, + record->credit_report, + record->hdr.len, + src_eid); + break; + default: + ath11k_warn(ab, "Unhandled record: id:%d length:%d\n", + record->hdr.id, record->hdr.len); + break; + } + + if (status) + break; + + /* multiple records may be present in a trailer */ + buffer += sizeof(record->hdr) + record->hdr.len; + length -= sizeof(record->hdr) + record->hdr.len; + } + + return status; +} + +void ath11k_htc_rx_completion_handler(struct ath11k_base *ab, + struct sk_buff *skb) +{ + int status = 0; + struct ath11k_htc *htc = &ab->htc; + struct ath11k_htc_hdr *hdr; + struct ath11k_htc_ep *ep; + u16 payload_len; + u32 trailer_len = 0; + size_t min_len; + u8 eid; + bool trailer_present; + + hdr = (struct ath11k_htc_hdr *)skb->data; + skb_pull(skb, sizeof(*hdr)); + + eid = FIELD_GET(HTC_HDR_ENDPOINTID, hdr->htc_info); + + if (eid >= ATH11K_HTC_EP_COUNT) { + ath11k_warn(ab, "HTC Rx: invalid eid %d\n", eid); + goto out; + } + + ep = &htc->endpoint[eid]; + + payload_len = FIELD_GET(HTC_HDR_PAYLOADLEN, hdr->htc_info); + + if (payload_len + sizeof(*hdr) > ATH11K_HTC_MAX_LEN) { + ath11k_warn(ab, "HTC rx frame too long, len: %zu\n", + payload_len + sizeof(*hdr)); + goto out; + } + + if (skb->len < payload_len) { + ath11k_warn(ab, "HTC Rx: insufficient length, got %d, expected %d\n", + skb->len, payload_len); + goto out; + } + + /* get flags to check for trailer */ + trailer_present = (FIELD_GET(HTC_HDR_FLAGS, hdr->htc_info)) & + ATH11K_HTC_FLAG_TRAILER_PRESENT; + + if (trailer_present) { + u8 *trailer; + + trailer_len = FIELD_GET(HTC_HDR_CONTROLBYTES0, hdr->ctrl_info); + min_len = sizeof(struct ath11k_htc_record_hdr); + + if ((trailer_len < min_len) || + (trailer_len > payload_len)) { + ath11k_warn(ab, "Invalid trailer length: %d\n", + trailer_len); + goto out; + } + + trailer = (u8 *)hdr; + trailer += sizeof(*hdr); + trailer += payload_len; + trailer -= trailer_len; + status = ath11k_htc_process_trailer(htc, trailer, + trailer_len, eid); + if (status) + goto out; + + skb_trim(skb, skb->len - trailer_len); + } + + if (trailer_len >= payload_len) + /* zero length packet with trailer data, just drop these */ + goto out; + + if (eid == ATH11K_HTC_EP_0) { + struct ath11k_htc_msg *msg = (struct ath11k_htc_msg *)skb->data; + + switch (FIELD_GET(HTC_MSG_MESSAGEID, msg->msg_svc_id)) { + case ATH11K_HTC_MSG_READY_ID: + case ATH11K_HTC_MSG_CONNECT_SERVICE_RESP_ID: + /* handle HTC control message */ + if (completion_done(&htc->ctl_resp)) { + /* this is a fatal error, target should not be + * sending unsolicited messages on the ep 0 + */ + ath11k_warn(ab, "HTC rx ctrl still processing\n"); + complete(&htc->ctl_resp); + goto out; + } + + htc->control_resp_len = + min_t(int, skb->len, + ATH11K_HTC_MAX_CTRL_MSG_LEN); + + memcpy(htc->control_resp_buffer, skb->data, + htc->control_resp_len); + + complete(&htc->ctl_resp); + break; + default: + ath11k_warn(ab, "ignoring unsolicited htc ep0 event\n"); + break; + } + goto out; + } + + ath11k_dbg(ab, ATH11K_DBG_HTC, "htc rx completion ep %d skb %pK\n", + eid, skb); + ep->ep_ops.ep_rx_complete(ab, skb); + + /* poll tx completion for interrupt disabled CE's */ + ath11k_ce_poll_send_completed(ab, ep->ul_pipe_id); + + /* skb is now owned by the rx completion handler */ + skb = NULL; +out: + kfree_skb(skb); +} + +static void ath11k_htc_control_rx_complete(struct ath11k_base *ab, + struct sk_buff *skb) +{ + /* This is unexpected. FW is not supposed to send regular rx on this + * endpoint. + */ + ath11k_warn(ab, "unexpected htc rx\n"); + kfree_skb(skb); +} + +static const char *htc_service_name(enum ath11k_htc_svc_id id) +{ + switch (id) { + case ATH11K_HTC_SVC_ID_RESERVED: + return "Reserved"; + case ATH11K_HTC_SVC_ID_RSVD_CTRL: + return "Control"; + case ATH11K_HTC_SVC_ID_WMI_CONTROL: + return "WMI"; + case ATH11K_HTC_SVC_ID_WMI_DATA_BE: + return "DATA BE"; + case ATH11K_HTC_SVC_ID_WMI_DATA_BK: + return "DATA BK"; + case ATH11K_HTC_SVC_ID_WMI_DATA_VI: + return "DATA VI"; + case ATH11K_HTC_SVC_ID_WMI_DATA_VO: + return "DATA VO"; + case ATH11K_HTC_SVC_ID_WMI_CONTROL_MAC1: + return "WMI MAC1"; + case ATH11K_HTC_SVC_ID_WMI_CONTROL_MAC2: + return "WMI MAC2"; + case ATH11K_HTC_SVC_ID_NMI_CONTROL: + return "NMI Control"; + case ATH11K_HTC_SVC_ID_NMI_DATA: + return "NMI Data"; + case ATH11K_HTC_SVC_ID_HTT_DATA_MSG: + return "HTT Data"; + case ATH11K_HTC_SVC_ID_TEST_RAW_STREAMS: + return "RAW"; + case ATH11K_HTC_SVC_ID_IPA_TX: + return "IPA TX"; + case ATH11K_HTC_SVC_ID_PKT_LOG: + return "PKT LOG"; + } + + return "Unknown"; +} + +static void ath11k_htc_reset_endpoint_states(struct ath11k_htc *htc) +{ + struct ath11k_htc_ep *ep; + int i; + + for (i = ATH11K_HTC_EP_0; i < ATH11K_HTC_EP_COUNT; i++) { + ep = &htc->endpoint[i]; + ep->service_id = ATH11K_HTC_SVC_ID_UNUSED; + ep->max_ep_message_len = 0; + ep->max_tx_queue_depth = 0; + ep->eid = i; + ep->htc = htc; + ep->tx_credit_flow_enabled = true; + } +} + +static u8 ath11k_htc_get_credit_allocation(struct ath11k_htc *htc, + u16 service_id) +{ + u8 i, allocation = 0; + + for (i = 0; i < ATH11K_HTC_MAX_SERVICE_ALLOC_ENTRIES; i++) { + if (htc->service_alloc_table[i].service_id == service_id) { + allocation = + htc->service_alloc_table[i].credit_allocation; + } + } + + return allocation; +} + +static int ath11k_htc_setup_target_buffer_assignments(struct ath11k_htc *htc) +{ + struct ath11k_htc_svc_tx_credits *serv_entry; + u32 svc_id[] = { + ATH11K_HTC_SVC_ID_WMI_CONTROL, + ATH11K_HTC_SVC_ID_WMI_CONTROL_MAC1, + ATH11K_HTC_SVC_ID_WMI_CONTROL_MAC2, + }; + int i, credits; + + credits = htc->total_transmit_credits; + serv_entry = htc->service_alloc_table; + + if ((htc->wmi_ep_count == 0) || + (htc->wmi_ep_count > ARRAY_SIZE(svc_id))) + return -EINVAL; + + /* Divide credits among number of endpoints for WMI */ + credits = credits / htc->wmi_ep_count; + for (i = 0; i < htc->wmi_ep_count; i++) { + serv_entry[i].service_id = svc_id[i]; + serv_entry[i].credit_allocation = credits; + } + + return 0; +} + +int ath11k_htc_wait_target(struct ath11k_htc *htc) +{ + int i, status = 0; + struct ath11k_base *ab = htc->ab; + unsigned long time_left; + struct ath11k_htc_ready *ready; + u16 message_id; + u16 credit_count; + u16 credit_size; + + time_left = wait_for_completion_timeout(&htc->ctl_resp, + ATH11K_HTC_WAIT_TIMEOUT_HZ); + if (!time_left) { + ath11k_warn(ab, "failed to receive control response completion, polling..\n"); + + for (i = 0; i < CE_COUNT; i++) + ath11k_ce_per_engine_service(htc->ab, i); + + time_left = + wait_for_completion_timeout(&htc->ctl_resp, + ATH11K_HTC_WAIT_TIMEOUT_HZ); + + if (!time_left) + status = -ETIMEDOUT; + } + + if (status < 0) { + ath11k_warn(ab, "ctl_resp never came in (%d)\n", status); + return status; + } + + if (htc->control_resp_len < sizeof(*ready)) { + ath11k_warn(ab, "Invalid HTC ready msg len:%d\n", + htc->control_resp_len); + return -ECOMM; + } + + ready = (struct ath11k_htc_ready *)htc->control_resp_buffer; + message_id = FIELD_GET(HTC_MSG_MESSAGEID, ready->id_credit_count); + credit_count = FIELD_GET(HTC_READY_MSG_CREDITCOUNT, + ready->id_credit_count); + credit_size = FIELD_GET(HTC_READY_MSG_CREDITSIZE, ready->size_ep); + + if (message_id != ATH11K_HTC_MSG_READY_ID) { + ath11k_warn(ab, "Invalid HTC ready msg: 0x%x\n", message_id); + return -ECOMM; + } + + htc->total_transmit_credits = credit_count; + htc->target_credit_size = credit_size; + + ath11k_dbg(ab, ATH11K_DBG_HTC, + "Target ready! transmit resources: %d size:%d\n", + htc->total_transmit_credits, htc->target_credit_size); + + if ((htc->total_transmit_credits == 0) || + (htc->target_credit_size == 0)) { + ath11k_warn(ab, "Invalid credit size received\n"); + return -ECOMM; + } + + ath11k_htc_setup_target_buffer_assignments(htc); + + return 0; +} + +int ath11k_htc_connect_service(struct ath11k_htc *htc, + struct ath11k_htc_svc_conn_req *conn_req, + struct ath11k_htc_svc_conn_resp *conn_resp) +{ + struct ath11k_base *ab = htc->ab; + struct ath11k_htc_conn_svc *req_msg; + struct ath11k_htc_conn_svc_resp resp_msg_dummy; + struct ath11k_htc_conn_svc_resp *resp_msg = &resp_msg_dummy; + enum ath11k_htc_ep_id assigned_eid = ATH11K_HTC_EP_COUNT; + struct ath11k_htc_ep *ep; + struct sk_buff *skb; + unsigned int max_msg_size = 0; + int length, status; + unsigned long time_left; + bool disable_credit_flow_ctrl = false; + u16 message_id, service_id, flags = 0; + u8 tx_alloc = 0; + + /* special case for HTC pseudo control service */ + if (conn_req->service_id == ATH11K_HTC_SVC_ID_RSVD_CTRL) { + disable_credit_flow_ctrl = true; + assigned_eid = ATH11K_HTC_EP_0; + max_msg_size = ATH11K_HTC_MAX_CTRL_MSG_LEN; + memset(&resp_msg_dummy, 0, sizeof(resp_msg_dummy)); + goto setup; + } + + tx_alloc = ath11k_htc_get_credit_allocation(htc, + conn_req->service_id); + if (!tx_alloc) + ath11k_dbg(ab, ATH11K_DBG_BOOT, + "boot htc service %s does not allocate target credits\n", + htc_service_name(conn_req->service_id)); + + skb = ath11k_htc_build_tx_ctrl_skb(htc->ab); + if (!skb) { + ath11k_warn(ab, "Failed to allocate HTC packet\n"); + return -ENOMEM; + } + + length = sizeof(*req_msg); + skb_put(skb, length); + memset(skb->data, 0, length); + + req_msg = (struct ath11k_htc_conn_svc *)skb->data; + req_msg->msg_svc_id = FIELD_PREP(HTC_MSG_MESSAGEID, + ATH11K_HTC_MSG_CONNECT_SERVICE_ID); + + flags |= FIELD_PREP(ATH11K_HTC_CONN_FLAGS_RECV_ALLOC, tx_alloc); + + /* Only enable credit flow control for WMI ctrl service */ + if (!(conn_req->service_id == ATH11K_HTC_SVC_ID_WMI_CONTROL || + conn_req->service_id == ATH11K_HTC_SVC_ID_WMI_CONTROL_MAC1 || + conn_req->service_id == ATH11K_HTC_SVC_ID_WMI_CONTROL_MAC2)) { + flags |= ATH11K_HTC_CONN_FLAGS_DISABLE_CREDIT_FLOW_CTRL; + disable_credit_flow_ctrl = true; + } + + req_msg->flags_len = FIELD_PREP(HTC_SVC_MSG_CONNECTIONFLAGS, flags); + req_msg->msg_svc_id |= FIELD_PREP(HTC_SVC_MSG_SERVICE_ID, + conn_req->service_id); + + reinit_completion(&htc->ctl_resp); + + status = ath11k_htc_send(htc, ATH11K_HTC_EP_0, skb); + if (status) { + kfree_skb(skb); + return status; + } + + /* wait for response */ + time_left = wait_for_completion_timeout(&htc->ctl_resp, + ATH11K_HTC_CONN_SVC_TIMEOUT_HZ); + if (!time_left) { + ath11k_err(ab, "Service connect timeout\n"); + return -ETIMEDOUT; + } + + /* we controlled the buffer creation, it's aligned */ + resp_msg = (struct ath11k_htc_conn_svc_resp *)htc->control_resp_buffer; + message_id = FIELD_GET(HTC_MSG_MESSAGEID, resp_msg->msg_svc_id); + service_id = FIELD_GET(HTC_SVC_RESP_MSG_SERVICEID, + resp_msg->msg_svc_id); + + if ((message_id != ATH11K_HTC_MSG_CONNECT_SERVICE_RESP_ID) || + (htc->control_resp_len < sizeof(*resp_msg))) { + ath11k_err(ab, "Invalid resp message ID 0x%x", message_id); + return -EPROTO; + } + + ath11k_dbg(ab, ATH11K_DBG_HTC, + "HTC Service %s connect response: status: 0x%lx, assigned ep: 0x%lx\n", + htc_service_name(service_id), + FIELD_GET(HTC_SVC_RESP_MSG_STATUS, resp_msg->flags_len), + FIELD_GET(HTC_SVC_RESP_MSG_ENDPOINTID, resp_msg->flags_len)); + + conn_resp->connect_resp_code = FIELD_GET(HTC_SVC_RESP_MSG_STATUS, + resp_msg->flags_len); + + /* check response status */ + if (conn_resp->connect_resp_code != ATH11K_HTC_CONN_SVC_STATUS_SUCCESS) { + ath11k_err(ab, "HTC Service %s connect request failed: 0x%x)\n", + htc_service_name(service_id), + conn_resp->connect_resp_code); + return -EPROTO; + } + + assigned_eid = (enum ath11k_htc_ep_id)FIELD_GET( + HTC_SVC_RESP_MSG_ENDPOINTID, + resp_msg->flags_len); + + max_msg_size = FIELD_GET(HTC_SVC_RESP_MSG_MAXMSGSIZE, + resp_msg->flags_len); + +setup: + + if (assigned_eid >= ATH11K_HTC_EP_COUNT) + return -EPROTO; + + if (max_msg_size == 0) + return -EPROTO; + + ep = &htc->endpoint[assigned_eid]; + ep->eid = assigned_eid; + + if (ep->service_id != ATH11K_HTC_SVC_ID_UNUSED) + return -EPROTO; + + /* return assigned endpoint to caller */ + conn_resp->eid = assigned_eid; + conn_resp->max_msg_len = FIELD_GET(HTC_SVC_RESP_MSG_MAXMSGSIZE, + resp_msg->flags_len); + + /* setup the endpoint */ + ep->service_id = conn_req->service_id; + ep->max_tx_queue_depth = conn_req->max_send_queue_depth; + ep->max_ep_message_len = FIELD_GET(HTC_SVC_RESP_MSG_MAXMSGSIZE, + resp_msg->flags_len); + ep->tx_credits = tx_alloc; + + /* copy all the callbacks */ + ep->ep_ops = conn_req->ep_ops; + + status = ath11k_ahb_map_service_to_pipe(htc->ab, + ep->service_id, + &ep->ul_pipe_id, + &ep->dl_pipe_id); + if (status) + return status; + + ath11k_dbg(ab, ATH11K_DBG_BOOT, + "boot htc service '%s' ul pipe %d dl pipe %d eid %d ready\n", + htc_service_name(ep->service_id), ep->ul_pipe_id, + ep->dl_pipe_id, ep->eid); + + if (disable_credit_flow_ctrl && ep->tx_credit_flow_enabled) { + ep->tx_credit_flow_enabled = false; + ath11k_dbg(ab, ATH11K_DBG_BOOT, + "boot htc service '%s' eid %d TX flow control disabled\n", + htc_service_name(ep->service_id), assigned_eid); + } + + return status; +} + +int ath11k_htc_start(struct ath11k_htc *htc) +{ + struct sk_buff *skb; + int status = 0; + struct ath11k_base *ab = htc->ab; + struct ath11k_htc_setup_complete_extended *msg; + + skb = ath11k_htc_build_tx_ctrl_skb(htc->ab); + if (!skb) + return -ENOMEM; + + skb_put(skb, sizeof(*msg)); + memset(skb->data, 0, skb->len); + + msg = (struct ath11k_htc_setup_complete_extended *)skb->data; + msg->msg_id = FIELD_PREP(HTC_MSG_MESSAGEID, + ATH11K_HTC_MSG_SETUP_COMPLETE_EX_ID); + + ath11k_dbg(ab, ATH11K_DBG_HTC, "HTC is using TX credit flow control\n"); + + status = ath11k_htc_send(htc, ATH11K_HTC_EP_0, skb); + if (status) { + kfree_skb(skb); + return status; + } + + return 0; +} + +int ath11k_htc_init(struct ath11k_base *ab) +{ + struct ath11k_htc *htc = &ab->htc; + struct ath11k_htc_svc_conn_req conn_req; + struct ath11k_htc_svc_conn_resp conn_resp; + int ret; + + spin_lock_init(&htc->tx_lock); + + ath11k_htc_reset_endpoint_states(htc); + + htc->ab = ab; + + switch (ab->wmi_sc.preferred_hw_mode) { + case WMI_HOST_HW_MODE_SINGLE: + htc->wmi_ep_count = 1; + break; + case WMI_HOST_HW_MODE_DBS: + case WMI_HOST_HW_MODE_DBS_OR_SBS: + htc->wmi_ep_count = 2; + break; + case WMI_HOST_HW_MODE_DBS_SBS: + htc->wmi_ep_count = 3; + break; + default: + htc->wmi_ep_count = 3; + break; + } + + /* setup our pseudo HTC control endpoint connection */ + memset(&conn_req, 0, sizeof(conn_req)); + memset(&conn_resp, 0, sizeof(conn_resp)); + conn_req.ep_ops.ep_tx_complete = ath11k_htc_control_tx_complete; + conn_req.ep_ops.ep_rx_complete = ath11k_htc_control_rx_complete; + conn_req.max_send_queue_depth = ATH11K_NUM_CONTROL_TX_BUFFERS; + conn_req.service_id = ATH11K_HTC_SVC_ID_RSVD_CTRL; + + /* connect fake service */ + ret = ath11k_htc_connect_service(htc, &conn_req, &conn_resp); + if (ret) { + ath11k_err(ab, "could not connect to htc service (%d)\n", ret); + return ret; + } + + init_completion(&htc->ctl_resp); + + return 0; +} diff --git a/drivers/net/wireless/ath/ath11k/htc.h b/drivers/net/wireless/ath/ath11k/htc.h new file mode 100644 index 0000000000000..f0a3387567cae --- /dev/null +++ b/drivers/net/wireless/ath/ath11k/htc.h @@ -0,0 +1,313 @@ +/* SPDX-License-Identifier: BSD-3-Clause-Clear */ +/* + * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. + */ + +#ifndef ATH11K_HTC_H +#define ATH11K_HTC_H + +#include +#include +#include +#include +#include + +struct ath11k_base; + +#define HTC_HDR_ENDPOINTID GENMASK(7, 0) +#define HTC_HDR_FLAGS GENMASK(15, 8) +#define HTC_HDR_PAYLOADLEN GENMASK(31, 16) +#define HTC_HDR_CONTROLBYTES0 GENMASK(7, 0) +#define HTC_HDR_CONTROLBYTES1 GENMASK(15, 8) +#define HTC_HDR_RESERVED GENMASK(31, 16) + +#define HTC_SVC_MSG_SERVICE_ID GENMASK(31, 16) +#define HTC_SVC_MSG_CONNECTIONFLAGS GENMASK(15, 0) +#define HTC_SVC_MSG_SERVICEMETALENGTH GENMASK(23, 16) +#define HTC_READY_MSG_CREDITCOUNT GENMASK(31, 16) +#define HTC_READY_MSG_CREDITSIZE GENMASK(15, 0) +#define HTC_READY_MSG_MAXENDPOINTS GENMASK(23, 16) + +#define HTC_READY_EX_MSG_HTCVERSION GENMASK(7, 0) +#define HTC_READY_EX_MSG_MAXMSGSPERHTCBUNDLE GENMASK(15, 8) + +#define HTC_SVC_RESP_MSG_SERVICEID GENMASK(31, 16) +#define HTC_SVC_RESP_MSG_STATUS GENMASK(7, 0) +#define HTC_SVC_RESP_MSG_ENDPOINTID GENMASK(15, 8) +#define HTC_SVC_RESP_MSG_MAXMSGSIZE GENMASK(31, 16) +#define HTC_SVC_RESP_MSG_SERVICEMETALENGTH GENMASK(7, 0) + +#define HTC_MSG_MESSAGEID GENMASK(15, 0) +#define HTC_SETUP_COMPLETE_EX_MSG_SETUPFLAGS GENMASK(31, 0) +#define HTC_SETUP_COMPLETE_EX_MSG_MAXMSGSPERBUNDLEDRECV GENMASK(7, 0) +#define HTC_SETUP_COMPLETE_EX_MSG_RSVD0 GENMASK(15, 8) +#define HTC_SETUP_COMPLETE_EX_MSG_RSVD1 GENMASK(23, 16) +#define HTC_SETUP_COMPLETE_EX_MSG_RSVD2 GENMASK(31, 24) + +enum ath11k_htc_tx_flags { + ATH11K_HTC_FLAG_NEED_CREDIT_UPDATE = 0x01, + ATH11K_HTC_FLAG_SEND_BUNDLE = 0x02 +}; + +enum ath11k_htc_rx_flags { + ATH11K_HTC_FLAG_TRAILER_PRESENT = 0x02, + ATH11K_HTC_FLAG_BUNDLE_MASK = 0xF0 +}; + +struct ath11k_htc_hdr { + u32 htc_info; + u32 ctrl_info; +} __packed __aligned(4); + +enum ath11k_htc_msg_id { + ATH11K_HTC_MSG_READY_ID = 1, + ATH11K_HTC_MSG_CONNECT_SERVICE_ID = 2, + ATH11K_HTC_MSG_CONNECT_SERVICE_RESP_ID = 3, + ATH11K_HTC_MSG_SETUP_COMPLETE_ID = 4, + ATH11K_HTC_MSG_SETUP_COMPLETE_EX_ID = 5, + ATH11K_HTC_MSG_SEND_SUSPEND_COMPLETE = 6 +}; + +enum ath11k_htc_version { + ATH11K_HTC_VERSION_2P0 = 0x00, /* 2.0 */ + ATH11K_HTC_VERSION_2P1 = 0x01, /* 2.1 */ +}; + +#define ATH11K_HTC_CONN_FLAGS_THRESHOLD_LEVEL_MASK GENMASK(1, 0) +#define ATH11K_HTC_CONN_FLAGS_RECV_ALLOC GENMASK(15, 8) + +enum ath11k_htc_conn_flags { + ATH11K_HTC_CONN_FLAGS_THRESHOLD_LEVEL_ONE_FOURTH = 0x0, + ATH11K_HTC_CONN_FLAGS_THRESHOLD_LEVEL_ONE_HALF = 0x1, + ATH11K_HTC_CONN_FLAGS_THRESHOLD_LEVEL_THREE_FOURTHS = 0x2, + ATH11K_HTC_CONN_FLAGS_THRESHOLD_LEVEL_UNITY = 0x3, + ATH11K_HTC_CONN_FLAGS_REDUCE_CREDIT_DRIBBLE = 1 << 2, + ATH11K_HTC_CONN_FLAGS_DISABLE_CREDIT_FLOW_CTRL = 1 << 3 +}; + +enum ath11k_htc_conn_svc_status { + ATH11K_HTC_CONN_SVC_STATUS_SUCCESS = 0, + ATH11K_HTC_CONN_SVC_STATUS_NOT_FOUND = 1, + ATH11K_HTC_CONN_SVC_STATUS_FAILED = 2, + ATH11K_HTC_CONN_SVC_STATUS_NO_RESOURCES = 3, + ATH11K_HTC_CONN_SVC_STATUS_NO_MORE_EP = 4 +}; + +struct ath11k_htc_ready { + u32 id_credit_count; + u32 size_ep; +} __packed; + +struct ath11k_htc_ready_extended { + struct ath11k_htc_ready base; + u32 ver_bundle; +} __packed; + +struct ath11k_htc_conn_svc { + u32 msg_svc_id; + u32 flags_len; +} __packed; + +struct ath11k_htc_conn_svc_resp { + u32 msg_svc_id; + u32 flags_len; + u32 svc_meta_pad; +} __packed; + +struct ath11k_htc_setup_complete_extended { + u32 msg_id; + u32 flags; + u32 max_msgs_per_bundled_recv; +} __packed; + +struct ath11k_htc_msg { + u32 msg_svc_id; + u32 flags_len; +} __packed __aligned(4); + +enum ath11k_htc_record_id { + ATH11K_HTC_RECORD_NULL = 0, + ATH11K_HTC_RECORD_CREDITS = 1 +}; + +struct ath11k_htc_record_hdr { + u8 id; /* @enum ath11k_htc_record_id */ + u8 len; + u8 pad0; + u8 pad1; +} __packed; + +struct ath11k_htc_credit_report { + u8 eid; /* @enum ath11k_htc_ep_id */ + u8 credits; + u8 pad0; + u8 pad1; +} __packed; + +struct ath11k_htc_record { + struct ath11k_htc_record_hdr hdr; + union { + struct ath11k_htc_credit_report credit_report[0]; + u8 pauload[0]; + }; +} __packed __aligned(4); + +/* note: the trailer offset is dynamic depending + * on payload length. this is only a struct layout draft + */ +struct ath11k_htc_frame { + struct ath11k_htc_hdr hdr; + union { + struct ath11k_htc_msg msg; + u8 payload[0]; + }; + struct ath11k_htc_record trailer[0]; +} __packed __aligned(4); + +enum ath11k_htc_svc_gid { + ATH11K_HTC_SVC_GRP_RSVD = 0, + ATH11K_HTC_SVC_GRP_WMI = 1, + ATH11K_HTC_SVC_GRP_NMI = 2, + ATH11K_HTC_SVC_GRP_HTT = 3, + ATH11K_HTC_SVC_GRP_CFG = 4, + ATH11K_HTC_SVC_GRP_IPA = 5, + ATH11K_HTC_SVC_GRP_PKTLOG = 6, + + ATH11K_HTC_SVC_GRP_TEST = 254, + ATH11K_HTC_SVC_GRP_LAST = 255, +}; + +#define SVC(group, idx) \ + (int)(((int)(group) << 8) | (int)(idx)) + +enum ath11k_htc_svc_id { + /* NOTE: service ID of 0x0000 is reserved and should never be used */ + ATH11K_HTC_SVC_ID_RESERVED = 0x0000, + ATH11K_HTC_SVC_ID_UNUSED = ATH11K_HTC_SVC_ID_RESERVED, + + ATH11K_HTC_SVC_ID_RSVD_CTRL = SVC(ATH11K_HTC_SVC_GRP_RSVD, 1), + ATH11K_HTC_SVC_ID_WMI_CONTROL = SVC(ATH11K_HTC_SVC_GRP_WMI, 0), + ATH11K_HTC_SVC_ID_WMI_DATA_BE = SVC(ATH11K_HTC_SVC_GRP_WMI, 1), + ATH11K_HTC_SVC_ID_WMI_DATA_BK = SVC(ATH11K_HTC_SVC_GRP_WMI, 2), + ATH11K_HTC_SVC_ID_WMI_DATA_VI = SVC(ATH11K_HTC_SVC_GRP_WMI, 3), + ATH11K_HTC_SVC_ID_WMI_DATA_VO = SVC(ATH11K_HTC_SVC_GRP_WMI, 4), + ATH11K_HTC_SVC_ID_WMI_CONTROL_MAC1 = SVC(ATH11K_HTC_SVC_GRP_WMI, 5), + ATH11K_HTC_SVC_ID_WMI_CONTROL_MAC2 = SVC(ATH11K_HTC_SVC_GRP_WMI, 6), + + ATH11K_HTC_SVC_ID_NMI_CONTROL = SVC(ATH11K_HTC_SVC_GRP_NMI, 0), + ATH11K_HTC_SVC_ID_NMI_DATA = SVC(ATH11K_HTC_SVC_GRP_NMI, 1), + + ATH11K_HTC_SVC_ID_HTT_DATA_MSG = SVC(ATH11K_HTC_SVC_GRP_HTT, 0), + + /* raw stream service (i.e. flash, tcmd, calibration apps) */ + ATH11K_HTC_SVC_ID_TEST_RAW_STREAMS = SVC(ATH11K_HTC_SVC_GRP_TEST, 0), + ATH11K_HTC_SVC_ID_IPA_TX = SVC(ATH11K_HTC_SVC_GRP_IPA, 0), + ATH11K_HTC_SVC_ID_PKT_LOG = SVC(ATH11K_HTC_SVC_GRP_PKTLOG, 0), +}; + +#undef SVC + +enum ath11k_htc_ep_id { + ATH11K_HTC_EP_UNUSED = -1, + ATH11K_HTC_EP_0 = 0, + ATH11K_HTC_EP_1 = 1, + ATH11K_HTC_EP_2, + ATH11K_HTC_EP_3, + ATH11K_HTC_EP_4, + ATH11K_HTC_EP_5, + ATH11K_HTC_EP_6, + ATH11K_HTC_EP_7, + ATH11K_HTC_EP_8, + ATH11K_HTC_EP_COUNT, +}; + +struct ath11k_htc_ops { + void (*target_send_suspend_complete)(struct ath11k_base *ar); +}; + +struct ath11k_htc_ep_ops { + void (*ep_tx_complete)(struct ath11k_base *, struct sk_buff *); + void (*ep_rx_complete)(struct ath11k_base *, struct sk_buff *); + void (*ep_tx_credits)(struct ath11k_base *); +}; + +/* service connection information */ +struct ath11k_htc_svc_conn_req { + u16 service_id; + struct ath11k_htc_ep_ops ep_ops; + int max_send_queue_depth; +}; + +/* service connection response information */ +struct ath11k_htc_svc_conn_resp { + u8 buffer_len; + u8 actual_len; + enum ath11k_htc_ep_id eid; + unsigned int max_msg_len; + u8 connect_resp_code; +}; + +#define ATH11K_NUM_CONTROL_TX_BUFFERS 2 +#define ATH11K_HTC_MAX_LEN 4096 +#define ATH11K_HTC_MAX_CTRL_MSG_LEN 256 +#define ATH11K_HTC_WAIT_TIMEOUT_HZ (1 * HZ) +#define ATH11K_HTC_CONTROL_BUFFER_SIZE (ATH11K_HTC_MAX_CTRL_MSG_LEN + \ + sizeof(struct ath11k_htc_hdr)) +#define ATH11K_HTC_CONN_SVC_TIMEOUT_HZ (1 * HZ) +#define ATH11K_HTC_MAX_SERVICE_ALLOC_ENTRIES 8 + +struct ath11k_htc_ep { + struct ath11k_htc *htc; + enum ath11k_htc_ep_id eid; + enum ath11k_htc_svc_id service_id; + struct ath11k_htc_ep_ops ep_ops; + + int max_tx_queue_depth; + int max_ep_message_len; + u8 ul_pipe_id; + u8 dl_pipe_id; + + u8 seq_no; /* for debugging */ + int tx_credits; + bool tx_credit_flow_enabled; +}; + +struct ath11k_htc_svc_tx_credits { + u16 service_id; + u8 credit_allocation; +}; + +struct ath11k_htc { + struct ath11k_base *ab; + struct ath11k_htc_ep endpoint[ATH11K_HTC_EP_COUNT]; + + /* protects endpoints */ + spinlock_t tx_lock; + + struct ath11k_htc_ops htc_ops; + + u8 control_resp_buffer[ATH11K_HTC_MAX_CTRL_MSG_LEN]; + int control_resp_len; + + struct completion ctl_resp; + + int total_transmit_credits; + struct ath11k_htc_svc_tx_credits + service_alloc_table[ATH11K_HTC_MAX_SERVICE_ALLOC_ENTRIES]; + int target_credit_size; + u8 wmi_ep_count; +}; + +int ath11k_htc_init(struct ath11k_base *ar); +int ath11k_htc_wait_target(struct ath11k_htc *htc); +int ath11k_htc_start(struct ath11k_htc *htc); +int ath11k_htc_connect_service(struct ath11k_htc *htc, + struct ath11k_htc_svc_conn_req *conn_req, + struct ath11k_htc_svc_conn_resp *conn_resp); +int ath11k_htc_send(struct ath11k_htc *htc, enum ath11k_htc_ep_id eid, + struct sk_buff *packet); +struct sk_buff *ath11k_htc_alloc_skb(struct ath11k_base *ar, int size); +void ath11k_htc_rx_completion_handler(struct ath11k_base *ar, + struct sk_buff *skb); + +#endif diff --git a/drivers/net/wireless/ath/ath11k/hw.h b/drivers/net/wireless/ath/ath11k/hw.h new file mode 100644 index 0000000000000..dd39333ec0ea7 --- /dev/null +++ b/drivers/net/wireless/ath/ath11k/hw.h @@ -0,0 +1,127 @@ +/* SPDX-License-Identifier: BSD-3-Clause-Clear */ +/* + * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. + */ + +#ifndef ATH11K_HW_H +#define ATH11K_HW_H + +/* Target configuration defines */ + +/* Num VDEVS per radio */ +#define TARGET_NUM_VDEVS (16 + 1) + +#define TARGET_NUM_PEERS_PDEV (512 + TARGET_NUM_VDEVS) + +/* Num of peers for Single Radio mode */ +#define TARGET_NUM_PEERS_SINGLE (TARGET_NUM_PEERS_PDEV) + +/* Num of peers for DBS */ +#define TARGET_NUM_PEERS_DBS (2 * TARGET_NUM_PEERS_PDEV) + +/* Num of peers for DBS_SBS */ +#define TARGET_NUM_PEERS_DBS_SBS (3 * TARGET_NUM_PEERS_PDEV) + +/* Max num of stations (per radio) */ +#define TARGET_NUM_STATIONS 512 + +#define TARGET_NUM_PEERS(x) TARGET_NUM_PEERS_##x +#define TARGET_NUM_PEER_KEYS 2 +#define TARGET_NUM_TIDS(x) (2 * TARGET_NUM_PEERS(x) + \ + 4 * TARGET_NUM_VDEVS + 8) + +#define TARGET_AST_SKID_LIMIT 16 +#define TARGET_NUM_OFFLD_PEERS 4 +#define TARGET_NUM_OFFLD_REORDER_BUFFS 4 + +#define TARGET_TX_CHAIN_MASK (BIT(0) | BIT(1) | BIT(2) | BIT(4)) +#define TARGET_RX_CHAIN_MASK (BIT(0) | BIT(1) | BIT(2) | BIT(4)) +#define TARGET_RX_TIMEOUT_LO_PRI 100 +#define TARGET_RX_TIMEOUT_HI_PRI 40 + +#define TARGET_DECAP_MODE_RAW 0 +#define TARGET_DECAP_MODE_NATIVE_WIFI 1 +#define TARGET_DECAP_MODE_ETH 2 + +#define TARGET_SCAN_MAX_PENDING_REQS 4 +#define TARGET_BMISS_OFFLOAD_MAX_VDEV 3 +#define TARGET_ROAM_OFFLOAD_MAX_VDEV 3 +#define TARGET_ROAM_OFFLOAD_MAX_AP_PROFILES 8 +#define TARGET_GTK_OFFLOAD_MAX_VDEV 3 +#define TARGET_NUM_MCAST_GROUPS 12 +#define TARGET_NUM_MCAST_TABLE_ELEMS 64 +#define TARGET_MCAST2UCAST_MODE 2 +#define TARGET_TX_DBG_LOG_SIZE 1024 +#define TARGET_RX_SKIP_DEFRAG_TIMEOUT_DUP_DETECTION_CHECK 1 +#define TARGET_VOW_CONFIG 0 +#define TARGET_NUM_MSDU_DESC (2500) +#define TARGET_MAX_FRAG_ENTRIES 6 +#define TARGET_MAX_BCN_OFFLD 16 +#define TARGET_NUM_WDS_ENTRIES 32 +#define TARGET_DMA_BURST_SIZE 1 +#define TARGET_RX_BATCHMODE 1 + +#define ATH11K_HW_MAX_QUEUES 4 + +#define ATH11k_HW_RATECODE_CCK_SHORT_PREAM_MASK 0x4 + +#define ATH11K_FW_DIR "ath11k" + +/* IPQ8074 definitions */ +#define IPQ8074_FW_DIR "IPQ8074" +#define IPQ8074_MAX_BOARD_DATA_SZ (256 * 1024) +#define IPQ8074_MAX_CAL_DATA_SZ IPQ8074_MAX_BOARD_DATA_SZ + +#define ATH11K_BOARD_MAGIC "QCA-ATH11K-BOARD" +#define ATH11K_BOARD_API2_FILE "board-2.bin" +#define ATH11K_DEFAULT_BOARD_FILE "bdwlan.bin" +#define ATH11K_DEFAULT_CAL_FILE "caldata.bin" + +enum ath11k_hw_rate_cck { + ATH11K_HW_RATE_CCK_LP_11M = 0, + ATH11K_HW_RATE_CCK_LP_5_5M, + ATH11K_HW_RATE_CCK_LP_2M, + ATH11K_HW_RATE_CCK_LP_1M, + ATH11K_HW_RATE_CCK_SP_11M, + ATH11K_HW_RATE_CCK_SP_5_5M, + ATH11K_HW_RATE_CCK_SP_2M, +}; + +enum ath11k_hw_rate_ofdm { + ATH11K_HW_RATE_OFDM_48M = 0, + ATH11K_HW_RATE_OFDM_24M, + ATH11K_HW_RATE_OFDM_12M, + ATH11K_HW_RATE_OFDM_6M, + ATH11K_HW_RATE_OFDM_54M, + ATH11K_HW_RATE_OFDM_36M, + ATH11K_HW_RATE_OFDM_18M, + ATH11K_HW_RATE_OFDM_9M, +}; + +struct ath11k_hw_params { + const char *name; + struct { + const char *dir; + size_t board_size; + size_t cal_size; + } fw; +}; + +struct ath11k_fw_ie { + __le32 id; + __le32 len; + u8 data[0]; +}; + +enum ath11k_bd_ie_board_type { + ATH11K_BD_IE_BOARD_NAME = 0, + ATH11K_BD_IE_BOARD_DATA = 1, +}; + +enum ath11k_bd_ie_type { + /* contains sub IEs of enum ath11k_bd_ie_board_type */ + ATH11K_BD_IE_BOARD = 0, + ATH11K_BD_IE_BOARD_EXT = 1, +}; + +#endif diff --git a/drivers/net/wireless/ath/ath11k/mac.c b/drivers/net/wireless/ath/ath11k/mac.c new file mode 100644 index 0000000000000..cb025a4a5785f --- /dev/null +++ b/drivers/net/wireless/ath/ath11k/mac.c @@ -0,0 +1,5427 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* + * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. + */ + +#include +#include +#include "mac.h" +#include "core.h" +#include "debug.h" +#include "wmi.h" +#include "hw.h" +#include "dp_tx.h" +#include "dp_rx.h" +#include "testmode.h" +#include "peer.h" + +#define CHAN2G(_channel, _freq, _flags) { \ + .band = NL80211_BAND_2GHZ, \ + .hw_value = (_channel), \ + .center_freq = (_freq), \ + .flags = (_flags), \ + .max_antenna_gain = 0, \ + .max_power = 30, \ +} + +#define CHAN5G(_channel, _freq, _flags) { \ + .band = NL80211_BAND_5GHZ, \ + .hw_value = (_channel), \ + .center_freq = (_freq), \ + .flags = (_flags), \ + .max_antenna_gain = 0, \ + .max_power = 30, \ +} + +static const struct ieee80211_channel ath11k_2ghz_channels[] = { + CHAN2G(1, 2412, 0), + CHAN2G(2, 2417, 0), + CHAN2G(3, 2422, 0), + CHAN2G(4, 2427, 0), + CHAN2G(5, 2432, 0), + CHAN2G(6, 2437, 0), + CHAN2G(7, 2442, 0), + CHAN2G(8, 2447, 0), + CHAN2G(9, 2452, 0), + CHAN2G(10, 2457, 0), + CHAN2G(11, 2462, 0), + CHAN2G(12, 2467, 0), + CHAN2G(13, 2472, 0), + CHAN2G(14, 2484, 0), +}; + +static const struct ieee80211_channel ath11k_5ghz_channels[] = { + CHAN5G(36, 5180, 0), + CHAN5G(40, 5200, 0), + CHAN5G(44, 5220, 0), + CHAN5G(48, 5240, 0), + CHAN5G(52, 5260, 0), + CHAN5G(56, 5280, 0), + CHAN5G(60, 5300, 0), + CHAN5G(64, 5320, 0), + CHAN5G(100, 5500, 0), + CHAN5G(104, 5520, 0), + CHAN5G(108, 5540, 0), + CHAN5G(112, 5560, 0), + CHAN5G(116, 5580, 0), + CHAN5G(120, 5600, 0), + CHAN5G(124, 5620, 0), + CHAN5G(128, 5640, 0), + CHAN5G(132, 5660, 0), + CHAN5G(136, 5680, 0), + CHAN5G(140, 5700, 0), + CHAN5G(144, 5720, 0), + CHAN5G(149, 5745, 0), + CHAN5G(153, 5765, 0), + CHAN5G(157, 5785, 0), + CHAN5G(161, 5805, 0), + CHAN5G(165, 5825, 0), + CHAN5G(169, 5845, 0), + CHAN5G(173, 5865, 0), +}; + +static struct ieee80211_rate ath11k_legacy_rates[] = { + { .bitrate = 10, + .hw_value = ATH11K_HW_RATE_CCK_LP_1M }, + { .bitrate = 20, + .hw_value = ATH11K_HW_RATE_CCK_LP_2M, + .hw_value_short = ATH11K_HW_RATE_CCK_SP_2M, + .flags = IEEE80211_RATE_SHORT_PREAMBLE }, + { .bitrate = 55, + .hw_value = ATH11K_HW_RATE_CCK_LP_5_5M, + .hw_value_short = ATH11K_HW_RATE_CCK_SP_5_5M, + .flags = IEEE80211_RATE_SHORT_PREAMBLE }, + { .bitrate = 110, + .hw_value = ATH11K_HW_RATE_CCK_LP_11M, + .hw_value_short = ATH11K_HW_RATE_CCK_SP_11M, + .flags = IEEE80211_RATE_SHORT_PREAMBLE }, + + { .bitrate = 60, .hw_value = ATH11K_HW_RATE_OFDM_6M }, + { .bitrate = 90, .hw_value = ATH11K_HW_RATE_OFDM_9M }, + { .bitrate = 120, .hw_value = ATH11K_HW_RATE_OFDM_12M }, + { .bitrate = 180, .hw_value = ATH11K_HW_RATE_OFDM_18M }, + { .bitrate = 240, .hw_value = ATH11K_HW_RATE_OFDM_24M }, + { .bitrate = 360, .hw_value = ATH11K_HW_RATE_OFDM_36M }, + { .bitrate = 480, .hw_value = ATH11K_HW_RATE_OFDM_48M }, + { .bitrate = 540, .hw_value = ATH11K_HW_RATE_OFDM_54M }, +}; + +static const int +ath11k_phymodes[NUM_NL80211_BANDS][ATH11K_CHAN_WIDTH_NUM] = { + [NL80211_BAND_2GHZ] = { + [NL80211_CHAN_WIDTH_5] = MODE_UNKNOWN, + [NL80211_CHAN_WIDTH_10] = MODE_UNKNOWN, + [NL80211_CHAN_WIDTH_20_NOHT] = MODE_11AX_HE20_2G, + [NL80211_CHAN_WIDTH_20] = MODE_11AX_HE20_2G, + [NL80211_CHAN_WIDTH_40] = MODE_11AX_HE40_2G, + [NL80211_CHAN_WIDTH_80] = MODE_11AX_HE80_2G, + [NL80211_CHAN_WIDTH_80P80] = MODE_UNKNOWN, + [NL80211_CHAN_WIDTH_160] = MODE_UNKNOWN, + }, + [NL80211_BAND_5GHZ] = { + [NL80211_CHAN_WIDTH_5] = MODE_UNKNOWN, + [NL80211_CHAN_WIDTH_10] = MODE_UNKNOWN, + [NL80211_CHAN_WIDTH_20_NOHT] = MODE_11AX_HE20, + [NL80211_CHAN_WIDTH_20] = MODE_11AX_HE20, + [NL80211_CHAN_WIDTH_40] = MODE_11AX_HE40, + [NL80211_CHAN_WIDTH_80] = MODE_11AX_HE80, + [NL80211_CHAN_WIDTH_160] = MODE_11AX_HE160, + [NL80211_CHAN_WIDTH_80P80] = MODE_11AX_HE80_80, + }, +}; + +const struct htt_rx_ring_tlv_filter ath11k_mac_mon_status_filter_default = { + .rx_filter = HTT_RX_FILTER_TLV_FLAGS_MPDU_START | + HTT_RX_FILTER_TLV_FLAGS_PPDU_END | + HTT_RX_FILTER_TLV_FLAGS_PPDU_END_STATUS_DONE, + .pkt_filter_flags0 = HTT_RX_FP_MGMT_FILTER_FLAGS0, + .pkt_filter_flags1 = HTT_RX_FP_MGMT_FILTER_FLAGS1, + .pkt_filter_flags2 = HTT_RX_FP_CTRL_FILTER_FLASG2, + .pkt_filter_flags3 = HTT_RX_FP_DATA_FILTER_FLASG3 | + HTT_RX_FP_CTRL_FILTER_FLASG3 +}; + +#define ATH11K_MAC_FIRST_OFDM_RATE_IDX 4 +#define ath11k_g_rates ath11k_legacy_rates +#define ath11k_g_rates_size (ARRAY_SIZE(ath11k_legacy_rates)) +#define ath11k_a_rates (ath11k_legacy_rates + 4) +#define ath11k_a_rates_size (ARRAY_SIZE(ath11k_legacy_rates) - 4) + +#define ATH11K_MAC_SCAN_TIMEOUT_MSECS 200 /* in msecs */ + +static const u32 ath11k_smps_map[] = { + [WLAN_HT_CAP_SM_PS_STATIC] = WMI_PEER_SMPS_STATIC, + [WLAN_HT_CAP_SM_PS_DYNAMIC] = WMI_PEER_SMPS_DYNAMIC, + [WLAN_HT_CAP_SM_PS_INVALID] = WMI_PEER_SMPS_PS_NONE, + [WLAN_HT_CAP_SM_PS_DISABLED] = WMI_PEER_SMPS_PS_NONE, +}; + +int ath11k_mac_hw_ratecode_to_legacy_rate(u8 hw_rc, u8 preamble, u8 *rateidx, + u16 *rate) +{ + /* As default, it is OFDM rates */ + int i = ATH11K_MAC_FIRST_OFDM_RATE_IDX; + int max_rates_idx = ath11k_g_rates_size; + + if (preamble == WMI_RATE_PREAMBLE_CCK) { + hw_rc &= ~ATH11k_HW_RATECODE_CCK_SHORT_PREAM_MASK; + i = 0; + max_rates_idx = ATH11K_MAC_FIRST_OFDM_RATE_IDX; + } + + while (i < max_rates_idx) { + if (hw_rc == ath11k_legacy_rates[i].hw_value) { + *rateidx = i; + *rate = ath11k_legacy_rates[i].bitrate; + return 0; + } + i++; + } + + return -EINVAL; +} + +static int get_num_chains(u32 mask) +{ + int num_chains = 0; + + while (mask) { + if (mask & BIT(0)) + num_chains++; + mask >>= 1; + } + + return num_chains; +} + +u8 ath11k_mac_bitrate_to_idx(const struct ieee80211_supported_band *sband, + u32 bitrate) +{ + int i; + + for (i = 0; i < sband->n_bitrates; i++) + if (sband->bitrates[i].bitrate == bitrate) + return i; + + return 0; +} + +static u32 +ath11k_mac_max_ht_nss(const u8 ht_mcs_mask[IEEE80211_HT_MCS_MASK_LEN]) +{ + int nss; + + for (nss = IEEE80211_HT_MCS_MASK_LEN - 1; nss >= 0; nss--) + if (ht_mcs_mask[nss]) + return nss + 1; + + return 1; +} + +static u32 +ath11k_mac_max_vht_nss(const u16 vht_mcs_mask[NL80211_VHT_NSS_MAX]) +{ + int nss; + + for (nss = NL80211_VHT_NSS_MAX - 1; nss >= 0; nss--) + if (vht_mcs_mask[nss]) + return nss + 1; + + return 1; +} + +static u8 ath11k_parse_mpdudensity(u8 mpdudensity) +{ +/* 802.11n D2.0 defined values for "Minimum MPDU Start Spacing": + * 0 for no restriction + * 1 for 1/4 us + * 2 for 1/2 us + * 3 for 1 us + * 4 for 2 us + * 5 for 4 us + * 6 for 8 us + * 7 for 16 us + */ + switch (mpdudensity) { + case 0: + return 0; + case 1: + case 2: + case 3: + /* Our lower layer calculations limit our precision to + * 1 microsecond + */ + return 1; + case 4: + return 2; + case 5: + return 4; + case 6: + return 8; + case 7: + return 16; + default: + return 0; + } +} + +static int ath11k_mac_vif_chan(struct ieee80211_vif *vif, + struct cfg80211_chan_def *def) +{ + struct ieee80211_chanctx_conf *conf; + + rcu_read_lock(); + conf = rcu_dereference(vif->chanctx_conf); + if (!conf) { + rcu_read_unlock(); + return -ENOENT; + } + + *def = conf->def; + rcu_read_unlock(); + + return 0; +} + +static bool ath11k_mac_bitrate_is_cck(int bitrate) +{ + switch (bitrate) { + case 10: + case 20: + case 55: + case 110: + return true; + } + + return false; +} + +u8 ath11k_mac_hw_rate_to_idx(const struct ieee80211_supported_band *sband, + u8 hw_rate, bool cck) +{ + const struct ieee80211_rate *rate; + int i; + + for (i = 0; i < sband->n_bitrates; i++) { + rate = &sband->bitrates[i]; + + if (ath11k_mac_bitrate_is_cck(rate->bitrate) != cck) + continue; + + if (rate->hw_value == hw_rate) + return i; + else if (rate->flags & IEEE80211_RATE_SHORT_PREAMBLE && + rate->hw_value_short == hw_rate) + return i; + } + + return 0; +} + +static u8 ath11k_mac_bitrate_to_rate(int bitrate) +{ + return DIV_ROUND_UP(bitrate, 5) | + (ath11k_mac_bitrate_is_cck(bitrate) ? BIT(7) : 0); +} + +static void ath11k_get_arvif_iter(void *data, u8 *mac, + struct ieee80211_vif *vif) +{ + struct ath11k_vif_iter *arvif_iter = data; + struct ath11k_vif *arvif = (void *)vif->drv_priv; + + if (arvif->vdev_id == arvif_iter->vdev_id) + arvif_iter->arvif = arvif; +} + +struct ath11k_vif *ath11k_mac_get_arvif(struct ath11k *ar, u32 vdev_id) +{ + struct ath11k_vif_iter arvif_iter; + u32 flags; + + memset(&arvif_iter, 0, sizeof(struct ath11k_vif_iter)); + arvif_iter.vdev_id = vdev_id; + + flags = IEEE80211_IFACE_ITER_RESUME_ALL; + ieee80211_iterate_active_interfaces_atomic(ar->hw, + flags, + ath11k_get_arvif_iter, + &arvif_iter); + if (!arvif_iter.arvif) + return NULL; + + return arvif_iter.arvif; +} + +struct ath11k_vif *ath11k_mac_get_arvif_by_vdev_id(struct ath11k_base *ab, + u32 vdev_id) +{ + int i; + struct ath11k_pdev *pdev; + struct ath11k_vif *arvif; + + for (i = 0; i < ab->num_radios; i++) { + pdev = rcu_dereference(ab->pdevs_active[i]); + if (pdev && pdev->ar) { + arvif = ath11k_mac_get_arvif(pdev->ar, vdev_id); + if (arvif) + return arvif; + } + } + + return NULL; +} + +struct ath11k *ath11k_mac_get_ar_by_vdev_id(struct ath11k_base *ab, u32 vdev_id) +{ + int i; + struct ath11k_pdev *pdev; + struct ath11k_vif *arvif; + + for (i = 0; i < ab->num_radios; i++) { + pdev = rcu_dereference(ab->pdevs_active[i]); + if (pdev && pdev->ar) { + arvif = ath11k_mac_get_arvif(pdev->ar, vdev_id); + if (arvif) + return arvif->ar; + } + } + + return NULL; +} + +struct ath11k *ath11k_mac_get_ar_by_pdev_id(struct ath11k_base *ab, u32 pdev_id) +{ + int i; + struct ath11k_pdev *pdev; + + if (WARN_ON(pdev_id > ab->num_radios)) + return NULL; + + for (i = 0; i < ab->num_radios; i++) { + pdev = rcu_dereference(ab->pdevs_active[i]); + + if (pdev && pdev->pdev_id == pdev_id) + return (pdev->ar ? pdev->ar : NULL); + } + + return NULL; +} + +struct ath11k *ath11k_mac_get_ar_vdev_stop_status(struct ath11k_base *ab, + u32 vdev_id) +{ + int i; + struct ath11k_pdev *pdev; + struct ath11k *ar; + + for (i = 0; i < ab->num_radios; i++) { + pdev = rcu_dereference(ab->pdevs_active[i]); + if (pdev && pdev->ar) { + ar = pdev->ar; + + spin_lock_bh(&ar->data_lock); + if (ar->vdev_stop_status.stop_in_progress && + ar->vdev_stop_status.vdev_id == vdev_id) { + ar->vdev_stop_status.stop_in_progress = false; + spin_unlock_bh(&ar->data_lock); + return ar; + } + spin_unlock_bh(&ar->data_lock); + } + } + return NULL; +} + +static void ath11k_pdev_caps_update(struct ath11k *ar) +{ + struct ath11k_base *ab = ar->ab; + + ar->max_tx_power = ab->target_caps.hw_max_tx_power; + + /* FIXME Set min_tx_power to ab->target_caps.hw_min_tx_power. + * But since the received value in svcrdy is same as hw_max_tx_power, + * we can set ar->min_tx_power to 0 currently until + * this is fixed in firmware + */ + ar->min_tx_power = 0; + + ar->txpower_limit_2g = ar->max_tx_power; + ar->txpower_limit_5g = ar->max_tx_power; + ar->txpower_scale = WMI_HOST_TP_SCALE_MAX; +} + +static int ath11k_mac_txpower_recalc(struct ath11k *ar) +{ + struct ath11k_pdev *pdev = ar->pdev; + struct ath11k_vif *arvif; + int ret, txpower = -1; + u32 param; + + lockdep_assert_held(&ar->conf_mutex); + + list_for_each_entry(arvif, &ar->arvifs, list) { + if (arvif->txpower <= 0) + continue; + + if (txpower == -1) + txpower = arvif->txpower; + else + txpower = min(txpower, arvif->txpower); + } + + if (txpower == -1) + return 0; + + /* txpwr is set as 2 units per dBm in FW*/ + txpower = min_t(u32, max_t(u32, ar->min_tx_power, txpower), + ar->max_tx_power) * 2; + + ath11k_dbg(ar->ab, ATH11K_DBG_MAC, "txpower to set in hw %d\n", + txpower / 2); + + if ((pdev->cap.supported_bands & WMI_HOST_WLAN_2G_CAP) && + ar->txpower_limit_2g != txpower) { + param = WMI_PDEV_PARAM_TXPOWER_LIMIT2G; + ret = ath11k_wmi_pdev_set_param(ar, param, + txpower, ar->pdev->pdev_id); + if (ret) + goto fail; + ar->txpower_limit_2g = txpower; + } + + if ((pdev->cap.supported_bands & WMI_HOST_WLAN_5G_CAP) && + ar->txpower_limit_5g != txpower) { + param = WMI_PDEV_PARAM_TXPOWER_LIMIT5G; + ret = ath11k_wmi_pdev_set_param(ar, param, + txpower, ar->pdev->pdev_id); + if (ret) + goto fail; + ar->txpower_limit_5g = txpower; + } + + return 0; + +fail: + ath11k_warn(ar->ab, "failed to recalc txpower limit %d using pdev param %d: %d\n", + txpower / 2, param, ret); + return ret; +} + +static int ath11k_recalc_rtscts_prot(struct ath11k_vif *arvif) +{ + struct ath11k *ar = arvif->ar; + u32 vdev_param, rts_cts = 0; + int ret; + + lockdep_assert_held(&ar->conf_mutex); + + vdev_param = WMI_VDEV_PARAM_ENABLE_RTSCTS; + + /* Enable RTS/CTS protection for sw retries (when legacy stations + * are in BSS) or by default only for second rate series. + * TODO: Check if we need to enable CTS 2 Self in any case + */ + rts_cts = WMI_USE_RTS_CTS; + + if (arvif->num_legacy_stations > 0) + rts_cts |= WMI_RTSCTS_ACROSS_SW_RETRIES << 4; + else + rts_cts |= WMI_RTSCTS_FOR_SECOND_RATESERIES << 4; + + /* Need not send duplicate param value to firmware */ + if (arvif->rtscts_prot_mode == rts_cts) + return 0; + + arvif->rtscts_prot_mode = rts_cts; + + ath11k_dbg(ar->ab, ATH11K_DBG_MAC, "mac vdev %d recalc rts/cts prot %d\n", + arvif->vdev_id, rts_cts); + + ret = ath11k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id, + vdev_param, rts_cts); + if (ret) + ath11k_warn(ar->ab, "failed to recalculate rts/cts prot for vdev %d: %d\n", + arvif->vdev_id, ret); + + return ret; +} + +static int ath11k_mac_set_kickout(struct ath11k_vif *arvif) +{ + struct ath11k *ar = arvif->ar; + u32 param; + int ret; + + ret = ath11k_wmi_pdev_set_param(ar, WMI_PDEV_PARAM_STA_KICKOUT_TH, + ATH11K_KICKOUT_THRESHOLD, + ar->pdev->pdev_id); + if (ret) { + ath11k_warn(ar->ab, "failed to set kickout threshold on vdev %i: %d\n", + arvif->vdev_id, ret); + return ret; + } + + param = WMI_VDEV_PARAM_AP_KEEPALIVE_MIN_IDLE_INACTIVE_TIME_SECS; + ret = ath11k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id, param, + ATH11K_KEEPALIVE_MIN_IDLE); + if (ret) { + ath11k_warn(ar->ab, "failed to set keepalive minimum idle time on vdev %i: %d\n", + arvif->vdev_id, ret); + return ret; + } + + param = WMI_VDEV_PARAM_AP_KEEPALIVE_MAX_IDLE_INACTIVE_TIME_SECS; + ret = ath11k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id, param, + ATH11K_KEEPALIVE_MAX_IDLE); + if (ret) { + ath11k_warn(ar->ab, "failed to set keepalive maximum idle time on vdev %i: %d\n", + arvif->vdev_id, ret); + return ret; + } + + param = WMI_VDEV_PARAM_AP_KEEPALIVE_MAX_UNRESPONSIVE_TIME_SECS; + ret = ath11k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id, param, + ATH11K_KEEPALIVE_MAX_UNRESPONSIVE); + if (ret) { + ath11k_warn(ar->ab, "failed to set keepalive maximum unresponsive time on vdev %i: %d\n", + arvif->vdev_id, ret); + return ret; + } + + return 0; +} + +void ath11k_mac_peer_cleanup_all(struct ath11k *ar) +{ + struct ath11k_peer *peer, *tmp; + struct ath11k_base *ab = ar->ab; + + lockdep_assert_held(&ar->conf_mutex); + + spin_lock_bh(&ab->base_lock); + list_for_each_entry_safe(peer, tmp, &ab->peers, list) { + ath11k_peer_rx_tid_cleanup(ar, peer); + list_del(&peer->list); + kfree(peer); + } + spin_unlock_bh(&ab->base_lock); + + ar->num_peers = 0; + ar->num_stations = 0; +} + +static int ath11k_monitor_vdev_up(struct ath11k *ar, int vdev_id) +{ + int ret = 0; + + ret = ath11k_wmi_vdev_up(ar, vdev_id, 0, ar->mac_addr); + if (ret) { + ath11k_warn(ar->ab, "failed to put up monitor vdev %i: %d\n", + vdev_id, ret); + return ret; + } + + ath11k_dbg(ar->ab, ATH11K_DBG_MAC, "mac monitor vdev %i started\n", + vdev_id); + return 0; +} + +static int ath11k_mac_op_config(struct ieee80211_hw *hw, u32 changed) +{ + struct ath11k *ar = hw->priv; + int ret = 0; + + /* mac80211 requires this op to be present and that's why + * there's an empty function, this can be extended when + * required. + */ + + mutex_lock(&ar->conf_mutex); + + /* TODO: Handle configuration changes as appropriate */ + + mutex_unlock(&ar->conf_mutex); + + return ret; +} + +static int ath11k_mac_setup_bcn_tmpl(struct ath11k_vif *arvif) +{ + struct ath11k *ar = arvif->ar; + struct ath11k_base *ab = ar->ab; + struct ieee80211_hw *hw = ar->hw; + struct ieee80211_vif *vif = arvif->vif; + struct ieee80211_mutable_offsets offs = {}; + struct sk_buff *bcn; + int ret; + + if (arvif->vdev_type != WMI_VDEV_TYPE_AP) + return 0; + + bcn = ieee80211_beacon_get_template(hw, vif, &offs); + if (!bcn) { + ath11k_warn(ab, "failed to get beacon template from mac80211\n"); + return -EPERM; + } + + ret = ath11k_wmi_bcn_tmpl(ar, arvif->vdev_id, &offs, bcn); + + kfree_skb(bcn); + + if (ret) + ath11k_warn(ab, "failed to submit beacon template command: %d\n", + ret); + + return ret; +} + +static void ath11k_control_beaconing(struct ath11k_vif *arvif, + struct ieee80211_bss_conf *info) +{ + struct ath11k *ar = arvif->ar; + int ret = 0; + + lockdep_assert_held(&arvif->ar->conf_mutex); + + if (!info->enable_beacon) { + ret = ath11k_wmi_vdev_down(ar, arvif->vdev_id); + if (ret) + ath11k_warn(ar->ab, "failed to down vdev_id %i: %d\n", + arvif->vdev_id, ret); + + arvif->is_up = false; + return; + } + + /* Install the beacon template to the FW */ + ret = ath11k_mac_setup_bcn_tmpl(arvif); + if (ret) { + ath11k_warn(ar->ab, "failed to update bcn tmpl during vdev up: %d\n", + ret); + return; + } + + arvif->tx_seq_no = 0x1000; + + arvif->aid = 0; + + ether_addr_copy(arvif->bssid, info->bssid); + + ret = ath11k_wmi_vdev_up(arvif->ar, arvif->vdev_id, arvif->aid, + arvif->bssid); + if (ret) { + ath11k_warn(ar->ab, "failed to bring up vdev %d: %i\n", + arvif->vdev_id, ret); + return; + } + + arvif->is_up = true; + + ath11k_dbg(ar->ab, ATH11K_DBG_MAC, "mac vdev %d up\n", arvif->vdev_id); +} + +static void ath11k_peer_assoc_h_basic(struct ath11k *ar, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct peer_assoc_params *arg) +{ + struct ath11k_vif *arvif = (void *)vif->drv_priv; + u32 aid; + + lockdep_assert_held(&ar->conf_mutex); + + if (vif->type == NL80211_IFTYPE_STATION) + aid = vif->bss_conf.aid; + else + aid = sta->aid; + + ether_addr_copy(arg->peer_mac, sta->addr); + arg->vdev_id = arvif->vdev_id; + arg->peer_associd = aid; + arg->auth_flag = true; + /* TODO: STA WAR in ath10k for listen interval required? */ + arg->peer_listen_intval = ar->hw->conf.listen_interval; + arg->peer_nss = 1; + arg->peer_caps = vif->bss_conf.assoc_capability; +} + +static void ath11k_peer_assoc_h_crypto(struct ath11k *ar, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct peer_assoc_params *arg) +{ + struct ieee80211_bss_conf *info = &vif->bss_conf; + struct cfg80211_chan_def def; + struct cfg80211_bss *bss; + const u8 *rsnie = NULL; + const u8 *wpaie = NULL; + + lockdep_assert_held(&ar->conf_mutex); + + if (WARN_ON(ath11k_mac_vif_chan(vif, &def))) + return; + + bss = cfg80211_get_bss(ar->hw->wiphy, def.chan, info->bssid, NULL, 0, + IEEE80211_BSS_TYPE_ANY, IEEE80211_PRIVACY_ANY); + if (bss) { + const struct cfg80211_bss_ies *ies; + + rcu_read_lock(); + rsnie = ieee80211_bss_get_ie(bss, WLAN_EID_RSN); + + ies = rcu_dereference(bss->ies); + + wpaie = cfg80211_find_vendor_ie(WLAN_OUI_MICROSOFT, + WLAN_OUI_TYPE_MICROSOFT_WPA, + ies->data, + ies->len); + rcu_read_unlock(); + cfg80211_put_bss(ar->hw->wiphy, bss); + } + + /* FIXME: base on RSN IE/WPA IE is a correct idea? */ + if (rsnie || wpaie) { + ath11k_dbg(ar->ab, ATH11K_DBG_WMI, + "%s: rsn ie found\n", __func__); + arg->need_ptk_4_way = true; + } + + if (wpaie) { + ath11k_dbg(ar->ab, ATH11K_DBG_WMI, + "%s: wpa ie found\n", __func__); + arg->need_gtk_2_way = true; + } + + if (sta->mfp) { + /* TODO: Need to check if FW supports PMF? */ + arg->is_pmf_enabled = true; + } + + /* TODO: safe_mode_enabled (bypass 4-way handshake) flag req? */ +} + +static void ath11k_peer_assoc_h_rates(struct ath11k *ar, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct peer_assoc_params *arg) +{ + struct ath11k_vif *arvif = (void *)vif->drv_priv; + struct wmi_rate_set_arg *rateset = &arg->peer_legacy_rates; + struct cfg80211_chan_def def; + const struct ieee80211_supported_band *sband; + const struct ieee80211_rate *rates; + enum nl80211_band band; + u32 ratemask; + u8 rate; + int i; + + lockdep_assert_held(&ar->conf_mutex); + + if (WARN_ON(ath11k_mac_vif_chan(vif, &def))) + return; + + band = def.chan->band; + sband = ar->hw->wiphy->bands[band]; + ratemask = sta->supp_rates[band]; + ratemask &= arvif->bitrate_mask.control[band].legacy; + rates = sband->bitrates; + + rateset->num_rates = 0; + + for (i = 0; i < 32; i++, ratemask >>= 1, rates++) { + if (!(ratemask & 1)) + continue; + + rate = ath11k_mac_bitrate_to_rate(rates->bitrate); + rateset->rates[rateset->num_rates] = rate; + rateset->num_rates++; + } +} + +static bool +ath11k_peer_assoc_h_ht_masked(const u8 ht_mcs_mask[IEEE80211_HT_MCS_MASK_LEN]) +{ + int nss; + + for (nss = 0; nss < IEEE80211_HT_MCS_MASK_LEN; nss++) + if (ht_mcs_mask[nss]) + return false; + + return true; +} + +static bool +ath11k_peer_assoc_h_vht_masked(const u16 vht_mcs_mask[NL80211_VHT_NSS_MAX]) +{ + int nss; + + for (nss = 0; nss < NL80211_VHT_NSS_MAX; nss++) + if (vht_mcs_mask[nss]) + return false; + + return true; +} + +static void ath11k_peer_assoc_h_ht(struct ath11k *ar, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct peer_assoc_params *arg) +{ + const struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap; + struct ath11k_vif *arvif = (void *)vif->drv_priv; + struct cfg80211_chan_def def; + enum nl80211_band band; + const u8 *ht_mcs_mask; + int i, n; + u8 max_nss; + u32 stbc; + + lockdep_assert_held(&ar->conf_mutex); + + if (WARN_ON(ath11k_mac_vif_chan(vif, &def))) + return; + + if (!ht_cap->ht_supported) + return; + + band = def.chan->band; + ht_mcs_mask = arvif->bitrate_mask.control[band].ht_mcs; + + if (ath11k_peer_assoc_h_ht_masked(ht_mcs_mask)) + return; + + arg->ht_flag = true; + + arg->peer_max_mpdu = (1 << (IEEE80211_HT_MAX_AMPDU_FACTOR + + ht_cap->ampdu_factor)) - 1; + + arg->peer_mpdu_density = + ath11k_parse_mpdudensity(ht_cap->ampdu_density); + + arg->peer_ht_caps = ht_cap->cap; + arg->peer_rate_caps |= WMI_HOST_RC_HT_FLAG; + + if (ht_cap->cap & IEEE80211_HT_CAP_LDPC_CODING) + arg->ldpc_flag = true; + + if (sta->bandwidth >= IEEE80211_STA_RX_BW_40) { + arg->bw_40 = true; + arg->peer_rate_caps |= WMI_HOST_RC_CW40_FLAG; + } + + if (arvif->bitrate_mask.control[band].gi != NL80211_TXRATE_FORCE_LGI) { + if (ht_cap->cap & (IEEE80211_HT_CAP_SGI_20 | + IEEE80211_HT_CAP_SGI_40)) + arg->peer_rate_caps |= WMI_HOST_RC_SGI_FLAG; + } + + if (ht_cap->cap & IEEE80211_HT_CAP_TX_STBC) { + arg->peer_rate_caps |= WMI_HOST_RC_TX_STBC_FLAG; + arg->stbc_flag = true; + } + + if (ht_cap->cap & IEEE80211_HT_CAP_RX_STBC) { + stbc = ht_cap->cap & IEEE80211_HT_CAP_RX_STBC; + stbc = stbc >> IEEE80211_HT_CAP_RX_STBC_SHIFT; + stbc = stbc << WMI_HOST_RC_RX_STBC_FLAG_S; + arg->peer_rate_caps |= stbc; + arg->stbc_flag = true; + } + + if (ht_cap->mcs.rx_mask[1] && ht_cap->mcs.rx_mask[2]) + arg->peer_rate_caps |= WMI_HOST_RC_TS_FLAG; + else if (ht_cap->mcs.rx_mask[1]) + arg->peer_rate_caps |= WMI_HOST_RC_DS_FLAG; + + for (i = 0, n = 0, max_nss = 0; i < IEEE80211_HT_MCS_MASK_LEN * 8; i++) + if ((ht_cap->mcs.rx_mask[i / 8] & BIT(i % 8)) && + (ht_mcs_mask[i / 8] & BIT(i % 8))) { + max_nss = (i / 8) + 1; + arg->peer_ht_rates.rates[n++] = i; + } + + /* This is a workaround for HT-enabled STAs which break the spec + * and have no HT capabilities RX mask (no HT RX MCS map). + * + * As per spec, in section 20.3.5 Modulation and coding scheme (MCS), + * MCS 0 through 7 are mandatory in 20MHz with 800 ns GI at all STAs. + * + * Firmware asserts if such situation occurs. + */ + if (n == 0) { + arg->peer_ht_rates.num_rates = 8; + for (i = 0; i < arg->peer_ht_rates.num_rates; i++) + arg->peer_ht_rates.rates[i] = i; + } else { + arg->peer_ht_rates.num_rates = n; + arg->peer_nss = min(sta->rx_nss, max_nss); + } + + ath11k_dbg(ar->ab, ATH11K_DBG_MAC, "mac ht peer %pM mcs cnt %d nss %d\n", + arg->peer_mac, + arg->peer_ht_rates.num_rates, + arg->peer_nss); +} + +static int ath11k_mac_get_max_vht_mcs_map(u16 mcs_map, int nss) +{ + switch ((mcs_map >> (2 * nss)) & 0x3) { + case IEEE80211_VHT_MCS_SUPPORT_0_7: return BIT(8) - 1; + case IEEE80211_VHT_MCS_SUPPORT_0_8: return BIT(9) - 1; + case IEEE80211_VHT_MCS_SUPPORT_0_9: return BIT(10) - 1; + } + return 0; +} + +static u16 +ath11k_peer_assoc_h_vht_limit(u16 tx_mcs_set, + const u16 vht_mcs_limit[NL80211_VHT_NSS_MAX]) +{ + int idx_limit; + int nss; + u16 mcs_map; + u16 mcs; + + for (nss = 0; nss < NL80211_VHT_NSS_MAX; nss++) { + mcs_map = ath11k_mac_get_max_vht_mcs_map(tx_mcs_set, nss) & + vht_mcs_limit[nss]; + + if (mcs_map) + idx_limit = fls(mcs_map) - 1; + else + idx_limit = -1; + + switch (idx_limit) { + case 0: /* fall through */ + case 1: /* fall through */ + case 2: /* fall through */ + case 3: /* fall through */ + case 4: /* fall through */ + case 5: /* fall through */ + case 6: /* fall through */ + case 7: + mcs = IEEE80211_VHT_MCS_SUPPORT_0_7; + break; + case 8: + mcs = IEEE80211_VHT_MCS_SUPPORT_0_8; + break; + case 9: + mcs = IEEE80211_VHT_MCS_SUPPORT_0_9; + break; + default: + WARN_ON(1); + /* fall through */ + case -1: + mcs = IEEE80211_VHT_MCS_NOT_SUPPORTED; + break; + } + + tx_mcs_set &= ~(0x3 << (nss * 2)); + tx_mcs_set |= mcs << (nss * 2); + } + + return tx_mcs_set; +} + +static void ath11k_peer_assoc_h_vht(struct ath11k *ar, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct peer_assoc_params *arg) +{ + const struct ieee80211_sta_vht_cap *vht_cap = &sta->vht_cap; + struct ath11k_vif *arvif = (void *)vif->drv_priv; + struct cfg80211_chan_def def; + enum nl80211_band band; + const u16 *vht_mcs_mask; + u8 ampdu_factor; + u8 max_nss, vht_mcs; + int i; + + if (WARN_ON(ath11k_mac_vif_chan(vif, &def))) + return; + + if (!vht_cap->vht_supported) + return; + + band = def.chan->band; + vht_mcs_mask = arvif->bitrate_mask.control[band].vht_mcs; + + if (ath11k_peer_assoc_h_vht_masked(vht_mcs_mask)) + return; + + arg->vht_flag = true; + + /* TODO: similar flags required? */ + arg->vht_capable = true; + + if (def.chan->band == NL80211_BAND_2GHZ) + arg->vht_ng_flag = true; + + arg->peer_vht_caps = vht_cap->cap; + + ampdu_factor = (vht_cap->cap & + IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK) >> + IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_SHIFT; + + /* Workaround: Some Netgear/Linksys 11ac APs set Rx A-MPDU factor to + * zero in VHT IE. Using it would result in degraded throughput. + * arg->peer_max_mpdu at this point contains HT max_mpdu so keep + * it if VHT max_mpdu is smaller. + */ + arg->peer_max_mpdu = max(arg->peer_max_mpdu, + (1U << (IEEE80211_HT_MAX_AMPDU_FACTOR + + ampdu_factor)) - 1); + + if (sta->bandwidth == IEEE80211_STA_RX_BW_80) + arg->bw_80 = true; + + if (sta->bandwidth == IEEE80211_STA_RX_BW_160) + arg->bw_160 = true; + + /* Calculate peer NSS capability from VHT capabilities if STA + * supports VHT. + */ + for (i = 0, max_nss = 0, vht_mcs = 0; i < NL80211_VHT_NSS_MAX; i++) { + vht_mcs = __le16_to_cpu(vht_cap->vht_mcs.rx_mcs_map) >> + (2 * i) & 3; + + if (vht_mcs != IEEE80211_VHT_MCS_NOT_SUPPORTED && + vht_mcs_mask[i]) + max_nss = i + 1; + } + arg->peer_nss = min(sta->rx_nss, max_nss); + arg->rx_max_rate = __le16_to_cpu(vht_cap->vht_mcs.rx_highest); + arg->rx_mcs_set = __le16_to_cpu(vht_cap->vht_mcs.rx_mcs_map); + arg->tx_max_rate = __le16_to_cpu(vht_cap->vht_mcs.tx_highest); + arg->tx_mcs_set = ath11k_peer_assoc_h_vht_limit( + __le16_to_cpu(vht_cap->vht_mcs.tx_mcs_map), vht_mcs_mask); + + /* In IPQ8074 platform, VHT mcs rate 10 and 11 is enabled by default. + * VHT mcs rate 10 and 11 is not suppoerted in 11ac standard. + * so explicitly disable the VHT MCS rate 10 and 11 in 11ac mode. + */ + arg->tx_mcs_set &= ~IEEE80211_VHT_MCS_SUPPORT_0_11_MASK; + arg->tx_mcs_set |= IEEE80211_DISABLE_VHT_MCS_SUPPORT_0_11; + + /* TODO: Check */ + arg->tx_max_mcs_nss = 0xFF; + + ath11k_dbg(ar->ab, ATH11K_DBG_MAC, "mac vht peer %pM max_mpdu %d flags 0x%x\n", + sta->addr, arg->peer_max_mpdu, arg->peer_flags); + + /* TODO: rxnss_override */ +} + +static void ath11k_peer_assoc_h_he(struct ath11k *ar, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct peer_assoc_params *arg) +{ + /* TODO: Implementation */ +} + +static void ath11k_peer_assoc_h_smps(struct ieee80211_sta *sta, + struct peer_assoc_params *arg) +{ + const struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap; + int smps; + + if (!ht_cap->ht_supported) + return; + + smps = ht_cap->cap & IEEE80211_HT_CAP_SM_PS; + smps >>= IEEE80211_HT_CAP_SM_PS_SHIFT; + + switch (smps) { + case WLAN_HT_CAP_SM_PS_STATIC: + arg->static_mimops_flag = true; + break; + case WLAN_HT_CAP_SM_PS_DYNAMIC: + arg->dynamic_mimops_flag = true; + break; + case WLAN_HT_CAP_SM_PS_DISABLED: + arg->spatial_mux_flag = true; + break; + default: + break; + } +} + +static void ath11k_peer_assoc_h_qos(struct ath11k *ar, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct peer_assoc_params *arg) +{ + struct ath11k_vif *arvif = (void *)vif->drv_priv; + + switch (arvif->vdev_type) { + case WMI_VDEV_TYPE_AP: + if (sta->wme) { + /* TODO: Check WME vs QoS */ + arg->is_wme_set = true; + arg->qos_flag = true; + } + + if (sta->wme && sta->uapsd_queues) { + /* TODO: Check WME vs QoS */ + arg->is_wme_set = true; + arg->apsd_flag = true; + arg->peer_rate_caps |= WMI_HOST_RC_UAPSD_FLAG; + } + break; + case WMI_VDEV_TYPE_STA: + if (sta->wme) { + arg->is_wme_set = true; + arg->qos_flag = true; + } + break; + default: + break; + } + + ath11k_dbg(ar->ab, ATH11K_DBG_MAC, "mac peer %pM qos %d\n", + sta->addr, arg->qos_flag); +} + +static int ath11k_peer_assoc_qos_ap(struct ath11k *ar, + struct ath11k_vif *arvif, + struct ieee80211_sta *sta) +{ + struct ap_ps_params params; + u32 max_sp; + u32 uapsd; + int ret; + + lockdep_assert_held(&ar->conf_mutex); + + params.vdev_id = arvif->vdev_id; + + ath11k_dbg(ar->ab, ATH11K_DBG_MAC, "mac uapsd_queues 0x%x max_sp %d\n", + sta->uapsd_queues, sta->max_sp); + + uapsd = 0; + if (sta->uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_VO) + uapsd |= WMI_AP_PS_UAPSD_AC3_DELIVERY_EN | + WMI_AP_PS_UAPSD_AC3_TRIGGER_EN; + if (sta->uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_VI) + uapsd |= WMI_AP_PS_UAPSD_AC2_DELIVERY_EN | + WMI_AP_PS_UAPSD_AC2_TRIGGER_EN; + if (sta->uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_BK) + uapsd |= WMI_AP_PS_UAPSD_AC1_DELIVERY_EN | + WMI_AP_PS_UAPSD_AC1_TRIGGER_EN; + if (sta->uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_BE) + uapsd |= WMI_AP_PS_UAPSD_AC0_DELIVERY_EN | + WMI_AP_PS_UAPSD_AC0_TRIGGER_EN; + + max_sp = 0; + if (sta->max_sp < MAX_WMI_AP_PS_PEER_PARAM_MAX_SP) + max_sp = sta->max_sp; + + params.param = WMI_AP_PS_PEER_PARAM_UAPSD; + params.value = uapsd; + ret = ath11k_wmi_send_set_ap_ps_param_cmd(ar, sta->addr, ¶ms); + if (ret) + goto err; + + params.param = WMI_AP_PS_PEER_PARAM_MAX_SP; + params.value = max_sp; + ret = ath11k_wmi_send_set_ap_ps_param_cmd(ar, sta->addr, ¶ms); + if (ret) + goto err; + + /* TODO revisit during testing */ + params.param = WMI_AP_PS_PEER_PARAM_SIFS_RESP_FRMTYPE; + params.value = DISABLE_SIFS_RESPONSE_TRIGGER; + ret = ath11k_wmi_send_set_ap_ps_param_cmd(ar, sta->addr, ¶ms); + if (ret) + goto err; + + params.param = WMI_AP_PS_PEER_PARAM_SIFS_RESP_UAPSD; + params.value = DISABLE_SIFS_RESPONSE_TRIGGER; + ret = ath11k_wmi_send_set_ap_ps_param_cmd(ar, sta->addr, ¶ms); + if (ret) + goto err; + + return 0; + +err: + ath11k_warn(ar->ab, "failed to set ap ps peer param %d for vdev %i: %d\n", + params.param, arvif->vdev_id, ret); + return ret; +} + +static bool ath11k_mac_sta_has_ofdm_only(struct ieee80211_sta *sta) +{ + return sta->supp_rates[NL80211_BAND_2GHZ] >> + ATH11K_MAC_FIRST_OFDM_RATE_IDX; +} + +static enum wmi_phy_mode ath11k_mac_get_phymode_vht(struct ath11k *ar, + struct ieee80211_sta *sta) +{ + if (sta->bandwidth == IEEE80211_STA_RX_BW_160) { + switch (sta->vht_cap.cap & + IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK) { + case IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ: + return MODE_11AC_VHT160; + case IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ: + return MODE_11AC_VHT80_80; + default: + /* not sure if this is a valid case? */ + return MODE_11AC_VHT160; + } + } + + if (sta->bandwidth == IEEE80211_STA_RX_BW_80) + return MODE_11AC_VHT80; + + if (sta->bandwidth == IEEE80211_STA_RX_BW_40) + return MODE_11AC_VHT40; + + if (sta->bandwidth == IEEE80211_STA_RX_BW_20) + return MODE_11AC_VHT20; + + return MODE_UNKNOWN; +} + +static void ath11k_peer_assoc_h_phymode(struct ath11k *ar, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct peer_assoc_params *arg) +{ + struct ath11k_vif *arvif = (void *)vif->drv_priv; + struct cfg80211_chan_def def; + enum nl80211_band band; + const u8 *ht_mcs_mask; + const u16 *vht_mcs_mask; + enum wmi_phy_mode phymode = MODE_UNKNOWN; + + if (WARN_ON(ath11k_mac_vif_chan(vif, &def))) + return; + + band = def.chan->band; + ht_mcs_mask = arvif->bitrate_mask.control[band].ht_mcs; + vht_mcs_mask = arvif->bitrate_mask.control[band].vht_mcs; + + switch (band) { + case NL80211_BAND_2GHZ: + if (sta->vht_cap.vht_supported && + !ath11k_peer_assoc_h_vht_masked(vht_mcs_mask)) { + if (sta->bandwidth == IEEE80211_STA_RX_BW_40) + phymode = MODE_11AC_VHT40; + else + phymode = MODE_11AC_VHT20; + } else if (sta->ht_cap.ht_supported && + !ath11k_peer_assoc_h_ht_masked(ht_mcs_mask)) { + if (sta->bandwidth == IEEE80211_STA_RX_BW_40) + phymode = MODE_11NG_HT40; + else + phymode = MODE_11NG_HT20; + } else if (ath11k_mac_sta_has_ofdm_only(sta)) { + phymode = MODE_11G; + } else { + phymode = MODE_11B; + } + /* TODO: HE */ + + break; + case NL80211_BAND_5GHZ: + /* Check VHT first */ + if (sta->vht_cap.vht_supported && + !ath11k_peer_assoc_h_vht_masked(vht_mcs_mask)) { + phymode = ath11k_mac_get_phymode_vht(ar, sta); + } else if (sta->ht_cap.ht_supported && + !ath11k_peer_assoc_h_ht_masked(ht_mcs_mask)) { + if (sta->bandwidth >= IEEE80211_STA_RX_BW_40) + phymode = MODE_11NA_HT40; + else + phymode = MODE_11NA_HT20; + } else { + phymode = MODE_11A; + } + /* TODO: HE Phymode */ + break; + default: + break; + } + + ath11k_dbg(ar->ab, ATH11K_DBG_MAC, "mac peer %pM phymode %s\n", + sta->addr, ath11k_wmi_phymode_str(phymode)); + + arg->peer_phymode = phymode; + WARN_ON(phymode == MODE_UNKNOWN); +} + +static void ath11k_peer_assoc_prepare(struct ath11k *ar, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct peer_assoc_params *arg, + bool reassoc) +{ + lockdep_assert_held(&ar->conf_mutex); + + memset(arg, 0, sizeof(*arg)); + + reinit_completion(&ar->peer_assoc_done); + + arg->peer_new_assoc = !reassoc; + ath11k_peer_assoc_h_basic(ar, vif, sta, arg); + ath11k_peer_assoc_h_crypto(ar, vif, sta, arg); + ath11k_peer_assoc_h_rates(ar, vif, sta, arg); + ath11k_peer_assoc_h_ht(ar, vif, sta, arg); + ath11k_peer_assoc_h_vht(ar, vif, sta, arg); + ath11k_peer_assoc_h_he(ar, vif, sta, arg); + ath11k_peer_assoc_h_qos(ar, vif, sta, arg); + ath11k_peer_assoc_h_phymode(ar, vif, sta, arg); + ath11k_peer_assoc_h_smps(sta, arg); + + /* TODO: amsdu_disable req? */ +} + +static int ath11k_setup_peer_smps(struct ath11k *ar, struct ath11k_vif *arvif, + const u8 *addr, + const struct ieee80211_sta_ht_cap *ht_cap) +{ + int smps; + + if (!ht_cap->ht_supported) + return 0; + + smps = ht_cap->cap & IEEE80211_HT_CAP_SM_PS; + smps >>= IEEE80211_HT_CAP_SM_PS_SHIFT; + + if (smps >= ARRAY_SIZE(ath11k_smps_map)) + return -EINVAL; + + return ath11k_wmi_set_peer_param(ar, addr, arvif->vdev_id, + WMI_PEER_MIMO_PS_STATE, + ath11k_smps_map[smps]); +} + +static void ath11k_bss_assoc(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *bss_conf) +{ + struct ath11k *ar = hw->priv; + struct ath11k_vif *arvif = (void *)vif->drv_priv; + struct peer_assoc_params peer_arg; + struct ieee80211_sta *ap_sta; + int ret; + + lockdep_assert_held(&ar->conf_mutex); + + ath11k_dbg(ar->ab, ATH11K_DBG_MAC, "mac vdev %i assoc bssid %pM aid %d\n", + arvif->vdev_id, arvif->bssid, arvif->aid); + + rcu_read_lock(); + + ap_sta = ieee80211_find_sta(vif, bss_conf->bssid); + if (!ap_sta) { + ath11k_warn(ar->ab, "failed to find station entry for bss %pM vdev %i\n", + bss_conf->bssid, arvif->vdev_id); + rcu_read_unlock(); + return; + } + + ath11k_peer_assoc_prepare(ar, vif, ap_sta, &peer_arg, false); + + rcu_read_unlock(); + + ret = ath11k_wmi_send_peer_assoc_cmd(ar, &peer_arg); + if (ret) { + ath11k_warn(ar->ab, "failed to run peer assoc for %pM vdev %i: %d\n", + bss_conf->bssid, arvif->vdev_id, ret); + return; + } + + if (!wait_for_completion_timeout(&ar->peer_assoc_done, 1 * HZ)) { + ath11k_warn(ar->ab, "failed to get peer assoc conf event for %pM vdev %i\n", + bss_conf->bssid, arvif->vdev_id); + return; + } + + ret = ath11k_setup_peer_smps(ar, arvif, bss_conf->bssid, + &ap_sta->ht_cap); + if (ret) { + ath11k_warn(ar->ab, "failed to setup peer SMPS for vdev %d: %d\n", + arvif->vdev_id, ret); + return; + } + + WARN_ON(arvif->is_up); + + arvif->aid = bss_conf->aid; + ether_addr_copy(arvif->bssid, bss_conf->bssid); + + ret = ath11k_wmi_vdev_up(ar, arvif->vdev_id, arvif->aid, arvif->bssid); + if (ret) { + ath11k_warn(ar->ab, "failed to set vdev %d up: %d\n", + arvif->vdev_id, ret); + return; + } + + arvif->is_up = true; + + ath11k_dbg(ar->ab, ATH11K_DBG_MAC, + "mac vdev %d up (associated) bssid %pM aid %d\n", + arvif->vdev_id, bss_conf->bssid, bss_conf->aid); + + /* Authorize BSS Peer */ + ret = ath11k_wmi_set_peer_param(ar, arvif->bssid, + arvif->vdev_id, + WMI_PEER_AUTHORIZE, + 1); + if (ret) + ath11k_warn(ar->ab, "Unable to authorize BSS peer: %d\n", ret); +} + +static void ath11k_bss_disassoc(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + struct ath11k *ar = hw->priv; + struct ath11k_vif *arvif = (void *)vif->drv_priv; + int ret; + + lockdep_assert_held(&ar->conf_mutex); + + ath11k_dbg(ar->ab, ATH11K_DBG_MAC, "mac vdev %i disassoc bssid %pM\n", + arvif->vdev_id, arvif->bssid); + + ret = ath11k_wmi_vdev_down(ar, arvif->vdev_id); + if (ret) + ath11k_warn(ar->ab, "failed to down vdev %i: %d\n", + arvif->vdev_id, ret); + + arvif->is_up = false; + + /* TODO: cancel connection_loss_work */ +} + +static u32 ath11k_mac_get_rate_hw_value(int bitrate) +{ + u32 preamble; + u16 hw_value; + int rate; + size_t i; + + if (ath11k_mac_bitrate_is_cck(bitrate)) + preamble = WMI_RATE_PREAMBLE_CCK; + else + preamble = WMI_RATE_PREAMBLE_OFDM; + + for (i = 0; i < ARRAY_SIZE(ath11k_legacy_rates); i++) { + if (ath11k_legacy_rates[i].bitrate != bitrate) + continue; + + hw_value = ath11k_legacy_rates[i].hw_value; + rate = ATH11K_HW_RATE_CODE(hw_value, 0, preamble); + + return rate; + } + + return -EINVAL; +} + +static void ath11k_recalculate_mgmt_rate(struct ath11k *ar, + struct ieee80211_vif *vif, + struct cfg80211_chan_def *def) +{ + struct ath11k_vif *arvif = (void *)vif->drv_priv; + const struct ieee80211_supported_band *sband; + u8 basic_rate_idx; + int hw_rate_code; + u32 vdev_param; + u16 bitrate; + int ret; + + lockdep_assert_held(&ar->conf_mutex); + + sband = ar->hw->wiphy->bands[def->chan->band]; + basic_rate_idx = ffs(vif->bss_conf.basic_rates) - 1; + bitrate = sband->bitrates[basic_rate_idx].bitrate; + + hw_rate_code = ath11k_mac_get_rate_hw_value(bitrate); + if (hw_rate_code < 0) { + ath11k_warn(ar->ab, "bitrate not supported %d\n", bitrate); + return; + } + + vdev_param = WMI_VDEV_PARAM_MGMT_RATE; + ret = ath11k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id, vdev_param, + hw_rate_code); + if (ret) + ath11k_warn(ar->ab, "failed to set mgmt tx rate %d\n", ret); + + vdev_param = WMI_VDEV_PARAM_BEACON_RATE; + ret = ath11k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id, vdev_param, + hw_rate_code); + if (ret) + ath11k_warn(ar->ab, "failed to set beacon tx rate %d\n", ret); +} + +static void ath11k_mac_op_bss_info_changed(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *info, + u32 changed) +{ + struct ath11k *ar = hw->priv; + struct ath11k_vif *arvif = ath11k_vif_to_arvif(vif); + struct cfg80211_chan_def def; + u32 param_id, param_value; + enum nl80211_band band; + u32 vdev_param; + int mcast_rate; + u32 preamble; + u16 hw_value; + u16 bitrate; + int ret = 0; + u8 rateidx; + u32 rate; + + mutex_lock(&ar->conf_mutex); + + if (changed & BSS_CHANGED_BEACON_INT) { + arvif->beacon_interval = info->beacon_int; + + param_id = WMI_VDEV_PARAM_BEACON_INTERVAL; + ret = ath11k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id, + param_id, + arvif->beacon_interval); + if (ret) + ath11k_warn(ar->ab, "Failed to set beacon interval for VDEV: %d\n", + arvif->vdev_id); + else + ath11k_dbg(ar->ab, ATH11K_DBG_MAC, + "Beacon interval: %d set for VDEV: %d\n", + arvif->beacon_interval, arvif->vdev_id); + } + + if (changed & BSS_CHANGED_BEACON) { + param_id = WMI_PDEV_PARAM_BEACON_TX_MODE; + param_value = WMI_BEACON_STAGGERED_MODE; + ret = ath11k_wmi_pdev_set_param(ar, param_id, + param_value, ar->pdev->pdev_id); + if (ret) + ath11k_warn(ar->ab, "Failed to set beacon mode for VDEV: %d\n", + arvif->vdev_id); + else + ath11k_dbg(ar->ab, ATH11K_DBG_MAC, + "Set staggered beacon mode for VDEV: %d\n", + arvif->vdev_id); + + ret = ath11k_mac_setup_bcn_tmpl(arvif); + if (ret) + ath11k_warn(ar->ab, "failed to update bcn template: %d\n", + ret); + } + + if (changed & (BSS_CHANGED_BEACON_INFO | BSS_CHANGED_BEACON)) { + arvif->dtim_period = info->dtim_period; + + param_id = WMI_VDEV_PARAM_DTIM_PERIOD; + ret = ath11k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id, + param_id, + arvif->dtim_period); + + if (ret) + ath11k_warn(ar->ab, "Failed to set dtim period for VDEV %d: %i\n", + arvif->vdev_id, ret); + else + ath11k_dbg(ar->ab, ATH11K_DBG_MAC, + "DTIM period: %d set for VDEV: %d\n", + arvif->dtim_period, arvif->vdev_id); + } + + if (changed & BSS_CHANGED_SSID && + vif->type == NL80211_IFTYPE_AP) { + arvif->u.ap.ssid_len = info->ssid_len; + if (info->ssid_len) + memcpy(arvif->u.ap.ssid, info->ssid, info->ssid_len); + arvif->u.ap.hidden_ssid = info->hidden_ssid; + } + + if (changed & BSS_CHANGED_BSSID && !is_zero_ether_addr(info->bssid)) + ether_addr_copy(arvif->bssid, info->bssid); + + if (changed & BSS_CHANGED_BEACON_ENABLED) + ath11k_control_beaconing(arvif, info); + + if (changed & BSS_CHANGED_ERP_CTS_PROT) { + u32 cts_prot; + + cts_prot = !!(info->use_cts_prot); + param_id = WMI_VDEV_PARAM_PROTECTION_MODE; + + if (arvif->is_started) { + ret = ath11k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id, + param_id, cts_prot); + if (ret) + ath11k_warn(ar->ab, "Failed to set CTS prot for VDEV: %d\n", + arvif->vdev_id); + else + ath11k_dbg(ar->ab, ATH11K_DBG_MAC, "Set CTS prot: %d for VDEV: %d\n", + cts_prot, arvif->vdev_id); + } else { + ath11k_dbg(ar->ab, ATH11K_DBG_MAC, "defer protection mode setup, vdev is not ready yet\n"); + } + } + + if (changed & BSS_CHANGED_ERP_SLOT) { + u32 slottime; + + if (info->use_short_slot) + slottime = WMI_VDEV_SLOT_TIME_SHORT; /* 9us */ + + else + slottime = WMI_VDEV_SLOT_TIME_LONG; /* 20us */ + + param_id = WMI_VDEV_PARAM_SLOT_TIME; + ret = ath11k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id, + param_id, slottime); + if (ret) + ath11k_warn(ar->ab, "Failed to set erp slot for VDEV: %d\n", + arvif->vdev_id); + else + ath11k_dbg(ar->ab, ATH11K_DBG_MAC, + "Set slottime: %d for VDEV: %d\n", + slottime, arvif->vdev_id); + } + + if (changed & BSS_CHANGED_ERP_PREAMBLE) { + u32 preamble; + + if (info->use_short_preamble) + preamble = WMI_VDEV_PREAMBLE_SHORT; + else + preamble = WMI_VDEV_PREAMBLE_LONG; + + param_id = WMI_VDEV_PARAM_PREAMBLE; + ret = ath11k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id, + param_id, preamble); + if (ret) + ath11k_warn(ar->ab, "Failed to set preamble for VDEV: %d\n", + arvif->vdev_id); + else + ath11k_dbg(ar->ab, ATH11K_DBG_MAC, + "Set preamble: %d for VDEV: %d\n", + preamble, arvif->vdev_id); + } + + if (changed & BSS_CHANGED_ASSOC) { + if (info->assoc) + ath11k_bss_assoc(hw, vif, info); + else + ath11k_bss_disassoc(hw, vif); + } + + if (changed & BSS_CHANGED_TXPOWER) { + ath11k_dbg(ar->ab, ATH11K_DBG_MAC, "mac vdev_id %i txpower %d\n", + arvif->vdev_id, info->txpower); + + arvif->txpower = info->txpower; + ath11k_mac_txpower_recalc(ar); + } + + if (changed & BSS_CHANGED_MCAST_RATE && + !ath11k_mac_vif_chan(arvif->vif, &def)) { + band = def.chan->band; + mcast_rate = vif->bss_conf.mcast_rate[band]; + + if (mcast_rate > 0) + rateidx = mcast_rate - 1; + else + rateidx = ffs(vif->bss_conf.basic_rates) - 1; + + if (ar->pdev->cap.supported_bands & WMI_HOST_WLAN_5G_CAP) + rateidx += ATH11K_MAC_FIRST_OFDM_RATE_IDX; + + bitrate = ath11k_legacy_rates[rateidx].bitrate; + hw_value = ath11k_legacy_rates[rateidx].hw_value; + + if (ath11k_mac_bitrate_is_cck(bitrate)) + preamble = WMI_RATE_PREAMBLE_CCK; + else + preamble = WMI_RATE_PREAMBLE_OFDM; + + rate = ATH11K_HW_RATE_CODE(hw_value, 0, preamble); + + ath11k_dbg(ar->ab, ATH11K_DBG_MAC, + "mac vdev %d mcast_rate %x\n", + arvif->vdev_id, rate); + + vdev_param = WMI_VDEV_PARAM_MCAST_DATA_RATE; + ret = ath11k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id, + vdev_param, rate); + if (ret) + ath11k_warn(ar->ab, + "failed to set mcast rate on vdev %i: %d\n", + arvif->vdev_id, ret); + + vdev_param = WMI_VDEV_PARAM_BCAST_DATA_RATE; + ret = ath11k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id, + vdev_param, rate); + if (ret) + ath11k_warn(ar->ab, + "failed to set bcast rate on vdev %i: %d\n", + arvif->vdev_id, ret); + } + + if (changed & BSS_CHANGED_BASIC_RATES && + !ath11k_mac_vif_chan(arvif->vif, &def)) + ath11k_recalculate_mgmt_rate(ar, vif, &def); + + mutex_unlock(&ar->conf_mutex); +} + +void __ath11k_mac_scan_finish(struct ath11k *ar) +{ + lockdep_assert_held(&ar->data_lock); + + switch (ar->scan.state) { + case ATH11K_SCAN_IDLE: + break; + case ATH11K_SCAN_RUNNING: + case ATH11K_SCAN_ABORTING: + if (!ar->scan.is_roc) { + struct cfg80211_scan_info info = { + .aborted = (ar->scan.state == + ATH11K_SCAN_ABORTING), + }; + + ieee80211_scan_completed(ar->hw, &info); + } else if (ar->scan.roc_notify) { + ieee80211_remain_on_channel_expired(ar->hw); + } + /* fall through */ + case ATH11K_SCAN_STARTING: + ar->scan.state = ATH11K_SCAN_IDLE; + ar->scan_channel = NULL; + ar->scan.roc_freq = 0; + cancel_delayed_work(&ar->scan.timeout); + complete(&ar->scan.completed); + break; + } +} + +void ath11k_mac_scan_finish(struct ath11k *ar) +{ + spin_lock_bh(&ar->data_lock); + __ath11k_mac_scan_finish(ar); + spin_unlock_bh(&ar->data_lock); +} + +static int ath11k_scan_stop(struct ath11k *ar) +{ + struct scan_cancel_param arg = { + .req_type = WLAN_SCAN_CANCEL_SINGLE, + .scan_id = ATH11K_SCAN_ID, + }; + int ret; + + lockdep_assert_held(&ar->conf_mutex); + + /* TODO: Fill other STOP Params */ + arg.pdev_id = ar->pdev->pdev_id; + + ret = ath11k_wmi_send_scan_stop_cmd(ar, &arg); + if (ret) { + ath11k_warn(ar->ab, "failed to stop wmi scan: %d\n", ret); + goto out; + } + + ret = wait_for_completion_timeout(&ar->scan.completed, 3 * HZ); + if (ret == 0) { + ath11k_warn(ar->ab, + "failed to receive scan abort comple: timed out\n"); + ret = -ETIMEDOUT; + } else if (ret > 0) { + ret = 0; + } + +out: + /* Scan state should be updated upon scan completion but in case + * firmware fails to deliver the event (for whatever reason) it is + * desired to clean up scan state anyway. Firmware may have just + * dropped the scan completion event delivery due to transport pipe + * being overflown with data and/or it can recover on its own before + * next scan request is submitted. + */ + spin_lock_bh(&ar->data_lock); + if (ar->scan.state != ATH11K_SCAN_IDLE) + __ath11k_mac_scan_finish(ar); + spin_unlock_bh(&ar->data_lock); + + return ret; +} + +static void ath11k_scan_abort(struct ath11k *ar) +{ + int ret; + + lockdep_assert_held(&ar->conf_mutex); + + spin_lock_bh(&ar->data_lock); + + switch (ar->scan.state) { + case ATH11K_SCAN_IDLE: + /* This can happen if timeout worker kicked in and called + * abortion while scan completion was being processed. + */ + break; + case ATH11K_SCAN_STARTING: + case ATH11K_SCAN_ABORTING: + ath11k_warn(ar->ab, "refusing scan abortion due to invalid scan state: %d\n", + ar->scan.state); + break; + case ATH11K_SCAN_RUNNING: + ar->scan.state = ATH11K_SCAN_ABORTING; + spin_unlock_bh(&ar->data_lock); + + ret = ath11k_scan_stop(ar); + if (ret) + ath11k_warn(ar->ab, "failed to abort scan: %d\n", ret); + + spin_lock_bh(&ar->data_lock); + break; + } + + spin_unlock_bh(&ar->data_lock); +} + +static void ath11k_scan_timeout_work(struct work_struct *work) +{ + struct ath11k *ar = container_of(work, struct ath11k, + scan.timeout.work); + + mutex_lock(&ar->conf_mutex); + ath11k_scan_abort(ar); + mutex_unlock(&ar->conf_mutex); +} + +static int ath11k_start_scan(struct ath11k *ar, + struct scan_req_params *arg) +{ + int ret; + + lockdep_assert_held(&ar->conf_mutex); + + ret = ath11k_wmi_send_scan_start_cmd(ar, arg); + if (ret) + return ret; + + ret = wait_for_completion_timeout(&ar->scan.started, 1 * HZ); + if (ret == 0) { + ret = ath11k_scan_stop(ar); + if (ret) + ath11k_warn(ar->ab, "failed to stop scan: %d\n", ret); + + return -ETIMEDOUT; + } + + /* If we failed to start the scan, return error code at + * this point. This is probably due to some issue in the + * firmware, but no need to wedge the driver due to that... + */ + spin_lock_bh(&ar->data_lock); + if (ar->scan.state == ATH11K_SCAN_IDLE) { + spin_unlock_bh(&ar->data_lock); + return -EINVAL; + } + spin_unlock_bh(&ar->data_lock); + + return 0; +} + +static int ath11k_mac_op_hw_scan(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_scan_request *hw_req) +{ + struct ath11k *ar = hw->priv; + struct ath11k_vif *arvif = ath11k_vif_to_arvif(vif); + struct cfg80211_scan_request *req = &hw_req->req; + struct scan_req_params arg; + int ret = 0; + int i; + + mutex_lock(&ar->conf_mutex); + + spin_lock_bh(&ar->data_lock); + switch (ar->scan.state) { + case ATH11K_SCAN_IDLE: + reinit_completion(&ar->scan.started); + reinit_completion(&ar->scan.completed); + ar->scan.state = ATH11K_SCAN_STARTING; + ar->scan.is_roc = false; + ar->scan.vdev_id = arvif->vdev_id; + ret = 0; + break; + case ATH11K_SCAN_STARTING: + case ATH11K_SCAN_RUNNING: + case ATH11K_SCAN_ABORTING: + ret = -EBUSY; + break; + } + spin_unlock_bh(&ar->data_lock); + + if (ret) + goto exit; + + memset(&arg, 0, sizeof(arg)); + ath11k_wmi_start_scan_init(ar, &arg); + arg.vdev_id = arvif->vdev_id; + arg.scan_id = ATH11K_SCAN_ID; + + if (req->ie_len) { + arg.extraie.len = req->ie_len; + arg.extraie.ptr = kzalloc(req->ie_len, GFP_KERNEL); + memcpy(arg.extraie.ptr, req->ie, req->ie_len); + } + + if (req->n_ssids) { + arg.num_ssids = req->n_ssids; + for (i = 0; i < arg.num_ssids; i++) { + arg.ssid[i].length = req->ssids[i].ssid_len; + memcpy(&arg.ssid[i].ssid, req->ssids[i].ssid, + req->ssids[i].ssid_len); + } + } else { + arg.scan_flags |= WMI_SCAN_FLAG_PASSIVE; + } + + if (req->n_channels) { + arg.num_chan = req->n_channels; + for (i = 0; i < arg.num_chan; i++) + arg.chan_list[i] = req->channels[i]->center_freq; + } + + ret = ath11k_start_scan(ar, &arg); + if (ret) { + ath11k_warn(ar->ab, "failed to start hw scan: %d\n", ret); + spin_lock_bh(&ar->data_lock); + ar->scan.state = ATH11K_SCAN_IDLE; + spin_unlock_bh(&ar->data_lock); + } + + /* Add a 200ms margin to account for event/command processing */ + ieee80211_queue_delayed_work(ar->hw, &ar->scan.timeout, + msecs_to_jiffies(arg.max_scan_time + + ATH11K_MAC_SCAN_TIMEOUT_MSECS)); + +exit: + if (req->ie_len) + kfree(arg.extraie.ptr); + + mutex_unlock(&ar->conf_mutex); + return ret; +} + +static void ath11k_mac_op_cancel_hw_scan(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + struct ath11k *ar = hw->priv; + + mutex_lock(&ar->conf_mutex); + ath11k_scan_abort(ar); + mutex_unlock(&ar->conf_mutex); + + cancel_delayed_work_sync(&ar->scan.timeout); +} + +static int ath11k_install_key(struct ath11k_vif *arvif, + struct ieee80211_key_conf *key, + enum set_key_cmd cmd, + const u8 *macaddr, u32 flags) +{ + int ret; + struct ath11k *ar = arvif->ar; + struct wmi_vdev_install_key_arg arg = { + .vdev_id = arvif->vdev_id, + .key_idx = key->keyidx, + .key_len = key->keylen, + .key_data = key->key, + .key_flags = flags, + .macaddr = macaddr, + }; + + lockdep_assert_held(&arvif->ar->conf_mutex); + + reinit_completion(&ar->install_key_done); + + if (cmd == DISABLE_KEY) { + /* TODO: Check if FW expects value other than NONE for del */ + /* arg.key_cipher = WMI_CIPHER_NONE; */ + arg.key_len = 0; + arg.key_data = NULL; + goto install; + } + + switch (key->cipher) { + case WLAN_CIPHER_SUITE_CCMP: + arg.key_cipher = WMI_CIPHER_AES_CCM; + /* TODO: Re-check if flag is valid */ + key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV_MGMT; + break; + case WLAN_CIPHER_SUITE_TKIP: + arg.key_cipher = WMI_CIPHER_TKIP; + arg.key_txmic_len = 8; + arg.key_rxmic_len = 8; + break; + case WLAN_CIPHER_SUITE_CCMP_256: + arg.key_cipher = WMI_CIPHER_AES_CCM; + break; + case WLAN_CIPHER_SUITE_GCMP: + case WLAN_CIPHER_SUITE_GCMP_256: + arg.key_cipher = WMI_CIPHER_AES_GCM; + break; + default: + ath11k_warn(ar->ab, "cipher %d is not supported\n", key->cipher); + return -EOPNOTSUPP; + } + +install: + ret = ath11k_wmi_vdev_install_key(arvif->ar, &arg); + if (ret) + return ret; + + if (!wait_for_completion_timeout(&ar->install_key_done, 1 * HZ)) + return -ETIMEDOUT; + + return ar->install_key_status ? -EINVAL : 0; +} + +static int ath11k_clear_peer_keys(struct ath11k_vif *arvif, + const u8 *addr) +{ + struct ath11k *ar = arvif->ar; + struct ath11k_base *ab = ar->ab; + struct ath11k_peer *peer; + int first_errno = 0; + int ret; + int i; + u32 flags = 0; + + lockdep_assert_held(&ar->conf_mutex); + + spin_lock_bh(&ab->base_lock); + peer = ath11k_peer_find(ab, arvif->vdev_id, addr); + spin_unlock_bh(&ab->base_lock); + + if (!peer) + return -ENOENT; + + for (i = 0; i < ARRAY_SIZE(peer->keys); i++) { + if (!peer->keys[i]) + continue; + + /* key flags are not required to delete the key */ + ret = ath11k_install_key(arvif, peer->keys[i], + DISABLE_KEY, addr, flags); + if (ret < 0 && first_errno == 0) + first_errno = ret; + + if (ret < 0) + ath11k_warn(ab, "failed to remove peer key %d: %d\n", + i, ret); + + spin_lock_bh(&ab->base_lock); + peer->keys[i] = NULL; + spin_unlock_bh(&ab->base_lock); + } + + return first_errno; +} + +static int ath11k_mac_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, + struct ieee80211_vif *vif, struct ieee80211_sta *sta, + struct ieee80211_key_conf *key) +{ + struct ath11k *ar = hw->priv; + struct ath11k_base *ab = ar->ab; + struct ath11k_vif *arvif = ath11k_vif_to_arvif(vif); + struct ath11k_peer *peer; + const u8 *peer_addr; + int ret = 0; + u32 flags = 0; + + /* BIP needs to be done in software */ + if (key->cipher == WLAN_CIPHER_SUITE_AES_CMAC || + key->cipher == WLAN_CIPHER_SUITE_BIP_GMAC_128 || + key->cipher == WLAN_CIPHER_SUITE_BIP_GMAC_256 || + key->cipher == WLAN_CIPHER_SUITE_BIP_CMAC_256) + return 1; + + if (key->keyidx > WMI_MAX_KEY_INDEX) + return -ENOSPC; + + mutex_lock(&ar->conf_mutex); + + if (sta) + peer_addr = sta->addr; + else if (arvif->vdev_type == WMI_VDEV_TYPE_STA) + peer_addr = vif->bss_conf.bssid; + else + peer_addr = vif->addr; + + key->hw_key_idx = key->keyidx; + + /* the peer should not disappear in mid-way (unless FW goes awry) since + * we already hold conf_mutex. we just make sure its there now. + */ + spin_lock_bh(&ab->base_lock); + peer = ath11k_peer_find(ab, arvif->vdev_id, peer_addr); + spin_unlock_bh(&ab->base_lock); + + if (!peer) { + if (cmd == SET_KEY) { + ath11k_warn(ab, "cannot install key for non-existent peer %pM\n", + peer_addr); + ret = -EOPNOTSUPP; + goto exit; + } else { + /* if the peer doesn't exist there is no key to disable + * anymore + */ + goto exit; + } + } + + if (key->flags & IEEE80211_KEY_FLAG_PAIRWISE) + flags |= WMI_KEY_PAIRWISE; + else + flags |= WMI_KEY_GROUP; + + ret = ath11k_install_key(arvif, key, cmd, peer_addr, flags); + if (ret) { + ath11k_warn(ab, "ath11k_install_key failed (%d)\n", ret); + goto exit; + } + + spin_lock_bh(&ab->base_lock); + peer = ath11k_peer_find(ab, arvif->vdev_id, peer_addr); + if (peer && cmd == SET_KEY) + peer->keys[key->keyidx] = key; + else if (peer && cmd == DISABLE_KEY) + peer->keys[key->keyidx] = NULL; + else if (!peer) + /* impossible unless FW goes crazy */ + ath11k_warn(ab, "peer %pM disappeared!\n", peer_addr); + spin_unlock_bh(&ab->base_lock); + +exit: + mutex_unlock(&ar->conf_mutex); + return ret; +} + +static int +ath11k_mac_bitrate_mask_num_vht_rates(struct ath11k *ar, + enum nl80211_band band, + const struct cfg80211_bitrate_mask *mask) +{ + int num_rates = 0; + int i; + + for (i = 0; i < ARRAY_SIZE(mask->control[band].vht_mcs); i++) + num_rates += hweight16(mask->control[band].vht_mcs[i]); + + return num_rates; +} + +static int +ath11k_mac_set_peer_vht_fixed_rate(struct ath11k_vif *arvif, + struct ieee80211_sta *sta, + const struct cfg80211_bitrate_mask *mask, + enum nl80211_band band) +{ + struct ath11k *ar = arvif->ar; + u8 vht_rate, nss; + u32 rate_code; + int ret, i; + + lockdep_assert_held(&ar->conf_mutex); + + nss = 0; + + for (i = 0; i < ARRAY_SIZE(mask->control[band].vht_mcs); i++) { + if (hweight16(mask->control[band].vht_mcs[i]) == 1) { + nss = i + 1; + vht_rate = ffs(mask->control[band].vht_mcs[i]) - 1; + } + } + + if (!nss) { + ath11k_warn(ar->ab, "No single VHT Fixed rate found to set for %pM", + sta->addr); + return -EINVAL; + } + + ath11k_dbg(ar->ab, ATH11K_DBG_MAC, + "Setting Fixed VHT Rate for peer %pM. Device will not switch to any other selected rates", + sta->addr); + + rate_code = ATH11K_HW_RATE_CODE(vht_rate, nss - 1, + WMI_RATE_PREAMBLE_VHT); + ret = ath11k_wmi_set_peer_param(ar, sta->addr, + arvif->vdev_id, + WMI_PEER_PARAM_FIXED_RATE, + rate_code); + if (ret) + ath11k_warn(ar->ab, + "failed to update STA %pM Fixed Rate %d: %d\n", + sta->addr, rate_code, ret); + + return ret; +} + +static int ath11k_station_assoc(struct ath11k *ar, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + bool reassoc) +{ + struct ath11k_vif *arvif = ath11k_vif_to_arvif(vif); + struct peer_assoc_params peer_arg; + int ret = 0; + struct cfg80211_chan_def def; + enum nl80211_band band; + struct cfg80211_bitrate_mask *mask; + u8 num_vht_rates; + + lockdep_assert_held(&ar->conf_mutex); + + if (WARN_ON(ath11k_mac_vif_chan(vif, &def))) + return -EPERM; + + band = def.chan->band; + mask = &arvif->bitrate_mask; + + ath11k_peer_assoc_prepare(ar, vif, sta, &peer_arg, reassoc); + + ret = ath11k_wmi_send_peer_assoc_cmd(ar, &peer_arg); + if (ret) { + ath11k_warn(ar->ab, "failed to run peer assoc for STA %pM vdev %i: %d\n", + sta->addr, arvif->vdev_id, ret); + return ret; + } + + if (!wait_for_completion_timeout(&ar->peer_assoc_done, 1 * HZ)) { + ath11k_warn(ar->ab, "failed to get peer assoc conf event for %pM vdev %i\n", + sta->addr, arvif->vdev_id); + return -ETIMEDOUT; + } + + num_vht_rates = ath11k_mac_bitrate_mask_num_vht_rates(ar, band, mask); + + /* If single VHT rate is configured (by set_bitrate_mask()), + * peer_assoc will disable VHT. This is now enabled by a peer specific + * fixed param. + * Note that all other rates and NSS will be disabled for this peer. + */ + if (sta->vht_cap.vht_supported && num_vht_rates == 1) { + ret = ath11k_mac_set_peer_vht_fixed_rate(arvif, sta, mask, + band); + if (ret) + return ret; + } + + /* Re-assoc is run only to update supported rates for given station. It + * doesn't make much sense to reconfigure the peer completely. + */ + if (reassoc) + return 0; + + ret = ath11k_setup_peer_smps(ar, arvif, sta->addr, + &sta->ht_cap); + if (ret) { + ath11k_warn(ar->ab, "failed to setup peer SMPS for vdev %d: %d\n", + arvif->vdev_id, ret); + return ret; + } + + if (!sta->wme) { + arvif->num_legacy_stations++; + ret = ath11k_recalc_rtscts_prot(arvif); + if (ret) + return ret; + } + + if (sta->wme && sta->uapsd_queues) { + ret = ath11k_peer_assoc_qos_ap(ar, arvif, sta); + if (ret) { + ath11k_warn(ar->ab, "failed to set qos params for STA %pM for vdev %i: %d\n", + sta->addr, arvif->vdev_id, ret); + return ret; + } + } + + return 0; +} + +static int ath11k_station_disassoc(struct ath11k *ar, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct ath11k_vif *arvif = (void *)vif->drv_priv; + int ret = 0; + + lockdep_assert_held(&ar->conf_mutex); + + if (!sta->wme) { + arvif->num_legacy_stations--; + ret = ath11k_recalc_rtscts_prot(arvif); + if (ret) + return ret; + } + + ret = ath11k_clear_peer_keys(arvif, sta->addr); + if (ret) { + ath11k_warn(ar->ab, "failed to clear all peer keys for vdev %i: %d\n", + arvif->vdev_id, ret); + return ret; + } + return 0; +} + +static void ath11k_sta_rc_update_wk(struct work_struct *wk) +{ + struct ath11k *ar; + struct ath11k_vif *arvif; + struct ath11k_sta *arsta; + struct ieee80211_sta *sta; + struct cfg80211_chan_def def; + enum nl80211_band band; + const u8 *ht_mcs_mask; + const u16 *vht_mcs_mask; + u32 changed, bw, nss, smps; + int err, num_vht_rates; + const struct cfg80211_bitrate_mask *mask; + struct peer_assoc_params peer_arg; + + arsta = container_of(wk, struct ath11k_sta, update_wk); + sta = container_of((void *)arsta, struct ieee80211_sta, drv_priv); + arvif = arsta->arvif; + ar = arvif->ar; + + if (WARN_ON(ath11k_mac_vif_chan(arvif->vif, &def))) + return; + + band = def.chan->band; + ht_mcs_mask = arvif->bitrate_mask.control[band].ht_mcs; + vht_mcs_mask = arvif->bitrate_mask.control[band].vht_mcs; + + spin_lock_bh(&ar->data_lock); + + changed = arsta->changed; + arsta->changed = 0; + + bw = arsta->bw; + nss = arsta->nss; + smps = arsta->smps; + + spin_unlock_bh(&ar->data_lock); + + mutex_lock(&ar->conf_mutex); + + nss = max_t(u32, 1, nss); + nss = min(nss, max(ath11k_mac_max_ht_nss(ht_mcs_mask), + ath11k_mac_max_vht_nss(vht_mcs_mask))); + + if (changed & IEEE80211_RC_BW_CHANGED) { + err = ath11k_wmi_set_peer_param(ar, sta->addr, arvif->vdev_id, + WMI_PEER_CHWIDTH, bw); + if (err) + ath11k_warn(ar->ab, "failed to update STA %pM peer bw %d: %d\n", + sta->addr, bw, err); + } + + if (changed & IEEE80211_RC_NSS_CHANGED) { + ath11k_dbg(ar->ab, ATH11K_DBG_MAC, "mac update sta %pM nss %d\n", + sta->addr, nss); + + err = ath11k_wmi_set_peer_param(ar, sta->addr, arvif->vdev_id, + WMI_PEER_NSS, nss); + if (err) + ath11k_warn(ar->ab, "failed to update STA %pM nss %d: %d\n", + sta->addr, nss, err); + } + + if (changed & IEEE80211_RC_SMPS_CHANGED) { + ath11k_dbg(ar->ab, ATH11K_DBG_MAC, "mac update sta %pM smps %d\n", + sta->addr, smps); + + err = ath11k_wmi_set_peer_param(ar, sta->addr, arvif->vdev_id, + WMI_PEER_MIMO_PS_STATE, smps); + if (err) + ath11k_warn(ar->ab, "failed to update STA %pM smps %d: %d\n", + sta->addr, smps, err); + } + + if (changed & IEEE80211_RC_SUPP_RATES_CHANGED) { + mask = &arvif->bitrate_mask; + num_vht_rates = ath11k_mac_bitrate_mask_num_vht_rates(ar, band, + mask); + + /* Peer_assoc_prepare will reject vht rates in + * bitrate_mask if its not available in range format and + * sets vht tx_rateset as unsupported. So multiple VHT MCS + * setting(eg. MCS 4,5,6) per peer is not supported here. + * But, Single rate in VHT mask can be set as per-peer + * fixed rate. But even if any HT rates are configured in + * the bitrate mask, device will not switch to those rates + * when per-peer Fixed rate is set. + * TODO: Check RATEMASK_CMDID to support auto rates selection + * across HT/VHT and for multiple VHT MCS support. + */ + if (sta->vht_cap.vht_supported && num_vht_rates == 1) { + ath11k_mac_set_peer_vht_fixed_rate(arvif, sta, mask, + band); + } else { + /* If the peer is non-VHT or no fixed VHT rate + * is provided in the new bitrate mask we set the + * other rates using peer_assoc command. + */ + ath11k_peer_assoc_prepare(ar, arvif->vif, sta, + &peer_arg, true); + + err = ath11k_wmi_send_peer_assoc_cmd(ar, &peer_arg); + if (err) + ath11k_warn(ar->ab, "failed to run peer assoc for STA %pM vdev %i: %d\n", + sta->addr, arvif->vdev_id, err); + + if (!wait_for_completion_timeout(&ar->peer_assoc_done, 1 * HZ)) + ath11k_warn(ar->ab, "failed to get peer assoc conf event for %pM vdev %i\n", + sta->addr, arvif->vdev_id); + } + } + + mutex_unlock(&ar->conf_mutex); +} + +static int ath11k_mac_inc_num_stations(struct ath11k_vif *arvif, + struct ieee80211_sta *sta) +{ + struct ath11k *ar = arvif->ar; + + lockdep_assert_held(&ar->conf_mutex); + + if (arvif->vdev_type == WMI_VDEV_TYPE_STA && !sta->tdls) + return 0; + + if (ar->num_stations >= ar->max_num_stations) + return -ENOBUFS; + + ar->num_stations++; + + return 0; +} + +static void ath11k_mac_dec_num_stations(struct ath11k_vif *arvif, + struct ieee80211_sta *sta) +{ + struct ath11k *ar = arvif->ar; + + lockdep_assert_held(&ar->conf_mutex); + + if (arvif->vdev_type == WMI_VDEV_TYPE_STA && !sta->tdls) + return; + + ar->num_stations--; +} + +static int ath11k_mac_op_sta_state(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + enum ieee80211_sta_state old_state, + enum ieee80211_sta_state new_state) +{ + struct ath11k *ar = hw->priv; + struct ath11k_vif *arvif = ath11k_vif_to_arvif(vif); + struct ath11k_sta *arsta = (struct ath11k_sta *)sta->drv_priv; + struct peer_create_params peer_param; + int ret = 0; + + /* cancel must be done outside the mutex to avoid deadlock */ + if ((old_state == IEEE80211_STA_NONE && + new_state == IEEE80211_STA_NOTEXIST)) + cancel_work_sync(&arsta->update_wk); + + mutex_lock(&ar->conf_mutex); + + if (old_state == IEEE80211_STA_NOTEXIST && + new_state == IEEE80211_STA_NONE) { + memset(arsta, 0, sizeof(*arsta)); + arsta->arvif = arvif; + INIT_WORK(&arsta->update_wk, ath11k_sta_rc_update_wk); + + ret = ath11k_mac_inc_num_stations(arvif, sta); + if (ret) { + ath11k_warn(ar->ab, "refusing to associate station: too many connected already (%d)\n", + ar->max_num_stations); + goto exit; + } + + arsta->rx_stats = kzalloc(sizeof(*arsta->rx_stats), GFP_KERNEL); + if (!arsta->rx_stats) { + ret = -ENOMEM; + goto exit; + } + + peer_param.vdev_id = arvif->vdev_id; + peer_param.peer_addr = sta->addr; + peer_param.peer_type = WMI_PEER_TYPE_DEFAULT; + ret = ath11k_peer_create(ar, arvif, sta, &peer_param); + if (ret) { + ath11k_warn(ar->ab, "Failed to add peer: %pM for VDEV: %d\n", + sta->addr, arvif->vdev_id); + ath11k_mac_dec_num_stations(arvif, sta); + goto exit; + } + + ath11k_info(ar->ab, "Added peer: %pM for VDEV: %d\n", + sta->addr, arvif->vdev_id); + + if (ath11k_debug_is_extd_tx_stats_enabled(ar)) { + arsta->tx_stats = kzalloc(sizeof(*arsta->tx_stats), + GFP_KERNEL); + if (!arsta->tx_stats) { + ret = -ENOMEM; + goto exit; + } + } + + if (ieee80211_vif_is_mesh(vif)) { + ret = ath11k_wmi_set_peer_param(ar, sta->addr, + arvif->vdev_id, + WMI_PEER_USE_4ADDR, 1); + if (ret) { + ath11k_warn(ar->ab, "failed to STA %pM 4addr capability: %d\n", + sta->addr, ret); + goto exit; + } + } + + ret = ath11k_dp_peer_setup(ar, arvif->vdev_id, sta->addr); + if (ret) { + ath11k_warn(ar->ab, "failed to setup dp for peer %pM on vdev %i (%d)\n", + sta->addr, arvif->vdev_id, ret); + ath11k_peer_delete(ar, arvif->vdev_id, sta->addr); + ath11k_mac_dec_num_stations(arvif, sta); + } + } else if ((old_state == IEEE80211_STA_NONE && + new_state == IEEE80211_STA_NOTEXIST)) { + ath11k_dp_peer_cleanup(ar, arvif->vdev_id, sta->addr); + + ret = ath11k_peer_delete(ar, arvif->vdev_id, sta->addr); + if (ret) + ath11k_warn(ar->ab, "Failed to delete peer: %pM for VDEV: %d\n", + sta->addr, arvif->vdev_id); + else + ath11k_info(ar->ab, + "Removed peer: %pM for VDEV: %d\n", + sta->addr, arvif->vdev_id); + + ath11k_mac_dec_num_stations(arvif, sta); + + kfree(arsta->tx_stats); + arsta->tx_stats = NULL; + + kfree(arsta->rx_stats); + arsta->rx_stats = NULL; + } else if (old_state == IEEE80211_STA_AUTH && + new_state == IEEE80211_STA_ASSOC && + (vif->type == NL80211_IFTYPE_AP || + vif->type == NL80211_IFTYPE_MESH_POINT || + vif->type == NL80211_IFTYPE_ADHOC)) { + ret = ath11k_station_assoc(ar, vif, sta, false); + if (ret) + ath11k_warn(ar->ab, "Failed to associate station: %pM\n", + sta->addr); + else + ath11k_info(ar->ab, + "Station %pM moved to assoc state\n", + sta->addr); + } else if (old_state == IEEE80211_STA_ASSOC && + new_state == IEEE80211_STA_AUTH && + (vif->type == NL80211_IFTYPE_AP || + vif->type == NL80211_IFTYPE_MESH_POINT || + vif->type == NL80211_IFTYPE_ADHOC)) { + ret = ath11k_station_disassoc(ar, vif, sta); + if (ret) + ath11k_warn(ar->ab, "Failed to disassociate station: %pM\n", + sta->addr); + else + ath11k_info(ar->ab, + "Station %pM moved to disassociated state\n", + sta->addr); + } + +exit: + mutex_unlock(&ar->conf_mutex); + return ret; +} + +static void ath11k_mac_op_sta_rc_update(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + u32 changed) +{ + struct ath11k *ar = hw->priv; + struct ath11k_sta *arsta = (struct ath11k_sta *)sta->drv_priv; + struct ath11k_vif *arvif = (void *)vif->drv_priv; + struct ath11k_peer *peer; + u32 bw, smps; + + spin_lock_bh(&ar->ab->base_lock); + + peer = ath11k_peer_find(ar->ab, arvif->vdev_id, sta->addr); + if (!peer) { + spin_unlock_bh(&ar->ab->base_lock); + ath11k_warn(ar->ab, "mac sta rc update failed to find peer %pM on vdev %i\n", + sta->addr, arvif->vdev_id); + return; + } + + spin_unlock_bh(&ar->ab->base_lock); + + ath11k_dbg(ar->ab, ATH11K_DBG_MAC, + "mac sta rc update for %pM changed %08x bw %d nss %d smps %d\n", + sta->addr, changed, sta->bandwidth, sta->rx_nss, + sta->smps_mode); + + spin_lock_bh(&ar->data_lock); + + if (changed & IEEE80211_RC_BW_CHANGED) { + bw = WMI_PEER_CHWIDTH_20MHZ; + + switch (sta->bandwidth) { + case IEEE80211_STA_RX_BW_20: + bw = WMI_PEER_CHWIDTH_20MHZ; + break; + case IEEE80211_STA_RX_BW_40: + bw = WMI_PEER_CHWIDTH_40MHZ; + break; + case IEEE80211_STA_RX_BW_80: + bw = WMI_PEER_CHWIDTH_80MHZ; + break; + case IEEE80211_STA_RX_BW_160: + bw = WMI_PEER_CHWIDTH_160MHZ; + break; + default: + ath11k_warn(ar->ab, "Invalid bandwidth %d in rc update for %pM\n", + sta->bandwidth, sta->addr); + bw = WMI_PEER_CHWIDTH_20MHZ; + break; + } + + arsta->bw = bw; + } + + if (changed & IEEE80211_RC_NSS_CHANGED) + arsta->nss = sta->rx_nss; + + if (changed & IEEE80211_RC_SMPS_CHANGED) { + smps = WMI_PEER_SMPS_PS_NONE; + + switch (sta->smps_mode) { + case IEEE80211_SMPS_AUTOMATIC: + case IEEE80211_SMPS_OFF: + smps = WMI_PEER_SMPS_PS_NONE; + break; + case IEEE80211_SMPS_STATIC: + smps = WMI_PEER_SMPS_STATIC; + break; + case IEEE80211_SMPS_DYNAMIC: + smps = WMI_PEER_SMPS_DYNAMIC; + break; + default: + ath11k_warn(ar->ab, "Invalid smps %d in sta rc update for %pM\n", + sta->smps_mode, sta->addr); + smps = WMI_PEER_SMPS_PS_NONE; + break; + } + + arsta->smps = smps; + } + + arsta->changed |= changed; + + spin_unlock_bh(&ar->data_lock); + + ieee80211_queue_work(hw, &arsta->update_wk); +} + +static int ath11k_conf_tx_uapsd(struct ath11k *ar, struct ieee80211_vif *vif, + u16 ac, bool enable) +{ + struct ath11k_vif *arvif = ath11k_vif_to_arvif(vif); + u32 value = 0; + int ret = 0; + + if (arvif->vdev_type != WMI_VDEV_TYPE_STA) + return 0; + + switch (ac) { + case IEEE80211_AC_VO: + value = WMI_STA_PS_UAPSD_AC3_DELIVERY_EN | + WMI_STA_PS_UAPSD_AC3_TRIGGER_EN; + break; + case IEEE80211_AC_VI: + value = WMI_STA_PS_UAPSD_AC2_DELIVERY_EN | + WMI_STA_PS_UAPSD_AC2_TRIGGER_EN; + break; + case IEEE80211_AC_BE: + value = WMI_STA_PS_UAPSD_AC1_DELIVERY_EN | + WMI_STA_PS_UAPSD_AC1_TRIGGER_EN; + break; + case IEEE80211_AC_BK: + value = WMI_STA_PS_UAPSD_AC0_DELIVERY_EN | + WMI_STA_PS_UAPSD_AC0_TRIGGER_EN; + break; + } + + if (enable) + arvif->u.sta.uapsd |= value; + else + arvif->u.sta.uapsd &= ~value; + + ret = ath11k_wmi_set_sta_ps_param(ar, arvif->vdev_id, + WMI_STA_PS_PARAM_UAPSD, + arvif->u.sta.uapsd); + if (ret) { + ath11k_warn(ar->ab, "could not set uapsd params %d\n", ret); + goto exit; + } + + if (arvif->u.sta.uapsd) + value = WMI_STA_PS_RX_WAKE_POLICY_POLL_UAPSD; + else + value = WMI_STA_PS_RX_WAKE_POLICY_WAKE; + + ret = ath11k_wmi_set_sta_ps_param(ar, arvif->vdev_id, + WMI_STA_PS_PARAM_RX_WAKE_POLICY, + value); + if (ret) + ath11k_warn(ar->ab, "could not set rx wake param %d\n", ret); + +exit: + return ret; +} + +static int ath11k_mac_op_conf_tx(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, u16 ac, + const struct ieee80211_tx_queue_params *params) +{ + struct ath11k *ar = hw->priv; + struct ath11k_vif *arvif = (void *)vif->drv_priv; + struct wmi_wmm_params_arg *p = NULL; + int ret; + + mutex_lock(&ar->conf_mutex); + + switch (ac) { + case IEEE80211_AC_VO: + p = &arvif->wmm_params.ac_vo; + break; + case IEEE80211_AC_VI: + p = &arvif->wmm_params.ac_vi; + break; + case IEEE80211_AC_BE: + p = &arvif->wmm_params.ac_be; + break; + case IEEE80211_AC_BK: + p = &arvif->wmm_params.ac_bk; + break; + } + + if (WARN_ON(!p)) { + ret = -EINVAL; + goto exit; + } + + p->cwmin = params->cw_min; + p->cwmax = params->cw_max; + p->aifs = params->aifs; + + /* The channel time duration programmed in the HW is in absolute + * microseconds, while mac80211 gives the txop in units of + * 32 microseconds. + */ + p->txop = params->txop * 32; + + ret = ath11k_wmi_send_wmm_update_cmd_tlv(ar, arvif->vdev_id, + &arvif->wmm_params); + if (ret) { + ath11k_warn(ar->ab, "failed to set wmm params: %d\n", ret); + goto exit; + } + + ret = ath11k_conf_tx_uapsd(ar, vif, ac, params->uapsd); + + if (ret) + ath11k_warn(ar->ab, "failed to set sta uapsd: %d\n", ret); + +exit: + mutex_unlock(&ar->conf_mutex); + return ret; +} + +static struct ieee80211_sta_ht_cap +ath11k_create_ht_cap(struct ath11k *ar, u32 ar_ht_cap, u32 rate_cap_rx_chainmask) +{ + int i; + struct ieee80211_sta_ht_cap ht_cap = {0}; + u32 ar_vht_cap = ar->pdev->cap.vht_cap; + + if (!(ar_ht_cap & WMI_HT_CAP_ENABLED)) + return ht_cap; + + ht_cap.ht_supported = 1; + ht_cap.ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K; + ht_cap.ampdu_density = IEEE80211_HT_MPDU_DENSITY_8; + ht_cap.cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40; + ht_cap.cap |= IEEE80211_HT_CAP_DSSSCCK40; + ht_cap.cap |= WLAN_HT_CAP_SM_PS_STATIC << IEEE80211_HT_CAP_SM_PS_SHIFT; + + if (ar_ht_cap & WMI_HT_CAP_HT20_SGI) + ht_cap.cap |= IEEE80211_HT_CAP_SGI_20; + + if (ar_ht_cap & WMI_HT_CAP_HT40_SGI) + ht_cap.cap |= IEEE80211_HT_CAP_SGI_40; + + if (ar_ht_cap & WMI_HT_CAP_DYNAMIC_SMPS) { + u32 smps; + + smps = WLAN_HT_CAP_SM_PS_DYNAMIC; + smps <<= IEEE80211_HT_CAP_SM_PS_SHIFT; + + ht_cap.cap |= smps; + } + + if (ar_ht_cap & WMI_HT_CAP_TX_STBC) + ht_cap.cap |= IEEE80211_HT_CAP_TX_STBC; + + if (ar_ht_cap & WMI_HT_CAP_RX_STBC) { + u32 stbc; + + stbc = ar_ht_cap; + stbc &= WMI_HT_CAP_RX_STBC; + stbc >>= WMI_HT_CAP_RX_STBC_MASK_SHIFT; + stbc <<= IEEE80211_HT_CAP_RX_STBC_SHIFT; + stbc &= IEEE80211_HT_CAP_RX_STBC; + + ht_cap.cap |= stbc; + } + + if (ar_ht_cap & WMI_HT_CAP_RX_LDPC) + ht_cap.cap |= IEEE80211_HT_CAP_LDPC_CODING; + + if (ar_ht_cap & WMI_HT_CAP_L_SIG_TXOP_PROT) + ht_cap.cap |= IEEE80211_HT_CAP_LSIG_TXOP_PROT; + + if (ar_vht_cap & WMI_VHT_CAP_MAX_MPDU_LEN_MASK) + ht_cap.cap |= IEEE80211_HT_CAP_MAX_AMSDU; + + for (i = 0; i < ar->num_rx_chains; i++) { + if (rate_cap_rx_chainmask & BIT(i)) + ht_cap.mcs.rx_mask[i] = 0xFF; + } + + ht_cap.mcs.tx_params |= IEEE80211_HT_MCS_TX_DEFINED; + + return ht_cap; +} + +static int ath11k_mac_set_txbf_conf(struct ath11k_vif *arvif) +{ + u32 value = 0; + struct ath11k *ar = arvif->ar; + int nsts; + int sound_dim; + u32 vht_cap = ar->pdev->cap.vht_cap; + u32 vdev_param = WMI_VDEV_PARAM_TXBF; + + if (vht_cap & (IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE)) { + nsts = vht_cap & IEEE80211_VHT_CAP_BEAMFORMEE_STS_MASK; + nsts >>= IEEE80211_VHT_CAP_BEAMFORMEE_STS_SHIFT; + value |= SM(nsts, WMI_TXBF_STS_CAP_OFFSET); + } + + if (vht_cap & (IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE)) { + sound_dim = vht_cap & + IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_MASK; + sound_dim >>= IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_SHIFT; + if (sound_dim > (ar->num_tx_chains - 1)) + sound_dim = ar->num_tx_chains - 1; + value |= SM(sound_dim, WMI_BF_SOUND_DIM_OFFSET); + } + + if (!value) + return 0; + + if (vht_cap & IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE) { + value |= WMI_VDEV_PARAM_TXBF_SU_TX_BFER; + + if ((vht_cap & IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE) && + arvif->vdev_type == WMI_VDEV_TYPE_AP) + value |= WMI_VDEV_PARAM_TXBF_MU_TX_BFER; + } + + /* TODO: SUBFEE not validated in HK, disable here until validated? */ + + if (vht_cap & IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE) { + value |= WMI_VDEV_PARAM_TXBF_SU_TX_BFEE; + + if ((vht_cap & IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE) && + arvif->vdev_type == WMI_VDEV_TYPE_STA) + value |= WMI_VDEV_PARAM_TXBF_MU_TX_BFEE; + } + + return ath11k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id, + vdev_param, value); +} + +static void ath11k_set_vht_txbf_cap(struct ath11k *ar, u32 *vht_cap) +{ + bool subfer, subfee; + int sound_dim = 0; + + subfer = !!(*vht_cap & (IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE)); + subfee = !!(*vht_cap & (IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE)); + + if (ar->num_tx_chains < 2) { + *vht_cap &= ~(IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE); + subfer = false; + } + + /* If SU Beaformer is not set, then disable MU Beamformer Capability */ + if (!subfer) + *vht_cap &= ~(IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE); + + /* If SU Beaformee is not set, then disable MU Beamformee Capability */ + if (!subfee) + *vht_cap &= ~(IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE); + + sound_dim = (*vht_cap & IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_MASK); + sound_dim >>= IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_SHIFT; + *vht_cap &= ~IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_MASK; + + /* TODO: Need to check invalid STS and Sound_dim values set by FW? */ + + /* Enable Sounding Dimension Field only if SU BF is enabled */ + if (subfer) { + if (sound_dim > (ar->num_tx_chains - 1)) + sound_dim = ar->num_tx_chains - 1; + + sound_dim <<= IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_SHIFT; + sound_dim &= IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_MASK; + *vht_cap |= sound_dim; + } + + /* Use the STS advertised by FW unless SU Beamformee is not supported*/ + if (!subfee) + *vht_cap &= ~(IEEE80211_VHT_CAP_BEAMFORMEE_STS_MASK); +} + +static struct ieee80211_sta_vht_cap +ath11k_create_vht_cap(struct ath11k *ar, u32 rate_cap_tx_chainmask, + u32 rate_cap_rx_chainmask) +{ + struct ieee80211_sta_vht_cap vht_cap = {0}; + u16 txmcs_map, rxmcs_map; + int i; + + vht_cap.vht_supported = 1; + vht_cap.cap = ar->pdev->cap.vht_cap; + + ath11k_set_vht_txbf_cap(ar, &vht_cap.cap); + + /* TODO: Enable back VHT160 mode once association issues are fixed */ + /* Disabling VHT160 and VHT80+80 modes */ + vht_cap.cap &= ~IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK; + vht_cap.cap &= ~IEEE80211_VHT_CAP_SHORT_GI_160; + + rxmcs_map = 0; + txmcs_map = 0; + for (i = 0; i < 8; i++) { + if (i < ar->num_tx_chains && rate_cap_tx_chainmask & BIT(i)) + txmcs_map |= IEEE80211_VHT_MCS_SUPPORT_0_9 << (i * 2); + else + txmcs_map |= IEEE80211_VHT_MCS_NOT_SUPPORTED << (i * 2); + + if (i < ar->num_rx_chains && rate_cap_rx_chainmask & BIT(i)) + rxmcs_map |= IEEE80211_VHT_MCS_SUPPORT_0_9 << (i * 2); + else + rxmcs_map |= IEEE80211_VHT_MCS_NOT_SUPPORTED << (i * 2); + } + + if (rate_cap_tx_chainmask <= 1) + vht_cap.cap &= ~IEEE80211_VHT_CAP_TXSTBC; + + vht_cap.vht_mcs.rx_mcs_map = cpu_to_le16(rxmcs_map); + vht_cap.vht_mcs.tx_mcs_map = cpu_to_le16(txmcs_map); + + return vht_cap; +} + +static void ath11k_mac_setup_ht_vht_cap(struct ath11k *ar, + struct ath11k_pdev_cap *cap, + u32 *ht_cap_info) +{ + struct ieee80211_supported_band *band; + u32 rate_cap_tx_chainmask; + u32 rate_cap_rx_chainmask; + u32 ht_cap; + + rate_cap_tx_chainmask = ar->cfg_tx_chainmask >> cap->tx_chain_mask_shift; + rate_cap_rx_chainmask = ar->cfg_rx_chainmask >> cap->rx_chain_mask_shift; + + if (cap->supported_bands & WMI_HOST_WLAN_2G_CAP) { + band = &ar->mac.sbands[NL80211_BAND_2GHZ]; + ht_cap = cap->band[NL80211_BAND_2GHZ].ht_cap_info; + if (ht_cap_info) + *ht_cap_info = ht_cap; + band->ht_cap = ath11k_create_ht_cap(ar, ht_cap, + rate_cap_rx_chainmask); + } + + if (cap->supported_bands & WMI_HOST_WLAN_5G_CAP) { + band = &ar->mac.sbands[NL80211_BAND_5GHZ]; + ht_cap = cap->band[NL80211_BAND_5GHZ].ht_cap_info; + if (ht_cap_info) + *ht_cap_info = ht_cap; + band->ht_cap = ath11k_create_ht_cap(ar, ht_cap, + rate_cap_rx_chainmask); + band->vht_cap = ath11k_create_vht_cap(ar, rate_cap_tx_chainmask, + rate_cap_rx_chainmask); + } +} + +static int ath11k_check_chain_mask(struct ath11k *ar, u32 ant, bool is_tx_ant) +{ + /* TODO: Check the request chainmask against the supported + * chainmask table which is advertised in extented_service_ready event + */ + + return 0; +} + +static int __ath11k_set_antenna(struct ath11k *ar, u32 tx_ant, u32 rx_ant) +{ + int ret; + + lockdep_assert_held(&ar->conf_mutex); + + if (ath11k_check_chain_mask(ar, tx_ant, true)) + return -EINVAL; + + if (ath11k_check_chain_mask(ar, rx_ant, false)) + return -EINVAL; + + ar->cfg_tx_chainmask = tx_ant; + ar->cfg_rx_chainmask = rx_ant; + + if (ar->state != ATH11K_STATE_ON && + ar->state != ATH11K_STATE_RESTARTED) + return 0; + + ret = ath11k_wmi_pdev_set_param(ar, WMI_PDEV_PARAM_TX_CHAIN_MASK, + tx_ant, ar->pdev->pdev_id); + if (ret) { + ath11k_warn(ar->ab, "failed to set tx-chainmask: %d, req 0x%x\n", + ret, tx_ant); + return ret; + } + + ret = ath11k_wmi_pdev_set_param(ar, WMI_PDEV_PARAM_RX_CHAIN_MASK, + rx_ant, ar->pdev->pdev_id); + if (ret) { + ath11k_warn(ar->ab, "failed to set rx-chainmask: %d, req 0x%x\n", + ret, rx_ant); + return ret; + } + + /* Reload HT/VHT capability */ + ath11k_mac_setup_ht_vht_cap(ar, &ar->pdev->cap, NULL); + + return 0; +} + +int ath11k_mac_tx_mgmt_pending_free(int buf_id, void *skb, void *ctx) +{ + struct ath11k *ar = ctx; + struct ath11k_base *ab = ar->ab; + struct sk_buff *msdu = skb; + struct ieee80211_tx_info *info; + + spin_lock_bh(&ar->txmgmt_idr_lock); + idr_remove(&ar->txmgmt_idr, buf_id); + spin_unlock_bh(&ar->txmgmt_idr_lock); + dma_unmap_single(ab->dev, ATH11K_SKB_CB(msdu)->paddr, msdu->len, + DMA_TO_DEVICE); + + info = IEEE80211_SKB_CB(msdu); + memset(&info->status, 0, sizeof(info->status)); + + ieee80211_free_txskb(ar->hw, msdu); + + return 0; +} + +static int ath11k_mac_vif_txmgmt_idr_remove(int buf_id, void *skb, void *ctx) +{ + struct ieee80211_vif *vif = ctx; + struct ath11k_skb_cb *skb_cb = ATH11K_SKB_CB((struct sk_buff *)skb); + struct sk_buff *msdu = skb; + struct ath11k *ar = skb_cb->ar; + struct ath11k_base *ab = ar->ab; + + if (skb_cb->vif == vif) { + spin_lock_bh(&ar->txmgmt_idr_lock); + idr_remove(&ar->txmgmt_idr, buf_id); + spin_unlock_bh(&ar->txmgmt_idr_lock); + dma_unmap_single(ab->dev, skb_cb->paddr, msdu->len, + DMA_TO_DEVICE); + } + + return 0; +} + +static int ath11k_mac_mgmt_tx_wmi(struct ath11k *ar, struct ath11k_vif *arvif, + struct sk_buff *skb) +{ + struct ath11k_base *ab = ar->ab; + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; + dma_addr_t paddr; + int buf_id; + int ret; + + spin_lock_bh(&ar->txmgmt_idr_lock); + buf_id = idr_alloc(&ar->txmgmt_idr, skb, 0, + ATH11K_TX_MGMT_NUM_PENDING_MAX, GFP_ATOMIC); + spin_unlock_bh(&ar->txmgmt_idr_lock); + if (buf_id < 0) + return -ENOSPC; + + if ((ieee80211_is_action(hdr->frame_control) || + ieee80211_is_deauth(hdr->frame_control) || + ieee80211_is_disassoc(hdr->frame_control)) && + ieee80211_has_protected(hdr->frame_control)) { + skb_put(skb, IEEE80211_CCMP_MIC_LEN); + } + + paddr = dma_map_single(ab->dev, skb->data, skb->len, DMA_TO_DEVICE); + if (dma_mapping_error(ab->dev, paddr)) { + ath11k_warn(ab, "failed to DMA map mgmt Tx buffer\n"); + ret = -EIO; + goto err_free_idr; + } + + ATH11K_SKB_CB(skb)->paddr = paddr; + + ret = ath11k_wmi_mgmt_send(ar, arvif->vdev_id, buf_id, skb); + if (ret) { + ath11k_warn(ar->ab, "failed to send mgmt frame: %d\n", ret); + goto err_unmap_buf; + } + + return 0; + +err_unmap_buf: + dma_unmap_single(ab->dev, ATH11K_SKB_CB(skb)->paddr, + skb->len, DMA_TO_DEVICE); +err_free_idr: + spin_lock_bh(&ar->txmgmt_idr_lock); + idr_remove(&ar->txmgmt_idr, buf_id); + spin_unlock_bh(&ar->txmgmt_idr_lock); + + return ret; +} + +static void ath11k_mgmt_over_wmi_tx_purge(struct ath11k *ar) +{ + struct sk_buff *skb; + + while ((skb = skb_dequeue(&ar->wmi_mgmt_tx_queue)) != NULL) + ieee80211_free_txskb(ar->hw, skb); +} + +static void ath11k_mgmt_over_wmi_tx_work(struct work_struct *work) +{ + struct ath11k *ar = container_of(work, struct ath11k, wmi_mgmt_tx_work); + struct ieee80211_tx_info *info; + struct ath11k_vif *arvif; + struct sk_buff *skb; + int ret; + + while ((skb = skb_dequeue(&ar->wmi_mgmt_tx_queue)) != NULL) { + info = IEEE80211_SKB_CB(skb); + arvif = ath11k_vif_to_arvif(info->control.vif); + + ret = ath11k_mac_mgmt_tx_wmi(ar, arvif, skb); + if (ret) { + ath11k_warn(ar->ab, "failed to transmit management frame %d\n", + ret); + ieee80211_free_txskb(ar->hw, skb); + } else { + atomic_inc(&ar->num_pending_mgmt_tx); + } + } +} + +static int ath11k_mac_mgmt_tx(struct ath11k *ar, struct sk_buff *skb, + bool is_prb_rsp) +{ + struct sk_buff_head *q = &ar->wmi_mgmt_tx_queue; + + if (test_bit(ATH11K_FLAG_CRASH_FLUSH, &ar->ab->dev_flags)) + return -ESHUTDOWN; + + /* Drop probe response packets when the pending management tx + * count has reached a certain threshold, so as to prioritize + * other mgmt packets like auth and assoc to be sent on time + * for establishing successful connections. + */ + if (is_prb_rsp && + atomic_read(&ar->num_pending_mgmt_tx) > ATH11K_PRB_RSP_DROP_THRESHOLD) { + ath11k_warn(ar->ab, + "dropping probe response as pending queue is almost full\n"); + return -ENOSPC; + } + + if (skb_queue_len(q) == ATH11K_TX_MGMT_NUM_PENDING_MAX) { + ath11k_warn(ar->ab, "mgmt tx queue is full\n"); + return -ENOSPC; + } + + skb_queue_tail(q, skb); + ieee80211_queue_work(ar->hw, &ar->wmi_mgmt_tx_work); + + return 0; +} + +static void ath11k_mac_op_tx(struct ieee80211_hw *hw, + struct ieee80211_tx_control *control, + struct sk_buff *skb) +{ + struct ath11k *ar = hw->priv; + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct ieee80211_vif *vif = info->control.vif; + struct ath11k_vif *arvif = ath11k_vif_to_arvif(vif); + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; + bool is_prb_rsp; + int ret; + + if (ieee80211_is_mgmt(hdr->frame_control)) { + is_prb_rsp = ieee80211_is_probe_resp(hdr->frame_control); + ret = ath11k_mac_mgmt_tx(ar, skb, is_prb_rsp); + if (ret) { + ath11k_warn(ar->ab, "failed to queue management frame %d\n", + ret); + ieee80211_free_txskb(ar->hw, skb); + } + return; + } + + ret = ath11k_dp_tx(ar, arvif, skb); + if (ret) { + ath11k_warn(ar->ab, "failed to transmit frame %d\n", ret); + ieee80211_free_txskb(ar->hw, skb); + } +} + +void ath11k_mac_drain_tx(struct ath11k *ar) +{ + /* make sure rcu-protected mac80211 tx path itself is drained */ + synchronize_net(); + + cancel_work_sync(&ar->wmi_mgmt_tx_work); + ath11k_mgmt_over_wmi_tx_purge(ar); +} + +static int ath11k_mac_config_mon_status_default(struct ath11k *ar, bool enable) +{ + struct htt_rx_ring_tlv_filter tlv_filter = {0}; + u32 ring_id; + + if (enable) + tlv_filter = ath11k_mac_mon_status_filter_default; + + ring_id = ar->dp.rx_mon_status_refill_ring.refill_buf_ring.ring_id; + + return ath11k_dp_tx_htt_rx_filter_setup(ar->ab, ring_id, ar->dp.mac_id, + HAL_RXDMA_MONITOR_STATUS, + DP_RX_BUFFER_SIZE, &tlv_filter); +} + +static int ath11k_mac_op_start(struct ieee80211_hw *hw) +{ + struct ath11k *ar = hw->priv; + struct ath11k_base *ab = ar->ab; + struct ath11k_pdev *pdev = ar->pdev; + int ret; + + ath11k_mac_drain_tx(ar); + mutex_lock(&ar->conf_mutex); + + switch (ar->state) { + case ATH11K_STATE_OFF: + ar->state = ATH11K_STATE_ON; + break; + case ATH11K_STATE_RESTARTING: + ar->state = ATH11K_STATE_RESTARTED; + break; + case ATH11K_STATE_RESTARTED: + case ATH11K_STATE_WEDGED: + case ATH11K_STATE_ON: + WARN_ON(1); + ret = -EINVAL; + goto err; + } + + ret = ath11k_wmi_pdev_set_param(ar, WMI_PDEV_PARAM_PMF_QOS, + 1, pdev->pdev_id); + + if (ret) { + ath11k_err(ar->ab, "failed to enable PMF QOS: (%d\n", ret); + goto err; + } + + ret = ath11k_wmi_pdev_set_param(ar, WMI_PDEV_PARAM_DYNAMIC_BW, 1, + pdev->pdev_id); + if (ret) { + ath11k_err(ar->ab, "failed to enable dynamic bw: %d\n", ret); + goto err; + } + + ret = ath11k_wmi_pdev_set_param(ar, WMI_PDEV_PARAM_ARP_AC_OVERRIDE, + 0, pdev->pdev_id); + if (ret) { + ath11k_err(ab, "failed to set ac override for ARP: %d\n", + ret); + goto err; + } + + ret = ath11k_wmi_send_dfs_phyerr_offload_enable_cmd(ar, pdev->pdev_id); + if (ret) { + ath11k_err(ab, "failed to offload radar detection: %d\n", + ret); + goto err; + } + + ret = ath11k_dp_tx_htt_h2t_ppdu_stats_req(ar, + HTT_PPDU_STATS_TAG_DEFAULT); + if (ret) { + ath11k_err(ab, "failed to req ppdu stats: %d\n", ret); + goto err; + } + + ret = ath11k_wmi_pdev_set_param(ar, WMI_PDEV_PARAM_MESH_MCAST_ENABLE, + 1, pdev->pdev_id); + + if (ret) { + ath11k_err(ar->ab, "failed to enable MESH MCAST ENABLE: (%d\n", ret); + goto err; + } + + __ath11k_set_antenna(ar, ar->cfg_tx_chainmask, ar->cfg_rx_chainmask); + + /* TODO: Do we need to enable ANI? */ + + ath11k_reg_update_chan_list(ar); + + ar->num_started_vdevs = 0; + ar->num_created_vdevs = 0; + ar->num_peers = 0; + + /* Configure monitor status ring with default rx_filter to get rx status + * such as rssi, rx_duration. + */ + ret = ath11k_mac_config_mon_status_default(ar, true); + if (ret) { + ath11k_err(ab, "failed to configure monitor status ring with default rx_filter: (%d)\n", + ret); + goto err; + } + + mutex_unlock(&ar->conf_mutex); + + rcu_assign_pointer(ab->pdevs_active[ar->pdev_idx], + &ab->pdevs[ar->pdev_idx]); + + return 0; + +err: + ar->state = ATH11K_STATE_OFF; + mutex_unlock(&ar->conf_mutex); + + return ret; +} + +static void ath11k_mac_op_stop(struct ieee80211_hw *hw) +{ + struct ath11k *ar = hw->priv; + struct htt_ppdu_stats_info *ppdu_stats, *tmp; + int ret; + + ath11k_mac_drain_tx(ar); + + mutex_lock(&ar->conf_mutex); + ret = ath11k_mac_config_mon_status_default(ar, false); + if (ret) + ath11k_err(ar->ab, "failed to clear rx_filter for monitor status ring: (%d)\n", + ret); + + clear_bit(ATH11K_CAC_RUNNING, &ar->dev_flags); + ar->state = ATH11K_STATE_OFF; + mutex_unlock(&ar->conf_mutex); + + cancel_delayed_work_sync(&ar->scan.timeout); + cancel_work_sync(&ar->regd_update_work); + + spin_lock_bh(&ar->data_lock); + list_for_each_entry_safe(ppdu_stats, tmp, &ar->ppdu_stats_info, list) { + list_del(&ppdu_stats->list); + kfree(ppdu_stats); + } + spin_unlock_bh(&ar->data_lock); + + rcu_assign_pointer(ar->ab->pdevs_active[ar->pdev_idx], NULL); + + synchronize_rcu(); + + atomic_set(&ar->num_pending_mgmt_tx, 0); +} + +static void +ath11k_mac_setup_vdev_create_params(struct ath11k_vif *arvif, + struct vdev_create_params *params) +{ + struct ath11k *ar = arvif->ar; + struct ath11k_pdev *pdev = ar->pdev; + + params->if_id = arvif->vdev_id; + params->type = arvif->vdev_type; + params->subtype = arvif->vdev_subtype; + params->pdev_id = pdev->pdev_id; + + if (pdev->cap.supported_bands & WMI_HOST_WLAN_2G_CAP) { + params->chains[NL80211_BAND_2GHZ].tx = ar->num_tx_chains; + params->chains[NL80211_BAND_2GHZ].rx = ar->num_rx_chains; + } + if (pdev->cap.supported_bands & WMI_HOST_WLAN_5G_CAP) { + params->chains[NL80211_BAND_5GHZ].tx = ar->num_tx_chains; + params->chains[NL80211_BAND_5GHZ].rx = ar->num_rx_chains; + } +} + +static u32 +ath11k_mac_prepare_he_mode(struct ath11k_pdev *pdev, u32 viftype) +{ + struct ath11k_pdev_cap *pdev_cap = &pdev->cap; + struct ath11k_band_cap *cap_band = NULL; + u32 *hecap_phy_ptr = NULL; + u32 hemode = 0; + + if (pdev->cap.supported_bands & WMI_HOST_WLAN_2G_CAP) + cap_band = &pdev_cap->band[NL80211_BAND_2GHZ]; + else + cap_band = &pdev_cap->band[NL80211_BAND_5GHZ]; + + hecap_phy_ptr = &cap_band->he_cap_phy_info[0]; + + hemode = FIELD_PREP(HE_MODE_SU_TX_BFEE, HE_SU_BFEE_ENABLE) | + FIELD_PREP(HE_MODE_SU_TX_BFER, HECAP_PHY_SUBFMR_GET(hecap_phy_ptr)) | + FIELD_PREP(HE_MODE_UL_MUMIMO, HECAP_PHY_ULMUMIMO_GET(hecap_phy_ptr)); + + /* TODO WDS and other modes */ + if (viftype == NL80211_IFTYPE_AP) { + hemode |= FIELD_PREP(HE_MODE_MU_TX_BFER, + HECAP_PHY_MUBFMR_GET(hecap_phy_ptr)) | + FIELD_PREP(HE_MODE_DL_OFDMA, HE_DL_MUOFDMA_ENABLE) | + FIELD_PREP(HE_MODE_UL_OFDMA, HE_UL_MUOFDMA_ENABLE); + } else { + hemode |= FIELD_PREP(HE_MODE_MU_TX_BFEE, HE_MU_BFEE_ENABLE); + } + + return hemode; +} + +static int ath11k_set_he_mu_sounding_mode(struct ath11k *ar, + struct ath11k_vif *arvif) +{ + u32 param_id, param_value; + struct ath11k_base *ab = ar->ab; + int ret = 0; + + param_id = WMI_VDEV_PARAM_SET_HEMU_MODE; + param_value = ath11k_mac_prepare_he_mode(ar->pdev, arvif->vif->type); + ret = ath11k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id, + param_id, param_value); + if (ret) { + ath11k_warn(ab, "failed to set vdev %d HE MU mode: %d param_value %x\n", + arvif->vdev_id, ret, param_value); + return ret; + } + param_id = WMI_VDEV_PARAM_SET_HE_SOUNDING_MODE; + param_value = + FIELD_PREP(HE_VHT_SOUNDING_MODE, HE_VHT_SOUNDING_MODE_ENABLE) | + FIELD_PREP(HE_TRIG_NONTRIG_SOUNDING_MODE, + HE_TRIG_NONTRIG_SOUNDING_MODE_ENABLE); + ret = ath11k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id, + param_id, param_value); + if (ret) { + ath11k_warn(ab, "failed to set vdev %d HE MU mode: %d\n", + arvif->vdev_id, ret); + return ret; + } + return ret; +} + +static int ath11k_mac_op_add_interface(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + struct ath11k *ar = hw->priv; + struct ath11k_base *ab = ar->ab; + struct ath11k_vif *arvif = ath11k_vif_to_arvif(vif); + struct vdev_create_params vdev_param = {0}; + struct peer_create_params peer_param; + u32 param_id, param_value; + u16 nss; + int i; + int ret; + int bit; + + vif->driver_flags |= IEEE80211_VIF_SUPPORTS_UAPSD; + + mutex_lock(&ar->conf_mutex); + + if (vif->type == NL80211_IFTYPE_AP && + ar->num_peers > (ar->max_num_peers - 1)) { + ath11k_warn(ab, "failed to create vdev due to insufficient peer entry resource in firmware\n"); + ret = -ENOBUFS; + goto err; + } + + if (ar->num_created_vdevs > (TARGET_NUM_VDEVS - 1)) { + ath11k_warn(ab, "failed to create vdev, reached max vdev limit %d\n", + TARGET_NUM_VDEVS); + ret = -EBUSY; + goto err; + } + + memset(arvif, 0, sizeof(*arvif)); + + arvif->ar = ar; + arvif->vif = vif; + + INIT_LIST_HEAD(&arvif->list); + + /* Should we initialize any worker to handle connection loss indication + * from firmware in sta mode? + */ + + for (i = 0; i < ARRAY_SIZE(arvif->bitrate_mask.control); i++) { + arvif->bitrate_mask.control[i].legacy = 0xffffffff; + memset(arvif->bitrate_mask.control[i].ht_mcs, 0xff, + sizeof(arvif->bitrate_mask.control[i].ht_mcs)); + memset(arvif->bitrate_mask.control[i].vht_mcs, 0xff, + sizeof(arvif->bitrate_mask.control[i].vht_mcs)); + } + + bit = __ffs64(ab->free_vdev_map); + + arvif->vdev_id = bit; + arvif->vdev_subtype = WMI_VDEV_SUBTYPE_NONE; + + switch (vif->type) { + case NL80211_IFTYPE_UNSPECIFIED: + case NL80211_IFTYPE_STATION: + arvif->vdev_type = WMI_VDEV_TYPE_STA; + break; + case NL80211_IFTYPE_MESH_POINT: + arvif->vdev_subtype = WMI_VDEV_SUBTYPE_MESH_11S; + /* fall through */ + case NL80211_IFTYPE_AP: + arvif->vdev_type = WMI_VDEV_TYPE_AP; + break; + case NL80211_IFTYPE_MONITOR: + arvif->vdev_type = WMI_VDEV_TYPE_MONITOR; + break; + default: + WARN_ON(1); + break; + } + + ath11k_dbg(ar->ab, ATH11K_DBG_MAC, "mac add interface id %d type %d subtype %d map %llx\n", + arvif->vdev_id, arvif->vdev_type, arvif->vdev_subtype, + ab->free_vdev_map); + + vif->cab_queue = arvif->vdev_id % (ATH11K_HW_MAX_QUEUES - 1); + for (i = 0; i < ARRAY_SIZE(vif->hw_queue); i++) + vif->hw_queue[i] = i % (ATH11K_HW_MAX_QUEUES - 1); + + ath11k_mac_setup_vdev_create_params(arvif, &vdev_param); + + ret = ath11k_wmi_vdev_create(ar, vif->addr, &vdev_param); + if (ret) { + ath11k_warn(ab, "failed to create WMI vdev %d: %d\n", + arvif->vdev_id, ret); + goto err; + } + + ar->num_created_vdevs++; + + ab->free_vdev_map &= ~(1LL << arvif->vdev_id); + spin_lock_bh(&ar->data_lock); + list_add(&arvif->list, &ar->arvifs); + spin_unlock_bh(&ar->data_lock); + + param_id = WMI_VDEV_PARAM_TX_ENCAP_TYPE; + param_value = ATH11K_HW_TXRX_NATIVE_WIFI; + ret = ath11k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id, + param_id, param_value); + if (ret) { + ath11k_warn(ab, "failed to set vdev %d tx encap mode: %d\n", + arvif->vdev_id, ret); + goto err_vdev_del; + } + + nss = get_num_chains(ar->cfg_tx_chainmask) ? : 1; + ret = ath11k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id, + WMI_VDEV_PARAM_NSS, nss); + if (ret) { + ath11k_warn(ab, "failed to set vdev %d chainmask 0x%x, nss %d :%d\n", + arvif->vdev_id, ar->cfg_tx_chainmask, nss, ret); + goto err_vdev_del; + } + + switch (arvif->vdev_type) { + case WMI_VDEV_TYPE_AP: + peer_param.vdev_id = arvif->vdev_id; + peer_param.peer_addr = vif->addr; + peer_param.peer_type = WMI_PEER_TYPE_DEFAULT; + ret = ath11k_peer_create(ar, arvif, NULL, &peer_param); + if (ret) { + ath11k_warn(ab, "failed to vdev %d create peer for AP: %d\n", + arvif->vdev_id, ret); + goto err_vdev_del; + } + + ret = ath11k_mac_set_kickout(arvif); + if (ret) { + ath11k_warn(ar->ab, "failed to set vdev %i kickout parameters: %d\n", + arvif->vdev_id, ret); + goto err_peer_del; + } + break; + case WMI_VDEV_TYPE_STA: + param_id = WMI_STA_PS_PARAM_RX_WAKE_POLICY; + param_value = WMI_STA_PS_RX_WAKE_POLICY_WAKE; + ret = ath11k_wmi_set_sta_ps_param(ar, arvif->vdev_id, + param_id, param_value); + if (ret) { + ath11k_warn(ar->ab, "failed to set vdev %d RX wake policy: %d\n", + arvif->vdev_id, ret); + goto err_peer_del; + } + + param_id = WMI_STA_PS_PARAM_TX_WAKE_THRESHOLD; + param_value = WMI_STA_PS_TX_WAKE_THRESHOLD_ALWAYS; + ret = ath11k_wmi_set_sta_ps_param(ar, arvif->vdev_id, + param_id, param_value); + if (ret) { + ath11k_warn(ar->ab, "failed to set vdev %d TX wake threshold: %d\n", + arvif->vdev_id, ret); + goto err_peer_del; + } + + param_id = WMI_STA_PS_PARAM_PSPOLL_COUNT; + param_value = WMI_STA_PS_PSPOLL_COUNT_NO_MAX; + ret = ath11k_wmi_set_sta_ps_param(ar, arvif->vdev_id, + param_id, param_value); + if (ret) { + ath11k_warn(ar->ab, "failed to set vdev %d pspoll count: %d\n", + arvif->vdev_id, ret); + goto err_peer_del; + } + break; + default: + break; + } + + arvif->txpower = vif->bss_conf.txpower; + ret = ath11k_mac_txpower_recalc(ar); + if (ret) + goto err_peer_del; + + param_id = WMI_VDEV_PARAM_RTS_THRESHOLD; + param_value = ar->hw->wiphy->rts_threshold; + ret = ath11k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id, + param_id, param_value); + if (ret) { + ath11k_warn(ar->ab, "failed to set rts threshold for vdev %d: %d\n", + arvif->vdev_id, ret); + } + + ret = ath11k_mac_set_txbf_conf(arvif); + if (ret) { + ath11k_warn(ar->ab, "failed to set txbf conf for vdev %d: %d\n", + arvif->vdev_id, ret); + } + + ath11k_dp_vdev_tx_attach(ar, arvif); + + mutex_unlock(&ar->conf_mutex); + + return 0; + +err_peer_del: + if (arvif->vdev_type == WMI_VDEV_TYPE_AP) { + ar->num_peers--; + ath11k_wmi_send_peer_delete_cmd(ar, vif->addr, arvif->vdev_id); + } + +err_vdev_del: + ath11k_wmi_vdev_delete(ar, arvif->vdev_id); + ar->num_created_vdevs--; + ab->free_vdev_map |= 1LL << arvif->vdev_id; + spin_lock_bh(&ar->data_lock); + list_del(&arvif->list); + spin_unlock_bh(&ar->data_lock); + +err: + mutex_unlock(&ar->conf_mutex); + + return ret; +} + +static int ath11k_mac_vif_unref(int buf_id, void *skb, void *ctx) +{ + struct ieee80211_vif *vif = (struct ieee80211_vif *)ctx; + struct ath11k_skb_cb *skb_cb = ATH11K_SKB_CB((struct sk_buff *)skb); + + if (skb_cb->vif == vif) + skb_cb->vif = NULL; + + return 0; +} + +static void ath11k_mac_op_remove_interface(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + struct ath11k *ar = hw->priv; + struct ath11k_vif *arvif = ath11k_vif_to_arvif(vif); + struct ath11k_base *ab = ar->ab; + int ret; + int i; + + mutex_lock(&ar->conf_mutex); + + ath11k_dbg(ab, ATH11K_DBG_MAC, "mac remove interface (vdev %d)\n", + arvif->vdev_id); + + ab->free_vdev_map |= 1LL << (arvif->vdev_id); + spin_lock_bh(&ar->data_lock); + list_del(&arvif->list); + spin_unlock_bh(&ar->data_lock); + + if (arvif->vdev_type == WMI_VDEV_TYPE_AP) { + ret = ath11k_peer_delete(ar, arvif->vdev_id, vif->addr); + if (ret) + ath11k_warn(ab, "failed to submit AP self-peer removal on vdev %d: %d\n", + arvif->vdev_id, ret); + } + + ret = ath11k_wmi_vdev_delete(ar, arvif->vdev_id); + if (ret) + ath11k_warn(ab, "failed to delete WMI vdev %d: %d\n", + arvif->vdev_id, ret); + + ar->num_created_vdevs--; + + ath11k_peer_cleanup(ar, arvif->vdev_id); + + idr_for_each(&ar->txmgmt_idr, + ath11k_mac_vif_txmgmt_idr_remove, vif); + + for (i = 0; i < DP_TCL_NUM_RING_MAX; i++) { + spin_lock_bh(&ab->dp.tx_ring[i].tx_idr_lock); + idr_for_each(&ab->dp.tx_ring[i].txbuf_idr, + ath11k_mac_vif_unref, vif); + spin_unlock_bh(&ab->dp.tx_ring[i].tx_idr_lock); + } + + /* Recalc txpower for remaining vdev */ + ath11k_mac_txpower_recalc(ar); + clear_bit(ATH11K_FLAG_MONITOR_ENABLED, &ar->monitor_flags); + + /* TODO: recal traffic pause state based on the available vdevs */ + + mutex_unlock(&ar->conf_mutex); +} + +/* FIXME: Has to be verified. */ +#define SUPPORTED_FILTERS \ + (FIF_ALLMULTI | \ + FIF_CONTROL | \ + FIF_PSPOLL | \ + FIF_OTHER_BSS | \ + FIF_BCN_PRBRESP_PROMISC | \ + FIF_PROBE_REQ | \ + FIF_FCSFAIL) + +static void ath11k_mac_op_configure_filter(struct ieee80211_hw *hw, + unsigned int changed_flags, + unsigned int *total_flags, + u64 multicast) +{ + struct ath11k *ar = hw->priv; + bool reset_flag = false; + int ret = 0; + + mutex_lock(&ar->conf_mutex); + + changed_flags &= SUPPORTED_FILTERS; + *total_flags &= SUPPORTED_FILTERS; + ar->filter_flags = *total_flags; + + /* For monitor mode */ + reset_flag = !(ar->filter_flags & FIF_BCN_PRBRESP_PROMISC); + + ret = ath11k_dp_tx_htt_monitor_mode_ring_config(ar, reset_flag); + if (!ret) { + if (!reset_flag) + set_bit(ATH11K_FLAG_MONITOR_ENABLED, &ar->monitor_flags); + else + clear_bit(ATH11K_FLAG_MONITOR_ENABLED, &ar->monitor_flags); + } else { + ath11k_warn(ar->ab, + "fail to set monitor filter: %d\n", ret); + } + mutex_unlock(&ar->conf_mutex); +} + +static int ath11k_mac_op_get_antenna(struct ieee80211_hw *hw, u32 *tx_ant, u32 *rx_ant) +{ + struct ath11k *ar = hw->priv; + + mutex_lock(&ar->conf_mutex); + + *tx_ant = ar->cfg_tx_chainmask; + *rx_ant = ar->cfg_rx_chainmask; + + mutex_unlock(&ar->conf_mutex); + + return 0; +} + +static int ath11k_mac_op_set_antenna(struct ieee80211_hw *hw, u32 tx_ant, u32 rx_ant) +{ + struct ath11k *ar = hw->priv; + int ret; + + mutex_lock(&ar->conf_mutex); + ret = __ath11k_set_antenna(ar, tx_ant, rx_ant); + mutex_unlock(&ar->conf_mutex); + + return ret; +} + +static int ath11k_mac_op_ampdu_action(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_ampdu_params *params) +{ + struct ath11k *ar = hw->priv; + int ret = -EINVAL; + + mutex_lock(&ar->conf_mutex); + + switch (params->action) { + case IEEE80211_AMPDU_RX_START: + ret = ath11k_dp_rx_ampdu_start(ar, params); + break; + case IEEE80211_AMPDU_RX_STOP: + ret = ath11k_dp_rx_ampdu_stop(ar, params); + break; + case IEEE80211_AMPDU_TX_START: + case IEEE80211_AMPDU_TX_STOP_CONT: + case IEEE80211_AMPDU_TX_STOP_FLUSH: + case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT: + case IEEE80211_AMPDU_TX_OPERATIONAL: + /* Tx A-MPDU aggregation offloaded to hw/fw so deny mac80211 + * Tx aggregation requests. + */ + ret = -EOPNOTSUPP; + break; + } + + mutex_unlock(&ar->conf_mutex); + + return ret; +} + +static int ath11k_mac_op_add_chanctx(struct ieee80211_hw *hw, + struct ieee80211_chanctx_conf *ctx) +{ + struct ath11k *ar = hw->priv; + struct ath11k_base *ab = ar->ab; + + ath11k_dbg(ab, ATH11K_DBG_MAC, + "mac chanctx add freq %hu width %d ptr %pK\n", + ctx->def.chan->center_freq, ctx->def.width, ctx); + + mutex_lock(&ar->conf_mutex); + + spin_lock_bh(&ar->data_lock); + /* TODO: In case of multiple channel context, populate rx_channel from + * Rx PPDU desc information. + */ + ar->rx_channel = ctx->def.chan; + spin_unlock_bh(&ar->data_lock); + + mutex_unlock(&ar->conf_mutex); + + return 0; +} + +static void ath11k_mac_op_remove_chanctx(struct ieee80211_hw *hw, + struct ieee80211_chanctx_conf *ctx) +{ + struct ath11k *ar = hw->priv; + struct ath11k_base *ab = ar->ab; + + ath11k_dbg(ab, ATH11K_DBG_MAC, + "mac chanctx remove freq %hu width %d ptr %pK\n", + ctx->def.chan->center_freq, ctx->def.width, ctx); + + mutex_lock(&ar->conf_mutex); + + spin_lock_bh(&ar->data_lock); + /* TODO: In case of there is one more channel context left, populate + * rx_channel with the channel of that remaining channel context. + */ + ar->rx_channel = NULL; + spin_unlock_bh(&ar->data_lock); + + mutex_unlock(&ar->conf_mutex); +} + +static inline int ath11k_mac_vdev_setup_sync(struct ath11k *ar) +{ + lockdep_assert_held(&ar->conf_mutex); + + if (test_bit(ATH11K_FLAG_CRASH_FLUSH, &ar->ab->dev_flags)) + return -ESHUTDOWN; + + if (!wait_for_completion_timeout(&ar->vdev_setup_done, + ATH11K_VDEV_SETUP_TIMEOUT_HZ)) + return -ETIMEDOUT; + + return ar->last_wmi_vdev_start_status ? -EINVAL : 0; +} + +static int +ath11k_mac_vdev_start_restart(struct ath11k_vif *arvif, + const struct cfg80211_chan_def *chandef, + bool restart) +{ + struct ath11k *ar = arvif->ar; + struct ath11k_base *ab = ar->ab; + struct wmi_vdev_start_req_arg arg = {}; + int he_support = arvif->vif->bss_conf.he_support; + int ret = 0; + + lockdep_assert_held(&ar->conf_mutex); + + reinit_completion(&ar->vdev_setup_done); + + arg.vdev_id = arvif->vdev_id; + arg.dtim_period = arvif->dtim_period; + arg.bcn_intval = arvif->beacon_interval; + + arg.channel.freq = chandef->chan->center_freq; + arg.channel.band_center_freq1 = chandef->center_freq1; + arg.channel.band_center_freq2 = chandef->center_freq2; + arg.channel.mode = + ath11k_phymodes[chandef->chan->band][chandef->width]; + + arg.channel.min_power = 0; + arg.channel.max_power = chandef->chan->max_power * 2; + arg.channel.max_reg_power = chandef->chan->max_reg_power * 2; + arg.channel.max_antenna_gain = chandef->chan->max_antenna_gain * 2; + + arg.pref_tx_streams = ar->num_tx_chains; + arg.pref_rx_streams = ar->num_rx_chains; + + if (arvif->vdev_type == WMI_VDEV_TYPE_AP) { + arg.ssid = arvif->u.ap.ssid; + arg.ssid_len = arvif->u.ap.ssid_len; + arg.hidden_ssid = arvif->u.ap.hidden_ssid; + + /* For now allow DFS for AP mode */ + arg.channel.chan_radar = + !!(chandef->chan->flags & IEEE80211_CHAN_RADAR); + + arg.channel.passive = arg.channel.chan_radar; + + spin_lock_bh(&ab->base_lock); + arg.regdomain = ar->ab->dfs_region; + spin_unlock_bh(&ab->base_lock); + + /* TODO: Notify if secondary 80Mhz also needs radar detection */ + if (he_support) { + ret = ath11k_set_he_mu_sounding_mode(ar, arvif); + if (ret) { + ath11k_warn(ar->ab, "failed to set he mode vdev %i\n", + arg.vdev_id); + return ret; + } + } + } + + arg.channel.passive |= !!(chandef->chan->flags & IEEE80211_CHAN_NO_IR); + + ath11k_dbg(ab, ATH11K_DBG_MAC, + "mac vdev %d start center_freq %d phymode %s\n", + arg.vdev_id, arg.channel.freq, + ath11k_wmi_phymode_str(arg.channel.mode)); + + ret = ath11k_wmi_vdev_start(ar, &arg, restart); + if (ret) { + ath11k_warn(ar->ab, "failed to %s WMI vdev %i\n", + restart ? "restart" : "start", arg.vdev_id); + return ret; + } + + ret = ath11k_mac_vdev_setup_sync(ar); + if (ret) { + ath11k_warn(ab, "failed to synchronize setup for vdev %i %s: %d\n", + arg.vdev_id, restart ? "restart" : "start", ret); + return ret; + } + + ar->num_started_vdevs++; + + /* Enable CAC Flag in the driver by checking the channel DFS cac time, + * i.e dfs_cac_ms value which will be valid only for radar channels + * and state as NL80211_DFS_USABLE which indicates CAC needs to be + * done before channel usage. This flags is used to drop rx packets. + * during CAC. + */ + /* TODO Set the flag for other interface types as required */ + if (arvif->vdev_type == WMI_VDEV_TYPE_AP && + chandef->chan->dfs_cac_ms && + chandef->chan->dfs_state == NL80211_DFS_USABLE) { + set_bit(ATH11K_CAC_RUNNING, &ar->dev_flags); + ath11k_dbg(ab, ATH11K_DBG_MAC, + "CAC Started in chan_freq %d for vdev %d\n", + arg.channel.freq, arg.vdev_id); + } + + return 0; +} + +static int ath11k_mac_vdev_stop(struct ath11k_vif *arvif) +{ + struct ath11k *ar = arvif->ar; + int ret; + + lockdep_assert_held(&ar->conf_mutex); + + reinit_completion(&ar->vdev_setup_done); + + spin_lock_bh(&ar->data_lock); + + ar->vdev_stop_status.stop_in_progress = true; + ar->vdev_stop_status.vdev_id = arvif->vdev_id; + + spin_unlock_bh(&ar->data_lock); + + ret = ath11k_wmi_vdev_stop(ar, arvif->vdev_id); + if (ret) { + ath11k_warn(ar->ab, "failed to stop WMI vdev %i: %d\n", + arvif->vdev_id, ret); + goto err; + } + + ret = ath11k_mac_vdev_setup_sync(ar); + if (ret) { + ath11k_warn(ar->ab, "failed to synchronize setup for vdev %i: %d\n", + arvif->vdev_id, ret); + goto err; + } + + WARN_ON(ar->num_started_vdevs == 0); + + ar->num_started_vdevs--; + + if (test_bit(ATH11K_CAC_RUNNING, &ar->dev_flags)) { + clear_bit(ATH11K_CAC_RUNNING, &ar->dev_flags); + ath11k_dbg(ar->ab, ATH11K_DBG_MAC, "CAC Stopped for vdev %d\n", + arvif->vdev_id); + } + + return 0; +err: + spin_lock_bh(&ar->data_lock); + ar->vdev_stop_status.stop_in_progress = false; + spin_unlock_bh(&ar->data_lock); + + return ret; +} + +static int ath11k_mac_vdev_start(struct ath11k_vif *arvif, + const struct cfg80211_chan_def *chandef) +{ + return ath11k_mac_vdev_start_restart(arvif, chandef, false); +} + +static int ath11k_mac_vdev_restart(struct ath11k_vif *arvif, + const struct cfg80211_chan_def *chandef) +{ + return ath11k_mac_vdev_start_restart(arvif, chandef, true); +} + +struct ath11k_mac_change_chanctx_arg { + struct ieee80211_chanctx_conf *ctx; + struct ieee80211_vif_chanctx_switch *vifs; + int n_vifs; + int next_vif; +}; + +static void +ath11k_mac_change_chanctx_cnt_iter(void *data, u8 *mac, + struct ieee80211_vif *vif) +{ + struct ath11k_mac_change_chanctx_arg *arg = data; + + if (rcu_access_pointer(vif->chanctx_conf) != arg->ctx) + return; + + arg->n_vifs++; +} + +static void +ath11k_mac_change_chanctx_fill_iter(void *data, u8 *mac, + struct ieee80211_vif *vif) +{ + struct ath11k_mac_change_chanctx_arg *arg = data; + struct ieee80211_chanctx_conf *ctx; + + ctx = rcu_access_pointer(vif->chanctx_conf); + if (ctx != arg->ctx) + return; + + if (WARN_ON(arg->next_vif == arg->n_vifs)) + return; + + arg->vifs[arg->next_vif].vif = vif; + arg->vifs[arg->next_vif].old_ctx = ctx; + arg->vifs[arg->next_vif].new_ctx = ctx; + arg->next_vif++; +} + +static void +ath11k_mac_update_vif_chan(struct ath11k *ar, + struct ieee80211_vif_chanctx_switch *vifs, + int n_vifs) +{ + struct ath11k_base *ab = ar->ab; + struct ath11k_vif *arvif; + int ret; + int i; + + lockdep_assert_held(&ar->conf_mutex); + + for (i = 0; i < n_vifs; i++) { + arvif = (void *)vifs[i].vif->drv_priv; + + ath11k_dbg(ab, ATH11K_DBG_MAC, + "mac chanctx switch vdev_id %i freq %hu->%hu width %d->%d\n", + arvif->vdev_id, + vifs[i].old_ctx->def.chan->center_freq, + vifs[i].new_ctx->def.chan->center_freq, + vifs[i].old_ctx->def.width, + vifs[i].new_ctx->def.width); + + if (WARN_ON(!arvif->is_started)) + continue; + + if (WARN_ON(!arvif->is_up)) + continue; + + ret = ath11k_wmi_vdev_down(ar, arvif->vdev_id); + if (ret) { + ath11k_warn(ab, "failed to down vdev %d: %d\n", + arvif->vdev_id, ret); + continue; + } + } + + /* All relevant vdevs are downed and associated channel resources + * should be available for the channel switch now. + */ + + /* TODO: Update ar->rx_channel */ + + for (i = 0; i < n_vifs; i++) { + arvif = (void *)vifs[i].vif->drv_priv; + + if (WARN_ON(!arvif->is_started)) + continue; + + if (WARN_ON(!arvif->is_up)) + continue; + + ret = ath11k_mac_setup_bcn_tmpl(arvif); + if (ret) + ath11k_warn(ab, "failed to update bcn tmpl during csa: %d\n", + ret); + + ret = ath11k_mac_vdev_restart(arvif, &vifs[i].new_ctx->def); + if (ret) { + ath11k_warn(ab, "failed to restart vdev %d: %d\n", + arvif->vdev_id, ret); + continue; + } + + ret = ath11k_wmi_vdev_up(arvif->ar, arvif->vdev_id, arvif->aid, + arvif->bssid); + if (ret) { + ath11k_warn(ab, "failed to bring vdev up %d: %d\n", + arvif->vdev_id, ret); + continue; + } + } +} + +static void +ath11k_mac_update_active_vif_chan(struct ath11k *ar, + struct ieee80211_chanctx_conf *ctx) +{ + struct ath11k_mac_change_chanctx_arg arg = { .ctx = ctx }; + + lockdep_assert_held(&ar->conf_mutex); + + ieee80211_iterate_active_interfaces_atomic(ar->hw, + IEEE80211_IFACE_ITER_NORMAL, + ath11k_mac_change_chanctx_cnt_iter, + &arg); + if (arg.n_vifs == 0) + return; + + arg.vifs = kcalloc(arg.n_vifs, sizeof(arg.vifs[0]), GFP_KERNEL); + if (!arg.vifs) + return; + + ieee80211_iterate_active_interfaces_atomic(ar->hw, + IEEE80211_IFACE_ITER_NORMAL, + ath11k_mac_change_chanctx_fill_iter, + &arg); + + ath11k_mac_update_vif_chan(ar, arg.vifs, arg.n_vifs); + + kfree(arg.vifs); +} + +static void ath11k_mac_op_change_chanctx(struct ieee80211_hw *hw, + struct ieee80211_chanctx_conf *ctx, + u32 changed) +{ + struct ath11k *ar = hw->priv; + struct ath11k_base *ab = ar->ab; + + mutex_lock(&ar->conf_mutex); + + ath11k_dbg(ab, ATH11K_DBG_MAC, + "mac chanctx change freq %hu width %d ptr %pK changed %x\n", + ctx->def.chan->center_freq, ctx->def.width, ctx, changed); + + /* This shouldn't really happen because channel switching should use + * switch_vif_chanctx(). + */ + if (WARN_ON(changed & IEEE80211_CHANCTX_CHANGE_CHANNEL)) + goto unlock; + + if (changed & IEEE80211_CHANCTX_CHANGE_WIDTH) + ath11k_mac_update_active_vif_chan(ar, ctx); + + /* TODO: Recalc radar detection */ + +unlock: + mutex_unlock(&ar->conf_mutex); +} + +static int +ath11k_mac_op_assign_vif_chanctx(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_chanctx_conf *ctx) +{ + struct ath11k *ar = hw->priv; + struct ath11k_base *ab = ar->ab; + struct ath11k_vif *arvif = (void *)vif->drv_priv; + int ret; + + mutex_lock(&ar->conf_mutex); + + ath11k_dbg(ab, ATH11K_DBG_MAC, + "mac chanctx assign ptr %pK vdev_id %i\n", + ctx, arvif->vdev_id); + + if (WARN_ON(arvif->is_started)) { + mutex_unlock(&ar->conf_mutex); + return -EBUSY; + } + + ret = ath11k_mac_vdev_start(arvif, &ctx->def); + if (ret) { + ath11k_warn(ab, "failed to start vdev %i addr %pM on freq %d: %d\n", + arvif->vdev_id, vif->addr, + ctx->def.chan->center_freq, ret); + goto err; + } + if (arvif->vdev_type == WMI_VDEV_TYPE_MONITOR) { + ret = ath11k_monitor_vdev_up(ar, arvif->vdev_id); + if (ret) + goto err; + } + + arvif->is_started = true; + + /* TODO: Setup ps and cts/rts protection */ + + mutex_unlock(&ar->conf_mutex); + + return 0; + +err: + mutex_unlock(&ar->conf_mutex); + + return ret; +} + +static void +ath11k_mac_op_unassign_vif_chanctx(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_chanctx_conf *ctx) +{ + struct ath11k *ar = hw->priv; + struct ath11k_base *ab = ar->ab; + struct ath11k_vif *arvif = (void *)vif->drv_priv; + int ret; + + mutex_lock(&ar->conf_mutex); + + ath11k_dbg(ab, ATH11K_DBG_MAC, + "mac chanctx unassign ptr %pK vdev_id %i\n", + ctx, arvif->vdev_id); + + WARN_ON(!arvif->is_started); + + ret = ath11k_mac_vdev_stop(arvif); + if (ret) + ath11k_warn(ab, "failed to stop vdev %i: %d\n", + arvif->vdev_id, ret); + + arvif->is_started = false; + + mutex_unlock(&ar->conf_mutex); +} + +static int +ath11k_mac_op_switch_vif_chanctx(struct ieee80211_hw *hw, + struct ieee80211_vif_chanctx_switch *vifs, + int n_vifs, + enum ieee80211_chanctx_switch_mode mode) +{ + struct ath11k *ar = hw->priv; + + mutex_lock(&ar->conf_mutex); + + ath11k_dbg(ar->ab, ATH11K_DBG_MAC, + "mac chanctx switch n_vifs %d mode %d\n", + n_vifs, mode); + ath11k_mac_update_vif_chan(ar, vifs, n_vifs); + + mutex_unlock(&ar->conf_mutex); + + return 0; +} + +static int +ath11k_set_vdev_param_to_all_vifs(struct ath11k *ar, int param, u32 value) +{ + struct ath11k_vif *arvif; + int ret = 0; + + mutex_lock(&ar->conf_mutex); + list_for_each_entry(arvif, &ar->arvifs, list) { + ath11k_dbg(ar->ab, ATH11K_DBG_MAC, "setting mac vdev %d param %d value %d\n", + param, arvif->vdev_id, value); + + ret = ath11k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id, + param, value); + if (ret) { + ath11k_warn(ar->ab, "failed to set param %d for vdev %d: %d\n", + param, arvif->vdev_id, ret); + break; + } + } + mutex_unlock(&ar->conf_mutex); + return ret; +} + +/* mac80211 stores device specific RTS/Fragmentation threshold value, + * this is set interface specific to firmware from ath11k driver + */ +static int ath11k_mac_op_set_rts_threshold(struct ieee80211_hw *hw, u32 value) +{ + struct ath11k *ar = hw->priv; + int param_id = WMI_VDEV_PARAM_RTS_THRESHOLD; + + return ath11k_set_vdev_param_to_all_vifs(ar, param_id, value); +} + +static int ath11k_mac_op_set_frag_threshold(struct ieee80211_hw *hw, u32 value) +{ + /* Even though there's a WMI vdev param for fragmentation threshold no + * known firmware actually implements it. Moreover it is not possible to + * rely frame fragmentation to mac80211 because firmware clears the + * "more fragments" bit in frame control making it impossible for remote + * devices to reassemble frames. + * + * Hence implement a dummy callback just to say fragmentation isn't + * supported. This effectively prevents mac80211 from doing frame + * fragmentation in software. + */ + return -EOPNOTSUPP; +} + +static void ath11k_mac_op_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + u32 queues, bool drop) +{ + struct ath11k *ar = hw->priv; + long time_left; + + if (drop) + return; + + time_left = wait_event_timeout(ar->dp.tx_empty_waitq, + (atomic_read(&ar->dp.num_tx_pending) == 0), + ATH11K_FLUSH_TIMEOUT); + if (time_left == 0) + ath11k_warn(ar->ab, "failed to flush transmit queue %ld\n", time_left); +} + +static int +ath11k_mac_bitrate_mask_num_ht_rates(struct ath11k *ar, + enum nl80211_band band, + const struct cfg80211_bitrate_mask *mask) +{ + int num_rates = 0; + int i; + + for (i = 0; i < ARRAY_SIZE(mask->control[band].ht_mcs); i++) + num_rates += hweight16(mask->control[band].ht_mcs[i]); + + return num_rates; +} + +static bool +ath11k_mac_has_single_legacy_rate(struct ath11k *ar, + enum nl80211_band band, + const struct cfg80211_bitrate_mask *mask) +{ + int num_rates = 0; + + num_rates = hweight32(mask->control[band].legacy); + + if (ath11k_mac_bitrate_mask_num_ht_rates(ar, band, mask)) + return false; + + if (ath11k_mac_bitrate_mask_num_vht_rates(ar, band, mask)) + return false; + + return num_rates == 1; +} + +static bool +ath11k_mac_bitrate_mask_get_single_nss(struct ath11k *ar, + enum nl80211_band band, + const struct cfg80211_bitrate_mask *mask, + int *nss) +{ + struct ieee80211_supported_band *sband = &ar->mac.sbands[band]; + u16 vht_mcs_map = le16_to_cpu(sband->vht_cap.vht_mcs.tx_mcs_map); + u8 ht_nss_mask = 0; + u8 vht_nss_mask = 0; + int i; + + /* No need to consider legacy here. Basic rates are always present + * in bitrate mask + */ + + for (i = 0; i < ARRAY_SIZE(mask->control[band].ht_mcs); i++) { + if (mask->control[band].ht_mcs[i] == 0) + continue; + else if (mask->control[band].ht_mcs[i] == + sband->ht_cap.mcs.rx_mask[i]) + ht_nss_mask |= BIT(i); + else + return false; + } + + for (i = 0; i < ARRAY_SIZE(mask->control[band].vht_mcs); i++) { + if (mask->control[band].vht_mcs[i] == 0) + continue; + else if (mask->control[band].vht_mcs[i] == + ath11k_mac_get_max_vht_mcs_map(vht_mcs_map, i)) + vht_nss_mask |= BIT(i); + else + return false; + } + + if (ht_nss_mask != vht_nss_mask) + return false; + + if (ht_nss_mask == 0) + return false; + + if (BIT(fls(ht_nss_mask)) - 1 != ht_nss_mask) + return false; + + *nss = fls(ht_nss_mask); + + return true; +} + +static int +ath11k_mac_get_single_legacy_rate(struct ath11k *ar, + enum nl80211_band band, + const struct cfg80211_bitrate_mask *mask, + u32 *rate, u8 *nss) +{ + int rate_idx; + u16 bitrate; + u8 preamble; + u8 hw_rate; + + if (hweight32(mask->control[band].legacy) != 1) + return -EINVAL; + + rate_idx = ffs(mask->control[band].legacy) - 1; + + if (band == NL80211_BAND_5GHZ) + rate_idx += ATH11K_MAC_FIRST_OFDM_RATE_IDX; + + hw_rate = ath11k_legacy_rates[rate_idx].hw_value; + bitrate = ath11k_legacy_rates[rate_idx].bitrate; + + if (ath11k_mac_bitrate_is_cck(bitrate)) + preamble = WMI_RATE_PREAMBLE_CCK; + else + preamble = WMI_RATE_PREAMBLE_OFDM; + + *nss = 1; + *rate = ATH11K_HW_RATE_CODE(hw_rate, 0, preamble); + + return 0; +} + +static int ath11k_mac_set_fixed_rate_params(struct ath11k_vif *arvif, + u32 rate, u8 nss, u8 sgi, u8 ldpc) +{ + struct ath11k *ar = arvif->ar; + u32 vdev_param; + int ret; + + lockdep_assert_held(&ar->conf_mutex); + + ath11k_dbg(ar->ab, ATH11K_DBG_MAC, "mac set fixed rate params vdev %i rate 0x%02hhx nss %hhu sgi %hhu\n", + arvif->vdev_id, rate, nss, sgi); + + vdev_param = WMI_VDEV_PARAM_FIXED_RATE; + ret = ath11k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id, + vdev_param, rate); + if (ret) { + ath11k_warn(ar->ab, "failed to set fixed rate param 0x%02x: %d\n", + rate, ret); + return ret; + } + + vdev_param = WMI_VDEV_PARAM_NSS; + ret = ath11k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id, + vdev_param, nss); + if (ret) { + ath11k_warn(ar->ab, "failed to set nss param %d: %d\n", + nss, ret); + return ret; + } + + vdev_param = WMI_VDEV_PARAM_SGI; + ret = ath11k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id, + vdev_param, sgi); + if (ret) { + ath11k_warn(ar->ab, "failed to set sgi param %d: %d\n", + sgi, ret); + return ret; + } + + vdev_param = WMI_VDEV_PARAM_LDPC; + ret = ath11k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id, + vdev_param, ldpc); + if (ret) { + ath11k_warn(ar->ab, "failed to set ldpc param %d: %d\n", + ldpc, ret); + return ret; + } + + return 0; +} + +static bool +ath11k_mac_vht_mcs_range_present(struct ath11k *ar, + enum nl80211_band band, + const struct cfg80211_bitrate_mask *mask) +{ + int i; + u16 vht_mcs; + + for (i = 0; i < NL80211_VHT_NSS_MAX; i++) { + vht_mcs = mask->control[band].vht_mcs[i]; + + switch (vht_mcs) { + case 0: + case BIT(8) - 1: + case BIT(9) - 1: + case BIT(10) - 1: + break; + default: + return false; + } + } + + return true; +} + +static void ath11k_mac_set_bitrate_mask_iter(void *data, + struct ieee80211_sta *sta) +{ + struct ath11k_vif *arvif = data; + struct ath11k_sta *arsta = (struct ath11k_sta *)sta->drv_priv; + struct ath11k *ar = arvif->ar; + + spin_lock_bh(&ar->data_lock); + arsta->changed |= IEEE80211_RC_SUPP_RATES_CHANGED; + spin_unlock_bh(&ar->data_lock); + + ieee80211_queue_work(ar->hw, &arsta->update_wk); +} + +static void ath11k_mac_disable_peer_fixed_rate(void *data, + struct ieee80211_sta *sta) +{ + struct ath11k_vif *arvif = data; + struct ath11k *ar = arvif->ar; + int ret; + + ret = ath11k_wmi_set_peer_param(ar, sta->addr, + arvif->vdev_id, + WMI_PEER_PARAM_FIXED_RATE, + WMI_FIXED_RATE_NONE); + if (ret) + ath11k_warn(ar->ab, + "failed to disable peer fixed rate for STA %pM ret %d\n", + sta->addr, ret); +} + +static int +ath11k_mac_op_set_bitrate_mask(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + const struct cfg80211_bitrate_mask *mask) +{ + struct ath11k_vif *arvif = (void *)vif->drv_priv; + struct cfg80211_chan_def def; + struct ath11k *ar = arvif->ar; + enum nl80211_band band; + const u8 *ht_mcs_mask; + const u16 *vht_mcs_mask; + u32 rate; + u8 nss; + u8 sgi; + u8 ldpc; + int single_nss; + int ret; + int num_rates; + + if (ath11k_mac_vif_chan(vif, &def)) + return -EPERM; + + band = def.chan->band; + ht_mcs_mask = mask->control[band].ht_mcs; + vht_mcs_mask = mask->control[band].vht_mcs; + ldpc = !!(ar->ht_cap_info & WMI_HT_CAP_LDPC); + + sgi = mask->control[band].gi; + if (sgi == NL80211_TXRATE_FORCE_LGI) + return -EINVAL; + + /* mac80211 doesn't support sending a fixed HT/VHT MCS alone, rather it + * requires passing atleast one of used basic rates along with them. + * Fixed rate setting across different preambles(legacy, HT, VHT) is + * not supported by the FW. Hence use of FIXED_RATE vdev param is not + * suitable for setting single HT/VHT rates. + * But, there could be a single basic rate passed from userspace which + * can be done through the FIXED_RATE param. + */ + if (ath11k_mac_has_single_legacy_rate(ar, band, mask)) { + ret = ath11k_mac_get_single_legacy_rate(ar, band, mask, &rate, + &nss); + if (ret) { + ath11k_warn(ar->ab, "failed to get single legacy rate for vdev %i: %d\n", + arvif->vdev_id, ret); + return ret; + } + ieee80211_iterate_stations_atomic(ar->hw, + ath11k_mac_disable_peer_fixed_rate, + arvif); + } else if (ath11k_mac_bitrate_mask_get_single_nss(ar, band, mask, + &single_nss)) { + rate = WMI_FIXED_RATE_NONE; + nss = single_nss; + } else { + rate = WMI_FIXED_RATE_NONE; + nss = min_t(u32, ar->num_tx_chains, + max(ath11k_mac_max_ht_nss(ht_mcs_mask), + ath11k_mac_max_vht_nss(vht_mcs_mask))); + + /* If multiple rates across different preambles are given + * we can reconfigure this info with all peers using PEER_ASSOC + * command with the below exception cases. + * - Single VHT Rate : peer_assoc command accommodates only MCS + * range values i.e 0-7, 0-8, 0-9 for VHT. Though mac80211 + * mandates passing basic rates along with HT/VHT rates, FW + * doesn't allow switching from VHT to Legacy. Hence instead of + * setting legacy and VHT rates using RATEMASK_CMD vdev cmd, + * we could set this VHT rate as peer fixed rate param, which + * will override FIXED rate and FW rate control algorithm. + * If single VHT rate is passed along with HT rates, we select + * the VHT rate as fixed rate for vht peers. + * - Multiple VHT Rates : When Multiple VHT rates are given,this + * can be set using RATEMASK CMD which uses FW rate-ctl alg. + * TODO: Setting multiple VHT MCS and replacing peer_assoc with + * RATEMASK_CMDID can cover all use cases of setting rates + * across multiple preambles and rates within same type. + * But requires more validation of the command at this point. + */ + + num_rates = ath11k_mac_bitrate_mask_num_vht_rates(ar, band, + mask); + + if (!ath11k_mac_vht_mcs_range_present(ar, band, mask) && + num_rates > 1) { + /* TODO: Handle multiple VHT MCS values setting using + * RATEMASK CMD + */ + ath11k_warn(ar->ab, + "Setting more than one MCS Value in bitrate mask not supported\n"); + return -EINVAL; + } + + ieee80211_iterate_stations_atomic(ar->hw, + ath11k_mac_disable_peer_fixed_rate, + arvif); + + mutex_lock(&ar->conf_mutex); + + arvif->bitrate_mask = *mask; + ieee80211_iterate_stations_atomic(ar->hw, + ath11k_mac_set_bitrate_mask_iter, + arvif); + + mutex_unlock(&ar->conf_mutex); + } + + mutex_lock(&ar->conf_mutex); + + ret = ath11k_mac_set_fixed_rate_params(arvif, rate, nss, sgi, ldpc); + if (ret) { + ath11k_warn(ar->ab, "failed to set fixed rate params on vdev %i: %d\n", + arvif->vdev_id, ret); + } + + mutex_unlock(&ar->conf_mutex); + + return ret; +} + +static void +ath11k_mac_op_reconfig_complete(struct ieee80211_hw *hw, + enum ieee80211_reconfig_type reconfig_type) +{ + struct ath11k *ar = hw->priv; + + if (reconfig_type != IEEE80211_RECONFIG_TYPE_RESTART) + return; + + mutex_lock(&ar->conf_mutex); + + if (ar->state == ATH11K_STATE_RESTARTED) { + ath11k_warn(ar->ab, "pdev %d successfully recovered\n", + ar->pdev->pdev_id); + ar->state = ATH11K_STATE_ON; + ieee80211_wake_queues(ar->hw); + } + + mutex_unlock(&ar->conf_mutex); +} + +static void +ath11k_mac_update_bss_chan_survey(struct ath11k *ar, + struct ieee80211_channel *channel) +{ + int ret; + enum wmi_bss_chan_info_req_type type = WMI_BSS_SURVEY_REQ_TYPE_READ; + + lockdep_assert_held(&ar->conf_mutex); + + if (!test_bit(WMI_TLV_SERVICE_BSS_CHANNEL_INFO_64, ar->ab->wmi_sc.svc_map) || + ar->rx_channel != channel) + return; + + if (ar->scan.state != ATH11K_SCAN_IDLE) { + ath11k_dbg(ar->ab, ATH11K_DBG_MAC, + "ignoring bss chan info req while scanning..\n"); + return; + } + + reinit_completion(&ar->bss_survey_done); + + ret = ath11k_wmi_pdev_bss_chan_info_request(ar, type); + if (ret) { + ath11k_warn(ar->ab, "failed to send pdev bss chan info request\n"); + return; + } + + ret = wait_for_completion_timeout(&ar->bss_survey_done, 3 * HZ); + if (ret == 0) + ath11k_warn(ar->ab, "bss channel survey timed out\n"); +} + +static int ath11k_mac_op_get_survey(struct ieee80211_hw *hw, int idx, + struct survey_info *survey) +{ + struct ath11k *ar = hw->priv; + struct ieee80211_supported_band *sband; + struct survey_info *ar_survey; + int ret = 0; + + if (idx >= ATH11K_NUM_CHANS) + return -ENOENT; + + ar_survey = &ar->survey[idx]; + + mutex_lock(&ar->conf_mutex); + + sband = hw->wiphy->bands[NL80211_BAND_2GHZ]; + if (sband && idx >= sband->n_channels) { + idx -= sband->n_channels; + sband = NULL; + } + + if (!sband) + sband = hw->wiphy->bands[NL80211_BAND_5GHZ]; + + if (!sband || idx >= sband->n_channels) { + ret = -ENOENT; + goto exit; + } + + ath11k_mac_update_bss_chan_survey(ar, &sband->channels[idx]); + + spin_lock_bh(&ar->data_lock); + memcpy(survey, ar_survey, sizeof(*survey)); + spin_unlock_bh(&ar->data_lock); + + survey->channel = &sband->channels[idx]; + + if (ar->rx_channel == survey->channel) + survey->filled |= SURVEY_INFO_IN_USE; + +exit: + mutex_unlock(&ar->conf_mutex); + return ret; +} + +static void ath11k_mac_op_sta_statistics(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct station_info *sinfo) +{ + struct ath11k_sta *arsta = (struct ath11k_sta *)sta->drv_priv; + + sinfo->rx_duration = arsta->rx_duration; + sinfo->filled |= BIT_ULL(NL80211_STA_INFO_RX_DURATION); + + if (!arsta->txrate.legacy && !arsta->txrate.nss) + return; + + if (arsta->txrate.legacy) { + sinfo->txrate.legacy = arsta->txrate.legacy; + } else { + sinfo->txrate.mcs = arsta->txrate.mcs; + sinfo->txrate.nss = arsta->txrate.nss; + sinfo->txrate.bw = arsta->txrate.bw; + sinfo->txrate.he_gi = arsta->txrate.he_gi; + sinfo->txrate.he_dcm = arsta->txrate.he_dcm; + sinfo->txrate.he_ru_alloc = arsta->txrate.he_ru_alloc; + } + sinfo->txrate.flags = arsta->txrate.flags; + sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_BITRATE); + + /* TODO: Use real NF instead of default one. */ + sinfo->signal = arsta->rssi_comb + ATH11K_DEFAULT_NOISE_FLOOR; +} + +static const struct ieee80211_ops ath11k_ops = { + .tx = ath11k_mac_op_tx, + .start = ath11k_mac_op_start, + .stop = ath11k_mac_op_stop, + .reconfig_complete = ath11k_mac_op_reconfig_complete, + .add_interface = ath11k_mac_op_add_interface, + .remove_interface = ath11k_mac_op_remove_interface, + .config = ath11k_mac_op_config, + .bss_info_changed = ath11k_mac_op_bss_info_changed, + .configure_filter = ath11k_mac_op_configure_filter, + .hw_scan = ath11k_mac_op_hw_scan, + .cancel_hw_scan = ath11k_mac_op_cancel_hw_scan, + .set_key = ath11k_mac_op_set_key, + .sta_state = ath11k_mac_op_sta_state, + .sta_rc_update = ath11k_mac_op_sta_rc_update, + .conf_tx = ath11k_mac_op_conf_tx, + .set_antenna = ath11k_mac_op_set_antenna, + .get_antenna = ath11k_mac_op_get_antenna, + .ampdu_action = ath11k_mac_op_ampdu_action, + .add_chanctx = ath11k_mac_op_add_chanctx, + .remove_chanctx = ath11k_mac_op_remove_chanctx, + .change_chanctx = ath11k_mac_op_change_chanctx, + .assign_vif_chanctx = ath11k_mac_op_assign_vif_chanctx, + .unassign_vif_chanctx = ath11k_mac_op_unassign_vif_chanctx, + .switch_vif_chanctx = ath11k_mac_op_switch_vif_chanctx, + .set_rts_threshold = ath11k_mac_op_set_rts_threshold, + .set_frag_threshold = ath11k_mac_op_set_frag_threshold, + .set_bitrate_mask = ath11k_mac_op_set_bitrate_mask, + .get_survey = ath11k_mac_op_get_survey, + .flush = ath11k_mac_op_flush, + .sta_statistics = ath11k_mac_op_sta_statistics, + CFG80211_TESTMODE_CMD(ath11k_tm_cmd) +#ifdef CONFIG_MAC80211_DEBUGFS + .sta_add_debugfs = ath11k_sta_add_debugfs, +#endif +}; + +static const struct ieee80211_iface_limit ath11k_if_limits[] = { + { + .max = 1, + .types = BIT(NL80211_IFTYPE_STATION), + }, + { + .max = 16, + .types = BIT(NL80211_IFTYPE_AP) +#ifdef CONFIG_MAC80211_MESH + | BIT(NL80211_IFTYPE_MESH_POINT) +#endif + }, +}; + +static const struct ieee80211_iface_combination ath11k_if_comb[] = { + { + .limits = ath11k_if_limits, + .n_limits = ARRAY_SIZE(ath11k_if_limits), + .max_interfaces = 16, + .num_different_channels = 1, + .beacon_int_infra_match = true, + .beacon_int_min_gcd = 100, + .radar_detect_widths = BIT(NL80211_CHAN_WIDTH_20_NOHT) | + BIT(NL80211_CHAN_WIDTH_20) | + BIT(NL80211_CHAN_WIDTH_40) | + BIT(NL80211_CHAN_WIDTH_80), + }, +}; + +static void ath11k_mac_update_ch_list(struct ath11k *ar, + struct ieee80211_supported_band *band, + u32 freq_low, u32 freq_high) +{ + int i; + + if (!(freq_low && freq_high)) + return; + + for (i = 0; i < band->n_channels; i++) { + if (band->channels[i].center_freq < freq_low || + band->channels[i].center_freq > freq_high) + band->channels[i].flags |= IEEE80211_CHAN_DISABLED; + } +} + +static int ath11k_mac_setup_channels_rates(struct ath11k *ar, + u32 supported_bands) +{ + struct ieee80211_supported_band *band; + struct ath11k_hal_reg_capabilities_ext *reg_cap; + void *channels; + + BUILD_BUG_ON((ARRAY_SIZE(ath11k_2ghz_channels) + + ARRAY_SIZE(ath11k_5ghz_channels)) != + ATH11K_NUM_CHANS); + + reg_cap = &ar->ab->hal_reg_cap[ar->pdev_idx]; + + if (supported_bands & WMI_HOST_WLAN_2G_CAP) { + channels = kmemdup(ath11k_2ghz_channels, + sizeof(ath11k_2ghz_channels), + GFP_KERNEL); + if (!channels) + return -ENOMEM; + + band = &ar->mac.sbands[NL80211_BAND_2GHZ]; + band->n_channels = ARRAY_SIZE(ath11k_2ghz_channels); + band->channels = channels; + band->n_bitrates = ath11k_g_rates_size; + band->bitrates = ath11k_g_rates; + ar->hw->wiphy->bands[NL80211_BAND_2GHZ] = band; + ath11k_mac_update_ch_list(ar, band, + reg_cap->low_2ghz_chan, + reg_cap->high_2ghz_chan); + } + + if (supported_bands & WMI_HOST_WLAN_5G_CAP) { + channels = kmemdup(ath11k_5ghz_channels, + sizeof(ath11k_5ghz_channels), + GFP_KERNEL); + if (!channels) { + kfree(ar->mac.sbands[NL80211_BAND_2GHZ].channels); + return -ENOMEM; + } + + band = &ar->mac.sbands[NL80211_BAND_5GHZ]; + band->n_channels = ARRAY_SIZE(ath11k_5ghz_channels); + band->channels = channels; + band->n_bitrates = ath11k_a_rates_size; + band->bitrates = ath11k_a_rates; + ar->hw->wiphy->bands[NL80211_BAND_5GHZ] = band; + ath11k_mac_update_ch_list(ar, band, + reg_cap->low_5ghz_chan, + reg_cap->high_5ghz_chan); + } + + return 0; +} + +static const u8 ath11k_if_types_ext_capa[] = { + [0] = WLAN_EXT_CAPA1_EXT_CHANNEL_SWITCHING, + [7] = WLAN_EXT_CAPA8_OPMODE_NOTIF, +}; + +static const u8 ath11k_if_types_ext_capa_sta[] = { + [0] = WLAN_EXT_CAPA1_EXT_CHANNEL_SWITCHING, + [7] = WLAN_EXT_CAPA8_OPMODE_NOTIF, + [9] = WLAN_EXT_CAPA10_TWT_REQUESTER_SUPPORT, +}; + +static const u8 ath11k_if_types_ext_capa_ap[] = { + [0] = WLAN_EXT_CAPA1_EXT_CHANNEL_SWITCHING, + [7] = WLAN_EXT_CAPA8_OPMODE_NOTIF, + [9] = WLAN_EXT_CAPA10_TWT_RESPONDER_SUPPORT, +}; + +static const struct wiphy_iftype_ext_capab ath11k_iftypes_ext_capa[] = { + { + .extended_capabilities = ath11k_if_types_ext_capa, + .extended_capabilities_mask = ath11k_if_types_ext_capa, + .extended_capabilities_len = sizeof(ath11k_if_types_ext_capa), + }, { + .iftype = NL80211_IFTYPE_STATION, + .extended_capabilities = ath11k_if_types_ext_capa_sta, + .extended_capabilities_mask = ath11k_if_types_ext_capa_sta, + .extended_capabilities_len = + sizeof(ath11k_if_types_ext_capa_sta), + }, { + .iftype = NL80211_IFTYPE_AP, + .extended_capabilities = ath11k_if_types_ext_capa_ap, + .extended_capabilities_mask = ath11k_if_types_ext_capa_ap, + .extended_capabilities_len = + sizeof(ath11k_if_types_ext_capa_ap), + }, +}; + +static int ath11k_mac_register(struct ath11k *ar) +{ + struct ath11k_base *ab = ar->ab; + struct ath11k_pdev_cap *cap = &ar->pdev->cap; + static const u32 cipher_suites[] = { + WLAN_CIPHER_SUITE_TKIP, + WLAN_CIPHER_SUITE_CCMP, + WLAN_CIPHER_SUITE_AES_CMAC, + WLAN_CIPHER_SUITE_BIP_CMAC_256, + WLAN_CIPHER_SUITE_BIP_GMAC_128, + WLAN_CIPHER_SUITE_BIP_GMAC_256, + WLAN_CIPHER_SUITE_GCMP, + WLAN_CIPHER_SUITE_GCMP_256, + WLAN_CIPHER_SUITE_CCMP_256, + }; + int ret; + u32 ht_cap = 0; + + ath11k_pdev_caps_update(ar); + + SET_IEEE80211_PERM_ADDR(ar->hw, ar->mac_addr); + + SET_IEEE80211_DEV(ar->hw, ab->dev); + + ret = ath11k_mac_setup_channels_rates(ar, + cap->supported_bands); + if (ret) + goto err_free; + + ath11k_mac_setup_ht_vht_cap(ar, cap, &ht_cap); + + ar->hw->wiphy->available_antennas_rx = cap->rx_chain_mask; + ar->hw->wiphy->available_antennas_tx = cap->tx_chain_mask; + + ar->hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | + BIT(NL80211_IFTYPE_AP) | + BIT(NL80211_IFTYPE_MESH_POINT); + + ieee80211_hw_set(ar->hw, SIGNAL_DBM); + ieee80211_hw_set(ar->hw, SUPPORTS_PS); + ieee80211_hw_set(ar->hw, SUPPORTS_DYNAMIC_PS); + ieee80211_hw_set(ar->hw, MFP_CAPABLE); + ieee80211_hw_set(ar->hw, REPORTS_TX_ACK_STATUS); + ieee80211_hw_set(ar->hw, HAS_RATE_CONTROL); + ieee80211_hw_set(ar->hw, AP_LINK_PS); + ieee80211_hw_set(ar->hw, SPECTRUM_MGMT); + ieee80211_hw_set(ar->hw, SUPPORT_FAST_XMIT); + ieee80211_hw_set(ar->hw, CONNECTION_MONITOR); + ieee80211_hw_set(ar->hw, SUPPORTS_PER_STA_GTK); + ieee80211_hw_set(ar->hw, WANT_MONITOR_VIF); + ieee80211_hw_set(ar->hw, CHANCTX_STA_CSA); + ieee80211_hw_set(ar->hw, QUEUE_CONTROL); + ieee80211_hw_set(ar->hw, SUPPORTS_TX_FRAG); + ieee80211_hw_set(ar->hw, REPORTS_LOW_ACK); + if (ht_cap & WMI_HT_CAP_ENABLED) { + ieee80211_hw_set(ar->hw, AMPDU_AGGREGATION); + ieee80211_hw_set(ar->hw, TX_AMPDU_SETUP_IN_HW); + ieee80211_hw_set(ar->hw, SUPPORTS_REORDERING_BUFFER); + ieee80211_hw_set(ar->hw, SUPPORTS_AMSDU_IN_AMPDU); + } + + ar->hw->wiphy->features |= NL80211_FEATURE_STATIC_SMPS; + ar->hw->wiphy->flags |= WIPHY_FLAG_IBSS_RSN; + + /* TODO: Check if HT capability advertised from firmware is different + * for each band for a dual band capable radio. It will be tricky to + * handle it when the ht capability different for each band. + */ + if (ht_cap & WMI_HT_CAP_DYNAMIC_SMPS) + ar->hw->wiphy->features |= NL80211_FEATURE_DYNAMIC_SMPS; + + ar->hw->wiphy->max_scan_ssids = WLAN_SCAN_PARAMS_MAX_SSID; + ar->hw->wiphy->max_scan_ie_len = WLAN_SCAN_PARAMS_MAX_IE_LEN; + + ar->hw->max_listen_interval = ATH11K_MAX_HW_LISTEN_INTERVAL; + + ar->hw->wiphy->flags |= WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL; + ar->hw->wiphy->flags |= WIPHY_FLAG_HAS_CHANNEL_SWITCH; + ar->hw->wiphy->max_remain_on_channel_duration = 5000; + + ar->hw->wiphy->flags |= WIPHY_FLAG_AP_UAPSD; + ar->hw->wiphy->features |= NL80211_FEATURE_AP_MODE_CHAN_WIDTH_CHANGE | + NL80211_FEATURE_AP_SCAN; + + ar->max_num_stations = TARGET_NUM_STATIONS; + ar->max_num_peers = TARGET_NUM_PEERS_PDEV; + + ar->hw->wiphy->max_ap_assoc_sta = ar->max_num_stations; + + ar->hw->queues = ATH11K_HW_MAX_QUEUES; + ar->hw->offchannel_tx_hw_queue = ATH11K_HW_MAX_QUEUES - 1; + ar->hw->max_rx_aggregation_subframes = IEEE80211_MAX_AMPDU_BUF; + + ar->hw->vif_data_size = sizeof(struct ath11k_vif); + ar->hw->sta_data_size = sizeof(struct ath11k_sta); + + ar->hw->wiphy->iface_combinations = ath11k_if_comb; + ar->hw->wiphy->n_iface_combinations = ARRAY_SIZE(ath11k_if_comb); + + wiphy_ext_feature_set(ar->hw->wiphy, NL80211_EXT_FEATURE_CQM_RSSI_LIST); + + ar->hw->wiphy->cipher_suites = cipher_suites; + ar->hw->wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites); + + ar->hw->wiphy->iftype_ext_capab = ath11k_iftypes_ext_capa; + ar->hw->wiphy->num_iftype_ext_capab = + ARRAY_SIZE(ath11k_iftypes_ext_capa); + + ath11k_reg_init(ar); + + /* advertise HW checksum offload capabilities */ + ar->hw->netdev_features = NETIF_F_HW_CSUM; + + ret = ieee80211_register_hw(ar->hw); + if (ret) { + ath11k_err(ar->ab, "ieee80211 registration failed: %d\n", ret); + goto err_free; + } + + /* Apply the regd received during initialization */ + ret = ath11k_regd_update(ar, true); + if (ret) { + ath11k_err(ar->ab, "ath11k regd update failed: %d\n", ret); + goto err_free; + } + + ret = ath11k_debug_register(ar); + if (ret) { + ath11k_err(ar->ab, "debugfs registration failed: %d\n", ret); + goto err_free; + } + + return 0; + +err_free: + kfree(ar->mac.sbands[NL80211_BAND_2GHZ].channels); + kfree(ar->mac.sbands[NL80211_BAND_5GHZ].channels); + + SET_IEEE80211_DEV(ar->hw, NULL); + return ret; +} + +void ath11k_mac_unregister(struct ath11k_base *ab) +{ + struct ath11k *ar; + struct ath11k_pdev *pdev; + int i; + + for (i = 0; i < ab->num_radios; i++) { + pdev = &ab->pdevs[i]; + ar = pdev->ar; + if (!ar) + continue; + cancel_work_sync(&ar->regd_update_work); + + ieee80211_unregister_hw(ar->hw); + + idr_for_each(&ar->txmgmt_idr, ath11k_mac_tx_mgmt_pending_free, ar); + idr_destroy(&ar->txmgmt_idr); + + kfree(ar->mac.sbands[NL80211_BAND_2GHZ].channels); + kfree(ar->mac.sbands[NL80211_BAND_5GHZ].channels); + + SET_IEEE80211_DEV(ar->hw, NULL); + } +} + +int ath11k_mac_create(struct ath11k_base *ab) +{ + struct ieee80211_hw *hw; + struct ath11k *ar; + struct ath11k_pdev *pdev; + int ret; + int i; + + if (test_bit(ATH11K_FLAG_REGISTERED, &ab->dev_flags)) + return 0; + + for (i = 0; i < ab->num_radios; i++) { + pdev = &ab->pdevs[i]; + hw = ieee80211_alloc_hw(sizeof(struct ath11k), &ath11k_ops); + if (!hw) { + ath11k_warn(ab, "failed to allocate mac80211 hw device\n"); + ret = -ENOMEM; + goto err_destroy_mac; + } + + ar = hw->priv; + ar->hw = hw; + ar->ab = ab; + ar->pdev = pdev; + ar->pdev_idx = i; + ar->lmac_id = ath11k_core_get_hw_mac_id(ab, i); + + ar->wmi = &ab->wmi_sc.wmi[i]; + /* FIXME wmi[0] is already initialized during attach, + * Should we do this again? + */ + ath11k_wmi_pdev_attach(ab, i); + + ar->cfg_tx_chainmask = pdev->cap.tx_chain_mask; + ar->cfg_rx_chainmask = pdev->cap.rx_chain_mask; + ar->num_tx_chains = get_num_chains(pdev->cap.tx_chain_mask); + ar->num_rx_chains = get_num_chains(pdev->cap.rx_chain_mask); + + if (ab->pdevs_macaddr_valid) { + ether_addr_copy(ar->mac_addr, pdev->mac_addr); + } else { + ether_addr_copy(ar->mac_addr, ab->mac_addr); + ar->mac_addr[4] += i; + } + + pdev->ar = ar; + spin_lock_init(&ar->data_lock); + INIT_LIST_HEAD(&ar->arvifs); + INIT_LIST_HEAD(&ar->ppdu_stats_info); + mutex_init(&ar->conf_mutex); + init_completion(&ar->vdev_setup_done); + init_completion(&ar->peer_assoc_done); + init_completion(&ar->install_key_done); + init_completion(&ar->bss_survey_done); + init_completion(&ar->scan.started); + init_completion(&ar->scan.completed); + INIT_DELAYED_WORK(&ar->scan.timeout, ath11k_scan_timeout_work); + INIT_WORK(&ar->regd_update_work, ath11k_regd_update_work); + + INIT_WORK(&ar->wmi_mgmt_tx_work, ath11k_mgmt_over_wmi_tx_work); + skb_queue_head_init(&ar->wmi_mgmt_tx_queue); + clear_bit(ATH11K_FLAG_MONITOR_ENABLED, &ar->monitor_flags); + + ret = ath11k_mac_register(ar); + if (ret) { + ath11k_warn(ab, "failed to register hw device\n"); + pdev->ar = NULL; + ieee80211_free_hw(hw); + goto err_destroy_mac; + } + + idr_init(&ar->txmgmt_idr); + spin_lock_init(&ar->txmgmt_idr_lock); + } + + /* Initialize channel counters frequency value in hertz */ + ab->cc_freq_hz = IPQ8074_CC_FREQ_HERTZ; + ab->free_vdev_map = (1LL << (ab->num_radios * TARGET_NUM_VDEVS)) - 1; + + return 0; + +err_destroy_mac: + ath11k_mac_destroy(ab); + + return ret; +} + +void ath11k_mac_destroy(struct ath11k_base *ab) +{ + struct ath11k *ar; + struct ath11k_pdev *pdev; + int i; + + for (i = 0; i < ab->num_radios; i++) { + pdev = &ab->pdevs[i]; + ar = pdev->ar; + if (!ar) + continue; + + ieee80211_free_hw(ar->hw); + pdev->ar = NULL; + } +} diff --git a/drivers/net/wireless/ath/ath11k/mac.h b/drivers/net/wireless/ath/ath11k/mac.h new file mode 100644 index 0000000000000..f84af1fc49527 --- /dev/null +++ b/drivers/net/wireless/ath/ath11k/mac.h @@ -0,0 +1,145 @@ +/* SPDX-License-Identifier: BSD-3-Clause-Clear */ +/* + * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. + */ + +#ifndef ATH11K_MAC_H +#define ATH11K_MAC_H + +#include +#include + +struct ath11k; +struct ath11k_base; + +struct ath11k_generic_iter { + struct ath11k *ar; + int ret; +}; + +/* number of failed packets (20 packets with 16 sw reties each) */ +#define ATH11K_KICKOUT_THRESHOLD (20 * 16) + +/* Use insanely high numbers to make sure that the firmware implementation + * won't start, we have the same functionality already in hostapd. Unit + * is seconds. + */ +#define ATH11K_KEEPALIVE_MIN_IDLE 3747 +#define ATH11K_KEEPALIVE_MAX_IDLE 3895 +#define ATH11K_KEEPALIVE_MAX_UNRESPONSIVE 3900 + +#define WMI_HOST_RC_DS_FLAG 0x01 +#define WMI_HOST_RC_CW40_FLAG 0x02 +#define WMI_HOST_RC_SGI_FLAG 0x04 +#define WMI_HOST_RC_HT_FLAG 0x08 +#define WMI_HOST_RC_RTSCTS_FLAG 0x10 +#define WMI_HOST_RC_TX_STBC_FLAG 0x20 +#define WMI_HOST_RC_RX_STBC_FLAG 0xC0 +#define WMI_HOST_RC_RX_STBC_FLAG_S 6 +#define WMI_HOST_RC_WEP_TKIP_FLAG 0x100 +#define WMI_HOST_RC_TS_FLAG 0x200 +#define WMI_HOST_RC_UAPSD_FLAG 0x400 + +#define WMI_HT_CAP_ENABLED 0x0001 +#define WMI_HT_CAP_HT20_SGI 0x0002 +#define WMI_HT_CAP_DYNAMIC_SMPS 0x0004 +#define WMI_HT_CAP_TX_STBC 0x0008 +#define WMI_HT_CAP_TX_STBC_MASK_SHIFT 3 +#define WMI_HT_CAP_RX_STBC 0x0030 +#define WMI_HT_CAP_RX_STBC_MASK_SHIFT 4 +#define WMI_HT_CAP_LDPC 0x0040 +#define WMI_HT_CAP_L_SIG_TXOP_PROT 0x0080 +#define WMI_HT_CAP_MPDU_DENSITY 0x0700 +#define WMI_HT_CAP_MPDU_DENSITY_MASK_SHIFT 8 +#define WMI_HT_CAP_HT40_SGI 0x0800 +#define WMI_HT_CAP_RX_LDPC 0x1000 +#define WMI_HT_CAP_TX_LDPC 0x2000 +#define WMI_HT_CAP_IBF_BFER 0x4000 + +/* These macros should be used when we wish to advertise STBC support for + * only 1SS or 2SS or 3SS. + */ +#define WMI_HT_CAP_RX_STBC_1SS 0x0010 +#define WMI_HT_CAP_RX_STBC_2SS 0x0020 +#define WMI_HT_CAP_RX_STBC_3SS 0x0030 + +#define WMI_HT_CAP_DEFAULT_ALL (WMI_HT_CAP_ENABLED | \ + WMI_HT_CAP_HT20_SGI | \ + WMI_HT_CAP_HT40_SGI | \ + WMI_HT_CAP_TX_STBC | \ + WMI_HT_CAP_RX_STBC | \ + WMI_HT_CAP_LDPC) + +#define WMI_VHT_CAP_MAX_MPDU_LEN_MASK 0x00000003 +#define WMI_VHT_CAP_RX_LDPC 0x00000010 +#define WMI_VHT_CAP_SGI_80MHZ 0x00000020 +#define WMI_VHT_CAP_SGI_160MHZ 0x00000040 +#define WMI_VHT_CAP_TX_STBC 0x00000080 +#define WMI_VHT_CAP_RX_STBC_MASK 0x00000300 +#define WMI_VHT_CAP_RX_STBC_MASK_SHIFT 8 +#define WMI_VHT_CAP_SU_BFER 0x00000800 +#define WMI_VHT_CAP_SU_BFEE 0x00001000 +#define WMI_VHT_CAP_MAX_CS_ANT_MASK 0x0000E000 +#define WMI_VHT_CAP_MAX_CS_ANT_MASK_SHIFT 13 +#define WMI_VHT_CAP_MAX_SND_DIM_MASK 0x00070000 +#define WMI_VHT_CAP_MAX_SND_DIM_MASK_SHIFT 16 +#define WMI_VHT_CAP_MU_BFER 0x00080000 +#define WMI_VHT_CAP_MU_BFEE 0x00100000 +#define WMI_VHT_CAP_MAX_AMPDU_LEN_EXP 0x03800000 +#define WMI_VHT_CAP_MAX_AMPDU_LEN_EXP_SHIT 23 +#define WMI_VHT_CAP_RX_FIXED_ANT 0x10000000 +#define WMI_VHT_CAP_TX_FIXED_ANT 0x20000000 + +#define WMI_VHT_CAP_MAX_MPDU_LEN_11454 0x00000002 + +/* These macros should be used when we wish to advertise STBC support for + * only 1SS or 2SS or 3SS. + */ +#define WMI_VHT_CAP_RX_STBC_1SS 0x00000100 +#define WMI_VHT_CAP_RX_STBC_2SS 0x00000200 +#define WMI_VHT_CAP_RX_STBC_3SS 0x00000300 + +#define WMI_VHT_CAP_DEFAULT_ALL (WMI_VHT_CAP_MAX_MPDU_LEN_11454 | \ + WMI_VHT_CAP_SGI_80MHZ | \ + WMI_VHT_CAP_TX_STBC | \ + WMI_VHT_CAP_RX_STBC_MASK | \ + WMI_VHT_CAP_RX_LDPC | \ + WMI_VHT_CAP_MAX_AMPDU_LEN_EXP | \ + WMI_VHT_CAP_RX_FIXED_ANT | \ + WMI_VHT_CAP_TX_FIXED_ANT) + +/* FIXME: should these be in ieee80211.h? */ +#define IEEE80211_VHT_MCS_SUPPORT_0_11_MASK GENMASK(23, 16) +#define IEEE80211_DISABLE_VHT_MCS_SUPPORT_0_11 BIT(24) + +#define WMI_MAX_SPATIAL_STREAM 3 + +#define ATH11K_CHAN_WIDTH_NUM 8 + +extern const struct htt_rx_ring_tlv_filter ath11k_mac_mon_status_filter_default; + +int ath11k_mac_create(struct ath11k_base *ab); +void ath11k_mac_destroy(struct ath11k_base *ab); +void ath11k_mac_unregister(struct ath11k_base *ab); +int ath11k_mac_hw_ratecode_to_legacy_rate(u8 hw_rc, u8 preamble, u8 *rateidx, + u16 *rate); +u8 ath11k_mac_bitrate_to_idx(const struct ieee80211_supported_band *sband, + u32 bitrate); +u8 ath11k_mac_hw_rate_to_idx(const struct ieee80211_supported_band *sband, + u8 hw_rate, bool cck); + +void __ath11k_mac_scan_finish(struct ath11k *ar); +void ath11k_mac_scan_finish(struct ath11k *ar); + +struct ath11k_vif *ath11k_mac_get_arvif(struct ath11k *ar, u32 vdev_id); +struct ath11k_vif *ath11k_mac_get_arvif_by_vdev_id(struct ath11k_base *ab, + u32 vdev_id); +struct ath11k *ath11k_mac_get_ar_by_vdev_id(struct ath11k_base *ab, u32 vdev_id); +struct ath11k *ath11k_mac_get_ar_by_pdev_id(struct ath11k_base *ab, u32 pdev_id); +struct ath11k *ath11k_mac_get_ar_vdev_stop_status(struct ath11k_base *ab, + u32 vdev_id); + +void ath11k_mac_drain_tx(struct ath11k *ar); +void ath11k_mac_peer_cleanup_all(struct ath11k *ar); +int ath11k_mac_tx_mgmt_pending_free(int buf_id, void *skb, void *ctx); +#endif diff --git a/drivers/net/wireless/ath/ath11k/peer.c b/drivers/net/wireless/ath/ath11k/peer.c new file mode 100644 index 0000000000000..4bf1dfa498b61 --- /dev/null +++ b/drivers/net/wireless/ath/ath11k/peer.c @@ -0,0 +1,236 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* + * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. + */ + +#include "core.h" +#include "peer.h" +#include "debug.h" + +struct ath11k_peer *ath11k_peer_find(struct ath11k_base *ab, int vdev_id, + const u8 *addr) +{ + struct ath11k_peer *peer; + + lockdep_assert_held(&ab->base_lock); + + list_for_each_entry(peer, &ab->peers, list) { + if (peer->vdev_id != vdev_id) + continue; + if (memcmp(peer->addr, addr, ETH_ALEN)) + continue; + + return peer; + } + + return NULL; +} + +struct ath11k_peer *ath11k_peer_find_by_addr(struct ath11k_base *ab, + const u8 *addr) +{ + struct ath11k_peer *peer; + + lockdep_assert_held(&ab->base_lock); + + list_for_each_entry(peer, &ab->peers, list) { + if (memcmp(peer->addr, addr, ETH_ALEN)) + continue; + + return peer; + } + + return NULL; +} + +struct ath11k_peer *ath11k_peer_find_by_id(struct ath11k_base *ab, + int peer_id) +{ + struct ath11k_peer *peer; + + lockdep_assert_held(&ab->base_lock); + + list_for_each_entry(peer, &ab->peers, list) + if (peer_id == peer->peer_id) + return peer; + + return NULL; +} + +void ath11k_peer_unmap_event(struct ath11k_base *ab, u16 peer_id) +{ + struct ath11k_peer *peer; + + spin_lock_bh(&ab->base_lock); + + peer = ath11k_peer_find_by_id(ab, peer_id); + if (!peer) { + ath11k_warn(ab, "peer-unmap-event: unknown peer id %d\n", + peer_id); + goto exit; + } + + ath11k_dbg(ab, ATH11K_DBG_DP_HTT, "htt peer unmap vdev %d peer %pM id %d\n", + peer->vdev_id, peer->addr, peer_id); + + list_del(&peer->list); + kfree(peer); + wake_up(&ab->peer_mapping_wq); + +exit: + spin_unlock_bh(&ab->base_lock); +} + +void ath11k_peer_map_event(struct ath11k_base *ab, u8 vdev_id, u16 peer_id, + u8 *mac_addr, u16 ast_hash) +{ + struct ath11k_peer *peer; + + spin_lock_bh(&ab->base_lock); + peer = ath11k_peer_find(ab, vdev_id, mac_addr); + if (!peer) { + peer = kzalloc(sizeof(*peer), GFP_ATOMIC); + if (!peer) + goto exit; + + peer->vdev_id = vdev_id; + peer->peer_id = peer_id; + peer->ast_hash = ast_hash; + ether_addr_copy(peer->addr, mac_addr); + list_add(&peer->list, &ab->peers); + wake_up(&ab->peer_mapping_wq); + } + + ath11k_dbg(ab, ATH11K_DBG_DP_HTT, "htt peer map vdev %d peer %pM id %d\n", + vdev_id, mac_addr, peer_id); + +exit: + spin_unlock_bh(&ab->base_lock); +} + +static int ath11k_wait_for_peer_common(struct ath11k_base *ab, int vdev_id, + const u8 *addr, bool expect_mapped) +{ + int ret; + + ret = wait_event_timeout(ab->peer_mapping_wq, ({ + bool mapped; + + spin_lock_bh(&ab->base_lock); + mapped = !!ath11k_peer_find(ab, vdev_id, addr); + spin_unlock_bh(&ab->base_lock); + + (mapped == expect_mapped || + test_bit(ATH11K_FLAG_CRASH_FLUSH, &ab->dev_flags)); + }), 3 * HZ); + + if (ret <= 0) + return -ETIMEDOUT; + + return 0; +} + +void ath11k_peer_cleanup(struct ath11k *ar, u32 vdev_id) +{ + struct ath11k_peer *peer, *tmp; + struct ath11k_base *ab = ar->ab; + + lockdep_assert_held(&ar->conf_mutex); + + spin_lock_bh(&ab->base_lock); + list_for_each_entry_safe(peer, tmp, &ab->peers, list) { + if (peer->vdev_id != vdev_id) + continue; + + ath11k_warn(ab, "removing stale peer %pM from vdev_id %d\n", + peer->addr, vdev_id); + + list_del(&peer->list); + kfree(peer); + ar->num_peers--; + } + + spin_unlock_bh(&ab->base_lock); +} + +static int ath11k_wait_for_peer_deleted(struct ath11k *ar, int vdev_id, const u8 *addr) +{ + return ath11k_wait_for_peer_common(ar->ab, vdev_id, addr, false); +} + +int ath11k_peer_delete(struct ath11k *ar, u32 vdev_id, u8 *addr) +{ + int ret; + + lockdep_assert_held(&ar->conf_mutex); + + ret = ath11k_wmi_send_peer_delete_cmd(ar, addr, vdev_id); + if (ret) { + ath11k_warn(ar->ab, + "failed to delete peer vdev_id %d addr %pM ret %d\n", + vdev_id, addr, ret); + return ret; + } + + ret = ath11k_wait_for_peer_deleted(ar, vdev_id, addr); + if (ret) + return ret; + + ar->num_peers--; + + return 0; +} + +static int ath11k_wait_for_peer_created(struct ath11k *ar, int vdev_id, const u8 *addr) +{ + return ath11k_wait_for_peer_common(ar->ab, vdev_id, addr, true); +} + +int ath11k_peer_create(struct ath11k *ar, struct ath11k_vif *arvif, + struct ieee80211_sta *sta, struct peer_create_params *param) +{ + struct ath11k_peer *peer; + int ret; + + lockdep_assert_held(&ar->conf_mutex); + + if (ar->num_peers > (ar->max_num_peers - 1)) { + ath11k_warn(ar->ab, + "failed to create peer due to insufficient peer entry resource in firmware\n"); + return -ENOBUFS; + } + + ret = ath11k_wmi_send_peer_create_cmd(ar, param); + if (ret) { + ath11k_warn(ar->ab, + "failed to send peer create vdev_id %d ret %d\n", + param->vdev_id, ret); + return ret; + } + + ret = ath11k_wait_for_peer_created(ar, param->vdev_id, + param->peer_addr); + if (ret) + return ret; + + spin_lock_bh(&ar->ab->base_lock); + + peer = ath11k_peer_find(ar->ab, param->vdev_id, param->peer_addr); + if (!peer) { + spin_unlock_bh(&ar->ab->base_lock); + ath11k_warn(ar->ab, "failed to find peer %pM on vdev %i after creation\n", + param->peer_addr, param->vdev_id); + ath11k_wmi_send_peer_delete_cmd(ar, param->peer_addr, + param->vdev_id); + return -ENOENT; + } + + peer->sta = sta; + arvif->ast_hash = peer->ast_hash; + + ar->num_peers++; + + spin_unlock_bh(&ar->ab->base_lock); + + return 0; +} diff --git a/drivers/net/wireless/ath/ath11k/peer.h b/drivers/net/wireless/ath/ath11k/peer.h new file mode 100644 index 0000000000000..9a40d1f6ccd9f --- /dev/null +++ b/drivers/net/wireless/ath/ath11k/peer.h @@ -0,0 +1,35 @@ +/* SPDX-License-Identifier: BSD-3-Clause-Clear */ +/* + * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. + */ + +#ifndef ATH11K_PEER_H +#define ATH11K_PEER_H + +struct ath11k_peer { + struct list_head list; + struct ieee80211_sta *sta; + int vdev_id; + u8 addr[ETH_ALEN]; + int peer_id; + u16 ast_hash; + + /* protected by ab->data_lock */ + struct ieee80211_key_conf *keys[WMI_MAX_KEY_INDEX + 1]; + struct dp_rx_tid rx_tid[IEEE80211_NUM_TIDS + 1]; +}; + +void ath11k_peer_unmap_event(struct ath11k_base *ab, u16 peer_id); +void ath11k_peer_map_event(struct ath11k_base *ab, u8 vdev_id, u16 peer_id, + u8 *mac_addr, u16 ast_hash); +struct ath11k_peer *ath11k_peer_find(struct ath11k_base *ab, int vdev_id, + const u8 *addr); +struct ath11k_peer *ath11k_peer_find_by_addr(struct ath11k_base *ab, + const u8 *addr); +struct ath11k_peer *ath11k_peer_find_by_id(struct ath11k_base *ab, int peer_id); +void ath11k_peer_cleanup(struct ath11k *ar, u32 vdev_id); +int ath11k_peer_delete(struct ath11k *ar, u32 vdev_id, u8 *addr); +int ath11k_peer_create(struct ath11k *ar, struct ath11k_vif *arvif, + struct ieee80211_sta *sta, struct peer_create_params *param); + +#endif /* _PEER_H_ */ diff --git a/drivers/net/wireless/ath/ath11k/qmi.c b/drivers/net/wireless/ath/ath11k/qmi.c new file mode 100644 index 0000000000000..adf41f28a2318 --- /dev/null +++ b/drivers/net/wireless/ath/ath11k/qmi.c @@ -0,0 +1,2435 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* + * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. + */ + +#include "qmi.h" +#include "core.h" +#include "debug.h" +#include +#include + +static struct qmi_elem_info qmi_wlanfw_host_cap_req_msg_v01_ei[] = { + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof(struct qmi_wlanfw_host_cap_req_msg_v01, + num_clients_valid), + }, + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(u32), + .array_type = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof(struct qmi_wlanfw_host_cap_req_msg_v01, + num_clients), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x11, + .offset = offsetof(struct qmi_wlanfw_host_cap_req_msg_v01, + wake_msi_valid), + }, + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(u32), + .array_type = NO_ARRAY, + .tlv_type = 0x11, + .offset = offsetof(struct qmi_wlanfw_host_cap_req_msg_v01, + wake_msi), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x12, + .offset = offsetof(struct qmi_wlanfw_host_cap_req_msg_v01, + gpios_valid), + }, + { + .data_type = QMI_DATA_LEN, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x12, + .offset = offsetof(struct qmi_wlanfw_host_cap_req_msg_v01, + gpios_len), + }, + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = QMI_WLFW_MAX_NUM_GPIO_V01, + .elem_size = sizeof(u32), + .array_type = VAR_LEN_ARRAY, + .tlv_type = 0x12, + .offset = offsetof(struct qmi_wlanfw_host_cap_req_msg_v01, + gpios), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x13, + .offset = offsetof(struct qmi_wlanfw_host_cap_req_msg_v01, + nm_modem_valid), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x13, + .offset = offsetof(struct qmi_wlanfw_host_cap_req_msg_v01, + nm_modem), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x14, + .offset = offsetof(struct qmi_wlanfw_host_cap_req_msg_v01, + bdf_support_valid), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x14, + .offset = offsetof(struct qmi_wlanfw_host_cap_req_msg_v01, + bdf_support), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x15, + .offset = offsetof(struct qmi_wlanfw_host_cap_req_msg_v01, + bdf_cache_support_valid), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x15, + .offset = offsetof(struct qmi_wlanfw_host_cap_req_msg_v01, + bdf_cache_support), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x16, + .offset = offsetof(struct qmi_wlanfw_host_cap_req_msg_v01, + m3_support_valid), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x16, + .offset = offsetof(struct qmi_wlanfw_host_cap_req_msg_v01, + m3_support), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x17, + .offset = offsetof(struct qmi_wlanfw_host_cap_req_msg_v01, + m3_cache_support_valid), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x17, + .offset = offsetof(struct qmi_wlanfw_host_cap_req_msg_v01, + m3_cache_support), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x18, + .offset = offsetof(struct qmi_wlanfw_host_cap_req_msg_v01, + cal_filesys_support_valid), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x18, + .offset = offsetof(struct qmi_wlanfw_host_cap_req_msg_v01, + cal_filesys_support), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x19, + .offset = offsetof(struct qmi_wlanfw_host_cap_req_msg_v01, + cal_cache_support_valid), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x19, + .offset = offsetof(struct qmi_wlanfw_host_cap_req_msg_v01, + cal_cache_support), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x1A, + .offset = offsetof(struct qmi_wlanfw_host_cap_req_msg_v01, + cal_done_valid), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x1A, + .offset = offsetof(struct qmi_wlanfw_host_cap_req_msg_v01, + cal_done), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x1B, + .offset = offsetof(struct qmi_wlanfw_host_cap_req_msg_v01, + mem_bucket_valid), + }, + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(u32), + .array_type = NO_ARRAY, + .tlv_type = 0x1B, + .offset = offsetof(struct qmi_wlanfw_host_cap_req_msg_v01, + mem_bucket), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x1C, + .offset = offsetof(struct qmi_wlanfw_host_cap_req_msg_v01, + mem_cfg_mode_valid), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x1C, + .offset = offsetof(struct qmi_wlanfw_host_cap_req_msg_v01, + mem_cfg_mode), + }, + { + .data_type = QMI_EOTI, + .array_type = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + }, +}; + +static struct qmi_elem_info qmi_wlanfw_host_cap_resp_msg_v01_ei[] = { + { + .data_type = QMI_STRUCT, + .elem_len = 1, + .elem_size = sizeof(struct qmi_response_type_v01), + .array_type = NO_ARRAY, + .tlv_type = 0x02, + .offset = offsetof(struct qmi_wlanfw_host_cap_resp_msg_v01, resp), + .ei_array = qmi_response_type_v01_ei, + }, + { + .data_type = QMI_EOTI, + .array_type = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + }, +}; + +static struct qmi_elem_info qmi_wlanfw_ind_register_req_msg_v01_ei[] = { + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof(struct qmi_wlanfw_ind_register_req_msg_v01, + fw_ready_enable_valid), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof(struct qmi_wlanfw_ind_register_req_msg_v01, + fw_ready_enable), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x11, + .offset = offsetof(struct qmi_wlanfw_ind_register_req_msg_v01, + initiate_cal_download_enable_valid), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x11, + .offset = offsetof(struct qmi_wlanfw_ind_register_req_msg_v01, + initiate_cal_download_enable), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x12, + .offset = offsetof(struct qmi_wlanfw_ind_register_req_msg_v01, + initiate_cal_update_enable_valid), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x12, + .offset = offsetof(struct qmi_wlanfw_ind_register_req_msg_v01, + initiate_cal_update_enable), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x13, + .offset = offsetof(struct qmi_wlanfw_ind_register_req_msg_v01, + msa_ready_enable_valid), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x13, + .offset = offsetof(struct qmi_wlanfw_ind_register_req_msg_v01, + msa_ready_enable), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x14, + .offset = offsetof(struct qmi_wlanfw_ind_register_req_msg_v01, + pin_connect_result_enable_valid), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x14, + .offset = offsetof(struct qmi_wlanfw_ind_register_req_msg_v01, + pin_connect_result_enable), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x15, + .offset = offsetof(struct qmi_wlanfw_ind_register_req_msg_v01, + client_id_valid), + }, + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(u32), + .array_type = NO_ARRAY, + .tlv_type = 0x15, + .offset = offsetof(struct qmi_wlanfw_ind_register_req_msg_v01, + client_id), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x16, + .offset = offsetof(struct qmi_wlanfw_ind_register_req_msg_v01, + request_mem_enable_valid), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x16, + .offset = offsetof(struct qmi_wlanfw_ind_register_req_msg_v01, + request_mem_enable), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x17, + .offset = offsetof(struct qmi_wlanfw_ind_register_req_msg_v01, + fw_mem_ready_enable_valid), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x17, + .offset = offsetof(struct qmi_wlanfw_ind_register_req_msg_v01, + fw_mem_ready_enable), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x18, + .offset = offsetof(struct qmi_wlanfw_ind_register_req_msg_v01, + fw_init_done_enable_valid), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x18, + .offset = offsetof(struct qmi_wlanfw_ind_register_req_msg_v01, + fw_init_done_enable), + }, + + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x19, + .offset = offsetof(struct qmi_wlanfw_ind_register_req_msg_v01, + rejuvenate_enable_valid), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x19, + .offset = offsetof(struct qmi_wlanfw_ind_register_req_msg_v01, + rejuvenate_enable), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x1A, + .offset = offsetof(struct qmi_wlanfw_ind_register_req_msg_v01, + xo_cal_enable_valid), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x1A, + .offset = offsetof(struct qmi_wlanfw_ind_register_req_msg_v01, + xo_cal_enable), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x1B, + .offset = offsetof(struct qmi_wlanfw_ind_register_req_msg_v01, + cal_done_enable_valid), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x1B, + .offset = offsetof(struct qmi_wlanfw_ind_register_req_msg_v01, + cal_done_enable), + }, + { + .data_type = QMI_EOTI, + .array_type = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + }, +}; + +static struct qmi_elem_info qmi_wlanfw_ind_register_resp_msg_v01_ei[] = { + { + .data_type = QMI_STRUCT, + .elem_len = 1, + .elem_size = sizeof(struct qmi_response_type_v01), + .array_type = NO_ARRAY, + .tlv_type = 0x02, + .offset = offsetof(struct qmi_wlanfw_ind_register_resp_msg_v01, + resp), + .ei_array = qmi_response_type_v01_ei, + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof(struct qmi_wlanfw_ind_register_resp_msg_v01, + fw_status_valid), + }, + { + .data_type = QMI_UNSIGNED_8_BYTE, + .elem_len = 1, + .elem_size = sizeof(u64), + .array_type = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof(struct qmi_wlanfw_ind_register_resp_msg_v01, + fw_status), + }, + { + .data_type = QMI_EOTI, + .array_type = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + }, +}; + +static struct qmi_elem_info qmi_wlanfw_mem_cfg_s_v01_ei[] = { + { + .data_type = QMI_UNSIGNED_8_BYTE, + .elem_len = 1, + .elem_size = sizeof(u64), + .array_type = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct qmi_wlanfw_mem_cfg_s_v01, offset), + }, + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(u32), + .array_type = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct qmi_wlanfw_mem_cfg_s_v01, size), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct qmi_wlanfw_mem_cfg_s_v01, secure_flag), + }, + { + .data_type = QMI_EOTI, + .array_type = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + }, +}; + +static struct qmi_elem_info qmi_wlanfw_mem_seg_s_v01_ei[] = { + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(u32), + .array_type = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct qmi_wlanfw_mem_seg_s_v01, + size), + }, + { + .data_type = QMI_SIGNED_4_BYTE_ENUM, + .elem_len = 1, + .elem_size = sizeof(enum qmi_wlanfw_mem_type_enum_v01), + .array_type = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct qmi_wlanfw_mem_seg_s_v01, type), + }, + { + .data_type = QMI_DATA_LEN, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct qmi_wlanfw_mem_seg_s_v01, mem_cfg_len), + }, + { + .data_type = QMI_STRUCT, + .elem_len = QMI_WLANFW_MAX_NUM_MEM_CFG_V01, + .elem_size = sizeof(struct qmi_wlanfw_mem_cfg_s_v01), + .array_type = VAR_LEN_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct qmi_wlanfw_mem_seg_s_v01, mem_cfg), + .ei_array = qmi_wlanfw_mem_cfg_s_v01_ei, + }, + { + .data_type = QMI_EOTI, + .array_type = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + }, +}; + +static struct qmi_elem_info qmi_wlanfw_request_mem_ind_msg_v01_ei[] = { + { + .data_type = QMI_DATA_LEN, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x01, + .offset = offsetof(struct qmi_wlanfw_request_mem_ind_msg_v01, + mem_seg_len), + }, + { + .data_type = QMI_STRUCT, + .elem_len = ATH11K_QMI_WLANFW_MAX_NUM_MEM_SEG_V01, + .elem_size = sizeof(struct qmi_wlanfw_mem_seg_s_v01), + .array_type = VAR_LEN_ARRAY, + .tlv_type = 0x01, + .offset = offsetof(struct qmi_wlanfw_request_mem_ind_msg_v01, + mem_seg), + .ei_array = qmi_wlanfw_mem_seg_s_v01_ei, + }, + { + .data_type = QMI_EOTI, + .array_type = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + }, +}; + +static struct qmi_elem_info qmi_wlanfw_mem_seg_resp_s_v01_ei[] = { + { + .data_type = QMI_UNSIGNED_8_BYTE, + .elem_len = 1, + .elem_size = sizeof(u64), + .array_type = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct qmi_wlanfw_mem_seg_resp_s_v01, addr), + }, + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(u32), + .array_type = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct qmi_wlanfw_mem_seg_resp_s_v01, size), + }, + { + .data_type = QMI_SIGNED_4_BYTE_ENUM, + .elem_len = 1, + .elem_size = sizeof(enum qmi_wlanfw_mem_type_enum_v01), + .array_type = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct qmi_wlanfw_mem_seg_resp_s_v01, type), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct qmi_wlanfw_mem_seg_resp_s_v01, restore), + }, + { + .data_type = QMI_EOTI, + .array_type = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + }, +}; + +static struct qmi_elem_info qmi_wlanfw_respond_mem_req_msg_v01_ei[] = { + { + .data_type = QMI_DATA_LEN, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x01, + .offset = offsetof(struct qmi_wlanfw_respond_mem_req_msg_v01, + mem_seg_len), + }, + { + .data_type = QMI_STRUCT, + .elem_len = ATH11K_QMI_WLANFW_MAX_NUM_MEM_SEG_V01, + .elem_size = sizeof(struct qmi_wlanfw_mem_seg_resp_s_v01), + .array_type = VAR_LEN_ARRAY, + .tlv_type = 0x01, + .offset = offsetof(struct qmi_wlanfw_respond_mem_req_msg_v01, + mem_seg), + .ei_array = qmi_wlanfw_mem_seg_resp_s_v01_ei, + }, + { + .data_type = QMI_EOTI, + .array_type = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + }, +}; + +static struct qmi_elem_info qmi_wlanfw_respond_mem_resp_msg_v01_ei[] = { + { + .data_type = QMI_STRUCT, + .elem_len = 1, + .elem_size = sizeof(struct qmi_response_type_v01), + .array_type = NO_ARRAY, + .tlv_type = 0x02, + .offset = offsetof(struct qmi_wlanfw_respond_mem_resp_msg_v01, + resp), + .ei_array = qmi_response_type_v01_ei, + }, + { + .data_type = QMI_EOTI, + .array_type = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + }, +}; + +static struct qmi_elem_info qmi_wlanfw_cap_req_msg_v01_ei[] = { + { + .data_type = QMI_EOTI, + .array_type = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + }, +}; + +static struct qmi_elem_info qmi_wlanfw_rf_chip_info_s_v01_ei[] = { + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(u32), + .array_type = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct qmi_wlanfw_rf_chip_info_s_v01, + chip_id), + }, + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(u32), + .array_type = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct qmi_wlanfw_rf_chip_info_s_v01, + chip_family), + }, + { + .data_type = QMI_EOTI, + .array_type = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + }, +}; + +static struct qmi_elem_info qmi_wlanfw_rf_board_info_s_v01_ei[] = { + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(u32), + .array_type = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct qmi_wlanfw_rf_board_info_s_v01, + board_id), + }, + { + .data_type = QMI_EOTI, + .array_type = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + }, +}; + +static struct qmi_elem_info qmi_wlanfw_soc_info_s_v01_ei[] = { + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(u32), + .array_type = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct qmi_wlanfw_soc_info_s_v01, soc_id), + }, + { + .data_type = QMI_EOTI, + .array_type = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + }, +}; + +static struct qmi_elem_info qmi_wlanfw_fw_version_info_s_v01_ei[] = { + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(u32), + .array_type = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct qmi_wlanfw_fw_version_info_s_v01, + fw_version), + }, + { + .data_type = QMI_STRING, + .elem_len = ATH11K_QMI_WLANFW_MAX_TIMESTAMP_LEN_V01 + 1, + .elem_size = sizeof(char), + .array_type = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct qmi_wlanfw_fw_version_info_s_v01, + fw_build_timestamp), + }, + { + .data_type = QMI_EOTI, + .array_type = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + }, +}; + +static struct qmi_elem_info qmi_wlanfw_cap_resp_msg_v01_ei[] = { + { + .data_type = QMI_STRUCT, + .elem_len = 1, + .elem_size = sizeof(struct qmi_response_type_v01), + .array_type = NO_ARRAY, + .tlv_type = 0x02, + .offset = offsetof(struct qmi_wlanfw_cap_resp_msg_v01, resp), + .ei_array = qmi_response_type_v01_ei, + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof(struct qmi_wlanfw_cap_resp_msg_v01, + chip_info_valid), + }, + { + .data_type = QMI_STRUCT, + .elem_len = 1, + .elem_size = sizeof(struct qmi_wlanfw_rf_chip_info_s_v01), + .array_type = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof(struct qmi_wlanfw_cap_resp_msg_v01, + chip_info), + .ei_array = qmi_wlanfw_rf_chip_info_s_v01_ei, + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x11, + .offset = offsetof(struct qmi_wlanfw_cap_resp_msg_v01, + board_info_valid), + }, + { + .data_type = QMI_STRUCT, + .elem_len = 1, + .elem_size = sizeof(struct qmi_wlanfw_rf_board_info_s_v01), + .array_type = NO_ARRAY, + .tlv_type = 0x11, + .offset = offsetof(struct qmi_wlanfw_cap_resp_msg_v01, + board_info), + .ei_array = qmi_wlanfw_rf_board_info_s_v01_ei, + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x12, + .offset = offsetof(struct qmi_wlanfw_cap_resp_msg_v01, + soc_info_valid), + }, + { + .data_type = QMI_STRUCT, + .elem_len = 1, + .elem_size = sizeof(struct qmi_wlanfw_soc_info_s_v01), + .array_type = NO_ARRAY, + .tlv_type = 0x12, + .offset = offsetof(struct qmi_wlanfw_cap_resp_msg_v01, + soc_info), + .ei_array = qmi_wlanfw_soc_info_s_v01_ei, + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x13, + .offset = offsetof(struct qmi_wlanfw_cap_resp_msg_v01, + fw_version_info_valid), + }, + { + .data_type = QMI_STRUCT, + .elem_len = 1, + .elem_size = sizeof(struct qmi_wlanfw_fw_version_info_s_v01), + .array_type = NO_ARRAY, + .tlv_type = 0x13, + .offset = offsetof(struct qmi_wlanfw_cap_resp_msg_v01, + fw_version_info), + .ei_array = qmi_wlanfw_fw_version_info_s_v01_ei, + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x14, + .offset = offsetof(struct qmi_wlanfw_cap_resp_msg_v01, + fw_build_id_valid), + }, + { + .data_type = QMI_STRING, + .elem_len = ATH11K_QMI_WLANFW_MAX_BUILD_ID_LEN_V01 + 1, + .elem_size = sizeof(char), + .array_type = NO_ARRAY, + .tlv_type = 0x14, + .offset = offsetof(struct qmi_wlanfw_cap_resp_msg_v01, + fw_build_id), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x15, + .offset = offsetof(struct qmi_wlanfw_cap_resp_msg_v01, + num_macs_valid), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x15, + .offset = offsetof(struct qmi_wlanfw_cap_resp_msg_v01, + num_macs), + }, + { + .data_type = QMI_EOTI, + .array_type = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + }, +}; + +static struct qmi_elem_info qmi_wlanfw_bdf_download_req_msg_v01_ei[] = { + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x01, + .offset = offsetof(struct qmi_wlanfw_bdf_download_req_msg_v01, + valid), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof(struct qmi_wlanfw_bdf_download_req_msg_v01, + file_id_valid), + }, + { + .data_type = QMI_SIGNED_4_BYTE_ENUM, + .elem_len = 1, + .elem_size = sizeof(enum qmi_wlanfw_cal_temp_id_enum_v01), + .array_type = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof(struct qmi_wlanfw_bdf_download_req_msg_v01, + file_id), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x11, + .offset = offsetof(struct qmi_wlanfw_bdf_download_req_msg_v01, + total_size_valid), + }, + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(u32), + .array_type = NO_ARRAY, + .tlv_type = 0x11, + .offset = offsetof(struct qmi_wlanfw_bdf_download_req_msg_v01, + total_size), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x12, + .offset = offsetof(struct qmi_wlanfw_bdf_download_req_msg_v01, + seg_id_valid), + }, + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(u32), + .array_type = NO_ARRAY, + .tlv_type = 0x12, + .offset = offsetof(struct qmi_wlanfw_bdf_download_req_msg_v01, + seg_id), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x13, + .offset = offsetof(struct qmi_wlanfw_bdf_download_req_msg_v01, + data_valid), + }, + { + .data_type = QMI_DATA_LEN, + .elem_len = 1, + .elem_size = sizeof(u16), + .array_type = NO_ARRAY, + .tlv_type = 0x13, + .offset = offsetof(struct qmi_wlanfw_bdf_download_req_msg_v01, + data_len), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = QMI_WLANFW_MAX_DATA_SIZE_V01, + .elem_size = sizeof(u8), + .array_type = VAR_LEN_ARRAY, + .tlv_type = 0x13, + .offset = offsetof(struct qmi_wlanfw_bdf_download_req_msg_v01, + data), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x14, + .offset = offsetof(struct qmi_wlanfw_bdf_download_req_msg_v01, + end_valid), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x14, + .offset = offsetof(struct qmi_wlanfw_bdf_download_req_msg_v01, + end), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x15, + .offset = offsetof(struct qmi_wlanfw_bdf_download_req_msg_v01, + bdf_type_valid), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x15, + .offset = offsetof(struct qmi_wlanfw_bdf_download_req_msg_v01, + bdf_type), + }, + + { + .data_type = QMI_EOTI, + .array_type = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + }, +}; + +static struct qmi_elem_info qmi_wlanfw_bdf_download_resp_msg_v01_ei[] = { + { + .data_type = QMI_STRUCT, + .elem_len = 1, + .elem_size = sizeof(struct qmi_response_type_v01), + .array_type = NO_ARRAY, + .tlv_type = 0x02, + .offset = offsetof(struct qmi_wlanfw_bdf_download_resp_msg_v01, + resp), + .ei_array = qmi_response_type_v01_ei, + }, + { + .data_type = QMI_EOTI, + .array_type = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + }, +}; + +static struct qmi_elem_info qmi_wlanfw_m3_info_req_msg_v01_ei[] = { + { + .data_type = QMI_UNSIGNED_8_BYTE, + .elem_len = 1, + .elem_size = sizeof(u64), + .array_type = NO_ARRAY, + .tlv_type = 0x01, + .offset = offsetof(struct qmi_wlanfw_m3_info_req_msg_v01, addr), + }, + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(u32), + .array_type = NO_ARRAY, + .tlv_type = 0x02, + .offset = offsetof(struct qmi_wlanfw_m3_info_req_msg_v01, size), + }, + { + .data_type = QMI_EOTI, + .array_type = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + }, +}; + +static struct qmi_elem_info qmi_wlanfw_m3_info_resp_msg_v01_ei[] = { + { + .data_type = QMI_STRUCT, + .elem_len = 1, + .elem_size = sizeof(struct qmi_response_type_v01), + .array_type = NO_ARRAY, + .tlv_type = 0x02, + .offset = offsetof(struct qmi_wlanfw_m3_info_resp_msg_v01, resp), + .ei_array = qmi_response_type_v01_ei, + }, + { + .data_type = QMI_EOTI, + .array_type = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + }, +}; + +static struct qmi_elem_info qmi_wlanfw_ce_tgt_pipe_cfg_s_v01_ei[] = { + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(u32), + .array_type = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct qmi_wlanfw_ce_tgt_pipe_cfg_s_v01, + pipe_num), + }, + { + .data_type = QMI_SIGNED_4_BYTE_ENUM, + .elem_len = 1, + .elem_size = sizeof(enum qmi_wlanfw_pipedir_enum_v01), + .array_type = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct qmi_wlanfw_ce_tgt_pipe_cfg_s_v01, + pipe_dir), + }, + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(u32), + .array_type = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct qmi_wlanfw_ce_tgt_pipe_cfg_s_v01, + nentries), + }, + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(u32), + .array_type = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct qmi_wlanfw_ce_tgt_pipe_cfg_s_v01, + nbytes_max), + }, + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(u32), + .array_type = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct qmi_wlanfw_ce_tgt_pipe_cfg_s_v01, + flags), + }, + { + .data_type = QMI_EOTI, + .array_type = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + }, +}; + +static struct qmi_elem_info qmi_wlanfw_ce_svc_pipe_cfg_s_v01_ei[] = { + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(u32), + .array_type = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct qmi_wlanfw_ce_svc_pipe_cfg_s_v01, + service_id), + }, + { + .data_type = QMI_SIGNED_4_BYTE_ENUM, + .elem_len = 1, + .elem_size = sizeof(enum qmi_wlanfw_pipedir_enum_v01), + .array_type = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct qmi_wlanfw_ce_svc_pipe_cfg_s_v01, + pipe_dir), + }, + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(u32), + .array_type = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct qmi_wlanfw_ce_svc_pipe_cfg_s_v01, + pipe_num), + }, + { + .data_type = QMI_EOTI, + .array_type = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + }, +}; + +static struct qmi_elem_info qmi_wlanfw_shadow_reg_cfg_s_v01_ei[] = { + { + .data_type = QMI_UNSIGNED_2_BYTE, + .elem_len = 1, + .elem_size = sizeof(u16), + .array_type = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct qmi_wlanfw_shadow_reg_cfg_s_v01, id), + }, + { + .data_type = QMI_UNSIGNED_2_BYTE, + .elem_len = 1, + .elem_size = sizeof(u16), + .array_type = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct qmi_wlanfw_shadow_reg_cfg_s_v01, + offset), + }, + { + .data_type = QMI_EOTI, + .array_type = QMI_COMMON_TLV_TYPE, + }, +}; + +static struct qmi_elem_info qmi_wlanfw_shadow_reg_v2_cfg_s_v01_ei[] = { + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(u32), + .array_type = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct qmi_wlanfw_shadow_reg_v2_cfg_s_v01, + addr), + }, + { + .data_type = QMI_EOTI, + .array_type = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + }, +}; + +static struct qmi_elem_info qmi_wlanfw_wlan_mode_req_msg_v01_ei[] = { + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(u32), + .array_type = NO_ARRAY, + .tlv_type = 0x01, + .offset = offsetof(struct qmi_wlanfw_wlan_mode_req_msg_v01, + mode), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof(struct qmi_wlanfw_wlan_mode_req_msg_v01, + hw_debug_valid), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof(struct qmi_wlanfw_wlan_mode_req_msg_v01, + hw_debug), + }, + { + .data_type = QMI_EOTI, + .array_type = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + }, +}; + +static struct qmi_elem_info qmi_wlanfw_wlan_mode_resp_msg_v01_ei[] = { + { + .data_type = QMI_STRUCT, + .elem_len = 1, + .elem_size = sizeof(struct qmi_response_type_v01), + .array_type = NO_ARRAY, + .tlv_type = 0x02, + .offset = offsetof(struct qmi_wlanfw_wlan_mode_resp_msg_v01, + resp), + .ei_array = qmi_response_type_v01_ei, + }, + { + .data_type = QMI_EOTI, + .array_type = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + }, +}; + +static struct qmi_elem_info qmi_wlanfw_wlan_cfg_req_msg_v01_ei[] = { + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof(struct qmi_wlanfw_wlan_cfg_req_msg_v01, + host_version_valid), + }, + { + .data_type = QMI_STRING, + .elem_len = QMI_WLANFW_MAX_STR_LEN_V01 + 1, + .elem_size = sizeof(char), + .array_type = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof(struct qmi_wlanfw_wlan_cfg_req_msg_v01, + host_version), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x11, + .offset = offsetof(struct qmi_wlanfw_wlan_cfg_req_msg_v01, + tgt_cfg_valid), + }, + { + .data_type = QMI_DATA_LEN, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x11, + .offset = offsetof(struct qmi_wlanfw_wlan_cfg_req_msg_v01, + tgt_cfg_len), + }, + { + .data_type = QMI_STRUCT, + .elem_len = QMI_WLANFW_MAX_NUM_CE_V01, + .elem_size = sizeof( + struct qmi_wlanfw_ce_tgt_pipe_cfg_s_v01), + .array_type = VAR_LEN_ARRAY, + .tlv_type = 0x11, + .offset = offsetof(struct qmi_wlanfw_wlan_cfg_req_msg_v01, + tgt_cfg), + .ei_array = qmi_wlanfw_ce_tgt_pipe_cfg_s_v01_ei, + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x12, + .offset = offsetof(struct qmi_wlanfw_wlan_cfg_req_msg_v01, + svc_cfg_valid), + }, + { + .data_type = QMI_DATA_LEN, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x12, + .offset = offsetof(struct qmi_wlanfw_wlan_cfg_req_msg_v01, + svc_cfg_len), + }, + { + .data_type = QMI_STRUCT, + .elem_len = QMI_WLANFW_MAX_NUM_SVC_V01, + .elem_size = sizeof(struct qmi_wlanfw_ce_svc_pipe_cfg_s_v01), + .array_type = VAR_LEN_ARRAY, + .tlv_type = 0x12, + .offset = offsetof(struct qmi_wlanfw_wlan_cfg_req_msg_v01, + svc_cfg), + .ei_array = qmi_wlanfw_ce_svc_pipe_cfg_s_v01_ei, + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x13, + .offset = offsetof(struct qmi_wlanfw_wlan_cfg_req_msg_v01, + shadow_reg_valid), + }, + { + .data_type = QMI_DATA_LEN, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x13, + .offset = offsetof(struct qmi_wlanfw_wlan_cfg_req_msg_v01, + shadow_reg_len), + }, + { + .data_type = QMI_STRUCT, + .elem_len = QMI_WLANFW_MAX_NUM_SHADOW_REG_V01, + .elem_size = sizeof(struct qmi_wlanfw_shadow_reg_cfg_s_v01), + .array_type = VAR_LEN_ARRAY, + .tlv_type = 0x13, + .offset = offsetof(struct qmi_wlanfw_wlan_cfg_req_msg_v01, + shadow_reg), + .ei_array = qmi_wlanfw_shadow_reg_cfg_s_v01_ei, + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x14, + .offset = offsetof(struct qmi_wlanfw_wlan_cfg_req_msg_v01, + shadow_reg_v2_valid), + }, + { + .data_type = QMI_DATA_LEN, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x14, + .offset = offsetof(struct qmi_wlanfw_wlan_cfg_req_msg_v01, + shadow_reg_v2_len), + }, + { + .data_type = QMI_STRUCT, + .elem_len = QMI_WLANFW_MAX_NUM_SHADOW_REG_V2_V01, + .elem_size = sizeof(struct qmi_wlanfw_shadow_reg_v2_cfg_s_v01), + .array_type = VAR_LEN_ARRAY, + .tlv_type = 0x14, + .offset = offsetof(struct qmi_wlanfw_wlan_cfg_req_msg_v01, + shadow_reg_v2), + .ei_array = qmi_wlanfw_shadow_reg_v2_cfg_s_v01_ei, + }, + { + .data_type = QMI_EOTI, + .array_type = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + }, +}; + +static struct qmi_elem_info qmi_wlanfw_wlan_cfg_resp_msg_v01_ei[] = { + { + .data_type = QMI_STRUCT, + .elem_len = 1, + .elem_size = sizeof(struct qmi_response_type_v01), + .array_type = NO_ARRAY, + .tlv_type = 0x02, + .offset = offsetof(struct qmi_wlanfw_wlan_cfg_resp_msg_v01, resp), + .ei_array = qmi_response_type_v01_ei, + }, + { + .data_type = QMI_EOTI, + .array_type = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + }, +}; + +static struct qmi_elem_info qmi_wlanfw_mem_ready_ind_msg_v01_ei[] = { + { + .data_type = QMI_EOTI, + .array_type = NO_ARRAY, + }, +}; + +static struct qmi_elem_info qmi_wlanfw_fw_ready_ind_msg_v01_ei[] = { + { + .data_type = QMI_EOTI, + .array_type = NO_ARRAY, + }, +}; + +static struct qmi_elem_info qmi_wlanfw_cold_boot_cal_done_ind_msg_v01_ei[] = { + { + .data_type = QMI_EOTI, + .array_type = NO_ARRAY, + }, +}; + +static int ath11k_qmi_host_cap_send(struct ath11k_base *ab) +{ + struct qmi_wlanfw_host_cap_req_msg_v01 req; + struct qmi_wlanfw_host_cap_resp_msg_v01 resp; + struct qmi_txn txn = {}; + int ret = 0; + + memset(&req, 0, sizeof(req)); + memset(&resp, 0, sizeof(resp)); + + req.num_clients_valid = 1; + req.num_clients = 1; + req.mem_cfg_mode = ab->qmi.target_mem_mode; + req.mem_cfg_mode_valid = 1; + req.bdf_support_valid = 1; + req.bdf_support = 1; + + req.m3_support_valid = 0; + req.m3_support = 0; + + req.m3_cache_support_valid = 0; + req.m3_cache_support = 0; + + req.cal_done_valid = 1; + req.cal_done = ab->qmi.cal_done; + + ret = qmi_txn_init(&ab->qmi.handle, &txn, + qmi_wlanfw_host_cap_resp_msg_v01_ei, &resp); + if (ret < 0) + goto out; + + ret = qmi_send_request(&ab->qmi.handle, NULL, &txn, + QMI_WLANFW_HOST_CAP_REQ_V01, + QMI_WLANFW_HOST_CAP_REQ_MSG_V01_MAX_LEN, + qmi_wlanfw_host_cap_req_msg_v01_ei, &req); + if (ret < 0) { + ath11k_warn(ab, "Failed to send host capability request,err = %d\n", ret); + goto out; + } + + ret = qmi_txn_wait(&txn, msecs_to_jiffies(ATH11K_QMI_WLANFW_TIMEOUT_MS)); + if (ret < 0) + goto out; + + if (resp.resp.result != QMI_RESULT_SUCCESS_V01) { + ath11k_warn(ab, "Host capability request failed, result: %d, err: %d\n", + resp.resp.result, resp.resp.error); + ret = -EINVAL; + goto out; + } + +out: + return ret; +} + +static int ath11k_qmi_fw_ind_register_send(struct ath11k_base *ab) +{ + struct qmi_wlanfw_ind_register_req_msg_v01 *req; + struct qmi_wlanfw_ind_register_resp_msg_v01 *resp; + struct qmi_handle *handle = &ab->qmi.handle; + struct qmi_txn txn; + int ret = 0; + + req = kzalloc(sizeof(*req), GFP_KERNEL); + if (!req) + return -ENOMEM; + + resp = kzalloc(sizeof(*resp), GFP_KERNEL); + if (!resp) + goto resp_out; + + req->client_id_valid = 1; + req->client_id = QMI_WLANFW_CLIENT_ID; + req->fw_ready_enable_valid = 1; + req->fw_ready_enable = 1; + req->request_mem_enable_valid = 1; + req->request_mem_enable = 1; + req->fw_mem_ready_enable_valid = 1; + req->fw_mem_ready_enable = 1; + req->cal_done_enable_valid = 1; + req->cal_done_enable = 1; + req->fw_init_done_enable_valid = 1; + req->fw_init_done_enable = 1; + + req->pin_connect_result_enable_valid = 0; + req->pin_connect_result_enable = 0; + + ret = qmi_txn_init(handle, &txn, + qmi_wlanfw_ind_register_resp_msg_v01_ei, resp); + if (ret < 0) + goto out; + + ret = qmi_send_request(&ab->qmi.handle, NULL, &txn, + QMI_WLANFW_IND_REGISTER_REQ_V01, + QMI_WLANFW_IND_REGISTER_REQ_MSG_V01_MAX_LEN, + qmi_wlanfw_ind_register_req_msg_v01_ei, req); + if (ret < 0) { + ath11k_warn(ab, "Failed to send indication register request, err = %d\n", + ret); + goto out; + } + + ret = qmi_txn_wait(&txn, msecs_to_jiffies(ATH11K_QMI_WLANFW_TIMEOUT_MS)); + if (ret < 0) { + ath11k_warn(ab, "failed to register fw indication %d\n", ret); + goto out; + } + + if (resp->resp.result != QMI_RESULT_SUCCESS_V01) { + ath11k_warn(ab, "FW Ind register request failed, result: %d, err: %d\n", + resp->resp.result, resp->resp.error); + ret = -EINVAL; + goto out; + } + +out: + kfree(resp); +resp_out: + kfree(req); + return ret; +} + +static int ath11k_qmi_respond_fw_mem_request(struct ath11k_base *ab) +{ + struct qmi_wlanfw_respond_mem_req_msg_v01 *req; + struct qmi_wlanfw_respond_mem_resp_msg_v01 resp; + struct qmi_txn txn = {}; + int ret = 0, i; + + req = kzalloc(sizeof(*req), GFP_KERNEL); + if (!req) + return -ENOMEM; + + memset(&resp, 0, sizeof(resp)); + + req->mem_seg_len = ab->qmi.mem_seg_count; + + ret = qmi_txn_init(&ab->qmi.handle, &txn, + qmi_wlanfw_respond_mem_resp_msg_v01_ei, &resp); + if (ret < 0) + goto out; + + for (i = 0; i < req->mem_seg_len ; i++) { + req->mem_seg[i].addr = ab->qmi.target_mem[i].paddr; + req->mem_seg[i].size = ab->qmi.target_mem[i].size; + req->mem_seg[i].type = ab->qmi.target_mem[i].type; + } + + ret = qmi_send_request(&ab->qmi.handle, NULL, &txn, + QMI_WLANFW_RESPOND_MEM_REQ_V01, + QMI_WLANFW_RESPOND_MEM_REQ_MSG_V01_MAX_LEN, + qmi_wlanfw_respond_mem_req_msg_v01_ei, req); + if (ret < 0) { + ath11k_warn(ab, "qmi failed to respond memory request, err = %d\n", + ret); + goto out; + } + + ret = qmi_txn_wait(&txn, msecs_to_jiffies(ATH11K_QMI_WLANFW_TIMEOUT_MS)); + if (ret < 0) { + ath11k_warn(ab, "qmi failed memory request, err = %d\n", ret); + goto out; + } + + if (resp.resp.result != QMI_RESULT_SUCCESS_V01) { + ath11k_warn(ab, "Respond mem req failed, result: %d, err: %d\n", + resp.resp.result, resp.resp.error); + ret = -EINVAL; + goto out; + } +out: + kfree(req); + return ret; +} + +static int ath11k_qmi_alloc_target_mem_chunk(struct ath11k_base *ab) +{ + int i, idx; + + for (i = 0, idx = 0; i < ab->qmi.mem_seg_count; i++) { + switch (ab->qmi.target_mem[i].type) { + case BDF_MEM_REGION_TYPE: + ab->qmi.target_mem[idx].paddr = ATH11K_QMI_BDF_ADDRESS; + ab->qmi.target_mem[idx].vaddr = ATH11K_QMI_BDF_ADDRESS; + ab->qmi.target_mem[idx].size = ab->qmi.target_mem[i].size; + ab->qmi.target_mem[idx].type = ab->qmi.target_mem[i].type; + idx++; + break; + case CALDB_MEM_REGION_TYPE: + if (ab->qmi.target_mem[i].size > ATH11K_QMI_CALDB_SIZE) { + ath11k_warn(ab, "qmi mem size is low to load caldata\n"); + return -EINVAL; + } + /* TODO ath11k does not support cold boot calibration */ + ab->qmi.target_mem[idx].paddr = 0; + ab->qmi.target_mem[idx].vaddr = 0; + ab->qmi.target_mem[idx].size = ab->qmi.target_mem[i].size; + ab->qmi.target_mem[idx].type = ab->qmi.target_mem[i].type; + idx++; + break; + default: + ath11k_warn(ab, "qmi ignore invalid mem req type %d\n", + ab->qmi.target_mem[i].type); + break; + } + } + ab->qmi.mem_seg_count = idx; + + return 0; +} + +static int ath11k_qmi_request_target_cap(struct ath11k_base *ab) +{ + struct qmi_wlanfw_cap_req_msg_v01 req; + struct qmi_wlanfw_cap_resp_msg_v01 resp; + struct qmi_txn txn = {}; + int ret = 0; + + memset(&req, 0, sizeof(req)); + memset(&resp, 0, sizeof(resp)); + + ret = qmi_txn_init(&ab->qmi.handle, &txn, + qmi_wlanfw_cap_resp_msg_v01_ei, &resp); + if (ret < 0) + goto out; + + ret = qmi_send_request(&ab->qmi.handle, NULL, &txn, + QMI_WLANFW_CAP_REQ_V01, + QMI_WLANFW_CAP_REQ_MSG_V01_MAX_LEN, + qmi_wlanfw_cap_req_msg_v01_ei, &req); + if (ret < 0) { + ath11k_warn(ab, "qmi failed to send target cap request, err = %d\n", + ret); + goto out; + } + + ret = qmi_txn_wait(&txn, msecs_to_jiffies(ATH11K_QMI_WLANFW_TIMEOUT_MS)); + if (ret < 0) { + ath11k_warn(ab, "qmi failed target cap request %d\n", ret); + goto out; + } + + if (resp.resp.result != QMI_RESULT_SUCCESS_V01) { + ath11k_warn(ab, "qmi targetcap req failed, result: %d, err: %d\n", + resp.resp.result, resp.resp.error); + ret = -EINVAL; + goto out; + } + + if (resp.chip_info_valid) { + ab->qmi.target.chip_id = resp.chip_info.chip_id; + ab->qmi.target.chip_family = resp.chip_info.chip_family; + } + + if (resp.board_info_valid) + ab->qmi.target.board_id = resp.board_info.board_id; + else + ab->qmi.target.board_id = 0xFF; + + if (resp.soc_info_valid) + ab->qmi.target.soc_id = resp.soc_info.soc_id; + + if (resp.fw_version_info_valid) { + ab->qmi.target.fw_version = resp.fw_version_info.fw_version; + strlcpy(ab->qmi.target.fw_build_timestamp, + resp.fw_version_info.fw_build_timestamp, + sizeof(ab->qmi.target.fw_build_timestamp)); + } + + if (resp.fw_build_id_valid) + strlcpy(ab->qmi.target.fw_build_id, resp.fw_build_id, + sizeof(ab->qmi.target.fw_build_id)); + + ath11k_info(ab, "qmi target: chip_id: 0x%x, chip_family: 0x%x, board_id: 0x%x, soc_id: 0x%x\n", + ab->qmi.target.chip_id, ab->qmi.target.chip_family, + ab->qmi.target.board_id, ab->qmi.target.soc_id); + + ath11k_info(ab, "qmi fw_version: 0x%x fw_build_timestamp: %s fw_build_id: %s", + ab->qmi.target.fw_version, + ab->qmi.target.fw_build_timestamp, + ab->qmi.target.fw_build_id); + +out: + return ret; +} + +static int +ath11k_qmi_prepare_bdf_download(struct ath11k_base *ab, int type, + struct qmi_wlanfw_bdf_download_req_msg_v01 *req, + void __iomem *bdf_addr) +{ + struct device *dev = ab->dev; + char filename[ATH11K_QMI_MAX_BDF_FILE_NAME_SIZE]; + const struct firmware *fw_entry; + struct ath11k_board_data bd; + u32 fw_size; + int ret = 0; + + memset(&bd, 0, sizeof(bd)); + + switch (type) { + case ATH11K_QMI_FILE_TYPE_BDF_GOLDEN: + ret = ath11k_core_fetch_bdf(ab, &bd); + if (ret) { + ath11k_warn(ab, "qmi failed to load BDF\n"); + goto out; + } + + fw_size = min_t(u32, ab->hw_params.fw.board_size, bd.len); + memcpy_toio(bdf_addr, bd.data, fw_size); + ath11k_core_free_bdf(ab, &bd); + break; + case ATH11K_QMI_FILE_TYPE_CALDATA: + snprintf(filename, sizeof(filename), + "%s/%s", ab->hw_params.fw.dir, ATH11K_QMI_DEFAULT_CAL_FILE_NAME); + ret = request_firmware(&fw_entry, filename, dev); + if (ret) { + ath11k_warn(ab, "qmi failed to load CAL: %s\n", filename); + goto out; + } + + fw_size = min_t(u32, ab->hw_params.fw.board_size, + fw_entry->size); + + memcpy_toio(bdf_addr + ATH11K_QMI_CALDATA_OFFSET, + fw_entry->data, fw_size); + ath11k_info(ab, "qmi downloading BDF: %s, size: %zu\n", + filename, fw_entry->size); + + release_firmware(fw_entry); + break; + default: + ret = -EINVAL; + goto out; + } + + req->total_size = fw_size; + +out: + return ret; +} + +static int ath11k_qmi_load_bdf(struct ath11k_base *ab) +{ + struct qmi_wlanfw_bdf_download_req_msg_v01 *req; + struct qmi_wlanfw_bdf_download_resp_msg_v01 resp; + struct qmi_txn txn = {}; + void __iomem *bdf_addr = NULL; + int type, ret; + + req = kzalloc(sizeof(*req), GFP_KERNEL); + if (!req) + return -ENOMEM; + memset(&resp, 0, sizeof(resp)); + + bdf_addr = ioremap(ATH11K_QMI_BDF_ADDRESS, ATH11K_QMI_BDF_MAX_SIZE); + if (!bdf_addr) { + ath11k_warn(ab, "qmi ioremap error for BDF\n"); + ret = -EIO; + goto out; + } + + for (type = 0; type < ATH11K_QMI_MAX_FILE_TYPE; type++) { + req->valid = 1; + req->file_id_valid = 1; + req->file_id = ab->qmi.target.board_id; + req->total_size_valid = 1; + req->seg_id_valid = 1; + req->seg_id = type; + req->data_valid = 0; + req->data_len = ATH11K_QMI_MAX_BDF_FILE_NAME_SIZE; + req->bdf_type = 0; + req->bdf_type_valid = 0; + req->end_valid = 1; + req->end = 1; + + ret = ath11k_qmi_prepare_bdf_download(ab, type, req, bdf_addr); + if (ret < 0) + goto out_qmi_bdf; + + ret = qmi_txn_init(&ab->qmi.handle, &txn, + qmi_wlanfw_bdf_download_resp_msg_v01_ei, + &resp); + if (ret < 0) + goto out_qmi_bdf; + + ret = qmi_send_request(&ab->qmi.handle, NULL, &txn, + QMI_WLANFW_BDF_DOWNLOAD_REQ_V01, + QMI_WLANFW_BDF_DOWNLOAD_REQ_MSG_V01_MAX_LEN, + qmi_wlanfw_bdf_download_req_msg_v01_ei, req); + if (ret < 0) { + qmi_txn_cancel(&txn); + goto out_qmi_bdf; + } + + ret = qmi_txn_wait(&txn, msecs_to_jiffies(ATH11K_QMI_WLANFW_TIMEOUT_MS)); + if (ret < 0) + goto out_qmi_bdf; + + if (resp.resp.result != QMI_RESULT_SUCCESS_V01) { + ath11k_warn(ab, "qmi BDF download failed, result: %d, err: %d\n", + resp.resp.result, resp.resp.error); + ret = -EINVAL; + goto out_qmi_bdf; + } + } + ath11k_info(ab, "qmi BDF downloaded\n"); + +out_qmi_bdf: + iounmap(bdf_addr); +out: + kfree(req); + return ret; +} + +static int ath11k_qmi_wlanfw_m3_info_send(struct ath11k_base *ab) +{ + struct qmi_wlanfw_m3_info_req_msg_v01 req; + struct qmi_wlanfw_m3_info_resp_msg_v01 resp; + struct qmi_txn txn = {}; + int ret = 0; + + memset(&req, 0, sizeof(req)); + memset(&resp, 0, sizeof(resp)); + req.addr = 0; + req.size = 0; + + ret = qmi_txn_init(&ab->qmi.handle, &txn, + qmi_wlanfw_m3_info_resp_msg_v01_ei, &resp); + if (ret < 0) + goto out; + + ret = qmi_send_request(&ab->qmi.handle, NULL, &txn, + QMI_WLANFW_M3_INFO_REQ_V01, + QMI_WLANFW_M3_INFO_REQ_MSG_V01_MAX_MSG_LEN, + qmi_wlanfw_m3_info_req_msg_v01_ei, &req); + if (ret < 0) { + ath11k_warn(ab, "qmi failed to send M3 information request, err = %d\n", + ret); + goto out; + } + + ret = qmi_txn_wait(&txn, msecs_to_jiffies(ATH11K_QMI_WLANFW_TIMEOUT_MS)); + if (ret < 0) { + ath11k_warn(ab, "qmi failed M3 information request %d\n", ret); + goto out; + } + + if (resp.resp.result != QMI_RESULT_SUCCESS_V01) { + ath11k_warn(ab, "qmi M3 info request failed, result: %d, err: %d\n", + resp.resp.result, resp.resp.error); + ret = -EINVAL; + goto out; + } +out: + return ret; +} + +static int ath11k_qmi_wlanfw_mode_send(struct ath11k_base *ab, + u32 mode) +{ + struct qmi_wlanfw_wlan_mode_req_msg_v01 req; + struct qmi_wlanfw_wlan_mode_resp_msg_v01 resp; + struct qmi_txn txn = {}; + int ret = 0; + + memset(&req, 0, sizeof(req)); + memset(&resp, 0, sizeof(resp)); + + req.mode = mode; + req.hw_debug_valid = 1; + req.hw_debug = 0; + + ret = qmi_txn_init(&ab->qmi.handle, &txn, + qmi_wlanfw_wlan_mode_resp_msg_v01_ei, &resp); + if (ret < 0) + goto out; + + ret = qmi_send_request(&ab->qmi.handle, NULL, &txn, + QMI_WLANFW_WLAN_MODE_REQ_V01, + QMI_WLANFW_WLAN_MODE_REQ_MSG_V01_MAX_LEN, + qmi_wlanfw_wlan_mode_req_msg_v01_ei, &req); + if (ret < 0) { + ath11k_warn(ab, "qmi failed to send mode request, mode: %d, err = %d\n", + mode, ret); + goto out; + } + + ret = qmi_txn_wait(&txn, msecs_to_jiffies(ATH11K_QMI_WLANFW_TIMEOUT_MS)); + if (ret < 0) { + if (mode == ATH11K_FIRMWARE_MODE_OFF && ret == -ENETRESET) { + ath11k_warn(ab, "WLFW service is dis-connected\n"); + return 0; + } + ath11k_warn(ab, "qmi failed set mode request, mode: %d, err = %d\n", + mode, ret); + goto out; + } + + if (resp.resp.result != QMI_RESULT_SUCCESS_V01) { + ath11k_warn(ab, "Mode request failed, mode: %d, result: %d err: %d\n", + mode, resp.resp.result, resp.resp.error); + ret = -EINVAL; + goto out; + } + +out: + return ret; +} + +static int ath11k_qmi_wlanfw_wlan_cfg_send(struct ath11k_base *ab) +{ + struct qmi_wlanfw_wlan_cfg_req_msg_v01 *req; + struct qmi_wlanfw_wlan_cfg_resp_msg_v01 resp; + struct ce_pipe_config *ce_cfg; + struct service_to_pipe *svc_cfg; + struct qmi_txn txn = {}; + int ret = 0; + + ce_cfg = (struct ce_pipe_config *)ab->qmi.ce_cfg.tgt_ce; + svc_cfg = (struct service_to_pipe *)ab->qmi.ce_cfg.svc_to_ce_map; + + req = kzalloc(sizeof(*req), GFP_KERNEL); + if (!req) + return -ENOMEM; + + memset(&resp, 0, sizeof(resp)); + + req->host_version_valid = 1; + strlcpy(req->host_version, ATH11K_HOST_VERSION_STRING, + sizeof(req->host_version)); + + req->tgt_cfg_valid = 1; + /* This is number of CE configs */ + req->tgt_cfg_len = ((ab->qmi.ce_cfg.tgt_ce_len) / + (sizeof(struct ce_pipe_config))) - 1; + for (ret = 0; ret <= req->tgt_cfg_len ; ret++) { + req->tgt_cfg[ret].pipe_num = ce_cfg[ret].pipenum; + req->tgt_cfg[ret].pipe_dir = ce_cfg[ret].pipedir; + req->tgt_cfg[ret].nentries = ce_cfg[ret].nentries; + req->tgt_cfg[ret].nbytes_max = ce_cfg[ret].nbytes_max; + req->tgt_cfg[ret].flags = ce_cfg[ret].flags; + } + + req->svc_cfg_valid = 1; + /* This is number of Service/CE configs */ + req->svc_cfg_len = (ab->qmi.ce_cfg.svc_to_ce_map_len) / + (sizeof(struct service_to_pipe)); + for (ret = 0; ret < req->svc_cfg_len; ret++) { + req->svc_cfg[ret].service_id = svc_cfg[ret].service_id; + req->svc_cfg[ret].pipe_dir = svc_cfg[ret].pipedir; + req->svc_cfg[ret].pipe_num = svc_cfg[ret].pipenum; + } + req->shadow_reg_valid = 0; + req->shadow_reg_v2_valid = 0; + + ret = qmi_txn_init(&ab->qmi.handle, &txn, + qmi_wlanfw_wlan_cfg_resp_msg_v01_ei, &resp); + if (ret < 0) + goto out; + + ret = qmi_send_request(&ab->qmi.handle, NULL, &txn, + QMI_WLANFW_WLAN_CFG_REQ_V01, + QMI_WLANFW_WLAN_CFG_REQ_MSG_V01_MAX_LEN, + qmi_wlanfw_wlan_cfg_req_msg_v01_ei, req); + if (ret < 0) { + ath11k_warn(ab, "qmi failed to send wlan config request, err = %d\n", + ret); + goto out; + } + + ret = qmi_txn_wait(&txn, msecs_to_jiffies(ATH11K_QMI_WLANFW_TIMEOUT_MS)); + if (ret < 0) { + ath11k_warn(ab, "qmi failed wlan config request, err = %d\n", ret); + goto out; + } + + if (resp.resp.result != QMI_RESULT_SUCCESS_V01) { + ath11k_warn(ab, "qmi wlan config request failed, result: %d, err: %d\n", + resp.resp.result, resp.resp.error); + ret = -EINVAL; + goto out; + } + +out: + kfree(req); + return ret; +} + +void ath11k_qmi_firmware_stop(struct ath11k_base *ab) +{ + int ret; + + ret = ath11k_qmi_wlanfw_mode_send(ab, ATH11K_FIRMWARE_MODE_OFF); + if (ret < 0) { + ath11k_warn(ab, "qmi failed to send wlan mode off\n"); + return; + } +} + +int ath11k_qmi_firmware_start(struct ath11k_base *ab, + u32 mode) +{ + int ret; + + ret = ath11k_qmi_wlanfw_wlan_cfg_send(ab); + if (ret < 0) { + ath11k_warn(ab, "qmi failed to send wlan cfg:%d\n", ret); + return ret; + } + + ret = ath11k_qmi_wlanfw_mode_send(ab, mode); + if (ret < 0) { + ath11k_warn(ab, "qmi failed to send wlan fw mode:%d\n", ret); + return ret; + } + + return 0; +} + +static int +ath11k_qmi_driver_event_post(struct ath11k_qmi *qmi, + enum ath11k_qmi_event_type type, + void *data) +{ + struct ath11k_qmi_driver_event *event; + + event = kzalloc(sizeof(*event), GFP_ATOMIC); + if (!event) + return -ENOMEM; + + event->type = type; + event->data = data; + + spin_lock(&qmi->event_lock); + list_add_tail(&event->list, &qmi->event_list); + spin_unlock(&qmi->event_lock); + + queue_work(qmi->event_wq, &qmi->event_work); + + return 0; +} + +static void ath11k_qmi_event_server_arrive(struct ath11k_qmi *qmi) +{ + struct ath11k_base *ab = qmi->ab; + int ret; + + ret = ath11k_qmi_fw_ind_register_send(ab); + if (ret < 0) { + ath11k_warn(ab, "qmi failed to send FW indication QMI:%d\n", ret); + return; + } + + ret = ath11k_qmi_host_cap_send(ab); + if (ret < 0) { + ath11k_warn(ab, "qmi failed to send host cap QMI:%d\n", ret); + return; + } +} + +static void ath11k_qmi_event_mem_request(struct ath11k_qmi *qmi) +{ + struct ath11k_base *ab = qmi->ab; + int ret; + + ret = ath11k_qmi_respond_fw_mem_request(ab); + if (ret < 0) { + ath11k_warn(ab, "qmi failed to respond fw mem req:%d\n", ret); + return; + } +} + +static void ath11k_qmi_event_load_bdf(struct ath11k_qmi *qmi) +{ + struct ath11k_base *ab = qmi->ab; + int ret; + + ret = ath11k_qmi_request_target_cap(ab); + if (ret < 0) { + ath11k_warn(ab, "qmi failed to req target capabilities:%d\n", ret); + return; + } + + ret = ath11k_qmi_load_bdf(ab); + if (ret < 0) { + ath11k_warn(ab, "qmi failed to load board data file:%d\n", ret); + return; + } + + ret = ath11k_qmi_wlanfw_m3_info_send(ab); + if (ret < 0) { + ath11k_warn(ab, "qmi failed to send m3 info req:%d\n", ret); + return; + } +} + +static void ath11k_qmi_msg_mem_request_cb(struct qmi_handle *qmi_hdl, + struct sockaddr_qrtr *sq, + struct qmi_txn *txn, + const void *data) +{ + struct ath11k_qmi *qmi = container_of(qmi_hdl, struct ath11k_qmi, handle); + struct ath11k_base *ab = qmi->ab; + const struct qmi_wlanfw_request_mem_ind_msg_v01 *msg = data; + int i, ret; + + ath11k_dbg(ab, ATH11K_DBG_QMI, "qmi firmware request memory request\n"); + + if (msg->mem_seg_len == 0 || + msg->mem_seg_len > ATH11K_QMI_WLANFW_MAX_NUM_MEM_SEG_V01) + ath11k_warn(ab, "Invalid memory segment length: %u\n", + msg->mem_seg_len); + + ab->qmi.mem_seg_count = msg->mem_seg_len; + + for (i = 0; i < qmi->mem_seg_count ; i++) { + ab->qmi.target_mem[i].type = msg->mem_seg[i].type; + ab->qmi.target_mem[i].size = msg->mem_seg[i].size; + ath11k_dbg(ab, ATH11K_DBG_QMI, "qmi mem seg type %d size %d\n", + msg->mem_seg[i].type, msg->mem_seg[i].size); + } + + ret = ath11k_qmi_alloc_target_mem_chunk(ab); + if (ret < 0) { + ath11k_warn(ab, "qmi failed to alloc target memory:%d\n", ret); + return; + } + + ath11k_qmi_driver_event_post(qmi, ATH11K_QMI_EVENT_REQUEST_MEM, NULL); +} + +static void ath11k_qmi_msg_mem_ready_cb(struct qmi_handle *qmi_hdl, + struct sockaddr_qrtr *sq, + struct qmi_txn *txn, + const void *decoded) +{ + struct ath11k_qmi *qmi = container_of(qmi_hdl, struct ath11k_qmi, handle); + struct ath11k_base *ab = qmi->ab; + + ath11k_dbg(ab, ATH11K_DBG_QMI, "qmi firmware memory ready indication\n"); + ath11k_qmi_driver_event_post(qmi, ATH11K_QMI_EVENT_FW_MEM_READY, NULL); +} + +static void ath11k_qmi_msg_fw_ready_cb(struct qmi_handle *qmi_hdl, + struct sockaddr_qrtr *sq, + struct qmi_txn *txn, + const void *decoded) +{ + struct ath11k_qmi *qmi = container_of(qmi_hdl, struct ath11k_qmi, handle); + struct ath11k_base *ab = qmi->ab; + + ath11k_dbg(ab, ATH11K_DBG_QMI, "qmi firmware ready\n"); + ath11k_qmi_driver_event_post(qmi, ATH11K_QMI_EVENT_FW_READY, NULL); +} + +static void ath11k_qmi_msg_cold_boot_cal_done_cb(struct qmi_handle *qmi, + struct sockaddr_qrtr *sq, + struct qmi_txn *txn, + const void *decoded) +{ +} + +static const struct qmi_msg_handler ath11k_qmi_msg_handlers[] = { + { + .type = QMI_INDICATION, + .msg_id = QMI_WLFW_REQUEST_MEM_IND_V01, + .ei = qmi_wlanfw_request_mem_ind_msg_v01_ei, + .decoded_size = sizeof(qmi_wlanfw_request_mem_ind_msg_v01_ei), + .fn = ath11k_qmi_msg_mem_request_cb, + }, + { + .type = QMI_INDICATION, + .msg_id = QMI_WLFW_FW_MEM_READY_IND_V01, + .ei = qmi_wlanfw_mem_ready_ind_msg_v01_ei, + .decoded_size = sizeof(qmi_wlanfw_mem_ready_ind_msg_v01_ei), + .fn = ath11k_qmi_msg_mem_ready_cb, + }, + { + .type = QMI_INDICATION, + .msg_id = QMI_WLFW_FW_READY_IND_V01, + .ei = qmi_wlanfw_fw_ready_ind_msg_v01_ei, + .decoded_size = sizeof(qmi_wlanfw_fw_ready_ind_msg_v01_ei), + .fn = ath11k_qmi_msg_fw_ready_cb, + }, + { + .type = QMI_INDICATION, + .msg_id = QMI_WLFW_COLD_BOOT_CAL_DONE_IND_V01, + .ei = qmi_wlanfw_cold_boot_cal_done_ind_msg_v01_ei, + .decoded_size = + sizeof(qmi_wlanfw_cold_boot_cal_done_ind_msg_v01_ei), + .fn = ath11k_qmi_msg_cold_boot_cal_done_cb, + }, +}; + +static int ath11k_qmi_ops_new_server(struct qmi_handle *qmi_hdl, + struct qmi_service *service) +{ + struct ath11k_qmi *qmi = container_of(qmi_hdl, struct ath11k_qmi, handle); + struct ath11k_base *ab = qmi->ab; + struct sockaddr_qrtr *sq = &qmi->sq; + int ret; + + sq->sq_family = AF_QIPCRTR; + sq->sq_node = service->node; + sq->sq_port = service->port; + + ret = kernel_connect(qmi_hdl->sock, (struct sockaddr *)sq, + sizeof(*sq), 0); + if (ret) { + ath11k_warn(ab, "qmi failed to connect to remote service %d\n", ret); + return ret; + } + + ath11k_dbg(ab, ATH11K_DBG_QMI, "qmi wifi fw qmi service connected\n"); + ath11k_qmi_driver_event_post(qmi, ATH11K_QMI_EVENT_SERVER_ARRIVE, NULL); + + return 0; +} + +static void ath11k_qmi_ops_del_server(struct qmi_handle *qmi_hdl, + struct qmi_service *service) +{ + struct ath11k_qmi *qmi = container_of(qmi_hdl, struct ath11k_qmi, handle); + struct ath11k_base *ab = qmi->ab; + + ath11k_dbg(ab, ATH11K_DBG_QMI, "qmi wifi fw del server\n"); + ath11k_qmi_driver_event_post(qmi, ATH11K_QMI_EVENT_SERVER_EXIT, NULL); +} + +static const struct qmi_ops ath11k_qmi_ops = { + .new_server = ath11k_qmi_ops_new_server, + .del_server = ath11k_qmi_ops_del_server, +}; + +static void ath11k_qmi_driver_event_work(struct work_struct *work) +{ + struct ath11k_qmi *qmi = container_of(work, struct ath11k_qmi, + event_work); + struct ath11k_qmi_driver_event *event; + struct ath11k_base *ab = qmi->ab; + + spin_lock(&qmi->event_lock); + while (!list_empty(&qmi->event_list)) { + event = list_first_entry(&qmi->event_list, + struct ath11k_qmi_driver_event, list); + list_del(&event->list); + spin_unlock(&qmi->event_lock); + + if (test_bit(ATH11K_FLAG_UNREGISTERING, &ab->dev_flags)) + return; + + switch (event->type) { + case ATH11K_QMI_EVENT_SERVER_ARRIVE: + ath11k_qmi_event_server_arrive(qmi); + break; + case ATH11K_QMI_EVENT_SERVER_EXIT: + set_bit(ATH11K_FLAG_CRASH_FLUSH, &ab->dev_flags); + set_bit(ATH11K_FLAG_RECOVERY, &ab->dev_flags); + break; + case ATH11K_QMI_EVENT_REQUEST_MEM: + ath11k_qmi_event_mem_request(qmi); + break; + case ATH11K_QMI_EVENT_FW_MEM_READY: + ath11k_qmi_event_load_bdf(qmi); + break; + case ATH11K_QMI_EVENT_FW_READY: + if (test_bit(ATH11K_FLAG_REGISTERED, &ab->dev_flags)) { + queue_work(ab->workqueue, &ab->restart_work); + break; + } + + ath11k_core_qmi_firmware_ready(ab); + ab->qmi.cal_done = 1; + set_bit(ATH11K_FLAG_REGISTERED, &ab->dev_flags); + + break; + case ATH11K_QMI_EVENT_COLD_BOOT_CAL_DONE: + break; + default: + ath11k_warn(ab, "invalid event type: %d", event->type); + break; + } + kfree(event); + spin_lock(&qmi->event_lock); + } + spin_unlock(&qmi->event_lock); +} + +int ath11k_qmi_init_service(struct ath11k_base *ab) +{ + int ret; + + memset(&ab->qmi.target, 0, sizeof(struct target_info)); + memset(&ab->qmi.target_mem, 0, sizeof(struct target_mem_chunk)); + ab->qmi.ab = ab; + + ab->qmi.target_mem_mode = ATH11K_QMI_TARGET_MEM_MODE_DEFAULT; + ret = qmi_handle_init(&ab->qmi.handle, ATH11K_QMI_RESP_LEN_MAX, + &ath11k_qmi_ops, ath11k_qmi_msg_handlers); + if (ret < 0) { + ath11k_warn(ab, "failed to initialize qmi handle\n"); + return ret; + } + + ab->qmi.event_wq = alloc_workqueue("ath11k_qmi_driver_event", + WQ_UNBOUND, 1); + if (!ab->qmi.event_wq) { + ath11k_err(ab, "failed to allocate workqueue\n"); + return -EFAULT; + } + + INIT_LIST_HEAD(&ab->qmi.event_list); + spin_lock_init(&ab->qmi.event_lock); + INIT_WORK(&ab->qmi.event_work, ath11k_qmi_driver_event_work); + + ret = qmi_add_lookup(&ab->qmi.handle, ATH11K_QMI_WLFW_SERVICE_ID_V01, + ATH11K_QMI_WLFW_SERVICE_VERS_V01, + ATH11K_QMI_WLFW_SERVICE_INS_ID_V01); + if (ret < 0) { + ath11k_warn(ab, "failed to add qmi lookup\n"); + return ret; + } + + return ret; +} + +void ath11k_qmi_deinit_service(struct ath11k_base *ab) +{ + qmi_handle_release(&ab->qmi.handle); + cancel_work_sync(&ab->qmi.event_work); + destroy_workqueue(ab->qmi.event_wq); +} + diff --git a/drivers/net/wireless/ath/ath11k/qmi.h b/drivers/net/wireless/ath/ath11k/qmi.h new file mode 100644 index 0000000000000..df0685564e21c --- /dev/null +++ b/drivers/net/wireless/ath/ath11k/qmi.h @@ -0,0 +1,445 @@ +/* SPDX-License-Identifier: BSD-3-Clause-Clear */ +/* + * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. + */ + +#ifndef ATH11K_QMI_H +#define ATH11K_QMI_H + +#include +#include + +#define ATH11K_HOST_VERSION_STRING "WIN" +#define ATH11K_QMI_WLANFW_TIMEOUT_MS 5000 +#define ATH11K_QMI_MAX_BDF_FILE_NAME_SIZE 64 +#define ATH11K_QMI_BDF_ADDRESS 0x4B0C0000 +#define ATH11K_QMI_BDF_MAX_SIZE (256 * 1024) +#define ATH11K_QMI_CALDATA_OFFSET (128 * 1024) +#define ATH11K_QMI_WLANFW_MAX_BUILD_ID_LEN_V01 128 +#define ATH11K_QMI_WLFW_SERVICE_ID_V01 0x45 +#define ATH11K_QMI_WLFW_SERVICE_VERS_V01 0x01 +#define ATH11K_QMI_WLFW_SERVICE_INS_ID_V01 0x02 +#define ATH11K_QMI_WLANFW_MAX_TIMESTAMP_LEN_V01 32 +#define ATH11K_QMI_RESP_LEN_MAX 8192 +#define ATH11K_QMI_WLANFW_MAX_NUM_MEM_SEG_V01 32 +#define ATH11K_QMI_CALDB_SIZE 0x480000 +#define ATH11K_QMI_DEFAULT_CAL_FILE_NAME "caldata.bin" + +#define QMI_WLFW_REQUEST_MEM_IND_V01 0x0035 +#define QMI_WLFW_FW_MEM_READY_IND_V01 0x0037 +#define QMI_WLFW_COLD_BOOT_CAL_DONE_IND_V01 0x0021 +#define QMI_WLFW_FW_READY_IND_V01 0x0038 + +#define QMI_WLANFW_MAX_DATA_SIZE_V01 6144 +#define ATH11K_FIRMWARE_MODE_OFF 4 +#define ATH11K_QMI_TARGET_MEM_MODE_DEFAULT 0 + +struct ath11k_base; + +enum ath11k_qmi_file_type { + ATH11K_QMI_FILE_TYPE_BDF_GOLDEN, + ATH11K_QMI_FILE_TYPE_CALDATA, + ATH11K_QMI_MAX_FILE_TYPE, +}; + +enum ath11k_qmi_event_type { + ATH11K_QMI_EVENT_SERVER_ARRIVE, + ATH11K_QMI_EVENT_SERVER_EXIT, + ATH11K_QMI_EVENT_REQUEST_MEM, + ATH11K_QMI_EVENT_FW_MEM_READY, + ATH11K_QMI_EVENT_FW_READY, + ATH11K_QMI_EVENT_COLD_BOOT_CAL_START, + ATH11K_QMI_EVENT_COLD_BOOT_CAL_DONE, + ATH11K_QMI_EVENT_REGISTER_DRIVER, + ATH11K_QMI_EVENT_UNREGISTER_DRIVER, + ATH11K_QMI_EVENT_RECOVERY, + ATH11K_QMI_EVENT_FORCE_FW_ASSERT, + ATH11K_QMI_EVENT_POWER_UP, + ATH11K_QMI_EVENT_POWER_DOWN, + ATH11K_QMI_EVENT_MAX, +}; + +struct ath11k_qmi_driver_event { + struct list_head list; + enum ath11k_qmi_event_type type; + void *data; +}; + +struct ath11k_qmi_ce_cfg { + const u8 *tgt_ce; + int tgt_ce_len; + const u8 *svc_to_ce_map; + int svc_to_ce_map_len; + const u8 *shadow_reg; + int shadow_reg_len; + u8 *shadow_reg_v2; + int shadow_reg_v2_len; +}; + +struct ath11k_qmi_event_msg { + struct list_head list; + enum ath11k_qmi_event_type type; +}; + +struct target_mem_chunk { + u32 size; + u32 type; + dma_addr_t paddr; + u32 vaddr; +}; + +struct target_info { + u32 chip_id; + u32 chip_family; + u32 board_id; + u32 soc_id; + u32 fw_version; + char fw_build_timestamp[ATH11K_QMI_WLANFW_MAX_TIMESTAMP_LEN_V01 + 1]; + char fw_build_id[ATH11K_QMI_WLANFW_MAX_BUILD_ID_LEN_V01 + 1]; +}; + +struct ath11k_qmi { + struct ath11k_base *ab; + struct qmi_handle handle; + struct sockaddr_qrtr sq; + struct work_struct event_work; + struct workqueue_struct *event_wq; + struct list_head event_list; + spinlock_t event_lock; /* spinlock for qmi event list */ + struct ath11k_qmi_ce_cfg ce_cfg; + struct target_mem_chunk target_mem[ATH11K_QMI_WLANFW_MAX_NUM_MEM_SEG_V01]; + u32 mem_seg_count; + u32 target_mem_mode; + u8 cal_done; + struct target_info target; +}; + +#define QMI_WLANFW_HOST_CAP_REQ_MSG_V01_MAX_LEN 189 +#define QMI_WLANFW_HOST_CAP_REQ_V01 0x0034 +#define QMI_WLANFW_HOST_CAP_RESP_MSG_V01_MAX_LEN 7 +#define QMI_WLFW_HOST_CAP_RESP_V01 0x0034 +#define QMI_WLFW_MAX_NUM_GPIO_V01 32 +#define QMI_IPQ8074_FW_MEM_MODE 0xFF +#define HOST_DDR_REGION_TYPE 0x1 +#define BDF_MEM_REGION_TYPE 0x2 +#define CALDB_MEM_REGION_TYPE 0x4 + +struct qmi_wlanfw_host_cap_req_msg_v01 { + u8 num_clients_valid; + u32 num_clients; + u8 wake_msi_valid; + u32 wake_msi; + u8 gpios_valid; + u32 gpios_len; + u32 gpios[QMI_WLFW_MAX_NUM_GPIO_V01]; + u8 nm_modem_valid; + u8 nm_modem; + u8 bdf_support_valid; + u8 bdf_support; + u8 bdf_cache_support_valid; + u8 bdf_cache_support; + u8 m3_support_valid; + u8 m3_support; + u8 m3_cache_support_valid; + u8 m3_cache_support; + u8 cal_filesys_support_valid; + u8 cal_filesys_support; + u8 cal_cache_support_valid; + u8 cal_cache_support; + u8 cal_done_valid; + u8 cal_done; + u8 mem_bucket_valid; + u32 mem_bucket; + u8 mem_cfg_mode_valid; + u8 mem_cfg_mode; +}; + +struct qmi_wlanfw_host_cap_resp_msg_v01 { + struct qmi_response_type_v01 resp; +}; + +#define QMI_WLANFW_IND_REGISTER_REQ_MSG_V01_MAX_LEN 54 +#define QMI_WLANFW_IND_REGISTER_REQ_V01 0x0020 +#define QMI_WLANFW_IND_REGISTER_RESP_MSG_V01_MAX_LEN 18 +#define QMI_WLANFW_IND_REGISTER_RESP_V01 0x0020 +#define QMI_WLANFW_CLIENT_ID 0x4b4e454c + +struct qmi_wlanfw_ind_register_req_msg_v01 { + u8 fw_ready_enable_valid; + u8 fw_ready_enable; + u8 initiate_cal_download_enable_valid; + u8 initiate_cal_download_enable; + u8 initiate_cal_update_enable_valid; + u8 initiate_cal_update_enable; + u8 msa_ready_enable_valid; + u8 msa_ready_enable; + u8 pin_connect_result_enable_valid; + u8 pin_connect_result_enable; + u8 client_id_valid; + u32 client_id; + u8 request_mem_enable_valid; + u8 request_mem_enable; + u8 fw_mem_ready_enable_valid; + u8 fw_mem_ready_enable; + u8 fw_init_done_enable_valid; + u8 fw_init_done_enable; + u8 rejuvenate_enable_valid; + u32 rejuvenate_enable; + u8 xo_cal_enable_valid; + u8 xo_cal_enable; + u8 cal_done_enable_valid; + u8 cal_done_enable; +}; + +struct qmi_wlanfw_ind_register_resp_msg_v01 { + struct qmi_response_type_v01 resp; + u8 fw_status_valid; + u64 fw_status; +}; + +#define QMI_WLANFW_REQUEST_MEM_IND_MSG_V01_MAX_LEN 1124 +#define QMI_WLANFW_RESPOND_MEM_REQ_MSG_V01_MAX_LEN 548 +#define QMI_WLANFW_RESPOND_MEM_RESP_MSG_V01_MAX_LEN 7 +#define QMI_WLANFW_REQUEST_MEM_IND_V01 0x0035 +#define QMI_WLANFW_RESPOND_MEM_REQ_V01 0x0036 +#define QMI_WLANFW_RESPOND_MEM_RESP_V01 0x0036 +#define QMI_WLANFW_MAX_NUM_MEM_CFG_V01 2 + +struct qmi_wlanfw_mem_cfg_s_v01 { + u64 offset; + u32 size; + u8 secure_flag; +}; + +enum qmi_wlanfw_mem_type_enum_v01 { + WLANFW_MEM_TYPE_ENUM_MIN_VAL_V01 = INT_MIN, + QMI_WLANFW_MEM_TYPE_MSA_V01 = 0, + QMI_WLANFW_MEM_TYPE_DDR_V01 = 1, + QMI_WLANFW_MEM_BDF_V01 = 2, + QMI_WLANFW_MEM_M3_V01 = 3, + QMI_WLANFW_MEM_CAL_V01 = 4, + QMI_WLANFW_MEM_DPD_V01 = 5, + WLANFW_MEM_TYPE_ENUM_MAX_VAL_V01 = INT_MAX, +}; + +struct qmi_wlanfw_mem_seg_s_v01 { + u32 size; + enum qmi_wlanfw_mem_type_enum_v01 type; + u32 mem_cfg_len; + struct qmi_wlanfw_mem_cfg_s_v01 mem_cfg[QMI_WLANFW_MAX_NUM_MEM_CFG_V01]; +}; + +struct qmi_wlanfw_request_mem_ind_msg_v01 { + u32 mem_seg_len; + struct qmi_wlanfw_mem_seg_s_v01 mem_seg[ATH11K_QMI_WLANFW_MAX_NUM_MEM_SEG_V01]; +}; + +struct qmi_wlanfw_mem_seg_resp_s_v01 { + u64 addr; + u32 size; + enum qmi_wlanfw_mem_type_enum_v01 type; + u8 restore; +}; + +struct qmi_wlanfw_respond_mem_req_msg_v01 { + u32 mem_seg_len; + struct qmi_wlanfw_mem_seg_resp_s_v01 mem_seg[ATH11K_QMI_WLANFW_MAX_NUM_MEM_SEG_V01]; +}; + +struct qmi_wlanfw_respond_mem_resp_msg_v01 { + struct qmi_response_type_v01 resp; +}; + +struct qmi_wlanfw_fw_mem_ready_ind_msg_v01 { + char placeholder; +}; + +#define QMI_WLANFW_CAP_REQ_MSG_V01_MAX_LEN 0 +#define QMI_WLANFW_CAP_RESP_MSG_V01_MAX_LEN 207 +#define QMI_WLANFW_CAP_REQ_V01 0x0024 +#define QMI_WLANFW_CAP_RESP_V01 0x0024 + +enum qmi_wlanfw_pipedir_enum_v01 { + QMI_WLFW_PIPEDIR_NONE_V01 = 0, + QMI_WLFW_PIPEDIR_IN_V01 = 1, + QMI_WLFW_PIPEDIR_OUT_V01 = 2, + QMI_WLFW_PIPEDIR_INOUT_V01 = 3, +}; + +struct qmi_wlanfw_ce_tgt_pipe_cfg_s_v01 { + __le32 pipe_num; + __le32 pipe_dir; + __le32 nentries; + __le32 nbytes_max; + __le32 flags; +}; + +struct qmi_wlanfw_ce_svc_pipe_cfg_s_v01 { + __le32 service_id; + __le32 pipe_dir; + __le32 pipe_num; +}; + +struct qmi_wlanfw_shadow_reg_cfg_s_v01 { + u16 id; + u16 offset; +}; + +struct qmi_wlanfw_shadow_reg_v2_cfg_s_v01 { + u32 addr; +}; + +struct qmi_wlanfw_memory_region_info_s_v01 { + u64 region_addr; + u32 size; + u8 secure_flag; +}; + +struct qmi_wlanfw_rf_chip_info_s_v01 { + u32 chip_id; + u32 chip_family; +}; + +struct qmi_wlanfw_rf_board_info_s_v01 { + u32 board_id; +}; + +struct qmi_wlanfw_soc_info_s_v01 { + u32 soc_id; +}; + +struct qmi_wlanfw_fw_version_info_s_v01 { + u32 fw_version; + char fw_build_timestamp[ATH11K_QMI_WLANFW_MAX_TIMESTAMP_LEN_V01 + 1]; +}; + +enum qmi_wlanfw_cal_temp_id_enum_v01 { + QMI_WLANFW_CAL_TEMP_IDX_0_V01 = 0, + QMI_WLANFW_CAL_TEMP_IDX_1_V01 = 1, + QMI_WLANFW_CAL_TEMP_IDX_2_V01 = 2, + QMI_WLANFW_CAL_TEMP_IDX_3_V01 = 3, + QMI_WLANFW_CAL_TEMP_IDX_4_V01 = 4, + QMI_WLANFW_CAL_TEMP_ID_MAX_V01 = 0xFF, +}; + +struct qmi_wlanfw_cap_resp_msg_v01 { + struct qmi_response_type_v01 resp; + u8 chip_info_valid; + struct qmi_wlanfw_rf_chip_info_s_v01 chip_info; + u8 board_info_valid; + struct qmi_wlanfw_rf_board_info_s_v01 board_info; + u8 soc_info_valid; + struct qmi_wlanfw_soc_info_s_v01 soc_info; + u8 fw_version_info_valid; + struct qmi_wlanfw_fw_version_info_s_v01 fw_version_info; + u8 fw_build_id_valid; + char fw_build_id[ATH11K_QMI_WLANFW_MAX_BUILD_ID_LEN_V01 + 1]; + u8 num_macs_valid; + u8 num_macs; +}; + +struct qmi_wlanfw_cap_req_msg_v01 { + char placeholder; +}; + +#define QMI_WLANFW_BDF_DOWNLOAD_REQ_MSG_V01_MAX_LEN 6182 +#define QMI_WLANFW_BDF_DOWNLOAD_RESP_MSG_V01_MAX_LEN 7 +#define QMI_WLANFW_BDF_DOWNLOAD_RESP_V01 0x0025 +#define QMI_WLANFW_BDF_DOWNLOAD_REQ_V01 0x0025 +/* TODO: Need to check with MCL and FW team that data can be pointer and + * can be last element in structure + */ +struct qmi_wlanfw_bdf_download_req_msg_v01 { + u8 valid; + u8 file_id_valid; + enum qmi_wlanfw_cal_temp_id_enum_v01 file_id; + u8 total_size_valid; + u32 total_size; + u8 seg_id_valid; + u32 seg_id; + u8 data_valid; + u32 data_len; + u8 data[QMI_WLANFW_MAX_DATA_SIZE_V01]; + u8 end_valid; + u8 end; + u8 bdf_type_valid; + u8 bdf_type; + +}; + +struct qmi_wlanfw_bdf_download_resp_msg_v01 { + struct qmi_response_type_v01 resp; +}; + +#define QMI_WLANFW_M3_INFO_REQ_MSG_V01_MAX_MSG_LEN 18 +#define QMI_WLANFW_M3_INFO_RESP_MSG_V01_MAX_MSG_LEN 7 +#define QMI_WLANFW_M3_INFO_RESP_V01 0x003C +#define QMI_WLANFW_M3_INFO_REQ_V01 0x003C + +struct qmi_wlanfw_m3_info_req_msg_v01 { + u64 addr; + u32 size; +}; + +struct qmi_wlanfw_m3_info_resp_msg_v01 { + struct qmi_response_type_v01 resp; +}; + +#define QMI_WLANFW_WLAN_MODE_REQ_MSG_V01_MAX_LEN 11 +#define QMI_WLANFW_WLAN_MODE_RESP_MSG_V01_MAX_LEN 7 +#define QMI_WLANFW_WLAN_CFG_REQ_MSG_V01_MAX_LEN 803 +#define QMI_WLANFW_WLAN_CFG_RESP_MSG_V01_MAX_LEN 7 +#define QMI_WLANFW_WLAN_MODE_REQ_V01 0x0022 +#define QMI_WLANFW_WLAN_MODE_RESP_V01 0x0022 +#define QMI_WLANFW_WLAN_CFG_REQ_V01 0x0023 +#define QMI_WLANFW_WLAN_CFG_RESP_V01 0x0023 +#define QMI_WLANFW_MAX_STR_LEN_V01 16 +#define QMI_WLANFW_MAX_NUM_CE_V01 12 +#define QMI_WLANFW_MAX_NUM_SVC_V01 24 +#define QMI_WLANFW_MAX_NUM_SHADOW_REG_V01 24 +#define QMI_WLANFW_MAX_NUM_SHADOW_REG_V2_V01 36 + +struct qmi_wlanfw_wlan_mode_req_msg_v01 { + u32 mode; + u8 hw_debug_valid; + u8 hw_debug; +}; + +struct qmi_wlanfw_wlan_mode_resp_msg_v01 { + struct qmi_response_type_v01 resp; +}; + +struct qmi_wlanfw_wlan_cfg_req_msg_v01 { + u8 host_version_valid; + char host_version[QMI_WLANFW_MAX_STR_LEN_V01 + 1]; + u8 tgt_cfg_valid; + u32 tgt_cfg_len; + struct qmi_wlanfw_ce_tgt_pipe_cfg_s_v01 + tgt_cfg[QMI_WLANFW_MAX_NUM_CE_V01]; + u8 svc_cfg_valid; + u32 svc_cfg_len; + struct qmi_wlanfw_ce_svc_pipe_cfg_s_v01 + svc_cfg[QMI_WLANFW_MAX_NUM_SVC_V01]; + u8 shadow_reg_valid; + u32 shadow_reg_len; + struct qmi_wlanfw_shadow_reg_cfg_s_v01 + shadow_reg[QMI_WLANFW_MAX_NUM_SHADOW_REG_V01]; + u8 shadow_reg_v2_valid; + u32 shadow_reg_v2_len; + struct qmi_wlanfw_shadow_reg_v2_cfg_s_v01 + shadow_reg_v2[QMI_WLANFW_MAX_NUM_SHADOW_REG_V2_V01]; +}; + +struct qmi_wlanfw_wlan_cfg_resp_msg_v01 { + struct qmi_response_type_v01 resp; +}; + +int ath11k_qmi_firmware_start(struct ath11k_base *ab, + u32 mode); +void ath11k_qmi_firmware_stop(struct ath11k_base *ab); +void ath11k_qmi_event_work(struct work_struct *work); +void ath11k_qmi_msg_recv_work(struct work_struct *work); +void ath11k_qmi_deinit_service(struct ath11k_base *ab); +int ath11k_qmi_init_service(struct ath11k_base *ab); + +#endif diff --git a/drivers/net/wireless/ath/ath11k/reg.c b/drivers/net/wireless/ath/ath11k/reg.c new file mode 100644 index 0000000000000..810050b033c4f --- /dev/null +++ b/drivers/net/wireless/ath/ath11k/reg.c @@ -0,0 +1,701 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* + * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. + */ +#include "core.h" +#include "debug.h" + +/* World regdom to be used in case default regd from fw is unavailable */ +#define ATH11K_2GHZ_CH01_11 REG_RULE(2412 - 10, 2462 + 10, 40, 0, 20, 0) +#define ATH11K_5GHZ_5150_5350 REG_RULE(5150 - 10, 5350 + 10, 80, 0, 30,\ + NL80211_RRF_NO_IR) +#define ATH11K_5GHZ_5725_5850 REG_RULE(5725 - 10, 5850 + 10, 80, 0, 30,\ + NL80211_RRF_NO_IR) + +#define ETSI_WEATHER_RADAR_BAND_LOW 5590 +#define ETSI_WEATHER_RADAR_BAND_HIGH 5650 +#define ETSI_WEATHER_RADAR_BAND_CAC_TIMEOUT 600000 + +static const struct ieee80211_regdomain ath11k_world_regd = { + .n_reg_rules = 3, + .alpha2 = "00", + .reg_rules = { + ATH11K_2GHZ_CH01_11, + ATH11K_5GHZ_5150_5350, + ATH11K_5GHZ_5725_5850, + } +}; + +static bool ath11k_regdom_changes(struct ath11k *ar, char *alpha2) +{ + const struct ieee80211_regdomain *regd; + + regd = rcu_dereference_rtnl(ar->hw->wiphy->regd); + /* This can happen during wiphy registration where the previous + * user request is received before we update the regd received + * from firmware. + */ + if (!regd) + return true; + + return memcmp(regd->alpha2, alpha2, 2) != 0; +} + +static void +ath11k_reg_notifier(struct wiphy *wiphy, struct regulatory_request *request) +{ + struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy); + struct wmi_init_country_params init_country_param; + struct ath11k *ar = hw->priv; + int ret; + + ath11k_dbg(ar->ab, ATH11K_DBG_REG, + "Regulatory Notification received for %s\n", wiphy_name(wiphy)); + + /* Currently supporting only General User Hints. Cell base user + * hints to be handled later. + * Hints from other sources like Core, Beacons are not expected for + * self managed wiphy's + */ + if (!(request->initiator == NL80211_REGDOM_SET_BY_USER && + request->user_reg_hint_type == NL80211_USER_REG_HINT_USER)) { + ath11k_warn(ar->ab, "Unexpected Regulatory event for this wiphy\n"); + return; + } + + if (!IS_ENABLED(CONFIG_ATH_REG_DYNAMIC_USER_REG_HINTS)) { + ath11k_dbg(ar->ab, ATH11K_DBG_REG, + "Country Setting is not allowed\n"); + return; + } + + if (!ath11k_regdom_changes(ar, request->alpha2)) { + ath11k_dbg(ar->ab, ATH11K_DBG_REG, "Country is already set\n"); + return; + } + + /* Set the country code to the firmware and wait for + * the WMI_REG_CHAN_LIST_CC EVENT for updating the + * reg info + */ + init_country_param.flags = ALPHA_IS_SET; + memcpy(&init_country_param.cc_info.alpha2, request->alpha2, 2); + + ret = ath11k_wmi_send_init_country_cmd(ar, init_country_param); + if (ret) + ath11k_warn(ar->ab, + "INIT Country code set to fw failed : %d\n", ret); +} + +int ath11k_reg_update_chan_list(struct ath11k *ar) +{ + struct ieee80211_supported_band **bands; + struct scan_chan_list_params *params; + struct ieee80211_channel *channel; + struct ieee80211_hw *hw = ar->hw; + struct channel_param *ch; + enum nl80211_band band; + int num_channels = 0; + int params_len; + int i, ret; + + bands = hw->wiphy->bands; + for (band = 0; band < NUM_NL80211_BANDS; band++) { + if (!bands[band]) + continue; + + for (i = 0; i < bands[band]->n_channels; i++) { + if (bands[band]->channels[i].flags & + IEEE80211_CHAN_DISABLED) + continue; + + num_channels++; + } + } + + if (WARN_ON(!num_channels)) + return -EINVAL; + + params_len = sizeof(struct scan_chan_list_params) + + num_channels * sizeof(struct channel_param); + params = kzalloc(params_len, GFP_KERNEL); + + if (!params) + return -ENOMEM; + + params->pdev_id = ar->pdev->pdev_id; + params->nallchans = num_channels; + + ch = params->ch_param; + + for (band = 0; band < NUM_NL80211_BANDS; band++) { + if (!bands[band]) + continue; + + for (i = 0; i < bands[band]->n_channels; i++) { + channel = &bands[band]->channels[i]; + + if (channel->flags & IEEE80211_CHAN_DISABLED) + continue; + + /* TODO: Set to true/false based on some condition? */ + ch->allow_ht = true; + ch->allow_vht = true; + + ch->dfs_set = + !!(channel->flags & IEEE80211_CHAN_RADAR); + ch->is_chan_passive = !!(channel->flags & + IEEE80211_CHAN_NO_IR); + ch->is_chan_passive |= ch->dfs_set; + ch->mhz = channel->center_freq; + ch->cfreq1 = channel->center_freq; + ch->minpower = 0; + ch->maxpower = channel->max_power * 2; + ch->maxregpower = channel->max_reg_power * 2; + ch->antennamax = channel->max_antenna_gain * 2; + + /* TODO: Use appropriate phymodes */ + if (channel->band == NL80211_BAND_2GHZ) + ch->phy_mode = MODE_11G; + else + ch->phy_mode = MODE_11A; + + ath11k_dbg(ar->ab, ATH11K_DBG_WMI, + "mac channel [%d/%d] freq %d maxpower %d regpower %d antenna %d mode %d\n", + i, params->nallchans, + ch->mhz, ch->maxpower, ch->maxregpower, + ch->antennamax, ch->phy_mode); + + ch++; + /* TODO: use quarrter/half rate, cfreq12, dfs_cfreq2 + * set_agile, reg_class_idx + */ + } + } + + ret = ath11k_wmi_send_scan_chan_list_cmd(ar, params); + kfree(params); + + return ret; +} + +static void ath11k_copy_regd(struct ieee80211_regdomain *regd_orig, + struct ieee80211_regdomain *regd_copy) +{ + u8 i; + + /* The caller should have checked error conditions */ + memcpy(regd_copy, regd_orig, sizeof(*regd_orig)); + + for (i = 0; i < regd_orig->n_reg_rules; i++) + memcpy(®d_copy->reg_rules[i], ®d_orig->reg_rules[i], + sizeof(struct ieee80211_reg_rule)); +} + +int ath11k_regd_update(struct ath11k *ar, bool init) +{ + struct ieee80211_regdomain *regd, *regd_copy = NULL; + int ret, regd_len, pdev_id; + struct ath11k_base *ab; + + ab = ar->ab; + pdev_id = ar->pdev_idx; + + spin_lock(&ab->base_lock); + + if (init) { + /* Apply the regd received during init through + * WMI_REG_CHAN_LIST_CC event. In case of failure to + * receive the regd, initialize with a default world + * regulatory. + */ + if (ab->default_regd[pdev_id]) { + regd = ab->default_regd[pdev_id]; + } else { + ath11k_warn(ab, + "failed to receive default regd during init\n"); + regd = (struct ieee80211_regdomain *)&ath11k_world_regd; + } + } else { + regd = ab->new_regd[pdev_id]; + } + + if (!regd) { + ret = -EINVAL; + spin_unlock(&ab->base_lock); + goto err; + } + + regd_len = sizeof(*regd) + (regd->n_reg_rules * + sizeof(struct ieee80211_reg_rule)); + + regd_copy = kzalloc(regd_len, GFP_ATOMIC); + if (regd_copy) + ath11k_copy_regd(regd, regd_copy); + + spin_unlock(&ab->base_lock); + + if (!regd_copy) { + ret = -ENOMEM; + goto err; + } + + rtnl_lock(); + ret = regulatory_set_wiphy_regd_sync_rtnl(ar->hw->wiphy, regd_copy); + rtnl_unlock(); + + kfree(regd_copy); + + if (ret) + goto err; + + if (ar->state == ATH11K_STATE_ON) { + ret = ath11k_reg_update_chan_list(ar); + if (ret) + goto err; + } + + return 0; +err: + ath11k_warn(ab, "failed to perform regd update : %d\n", ret); + return ret; +} + +static enum nl80211_dfs_regions +ath11k_map_fw_dfs_region(enum ath11k_dfs_region dfs_region) +{ + switch (dfs_region) { + case ATH11K_DFS_REG_FCC: + case ATH11K_DFS_REG_CN: + return NL80211_DFS_FCC; + case ATH11K_DFS_REG_ETSI: + case ATH11K_DFS_REG_KR: + return NL80211_DFS_ETSI; + case ATH11K_DFS_REG_MKK: + return NL80211_DFS_JP; + default: + return NL80211_DFS_UNSET; + } +} + +static u32 ath11k_map_fw_reg_flags(u16 reg_flags) +{ + u32 flags = 0; + + if (reg_flags & REGULATORY_CHAN_NO_IR) + flags = NL80211_RRF_NO_IR; + + if (reg_flags & REGULATORY_CHAN_RADAR) + flags |= NL80211_RRF_DFS; + + if (reg_flags & REGULATORY_CHAN_NO_OFDM) + flags |= NL80211_RRF_NO_OFDM; + + if (reg_flags & REGULATORY_CHAN_INDOOR_ONLY) + flags |= NL80211_RRF_NO_OUTDOOR; + + if (reg_flags & REGULATORY_CHAN_NO_HT40) + flags |= NL80211_RRF_NO_HT40; + + if (reg_flags & REGULATORY_CHAN_NO_80MHZ) + flags |= NL80211_RRF_NO_80MHZ; + + if (reg_flags & REGULATORY_CHAN_NO_160MHZ) + flags |= NL80211_RRF_NO_160MHZ; + + return flags; +} + +static bool +ath11k_reg_can_intersect(struct ieee80211_reg_rule *rule1, + struct ieee80211_reg_rule *rule2) +{ + u32 start_freq1, end_freq1; + u32 start_freq2, end_freq2; + + start_freq1 = rule1->freq_range.start_freq_khz; + start_freq2 = rule2->freq_range.start_freq_khz; + + end_freq1 = rule1->freq_range.end_freq_khz; + end_freq2 = rule2->freq_range.end_freq_khz; + + if ((start_freq1 >= start_freq2 && + start_freq1 < end_freq2) || + (start_freq2 > start_freq1 && + start_freq2 < end_freq1)) + return true; + + /* TODO: Should we restrict intersection feasibility + * based on min bandwidth of the intersected region also, + * say the intersected rule should have a min bandwidth + * of 20MHz? + */ + + return false; +} + +static void ath11k_reg_intersect_rules(struct ieee80211_reg_rule *rule1, + struct ieee80211_reg_rule *rule2, + struct ieee80211_reg_rule *new_rule) +{ + u32 start_freq1, end_freq1; + u32 start_freq2, end_freq2; + u32 freq_diff, max_bw; + + start_freq1 = rule1->freq_range.start_freq_khz; + start_freq2 = rule2->freq_range.start_freq_khz; + + end_freq1 = rule1->freq_range.end_freq_khz; + end_freq2 = rule2->freq_range.end_freq_khz; + + new_rule->freq_range.start_freq_khz = max_t(u32, start_freq1, + start_freq2); + new_rule->freq_range.end_freq_khz = min_t(u32, end_freq1, end_freq2); + + freq_diff = new_rule->freq_range.end_freq_khz - + new_rule->freq_range.start_freq_khz; + max_bw = min_t(u32, rule1->freq_range.max_bandwidth_khz, + rule2->freq_range.max_bandwidth_khz); + new_rule->freq_range.max_bandwidth_khz = min_t(u32, max_bw, freq_diff); + + new_rule->power_rule.max_antenna_gain = + min_t(u32, rule1->power_rule.max_antenna_gain, + rule2->power_rule.max_antenna_gain); + + new_rule->power_rule.max_eirp = min_t(u32, rule1->power_rule.max_eirp, + rule2->power_rule.max_eirp); + + /* Use the flags of both the rules */ + new_rule->flags = rule1->flags | rule2->flags; + + /* To be safe, lts use the max cac timeout of both rules */ + new_rule->dfs_cac_ms = max_t(u32, rule1->dfs_cac_ms, + rule2->dfs_cac_ms); +} + +static struct ieee80211_regdomain * +ath11k_regd_intersect(struct ieee80211_regdomain *default_regd, + struct ieee80211_regdomain *curr_regd) +{ + u8 num_old_regd_rules, num_curr_regd_rules, num_new_regd_rules; + struct ieee80211_reg_rule *old_rule, *curr_rule, *new_rule; + struct ieee80211_regdomain *new_regd = NULL; + u8 i, j, k; + + num_old_regd_rules = default_regd->n_reg_rules; + num_curr_regd_rules = curr_regd->n_reg_rules; + num_new_regd_rules = 0; + + /* Find the number of intersecting rules to allocate new regd memory */ + for (i = 0; i < num_old_regd_rules; i++) { + old_rule = default_regd->reg_rules + i; + for (j = 0; j < num_curr_regd_rules; j++) { + curr_rule = curr_regd->reg_rules + j; + + if (ath11k_reg_can_intersect(old_rule, curr_rule)) + num_new_regd_rules++; + } + } + + if (!num_new_regd_rules) + return NULL; + + new_regd = kzalloc(sizeof(*new_regd) + (num_new_regd_rules * + sizeof(struct ieee80211_reg_rule)), + GFP_ATOMIC); + + if (!new_regd) + return NULL; + + /* We set the new country and dfs region directly and only trim + * the freq, power, antenna gain by intersecting with the + * default regdomain. Also MAX of the dfs cac timeout is selected. + */ + new_regd->n_reg_rules = num_new_regd_rules; + memcpy(new_regd->alpha2, curr_regd->alpha2, sizeof(new_regd->alpha2)); + new_regd->dfs_region = curr_regd->dfs_region; + new_rule = new_regd->reg_rules; + + for (i = 0, k = 0; i < num_old_regd_rules; i++) { + old_rule = default_regd->reg_rules + i; + for (j = 0; j < num_curr_regd_rules; j++) { + curr_rule = curr_regd->reg_rules + j; + + if (ath11k_reg_can_intersect(old_rule, curr_rule)) + ath11k_reg_intersect_rules(old_rule, curr_rule, + (new_rule + k++)); + } + } + return new_regd; +} + +static const char * +ath11k_reg_get_regdom_str(enum nl80211_dfs_regions dfs_region) +{ + switch (dfs_region) { + case NL80211_DFS_FCC: + return "FCC"; + case NL80211_DFS_ETSI: + return "ETSI"; + case NL80211_DFS_JP: + return "JP"; + default: + return "UNSET"; + } +} + +static u16 +ath11k_reg_adjust_bw(u16 start_freq, u16 end_freq, u16 max_bw) +{ + u16 bw; + + bw = end_freq - start_freq; + bw = min_t(u16, bw, max_bw); + + if (bw >= 80 && bw < 160) + bw = 80; + else if (bw >= 40 && bw < 80) + bw = 40; + else if (bw < 40) + bw = 20; + + return bw; +} + +static void +ath11k_reg_update_rule(struct ieee80211_reg_rule *reg_rule, u32 start_freq, + u32 end_freq, u32 bw, u32 ant_gain, u32 reg_pwr, + u32 reg_flags) +{ + reg_rule->freq_range.start_freq_khz = MHZ_TO_KHZ(start_freq); + reg_rule->freq_range.end_freq_khz = MHZ_TO_KHZ(end_freq); + reg_rule->freq_range.max_bandwidth_khz = MHZ_TO_KHZ(bw); + reg_rule->power_rule.max_antenna_gain = DBI_TO_MBI(ant_gain); + reg_rule->power_rule.max_eirp = DBM_TO_MBM(reg_pwr); + reg_rule->flags = reg_flags; +} + +static void +ath11k_reg_update_weather_radar_band(struct ath11k_base *ab, + struct ieee80211_regdomain *regd, + struct cur_reg_rule *reg_rule, + u8 *rule_idx, u32 flags, u16 max_bw) +{ + u32 end_freq; + u16 bw; + u8 i; + + i = *rule_idx; + + bw = ath11k_reg_adjust_bw(reg_rule->start_freq, + ETSI_WEATHER_RADAR_BAND_LOW, max_bw); + + ath11k_reg_update_rule(regd->reg_rules + i, reg_rule->start_freq, + ETSI_WEATHER_RADAR_BAND_LOW, bw, + reg_rule->ant_gain, reg_rule->reg_power, + flags); + + ath11k_dbg(ab, ATH11K_DBG_REG, + "\t%d. (%d - %d @ %d) (%d, %d) (%d ms) (FLAGS %d)\n", + i + 1, reg_rule->start_freq, ETSI_WEATHER_RADAR_BAND_LOW, + bw, reg_rule->ant_gain, reg_rule->reg_power, + regd->reg_rules[i].dfs_cac_ms, + flags); + + if (reg_rule->end_freq > ETSI_WEATHER_RADAR_BAND_HIGH) + end_freq = ETSI_WEATHER_RADAR_BAND_HIGH; + else + end_freq = reg_rule->end_freq; + + bw = ath11k_reg_adjust_bw(ETSI_WEATHER_RADAR_BAND_LOW, end_freq, + max_bw); + + i++; + + ath11k_reg_update_rule(regd->reg_rules + i, + ETSI_WEATHER_RADAR_BAND_LOW, end_freq, bw, + reg_rule->ant_gain, reg_rule->reg_power, + flags); + + regd->reg_rules[i].dfs_cac_ms = ETSI_WEATHER_RADAR_BAND_CAC_TIMEOUT; + + ath11k_dbg(ab, ATH11K_DBG_REG, + "\t%d. (%d - %d @ %d) (%d, %d) (%d ms) (FLAGS %d)\n", + i + 1, ETSI_WEATHER_RADAR_BAND_LOW, end_freq, + bw, reg_rule->ant_gain, reg_rule->reg_power, + regd->reg_rules[i].dfs_cac_ms, + flags); + + if (end_freq == reg_rule->end_freq) { + regd->n_reg_rules--; + *rule_idx = i; + return; + } + + bw = ath11k_reg_adjust_bw(ETSI_WEATHER_RADAR_BAND_HIGH, + reg_rule->end_freq, max_bw); + + i++; + + ath11k_reg_update_rule(regd->reg_rules + i, ETSI_WEATHER_RADAR_BAND_HIGH, + reg_rule->end_freq, bw, + reg_rule->ant_gain, reg_rule->reg_power, + flags); + + ath11k_dbg(ab, ATH11K_DBG_REG, + "\t%d. (%d - %d @ %d) (%d, %d) (%d ms) (FLAGS %d)\n", + i + 1, ETSI_WEATHER_RADAR_BAND_HIGH, reg_rule->end_freq, + bw, reg_rule->ant_gain, reg_rule->reg_power, + regd->reg_rules[i].dfs_cac_ms, + flags); + + *rule_idx = i; +} + +struct ieee80211_regdomain * +ath11k_reg_build_regd(struct ath11k_base *ab, + struct cur_regulatory_info *reg_info, bool intersect) +{ + struct ieee80211_regdomain *tmp_regd, *default_regd, *new_regd = NULL; + struct cur_reg_rule *reg_rule; + u8 i = 0, j = 0; + u8 num_rules; + u16 max_bw; + u32 flags; + char alpha2[3]; + + num_rules = reg_info->num_5g_reg_rules + reg_info->num_2g_reg_rules; + + if (!num_rules) + goto ret; + + /* Add max additional rules to accommodate weather radar band */ + if (reg_info->dfs_region == ATH11K_DFS_REG_ETSI) + num_rules += 2; + + tmp_regd = kzalloc(sizeof(*tmp_regd) + + (num_rules * sizeof(struct ieee80211_reg_rule)), + GFP_ATOMIC); + if (!tmp_regd) + goto ret; + + tmp_regd->n_reg_rules = num_rules; + memcpy(tmp_regd->alpha2, reg_info->alpha2, REG_ALPHA2_LEN + 1); + memcpy(alpha2, reg_info->alpha2, REG_ALPHA2_LEN + 1); + alpha2[2] = '\0'; + tmp_regd->dfs_region = ath11k_map_fw_dfs_region(reg_info->dfs_region); + + ath11k_dbg(ab, ATH11K_DBG_REG, + "\r\nCountry %s, CFG Regdomain %s FW Regdomain %d, num_reg_rules %d\n", + alpha2, ath11k_reg_get_regdom_str(tmp_regd->dfs_region), + reg_info->dfs_region, num_rules); + /* Update reg_rules[] below. Firmware is expected to + * send these rules in order(2G rules first and then 5G) + */ + for (; i < tmp_regd->n_reg_rules; i++) { + if (reg_info->num_2g_reg_rules && + (i < reg_info->num_2g_reg_rules)) { + reg_rule = reg_info->reg_rules_2g_ptr + i; + max_bw = min_t(u16, reg_rule->max_bw, + reg_info->max_bw_2g); + flags = 0; + } else if (reg_info->num_5g_reg_rules && + (j < reg_info->num_5g_reg_rules)) { + reg_rule = reg_info->reg_rules_5g_ptr + j++; + max_bw = min_t(u16, reg_rule->max_bw, + reg_info->max_bw_5g); + + /* FW doesn't pass NL80211_RRF_AUTO_BW flag for + * BW Auto correction, we can enable this by default + * for all 5G rules here. The regulatory core performs + * BW correction if required and applies flags as + * per other BW rule flags we pass from here + */ + flags = NL80211_RRF_AUTO_BW; + } else { + break; + } + + flags |= ath11k_map_fw_reg_flags(reg_rule->flags); + + ath11k_reg_update_rule(tmp_regd->reg_rules + i, + reg_rule->start_freq, + reg_rule->end_freq, max_bw, + reg_rule->ant_gain, reg_rule->reg_power, + flags); + + /* Update dfs cac timeout if the dfs domain is ETSI and the + * new rule covers weather radar band. + * Default value of '0' corresponds to 60s timeout, so no + * need to update that for other rules. + */ + if (flags & NL80211_RRF_DFS && + reg_info->dfs_region == ATH11K_DFS_REG_ETSI && + (reg_rule->end_freq > ETSI_WEATHER_RADAR_BAND_LOW && + reg_rule->start_freq < ETSI_WEATHER_RADAR_BAND_HIGH)){ + ath11k_reg_update_weather_radar_band(ab, tmp_regd, + reg_rule, &i, + flags, max_bw); + continue; + } + + ath11k_dbg(ab, ATH11K_DBG_REG, + "\t%d. (%d - %d @ %d) (%d, %d) (%d ms) (FLAGS %d)\n", + i + 1, reg_rule->start_freq, reg_rule->end_freq, + max_bw, reg_rule->ant_gain, reg_rule->reg_power, + tmp_regd->reg_rules[i].dfs_cac_ms, + flags); + } + + if (intersect) { + default_regd = ab->default_regd[reg_info->phy_id]; + + /* Get a new regd by intersecting the received regd with + * our default regd. + */ + new_regd = ath11k_regd_intersect(default_regd, tmp_regd); + kfree(tmp_regd); + if (!new_regd) { + ath11k_warn(ab, "Unable to create intersected regdomain\n"); + goto ret; + } + } else { + new_regd = tmp_regd; + } + +ret: + return new_regd; +} + +void ath11k_regd_update_work(struct work_struct *work) +{ + struct ath11k *ar = container_of(work, struct ath11k, + regd_update_work); + int ret; + + ret = ath11k_regd_update(ar, false); + if (ret) { + /* Firmware has already moved to the new regd. We need + * to maintain channel consistency across FW, Host driver + * and userspace. Hence as a fallback mechanism we can set + * the prev or default country code to the firmware. + */ + /* TODO: Implement Fallback Mechanism */ + } +} + +void ath11k_reg_init(struct ath11k *ar) +{ + ar->hw->wiphy->regulatory_flags = REGULATORY_WIPHY_SELF_MANAGED; + ar->hw->wiphy->reg_notifier = ath11k_reg_notifier; +} + +void ath11k_reg_free(struct ath11k_base *ab) +{ + int i; + + for (i = 0; i < MAX_RADIOS; i++) { + kfree(ab->default_regd[i]); + kfree(ab->new_regd[i]); + } +} diff --git a/drivers/net/wireless/ath/ath11k/reg.h b/drivers/net/wireless/ath/ath11k/reg.h new file mode 100644 index 0000000000000..39b7fc9435415 --- /dev/null +++ b/drivers/net/wireless/ath/ath11k/reg.h @@ -0,0 +1,35 @@ +/* SPDX-License-Identifier: BSD-3-Clause-Clear */ +/* + * Copyright (c) 2019 The Linux Foundation. All rights reserved. + */ + +#ifndef ATH11K_REG_H +#define ATH11K_REG_H + +#include +#include + +struct ath11k_base; +struct ath11k; + +/* DFS regdomains supported by Firmware */ +enum ath11k_dfs_region { + ATH11K_DFS_REG_UNSET, + ATH11K_DFS_REG_FCC, + ATH11K_DFS_REG_ETSI, + ATH11K_DFS_REG_MKK, + ATH11K_DFS_REG_CN, + ATH11K_DFS_REG_KR, + ATH11K_DFS_REG_UNDEF, +}; + +/* ATH11K Regulatory API's */ +void ath11k_reg_init(struct ath11k *ar); +void ath11k_reg_free(struct ath11k_base *ab); +void ath11k_regd_update_work(struct work_struct *work); +struct ieee80211_regdomain * +ath11k_reg_build_regd(struct ath11k_base *ab, + struct cur_regulatory_info *reg_info, bool intersect); +int ath11k_regd_update(struct ath11k *ar, bool init); +int ath11k_reg_update_chan_list(struct ath11k *ar); +#endif diff --git a/drivers/net/wireless/ath/ath11k/rx_desc.h b/drivers/net/wireless/ath/ath11k/rx_desc.h new file mode 100644 index 0000000000000..a5aff801f17f8 --- /dev/null +++ b/drivers/net/wireless/ath/ath11k/rx_desc.h @@ -0,0 +1,1212 @@ +/* SPDX-License-Identifier: BSD-3-Clause-Clear */ +/* + * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. + */ +#ifndef ATH11K_RX_DESC_H +#define ATH11K_RX_DESC_H + +enum rx_desc_rxpcu_filter { + RX_DESC_RXPCU_FILTER_PASS, + RX_DESC_RXPCU_FILTER_MONITOR_CLIENT, + RX_DESC_RXPCU_FILTER_MONITOR_OTHER, +}; + +/* rxpcu_filter_pass + * This MPDU passed the normal frame filter programming of rxpcu. + * + * rxpcu_filter_monitor_client + * This MPDU did not pass the regular frame filter and would + * have been dropped, were it not for the frame fitting into the + * 'monitor_client' category. + * + * rxpcu_filter_monitor_other + * This MPDU did not pass the regular frame filter and also did + * not pass the rxpcu_monitor_client filter. It would have been + * dropped accept that it did pass the 'monitor_other' category. + */ + +#define RX_DESC_INFO0_RXPCU_MPDU_FITLER GENMASK(1, 0) +#define RX_DESC_INFO0_SW_FRAME_GRP_ID GENMASK(8, 2) + +enum rx_desc_sw_frame_grp_id { + RX_DESC_SW_FRAME_GRP_ID_NDP_FRAME, + RX_DESC_SW_FRAME_GRP_ID_MCAST_DATA, + RX_DESC_SW_FRAME_GRP_ID_UCAST_DATA, + RX_DESC_SW_FRAME_GRP_ID_NULL_DATA, + RX_DESC_SW_FRAME_GRP_ID_MGMT_0000, + RX_DESC_SW_FRAME_GRP_ID_MGMT_0001, + RX_DESC_SW_FRAME_GRP_ID_MGMT_0010, + RX_DESC_SW_FRAME_GRP_ID_MGMT_0011, + RX_DESC_SW_FRAME_GRP_ID_MGMT_0100, + RX_DESC_SW_FRAME_GRP_ID_MGMT_0101, + RX_DESC_SW_FRAME_GRP_ID_MGMT_0110, + RX_DESC_SW_FRAME_GRP_ID_MGMT_0111, + RX_DESC_SW_FRAME_GRP_ID_MGMT_1000, + RX_DESC_SW_FRAME_GRP_ID_MGMT_1001, + RX_DESC_SW_FRAME_GRP_ID_MGMT_1010, + RX_DESC_SW_FRAME_GRP_ID_MGMT_1011, + RX_DESC_SW_FRAME_GRP_ID_MGMT_1100, + RX_DESC_SW_FRAME_GRP_ID_MGMT_1101, + RX_DESC_SW_FRAME_GRP_ID_MGMT_1110, + RX_DESC_SW_FRAME_GRP_ID_MGMT_1111, + RX_DESC_SW_FRAME_GRP_ID_CTRL_0000, + RX_DESC_SW_FRAME_GRP_ID_CTRL_0001, + RX_DESC_SW_FRAME_GRP_ID_CTRL_0010, + RX_DESC_SW_FRAME_GRP_ID_CTRL_0011, + RX_DESC_SW_FRAME_GRP_ID_CTRL_0100, + RX_DESC_SW_FRAME_GRP_ID_CTRL_0101, + RX_DESC_SW_FRAME_GRP_ID_CTRL_0110, + RX_DESC_SW_FRAME_GRP_ID_CTRL_0111, + RX_DESC_SW_FRAME_GRP_ID_CTRL_1000, + RX_DESC_SW_FRAME_GRP_ID_CTRL_1001, + RX_DESC_SW_FRAME_GRP_ID_CTRL_1010, + RX_DESC_SW_FRAME_GRP_ID_CTRL_1011, + RX_DESC_SW_FRAME_GRP_ID_CTRL_1100, + RX_DESC_SW_FRAME_GRP_ID_CTRL_1101, + RX_DESC_SW_FRAME_GRP_ID_CTRL_1110, + RX_DESC_SW_FRAME_GRP_ID_CTRL_1111, + RX_DESC_SW_FRAME_GRP_ID_UNSUPPORTED, + RX_DESC_SW_FRAME_GRP_ID_PHY_ERR, +}; + +enum rx_desc_decap_type { + RX_DESC_DECAP_TYPE_RAW, + RX_DESC_DECAP_TYPE_NATIVE_WIFI, + RX_DESC_DECAP_TYPE_ETHERNET2_DIX, + RX_DESC_DECAP_TYPE_8023, +}; + +enum rx_desc_decrypt_status_code { + RX_DESC_DECRYPT_STATUS_CODE_OK, + RX_DESC_DECRYPT_STATUS_CODE_UNPROTECTED_FRAME, + RX_DESC_DECRYPT_STATUS_CODE_DATA_ERR, + RX_DESC_DECRYPT_STATUS_CODE_KEY_INVALID, + RX_DESC_DECRYPT_STATUS_CODE_PEER_ENTRY_INVALID, + RX_DESC_DECRYPT_STATUS_CODE_OTHER, +}; + +#define RX_ATTENTION_INFO1_FIRST_MPDU BIT(0) +#define RX_ATTENTION_INFO1_RSVD_1A BIT(1) +#define RX_ATTENTION_INFO1_MCAST_BCAST BIT(2) +#define RX_ATTENTION_INFO1_AST_IDX_NOT_FOUND BIT(3) +#define RX_ATTENTION_INFO1_AST_IDX_TIMEDOUT BIT(4) +#define RX_ATTENTION_INFO1_POWER_MGMT BIT(5) +#define RX_ATTENTION_INFO1_NON_QOS BIT(6) +#define RX_ATTENTION_INFO1_NULL_DATA BIT(7) +#define RX_ATTENTION_INFO1_MGMT_TYPE BIT(8) +#define RX_ATTENTION_INFO1_CTRL_TYPE BIT(9) +#define RX_ATTENTION_INFO1_MORE_DATA BIT(10) +#define RX_ATTENTION_INFO1_EOSP BIT(11) +#define RX_ATTENTION_INFO1_A_MSDU_ERROR BIT(12) +#define RX_ATTENTION_INFO1_FRAGMENT BIT(13) +#define RX_ATTENTION_INFO1_ORDER BIT(14) +#define RX_ATTENTION_INFO1_CCE_MATCH BIT(15) +#define RX_ATTENTION_INFO1_OVERFLOW_ERR BIT(16) +#define RX_ATTENTION_INFO1_MSDU_LEN_ERR BIT(17) +#define RX_ATTENTION_INFO1_TCP_UDP_CKSUM_FAIL BIT(18) +#define RX_ATTENTION_INFO1_IP_CKSUM_FAIL BIT(19) +#define RX_ATTENTION_INFO1_SA_IDX_INVALID BIT(20) +#define RX_ATTENTION_INFO1_DA_IDX_INVALID BIT(21) +#define RX_ATTENTION_INFO1_RSVD_1B BIT(22) +#define RX_ATTENTION_INFO1_RX_IN_TX_DECRYPT_BYP BIT(23) +#define RX_ATTENTION_INFO1_ENCRYPT_REQUIRED BIT(24) +#define RX_ATTENTION_INFO1_DIRECTED BIT(25) +#define RX_ATTENTION_INFO1_BUFFER_FRAGMENT BIT(26) +#define RX_ATTENTION_INFO1_MPDU_LEN_ERR BIT(27) +#define RX_ATTENTION_INFO1_TKIP_MIC_ERR BIT(28) +#define RX_ATTENTION_INFO1_DECRYPT_ERR BIT(29) +#define RX_ATTENTION_INFO1_UNDECRYPT_FRAME_ERR BIT(30) +#define RX_ATTENTION_INFO1_FCS_ERR BIT(31) + +#define RX_ATTENTION_INFO2_FLOW_IDX_TIMEOUT BIT(0) +#define RX_ATTENTION_INFO2_FLOW_IDX_INVALID BIT(1) +#define RX_ATTENTION_INFO2_WIFI_PARSER_ERR BIT(2) +#define RX_ATTENTION_INFO2_AMSDU_PARSER_ERR BIT(3) +#define RX_ATTENTION_INFO2_SA_IDX_TIMEOUT BIT(4) +#define RX_ATTENTION_INFO2_DA_IDX_TIMEOUT BIT(5) +#define RX_ATTENTION_INFO2_MSDU_LIMIT_ERR BIT(6) +#define RX_ATTENTION_INFO2_DA_IS_VALID BIT(7) +#define RX_ATTENTION_INFO2_DA_IS_MCBC BIT(8) +#define RX_ATTENTION_INFO2_SA_IS_VALID BIT(9) +#define RX_ATTENTION_INFO2_DCRYPT_STATUS_CODE GENMASK(12, 10) +#define RX_ATTENTION_INFO2_RX_BITMAP_NOT_UPDED BIT(13) +#define RX_ATTENTION_INFO2_MSDU_DONE BIT(31) + +struct rx_attention { + __le16 info0; + __le16 phy_ppdu_id; + __le32 info1; + __le32 info2; +} __packed; + +/* rx_attention + * + * rxpcu_mpdu_filter_in_category + * Field indicates what the reason was that this mpdu frame + * was allowed to come into the receive path by rxpcu. Values + * are defined in enum %RX_DESC_RXPCU_FILTER_*. + * + * sw_frame_group_id + * SW processes frames based on certain classifications. Values + * are defined in enum %RX_DESC_SW_FRAME_GRP_ID_*. + * + * phy_ppdu_id + * A ppdu counter value that PHY increments for every PPDU + * received. The counter value wraps around. + * + * first_mpdu + * Indicates the first MSDU of the PPDU. If both first_mpdu + * and last_mpdu are set in the MSDU then this is a not an + * A-MPDU frame but a stand alone MPDU. Interior MPDU in an + * A-MPDU shall have both first_mpdu and last_mpdu bits set to + * 0. The PPDU start status will only be valid when this bit + * is set. + * + * mcast_bcast + * Multicast / broadcast indicator. Only set when the MAC + * address 1 bit 0 is set indicating mcast/bcast and the BSSID + * matches one of the 4 BSSID registers. Only set when + * first_msdu is set. + * + * ast_index_not_found + * Only valid when first_msdu is set. Indicates no AST matching + * entries within the the max search count. + * + * ast_index_timeout + * Only valid when first_msdu is set. Indicates an unsuccessful + * search in the address search table due to timeout. + * + * power_mgmt + * Power management bit set in the 802.11 header. Only set + * when first_msdu is set. + * + * non_qos + * Set if packet is not a non-QoS data frame. Only set when + * first_msdu is set. + * + * null_data + * Set if frame type indicates either null data or QoS null + * data format. Only set when first_msdu is set. + * + * mgmt_type + * Set if packet is a management packet. Only set when + * first_msdu is set. + * + * ctrl_type + * Set if packet is a control packet. Only set when first_msdu + * is set. + * + * more_data + * Set if more bit in frame control is set. Only set when + * first_msdu is set. + * + * eosp + * Set if the EOSP (end of service period) bit in the QoS + * control field is set. Only set when first_msdu is set. + * + * a_msdu_error + * Set if number of MSDUs in A-MSDU is above a threshold or if the + * size of the MSDU is invalid. This receive buffer will contain + * all of the remainder of MSDUs in this MPDU w/o decapsulation. + * + * fragment + * Indicates that this is an 802.11 fragment frame. This is + * set when either the more_frag bit is set in the frame + * control or the fragment number is not zero. Only set when + * first_msdu is set. + * + * order + * Set if the order bit in the frame control is set. Only set + * when first_msdu is set. + * + * cce_match + * Indicates that this status has a corresponding MSDU that + * requires FW processing. The OLE will have classification + * ring mask registers which will indicate the ring(s) for + * packets and descriptors which need FW attention. + * + * overflow_err + * PCU Receive FIFO does not have enough space to store the + * full receive packet. Enough space is reserved in the + * receive FIFO for the status is written. This MPDU remaining + * packets in the PPDU will be filtered and no Ack response + * will be transmitted. + * + * msdu_length_err + * Indicates that the MSDU length from the 802.3 encapsulated + * length field extends beyond the MPDU boundary. + * + * tcp_udp_chksum_fail + * Indicates that the computed checksum (tcp_udp_chksum) did + * not match the checksum in the TCP/UDP header. + * + * ip_chksum_fail + * Indicates that the computed checksum did not match the + * checksum in the IP header. + * + * sa_idx_invalid + * Indicates no matching entry was found in the address search + * table for the source MAC address. + * + * da_idx_invalid + * Indicates no matching entry was found in the address search + * table for the destination MAC address. + * + * rx_in_tx_decrypt_byp + * Indicates that RX packet is not decrypted as Crypto is busy + * with TX packet processing. + * + * encrypt_required + * Indicates that this data type frame is not encrypted even if + * the policy for this MPDU requires encryption as indicated in + * the peer table key type. + * + * directed + * MPDU is a directed packet which means that the RA matched + * our STA addresses. In proxySTA it means that the TA matched + * an entry in our address search table with the corresponding + * 'no_ack' bit is the address search entry cleared. + * + * buffer_fragment + * Indicates that at least one of the rx buffers has been + * fragmented. If set the FW should look at the rx_frag_info + * descriptor described below. + * + * mpdu_length_err + * Indicates that the MPDU was pre-maturely terminated + * resulting in a truncated MPDU. Don't trust the MPDU length + * field. + * + * tkip_mic_err + * Indicates that the MPDU Michael integrity check failed + * + * decrypt_err + * Indicates that the MPDU decrypt integrity check failed + * + * fcs_err + * Indicates that the MPDU FCS check failed + * + * flow_idx_timeout + * Indicates an unsuccessful flow search due to the expiring of + * the search timer. + * + * flow_idx_invalid + * flow id is not valid. + * + * amsdu_parser_error + * A-MSDU could not be properly de-agregated. + * + * sa_idx_timeout + * Indicates an unsuccessful search for the source MAC address + * due to the expiring of the search timer. + * + * da_idx_timeout + * Indicates an unsuccessful search for the destination MAC + * address due to the expiring of the search timer. + * + * msdu_limit_error + * Indicates that the MSDU threshold was exceeded and thus + * all the rest of the MSDUs will not be scattered and will not + * be decasulated but will be DMA'ed in RAW format as a single + * MSDU buffer. + * + * da_is_valid + * Indicates that OLE found a valid DA entry. + * + * da_is_mcbc + * Field Only valid if da_is_valid is set. Indicates the DA address + * was a Multicast or Broadcast address. + * + * sa_is_valid + * Indicates that OLE found a valid SA entry. + * + * decrypt_status_code + * Field provides insight into the decryption performed. Values are + * defined in enum %RX_DESC_DECRYPT_STATUS_CODE*. + * + * rx_bitmap_not_updated + * Frame is received, but RXPCU could not update the receive bitmap + * due to (temporary) fifo constraints. + * + * msdu_done + * If set indicates that the RX packet data, RX header data, RX + * PPDU start descriptor, RX MPDU start/end descriptor, RX MSDU + * start/end descriptors and RX Attention descriptor are all + * valid. This bit must be in the last octet of the + * descriptor. + */ + +#define RX_MPDU_START_INFO0_NDP_FRAME BIT(9) +#define RX_MPDU_START_INFO0_PHY_ERR BIT(10) +#define RX_MPDU_START_INFO0_PHY_ERR_MPDU_HDR BIT(11) +#define RX_MPDU_START_INFO0_PROTO_VER_ERR BIT(12) +#define RX_MPDU_START_INFO0_AST_LOOKUP_VALID BIT(13) + +#define RX_MPDU_START_INFO1_MPDU_CTRL_VALID BIT(0) +#define RX_MPDU_START_INFO1_MPDU_DUR_VALID BIT(1) +#define RX_MPDU_START_INFO1_MAC_ADDR1_VALID BIT(2) +#define RX_MPDU_START_INFO1_MAC_ADDR2_VALID BIT(3) +#define RX_MPDU_START_INFO1_MAC_ADDR3_VALID BIT(4) +#define RX_MPDU_START_INFO1_MAC_ADDR4_VALID BIT(5) +#define RX_MPDU_START_INFO1_MPDU_SEQ_CTRL_VALID BIT(6) +#define RX_MPDU_START_INFO1_MPDU_QOS_CTRL_VALID BIT(7) +#define RX_MPDU_START_INFO1_MPDU_HT_CTRL_VALID BIT(8) +#define RX_MPDU_START_INFO1_ENCRYPT_INFO_VALID BIT(9) +#define RX_MPDU_START_INFO1_MPDU_FRAG_NUMBER GENMASK(13, 10) +#define RX_MPDU_START_INFO1_MORE_FRAG_FLAG BIT(14) +#define RX_MPDU_START_INFO1_FROM_DS BIT(16) +#define RX_MPDU_START_INFO1_TO_DS BIT(17) +#define RX_MPDU_START_INFO1_ENCRYPTED BIT(18) +#define RX_MPDU_START_INFO1_MPDU_RETRY BIT(19) +#define RX_MPDU_START_INFO1_MPDU_SEQ_NUM GENMASK(31, 20) + +#define RX_MPDU_START_INFO2_EPD_EN BIT(0) +#define RX_MPDU_START_INFO2_ALL_FRAME_ENCPD BIT(1) +#define RX_MPDU_START_INFO2_ENC_TYPE GENMASK(5, 2) +#define RX_MPDU_START_INFO2_VAR_WEP_KEY_WIDTH GENMASK(7, 6) +#define RX_MPDU_START_INFO2_MESH_STA BIT(8) +#define RX_MPDU_START_INFO2_BSSID_HIT BIT(9) +#define RX_MPDU_START_INFO2_BSSID_NUM GENMASK(13, 10) +#define RX_MPDU_START_INFO2_TID GENMASK(17, 14) + +#define RX_MPDU_START_INFO3_REO_DEST_IND GENMASK(4, 0) +#define RX_MPDU_START_INFO3_FLOW_ID_TOEPLITZ BIT(7) +#define RX_MPDU_START_INFO3_PKT_SEL_FP_UCAST_DATA BIT(8) +#define RX_MPDU_START_INFO3_PKT_SEL_FP_MCAST_DATA BIT(9) +#define RX_MPDU_START_INFO3_PKT_SEL_FP_CTRL_BAR BIT(10) +#define RX_MPDU_START_INFO3_RXDMA0_SRC_RING_SEL GENMASK(12, 11) +#define RX_MPDU_START_INFO3_RXDMA0_DST_RING_SEL GENMASK(14, 13) + +#define RX_MPDU_START_INFO4_REO_QUEUE_DESC_HI GENMASK(7, 0) +#define RX_MPDU_START_INFO4_RECV_QUEUE_NUM GENMASK(23, 8) +#define RX_MPDU_START_INFO4_PRE_DELIM_ERR_WARN BIT(24) +#define RX_MPDU_START_INFO4_FIRST_DELIM_ERR BIT(25) + +#define RX_MPDU_START_INFO5_KEY_ID GENMASK(7, 0) +#define RX_MPDU_START_INFO5_NEW_PEER_ENTRY BIT(8) +#define RX_MPDU_START_INFO5_DECRYPT_NEEDED BIT(9) +#define RX_MPDU_START_INFO5_DECAP_TYPE GENMASK(11, 10) +#define RX_MPDU_START_INFO5_VLAN_TAG_C_PADDING BIT(12) +#define RX_MPDU_START_INFO5_VLAN_TAG_S_PADDING BIT(13) +#define RX_MPDU_START_INFO5_STRIP_VLAN_TAG_C BIT(14) +#define RX_MPDU_START_INFO5_STRIP_VLAN_TAG_S BIT(15) +#define RX_MPDU_START_INFO5_PRE_DELIM_COUNT GENMASK(27, 16) +#define RX_MPDU_START_INFO5_AMPDU_FLAG BIT(28) +#define RX_MPDU_START_INFO5_BAR_FRAME BIT(29) + +#define RX_MPDU_START_INFO6_MPDU_LEN GENMASK(13, 0) +#define RX_MPDU_START_INFO6_FIRST_MPDU BIT(14) +#define RX_MPDU_START_INFO6_MCAST_BCAST BIT(15) +#define RX_MPDU_START_INFO6_AST_IDX_NOT_FOUND BIT(16) +#define RX_MPDU_START_INFO6_AST_IDX_TIMEOUT BIT(17) +#define RX_MPDU_START_INFO6_POWER_MGMT BIT(18) +#define RX_MPDU_START_INFO6_NON_QOS BIT(19) +#define RX_MPDU_START_INFO6_NULL_DATA BIT(20) +#define RX_MPDU_START_INFO6_MGMT_TYPE BIT(21) +#define RX_MPDU_START_INFO6_CTRL_TYPE BIT(22) +#define RX_MPDU_START_INFO6_MORE_DATA BIT(23) +#define RX_MPDU_START_INFO6_EOSP BIT(24) +#define RX_MPDU_START_INFO6_FRAGMENT BIT(25) +#define RX_MPDU_START_INFO6_ORDER BIT(26) +#define RX_MPDU_START_INFO6_UAPSD_TRIGGER BIT(27) +#define RX_MPDU_START_INFO6_ENCRYPT_REQUIRED BIT(28) +#define RX_MPDU_START_INFO6_DIRECTED BIT(29) + +#define RX_MPDU_START_RAW_MPDU BIT(0) + +struct rx_mpdu_start { + __le16 info0; + __le16 phy_ppdu_id; + __le16 ast_index; + __le16 sw_peer_id; + __le32 info1; + __le32 info2; + __le32 pn[4]; + __le32 peer_meta_data; + __le32 info3; + __le32 reo_queue_desc_lo; + __le32 info4; + __le32 info5; + __le32 info6; + __le16 frame_ctrl; + __le16 duration; + u8 addr1[ETH_ALEN]; + u8 addr2[ETH_ALEN]; + u8 addr3[ETH_ALEN]; + __le16 seq_ctrl; + u8 addr4[ETH_ALEN]; + __le16 qos_ctrl; + __le32 ht_ctrl; + __le32 raw; +} __packed; + +/* rx_mpdu_start + * + * rxpcu_mpdu_filter_in_category + * Field indicates what the reason was that this mpdu frame + * was allowed to come into the receive path by rxpcu. Values + * are defined in enum %RX_DESC_RXPCU_FILTER_*. + * Note: for ndp frame, if it was expected because the preceding + * NDPA was filter_pass, the setting rxpcu_filter_pass will be + * used. This setting will also be used for every ndp frame in + * case Promiscuous mode is enabled. + * + * sw_frame_group_id + * SW processes frames based on certain classifications. Values + * are defined in enum %RX_DESC_SW_FRAME_GRP_ID_*. + * + * ndp_frame + * Indicates that the received frame was an NDP frame. + * + * phy_err + * Indicates that PHY error was received before MAC received data. + * + * phy_err_during_mpdu_header + * PHY error was received before MAC received the complete MPDU + * header which was needed for proper decoding. + * + * protocol_version_err + * RXPCU detected a version error in the frame control field. + * + * ast_based_lookup_valid + * AST based lookup for this frame has found a valid result. + * + * phy_ppdu_id + * A ppdu counter value that PHY increments for every PPDU + * received. The counter value wraps around. + * + * ast_index + * This field indicates the index of the AST entry corresponding + * to this MPDU. It is provided by the GSE module instantiated in + * RXPCU. A value of 0xFFFF indicates an invalid AST index. + * + * sw_peer_id + * This field indicates a unique peer identifier. It is set equal + * to field 'sw_peer_id' from the AST entry. + * + * mpdu_frame_control_valid, mpdu_duration_valid, mpdu_qos_control_valid, + * mpdu_ht_control_valid, frame_encryption_info_valid + * Indicates that each fields have valid entries. + * + * mac_addr_adx_valid + * Corresponding mac_addr_adx_{lo/hi} has valid entries. + * + * from_ds, to_ds + * Valid only when mpdu_frame_control_valid is set. Indicates that + * frame is received from DS and sent to DS. + * + * encrypted + * Protected bit from the frame control. + * + * mpdu_retry + * Retry bit from frame control. Only valid when first_msdu is set. + * + * mpdu_sequence_number + * The sequence number from the 802.11 header. + * + * epd_en + * If set, use EPD instead of LPD. + * + * all_frames_shall_be_encrypted + * If set, all frames (data only?) shall be encrypted. If not, + * RX CRYPTO shall set an error flag. + * + * encrypt_type + * Values are defined in enum %HAL_ENCRYPT_TYPE_. + * + * mesh_sta + * Indicates a Mesh (11s) STA. + * + * bssid_hit + * BSSID of the incoming frame matched one of the 8 BSSID + * register values. + * + * bssid_number + * This number indicates which one out of the 8 BSSID register + * values matched the incoming frame. + * + * tid + * TID field in the QoS control field + * + * pn + * The PN number. + * + * peer_meta_data + * Meta data that SW has programmed in the Peer table entry + * of the transmitting STA. + * + * rx_reo_queue_desc_addr_lo + * Address (lower 32 bits) of the REO queue descriptor. + * + * rx_reo_queue_desc_addr_hi + * Address (upper 8 bits) of the REO queue descriptor. + * + * receive_queue_number + * Indicates the MPDU queue ID to which this MPDU link + * descriptor belongs. + * + * pre_delim_err_warning + * Indicates that a delimiter FCS error was found in between the + * previous MPDU and this MPDU. Note that this is just a warning, + * and does not mean that this MPDU is corrupted in any way. If + * it is, there will be other errors indicated such as FCS or + * decrypt errors. + * + * first_delim_err + * Indicates that the first delimiter had a FCS failure. + * + * key_id + * The key ID octet from the IV. + * + * new_peer_entry + * Set if new RX_PEER_ENTRY TLV follows. If clear, RX_PEER_ENTRY + * doesn't follow so RX DECRYPTION module either uses old peer + * entry or not decrypt. + * + * decrypt_needed + * When RXPCU sets bit 'ast_index_not_found or ast_index_timeout', + * RXPCU will also ensure that this bit is NOT set. CRYPTO for that + * reason only needs to evaluate this bit and non of the other ones + * + * decap_type + * Used by the OLE during decapsulation. Values are defined in + * enum %MPDU_START_DECAP_TYPE_*. + * + * rx_insert_vlan_c_tag_padding + * rx_insert_vlan_s_tag_padding + * Insert 4 byte of all zeros as VLAN tag or double VLAN tag if + * the rx payload does not have VLAN. + * + * strip_vlan_c_tag_decap + * strip_vlan_s_tag_decap + * Strip VLAN or double VLAN during decapsulation. + * + * pre_delim_count + * The number of delimiters before this MPDU. Note that this + * number is cleared at PPDU start. If this MPDU is the first + * received MPDU in the PPDU and this MPDU gets filtered-in, + * this field will indicate the number of delimiters located + * after the last MPDU in the previous PPDU. + * + * If this MPDU is located after the first received MPDU in + * an PPDU, this field will indicate the number of delimiters + * located between the previous MPDU and this MPDU. + * + * ampdu_flag + * Received frame was part of an A-MPDU. + * + * bar_frame + * Received frame is a BAR frame + * + * mpdu_length + * MPDU length before decapsulation. + * + * first_mpdu..directed + * See definition in RX attention descriptor + * + */ + +enum rx_msdu_start_pkt_type { + RX_MSDU_START_PKT_TYPE_11A, + RX_MSDU_START_PKT_TYPE_11B, + RX_MSDU_START_PKT_TYPE_11N, + RX_MSDU_START_PKT_TYPE_11AC, + RX_MSDU_START_PKT_TYPE_11AX, +}; + +enum rx_msdu_start_sgi { + RX_MSDU_START_SGI_0_8_US, + RX_MSDU_START_SGI_0_4_US, + RX_MSDU_START_SGI_1_6_US, + RX_MSDU_START_SGI_3_2_US, +}; + +enum rx_msdu_start_recv_bw { + RX_MSDU_START_RECV_BW_20MHZ, + RX_MSDU_START_RECV_BW_40MHZ, + RX_MSDU_START_RECV_BW_80MHZ, + RX_MSDU_START_RECV_BW_160MHZ, +}; + +enum rx_msdu_start_reception_type { + RX_MSDU_START_RECEPTION_TYPE_SU, + RX_MSDU_START_RECEPTION_TYPE_DL_MU_MIMO, + RX_MSDU_START_RECEPTION_TYPE_DL_MU_OFDMA, + RX_MSDU_START_RECEPTION_TYPE_DL_MU_OFDMA_MIMO, + RX_MSDU_START_RECEPTION_TYPE_UL_MU_MIMO, + RX_MSDU_START_RECEPTION_TYPE_UL_MU_OFDMA, + RX_MSDU_START_RECEPTION_TYPE_UL_MU_OFDMA_MIMO, +}; + +#define RX_MSDU_START_INFO1_MSDU_LENGTH GENMASK(13, 0) +#define RX_MSDU_START_INFO1_RSVD_1A BIT(14) +#define RX_MSDU_START_INFO1_IPSEC_ESP BIT(15) +#define RX_MSDU_START_INFO1_L3_OFFSET GENMASK(22, 16) +#define RX_MSDU_START_INFO1_IPSEC_AH BIT(23) +#define RX_MSDU_START_INFO1_L4_OFFSET GENMASK(31, 24) + +#define RX_MSDU_START_INFO2_MSDU_NUMBER GENMASK(7, 0) +#define RX_MSDU_START_INFO2_DECAP_TYPE GENMASK(9, 8) +#define RX_MSDU_START_INFO2_IPV4 BIT(10) +#define RX_MSDU_START_INFO2_IPV6 BIT(11) +#define RX_MSDU_START_INFO2_TCP BIT(12) +#define RX_MSDU_START_INFO2_UDP BIT(13) +#define RX_MSDU_START_INFO2_IP_FRAG BIT(14) +#define RX_MSDU_START_INFO2_TCP_ONLY_ACK BIT(15) +#define RX_MSDU_START_INFO2_DA_IS_BCAST_MCAST BIT(16) +#define RX_MSDU_START_INFO2_SELECTED_TOEPLITZ_HASH GENMASK(18, 17) +#define RX_MSDU_START_INFO2_IP_FIXED_HDR_VALID BIT(19) +#define RX_MSDU_START_INFO2_IP_EXTN_HDR_VALID BIT(20) +#define RX_MSDU_START_INFO2_IP_TCP_UDP_HDR_VALID BIT(21) +#define RX_MSDU_START_INFO2_MESH_CTRL_PRESENT BIT(22) +#define RX_MSDU_START_INFO2_LDPC BIT(23) +#define RX_MSDU_START_INFO2_IP4_IP6_NXT_HDR GENMASK(31, 24) +#define RX_MSDU_START_INFO2_DECAP_FORMAT GENMASK(9, 8) + +#define RX_MSDU_START_INFO3_USER_RSSI GENMASK(7, 0) +#define RX_MSDU_START_INFO3_PKT_TYPE GENMASK(11, 8) +#define RX_MSDU_START_INFO3_STBC BIT(12) +#define RX_MSDU_START_INFO3_SGI GENMASK(14, 13) +#define RX_MSDU_START_INFO3_RATE_MCS GENMASK(18, 15) +#define RX_MSDU_START_INFO3_RECV_BW GENMASK(20, 19) +#define RX_MSDU_START_INFO3_RECEPTION_TYPE GENMASK(23, 21) +#define RX_MSDU_START_INFO3_MIMO_SS_BITMAP GENMASK(31, 24) + +struct rx_msdu_start { + __le16 info0; + __le16 phy_ppdu_id; + __le32 info1; + __le32 info2; + __le32 toeplitz_hash; + __le32 flow_id_toeplitz; + __le32 info3; + __le32 ppdu_start_timestamp; + __le32 phy_meta_data; +} __packed; + +/* rx_msdu_start + * + * rxpcu_mpdu_filter_in_category + * Field indicates what the reason was that this mpdu frame + * was allowed to come into the receive path by rxpcu. Values + * are defined in enum %RX_DESC_RXPCU_FILTER_*. + * + * sw_frame_group_id + * SW processes frames based on certain classifications. Values + * are defined in enum %RX_DESC_SW_FRAME_GRP_ID_*. + * + * phy_ppdu_id + * A ppdu counter value that PHY increments for every PPDU + * received. The counter value wraps around. + * + * msdu_length + * MSDU length in bytes after decapsulation. + * + * ipsec_esp + * Set if IPv4/v6 packet is using IPsec ESP. + * + * l3_offset + * Depending upon mode bit, this field either indicates the + * L3 offset in bytes from the start of the RX_HEADER or the IP + * offset in bytes from the start of the packet after + * decapsulation. The latter is only valid if ipv4_proto or + * ipv6_proto is set. + * + * ipsec_ah + * Set if IPv4/v6 packet is using IPsec AH + * + * l4_offset + * Depending upon mode bit, this field either indicates the + * L4 offset nin bytes from the start of RX_HEADER (only valid + * if either ipv4_proto or ipv6_proto is set to 1) or indicates + * the offset in bytes to the start of TCP or UDP header from + * the start of the IP header after decapsulation (Only valid if + * tcp_proto or udp_proto is set). The value 0 indicates that + * the offset is longer than 127 bytes. + * + * msdu_number + * Indicates the MSDU number within a MPDU. This value is + * reset to zero at the start of each MPDU. If the number of + * MSDU exceeds 255 this number will wrap using modulo 256. + * + * decap_type + * Indicates the format after decapsulation. Values are defined in + * enum %MPDU_START_DECAP_TYPE_*. + * + * ipv4_proto + * Set if L2 layer indicates IPv4 protocol. + * + * ipv6_proto + * Set if L2 layer indicates IPv6 protocol. + * + * tcp_proto + * Set if the ipv4_proto or ipv6_proto are set and the IP protocol + * indicates TCP. + * + * udp_proto + * Set if the ipv4_proto or ipv6_proto are set and the IP protocol + * indicates UDP. + * + * ip_frag + * Indicates that either the IP More frag bit is set or IP frag + * number is non-zero. If set indicates that this is a fragmented + * IP packet. + * + * tcp_only_ack + * Set if only the TCP Ack bit is set in the TCP flags and if + * the TCP payload is 0. + * + * da_is_bcast_mcast + * The destination address is broadcast or multicast. + * + * toeplitz_hash + * Actual chosen Hash. + * 0 - Toeplitz hash of 2-tuple (IP source address, IP + * destination address) + * 1 - Toeplitz hash of 4-tuple (IP source address, + * IP destination address, L4 (TCP/UDP) source port, + * L4 (TCP/UDP) destination port) + * 2 - Toeplitz of flow_id + * 3 - Zero is used + * + * ip_fixed_header_valid + * Fixed 20-byte IPv4 header or 40-byte IPv6 header parsed + * fully within first 256 bytes of the packet + * + * ip_extn_header_valid + * IPv6/IPv6 header, including IPv4 options and + * recognizable extension headers parsed fully within first 256 + * bytes of the packet + * + * tcp_udp_header_valid + * Fixed 20-byte TCP (excluding TCP options) or 8-byte UDP + * header parsed fully within first 256 bytes of the packet + * + * mesh_control_present + * When set, this MSDU includes the 'Mesh Control' field + * + * ldpc + * + * ip4_protocol_ip6_next_header + * For IPv4, this is the 8 bit protocol field set). For IPv6 this + * is the 8 bit next_header field. + * + * toeplitz_hash_2_or_4 + * Controlled by RxOLE register - If register bit set to 0, + * Toeplitz hash is computed over 2-tuple IPv4 or IPv6 src/dest + * addresses; otherwise, toeplitz hash is computed over 4-tuple + * IPv4 or IPv6 src/dest addresses and src/dest ports. + * + * flow_id_toeplitz + * Toeplitz hash of 5-tuple + * {IP source address, IP destination address, IP source port, IP + * destination port, L4 protocol} in case of non-IPSec. + * + * In case of IPSec - Toeplitz hash of 4-tuple + * {IP source address, IP destination address, SPI, L4 protocol} + * + * The relevant Toeplitz key registers are provided in RxOLE's + * instance of common parser module. These registers are separate + * from the Toeplitz keys used by ASE/FSE modules inside RxOLE. + * The actual value will be passed on from common parser module + * to RxOLE in one of the WHO_* TLVs. + * + * user_rssi + * RSSI for this user + * + * pkt_type + * Values are defined in enum %RX_MSDU_START_PKT_TYPE_*. + * + * stbc + * When set, use STBC transmission rates. + * + * sgi + * Field only valid when pkt type is HT, VHT or HE. Values are + * defined in enum %RX_MSDU_START_SGI_*. + * + * rate_mcs + * MCS Rate used. + * + * receive_bandwidth + * Full receive Bandwidth. Values are defined in enum + * %RX_MSDU_START_RECV_*. + * + * reception_type + * Indicates what type of reception this is and defined in enum + * %RX_MSDU_START_RECEPTION_TYPE_*. + * + * mimo_ss_bitmap + * Field only valid when + * Reception_type is RX_MSDU_START_RECEPTION_TYPE_DL_MU_MIMO or + * RX_MSDU_START_RECEPTION_TYPE_DL_MU_OFDMA_MIMO. + * + * Bitmap, with each bit indicating if the related spatial + * stream is used for this STA + * + * LSB related to SS 0 + * + * 0 - spatial stream not used for this reception + * 1 - spatial stream used for this reception + * + * ppdu_start_timestamp + * Timestamp that indicates when the PPDU that contained this MPDU + * started on the medium. + * + * phy_meta_data + * SW programmed Meta data provided by the PHY. Can be used for SW + * to indicate the channel the device is on. + */ + +#define RX_MSDU_END_INFO0_RXPCU_MPDU_FITLER GENMASK(1, 0) +#define RX_MSDU_END_INFO0_SW_FRAME_GRP_ID GENMASK(8, 2) + +#define RX_MSDU_END_INFO1_KEY_ID GENMASK(7, 0) +#define RX_MSDU_END_INFO1_CCE_SUPER_RULE GENMASK(13, 8) +#define RX_MSDU_END_INFO1_CCND_TRUNCATE BIT(14) +#define RX_MSDU_END_INFO1_CCND_CCE_DIS BIT(15) +#define RX_MSDU_END_INFO1_EXT_WAPI_PN GENMASK(31, 16) + +#define RX_MSDU_END_INFO2_REPORTED_MPDU_LEN GENMASK(13, 0) +#define RX_MSDU_END_INFO2_FIRST_MSDU BIT(14) +#define RX_MSDU_END_INFO2_LAST_MSDU BIT(15) +#define RX_MSDU_END_INFO2_SA_IDX_TIMEOUT BIT(16) +#define RX_MSDU_END_INFO2_DA_IDX_TIMEOUT BIT(17) +#define RX_MSDU_END_INFO2_MSDU_LIMIT_ERR BIT(18) +#define RX_MSDU_END_INFO2_FLOW_IDX_TIMEOUT BIT(19) +#define RX_MSDU_END_INFO2_FLOW_IDX_INVALID BIT(20) +#define RX_MSDU_END_INFO2_WIFI_PARSER_ERR BIT(21) +#define RX_MSDU_END_INFO2_AMSDU_PARSET_ERR BIT(22) +#define RX_MSDU_END_INFO2_SA_IS_VALID BIT(23) +#define RX_MSDU_END_INFO2_DA_IS_VALID BIT(24) +#define RX_MSDU_END_INFO2_DA_IS_MCBC BIT(25) +#define RX_MSDU_END_INFO2_L3_HDR_PADDING GENMASK(27, 26) + +#define RX_MSDU_END_INFO3_TCP_FLAG GENMASK(8, 0) +#define RX_MSDU_END_INFO3_LRO_ELIGIBLE BIT(9) + +#define RX_MSDU_END_INFO4_DA_OFFSET GENMASK(5, 0) +#define RX_MSDU_END_INFO4_SA_OFFSET GENMASK(11, 6) +#define RX_MSDU_END_INFO4_DA_OFFSET_VALID BIT(12) +#define RX_MSDU_END_INFO4_SA_OFFSET_VALID BIT(13) +#define RX_MSDU_END_INFO4_L3_TYPE GENMASK(31, 16) + +#define RX_MSDU_END_INFO5_MSDU_DROP BIT(0) +#define RX_MSDU_END_INFO5_REO_DEST_IND GENMASK(5, 1) +#define RX_MSDU_END_INFO5_FLOW_IDX GENMASK(25, 6) + +struct rx_msdu_end { + __le16 info0; + __le16 phy_ppdu_id; + __le16 ip_hdr_cksum; + __le16 tcp_udp_cksum; + __le32 info1; + __le32 ext_wapi_pn[2]; + __le32 info2; + __le32 ipv6_options_crc; + __le32 tcp_seq_num; + __le32 tcp_ack_num; + __le16 info3; + __le16 window_size; + __le32 info4; + __le32 rule_indication[2]; + __le16 sa_idx; + __le16 da_idx; + __le32 info5; + __le32 fse_metadata; + __le16 cce_metadata; + __le16 sa_sw_peer_id; +} __packed; + +/* rx_msdu_end + * + * rxpcu_mpdu_filter_in_category + * Field indicates what the reason was that this mpdu frame + * was allowed to come into the receive path by rxpcu. Values + * are defined in enum %RX_DESC_RXPCU_FILTER_*. + * + * sw_frame_group_id + * SW processes frames based on certain classifications. Values + * are defined in enum %RX_DESC_SW_FRAME_GRP_ID_*. + * + * phy_ppdu_id + * A ppdu counter value that PHY increments for every PPDU + * received. The counter value wraps around. + * + * ip_hdr_cksum + * This can include the IP header checksum or the pseudo + * header checksum used by TCP/UDP checksum. + * + * tcp_udp_chksum + * The value of the computed TCP/UDP checksum. A mode bit + * selects whether this checksum is the full checksum or the + * partial checksum which does not include the pseudo header. + * + * key_id + * The key ID octet from the IV. Only valid when first_msdu is set. + * + * cce_super_rule + * Indicates the super filter rule. + * + * cce_classify_not_done_truncate + * Classification failed due to truncated frame. + * + * cce_classify_not_done_cce_dis + * Classification failed due to CCE global disable + * + * ext_wapi_pn* + * Extension PN (packet number) which is only used by WAPI. + * + * reported_mpdu_length + * MPDU length before decapsulation. Only valid when first_msdu is + * set. This field is taken directly from the length field of the + * A-MPDU delimiter or the preamble length field for non-A-MPDU + * frames. + * + * first_msdu + * Indicates the first MSDU of A-MSDU. If both first_msdu and + * last_msdu are set in the MSDU then this is a non-aggregated MSDU + * frame: normal MPDU. Interior MSDU in an A-MSDU shall have both + * first_mpdu and last_mpdu bits set to 0. + * + * last_msdu + * Indicates the last MSDU of the A-MSDU. MPDU end status is only + * valid when last_msdu is set. + * + * sa_idx_timeout + * Indicates an unsuccessful MAC source address search due to the + * expiring of the search timer. + * + * da_idx_timeout + * Indicates an unsuccessful MAC destination address search due to + * the expiring of the search timer. + * + * msdu_limit_error + * Indicates that the MSDU threshold was exceeded and thus all the + * rest of the MSDUs will not be scattered and will not be + * decapsulated but will be DMA'ed in RAW format as a single MSDU. + * + * flow_idx_timeout + * Indicates an unsuccessful flow search due to the expiring of + * the search timer. + * + * flow_idx_invalid + * flow id is not valid. + * + * amsdu_parser_error + * A-MSDU could not be properly de-agregated. + * + * sa_is_valid + * Indicates that OLE found a valid SA entry. + * + * da_is_valid + * Indicates that OLE found a valid DA entry. + * + * da_is_mcbc + * Field Only valid if da_is_valid is set. Indicates the DA address + * was a Multicast of Broadcast address. + * + * l3_header_padding + * Number of bytes padded to make sure that the L3 header will + * always start of a Dword boundary. + * + * ipv6_options_crc + * 32 bit CRC computed out of IP v6 extension headers. + * + * tcp_seq_number + * TCP sequence number. + * + * tcp_ack_number + * TCP acknowledge number. + * + * tcp_flag + * TCP flags {NS, CWR, ECE, URG, ACK, PSH, RST, SYN, FIN}. + * + * lro_eligible + * Computed out of TCP and IP fields to indicate that this + * MSDU is eligible for LRO. + * + * window_size + * TCP receive window size. + * + * da_offset + * Offset into MSDU buffer for DA. + * + * sa_offset + * Offset into MSDU buffer for SA. + * + * da_offset_valid + * da_offset field is valid. This will be set to 0 in case + * of a dynamic A-MSDU when DA is compressed. + * + * sa_offset_valid + * sa_offset field is valid. This will be set to 0 in case + * of a dynamic A-MSDU when SA is compressed. + * + * l3_type + * The 16-bit type value indicating the type of L3 later + * extracted from LLC/SNAP, set to zero if SNAP is not + * available. + * + * rule_indication + * Bitmap indicating which of rules have matched. + * + * sa_idx + * The offset in the address table which matches MAC source address + * + * da_idx + * The offset in the address table which matches MAC destination + * address. + * + * msdu_drop + * REO shall drop this MSDU and not forward it to any other ring. + * + * reo_destination_indication + * The id of the reo exit ring where the msdu frame shall push + * after (MPDU level) reordering has finished. Values are defined + * in enum %HAL_RX_MSDU_DESC_REO_DEST_IND_. + * + * flow_idx + * Flow table index. + * + * fse_metadata + * FSE related meta data. + * + * cce_metadata + * CCE related meta data. + * + * sa_sw_peer_id + * sw_peer_id from the address search entry corresponding to the + * source address of the MSDU. + */ + +enum rx_mpdu_end_rxdma_dest_ring { + RX_MPDU_END_RXDMA_DEST_RING_RELEASE, + RX_MPDU_END_RXDMA_DEST_RING_FW, + RX_MPDU_END_RXDMA_DEST_RING_SW, + RX_MPDU_END_RXDMA_DEST_RING_REO, +}; + +#define RX_MPDU_END_INFO1_UNSUP_KTYPE_SHORT_FRAME BIT(11) +#define RX_MPDU_END_INFO1_RX_IN_TX_DECRYPT_BYT BIT(12) +#define RX_MPDU_END_INFO1_OVERFLOW_ERR BIT(13) +#define RX_MPDU_END_INFO1_MPDU_LEN_ERR BIT(14) +#define RX_MPDU_END_INFO1_TKIP_MIC_ERR BIT(15) +#define RX_MPDU_END_INFO1_DECRYPT_ERR BIT(16) +#define RX_MPDU_END_INFO1_UNENCRYPTED_FRAME_ERR BIT(17) +#define RX_MPDU_END_INFO1_PN_FIELDS_VALID BIT(18) +#define RX_MPDU_END_INFO1_FCS_ERR BIT(19) +#define RX_MPDU_END_INFO1_MSDU_LEN_ERR BIT(20) +#define RX_MPDU_END_INFO1_RXDMA0_DEST_RING GENMASK(22, 21) +#define RX_MPDU_END_INFO1_RXDMA1_DEST_RING GENMASK(24, 23) +#define RX_MPDU_END_INFO1_DECRYPT_STATUS_CODE GENMASK(27, 25) +#define RX_MPDU_END_INFO1_RX_BITMAP_NOT_UPD BIT(28) + +struct rx_mpdu_end { + __le16 info0; + __le16 phy_ppdu_id; + __le32 info1; +} __packed; + +/* rx_mpdu_end + * + * rxpcu_mpdu_filter_in_category + * Field indicates what the reason was that this mpdu frame + * was allowed to come into the receive path by rxpcu. Values + * are defined in enum %RX_DESC_RXPCU_FILTER_*. + * + * sw_frame_group_id + * SW processes frames based on certain classifications. Values + * are defined in enum %RX_DESC_SW_FRAME_GRP_ID_*. + * + * phy_ppdu_id + * A ppdu counter value that PHY increments for every PPDU + * received. The counter value wraps around. + * + * unsup_ktype_short_frame + * This bit will be '1' when WEP or TKIP or WAPI key type is + * received for 11ah short frame. Crypto will bypass the received + * packet without decryption to RxOLE after setting this bit. + * + * rx_in_tx_decrypt_byp + * Indicates that RX packet is not decrypted as Crypto is + * busy with TX packet processing. + * + * overflow_err + * RXPCU Receive FIFO ran out of space to receive the full MPDU. + * Therefore this MPDU is terminated early and is thus corrupted. + * + * This MPDU will not be ACKed. + * + * RXPCU might still be able to correctly receive the following + * MPDUs in the PPDU if enough fifo space became available in time. + * + * mpdu_length_err + * Set by RXPCU if the expected MPDU length does not correspond + * with the actually received number of bytes in the MPDU. + * + * tkip_mic_err + * Set by Rx crypto when crypto detected a TKIP MIC error for + * this MPDU. + * + * decrypt_err + * Set by RX CRYPTO when CRYPTO detected a decrypt error for this + * MPDU or CRYPTO received an encrypted frame, but did not get a + * valid corresponding key id in the peer entry. + * + * unencrypted_frame_err + * Set by RX CRYPTO when CRYPTO detected an unencrypted frame while + * in the peer entry field 'All_frames_shall_be_encrypted' is set. + * + * pn_fields_contain_valid_info + * Set by RX CRYPTO to indicate that there is a valid PN field + * present in this MPDU. + * + * fcs_err + * Set by RXPCU when there is an FCS error detected for this MPDU. + * + * msdu_length_err + * Set by RXOLE when there is an msdu length error detected + * in at least 1 of the MSDUs embedded within the MPDU. + * + * rxdma0_destination_ring + * rxdma1_destination_ring + * The ring to which RXDMA0/1 shall push the frame, assuming + * no MPDU level errors are detected. In case of MPDU level + * errors, RXDMA0/1 might change the RXDMA0/1 destination. Values + * are defined in %enum RX_MPDU_END_RXDMA_DEST_RING_*. + * + * decrypt_status_code + * Field provides insight into the decryption performed. Values + * are defined in enum %RX_DESC_DECRYPT_STATUS_CODE_*. + * + * rx_bitmap_not_updated + * Frame is received, but RXPCU could not update the receive bitmap + * due to (temporary) fifo constraints. + */ + +/* Padding bytes to avoid TLV's spanning across 128 byte boundary */ +#define HAL_RX_DESC_PADDING0_BYTES 4 +#define HAL_RX_DESC_PADDING1_BYTES 16 + +#define HAL_RX_DESC_HDR_STATUS_LEN 120 + +struct hal_rx_desc { + __le32 msdu_end_tag; + struct rx_msdu_end msdu_end; + __le32 rx_attn_tag; + struct rx_attention attention; + __le32 msdu_start_tag; + struct rx_msdu_start msdu_start; + u8 rx_padding0[HAL_RX_DESC_PADDING0_BYTES]; + __le32 mpdu_start_tag; + struct rx_mpdu_start mpdu_start; + __le32 mpdu_end_tag; + struct rx_mpdu_end mpdu_end; + u8 rx_padding1[HAL_RX_DESC_PADDING1_BYTES]; + __le32 hdr_status_tag; + __le32 phy_ppdu_id; + u8 hdr_status[HAL_RX_DESC_HDR_STATUS_LEN]; + u8 msdu_payload[0]; +} __packed; + +#endif /* ATH11K_RX_DESC_H */ diff --git a/drivers/net/wireless/ath/ath11k/testmode.c b/drivers/net/wireless/ath/ath11k/testmode.c new file mode 100644 index 0000000000000..932bee6d17407 --- /dev/null +++ b/drivers/net/wireless/ath/ath11k/testmode.c @@ -0,0 +1,199 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* + * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. + */ + +#include "testmode.h" +#include +#include "debug.h" +#include "wmi.h" +#include "hw.h" +#include "core.h" +#include "testmode_i.h" + +static const struct nla_policy ath11k_tm_policy[ATH11K_TM_ATTR_MAX + 1] = { + [ATH11K_TM_ATTR_CMD] = { .type = NLA_U32 }, + [ATH11K_TM_ATTR_DATA] = { .type = NLA_BINARY, + .len = ATH11K_TM_DATA_MAX_LEN }, + [ATH11K_TM_ATTR_WMI_CMDID] = { .type = NLA_U32 }, + [ATH11K_TM_ATTR_VERSION_MAJOR] = { .type = NLA_U32 }, + [ATH11K_TM_ATTR_VERSION_MINOR] = { .type = NLA_U32 }, +}; + +/* Returns true if callee consumes the skb and the skb should be discarded. + * Returns false if skb is not used. Does not sleep. + */ +bool ath11k_tm_event_wmi(struct ath11k *ar, u32 cmd_id, struct sk_buff *skb) +{ + struct sk_buff *nl_skb; + bool consumed; + int ret; + + ath11k_dbg(ar->ab, ATH11K_DBG_TESTMODE, + "testmode event wmi cmd_id %d skb %pK skb->len %d\n", + cmd_id, skb, skb->len); + + ath11k_dbg_dump(ar->ab, ATH11K_DBG_TESTMODE, NULL, "", skb->data, skb->len); + + spin_lock_bh(&ar->data_lock); + + consumed = true; + + nl_skb = cfg80211_testmode_alloc_event_skb(ar->hw->wiphy, + 2 * sizeof(u32) + skb->len, + GFP_ATOMIC); + if (!nl_skb) { + ath11k_warn(ar->ab, + "failed to allocate skb for testmode wmi event\n"); + goto out; + } + + ret = nla_put_u32(nl_skb, ATH11K_TM_ATTR_CMD, ATH11K_TM_CMD_WMI); + if (ret) { + ath11k_warn(ar->ab, + "failed to to put testmode wmi event cmd attribute: %d\n", + ret); + kfree_skb(nl_skb); + goto out; + } + + ret = nla_put_u32(nl_skb, ATH11K_TM_ATTR_WMI_CMDID, cmd_id); + if (ret) { + ath11k_warn(ar->ab, + "failed to to put testmode wmi even cmd_id: %d\n", + ret); + kfree_skb(nl_skb); + goto out; + } + + ret = nla_put(nl_skb, ATH11K_TM_ATTR_DATA, skb->len, skb->data); + if (ret) { + ath11k_warn(ar->ab, + "failed to copy skb to testmode wmi event: %d\n", + ret); + kfree_skb(nl_skb); + goto out; + } + + cfg80211_testmode_event(nl_skb, GFP_ATOMIC); + +out: + spin_unlock_bh(&ar->data_lock); + + return consumed; +} + +static int ath11k_tm_cmd_get_version(struct ath11k *ar, struct nlattr *tb[]) +{ + struct sk_buff *skb; + int ret; + + ath11k_dbg(ar->ab, ATH11K_DBG_TESTMODE, + "testmode cmd get version_major %d version_minor %d\n", + ATH11K_TESTMODE_VERSION_MAJOR, + ATH11K_TESTMODE_VERSION_MINOR); + + skb = cfg80211_testmode_alloc_reply_skb(ar->hw->wiphy, + nla_total_size(sizeof(u32))); + if (!skb) + return -ENOMEM; + + ret = nla_put_u32(skb, ATH11K_TM_ATTR_VERSION_MAJOR, + ATH11K_TESTMODE_VERSION_MAJOR); + if (ret) { + kfree_skb(skb); + return ret; + } + + ret = nla_put_u32(skb, ATH11K_TM_ATTR_VERSION_MINOR, + ATH11K_TESTMODE_VERSION_MINOR); + if (ret) { + kfree_skb(skb); + return ret; + } + + return cfg80211_testmode_reply(skb); +} + +static int ath11k_tm_cmd_wmi(struct ath11k *ar, struct nlattr *tb[]) +{ + struct ath11k_pdev_wmi *wmi = ar->wmi; + struct sk_buff *skb; + u32 cmd_id, buf_len; + int ret; + void *buf; + + mutex_lock(&ar->conf_mutex); + + if (ar->state != ATH11K_STATE_ON) { + ret = -ENETDOWN; + goto out; + } + + if (!tb[ATH11K_TM_ATTR_DATA]) { + ret = -EINVAL; + goto out; + } + + if (!tb[ATH11K_TM_ATTR_WMI_CMDID]) { + ret = -EINVAL; + goto out; + } + + buf = nla_data(tb[ATH11K_TM_ATTR_DATA]); + buf_len = nla_len(tb[ATH11K_TM_ATTR_DATA]); + cmd_id = nla_get_u32(tb[ATH11K_TM_ATTR_WMI_CMDID]); + + ath11k_dbg(ar->ab, ATH11K_DBG_TESTMODE, + "testmode cmd wmi cmd_id %d buf %pK buf_len %d\n", + cmd_id, buf, buf_len); + + ath11k_dbg_dump(ar->ab, ATH11K_DBG_TESTMODE, NULL, "", buf, buf_len); + + skb = ath11k_wmi_alloc_skb(wmi->wmi_sc, buf_len); + if (!skb) { + ret = -ENOMEM; + goto out; + } + + memcpy(skb->data, buf, buf_len); + + ret = ath11k_wmi_cmd_send(wmi, skb, cmd_id); + if (ret) { + dev_kfree_skb(skb); + ath11k_warn(ar->ab, "failed to transmit wmi command (testmode): %d\n", + ret); + goto out; + } + + ret = 0; + +out: + mutex_unlock(&ar->conf_mutex); + return ret; +} + +int ath11k_tm_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + void *data, int len) +{ + struct ath11k *ar = hw->priv; + struct nlattr *tb[ATH11K_TM_ATTR_MAX + 1]; + int ret; + + ret = nla_parse(tb, ATH11K_TM_ATTR_MAX, data, len, ath11k_tm_policy, + NULL); + if (ret) + return ret; + + if (!tb[ATH11K_TM_ATTR_CMD]) + return -EINVAL; + + switch (nla_get_u32(tb[ATH11K_TM_ATTR_CMD])) { + case ATH11K_TM_CMD_GET_VERSION: + return ath11k_tm_cmd_get_version(ar, tb); + case ATH11K_TM_CMD_WMI: + return ath11k_tm_cmd_wmi(ar, tb); + default: + return -EOPNOTSUPP; + } +} diff --git a/drivers/net/wireless/ath/ath11k/testmode.h b/drivers/net/wireless/ath/ath11k/testmode.h new file mode 100644 index 0000000000000..aaa122ed90697 --- /dev/null +++ b/drivers/net/wireless/ath/ath11k/testmode.h @@ -0,0 +1,29 @@ +/* SPDX-License-Identifier: BSD-3-Clause-Clear */ +/* + * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. + */ + +#include "core.h" + +#ifdef CONFIG_NL80211_TESTMODE + +bool ath11k_tm_event_wmi(struct ath11k *ar, u32 cmd_id, struct sk_buff *skb); +int ath11k_tm_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + void *data, int len); + +#else + +static inline bool ath11k_tm_event_wmi(struct ath11k *ar, u32 cmd_id, + struct sk_buff *skb) +{ + return false; +} + +static inline int ath11k_tm_cmd(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + void *data, int len) +{ + return 0; +} + +#endif diff --git a/drivers/net/wireless/ath/ath11k/testmode_i.h b/drivers/net/wireless/ath/ath11k/testmode_i.h new file mode 100644 index 0000000000000..4bae2a9eeea4f --- /dev/null +++ b/drivers/net/wireless/ath/ath11k/testmode_i.h @@ -0,0 +1,50 @@ +/* SPDX-License-Identifier: BSD-3-Clause-Clear */ +/* + * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. + */ + +/* "API" level of the ath11k testmode interface. Bump it after every + * incompatible interface change. + */ +#define ATH11K_TESTMODE_VERSION_MAJOR 1 + +/* Bump this after every _compatible_ interface change, for example + * addition of a new command or an attribute. + */ +#define ATH11K_TESTMODE_VERSION_MINOR 0 + +#define ATH11K_TM_DATA_MAX_LEN 5000 + +enum ath11k_tm_attr { + __ATH11K_TM_ATTR_INVALID = 0, + ATH11K_TM_ATTR_CMD = 1, + ATH11K_TM_ATTR_DATA = 2, + ATH11K_TM_ATTR_WMI_CMDID = 3, + ATH11K_TM_ATTR_VERSION_MAJOR = 4, + ATH11K_TM_ATTR_VERSION_MINOR = 5, + ATH11K_TM_ATTR_WMI_OP_VERSION = 6, + + /* keep last */ + __ATH11K_TM_ATTR_AFTER_LAST, + ATH11K_TM_ATTR_MAX = __ATH11K_TM_ATTR_AFTER_LAST - 1, +}; + +/* All ath11k testmode interface commands specified in + * ATH11K_TM_ATTR_CMD + */ +enum ath11k_tm_cmd { + /* Returns the supported ath11k testmode interface version in + * ATH11K_TM_ATTR_VERSION. Always guaranteed to work. User space + * uses this to verify it's using the correct version of the + * testmode interface + */ + ATH11K_TM_CMD_GET_VERSION = 0, + + /* The command used to transmit a WMI command to the firmware and + * the event to receive WMI events from the firmware. Without + * struct wmi_cmd_hdr header, only the WMI payload. Command id is + * provided with ATH11K_TM_ATTR_WMI_CMDID and payload in + * ATH11K_TM_ATTR_DATA. + */ + ATH11K_TM_CMD_WMI = 1, +}; diff --git a/drivers/net/wireless/ath/ath11k/trace.c b/drivers/net/wireless/ath/ath11k/trace.c new file mode 100644 index 0000000000000..f0cc49ba0387f --- /dev/null +++ b/drivers/net/wireless/ath/ath11k/trace.c @@ -0,0 +1,9 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* + * Copyright (c) 2019 The Linux Foundation. All rights reserved. + */ + +#include + +#define CREATE_TRACE_POINTS +#include "trace.h" diff --git a/drivers/net/wireless/ath/ath11k/trace.h b/drivers/net/wireless/ath/ath11k/trace.h new file mode 100644 index 0000000000000..400f759568e14 --- /dev/null +++ b/drivers/net/wireless/ath/ath11k/trace.h @@ -0,0 +1,110 @@ +/* SPDX-License-Identifier: BSD-3-Clause-Clear */ +/* + * Copyright (c) 2019 The Linux Foundation. All rights reserved. + */ + +#if !defined(_TRACE_H_) || defined(TRACE_HEADER_MULTI_READ) + +#include +#include "core.h" + +#define _TRACE_H_ + +/* create empty functions when tracing is disabled */ +#if !defined(CONFIG_ATH11K_TRACING) +#undef TRACE_EVENT +#define TRACE_EVENT(name, proto, ...) \ +static inline void trace_ ## name(proto) {} +#endif /* !CONFIG_ATH11K_TRACING || __CHECKER__ */ + +TRACE_EVENT(ath11k_htt_pktlog, + TP_PROTO(struct ath11k *ar, const void *buf, u16 buf_len), + + TP_ARGS(ar, buf, buf_len), + + TP_STRUCT__entry( + __string(device, dev_name(ar->ab->dev)) + __string(driver, dev_driver_string(ar->ab->dev)) + __field(u16, buf_len) + __dynamic_array(u8, pktlog, buf_len) + ), + + TP_fast_assign( + __assign_str(device, dev_name(ar->ab->dev)); + __assign_str(driver, dev_driver_string(ar->ab->dev)); + __entry->buf_len = buf_len; + memcpy(__get_dynamic_array(pktlog), buf, buf_len); + ), + + TP_printk( + "%s %s size %hu", + __get_str(driver), + __get_str(device), + __entry->buf_len + ) +); + +TRACE_EVENT(ath11k_htt_ppdu_stats, + TP_PROTO(struct ath11k *ar, const void *data, size_t len), + + TP_ARGS(ar, data, len), + + TP_STRUCT__entry( + __string(device, dev_name(ar->ab->dev)) + __string(driver, dev_driver_string(ar->ab->dev)) + __field(u16, len) + __dynamic_array(u8, ppdu, len) + ), + + TP_fast_assign( + __assign_str(device, dev_name(ar->ab->dev)); + __assign_str(driver, dev_driver_string(ar->ab->dev)); + __entry->len = len; + memcpy(__get_dynamic_array(ppdu), data, len); + ), + + TP_printk( + "%s %s ppdu len %d", + __get_str(driver), + __get_str(device), + __entry->len + ) +); + +TRACE_EVENT(ath11k_htt_rxdesc, + TP_PROTO(struct ath11k *ar, const void *data, size_t len), + + TP_ARGS(ar, data, len), + + TP_STRUCT__entry( + __string(device, dev_name(ar->ab->dev)) + __string(driver, dev_driver_string(ar->ab->dev)) + __field(u16, len) + __dynamic_array(u8, rxdesc, len) + ), + + TP_fast_assign( + __assign_str(device, dev_name(ar->ab->dev)); + __assign_str(driver, dev_driver_string(ar->ab->dev)); + __entry->len = len; + memcpy(__get_dynamic_array(rxdesc), data, len); + ), + + TP_printk( + "%s %s rxdesc len %d", + __get_str(driver), + __get_str(device), + __entry->len + ) +); + +#endif /* _TRACE_H_ || TRACE_HEADER_MULTI_READ*/ + +/* we don't want to use include/trace/events */ +#undef TRACE_INCLUDE_PATH +#define TRACE_INCLUDE_PATH . +#undef TRACE_INCLUDE_FILE +#define TRACE_INCLUDE_FILE trace + +/* This part must be outside protection */ +#include diff --git a/drivers/net/wireless/ath/ath11k/wmi.c b/drivers/net/wireless/ath/ath11k/wmi.c new file mode 100644 index 0000000000000..3d1d4d3c3f20e --- /dev/null +++ b/drivers/net/wireless/ath/ath11k/wmi.c @@ -0,0 +1,5632 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* + * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "core.h" +#include "debug.h" +#include "mac.h" +#include "hw.h" +#include "peer.h" + +struct wmi_tlv_policy { + size_t min_len; +}; + +struct wmi_tlv_svc_ready_parse { + bool wmi_svc_bitmap_done; +}; + +struct wmi_tlv_svc_rdy_ext_parse { + struct ath11k_service_ext_param param; + struct wmi_soc_mac_phy_hw_mode_caps *hw_caps; + struct wmi_hw_mode_capabilities *hw_mode_caps; + u32 n_hw_mode_caps; + u32 tot_phy_id; + struct wmi_hw_mode_capabilities pref_hw_mode_caps; + struct wmi_mac_phy_capabilities *mac_phy_caps; + u32 n_mac_phy_caps; + struct wmi_soc_hal_reg_capabilities *soc_hal_reg_caps; + struct wmi_hal_reg_capabilities_ext *ext_hal_reg_caps; + u32 n_ext_hal_reg_caps; + bool hw_mode_done; + bool mac_phy_done; + bool ext_hal_reg_done; +}; + +struct wmi_tlv_rdy_parse { + u32 num_extra_mac_addr; +}; + +static const struct wmi_tlv_policy wmi_tlv_policies[] = { + [WMI_TAG_ARRAY_BYTE] + = { .min_len = 0 }, + [WMI_TAG_ARRAY_UINT32] + = { .min_len = 0 }, + [WMI_TAG_SERVICE_READY_EVENT] + = { .min_len = sizeof(struct wmi_service_ready_event) }, + [WMI_TAG_SERVICE_READY_EXT_EVENT] + = { .min_len = sizeof(struct wmi_service_ready_ext_event) }, + [WMI_TAG_SOC_MAC_PHY_HW_MODE_CAPS] + = { .min_len = sizeof(struct wmi_soc_mac_phy_hw_mode_caps) }, + [WMI_TAG_SOC_HAL_REG_CAPABILITIES] + = { .min_len = sizeof(struct wmi_soc_hal_reg_capabilities) }, + [WMI_TAG_VDEV_START_RESPONSE_EVENT] + = { .min_len = sizeof(struct wmi_vdev_start_resp_event) }, + [WMI_TAG_PEER_DELETE_RESP_EVENT] + = { .min_len = sizeof(struct wmi_peer_delete_resp_event) }, + [WMI_TAG_OFFLOAD_BCN_TX_STATUS_EVENT] + = { .min_len = sizeof(struct wmi_bcn_tx_status_event) }, + [WMI_TAG_VDEV_STOPPED_EVENT] + = { .min_len = sizeof(struct wmi_vdev_stopped_event) }, + [WMI_TAG_REG_CHAN_LIST_CC_EVENT] + = { .min_len = sizeof(struct wmi_reg_chan_list_cc_event) }, + [WMI_TAG_MGMT_RX_HDR] + = { .min_len = sizeof(struct wmi_mgmt_rx_hdr) }, + [WMI_TAG_MGMT_TX_COMPL_EVENT] + = { .min_len = sizeof(struct wmi_mgmt_tx_compl_event) }, + [WMI_TAG_SCAN_EVENT] + = { .min_len = sizeof(struct wmi_scan_event) }, + [WMI_TAG_PEER_STA_KICKOUT_EVENT] + = { .min_len = sizeof(struct wmi_peer_sta_kickout_event) }, + [WMI_TAG_ROAM_EVENT] + = { .min_len = sizeof(struct wmi_roam_event) }, + [WMI_TAG_CHAN_INFO_EVENT] + = { .min_len = sizeof(struct wmi_chan_info_event) }, + [WMI_TAG_PDEV_BSS_CHAN_INFO_EVENT] + = { .min_len = sizeof(struct wmi_pdev_bss_chan_info_event) }, + [WMI_TAG_VDEV_INSTALL_KEY_COMPLETE_EVENT] + = { .min_len = sizeof(struct wmi_vdev_install_key_compl_event) }, + [WMI_TAG_READY_EVENT] + = {.min_len = sizeof(struct wmi_ready_event) }, + [WMI_TAG_SERVICE_AVAILABLE_EVENT] + = {.min_len = sizeof(struct wmi_service_available_event) }, + [WMI_TAG_PEER_ASSOC_CONF_EVENT] + = { .min_len = sizeof(struct wmi_peer_assoc_conf_event) }, + [WMI_TAG_STATS_EVENT] + = { .min_len = sizeof(struct wmi_stats_event) }, + [WMI_TAG_PDEV_CTL_FAILSAFE_CHECK_EVENT] + = { .min_len = sizeof(struct wmi_pdev_ctl_failsafe_chk_event) }, +}; + +#define PRIMAP(_hw_mode_) \ + [_hw_mode_] = _hw_mode_##_PRI + +static const int ath11k_hw_mode_pri_map[] = { + PRIMAP(WMI_HOST_HW_MODE_SINGLE), + PRIMAP(WMI_HOST_HW_MODE_DBS), + PRIMAP(WMI_HOST_HW_MODE_SBS_PASSIVE), + PRIMAP(WMI_HOST_HW_MODE_SBS), + PRIMAP(WMI_HOST_HW_MODE_DBS_SBS), + PRIMAP(WMI_HOST_HW_MODE_DBS_OR_SBS), + /* keep last */ + PRIMAP(WMI_HOST_HW_MODE_MAX), +}; + +static int +ath11k_wmi_tlv_iter(struct ath11k_base *ab, const void *ptr, size_t len, + int (*iter)(struct ath11k_base *ab, u16 tag, u16 len, + const void *ptr, void *data), + void *data) +{ + const void *begin = ptr; + const struct wmi_tlv *tlv; + u16 tlv_tag, tlv_len; + int ret; + + while (len > 0) { + if (len < sizeof(*tlv)) { + ath11k_err(ab, "wmi tlv parse failure at byte %zd (%zu bytes left, %zu expected)\n", + ptr - begin, len, sizeof(*tlv)); + return -EINVAL; + } + + tlv = ptr; + tlv_tag = FIELD_GET(WMI_TLV_TAG, tlv->header); + tlv_len = FIELD_GET(WMI_TLV_LEN, tlv->header); + ptr += sizeof(*tlv); + len -= sizeof(*tlv); + + if (tlv_len > len) { + ath11k_err(ab, "wmi tlv parse failure of tag %hhu at byte %zd (%zu bytes left, %hhu expected)\n", + tlv_tag, ptr - begin, len, tlv_len); + return -EINVAL; + } + + if (tlv_tag < ARRAY_SIZE(wmi_tlv_policies) && + wmi_tlv_policies[tlv_tag].min_len && + wmi_tlv_policies[tlv_tag].min_len > tlv_len) { + ath11k_err(ab, "wmi tlv parse failure of tag %hhu at byte %zd (%hhu bytes is less than min length %zu)\n", + tlv_tag, ptr - begin, tlv_len, + wmi_tlv_policies[tlv_tag].min_len); + return -EINVAL; + } + + ret = iter(ab, tlv_tag, tlv_len, ptr, data); + if (ret) + return ret; + + ptr += tlv_len; + len -= tlv_len; + } + + return 0; +} + +static int ath11k_wmi_tlv_iter_parse(struct ath11k_base *ab, u16 tag, u16 len, + const void *ptr, void *data) +{ + const void **tb = data; + + if (tag < WMI_TAG_MAX) + tb[tag] = ptr; + + return 0; +} + +static int ath11k_wmi_tlv_parse(struct ath11k_base *ar, const void **tb, + const void *ptr, size_t len) +{ + return ath11k_wmi_tlv_iter(ar, ptr, len, ath11k_wmi_tlv_iter_parse, + (void *)tb); +} + +static const void ** +ath11k_wmi_tlv_parse_alloc(struct ath11k_base *ab, const void *ptr, + size_t len, gfp_t gfp) +{ + const void **tb; + int ret; + + tb = kcalloc(WMI_TAG_MAX, sizeof(*tb), gfp); + if (!tb) + return ERR_PTR(-ENOMEM); + + ret = ath11k_wmi_tlv_parse(ab, tb, ptr, len); + if (ret) { + kfree(tb); + return ERR_PTR(ret); + } + + return tb; +} + +static int ath11k_wmi_cmd_send_nowait(struct ath11k_pdev_wmi *wmi, struct sk_buff *skb, + u32 cmd_id) +{ + struct ath11k_skb_cb *skb_cb = ATH11K_SKB_CB(skb); + struct ath11k_base *ab = wmi->wmi_sc->ab; + struct wmi_cmd_hdr *cmd_hdr; + int ret; + u32 cmd = 0; + + if (skb_push(skb, sizeof(struct wmi_cmd_hdr)) == NULL) + return -ENOMEM; + + cmd |= FIELD_PREP(WMI_CMD_HDR_CMD_ID, cmd_id); + + cmd_hdr = (struct wmi_cmd_hdr *)skb->data; + cmd_hdr->cmd_id = cmd; + + memset(skb_cb, 0, sizeof(*skb_cb)); + ret = ath11k_htc_send(&ab->htc, wmi->eid, skb); + + if (ret) + goto err_pull; + + return 0; + +err_pull: + skb_pull(skb, sizeof(struct wmi_cmd_hdr)); + return ret; +} + +int ath11k_wmi_cmd_send(struct ath11k_pdev_wmi *wmi, struct sk_buff *skb, + u32 cmd_id) +{ + struct ath11k_wmi_base *wmi_sc = wmi->wmi_sc; + int ret = -EOPNOTSUPP; + + might_sleep(); + + wait_event_timeout(wmi_sc->tx_credits_wq, ({ + ret = ath11k_wmi_cmd_send_nowait(wmi, skb, cmd_id); + + if (ret && test_bit(ATH11K_FLAG_CRASH_FLUSH, &wmi_sc->ab->dev_flags)) + ret = -ESHUTDOWN; + + (ret != -EAGAIN); + }), WMI_SEND_TIMEOUT_HZ); + + if (ret == -EAGAIN) + ath11k_warn(wmi_sc->ab, "wmi command %d timeout\n", cmd_id); + + return ret; +} + +static int ath11k_pull_svc_ready_ext(struct ath11k_pdev_wmi *wmi_handle, + const void *ptr, + struct ath11k_service_ext_param *param) +{ + const struct wmi_service_ready_ext_event *ev = ptr; + + if (!ev) + return -EINVAL; + + /* Move this to host based bitmap */ + param->default_conc_scan_config_bits = ev->default_conc_scan_config_bits; + param->default_fw_config_bits = ev->default_fw_config_bits; + param->he_cap_info = ev->he_cap_info; + param->mpdu_density = ev->mpdu_density; + param->max_bssid_rx_filters = ev->max_bssid_rx_filters; + memcpy(¶m->ppet, &ev->ppet, sizeof(param->ppet)); + + return 0; +} + +static int +ath11k_pull_mac_phy_cap_svc_ready_ext(struct ath11k_pdev_wmi *wmi_handle, + struct wmi_soc_mac_phy_hw_mode_caps *hw_caps, + struct wmi_hw_mode_capabilities *wmi_hw_mode_caps, + struct wmi_soc_hal_reg_capabilities *hal_reg_caps, + struct wmi_mac_phy_capabilities *wmi_mac_phy_caps, + u8 hw_mode_id, u8 phy_id, + struct ath11k_pdev *pdev) +{ + struct wmi_mac_phy_capabilities *mac_phy_caps; + struct ath11k_band_cap *cap_band; + struct ath11k_pdev_cap *pdev_cap = &pdev->cap; + u32 phy_map; + u32 hw_idx, phy_idx = 0; + + if (!hw_caps || !wmi_hw_mode_caps || !hal_reg_caps) + return -EINVAL; + + for (hw_idx = 0; hw_idx < hw_caps->num_hw_modes; hw_idx++) { + if (hw_mode_id == wmi_hw_mode_caps[hw_idx].hw_mode_id) + break; + + phy_map = wmi_hw_mode_caps[hw_idx].phy_id_map; + while (phy_map) { + phy_map >>= 1; + phy_idx++; + } + } + + if (hw_idx == hw_caps->num_hw_modes) + return -EINVAL; + + phy_idx += phy_id; + if (phy_id >= hal_reg_caps->num_phy) + return -EINVAL; + + mac_phy_caps = &wmi_mac_phy_caps[phy_idx]; + + pdev->pdev_id = mac_phy_caps->pdev_id; + pdev_cap->supported_bands = mac_phy_caps->supported_bands; + pdev_cap->ampdu_density = mac_phy_caps->ampdu_density; + + /* Take non-zero tx/rx chainmask. If tx/rx chainmask differs from + * band to band for a single radio, need to see how this should be + * handled. + */ + if (mac_phy_caps->supported_bands & WMI_HOST_WLAN_2G_CAP) { + pdev_cap->tx_chain_mask = mac_phy_caps->tx_chain_mask_2g; + pdev_cap->rx_chain_mask = mac_phy_caps->rx_chain_mask_2g; + } else if (mac_phy_caps->supported_bands & WMI_HOST_WLAN_5G_CAP) { + pdev_cap->vht_cap = mac_phy_caps->vht_cap_info_5g; + pdev_cap->vht_mcs = mac_phy_caps->vht_supp_mcs_5g; + pdev_cap->he_mcs = mac_phy_caps->he_supp_mcs_5g; + pdev_cap->tx_chain_mask = mac_phy_caps->tx_chain_mask_5g; + pdev_cap->rx_chain_mask = mac_phy_caps->rx_chain_mask_5g; + } else { + return -EINVAL; + } + + /* tx/rx chainmask reported from fw depends on the actual hw chains used, + * For example, for 4x4 capable macphys, first 4 chains can be used for first + * mac and the remaing 4 chains can be used for the second mac or vice-versa. + * In this case, tx/rx chainmask 0xf will be advertised for first mac and 0xf0 + * will be advertised for second mac or vice-versa. Compute the shift value for + * for tx/rx chainmask which will be used to advertise supported ht/vht rates to + * mac80211. + */ + pdev_cap->tx_chain_mask_shift = + find_first_bit((unsigned long *)&pdev_cap->tx_chain_mask, 32); + pdev_cap->rx_chain_mask_shift = + find_first_bit((unsigned long *)&pdev_cap->rx_chain_mask, 32); + + cap_band = &pdev_cap->band[NL80211_BAND_2GHZ]; + cap_band->max_bw_supported = mac_phy_caps->max_bw_supported_2g; + cap_band->ht_cap_info = mac_phy_caps->ht_cap_info_2g; + cap_band->he_cap_info[0] = mac_phy_caps->he_cap_info_2g; + cap_band->he_cap_info[1] = mac_phy_caps->he_cap_info_2g_ext; + cap_band->he_mcs = mac_phy_caps->he_supp_mcs_2g; + memcpy(cap_band->he_cap_phy_info, &mac_phy_caps->he_cap_phy_info_2g, + sizeof(u32) * PSOC_HOST_MAX_PHY_SIZE); + memcpy(&cap_band->he_ppet, &mac_phy_caps->he_ppet2g, + sizeof(struct ath11k_ppe_threshold)); + + cap_band = &pdev_cap->band[NL80211_BAND_5GHZ]; + cap_band->max_bw_supported = mac_phy_caps->max_bw_supported_5g; + cap_band->ht_cap_info = mac_phy_caps->ht_cap_info_5g; + cap_band->he_cap_info[0] = mac_phy_caps->he_cap_info_5g; + cap_band->he_cap_info[1] = mac_phy_caps->he_cap_info_5g_ext; + cap_band->he_mcs = mac_phy_caps->he_supp_mcs_5g; + memcpy(cap_band->he_cap_phy_info, &mac_phy_caps->he_cap_phy_info_5g, + sizeof(u32) * PSOC_HOST_MAX_PHY_SIZE); + memcpy(&cap_band->he_ppet, &mac_phy_caps->he_ppet5g, + sizeof(struct ath11k_ppe_threshold)); + + return 0; +} + +static int +ath11k_pull_reg_cap_svc_rdy_ext(struct ath11k_pdev_wmi *wmi_handle, + struct wmi_soc_hal_reg_capabilities *reg_caps, + struct wmi_hal_reg_capabilities_ext *wmi_ext_reg_cap, + u8 phy_idx, + struct ath11k_hal_reg_capabilities_ext *param) +{ + struct wmi_hal_reg_capabilities_ext *ext_reg_cap; + + if (!reg_caps || !wmi_ext_reg_cap) + return -EINVAL; + + if (phy_idx >= reg_caps->num_phy) + return -EINVAL; + + ext_reg_cap = &wmi_ext_reg_cap[phy_idx]; + + param->phy_id = ext_reg_cap->phy_id; + param->eeprom_reg_domain = ext_reg_cap->eeprom_reg_domain; + param->eeprom_reg_domain_ext = + ext_reg_cap->eeprom_reg_domain_ext; + param->regcap1 = ext_reg_cap->regcap1; + param->regcap2 = ext_reg_cap->regcap2; + /* check if param->wireless_mode is needed */ + param->low_2ghz_chan = ext_reg_cap->low_2ghz_chan; + param->high_2ghz_chan = ext_reg_cap->high_2ghz_chan; + param->low_5ghz_chan = ext_reg_cap->low_5ghz_chan; + param->high_5ghz_chan = ext_reg_cap->high_5ghz_chan; + + return 0; +} + +static int ath11k_pull_service_ready_tlv(struct ath11k_base *ab, + const void *evt_buf, + struct ath11k_targ_cap *cap) +{ + const struct wmi_service_ready_event *ev = evt_buf; + + if (!ev) { + ath11k_err(ab, "%s: failed by NULL param\n", + __func__); + return -EINVAL; + } + + cap->phy_capability = ev->phy_capability; + cap->max_frag_entry = ev->max_frag_entry; + cap->num_rf_chains = ev->num_rf_chains; + cap->ht_cap_info = ev->ht_cap_info; + cap->vht_cap_info = ev->vht_cap_info; + cap->vht_supp_mcs = ev->vht_supp_mcs; + cap->hw_min_tx_power = ev->hw_min_tx_power; + cap->hw_max_tx_power = ev->hw_max_tx_power; + cap->sys_cap_info = ev->sys_cap_info; + cap->min_pkt_size_enable = ev->min_pkt_size_enable; + cap->max_bcn_ie_size = ev->max_bcn_ie_size; + cap->max_num_scan_channels = ev->max_num_scan_channels; + cap->max_supported_macs = ev->max_supported_macs; + cap->wmi_fw_sub_feat_caps = ev->wmi_fw_sub_feat_caps; + cap->txrx_chainmask = ev->txrx_chainmask; + cap->default_dbs_hw_mode_index = ev->default_dbs_hw_mode_index; + cap->num_msdu_desc = ev->num_msdu_desc; + + return 0; +} + +/* Save the wmi_service_bitmap into a linear bitmap. The wmi_services in + * wmi_service ready event are advertised in b0-b3 (LSB 4-bits) of each + * 4-byte word. + */ +static void ath11k_wmi_service_bitmap_copy(struct ath11k_pdev_wmi *wmi, + const u32 *wmi_svc_bm) +{ + int i, j; + + for (i = 0, j = 0; i < WMI_SERVICE_BM_SIZE && j < WMI_MAX_SERVICE; i++) { + do { + if (wmi_svc_bm[i] & BIT(j % WMI_SERVICE_BITS_IN_SIZE32)) + set_bit(j, wmi->wmi_sc->svc_map); + } while (++j % WMI_SERVICE_BITS_IN_SIZE32); + } +} + +static int ath11k_wmi_tlv_svc_rdy_parse(struct ath11k_base *ab, u16 tag, u16 len, + const void *ptr, void *data) +{ + struct wmi_tlv_svc_ready_parse *svc_ready = data; + struct ath11k_pdev_wmi *wmi_handle = &ab->wmi_sc.wmi[0]; + u16 expect_len; + + switch (tag) { + case WMI_TAG_SERVICE_READY_EVENT: + if (ath11k_pull_service_ready_tlv(ab, ptr, &ab->target_caps)) + return -EINVAL; + break; + + case WMI_TAG_ARRAY_UINT32: + if (!svc_ready->wmi_svc_bitmap_done) { + expect_len = WMI_SERVICE_BM_SIZE * sizeof(u32); + if (len < expect_len) { + ath11k_warn(ab, "invalid len %d for the tag 0x%x\n", + len, tag); + return -EINVAL; + } + + ath11k_wmi_service_bitmap_copy(wmi_handle, ptr); + + svc_ready->wmi_svc_bitmap_done = true; + } + break; + default: + break; + } + + return 0; +} + +static int ath11k_service_ready_event(struct ath11k_base *ab, struct sk_buff *skb) +{ + struct wmi_tlv_svc_ready_parse svc_ready = { }; + int ret; + + ret = ath11k_wmi_tlv_iter(ab, skb->data, skb->len, + ath11k_wmi_tlv_svc_rdy_parse, + &svc_ready); + if (ret) { + ath11k_warn(ab, "failed to parse tlv %d\n", ret); + return ret; + } + + return 0; +} + +struct sk_buff *ath11k_wmi_alloc_skb(struct ath11k_wmi_base *wmi_sc, u32 len) +{ + struct sk_buff *skb; + struct ath11k_base *ab = wmi_sc->ab; + u32 round_len = roundup(len, 4); + + skb = ath11k_htc_alloc_skb(ab, WMI_SKB_HEADROOM + round_len); + if (!skb) + return NULL; + + skb_reserve(skb, WMI_SKB_HEADROOM); + if (!IS_ALIGNED((unsigned long)skb->data, 4)) + ath11k_warn(ab, "unaligned WMI skb data\n"); + + skb_put(skb, round_len); + memset(skb->data, 0, round_len); + + return skb; +} + +int ath11k_wmi_mgmt_send(struct ath11k *ar, u32 vdev_id, u32 buf_id, + struct sk_buff *frame) +{ + struct ath11k_pdev_wmi *wmi = ar->wmi; + struct wmi_mgmt_send_cmd *cmd; + struct wmi_tlv *frame_tlv; + struct sk_buff *skb; + u32 buf_len; + int ret, len; + + buf_len = frame->len < WMI_MGMT_SEND_DOWNLD_LEN ? + frame->len : WMI_MGMT_SEND_DOWNLD_LEN; + + len = sizeof(*cmd) + sizeof(*frame_tlv) + roundup(buf_len, 4); + + skb = ath11k_wmi_alloc_skb(wmi->wmi_sc, len); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_mgmt_send_cmd *)skb->data; + cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_MGMT_TX_SEND_CMD) | + FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE); + cmd->vdev_id = vdev_id; + cmd->desc_id = buf_id; + cmd->chanfreq = 0; + cmd->paddr_lo = lower_32_bits(ATH11K_SKB_CB(frame)->paddr); + cmd->paddr_hi = upper_32_bits(ATH11K_SKB_CB(frame)->paddr); + cmd->frame_len = frame->len; + cmd->buf_len = buf_len; + cmd->tx_params_valid = 0; + + frame_tlv = (struct wmi_tlv *)(skb->data + sizeof(*cmd)); + frame_tlv->header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_ARRAY_BYTE) | + FIELD_PREP(WMI_TLV_LEN, buf_len); + + memcpy(frame_tlv->value, frame->data, buf_len); + + ath11k_ce_byte_swap(frame_tlv->value, buf_len); + + ret = ath11k_wmi_cmd_send(wmi, skb, WMI_MGMT_TX_SEND_CMDID); + if (ret) { + ath11k_warn(ar->ab, + "failed to submit WMI_MGMT_TX_SEND_CMDID cmd\n"); + dev_kfree_skb(skb); + } + + return ret; +} + +int ath11k_wmi_vdev_create(struct ath11k *ar, u8 *macaddr, + struct vdev_create_params *param) +{ + struct ath11k_pdev_wmi *wmi = ar->wmi; + struct wmi_vdev_create_cmd *cmd; + struct sk_buff *skb; + struct wmi_vdev_txrx_streams *txrx_streams; + struct wmi_tlv *tlv; + int ret, len; + void *ptr; + + /* It can be optimized my sending tx/rx chain configuration + * only for supported bands instead of always sending it for + * both the bands. + */ + len = sizeof(*cmd) + TLV_HDR_SIZE + + (WMI_NUM_SUPPORTED_BAND_MAX * sizeof(*txrx_streams)); + + skb = ath11k_wmi_alloc_skb(wmi->wmi_sc, len); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_vdev_create_cmd *)skb->data; + cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_VDEV_CREATE_CMD) | + FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE); + + cmd->vdev_id = param->if_id; + cmd->vdev_type = param->type; + cmd->vdev_subtype = param->subtype; + cmd->num_cfg_txrx_streams = WMI_NUM_SUPPORTED_BAND_MAX; + cmd->pdev_id = param->pdev_id; + ether_addr_copy(cmd->vdev_macaddr.addr, macaddr); + + ptr = skb->data + sizeof(*cmd); + len = WMI_NUM_SUPPORTED_BAND_MAX * sizeof(*txrx_streams); + + tlv = ptr; + tlv->header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_ARRAY_STRUCT) | + FIELD_PREP(WMI_TLV_LEN, len); + + ptr += TLV_HDR_SIZE; + txrx_streams = ptr; + len = sizeof(*txrx_streams); + txrx_streams->tlv_header = + FIELD_PREP(WMI_TLV_TAG, WMI_TAG_VDEV_TXRX_STREAMS) | + FIELD_PREP(WMI_TLV_LEN, len - TLV_HDR_SIZE); + txrx_streams->band = WMI_TPC_CHAINMASK_CONFIG_BAND_2G; + txrx_streams->supported_tx_streams = + param->chains[NL80211_BAND_2GHZ].tx; + txrx_streams->supported_rx_streams = + param->chains[NL80211_BAND_2GHZ].rx; + + txrx_streams++; + txrx_streams->tlv_header = + FIELD_PREP(WMI_TLV_TAG, WMI_TAG_VDEV_TXRX_STREAMS) | + FIELD_PREP(WMI_TLV_LEN, len - TLV_HDR_SIZE); + txrx_streams->band = WMI_TPC_CHAINMASK_CONFIG_BAND_5G; + txrx_streams->supported_tx_streams = + param->chains[NL80211_BAND_5GHZ].tx; + txrx_streams->supported_rx_streams = + param->chains[NL80211_BAND_5GHZ].rx; + + ret = ath11k_wmi_cmd_send(wmi, skb, WMI_VDEV_CREATE_CMDID); + if (ret) { + ath11k_warn(ar->ab, + "failed to submit WMI_VDEV_CREATE_CMDID\n"); + dev_kfree_skb(skb); + } + + ath11k_dbg(ar->ab, ATH11K_DBG_WMI, + "WMI vdev create: id %d type %d subtype %d macaddr %pM pdevid %d\n", + param->if_id, param->type, param->subtype, + macaddr, param->pdev_id); + + return ret; +} + +int ath11k_wmi_vdev_delete(struct ath11k *ar, u8 vdev_id) +{ + struct ath11k_pdev_wmi *wmi = ar->wmi; + struct wmi_vdev_delete_cmd *cmd; + struct sk_buff *skb; + int ret; + + skb = ath11k_wmi_alloc_skb(wmi->wmi_sc, sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_vdev_delete_cmd *)skb->data; + cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_VDEV_DELETE_CMD) | + FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE); + cmd->vdev_id = vdev_id; + + ret = ath11k_wmi_cmd_send(wmi, skb, WMI_VDEV_DELETE_CMDID); + if (ret) { + ath11k_warn(ar->ab, "failed to submit WMI_VDEV_DELETE_CMDID\n"); + dev_kfree_skb(skb); + } + + ath11k_dbg(ar->ab, ATH11K_DBG_WMI, "WMI vdev delete id %d\n", vdev_id); + + return ret; +} + +int ath11k_wmi_vdev_stop(struct ath11k *ar, u8 vdev_id) +{ + struct ath11k_pdev_wmi *wmi = ar->wmi; + struct wmi_vdev_stop_cmd *cmd; + struct sk_buff *skb; + int ret; + + skb = ath11k_wmi_alloc_skb(wmi->wmi_sc, sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_vdev_stop_cmd *)skb->data; + + cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_VDEV_STOP_CMD) | + FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE); + cmd->vdev_id = vdev_id; + + ret = ath11k_wmi_cmd_send(wmi, skb, WMI_VDEV_STOP_CMDID); + if (ret) { + ath11k_warn(ar->ab, "failed to submit WMI_VDEV_STOP cmd\n"); + dev_kfree_skb(skb); + } + + ath11k_dbg(ar->ab, ATH11K_DBG_WMI, "WMI vdev stop id 0x%x\n", vdev_id); + + return ret; +} + +int ath11k_wmi_vdev_down(struct ath11k *ar, u8 vdev_id) +{ + struct ath11k_pdev_wmi *wmi = ar->wmi; + struct wmi_vdev_down_cmd *cmd; + struct sk_buff *skb; + int ret; + + skb = ath11k_wmi_alloc_skb(wmi->wmi_sc, sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_vdev_down_cmd *)skb->data; + + cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_VDEV_DOWN_CMD) | + FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE); + cmd->vdev_id = vdev_id; + + ret = ath11k_wmi_cmd_send(wmi, skb, WMI_VDEV_DOWN_CMDID); + if (ret) { + ath11k_warn(ar->ab, "failed to submit WMI_VDEV_DOWN cmd\n"); + dev_kfree_skb(skb); + } + + ath11k_dbg(ar->ab, ATH11K_DBG_WMI, "WMI vdev down id 0x%x\n", vdev_id); + + return ret; +} + +static void ath11k_wmi_put_wmi_channel(struct wmi_channel *chan, + struct wmi_vdev_start_req_arg *arg) +{ + memset(chan, 0, sizeof(*chan)); + + chan->mhz = arg->channel.freq; + chan->band_center_freq1 = arg->channel.band_center_freq1; + if (arg->channel.mode == MODE_11AC_VHT80_80) + chan->band_center_freq2 = arg->channel.band_center_freq2; + else + chan->band_center_freq2 = 0; + + chan->info |= FIELD_PREP(WMI_CHAN_INFO_MODE, arg->channel.mode); + if (arg->channel.passive) + chan->info |= WMI_CHAN_INFO_PASSIVE; + if (arg->channel.allow_ibss) + chan->info |= WMI_CHAN_INFO_ADHOC_ALLOWED; + if (arg->channel.allow_ht) + chan->info |= WMI_CHAN_INFO_ALLOW_HT; + if (arg->channel.allow_vht) + chan->info |= WMI_CHAN_INFO_ALLOW_VHT; + if (arg->channel.ht40plus) + chan->info |= WMI_CHAN_INFO_HT40_PLUS; + if (arg->channel.chan_radar) + chan->info |= WMI_CHAN_INFO_DFS; + if (arg->channel.freq2_radar) + chan->info |= WMI_CHAN_INFO_DFS_FREQ2; + + chan->reg_info_1 = FIELD_PREP(WMI_CHAN_REG_INFO1_MAX_PWR, + arg->channel.max_power) | + FIELD_PREP(WMI_CHAN_REG_INFO1_MAX_REG_PWR, + arg->channel.max_reg_power); + + chan->reg_info_2 = FIELD_PREP(WMI_CHAN_REG_INFO2_ANT_MAX, + arg->channel.max_antenna_gain) | + FIELD_PREP(WMI_CHAN_REG_INFO2_MAX_TX_PWR, + arg->channel.max_power); +} + +int ath11k_wmi_vdev_start(struct ath11k *ar, struct wmi_vdev_start_req_arg *arg, + bool restart) +{ + struct ath11k_pdev_wmi *wmi = ar->wmi; + struct wmi_vdev_start_request_cmd *cmd; + struct sk_buff *skb; + struct wmi_channel *chan; + struct wmi_tlv *tlv; + void *ptr; + int ret, len; + + if (WARN_ON(arg->ssid_len > sizeof(cmd->ssid.ssid))) + return -EINVAL; + + len = sizeof(*cmd) + sizeof(*chan) + TLV_HDR_SIZE; + + skb = ath11k_wmi_alloc_skb(wmi->wmi_sc, len); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_vdev_start_request_cmd *)skb->data; + cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, + WMI_TAG_VDEV_START_REQUEST_CMD) | + FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE); + cmd->vdev_id = arg->vdev_id; + cmd->beacon_interval = arg->bcn_intval; + cmd->bcn_tx_rate = arg->bcn_tx_rate; + cmd->dtim_period = arg->dtim_period; + cmd->num_noa_descriptors = arg->num_noa_descriptors; + cmd->preferred_rx_streams = arg->pref_rx_streams; + cmd->preferred_tx_streams = arg->pref_tx_streams; + cmd->cac_duration_ms = arg->cac_duration_ms; + cmd->regdomain = arg->regdomain; + cmd->he_ops = arg->he_ops; + + if (!restart) { + if (arg->ssid) { + cmd->ssid.ssid_len = arg->ssid_len; + memcpy(cmd->ssid.ssid, arg->ssid, arg->ssid_len); + } + if (arg->hidden_ssid) + cmd->flags |= WMI_VDEV_START_HIDDEN_SSID; + if (arg->pmf_enabled) + cmd->flags |= WMI_VDEV_START_PMF_ENABLED; + } + + cmd->flags |= WMI_VDEV_START_LDPC_RX_ENABLED; + + ptr = skb->data + sizeof(*cmd); + chan = ptr; + + ath11k_wmi_put_wmi_channel(chan, arg); + + chan->tlv_header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_CHANNEL) | + FIELD_PREP(WMI_TLV_LEN, + sizeof(*chan) - TLV_HDR_SIZE); + ptr += sizeof(*chan); + + tlv = ptr; + tlv->header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_ARRAY_STRUCT) | + FIELD_PREP(WMI_TLV_LEN, 0); + + /* Note: This is a nested TLV containing: + * [wmi_tlv][wmi_p2p_noa_descriptor][wmi_tlv].. + */ + + ptr += sizeof(*tlv); + + if (restart) + ret = ath11k_wmi_cmd_send(wmi, skb, + WMI_VDEV_RESTART_REQUEST_CMDID); + else + ret = ath11k_wmi_cmd_send(wmi, skb, + WMI_VDEV_START_REQUEST_CMDID); + if (ret) { + ath11k_warn(ar->ab, "failed to submit vdev_%s cmd\n", + restart ? "restart" : "start"); + dev_kfree_skb(skb); + } + + ath11k_dbg(ar->ab, ATH11K_DBG_WMI, "vdev %s id 0x%x freq 0x%x mode 0x%x\n", + restart ? "restart" : "start", arg->vdev_id, + arg->channel.freq, arg->channel.mode); + + return ret; +} + +int ath11k_wmi_vdev_up(struct ath11k *ar, u32 vdev_id, u32 aid, const u8 *bssid) +{ + struct ath11k_pdev_wmi *wmi = ar->wmi; + struct wmi_vdev_up_cmd *cmd; + struct sk_buff *skb; + int ret; + + skb = ath11k_wmi_alloc_skb(wmi->wmi_sc, sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_vdev_up_cmd *)skb->data; + + cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_VDEV_UP_CMD) | + FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE); + cmd->vdev_id = vdev_id; + cmd->vdev_assoc_id = aid; + + ether_addr_copy(cmd->vdev_bssid.addr, bssid); + + ret = ath11k_wmi_cmd_send(wmi, skb, WMI_VDEV_UP_CMDID); + if (ret) { + ath11k_warn(ar->ab, "failed to submit WMI_VDEV_UP cmd\n"); + dev_kfree_skb(skb); + } + + ath11k_dbg(ar->ab, ATH11K_DBG_WMI, + "WMI mgmt vdev up id 0x%x assoc id %d bssid %pM\n", + vdev_id, aid, bssid); + + return ret; +} + +int ath11k_wmi_send_peer_create_cmd(struct ath11k *ar, + struct peer_create_params *param) +{ + struct ath11k_pdev_wmi *wmi = ar->wmi; + struct wmi_peer_create_cmd *cmd; + struct sk_buff *skb; + int ret; + + skb = ath11k_wmi_alloc_skb(wmi->wmi_sc, sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_peer_create_cmd *)skb->data; + cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_PEER_CREATE_CMD) | + FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE); + + ether_addr_copy(cmd->peer_macaddr.addr, param->peer_addr); + cmd->peer_type = param->peer_type; + cmd->vdev_id = param->vdev_id; + + ret = ath11k_wmi_cmd_send(wmi, skb, WMI_PEER_CREATE_CMDID); + if (ret) { + ath11k_warn(ar->ab, "failed to submit WMI_PEER_CREATE cmd\n"); + dev_kfree_skb(skb); + } + + ath11k_dbg(ar->ab, ATH11K_DBG_WMI, + "WMI peer create vdev_id %d peer_addr %pM\n", + param->vdev_id, param->peer_addr); + + return ret; +} + +int ath11k_wmi_send_peer_delete_cmd(struct ath11k *ar, + const u8 *peer_addr, u8 vdev_id) +{ + struct ath11k_pdev_wmi *wmi = ar->wmi; + struct wmi_peer_delete_cmd *cmd; + struct sk_buff *skb; + int ret; + + skb = ath11k_wmi_alloc_skb(wmi->wmi_sc, sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_peer_delete_cmd *)skb->data; + cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_PEER_DELETE_CMD) | + FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE); + + ether_addr_copy(cmd->peer_macaddr.addr, peer_addr); + cmd->vdev_id = vdev_id; + + ath11k_dbg(ar->ab, ATH11K_DBG_WMI, + "WMI peer delete vdev_id %d peer_addr %pM\n", + vdev_id, peer_addr); + + ret = ath11k_wmi_cmd_send(wmi, skb, WMI_PEER_DELETE_CMDID); + if (ret) { + ath11k_warn(ar->ab, "failed to send WMI_PEER_DELETE cmd\n"); + dev_kfree_skb(skb); + } + + return ret; +} + +int ath11k_wmi_send_pdev_set_regdomain(struct ath11k *ar, + struct pdev_set_regdomain_params *param) +{ + struct ath11k_pdev_wmi *wmi = ar->wmi; + struct wmi_pdev_set_regdomain_cmd *cmd; + struct sk_buff *skb; + int ret; + + skb = ath11k_wmi_alloc_skb(wmi->wmi_sc, sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_pdev_set_regdomain_cmd *)skb->data; + cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, + WMI_TAG_PDEV_SET_REGDOMAIN_CMD) | + FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE); + + cmd->reg_domain = param->current_rd_in_use; + cmd->reg_domain_2g = param->current_rd_2g; + cmd->reg_domain_5g = param->current_rd_5g; + cmd->conformance_test_limit_2g = param->ctl_2g; + cmd->conformance_test_limit_5g = param->ctl_5g; + cmd->dfs_domain = param->dfs_domain; + cmd->pdev_id = param->pdev_id; + + ath11k_dbg(ar->ab, ATH11K_DBG_WMI, + "WMI pdev regd rd %d rd2g %d rd5g %d domain %d pdev id %d\n", + param->current_rd_in_use, param->current_rd_2g, + param->current_rd_5g, param->dfs_domain, param->pdev_id); + + ret = ath11k_wmi_cmd_send(wmi, skb, WMI_PDEV_SET_REGDOMAIN_CMDID); + if (ret) { + ath11k_warn(ar->ab, + "failed to send WMI_PDEV_SET_REGDOMAIN cmd\n"); + dev_kfree_skb(skb); + } + + return ret; +} + +int ath11k_wmi_set_peer_param(struct ath11k *ar, const u8 *peer_addr, + u32 vdev_id, u32 param_id, u32 param_val) +{ + struct ath11k_pdev_wmi *wmi = ar->wmi; + struct wmi_peer_set_param_cmd *cmd; + struct sk_buff *skb; + int ret; + + skb = ath11k_wmi_alloc_skb(wmi->wmi_sc, sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_peer_set_param_cmd *)skb->data; + cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_PEER_SET_PARAM_CMD) | + FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE); + ether_addr_copy(cmd->peer_macaddr.addr, peer_addr); + cmd->vdev_id = vdev_id; + cmd->param_id = param_id; + cmd->param_value = param_val; + + ret = ath11k_wmi_cmd_send(wmi, skb, WMI_PEER_SET_PARAM_CMDID); + if (ret) { + ath11k_warn(ar->ab, "failed to send WMI_PEER_SET_PARAM cmd\n"); + dev_kfree_skb(skb); + } + + ath11k_dbg(ar->ab, ATH11K_DBG_WMI, + "WMI vdev %d peer 0x%pM set param %d value %d\n", + vdev_id, peer_addr, param_id, param_val); + + return ret; +} + +int ath11k_wmi_send_peer_flush_tids_cmd(struct ath11k *ar, + u8 peer_addr[ETH_ALEN], + struct peer_flush_params *param) +{ + struct ath11k_pdev_wmi *wmi = ar->wmi; + struct wmi_peer_flush_tids_cmd *cmd; + struct sk_buff *skb; + int ret; + + skb = ath11k_wmi_alloc_skb(wmi->wmi_sc, sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_peer_flush_tids_cmd *)skb->data; + cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_PEER_FLUSH_TIDS_CMD) | + FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE); + + ether_addr_copy(cmd->peer_macaddr.addr, peer_addr); + cmd->peer_tid_bitmap = param->peer_tid_bitmap; + cmd->vdev_id = param->vdev_id; + + ret = ath11k_wmi_cmd_send(wmi, skb, WMI_PEER_FLUSH_TIDS_CMDID); + if (ret) { + ath11k_warn(ar->ab, + "failed to send WMI_PEER_FLUSH_TIDS cmd\n"); + dev_kfree_skb(skb); + } + + ath11k_dbg(ar->ab, ATH11K_DBG_WMI, + "WMI peer flush vdev_id %d peer_addr %pM tids %08x\n", + param->vdev_id, peer_addr, param->peer_tid_bitmap); + + return ret; +} + +int ath11k_wmi_peer_rx_reorder_queue_setup(struct ath11k *ar, + int vdev_id, const u8 *addr, + dma_addr_t paddr, u8 tid, + u8 ba_window_size_valid, + u32 ba_window_size) +{ + struct wmi_peer_reorder_queue_setup_cmd *cmd; + struct sk_buff *skb; + int ret; + + skb = ath11k_wmi_alloc_skb(ar->wmi->wmi_sc, sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_peer_reorder_queue_setup_cmd *)skb->data; + cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, + WMI_TAG_REORDER_QUEUE_SETUP_CMD) | + FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE); + + ether_addr_copy(cmd->peer_macaddr.addr, addr); + cmd->vdev_id = vdev_id; + cmd->tid = tid; + cmd->queue_ptr_lo = lower_32_bits(paddr); + cmd->queue_ptr_hi = upper_32_bits(paddr); + cmd->queue_no = tid; + cmd->ba_window_size_valid = ba_window_size_valid; + cmd->ba_window_size = ba_window_size; + + ret = ath11k_wmi_cmd_send(ar->wmi, skb, + WMI_PEER_REORDER_QUEUE_SETUP_CMDID); + if (ret) { + ath11k_warn(ar->ab, + "failed to send WMI_PEER_REORDER_QUEUE_SETUP\n"); + dev_kfree_skb(skb); + } + + ath11k_dbg(ar->ab, ATH11K_DBG_WMI, + "wmi rx reorder queue setup addr %pM vdev_id %d tid %d\n", + addr, vdev_id, tid); + + return ret; +} + +int +ath11k_wmi_rx_reord_queue_remove(struct ath11k *ar, + struct rx_reorder_queue_remove_params *param) +{ + struct ath11k_pdev_wmi *wmi = ar->wmi; + struct wmi_peer_reorder_queue_remove_cmd *cmd; + struct sk_buff *skb; + int ret; + + skb = ath11k_wmi_alloc_skb(wmi->wmi_sc, sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_peer_reorder_queue_remove_cmd *)skb->data; + cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, + WMI_TAG_REORDER_QUEUE_REMOVE_CMD) | + FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE); + + ether_addr_copy(cmd->peer_macaddr.addr, param->peer_macaddr); + cmd->vdev_id = param->vdev_id; + cmd->tid_mask = param->peer_tid_bitmap; + + ath11k_dbg(ar->ab, ATH11K_DBG_WMI, + "%s: peer_macaddr %pM vdev_id %d, tid_map %d", __func__, + param->peer_macaddr, param->vdev_id, param->peer_tid_bitmap); + + ret = ath11k_wmi_cmd_send(wmi, skb, + WMI_PEER_REORDER_QUEUE_REMOVE_CMDID); + if (ret) { + ath11k_warn(ar->ab, + "failed to send WMI_PEER_REORDER_QUEUE_REMOVE_CMDID"); + dev_kfree_skb(skb); + } + + return ret; +} + +int ath11k_wmi_pdev_set_param(struct ath11k *ar, u32 param_id, + u32 param_value, u8 pdev_id) +{ + struct ath11k_pdev_wmi *wmi = ar->wmi; + struct wmi_pdev_set_param_cmd *cmd; + struct sk_buff *skb; + int ret; + + skb = ath11k_wmi_alloc_skb(wmi->wmi_sc, sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_pdev_set_param_cmd *)skb->data; + cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_PDEV_SET_PARAM_CMD) | + FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE); + cmd->pdev_id = pdev_id; + cmd->param_id = param_id; + cmd->param_value = param_value; + + ret = ath11k_wmi_cmd_send(wmi, skb, WMI_PDEV_SET_PARAM_CMDID); + if (ret) { + ath11k_warn(ar->ab, "failed to send WMI_PDEV_SET_PARAM cmd\n"); + dev_kfree_skb(skb); + } + + ath11k_dbg(ar->ab, ATH11K_DBG_WMI, + "WMI pdev set param %d pdev id %d value %d\n", + param_id, pdev_id, param_value); + + return ret; +} + +int ath11k_wmi_pdev_suspend(struct ath11k *ar, u32 suspend_opt, + u32 pdev_id) +{ + struct ath11k_pdev_wmi *wmi = ar->wmi; + struct wmi_pdev_suspend_cmd *cmd; + struct sk_buff *skb; + int ret; + + skb = ath11k_wmi_alloc_skb(wmi->wmi_sc, sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_pdev_suspend_cmd *)skb->data; + + cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_PDEV_SUSPEND_CMD) | + FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE); + + cmd->suspend_opt = suspend_opt; + cmd->pdev_id = pdev_id; + + ret = ath11k_wmi_cmd_send(wmi, skb, WMI_PDEV_SUSPEND_CMDID); + if (ret) { + ath11k_warn(ar->ab, "failed to send WMI_PDEV_SUSPEND cmd\n"); + dev_kfree_skb(skb); + } + + ath11k_dbg(ar->ab, ATH11K_DBG_WMI, + "WMI pdev suspend pdev_id %d\n", pdev_id); + + return ret; +} + +int ath11k_wmi_pdev_resume(struct ath11k *ar, u32 pdev_id) +{ + struct ath11k_pdev_wmi *wmi = ar->wmi; + struct wmi_pdev_resume_cmd *cmd; + struct sk_buff *skb; + int ret; + + skb = ath11k_wmi_alloc_skb(wmi->wmi_sc, sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_pdev_resume_cmd *)skb->data; + + cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_PDEV_RESUME_CMD) | + FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE); + cmd->pdev_id = pdev_id; + + ath11k_dbg(ar->ab, ATH11K_DBG_WMI, + "WMI pdev resume pdev id %d\n", pdev_id); + + ret = ath11k_wmi_cmd_send(wmi, skb, WMI_PDEV_RESUME_CMDID); + if (ret) { + ath11k_warn(ar->ab, "failed to send WMI_PDEV_RESUME cmd\n"); + dev_kfree_skb(skb); + } + + return ret; +} + +/* TODO FW Support for the cmd is not available yet. + * Can be tested once the command and corresponding + * event is implemented in FW + */ +int ath11k_wmi_pdev_bss_chan_info_request(struct ath11k *ar, + enum wmi_bss_chan_info_req_type type) +{ + struct ath11k_pdev_wmi *wmi = ar->wmi; + struct wmi_pdev_bss_chan_info_req_cmd *cmd; + struct sk_buff *skb; + int ret; + + skb = ath11k_wmi_alloc_skb(wmi->wmi_sc, sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_pdev_bss_chan_info_req_cmd *)skb->data; + + cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, + WMI_TAG_PDEV_BSS_CHAN_INFO_REQUEST) | + FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE); + cmd->req_type = type; + + ath11k_dbg(ar->ab, ATH11K_DBG_WMI, + "WMI bss chan info req type %d\n", type); + + ret = ath11k_wmi_cmd_send(wmi, skb, + WMI_PDEV_BSS_CHAN_INFO_REQUEST_CMDID); + if (ret) { + ath11k_warn(ar->ab, + "failed to send WMI_PDEV_BSS_CHAN_INFO_REQUEST cmd\n"); + dev_kfree_skb(skb); + } + + return ret; +} + +int ath11k_wmi_send_set_ap_ps_param_cmd(struct ath11k *ar, u8 *peer_addr, + struct ap_ps_params *param) +{ + struct ath11k_pdev_wmi *wmi = ar->wmi; + struct wmi_ap_ps_peer_cmd *cmd; + struct sk_buff *skb; + int ret; + + skb = ath11k_wmi_alloc_skb(wmi->wmi_sc, sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_ap_ps_peer_cmd *)skb->data; + cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_AP_PS_PEER_CMD) | + FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE); + + cmd->vdev_id = param->vdev_id; + ether_addr_copy(cmd->peer_macaddr.addr, peer_addr); + cmd->param = param->param; + cmd->value = param->value; + + ret = ath11k_wmi_cmd_send(wmi, skb, WMI_AP_PS_PEER_PARAM_CMDID); + if (ret) { + ath11k_warn(ar->ab, + "failed to send WMI_AP_PS_PEER_PARAM_CMDID\n"); + dev_kfree_skb(skb); + } + + ath11k_dbg(ar->ab, ATH11K_DBG_WMI, + "WMI set ap ps vdev id %d peer %pM param %d value %d\n", + param->vdev_id, peer_addr, param->param, param->value); + + return ret; +} + +int ath11k_wmi_set_sta_ps_param(struct ath11k *ar, u32 vdev_id, + u32 param, u32 param_value) +{ + struct ath11k_pdev_wmi *wmi = ar->wmi; + struct wmi_sta_powersave_param_cmd *cmd; + struct sk_buff *skb; + int ret; + + skb = ath11k_wmi_alloc_skb(wmi->wmi_sc, sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_sta_powersave_param_cmd *)skb->data; + cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, + WMI_TAG_STA_POWERSAVE_PARAM_CMD) | + FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE); + + cmd->vdev_id = vdev_id; + cmd->param = param; + cmd->value = param_value; + + ath11k_dbg(ar->ab, ATH11K_DBG_WMI, + "WMI set sta ps vdev_id %d param %d value %d\n", + vdev_id, param, param_value); + + ret = ath11k_wmi_cmd_send(wmi, skb, WMI_STA_POWERSAVE_PARAM_CMDID); + if (ret) { + ath11k_warn(ar->ab, "failed to send WMI_STA_POWERSAVE_PARAM_CMDID"); + dev_kfree_skb(skb); + } + + return ret; +} + +int ath11k_wmi_force_fw_hang_cmd(struct ath11k *ar, u32 type, u32 delay_time_ms) +{ + struct ath11k_pdev_wmi *wmi = ar->wmi; + struct wmi_force_fw_hang_cmd *cmd; + struct sk_buff *skb; + int ret, len; + + len = sizeof(*cmd); + + skb = ath11k_wmi_alloc_skb(wmi->wmi_sc, len); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_force_fw_hang_cmd *)skb->data; + cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_FORCE_FW_HANG_CMD) | + FIELD_PREP(WMI_TLV_LEN, len - TLV_HDR_SIZE); + + cmd->type = type; + cmd->delay_time_ms = delay_time_ms; + + ret = ath11k_wmi_cmd_send(wmi, skb, WMI_FORCE_FW_HANG_CMDID); + + if (ret) { + ath11k_warn(ar->ab, "Failed to send WMI_FORCE_FW_HANG_CMDID"); + dev_kfree_skb(skb); + } + return ret; +} + +int ath11k_wmi_vdev_set_param_cmd(struct ath11k *ar, u32 vdev_id, + u32 param_id, u32 param_value) +{ + struct ath11k_pdev_wmi *wmi = ar->wmi; + struct wmi_vdev_set_param_cmd *cmd; + struct sk_buff *skb; + int ret; + + skb = ath11k_wmi_alloc_skb(wmi->wmi_sc, sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_vdev_set_param_cmd *)skb->data; + cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_VDEV_SET_PARAM_CMD) | + FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE); + + cmd->vdev_id = vdev_id; + cmd->param_id = param_id; + cmd->param_value = param_value; + + ret = ath11k_wmi_cmd_send(wmi, skb, WMI_VDEV_SET_PARAM_CMDID); + if (ret) { + ath11k_warn(ar->ab, + "failed to send WMI_VDEV_SET_PARAM_CMDID\n"); + dev_kfree_skb(skb); + } + + ath11k_dbg(ar->ab, ATH11K_DBG_WMI, + "WMI vdev id 0x%x set param %d value %d\n", + vdev_id, param_id, param_value); + + return ret; +} + +int ath11k_wmi_send_stats_request_cmd(struct ath11k *ar, + struct stats_request_params *param) +{ + struct ath11k_pdev_wmi *wmi = ar->wmi; + struct wmi_request_stats_cmd *cmd; + struct sk_buff *skb; + int ret; + + skb = ath11k_wmi_alloc_skb(wmi->wmi_sc, sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_request_stats_cmd *)skb->data; + cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_REQUEST_STATS_CMD) | + FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE); + + cmd->stats_id = param->stats_id; + cmd->vdev_id = param->vdev_id; + cmd->pdev_id = param->pdev_id; + + ret = ath11k_wmi_cmd_send(wmi, skb, WMI_REQUEST_STATS_CMDID); + if (ret) { + ath11k_warn(ar->ab, "failed to send WMI_REQUEST_STATS cmd\n"); + dev_kfree_skb(skb); + } + + ath11k_dbg(ar->ab, ATH11K_DBG_WMI, + "WMI request stats 0x%x vdev id %d pdev id %d\n", + param->stats_id, param->vdev_id, param->pdev_id); + + return ret; +} + +int ath11k_wmi_send_bcn_offload_control_cmd(struct ath11k *ar, + u32 vdev_id, u32 bcn_ctrl_op) +{ + struct ath11k_pdev_wmi *wmi = ar->wmi; + struct wmi_bcn_offload_ctrl_cmd *cmd; + struct sk_buff *skb; + int ret; + + skb = ath11k_wmi_alloc_skb(wmi->wmi_sc, sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_bcn_offload_ctrl_cmd *)skb->data; + cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, + WMI_TAG_BCN_OFFLOAD_CTRL_CMD) | + FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE); + + cmd->vdev_id = vdev_id; + cmd->bcn_ctrl_op = bcn_ctrl_op; + + ath11k_dbg(ar->ab, ATH11K_DBG_WMI, + "WMI bcn ctrl offload vdev id %d ctrl_op %d\n", + vdev_id, bcn_ctrl_op); + + ret = ath11k_wmi_cmd_send(wmi, skb, WMI_BCN_OFFLOAD_CTRL_CMDID); + if (ret) { + ath11k_warn(ar->ab, + "failed to send WMI_BCN_OFFLOAD_CTRL_CMDID\n"); + dev_kfree_skb(skb); + } + + return ret; +} + +int ath11k_wmi_bcn_tmpl(struct ath11k *ar, u32 vdev_id, + struct ieee80211_mutable_offsets *offs, + struct sk_buff *bcn) +{ + struct ath11k_pdev_wmi *wmi = ar->wmi; + struct wmi_bcn_tmpl_cmd *cmd; + struct wmi_bcn_prb_info *bcn_prb_info; + struct wmi_tlv *tlv; + struct sk_buff *skb; + void *ptr; + int ret, len; + size_t aligned_len = roundup(bcn->len, 4); + + len = sizeof(*cmd) + sizeof(*bcn_prb_info) + TLV_HDR_SIZE + aligned_len; + + skb = ath11k_wmi_alloc_skb(wmi->wmi_sc, len); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_bcn_tmpl_cmd *)skb->data; + cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_BCN_TMPL_CMD) | + FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE); + cmd->vdev_id = vdev_id; + cmd->tim_ie_offset = offs->tim_offset; + cmd->csa_switch_count_offset = offs->csa_counter_offs[0]; + cmd->ext_csa_switch_count_offset = offs->csa_counter_offs[1]; + cmd->buf_len = bcn->len; + + ptr = skb->data + sizeof(*cmd); + + bcn_prb_info = ptr; + len = sizeof(*bcn_prb_info); + bcn_prb_info->tlv_header = FIELD_PREP(WMI_TLV_TAG, + WMI_TAG_BCN_PRB_INFO) | + FIELD_PREP(WMI_TLV_LEN, len - TLV_HDR_SIZE); + bcn_prb_info->caps = 0; + bcn_prb_info->erp = 0; + + ptr += sizeof(*bcn_prb_info); + + tlv = ptr; + tlv->header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_ARRAY_BYTE) | + FIELD_PREP(WMI_TLV_LEN, aligned_len); + memcpy(tlv->value, bcn->data, bcn->len); + + ret = ath11k_wmi_cmd_send(wmi, skb, WMI_BCN_TMPL_CMDID); + if (ret) { + ath11k_warn(ar->ab, "failed to send WMI_BCN_TMPL_CMDID\n"); + dev_kfree_skb(skb); + } + + return ret; +} + +int ath11k_wmi_vdev_install_key(struct ath11k *ar, + struct wmi_vdev_install_key_arg *arg) +{ + struct ath11k_pdev_wmi *wmi = ar->wmi; + struct wmi_vdev_install_key_cmd *cmd; + struct wmi_tlv *tlv; + struct sk_buff *skb; + int ret, len; + int key_len_aligned = roundup(arg->key_len, sizeof(uint32_t)); + + len = sizeof(*cmd) + TLV_HDR_SIZE + key_len_aligned; + + skb = ath11k_wmi_alloc_skb(wmi->wmi_sc, len); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_vdev_install_key_cmd *)skb->data; + cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_VDEV_INSTALL_KEY_CMD) | + FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE); + cmd->vdev_id = arg->vdev_id; + ether_addr_copy(cmd->peer_macaddr.addr, arg->macaddr); + cmd->key_idx = arg->key_idx; + cmd->key_flags = arg->key_flags; + cmd->key_cipher = arg->key_cipher; + cmd->key_len = arg->key_len; + cmd->key_txmic_len = arg->key_txmic_len; + cmd->key_rxmic_len = arg->key_rxmic_len; + + if (arg->key_rsc_counter) + memcpy(&cmd->key_rsc_counter, &arg->key_rsc_counter, + sizeof(struct wmi_key_seq_counter)); + + tlv = (struct wmi_tlv *)(skb->data + sizeof(*cmd)); + tlv->header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_ARRAY_BYTE) | + FIELD_PREP(WMI_TLV_LEN, key_len_aligned); + memcpy(tlv->value, (u8 *)arg->key_data, key_len_aligned); + + ret = ath11k_wmi_cmd_send(wmi, skb, WMI_VDEV_INSTALL_KEY_CMDID); + if (ret) { + ath11k_warn(ar->ab, + "failed to send WMI_VDEV_INSTALL_KEY cmd\n"); + dev_kfree_skb(skb); + } + + ath11k_dbg(ar->ab, ATH11K_DBG_WMI, + "WMI vdev install key idx %d cipher %d len %d\n", + arg->key_idx, arg->key_cipher, arg->key_len); + + return ret; +} + +static inline void +ath11k_wmi_copy_peer_flags(struct wmi_peer_assoc_complete_cmd *cmd, + struct peer_assoc_params *param) +{ + cmd->peer_flags = 0; + + if (param->is_wme_set) { + if (param->qos_flag) + cmd->peer_flags |= WMI_PEER_QOS; + if (param->apsd_flag) + cmd->peer_flags |= WMI_PEER_APSD; + if (param->ht_flag) + cmd->peer_flags |= WMI_PEER_HT; + if (param->bw_40) + cmd->peer_flags |= WMI_PEER_40MHZ; + if (param->bw_80) + cmd->peer_flags |= WMI_PEER_80MHZ; + if (param->bw_160) + cmd->peer_flags |= WMI_PEER_160MHZ; + + /* Typically if STBC is enabled for VHT it should be enabled + * for HT as well + **/ + if (param->stbc_flag) + cmd->peer_flags |= WMI_PEER_STBC; + + /* Typically if LDPC is enabled for VHT it should be enabled + * for HT as well + **/ + if (param->ldpc_flag) + cmd->peer_flags |= WMI_PEER_LDPC; + + if (param->static_mimops_flag) + cmd->peer_flags |= WMI_PEER_STATIC_MIMOPS; + if (param->dynamic_mimops_flag) + cmd->peer_flags |= WMI_PEER_DYN_MIMOPS; + if (param->spatial_mux_flag) + cmd->peer_flags |= WMI_PEER_SPATIAL_MUX; + if (param->vht_flag) + cmd->peer_flags |= WMI_PEER_VHT; + if (param->he_flag) + cmd->peer_flags |= WMI_PEER_HE; + } + + /* Suppress authorization for all AUTH modes that need 4-way handshake + * (during re-association). + * Authorization will be done for these modes on key installation. + */ + if (param->auth_flag) + cmd->peer_flags |= WMI_PEER_AUTH; + if (param->need_ptk_4_way) + cmd->peer_flags |= WMI_PEER_NEED_PTK_4_WAY; + else + cmd->peer_flags &= ~WMI_PEER_NEED_PTK_4_WAY; + if (param->need_gtk_2_way) + cmd->peer_flags |= WMI_PEER_NEED_GTK_2_WAY; + /* safe mode bypass the 4-way handshake */ + if (param->safe_mode_enabled) + cmd->peer_flags &= ~(WMI_PEER_NEED_PTK_4_WAY | + WMI_PEER_NEED_GTK_2_WAY); + + if (param->is_pmf_enabled) + cmd->peer_flags |= WMI_PEER_PMF; + + /* Disable AMSDU for station transmit, if user configures it */ + /* Disable AMSDU for AP transmit to 11n Stations, if user configures + * it + * if (param->amsdu_disable) Add after FW support + **/ + + /* Target asserts if node is marked HT and all MCS is set to 0. + * Mark the node as non-HT if all the mcs rates are disabled through + * iwpriv + **/ + if (param->peer_ht_rates.num_rates == 0) + cmd->peer_flags &= ~WMI_PEER_HT; +} + +int ath11k_wmi_send_peer_assoc_cmd(struct ath11k *ar, + struct peer_assoc_params *param) +{ + struct ath11k_pdev_wmi *wmi = ar->wmi; + struct wmi_peer_assoc_complete_cmd *cmd; + struct wmi_vht_rate_set *mcs; + struct wmi_he_rate_set *he_mcs; + struct sk_buff *skb; + struct wmi_tlv *tlv; + void *ptr; + u32 peer_legacy_rates_align; + u32 peer_ht_rates_align; + int i, ret, len; + + peer_legacy_rates_align = roundup(param->peer_legacy_rates.num_rates, + sizeof(u32)); + peer_ht_rates_align = roundup(param->peer_ht_rates.num_rates, + sizeof(u32)); + + len = sizeof(*cmd) + + TLV_HDR_SIZE + (peer_legacy_rates_align * sizeof(u8)) + + TLV_HDR_SIZE + (peer_ht_rates_align * sizeof(u8)) + + sizeof(*mcs) + TLV_HDR_SIZE + + (sizeof(*he_mcs) * param->peer_he_mcs_count); + + skb = ath11k_wmi_alloc_skb(wmi->wmi_sc, len); + if (!skb) + return -ENOMEM; + + ptr = skb->data; + + cmd = ptr; + cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, + WMI_TAG_PEER_ASSOC_COMPLETE_CMD) | + FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE); + + cmd->vdev_id = param->vdev_id; + + cmd->peer_new_assoc = param->peer_new_assoc; + cmd->peer_associd = param->peer_associd; + + ath11k_wmi_copy_peer_flags(cmd, param); + + ether_addr_copy(cmd->peer_macaddr.addr, param->peer_mac); + + cmd->peer_rate_caps = param->peer_rate_caps; + cmd->peer_caps = param->peer_caps; + cmd->peer_listen_intval = param->peer_listen_intval; + cmd->peer_ht_caps = param->peer_ht_caps; + cmd->peer_max_mpdu = param->peer_max_mpdu; + cmd->peer_mpdu_density = param->peer_mpdu_density; + cmd->peer_vht_caps = param->peer_vht_caps; + cmd->peer_phymode = param->peer_phymode; + + /* Update 11ax capabilities */ + cmd->peer_he_cap_info = param->peer_he_cap_macinfo[0]; + cmd->peer_he_cap_info_ext = param->peer_he_cap_macinfo[1]; + cmd->peer_he_cap_info_internal = param->peer_he_cap_macinfo_internal; + cmd->peer_he_ops = param->peer_he_ops; + memcpy(&cmd->peer_he_cap_phy, ¶m->peer_he_cap_phyinfo, + sizeof(param->peer_he_cap_phyinfo)); + memcpy(&cmd->peer_ppet, ¶m->peer_ppet, + sizeof(param->peer_ppet)); + + /* Update peer legacy rate information */ + ptr += sizeof(*cmd); + + tlv = ptr; + tlv->header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_ARRAY_BYTE) | + FIELD_PREP(WMI_TLV_LEN, peer_legacy_rates_align); + + ptr += TLV_HDR_SIZE; + + cmd->num_peer_legacy_rates = param->peer_legacy_rates.num_rates; + memcpy(ptr, param->peer_legacy_rates.rates, + param->peer_legacy_rates.num_rates); + + /* Update peer HT rate information */ + ptr += peer_legacy_rates_align; + + tlv = ptr; + tlv->header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_ARRAY_BYTE) | + FIELD_PREP(WMI_TLV_LEN, peer_ht_rates_align); + ptr += TLV_HDR_SIZE; + cmd->num_peer_ht_rates = param->peer_ht_rates.num_rates; + memcpy(ptr, param->peer_ht_rates.rates, + param->peer_ht_rates.num_rates); + + /* VHT Rates */ + ptr += peer_ht_rates_align; + + mcs = ptr; + + mcs->tlv_header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_VHT_RATE_SET) | + FIELD_PREP(WMI_TLV_LEN, sizeof(*mcs) - TLV_HDR_SIZE); + + cmd->peer_nss = param->peer_nss; + + /* Update bandwidth-NSS mapping */ + cmd->peer_bw_rxnss_override = 0; + cmd->peer_bw_rxnss_override |= param->peer_bw_rxnss_override; + + if (param->vht_capable) { + mcs->rx_max_rate = param->rx_max_rate; + mcs->rx_mcs_set = param->rx_mcs_set; + mcs->tx_max_rate = param->tx_max_rate; + mcs->tx_mcs_set = param->tx_mcs_set; + } + + /* HE Rates */ + cmd->peer_he_mcs = param->peer_he_mcs_count; + + ptr += sizeof(*mcs); + + len = param->peer_he_mcs_count * sizeof(*he_mcs); + + tlv = ptr; + tlv->header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_ARRAY_STRUCT) | + FIELD_PREP(WMI_TLV_LEN, len); + ptr += TLV_HDR_SIZE; + + /* Loop through the HE rate set */ + for (i = 0; i < param->peer_he_mcs_count; i++) { + he_mcs = ptr; + he_mcs->tlv_header = FIELD_PREP(WMI_TLV_TAG, + WMI_TAG_HE_RATE_SET) | + FIELD_PREP(WMI_TLV_LEN, + sizeof(*he_mcs) - TLV_HDR_SIZE); + + he_mcs->rx_mcs_set = param->peer_he_rx_mcs_set[i]; + he_mcs->tx_mcs_set = param->peer_he_tx_mcs_set[i]; + ptr += sizeof(*he_mcs); + } + + ret = ath11k_wmi_cmd_send(wmi, skb, WMI_PEER_ASSOC_CMDID); + if (ret) { + ath11k_warn(ar->ab, + "failed to send WMI_PEER_ASSOC_CMDID\n"); + dev_kfree_skb(skb); + } + + ath11k_dbg(ar->ab, ATH11K_DBG_WMI, + "WMI peer assoc vdev id %d assoc id %d peer mac %pM\n", + param->vdev_id, param->peer_associd, param->peer_mac); + + return ret; +} + +void ath11k_wmi_start_scan_init(struct ath11k *ar, + struct scan_req_params *arg) +{ + /* setup commonly used values */ + arg->scan_req_id = 1; + arg->scan_priority = WMI_SCAN_PRIORITY_LOW; + arg->dwell_time_active = 50; + arg->dwell_time_active_2g = 0; + arg->dwell_time_passive = 150; + arg->min_rest_time = 50; + arg->max_rest_time = 500; + arg->repeat_probe_time = 0; + arg->probe_spacing_time = 0; + arg->idle_time = 0; + arg->max_scan_time = 20000; + arg->probe_delay = 5; + arg->notify_scan_events = WMI_SCAN_EVENT_STARTED | + WMI_SCAN_EVENT_COMPLETED | + WMI_SCAN_EVENT_BSS_CHANNEL | + WMI_SCAN_EVENT_FOREIGN_CHAN | + WMI_SCAN_EVENT_DEQUEUED; + arg->scan_flags |= WMI_SCAN_CHAN_STAT_EVENT; + arg->num_bssid = 1; +} + +static inline void +ath11k_wmi_copy_scan_event_cntrl_flags(struct wmi_start_scan_cmd *cmd, + struct scan_req_params *param) +{ + /* Scan events subscription */ + if (param->scan_ev_started) + cmd->notify_scan_events |= WMI_SCAN_EVENT_STARTED; + if (param->scan_ev_completed) + cmd->notify_scan_events |= WMI_SCAN_EVENT_COMPLETED; + if (param->scan_ev_bss_chan) + cmd->notify_scan_events |= WMI_SCAN_EVENT_BSS_CHANNEL; + if (param->scan_ev_foreign_chan) + cmd->notify_scan_events |= WMI_SCAN_EVENT_FOREIGN_CHAN; + if (param->scan_ev_dequeued) + cmd->notify_scan_events |= WMI_SCAN_EVENT_DEQUEUED; + if (param->scan_ev_preempted) + cmd->notify_scan_events |= WMI_SCAN_EVENT_PREEMPTED; + if (param->scan_ev_start_failed) + cmd->notify_scan_events |= WMI_SCAN_EVENT_START_FAILED; + if (param->scan_ev_restarted) + cmd->notify_scan_events |= WMI_SCAN_EVENT_RESTARTED; + if (param->scan_ev_foreign_chn_exit) + cmd->notify_scan_events |= WMI_SCAN_EVENT_FOREIGN_CHAN_EXIT; + if (param->scan_ev_suspended) + cmd->notify_scan_events |= WMI_SCAN_EVENT_SUSPENDED; + if (param->scan_ev_resumed) + cmd->notify_scan_events |= WMI_SCAN_EVENT_RESUMED; + + /** Set scan control flags */ + cmd->scan_ctrl_flags = 0; + if (param->scan_f_passive) + cmd->scan_ctrl_flags |= WMI_SCAN_FLAG_PASSIVE; + if (param->scan_f_strict_passive_pch) + cmd->scan_ctrl_flags |= WMI_SCAN_FLAG_STRICT_PASSIVE_ON_PCHN; + if (param->scan_f_promisc_mode) + cmd->scan_ctrl_flags |= WMI_SCAN_FILTER_PROMISCUOS; + if (param->scan_f_capture_phy_err) + cmd->scan_ctrl_flags |= WMI_SCAN_CAPTURE_PHY_ERROR; + if (param->scan_f_half_rate) + cmd->scan_ctrl_flags |= WMI_SCAN_FLAG_HALF_RATE_SUPPORT; + if (param->scan_f_quarter_rate) + cmd->scan_ctrl_flags |= WMI_SCAN_FLAG_QUARTER_RATE_SUPPORT; + if (param->scan_f_cck_rates) + cmd->scan_ctrl_flags |= WMI_SCAN_ADD_CCK_RATES; + if (param->scan_f_ofdm_rates) + cmd->scan_ctrl_flags |= WMI_SCAN_ADD_OFDM_RATES; + if (param->scan_f_chan_stat_evnt) + cmd->scan_ctrl_flags |= WMI_SCAN_CHAN_STAT_EVENT; + if (param->scan_f_filter_prb_req) + cmd->scan_ctrl_flags |= WMI_SCAN_FILTER_PROBE_REQ; + if (param->scan_f_bcast_probe) + cmd->scan_ctrl_flags |= WMI_SCAN_ADD_BCAST_PROBE_REQ; + if (param->scan_f_offchan_mgmt_tx) + cmd->scan_ctrl_flags |= WMI_SCAN_OFFCHAN_MGMT_TX; + if (param->scan_f_offchan_data_tx) + cmd->scan_ctrl_flags |= WMI_SCAN_OFFCHAN_DATA_TX; + if (param->scan_f_force_active_dfs_chn) + cmd->scan_ctrl_flags |= WMI_SCAN_FLAG_FORCE_ACTIVE_ON_DFS; + if (param->scan_f_add_tpc_ie_in_probe) + cmd->scan_ctrl_flags |= WMI_SCAN_ADD_TPC_IE_IN_PROBE_REQ; + if (param->scan_f_add_ds_ie_in_probe) + cmd->scan_ctrl_flags |= WMI_SCAN_ADD_DS_IE_IN_PROBE_REQ; + if (param->scan_f_add_spoofed_mac_in_probe) + cmd->scan_ctrl_flags |= WMI_SCAN_ADD_SPOOF_MAC_IN_PROBE_REQ; + if (param->scan_f_add_rand_seq_in_probe) + cmd->scan_ctrl_flags |= WMI_SCAN_RANDOM_SEQ_NO_IN_PROBE_REQ; + if (param->scan_f_en_ie_whitelist_in_probe) + cmd->scan_ctrl_flags |= + WMI_SCAN_ENABLE_IE_WHTELIST_IN_PROBE_REQ; + + /* for adaptive scan mode using 3 bits (21 - 23 bits) */ + WMI_SCAN_SET_DWELL_MODE(cmd->scan_ctrl_flags, + param->adaptive_dwell_time_mode); +} + +int ath11k_wmi_send_scan_start_cmd(struct ath11k *ar, + struct scan_req_params *params) +{ + struct ath11k_pdev_wmi *wmi = ar->wmi; + struct wmi_start_scan_cmd *cmd; + struct wmi_ssid *ssid = NULL; + struct wmi_mac_addr *bssid; + struct sk_buff *skb; + struct wmi_tlv *tlv; + void *ptr; + int i, ret, len; + u32 *tmp_ptr; + u8 extraie_len_with_pad = 0; + + len = sizeof(*cmd); + + len += TLV_HDR_SIZE; + if (params->num_chan) + len += params->num_chan * sizeof(u32); + + len += TLV_HDR_SIZE; + if (params->num_ssids) + len += params->num_ssids * sizeof(*ssid); + + len += TLV_HDR_SIZE; + if (params->num_bssid) + len += sizeof(*bssid) * params->num_bssid; + + len += TLV_HDR_SIZE; + if (params->extraie.len) + extraie_len_with_pad = + roundup(params->extraie.len, sizeof(u32)); + len += extraie_len_with_pad; + + skb = ath11k_wmi_alloc_skb(wmi->wmi_sc, len); + if (!skb) + return -ENOMEM; + + ptr = skb->data; + + cmd = ptr; + cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_START_SCAN_CMD) | + FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE); + + cmd->scan_id = params->scan_id; + cmd->scan_req_id = params->scan_req_id; + cmd->vdev_id = params->vdev_id; + cmd->scan_priority = params->scan_priority; + cmd->notify_scan_events = params->notify_scan_events; + + ath11k_wmi_copy_scan_event_cntrl_flags(cmd, params); + + cmd->dwell_time_active = params->dwell_time_active; + cmd->dwell_time_active_2g = params->dwell_time_active_2g; + cmd->dwell_time_passive = params->dwell_time_passive; + cmd->min_rest_time = params->min_rest_time; + cmd->max_rest_time = params->max_rest_time; + cmd->repeat_probe_time = params->repeat_probe_time; + cmd->probe_spacing_time = params->probe_spacing_time; + cmd->idle_time = params->idle_time; + cmd->max_scan_time = params->max_scan_time; + cmd->probe_delay = params->probe_delay; + cmd->burst_duration = params->burst_duration; + cmd->num_chan = params->num_chan; + cmd->num_bssid = params->num_bssid; + cmd->num_ssids = params->num_ssids; + cmd->ie_len = params->extraie.len; + cmd->n_probes = params->n_probes; + + ptr += sizeof(*cmd); + + len = params->num_chan * sizeof(u32); + + tlv = ptr; + tlv->header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_ARRAY_UINT32) | + FIELD_PREP(WMI_TLV_LEN, len); + ptr += TLV_HDR_SIZE; + tmp_ptr = (u32 *)ptr; + + for (i = 0; i < params->num_chan; ++i) + tmp_ptr[i] = params->chan_list[i]; + + ptr += len; + + len = params->num_ssids * sizeof(*ssid); + tlv = ptr; + tlv->header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_ARRAY_FIXED_STRUCT) | + FIELD_PREP(WMI_TLV_LEN, len); + + ptr += TLV_HDR_SIZE; + + if (params->num_ssids) { + ssid = ptr; + for (i = 0; i < params->num_ssids; ++i) { + ssid->ssid_len = params->ssid[i].length; + memcpy(ssid->ssid, params->ssid[i].ssid, + params->ssid[i].length); + ssid++; + } + } + + ptr += (params->num_ssids * sizeof(*ssid)); + len = params->num_bssid * sizeof(*bssid); + tlv = ptr; + tlv->header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_ARRAY_FIXED_STRUCT) | + FIELD_PREP(WMI_TLV_LEN, len); + + ptr += TLV_HDR_SIZE; + bssid = ptr; + + if (params->num_bssid) { + for (i = 0; i < params->num_bssid; ++i) { + ether_addr_copy(bssid->addr, + params->bssid_list[i].addr); + bssid++; + } + } + + ptr += params->num_bssid * sizeof(*bssid); + + len = extraie_len_with_pad; + tlv = ptr; + tlv->header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_ARRAY_BYTE) | + FIELD_PREP(WMI_TLV_LEN, len); + ptr += TLV_HDR_SIZE; + + if (params->extraie.len) + memcpy(ptr, params->extraie.ptr, + params->extraie.len); + + ptr += extraie_len_with_pad; + + ret = ath11k_wmi_cmd_send(wmi, skb, + WMI_START_SCAN_CMDID); + if (ret) { + ath11k_warn(ar->ab, "failed to send WMI_START_SCAN_CMDID\n"); + dev_kfree_skb(skb); + } + + return ret; +} + +int ath11k_wmi_send_scan_stop_cmd(struct ath11k *ar, + struct scan_cancel_param *param) +{ + struct ath11k_pdev_wmi *wmi = ar->wmi; + struct wmi_stop_scan_cmd *cmd; + struct sk_buff *skb; + int ret; + + skb = ath11k_wmi_alloc_skb(wmi->wmi_sc, sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_stop_scan_cmd *)skb->data; + + cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_STOP_SCAN_CMD) | + FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE); + + cmd->vdev_id = param->vdev_id; + cmd->requestor = param->requester; + cmd->scan_id = param->scan_id; + cmd->pdev_id = param->pdev_id; + /* stop the scan with the corresponding scan_id */ + if (param->req_type == WLAN_SCAN_CANCEL_PDEV_ALL) { + /* Cancelling all scans */ + cmd->req_type = WMI_SCAN_STOP_ALL; + } else if (param->req_type == WLAN_SCAN_CANCEL_VDEV_ALL) { + /* Cancelling VAP scans */ + cmd->req_type = WMI_SCN_STOP_VAP_ALL; + } else if (param->req_type == WLAN_SCAN_CANCEL_SINGLE) { + /* Cancelling specific scan */ + cmd->req_type = WMI_SCAN_STOP_ONE; + } else { + ath11k_warn(ar->ab, "invalid scan cancel param %d", + param->req_type); + dev_kfree_skb(skb); + return -EINVAL; + } + + ret = ath11k_wmi_cmd_send(wmi, skb, + WMI_STOP_SCAN_CMDID); + if (ret) { + ath11k_warn(ar->ab, "failed to send WMI_STOP_SCAN_CMDID\n"); + dev_kfree_skb(skb); + } + + return ret; +} + +int ath11k_wmi_send_scan_chan_list_cmd(struct ath11k *ar, + struct scan_chan_list_params *chan_list) +{ + struct ath11k_pdev_wmi *wmi = ar->wmi; + struct wmi_scan_chan_list_cmd *cmd; + struct sk_buff *skb; + struct wmi_channel *chan_info; + struct channel_param *tchan_info; + struct wmi_tlv *tlv; + void *ptr; + int i, ret, len; + u32 *reg1, *reg2; + + len = sizeof(*cmd) + TLV_HDR_SIZE + + sizeof(*chan_info) * chan_list->nallchans; + + skb = ath11k_wmi_alloc_skb(wmi->wmi_sc, len); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_scan_chan_list_cmd *)skb->data; + cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_SCAN_CHAN_LIST_CMD) | + FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE); + + ath11k_dbg(ar->ab, ATH11K_DBG_WMI, + "WMI no.of chan = %d len = %d\n", chan_list->nallchans, len); + cmd->pdev_id = chan_list->pdev_id; + cmd->num_scan_chans = chan_list->nallchans; + + ptr = skb->data + sizeof(*cmd); + + len = sizeof(*chan_info) * chan_list->nallchans; + tlv = ptr; + tlv->header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_ARRAY_STRUCT) | + FIELD_PREP(WMI_TLV_LEN, len - TLV_HDR_SIZE); + ptr += TLV_HDR_SIZE; + + tchan_info = &chan_list->ch_param[0]; + + for (i = 0; i < chan_list->nallchans; ++i) { + chan_info = ptr; + memset(chan_info, 0, sizeof(*chan_info)); + len = sizeof(*chan_info); + chan_info->tlv_header = FIELD_PREP(WMI_TLV_TAG, + WMI_TAG_CHANNEL) | + FIELD_PREP(WMI_TLV_LEN, + len - TLV_HDR_SIZE); + + reg1 = &chan_info->reg_info_1; + reg2 = &chan_info->reg_info_2; + chan_info->mhz = tchan_info->mhz; + chan_info->band_center_freq1 = tchan_info->cfreq1; + chan_info->band_center_freq2 = tchan_info->cfreq2; + + if (tchan_info->is_chan_passive) + chan_info->info |= WMI_CHAN_INFO_PASSIVE; + if (tchan_info->allow_vht) + chan_info->info |= WMI_CHAN_INFO_ALLOW_VHT; + else if (tchan_info->allow_ht) + chan_info->info |= WMI_CHAN_INFO_ALLOW_HT; + if (tchan_info->half_rate) + chan_info->info |= WMI_CHAN_INFO_HALF_RATE; + if (tchan_info->quarter_rate) + chan_info->info |= WMI_CHAN_INFO_QUARTER_RATE; + + chan_info->info |= FIELD_PREP(WMI_CHAN_INFO_MODE, + tchan_info->phy_mode); + *reg1 |= FIELD_PREP(WMI_CHAN_REG_INFO1_MIN_PWR, + tchan_info->minpower); + *reg1 |= FIELD_PREP(WMI_CHAN_REG_INFO1_MAX_PWR, + tchan_info->maxpower); + *reg1 |= FIELD_PREP(WMI_CHAN_REG_INFO1_MAX_REG_PWR, + tchan_info->maxregpower); + *reg1 |= FIELD_PREP(WMI_CHAN_REG_INFO1_REG_CLS, + tchan_info->reg_class_id); + *reg2 |= FIELD_PREP(WMI_CHAN_REG_INFO2_ANT_MAX, + tchan_info->antennamax); + + ath11k_dbg(ar->ab, ATH11K_DBG_WMI, + "WMI chan scan list chan[%d] = %u\n", + i, chan_info->mhz); + + ptr += sizeof(*chan_info); + + tchan_info++; + } + + ret = ath11k_wmi_cmd_send(wmi, skb, WMI_SCAN_CHAN_LIST_CMDID); + if (ret) { + ath11k_warn(ar->ab, "failed to send WMI_SCAN_CHAN_LIST cmd\n"); + dev_kfree_skb(skb); + } + + return ret; +} + +int ath11k_wmi_send_wmm_update_cmd_tlv(struct ath11k *ar, u32 vdev_id, + struct wmi_wmm_params_all_arg *param) +{ + struct ath11k_pdev_wmi *wmi = ar->wmi; + struct wmi_vdev_set_wmm_params_cmd *cmd; + struct wmi_wmm_params *wmm_param; + struct wmi_wmm_params_arg *wmi_wmm_arg; + struct sk_buff *skb; + int ret, ac; + + skb = ath11k_wmi_alloc_skb(wmi->wmi_sc, sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_vdev_set_wmm_params_cmd *)skb->data; + cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, + WMI_TAG_VDEV_SET_WMM_PARAMS_CMD) | + FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE); + + cmd->vdev_id = vdev_id; + cmd->wmm_param_type = 0; + + for (ac = 0; ac < WME_NUM_AC; ac++) { + switch (ac) { + case WME_AC_BE: + wmi_wmm_arg = ¶m->ac_be; + break; + case WME_AC_BK: + wmi_wmm_arg = ¶m->ac_bk; + break; + case WME_AC_VI: + wmi_wmm_arg = ¶m->ac_vi; + break; + case WME_AC_VO: + wmi_wmm_arg = ¶m->ac_vo; + break; + } + + wmm_param = (struct wmi_wmm_params *)&cmd->wmm_params[ac]; + wmm_param->tlv_header = + FIELD_PREP(WMI_TLV_TAG, + WMI_TAG_VDEV_SET_WMM_PARAMS_CMD) | + FIELD_PREP(WMI_TLV_LEN, + sizeof(*wmm_param) - TLV_HDR_SIZE); + + wmm_param->aifs = wmi_wmm_arg->aifs; + wmm_param->cwmin = wmi_wmm_arg->cwmin; + wmm_param->cwmax = wmi_wmm_arg->cwmax; + wmm_param->txoplimit = wmi_wmm_arg->txop; + wmm_param->acm = wmi_wmm_arg->acm; + wmm_param->no_ack = wmi_wmm_arg->no_ack; + + ath11k_dbg(ar->ab, ATH11K_DBG_WMI, + "wmi wmm set ac %d aifs %d cwmin %d cwmax %d txop %d acm %d no_ack %d\n", + ac, wmm_param->aifs, wmm_param->cwmin, + wmm_param->cwmax, wmm_param->txoplimit, + wmm_param->acm, wmm_param->no_ack); + } + ret = ath11k_wmi_cmd_send(wmi, skb, + WMI_VDEV_SET_WMM_PARAMS_CMDID); + if (ret) { + ath11k_warn(ar->ab, + "failed to send WMI_VDEV_SET_WMM_PARAMS_CMDID"); + dev_kfree_skb(skb); + } + + return ret; +} + +int ath11k_wmi_send_dfs_phyerr_offload_enable_cmd(struct ath11k *ar, + u32 pdev_id) +{ + struct ath11k_pdev_wmi *wmi = ar->wmi; + struct wmi_dfs_phyerr_offload_cmd *cmd; + struct sk_buff *skb; + int ret; + + skb = ath11k_wmi_alloc_skb(wmi->wmi_sc, sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_dfs_phyerr_offload_cmd *)skb->data; + cmd->tlv_header = + FIELD_PREP(WMI_TLV_TAG, + WMI_TAG_PDEV_DFS_PHYERR_OFFLOAD_ENABLE_CMD) | + FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE); + + cmd->pdev_id = pdev_id; + + ath11k_dbg(ar->ab, ATH11K_DBG_WMI, + "WMI dfs phy err offload enable pdev id %d\n", pdev_id); + + ret = ath11k_wmi_cmd_send(wmi, skb, + WMI_PDEV_DFS_PHYERR_OFFLOAD_ENABLE_CMDID); + if (ret) { + ath11k_warn(ar->ab, + "failed to send WMI_PDEV_DFS_PHYERR_OFFLOAD_ENABLE cmd\n"); + dev_kfree_skb(skb); + } + + return ret; +} + +int ath11k_wmi_pdev_peer_pktlog_filter(struct ath11k *ar, u8 *addr, u8 enable) +{ + struct ath11k_pdev_wmi *wmi = ar->wmi; + struct wmi_pdev_pktlog_filter_cmd *cmd; + struct wmi_pdev_pktlog_filter_info *info; + struct sk_buff *skb; + struct wmi_tlv *tlv; + void *ptr; + int ret, len; + + len = sizeof(*cmd) + sizeof(*info) + TLV_HDR_SIZE; + skb = ath11k_wmi_alloc_skb(wmi->wmi_sc, len); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_pdev_pktlog_filter_cmd *)skb->data; + + cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_PDEV_PEER_PKTLOG_FILTER_CMD) | + FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE); + + cmd->pdev_id = ar->pdev->pdev_id; + cmd->num_mac = 1; + cmd->enable = enable; + + ptr = skb->data + sizeof(*cmd); + + tlv = ptr; + tlv->header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_ARRAY_STRUCT) | + FIELD_PREP(WMI_TLV_LEN, 0); + + ptr += TLV_HDR_SIZE; + info = ptr; + + ether_addr_copy(info->peer_macaddr.addr, addr); + info->tlv_header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_PDEV_PEER_PKTLOG_FILTER_INFO) | + FIELD_PREP(WMI_TLV_LEN, + sizeof(*info) - TLV_HDR_SIZE); + + ret = ath11k_wmi_cmd_send(wmi, skb, + WMI_PDEV_PKTLOG_FILTER_CMDID); + if (ret) { + ath11k_warn(ar->ab, "failed to send WMI_PDEV_PKTLOG_ENABLE_CMDID\n"); + dev_kfree_skb(skb); + } + + return ret; +} + +int +ath11k_wmi_send_init_country_cmd(struct ath11k *ar, + struct wmi_init_country_params init_cc_params) +{ + struct ath11k_pdev_wmi *wmi = ar->wmi; + struct wmi_init_country_cmd *cmd; + struct sk_buff *skb; + int ret; + + skb = ath11k_wmi_alloc_skb(wmi->wmi_sc, sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_init_country_cmd *)skb->data; + cmd->tlv_header = + FIELD_PREP(WMI_TLV_TAG, + WMI_TAG_SET_INIT_COUNTRY_CMD) | + FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE); + + cmd->pdev_id = ar->pdev->pdev_id; + + switch (init_cc_params.flags) { + case ALPHA_IS_SET: + cmd->init_cc_type = WMI_COUNTRY_INFO_TYPE_ALPHA; + memcpy((u8 *)&cmd->cc_info.alpha2, + init_cc_params.cc_info.alpha2, 3); + break; + case CC_IS_SET: + cmd->init_cc_type = WMI_COUNTRY_INFO_TYPE_COUNTRY_CODE; + cmd->cc_info.country_code = init_cc_params.cc_info.country_code; + break; + case REGDMN_IS_SET: + cmd->init_cc_type = WMI_COUNTRY_INFO_TYPE_REGDOMAIN; + cmd->cc_info.regdom_id = init_cc_params.cc_info.regdom_id; + break; + default: + ret = -EINVAL; + goto out; + } + + ret = ath11k_wmi_cmd_send(wmi, skb, + WMI_SET_INIT_COUNTRY_CMDID); + +out: + if (ret) { + ath11k_warn(ar->ab, + "failed to send WMI_SET_INIT_COUNTRY CMD :%d\n", + ret); + dev_kfree_skb(skb); + } + + return ret; +} + +int ath11k_wmi_pdev_pktlog_enable(struct ath11k *ar, u32 pktlog_filter) +{ + struct ath11k_pdev_wmi *wmi = ar->wmi; + struct wmi_pktlog_enable_cmd *cmd; + struct sk_buff *skb; + int ret; + + skb = ath11k_wmi_alloc_skb(wmi->wmi_sc, sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_pktlog_enable_cmd *)skb->data; + + cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_PDEV_PKTLOG_ENABLE_CMD) | + FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE); + + cmd->pdev_id = ar->pdev->pdev_id; + cmd->evlist = pktlog_filter; + cmd->enable = ATH11K_WMI_PKTLOG_ENABLE_FORCE; + + ret = ath11k_wmi_cmd_send(wmi, skb, + WMI_PDEV_PKTLOG_ENABLE_CMDID); + if (ret) { + ath11k_warn(ar->ab, "failed to send WMI_PDEV_PKTLOG_ENABLE_CMDID\n"); + dev_kfree_skb(skb); + } + + return ret; +} + +int ath11k_wmi_pdev_pktlog_disable(struct ath11k *ar) +{ + struct ath11k_pdev_wmi *wmi = ar->wmi; + struct wmi_pktlog_disable_cmd *cmd; + struct sk_buff *skb; + int ret; + + skb = ath11k_wmi_alloc_skb(wmi->wmi_sc, sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_pktlog_disable_cmd *)skb->data; + + cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_PDEV_PKTLOG_DISABLE_CMD) | + FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE); + + cmd->pdev_id = ar->pdev->pdev_id; + + ret = ath11k_wmi_cmd_send(wmi, skb, + WMI_PDEV_PKTLOG_DISABLE_CMDID); + if (ret) { + ath11k_warn(ar->ab, "failed to send WMI_PDEV_PKTLOG_ENABLE_CMDID\n"); + dev_kfree_skb(skb); + } + + return ret; +} + +static void +ath11k_fill_band_to_mac_param(struct ath11k_base *soc, + struct wmi_host_pdev_band_to_mac *band_to_mac) +{ + u8 i; + struct ath11k_hal_reg_capabilities_ext *hal_reg_cap; + struct ath11k_pdev *pdev; + + for (i = 0; i < soc->num_radios; i++) { + pdev = &soc->pdevs[i]; + hal_reg_cap = &soc->hal_reg_cap[i]; + band_to_mac[i].pdev_id = pdev->pdev_id; + + switch (pdev->cap.supported_bands) { + case WMI_HOST_WLAN_2G_5G_CAP: + band_to_mac[i].start_freq = hal_reg_cap->low_2ghz_chan; + band_to_mac[i].end_freq = hal_reg_cap->high_5ghz_chan; + break; + case WMI_HOST_WLAN_2G_CAP: + band_to_mac[i].start_freq = hal_reg_cap->low_2ghz_chan; + band_to_mac[i].end_freq = hal_reg_cap->high_2ghz_chan; + break; + case WMI_HOST_WLAN_5G_CAP: + band_to_mac[i].start_freq = hal_reg_cap->low_5ghz_chan; + band_to_mac[i].end_freq = hal_reg_cap->high_5ghz_chan; + break; + default: + break; + } + } +} + +static void +ath11k_wmi_copy_resource_config(struct wmi_resource_config *wmi_cfg, + struct target_resource_config *tg_cfg) +{ + wmi_cfg->num_vdevs = tg_cfg->num_vdevs; + wmi_cfg->num_peers = tg_cfg->num_peers; + wmi_cfg->num_offload_peers = tg_cfg->num_offload_peers; + wmi_cfg->num_offload_reorder_buffs = tg_cfg->num_offload_reorder_buffs; + wmi_cfg->num_peer_keys = tg_cfg->num_peer_keys; + wmi_cfg->num_tids = tg_cfg->num_tids; + wmi_cfg->ast_skid_limit = tg_cfg->ast_skid_limit; + wmi_cfg->tx_chain_mask = tg_cfg->tx_chain_mask; + wmi_cfg->rx_chain_mask = tg_cfg->rx_chain_mask; + wmi_cfg->rx_timeout_pri[0] = tg_cfg->rx_timeout_pri[0]; + wmi_cfg->rx_timeout_pri[1] = tg_cfg->rx_timeout_pri[1]; + wmi_cfg->rx_timeout_pri[2] = tg_cfg->rx_timeout_pri[2]; + wmi_cfg->rx_timeout_pri[3] = tg_cfg->rx_timeout_pri[3]; + wmi_cfg->rx_decap_mode = tg_cfg->rx_decap_mode; + wmi_cfg->scan_max_pending_req = tg_cfg->scan_max_pending_req; + wmi_cfg->bmiss_offload_max_vdev = tg_cfg->bmiss_offload_max_vdev; + wmi_cfg->roam_offload_max_vdev = tg_cfg->roam_offload_max_vdev; + wmi_cfg->roam_offload_max_ap_profiles = + tg_cfg->roam_offload_max_ap_profiles; + wmi_cfg->num_mcast_groups = tg_cfg->num_mcast_groups; + wmi_cfg->num_mcast_table_elems = tg_cfg->num_mcast_table_elems; + wmi_cfg->mcast2ucast_mode = tg_cfg->mcast2ucast_mode; + wmi_cfg->tx_dbg_log_size = tg_cfg->tx_dbg_log_size; + wmi_cfg->num_wds_entries = tg_cfg->num_wds_entries; + wmi_cfg->dma_burst_size = tg_cfg->dma_burst_size; + wmi_cfg->mac_aggr_delim = tg_cfg->mac_aggr_delim; + wmi_cfg->rx_skip_defrag_timeout_dup_detection_check = + tg_cfg->rx_skip_defrag_timeout_dup_detection_check; + wmi_cfg->vow_config = tg_cfg->vow_config; + wmi_cfg->gtk_offload_max_vdev = tg_cfg->gtk_offload_max_vdev; + wmi_cfg->num_msdu_desc = tg_cfg->num_msdu_desc; + wmi_cfg->max_frag_entries = tg_cfg->max_frag_entries; + wmi_cfg->num_tdls_vdevs = tg_cfg->num_tdls_vdevs; + wmi_cfg->num_tdls_conn_table_entries = + tg_cfg->num_tdls_conn_table_entries; + wmi_cfg->beacon_tx_offload_max_vdev = + tg_cfg->beacon_tx_offload_max_vdev; + wmi_cfg->num_multicast_filter_entries = + tg_cfg->num_multicast_filter_entries; + wmi_cfg->num_wow_filters = tg_cfg->num_wow_filters; + wmi_cfg->num_keep_alive_pattern = tg_cfg->num_keep_alive_pattern; + wmi_cfg->keep_alive_pattern_size = tg_cfg->keep_alive_pattern_size; + wmi_cfg->max_tdls_concurrent_sleep_sta = + tg_cfg->max_tdls_concurrent_sleep_sta; + wmi_cfg->max_tdls_concurrent_buffer_sta = + tg_cfg->max_tdls_concurrent_buffer_sta; + wmi_cfg->wmi_send_separate = tg_cfg->wmi_send_separate; + wmi_cfg->num_ocb_vdevs = tg_cfg->num_ocb_vdevs; + wmi_cfg->num_ocb_channels = tg_cfg->num_ocb_channels; + wmi_cfg->num_ocb_schedules = tg_cfg->num_ocb_schedules; + wmi_cfg->bpf_instruction_size = tg_cfg->bpf_instruction_size; + wmi_cfg->max_bssid_rx_filters = tg_cfg->max_bssid_rx_filters; + wmi_cfg->use_pdev_id = tg_cfg->use_pdev_id; + wmi_cfg->flag1 = tg_cfg->atf_config; + wmi_cfg->peer_map_unmap_v2_support = tg_cfg->peer_map_unmap_v2_support; +} + +static int ath11k_init_cmd_send(struct ath11k_pdev_wmi *wmi, + struct wmi_init_cmd_param *param) +{ + struct ath11k_base *ab = wmi->wmi_sc->ab; + struct sk_buff *skb; + struct wmi_init_cmd *cmd; + struct wmi_resource_config *cfg; + struct wmi_pdev_set_hw_mode_cmd_param *hw_mode; + struct wmi_pdev_band_to_mac *band_to_mac; + struct wlan_host_mem_chunk *host_mem_chunks; + struct wmi_tlv *tlv; + size_t ret, len; + void *ptr; + u32 hw_mode_len = 0; + u16 idx; + + if (param->hw_mode_id != WMI_HOST_HW_MODE_MAX) + hw_mode_len = sizeof(*hw_mode) + TLV_HDR_SIZE + + (param->num_band_to_mac * sizeof(*band_to_mac)); + + len = sizeof(*cmd) + TLV_HDR_SIZE + sizeof(*cfg) + hw_mode_len + + (sizeof(*host_mem_chunks) * WMI_MAX_MEM_REQS); + + skb = ath11k_wmi_alloc_skb(wmi->wmi_sc, len); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_init_cmd *)skb->data; + + cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_INIT_CMD) | + FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE); + + ptr = skb->data + sizeof(*cmd); + cfg = ptr; + + ath11k_wmi_copy_resource_config(cfg, param->res_cfg); + + cfg->tlv_header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_RESOURCE_CONFIG) | + FIELD_PREP(WMI_TLV_LEN, sizeof(*cfg) - TLV_HDR_SIZE); + + ptr += sizeof(*cfg); + host_mem_chunks = ptr + TLV_HDR_SIZE; + len = sizeof(struct wlan_host_mem_chunk); + + for (idx = 0; idx < param->num_mem_chunks; ++idx) { + host_mem_chunks[idx].tlv_header = + FIELD_PREP(WMI_TLV_TAG, + WMI_TAG_WLAN_HOST_MEMORY_CHUNK) | + FIELD_PREP(WMI_TLV_LEN, len); + + host_mem_chunks[idx].ptr = param->mem_chunks[idx].paddr; + host_mem_chunks[idx].size = param->mem_chunks[idx].len; + host_mem_chunks[idx].req_id = param->mem_chunks[idx].req_id; + + ath11k_dbg(ab, ATH11K_DBG_WMI, + "WMI host mem chunk req_id %d paddr 0x%llx len %d\n", + param->mem_chunks[idx].req_id, + (u64)param->mem_chunks[idx].paddr, + param->mem_chunks[idx].len); + } + cmd->num_host_mem_chunks = param->num_mem_chunks; + len = sizeof(struct wlan_host_mem_chunk) * param->num_mem_chunks; + + /* num_mem_chunks is zero */ + tlv = ptr; + tlv->header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_ARRAY_STRUCT) | + FIELD_PREP(WMI_TLV_LEN, len); + ptr += TLV_HDR_SIZE + len; + + if (param->hw_mode_id != WMI_HOST_HW_MODE_MAX) { + hw_mode = (struct wmi_pdev_set_hw_mode_cmd_param *)ptr; + hw_mode->tlv_header = FIELD_PREP(WMI_TLV_TAG, + WMI_TAG_PDEV_SET_HW_MODE_CMD) | + FIELD_PREP(WMI_TLV_LEN, + sizeof(*hw_mode) - TLV_HDR_SIZE); + + hw_mode->hw_mode_index = param->hw_mode_id; + hw_mode->num_band_to_mac = param->num_band_to_mac; + + ptr += sizeof(*hw_mode); + + len = param->num_band_to_mac * sizeof(*band_to_mac); + tlv = ptr; + tlv->header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_ARRAY_STRUCT) | + FIELD_PREP(WMI_TLV_LEN, len); + + ptr += TLV_HDR_SIZE; + len = sizeof(*band_to_mac); + + for (idx = 0; idx < param->num_band_to_mac; idx++) { + band_to_mac = (void *)ptr; + + band_to_mac->tlv_header = FIELD_PREP(WMI_TLV_TAG, + WMI_TAG_PDEV_BAND_TO_MAC) | + FIELD_PREP(WMI_TLV_LEN, + len - TLV_HDR_SIZE); + band_to_mac->pdev_id = param->band_to_mac[idx].pdev_id; + band_to_mac->start_freq = + param->band_to_mac[idx].start_freq; + band_to_mac->end_freq = + param->band_to_mac[idx].end_freq; + ptr += sizeof(*band_to_mac); + } + } + + ret = ath11k_wmi_cmd_send(wmi, skb, WMI_INIT_CMDID); + if (ret) { + ath11k_warn(ab, "failed to send WMI_INIT_CMDID\n"); + dev_kfree_skb(skb); + } + + return ret; +} + +int ath11k_wmi_wait_for_service_ready(struct ath11k_base *ab) +{ + unsigned long time_left; + + time_left = wait_for_completion_timeout(&ab->wmi_sc.service_ready, + WMI_SERVICE_READY_TIMEOUT_HZ); + if (!time_left) + return -ETIMEDOUT; + + return 0; +} + +int ath11k_wmi_wait_for_unified_ready(struct ath11k_base *ab) +{ + unsigned long time_left; + + time_left = wait_for_completion_timeout(&ab->wmi_sc.unified_ready, + WMI_SERVICE_READY_TIMEOUT_HZ); + if (!time_left) + return -ETIMEDOUT; + + return 0; +} + +int ath11k_wmi_cmd_init(struct ath11k_base *ab) +{ + struct ath11k_wmi_base *wmi_sc = &ab->wmi_sc; + struct wmi_init_cmd_param init_param; + struct target_resource_config config; + + memset(&init_param, 0, sizeof(init_param)); + memset(&config, 0, sizeof(config)); + + config.num_vdevs = ab->num_radios * TARGET_NUM_VDEVS; + + if (ab->num_radios == 2) { + config.num_peers = TARGET_NUM_PEERS(DBS); + config.num_tids = TARGET_NUM_TIDS(DBS); + } else if (ab->num_radios == 3) { + config.num_peers = TARGET_NUM_PEERS(DBS_SBS); + config.num_tids = TARGET_NUM_TIDS(DBS_SBS); + } else { + /* Control should not reach here */ + config.num_peers = TARGET_NUM_PEERS(SINGLE); + config.num_tids = TARGET_NUM_TIDS(SINGLE); + } + config.num_offload_peers = TARGET_NUM_OFFLD_PEERS; + config.num_offload_reorder_buffs = TARGET_NUM_OFFLD_REORDER_BUFFS; + config.num_peer_keys = TARGET_NUM_PEER_KEYS; + config.ast_skid_limit = TARGET_AST_SKID_LIMIT; + config.tx_chain_mask = (1 << ab->target_caps.num_rf_chains) - 1; + config.rx_chain_mask = (1 << ab->target_caps.num_rf_chains) - 1; + config.rx_timeout_pri[0] = TARGET_RX_TIMEOUT_LO_PRI; + config.rx_timeout_pri[1] = TARGET_RX_TIMEOUT_LO_PRI; + config.rx_timeout_pri[2] = TARGET_RX_TIMEOUT_LO_PRI; + config.rx_timeout_pri[3] = TARGET_RX_TIMEOUT_HI_PRI; + config.rx_decap_mode = TARGET_DECAP_MODE_NATIVE_WIFI; + config.scan_max_pending_req = TARGET_SCAN_MAX_PENDING_REQS; + config.bmiss_offload_max_vdev = TARGET_BMISS_OFFLOAD_MAX_VDEV; + config.roam_offload_max_vdev = TARGET_ROAM_OFFLOAD_MAX_VDEV; + config.roam_offload_max_ap_profiles = TARGET_ROAM_OFFLOAD_MAX_AP_PROFILES; + config.num_mcast_groups = TARGET_NUM_MCAST_GROUPS; + config.num_mcast_table_elems = TARGET_NUM_MCAST_TABLE_ELEMS; + config.mcast2ucast_mode = TARGET_MCAST2UCAST_MODE; + config.tx_dbg_log_size = TARGET_TX_DBG_LOG_SIZE; + config.num_wds_entries = TARGET_NUM_WDS_ENTRIES; + config.dma_burst_size = TARGET_DMA_BURST_SIZE; + config.rx_skip_defrag_timeout_dup_detection_check = + TARGET_RX_SKIP_DEFRAG_TIMEOUT_DUP_DETECTION_CHECK; + config.vow_config = TARGET_VOW_CONFIG; + config.gtk_offload_max_vdev = TARGET_GTK_OFFLOAD_MAX_VDEV; + config.num_msdu_desc = TARGET_NUM_MSDU_DESC; + config.beacon_tx_offload_max_vdev = ab->num_radios * TARGET_MAX_BCN_OFFLD; + config.rx_batchmode = TARGET_RX_BATCHMODE; + config.peer_map_unmap_v2_support = 1; + + memcpy(&wmi_sc->wlan_resource_config, &config, sizeof(config)); + + init_param.res_cfg = &wmi_sc->wlan_resource_config; + init_param.num_mem_chunks = wmi_sc->num_mem_chunks; + init_param.hw_mode_id = wmi_sc->preferred_hw_mode; + init_param.mem_chunks = wmi_sc->mem_chunks; + + if (wmi_sc->preferred_hw_mode == WMI_HOST_HW_MODE_SINGLE) + init_param.hw_mode_id = WMI_HOST_HW_MODE_MAX; + + init_param.num_band_to_mac = ab->num_radios; + + ath11k_fill_band_to_mac_param(ab, init_param.band_to_mac); + + return ath11k_init_cmd_send(&wmi_sc->wmi[0], &init_param); +} + +static int ath11k_wmi_tlv_hw_mode_caps_parse(struct ath11k_base *soc, + u16 tag, u16 len, + const void *ptr, void *data) +{ + struct wmi_tlv_svc_rdy_ext_parse *svc_rdy_ext = data; + struct wmi_hw_mode_capabilities *hw_mode_cap; + u32 phy_map = 0; + + if (tag != WMI_TAG_HW_MODE_CAPABILITIES) + return -EPROTO; + + if (svc_rdy_ext->n_hw_mode_caps >= svc_rdy_ext->param.num_hw_modes) + return -ENOBUFS; + + hw_mode_cap = container_of(ptr, struct wmi_hw_mode_capabilities, + hw_mode_id); + svc_rdy_ext->n_hw_mode_caps++; + + phy_map = hw_mode_cap->phy_id_map; + while (phy_map) { + svc_rdy_ext->tot_phy_id++; + phy_map = phy_map >> 1; + } + + return 0; +} + +static int ath11k_wmi_tlv_hw_mode_caps(struct ath11k_base *soc, + u16 len, const void *ptr, void *data) +{ + struct wmi_tlv_svc_rdy_ext_parse *svc_rdy_ext = data; + struct wmi_hw_mode_capabilities *hw_mode_caps; + enum wmi_host_hw_mode_config_type mode, pref; + u32 i; + int ret; + + svc_rdy_ext->n_hw_mode_caps = 0; + svc_rdy_ext->hw_mode_caps = (struct wmi_hw_mode_capabilities *)ptr; + + ret = ath11k_wmi_tlv_iter(soc, ptr, len, + ath11k_wmi_tlv_hw_mode_caps_parse, + svc_rdy_ext); + if (ret) { + ath11k_warn(soc, "failed to parse tlv %d\n", ret); + return ret; + } + + i = 0; + while (i < svc_rdy_ext->n_hw_mode_caps) { + hw_mode_caps = &svc_rdy_ext->hw_mode_caps[i]; + mode = hw_mode_caps->hw_mode_id; + pref = soc->wmi_sc.preferred_hw_mode; + + if (ath11k_hw_mode_pri_map[mode] < ath11k_hw_mode_pri_map[pref]) { + svc_rdy_ext->pref_hw_mode_caps = *hw_mode_caps; + soc->wmi_sc.preferred_hw_mode = mode; + } + i++; + } + + if (soc->wmi_sc.preferred_hw_mode == WMI_HOST_HW_MODE_MAX) + return -EINVAL; + + return 0; +} + +static int ath11k_wmi_tlv_mac_phy_caps_parse(struct ath11k_base *soc, + u16 tag, u16 len, + const void *ptr, void *data) +{ + struct wmi_tlv_svc_rdy_ext_parse *svc_rdy_ext = data; + + if (tag != WMI_TAG_MAC_PHY_CAPABILITIES) + return -EPROTO; + + if (svc_rdy_ext->n_mac_phy_caps >= svc_rdy_ext->tot_phy_id) + return -ENOBUFS; + + svc_rdy_ext->n_mac_phy_caps++; + return 0; +} + +static int ath11k_wmi_tlv_ext_hal_reg_caps_parse(struct ath11k_base *soc, + u16 tag, u16 len, + const void *ptr, void *data) +{ + struct wmi_tlv_svc_rdy_ext_parse *svc_rdy_ext = data; + + if (tag != WMI_TAG_HAL_REG_CAPABILITIES_EXT) + return -EPROTO; + + if (svc_rdy_ext->n_ext_hal_reg_caps >= svc_rdy_ext->param.num_phy) + return -ENOBUFS; + + svc_rdy_ext->n_ext_hal_reg_caps++; + return 0; +} + +static int ath11k_wmi_tlv_ext_hal_reg_caps(struct ath11k_base *soc, + u16 len, const void *ptr, void *data) +{ + struct ath11k_pdev_wmi *wmi_handle = &soc->wmi_sc.wmi[0]; + struct wmi_tlv_svc_rdy_ext_parse *svc_rdy_ext = data; + struct ath11k_hal_reg_capabilities_ext reg_cap; + int ret; + u32 i; + + svc_rdy_ext->n_ext_hal_reg_caps = 0; + svc_rdy_ext->ext_hal_reg_caps = (struct wmi_hal_reg_capabilities_ext *)ptr; + ret = ath11k_wmi_tlv_iter(soc, ptr, len, + ath11k_wmi_tlv_ext_hal_reg_caps_parse, + svc_rdy_ext); + if (ret) { + ath11k_warn(soc, "failed to parse tlv %d\n", ret); + return ret; + } + + for (i = 0; i < svc_rdy_ext->param.num_phy; i++) { + ret = ath11k_pull_reg_cap_svc_rdy_ext(wmi_handle, + svc_rdy_ext->soc_hal_reg_caps, + svc_rdy_ext->ext_hal_reg_caps, i, + ®_cap); + if (ret) { + ath11k_warn(soc, "failed to extract reg cap %d\n", i); + return ret; + } + + memcpy(&soc->hal_reg_cap[reg_cap.phy_id], + ®_cap, sizeof(reg_cap)); + } + return 0; +} + +static int ath11k_wmi_tlv_ext_soc_hal_reg_caps_parse(struct ath11k_base *soc, + u16 len, const void *ptr, + void *data) +{ + struct ath11k_pdev_wmi *wmi_handle = &soc->wmi_sc.wmi[0]; + struct wmi_tlv_svc_rdy_ext_parse *svc_rdy_ext = data; + u8 hw_mode_id = svc_rdy_ext->pref_hw_mode_caps.hw_mode_id; + u32 phy_id_map; + int ret; + + svc_rdy_ext->soc_hal_reg_caps = (struct wmi_soc_hal_reg_capabilities *)ptr; + svc_rdy_ext->param.num_phy = svc_rdy_ext->soc_hal_reg_caps->num_phy; + + soc->num_radios = 0; + phy_id_map = svc_rdy_ext->pref_hw_mode_caps.phy_id_map; + + while (phy_id_map && soc->num_radios < MAX_RADIOS) { + ret = ath11k_pull_mac_phy_cap_svc_ready_ext(wmi_handle, + svc_rdy_ext->hw_caps, + svc_rdy_ext->hw_mode_caps, + svc_rdy_ext->soc_hal_reg_caps, + svc_rdy_ext->mac_phy_caps, + hw_mode_id, soc->num_radios, + &soc->pdevs[soc->num_radios]); + if (ret) { + ath11k_warn(soc, "failed to extract mac caps, idx :%d\n", + soc->num_radios); + return ret; + } + + soc->num_radios++; + + /* TODO: mac_phy_cap prints */ + phy_id_map >>= 1; + } + return 0; +} + +static int ath11k_wmi_tlv_svc_rdy_ext_parse(struct ath11k_base *ab, + u16 tag, u16 len, + const void *ptr, void *data) +{ + struct ath11k_pdev_wmi *wmi_handle = &ab->wmi_sc.wmi[0]; + struct wmi_tlv_svc_rdy_ext_parse *svc_rdy_ext = data; + int ret; + + switch (tag) { + case WMI_TAG_SERVICE_READY_EXT_EVENT: + ret = ath11k_pull_svc_ready_ext(wmi_handle, ptr, + &svc_rdy_ext->param); + if (ret) { + ath11k_warn(ab, "unable to extract ext params\n"); + return ret; + } + break; + + case WMI_TAG_SOC_MAC_PHY_HW_MODE_CAPS: + svc_rdy_ext->hw_caps = (struct wmi_soc_mac_phy_hw_mode_caps *)ptr; + svc_rdy_ext->param.num_hw_modes = svc_rdy_ext->hw_caps->num_hw_modes; + break; + + case WMI_TAG_SOC_HAL_REG_CAPABILITIES: + ret = ath11k_wmi_tlv_ext_soc_hal_reg_caps_parse(ab, len, ptr, + svc_rdy_ext); + if (ret) + return ret; + break; + + case WMI_TAG_ARRAY_STRUCT: + if (!svc_rdy_ext->hw_mode_done) { + ret = ath11k_wmi_tlv_hw_mode_caps(ab, len, ptr, + svc_rdy_ext); + if (ret) + return ret; + + svc_rdy_ext->hw_mode_done = true; + } else if (!svc_rdy_ext->mac_phy_done) { + svc_rdy_ext->n_mac_phy_caps = 0; + svc_rdy_ext->mac_phy_caps = + (struct wmi_mac_phy_capabilities *)ptr; + ret = ath11k_wmi_tlv_iter(ab, ptr, len, + ath11k_wmi_tlv_mac_phy_caps_parse, + svc_rdy_ext); + if (ret) { + ath11k_warn(ab, "failed to parse tlv %d\n", ret); + return ret; + } + + svc_rdy_ext->mac_phy_done = true; + } else if (!svc_rdy_ext->ext_hal_reg_done) { + ret = ath11k_wmi_tlv_ext_hal_reg_caps(ab, len, ptr, + svc_rdy_ext); + if (ret) + return ret; + + svc_rdy_ext->ext_hal_reg_done = true; + complete(&ab->wmi_sc.service_ready); + } + break; + + default: + break; + } + return 0; +} + +static int ath11k_service_ready_ext_event(struct ath11k_base *ab, + struct sk_buff *skb) +{ + struct wmi_tlv_svc_rdy_ext_parse svc_rdy_ext = { }; + int ret; + + ret = ath11k_wmi_tlv_iter(ab, skb->data, skb->len, + ath11k_wmi_tlv_svc_rdy_ext_parse, + &svc_rdy_ext); + if (ret) { + ath11k_warn(ab, "failed to parse tlv %d\n", ret); + return ret; + } + + return 0; +} + +static int ath11k_pull_vdev_start_resp_tlv(struct ath11k_base *ab, struct sk_buff *skb, + struct wmi_vdev_start_resp_event *vdev_rsp) +{ + const void **tb; + const struct wmi_vdev_start_resp_event *ev; + int ret; + + tb = ath11k_wmi_tlv_parse_alloc(ab, skb->data, skb->len, GFP_ATOMIC); + if (IS_ERR(tb)) { + ret = PTR_ERR(tb); + ath11k_warn(ab, "failed to parse tlv: %d\n", ret); + return ret; + } + + ev = tb[WMI_TAG_VDEV_START_RESPONSE_EVENT]; + if (!ev) { + ath11k_warn(ab, "failed to fetch vdev start resp ev"); + kfree(tb); + return -EPROTO; + } + + memset(vdev_rsp, 0, sizeof(*vdev_rsp)); + + vdev_rsp->vdev_id = ev->vdev_id; + vdev_rsp->requestor_id = ev->requestor_id; + vdev_rsp->resp_type = ev->resp_type; + vdev_rsp->status = ev->status; + vdev_rsp->chain_mask = ev->chain_mask; + vdev_rsp->smps_mode = ev->smps_mode; + vdev_rsp->mac_id = ev->mac_id; + vdev_rsp->cfgd_tx_streams = ev->cfgd_tx_streams; + vdev_rsp->cfgd_rx_streams = ev->cfgd_rx_streams; + + kfree(tb); + return 0; +} + +static struct cur_reg_rule +*create_reg_rules_from_wmi(u32 num_reg_rules, + struct wmi_regulatory_rule_struct *wmi_reg_rule) +{ + struct cur_reg_rule *reg_rule_ptr; + u32 count; + + reg_rule_ptr = kzalloc((num_reg_rules * sizeof(*reg_rule_ptr)), + GFP_ATOMIC); + + if (!reg_rule_ptr) + return NULL; + + for (count = 0; count < num_reg_rules; count++) { + reg_rule_ptr[count].start_freq = + FIELD_GET(REG_RULE_START_FREQ, + wmi_reg_rule[count].freq_info); + reg_rule_ptr[count].end_freq = + FIELD_GET(REG_RULE_END_FREQ, + wmi_reg_rule[count].freq_info); + reg_rule_ptr[count].max_bw = + FIELD_GET(REG_RULE_MAX_BW, + wmi_reg_rule[count].bw_pwr_info); + reg_rule_ptr[count].reg_power = + FIELD_GET(REG_RULE_REG_PWR, + wmi_reg_rule[count].bw_pwr_info); + reg_rule_ptr[count].ant_gain = + FIELD_GET(REG_RULE_ANT_GAIN, + wmi_reg_rule[count].bw_pwr_info); + reg_rule_ptr[count].flags = + FIELD_GET(REG_RULE_FLAGS, + wmi_reg_rule[count].flag_info); + } + + return reg_rule_ptr; +} + +static int ath11k_pull_reg_chan_list_update_ev(struct ath11k_base *ab, + struct sk_buff *skb, + struct cur_regulatory_info *reg_info) +{ + const void **tb; + const struct wmi_reg_chan_list_cc_event *chan_list_event_hdr; + struct wmi_regulatory_rule_struct *wmi_reg_rule; + u32 num_2g_reg_rules, num_5g_reg_rules; + int ret; + + ath11k_dbg(ab, ATH11K_DBG_WMI, "processing regulatory channel list\n"); + + tb = ath11k_wmi_tlv_parse_alloc(ab, skb->data, skb->len, GFP_ATOMIC); + if (IS_ERR(tb)) { + ret = PTR_ERR(tb); + ath11k_warn(ab, "failed to parse tlv: %d\n", ret); + return ret; + } + + chan_list_event_hdr = tb[WMI_TAG_REG_CHAN_LIST_CC_EVENT]; + if (!chan_list_event_hdr) { + ath11k_warn(ab, "failed to fetch reg chan list update ev\n"); + kfree(tb); + return -EPROTO; + } + + reg_info->num_2g_reg_rules = chan_list_event_hdr->num_2g_reg_rules; + reg_info->num_5g_reg_rules = chan_list_event_hdr->num_5g_reg_rules; + + if (!(reg_info->num_2g_reg_rules + reg_info->num_5g_reg_rules)) { + ath11k_warn(ab, "No regulatory rules available in the event info\n"); + kfree(tb); + return -EINVAL; + } + + memcpy(reg_info->alpha2, &chan_list_event_hdr->alpha2, + REG_ALPHA2_LEN); + reg_info->dfs_region = chan_list_event_hdr->dfs_region; + reg_info->phybitmap = chan_list_event_hdr->phybitmap; + reg_info->num_phy = chan_list_event_hdr->num_phy; + reg_info->phy_id = chan_list_event_hdr->phy_id; + reg_info->ctry_code = chan_list_event_hdr->country_id; + reg_info->reg_dmn_pair = chan_list_event_hdr->domain_code; + if (chan_list_event_hdr->status_code == WMI_REG_SET_CC_STATUS_PASS) + reg_info->status_code = REG_SET_CC_STATUS_PASS; + else if (chan_list_event_hdr->status_code == WMI_REG_CURRENT_ALPHA2_NOT_FOUND) + reg_info->status_code = REG_CURRENT_ALPHA2_NOT_FOUND; + else if (chan_list_event_hdr->status_code == WMI_REG_INIT_ALPHA2_NOT_FOUND) + reg_info->status_code = REG_INIT_ALPHA2_NOT_FOUND; + else if (chan_list_event_hdr->status_code == WMI_REG_SET_CC_CHANGE_NOT_ALLOWED) + reg_info->status_code = REG_SET_CC_CHANGE_NOT_ALLOWED; + else if (chan_list_event_hdr->status_code == WMI_REG_SET_CC_STATUS_NO_MEMORY) + reg_info->status_code = REG_SET_CC_STATUS_NO_MEMORY; + else if (chan_list_event_hdr->status_code == WMI_REG_SET_CC_STATUS_FAIL) + reg_info->status_code = REG_SET_CC_STATUS_FAIL; + + reg_info->min_bw_2g = chan_list_event_hdr->min_bw_2g; + reg_info->max_bw_2g = chan_list_event_hdr->max_bw_2g; + reg_info->min_bw_5g = chan_list_event_hdr->min_bw_5g; + reg_info->max_bw_5g = chan_list_event_hdr->max_bw_5g; + + num_2g_reg_rules = reg_info->num_2g_reg_rules; + num_5g_reg_rules = reg_info->num_5g_reg_rules; + + ath11k_dbg(ab, ATH11K_DBG_WMI, + "%s:cc %s dsf %d BW: min_2g %d max_2g %d min_5g %d max_5g %d", + __func__, reg_info->alpha2, reg_info->dfs_region, + reg_info->min_bw_2g, reg_info->max_bw_2g, + reg_info->min_bw_5g, reg_info->max_bw_5g); + + ath11k_dbg(ab, ATH11K_DBG_WMI, + "%s: num_2g_reg_rules %d num_5g_reg_rules %d", __func__, + num_2g_reg_rules, num_5g_reg_rules); + + wmi_reg_rule = + (struct wmi_regulatory_rule_struct *)((u8 *)chan_list_event_hdr + + sizeof(*chan_list_event_hdr) + + sizeof(struct wmi_tlv)); + + if (num_2g_reg_rules) { + reg_info->reg_rules_2g_ptr = create_reg_rules_from_wmi(num_2g_reg_rules, + wmi_reg_rule); + if (!reg_info->reg_rules_2g_ptr) { + kfree(tb); + ath11k_warn(ab, "Unable to Allocate memory for 2g rules\n"); + return -ENOMEM; + } + } + + if (num_5g_reg_rules) { + wmi_reg_rule += num_2g_reg_rules; + reg_info->reg_rules_5g_ptr = create_reg_rules_from_wmi(num_5g_reg_rules, + wmi_reg_rule); + if (!reg_info->reg_rules_5g_ptr) { + kfree(tb); + ath11k_warn(ab, "Unable to Allocate memory for 5g rules\n"); + return -ENOMEM; + } + } + + ath11k_dbg(ab, ATH11K_DBG_WMI, "processed regulatory channel list\n"); + + kfree(tb); + return 0; +} + +static int ath11k_pull_peer_del_resp_ev(struct ath11k_base *ab, struct sk_buff *skb, + struct wmi_peer_delete_resp_event *peer_del_resp) +{ + const void **tb; + const struct wmi_peer_delete_resp_event *ev; + int ret; + + tb = ath11k_wmi_tlv_parse_alloc(ab, skb->data, skb->len, GFP_ATOMIC); + if (IS_ERR(tb)) { + ret = PTR_ERR(tb); + ath11k_warn(ab, "failed to parse tlv: %d\n", ret); + return ret; + } + + ev = tb[WMI_TAG_PEER_DELETE_RESP_EVENT]; + if (!ev) { + ath11k_warn(ab, "failed to fetch peer delete resp ev"); + kfree(tb); + return -EPROTO; + } + + memset(peer_del_resp, 0, sizeof(*peer_del_resp)); + + peer_del_resp->vdev_id = ev->vdev_id; + ether_addr_copy(peer_del_resp->peer_macaddr.addr, + ev->peer_macaddr.addr); + + kfree(tb); + return 0; +} + +static int ath11k_pull_bcn_tx_status_ev(struct ath11k_base *ab, void *evt_buf, + u32 len, u32 *vdev_id, + u32 *tx_status) +{ + const void **tb; + const struct wmi_bcn_tx_status_event *ev; + int ret; + + tb = ath11k_wmi_tlv_parse_alloc(ab, evt_buf, len, GFP_ATOMIC); + if (IS_ERR(tb)) { + ret = PTR_ERR(tb); + ath11k_warn(ab, "failed to parse tlv: %d\n", ret); + return ret; + } + + ev = tb[WMI_TAG_OFFLOAD_BCN_TX_STATUS_EVENT]; + if (!ev) { + ath11k_warn(ab, "failed to fetch bcn tx status ev"); + kfree(tb); + return -EPROTO; + } + + *vdev_id = ev->vdev_id; + *tx_status = ev->tx_status; + + kfree(tb); + return 0; +} + +static int ath11k_pull_vdev_stopped_param_tlv(struct ath11k_base *ab, struct sk_buff *skb, + u32 *vdev_id) +{ + const void **tb; + const struct wmi_vdev_stopped_event *ev; + int ret; + + tb = ath11k_wmi_tlv_parse_alloc(ab, skb->data, skb->len, GFP_ATOMIC); + if (IS_ERR(tb)) { + ret = PTR_ERR(tb); + ath11k_warn(ab, "failed to parse tlv: %d\n", ret); + return ret; + } + + ev = tb[WMI_TAG_VDEV_STOPPED_EVENT]; + if (!ev) { + ath11k_warn(ab, "failed to fetch vdev stop ev"); + kfree(tb); + return -EPROTO; + } + + *vdev_id = ev->vdev_id; + + kfree(tb); + return 0; +} + +static int ath11k_pull_mgmt_rx_params_tlv(struct ath11k_base *ab, + struct sk_buff *skb, + struct mgmt_rx_event_params *hdr) +{ + const void **tb; + const struct wmi_mgmt_rx_hdr *ev; + const u8 *frame; + int ret; + + tb = ath11k_wmi_tlv_parse_alloc(ab, skb->data, skb->len, GFP_ATOMIC); + if (IS_ERR(tb)) { + ret = PTR_ERR(tb); + ath11k_warn(ab, "failed to parse tlv: %d\n", ret); + return ret; + } + + ev = tb[WMI_TAG_MGMT_RX_HDR]; + frame = tb[WMI_TAG_ARRAY_BYTE]; + + if (!ev || !frame) { + ath11k_warn(ab, "failed to fetch mgmt rx hdr"); + kfree(tb); + return -EPROTO; + } + + hdr->pdev_id = ev->pdev_id; + hdr->channel = ev->channel; + hdr->snr = ev->snr; + hdr->rate = ev->rate; + hdr->phy_mode = ev->phy_mode; + hdr->buf_len = ev->buf_len; + hdr->status = ev->status; + hdr->flags = ev->flags; + hdr->rssi = ev->rssi; + hdr->tsf_delta = ev->tsf_delta; + memcpy(hdr->rssi_ctl, ev->rssi_ctl, sizeof(hdr->rssi_ctl)); + + if (skb->len < (frame - skb->data) + hdr->buf_len) { + ath11k_warn(ab, "invalid length in mgmt rx hdr ev"); + kfree(tb); + return -EPROTO; + } + + /* shift the sk_buff to point to `frame` */ + skb_trim(skb, 0); + skb_put(skb, frame - skb->data); + skb_pull(skb, frame - skb->data); + skb_put(skb, hdr->buf_len); + + ath11k_ce_byte_swap(skb->data, hdr->buf_len); + + kfree(tb); + return 0; +} + +static int wmi_process_mgmt_tx_comp(struct ath11k *ar, u32 desc_id, + u32 status) +{ + struct sk_buff *msdu; + struct ieee80211_tx_info *info; + struct ath11k_skb_cb *skb_cb; + + spin_lock_bh(&ar->txmgmt_idr_lock); + msdu = idr_find(&ar->txmgmt_idr, desc_id); + + if (!msdu) { + ath11k_warn(ar->ab, "received mgmt tx compl for invalid msdu_id: %d\n", + desc_id); + spin_unlock_bh(&ar->txmgmt_idr_lock); + return -ENOENT; + } + + idr_remove(&ar->txmgmt_idr, desc_id); + spin_unlock_bh(&ar->txmgmt_idr_lock); + + skb_cb = ATH11K_SKB_CB(msdu); + dma_unmap_single(ar->ab->dev, skb_cb->paddr, msdu->len, DMA_TO_DEVICE); + + info = IEEE80211_SKB_CB(msdu); + if ((!(info->flags & IEEE80211_TX_CTL_NO_ACK)) && !status) + info->flags |= IEEE80211_TX_STAT_ACK; + + ieee80211_tx_status_irqsafe(ar->hw, msdu); + + WARN_ON_ONCE(atomic_read(&ar->num_pending_mgmt_tx) == 0); + atomic_dec(&ar->num_pending_mgmt_tx); + + return 0; +} + +static int ath11k_pull_mgmt_tx_compl_param_tlv(struct ath11k_base *ab, + struct sk_buff *skb, + struct wmi_mgmt_tx_compl_event *param) +{ + const void **tb; + const struct wmi_mgmt_tx_compl_event *ev; + int ret; + + tb = ath11k_wmi_tlv_parse_alloc(ab, skb->data, skb->len, GFP_ATOMIC); + if (IS_ERR(tb)) { + ret = PTR_ERR(tb); + ath11k_warn(ab, "failed to parse tlv: %d\n", ret); + return ret; + } + + ev = tb[WMI_TAG_MGMT_TX_COMPL_EVENT]; + if (!ev) { + ath11k_warn(ab, "failed to fetch mgmt tx compl ev"); + kfree(tb); + return -EPROTO; + } + + param->pdev_id = ev->pdev_id; + param->desc_id = ev->desc_id; + param->status = ev->status; + + kfree(tb); + return 0; +} + +static void ath11k_wmi_event_scan_started(struct ath11k *ar) +{ + lockdep_assert_held(&ar->data_lock); + + switch (ar->scan.state) { + case ATH11K_SCAN_IDLE: + case ATH11K_SCAN_RUNNING: + case ATH11K_SCAN_ABORTING: + ath11k_warn(ar->ab, "received scan started event in an invalid scan state: %s (%d)\n", + ath11k_scan_state_str(ar->scan.state), + ar->scan.state); + break; + case ATH11K_SCAN_STARTING: + ar->scan.state = ATH11K_SCAN_RUNNING; + complete(&ar->scan.started); + break; + } +} + +static void ath11k_wmi_event_scan_start_failed(struct ath11k *ar) +{ + lockdep_assert_held(&ar->data_lock); + + switch (ar->scan.state) { + case ATH11K_SCAN_IDLE: + case ATH11K_SCAN_RUNNING: + case ATH11K_SCAN_ABORTING: + ath11k_warn(ar->ab, "received scan start failed event in an invalid scan state: %s (%d)\n", + ath11k_scan_state_str(ar->scan.state), + ar->scan.state); + break; + case ATH11K_SCAN_STARTING: + complete(&ar->scan.started); + __ath11k_mac_scan_finish(ar); + break; + } +} + +static void ath11k_wmi_event_scan_completed(struct ath11k *ar) +{ + lockdep_assert_held(&ar->data_lock); + + switch (ar->scan.state) { + case ATH11K_SCAN_IDLE: + case ATH11K_SCAN_STARTING: + /* One suspected reason scan can be completed while starting is + * if firmware fails to deliver all scan events to the host, + * e.g. when transport pipe is full. This has been observed + * with spectral scan phyerr events starving wmi transport + * pipe. In such case the "scan completed" event should be (and + * is) ignored by the host as it may be just firmware's scan + * state machine recovering. + */ + ath11k_warn(ar->ab, "received scan completed event in an invalid scan state: %s (%d)\n", + ath11k_scan_state_str(ar->scan.state), + ar->scan.state); + break; + case ATH11K_SCAN_RUNNING: + case ATH11K_SCAN_ABORTING: + __ath11k_mac_scan_finish(ar); + break; + } +} + +static void ath11k_wmi_event_scan_bss_chan(struct ath11k *ar) +{ + lockdep_assert_held(&ar->data_lock); + + switch (ar->scan.state) { + case ATH11K_SCAN_IDLE: + case ATH11K_SCAN_STARTING: + ath11k_warn(ar->ab, "received scan bss chan event in an invalid scan state: %s (%d)\n", + ath11k_scan_state_str(ar->scan.state), + ar->scan.state); + break; + case ATH11K_SCAN_RUNNING: + case ATH11K_SCAN_ABORTING: + ar->scan_channel = NULL; + break; + } +} + +static void ath11k_wmi_event_scan_foreign_chan(struct ath11k *ar, u32 freq) +{ + lockdep_assert_held(&ar->data_lock); + + switch (ar->scan.state) { + case ATH11K_SCAN_IDLE: + case ATH11K_SCAN_STARTING: + ath11k_warn(ar->ab, "received scan foreign chan event in an invalid scan state: %s (%d)\n", + ath11k_scan_state_str(ar->scan.state), + ar->scan.state); + break; + case ATH11K_SCAN_RUNNING: + case ATH11K_SCAN_ABORTING: + ar->scan_channel = ieee80211_get_channel(ar->hw->wiphy, freq); + break; + } +} + +static const char * +ath11k_wmi_event_scan_type_str(enum wmi_scan_event_type type, + enum wmi_scan_completion_reason reason) +{ + switch (type) { + case WMI_SCAN_EVENT_STARTED: + return "started"; + case WMI_SCAN_EVENT_COMPLETED: + switch (reason) { + case WMI_SCAN_REASON_COMPLETED: + return "completed"; + case WMI_SCAN_REASON_CANCELLED: + return "completed [cancelled]"; + case WMI_SCAN_REASON_PREEMPTED: + return "completed [preempted]"; + case WMI_SCAN_REASON_TIMEDOUT: + return "completed [timedout]"; + case WMI_SCAN_REASON_INTERNAL_FAILURE: + return "completed [internal err]"; + case WMI_SCAN_REASON_MAX: + break; + } + return "completed [unknown]"; + case WMI_SCAN_EVENT_BSS_CHANNEL: + return "bss channel"; + case WMI_SCAN_EVENT_FOREIGN_CHAN: + return "foreign channel"; + case WMI_SCAN_EVENT_DEQUEUED: + return "dequeued"; + case WMI_SCAN_EVENT_PREEMPTED: + return "preempted"; + case WMI_SCAN_EVENT_START_FAILED: + return "start failed"; + case WMI_SCAN_EVENT_RESTARTED: + return "restarted"; + case WMI_SCAN_EVENT_FOREIGN_CHAN_EXIT: + return "foreign channel exit"; + default: + return "unknown"; + } +} + +static int ath11k_pull_scan_ev(struct ath11k_base *ab, struct sk_buff *skb, + struct wmi_scan_event *scan_evt_param) +{ + const void **tb; + const struct wmi_scan_event *ev; + int ret; + + tb = ath11k_wmi_tlv_parse_alloc(ab, skb->data, skb->len, GFP_ATOMIC); + if (IS_ERR(tb)) { + ret = PTR_ERR(tb); + ath11k_warn(ab, "failed to parse tlv: %d\n", ret); + return ret; + } + + ev = tb[WMI_TAG_SCAN_EVENT]; + if (!ev) { + ath11k_warn(ab, "failed to fetch scan ev"); + kfree(tb); + return -EPROTO; + } + + scan_evt_param->event_type = ev->event_type; + scan_evt_param->reason = ev->reason; + scan_evt_param->channel_freq = ev->channel_freq; + scan_evt_param->scan_req_id = ev->scan_req_id; + scan_evt_param->scan_id = ev->scan_id; + scan_evt_param->vdev_id = ev->vdev_id; + scan_evt_param->tsf_timestamp = ev->tsf_timestamp; + + kfree(tb); + return 0; +} + +static int ath11k_pull_peer_sta_kickout_ev(struct ath11k_base *ab, struct sk_buff *skb, + struct wmi_peer_sta_kickout_arg *arg) +{ + const void **tb; + const struct wmi_peer_sta_kickout_event *ev; + int ret; + + tb = ath11k_wmi_tlv_parse_alloc(ab, skb->data, skb->len, GFP_ATOMIC); + if (IS_ERR(tb)) { + ret = PTR_ERR(tb); + ath11k_warn(ab, "failed to parse tlv: %d\n", ret); + return ret; + } + + ev = tb[WMI_TAG_PEER_STA_KICKOUT_EVENT]; + if (!ev) { + ath11k_warn(ab, "failed to fetch peer sta kickout ev"); + kfree(tb); + return -EPROTO; + } + + arg->mac_addr = ev->peer_macaddr.addr; + + kfree(tb); + return 0; +} + +static int ath11k_pull_roam_ev(struct ath11k_base *ab, struct sk_buff *skb, + struct wmi_roam_event *roam_ev) +{ + const void **tb; + const struct wmi_roam_event *ev; + int ret; + + tb = ath11k_wmi_tlv_parse_alloc(ab, skb->data, skb->len, GFP_ATOMIC); + if (IS_ERR(tb)) { + ret = PTR_ERR(tb); + ath11k_warn(ab, "failed to parse tlv: %d\n", ret); + return ret; + } + + ev = tb[WMI_TAG_ROAM_EVENT]; + if (!ev) { + ath11k_warn(ab, "failed to fetch roam ev"); + kfree(tb); + return -EPROTO; + } + + roam_ev->vdev_id = ev->vdev_id; + roam_ev->reason = ev->reason; + roam_ev->rssi = ev->rssi; + + kfree(tb); + return 0; +} + +static int freq_to_idx(struct ath11k *ar, int freq) +{ + struct ieee80211_supported_band *sband; + int band, ch, idx = 0; + + for (band = NL80211_BAND_2GHZ; band < NUM_NL80211_BANDS; band++) { + sband = ar->hw->wiphy->bands[band]; + if (!sband) + continue; + + for (ch = 0; ch < sband->n_channels; ch++, idx++) + if (sband->channels[ch].center_freq == freq) + goto exit; + } + +exit: + return idx; +} + +static int ath11k_pull_chan_info_ev(struct ath11k_base *ab, u8 *evt_buf, + u32 len, struct wmi_chan_info_event *ch_info_ev) +{ + const void **tb; + const struct wmi_chan_info_event *ev; + int ret; + + tb = ath11k_wmi_tlv_parse_alloc(ab, evt_buf, len, GFP_ATOMIC); + if (IS_ERR(tb)) { + ret = PTR_ERR(tb); + ath11k_warn(ab, "failed to parse tlv: %d\n", ret); + return ret; + } + + ev = tb[WMI_TAG_CHAN_INFO_EVENT]; + if (!ev) { + ath11k_warn(ab, "failed to fetch chan info ev"); + kfree(tb); + return -EPROTO; + } + + ch_info_ev->err_code = ev->err_code; + ch_info_ev->freq = ev->freq; + ch_info_ev->cmd_flags = ev->cmd_flags; + ch_info_ev->noise_floor = ev->noise_floor; + ch_info_ev->rx_clear_count = ev->rx_clear_count; + ch_info_ev->cycle_count = ev->cycle_count; + ch_info_ev->chan_tx_pwr_range = ev->chan_tx_pwr_range; + ch_info_ev->chan_tx_pwr_tp = ev->chan_tx_pwr_tp; + ch_info_ev->rx_frame_count = ev->rx_frame_count; + ch_info_ev->tx_frame_cnt = ev->tx_frame_cnt; + ch_info_ev->mac_clk_mhz = ev->mac_clk_mhz; + ch_info_ev->vdev_id = ev->vdev_id; + + kfree(tb); + return 0; +} + +static int +ath11k_pull_pdev_bss_chan_info_ev(struct ath11k_base *ab, struct sk_buff *skb, + struct wmi_pdev_bss_chan_info_event *bss_ch_info_ev) +{ + const void **tb; + const struct wmi_pdev_bss_chan_info_event *ev; + int ret; + + tb = ath11k_wmi_tlv_parse_alloc(ab, skb->data, skb->len, GFP_ATOMIC); + if (IS_ERR(tb)) { + ret = PTR_ERR(tb); + ath11k_warn(ab, "failed to parse tlv: %d\n", ret); + return ret; + } + + ev = tb[WMI_TAG_PDEV_BSS_CHAN_INFO_EVENT]; + if (!ev) { + ath11k_warn(ab, "failed to fetch pdev bss chan info ev"); + kfree(tb); + return -EPROTO; + } + + bss_ch_info_ev->pdev_id = ev->pdev_id; + bss_ch_info_ev->freq = ev->freq; + bss_ch_info_ev->noise_floor = ev->noise_floor; + bss_ch_info_ev->rx_clear_count_low = ev->rx_clear_count_low; + bss_ch_info_ev->rx_clear_count_high = ev->rx_clear_count_high; + bss_ch_info_ev->cycle_count_low = ev->cycle_count_low; + bss_ch_info_ev->cycle_count_high = ev->cycle_count_high; + bss_ch_info_ev->tx_cycle_count_low = ev->tx_cycle_count_low; + bss_ch_info_ev->tx_cycle_count_high = ev->tx_cycle_count_high; + bss_ch_info_ev->rx_cycle_count_low = ev->rx_cycle_count_low; + bss_ch_info_ev->rx_cycle_count_high = ev->rx_cycle_count_high; + bss_ch_info_ev->rx_bss_cycle_count_low = ev->rx_bss_cycle_count_low; + bss_ch_info_ev->rx_bss_cycle_count_high = ev->rx_bss_cycle_count_high; + + kfree(tb); + return 0; +} + +static int +ath11k_pull_vdev_install_key_compl_ev(struct ath11k_base *ab, struct sk_buff *skb, + struct wmi_vdev_install_key_complete_arg *arg) +{ + const void **tb; + const struct wmi_vdev_install_key_compl_event *ev; + int ret; + + tb = ath11k_wmi_tlv_parse_alloc(ab, skb->data, skb->len, GFP_ATOMIC); + if (IS_ERR(tb)) { + ret = PTR_ERR(tb); + ath11k_warn(ab, "failed to parse tlv: %d\n", ret); + return ret; + } + + ev = tb[WMI_TAG_VDEV_INSTALL_KEY_COMPLETE_EVENT]; + if (!ev) { + ath11k_warn(ab, "failed to fetch vdev install key compl ev"); + kfree(tb); + return -EPROTO; + } + + arg->vdev_id = ev->vdev_id; + arg->macaddr = ev->peer_macaddr.addr; + arg->key_idx = ev->key_idx; + arg->key_flags = ev->key_flags; + arg->status = ev->status; + + kfree(tb); + return 0; +} + +static int ath11k_pull_peer_assoc_conf_ev(struct ath11k_base *ab, struct sk_buff *skb, + struct wmi_peer_assoc_conf_arg *peer_assoc_conf) +{ + const void **tb; + const struct wmi_peer_assoc_conf_event *ev; + int ret; + + tb = ath11k_wmi_tlv_parse_alloc(ab, skb->data, skb->len, GFP_ATOMIC); + if (IS_ERR(tb)) { + ret = PTR_ERR(tb); + ath11k_warn(ab, "failed to parse tlv: %d\n", ret); + return ret; + } + + ev = tb[WMI_TAG_PEER_ASSOC_CONF_EVENT]; + if (!ev) { + ath11k_warn(ab, "failed to fetch peer assoc conf ev"); + kfree(tb); + return -EPROTO; + } + + peer_assoc_conf->vdev_id = ev->vdev_id; + peer_assoc_conf->macaddr = ev->peer_macaddr.addr; + + kfree(tb); + return 0; +} + +static void ath11k_wmi_pull_pdev_stats_base(const struct wmi_pdev_stats_base *src, + struct ath11k_fw_stats_pdev *dst) +{ + dst->ch_noise_floor = src->chan_nf; + dst->tx_frame_count = src->tx_frame_count; + dst->rx_frame_count = src->rx_frame_count; + dst->rx_clear_count = src->rx_clear_count; + dst->cycle_count = src->cycle_count; + dst->phy_err_count = src->phy_err_count; + dst->chan_tx_power = src->chan_tx_pwr; +} + +static void +ath11k_wmi_pull_pdev_stats_tx(const struct wmi_pdev_stats_tx *src, + struct ath11k_fw_stats_pdev *dst) +{ + dst->comp_queued = src->comp_queued; + dst->comp_delivered = src->comp_delivered; + dst->msdu_enqued = src->msdu_enqued; + dst->mpdu_enqued = src->mpdu_enqued; + dst->wmm_drop = src->wmm_drop; + dst->local_enqued = src->local_enqued; + dst->local_freed = src->local_freed; + dst->hw_queued = src->hw_queued; + dst->hw_reaped = src->hw_reaped; + dst->underrun = src->underrun; + dst->tx_abort = src->tx_abort; + dst->mpdus_requed = src->mpdus_requed; + dst->tx_ko = src->tx_ko; + dst->data_rc = src->data_rc; + dst->self_triggers = src->self_triggers; + dst->sw_retry_failure = src->sw_retry_failure; + dst->illgl_rate_phy_err = src->illgl_rate_phy_err; + dst->pdev_cont_xretry = src->pdev_cont_xretry; + dst->pdev_tx_timeout = src->pdev_tx_timeout; + dst->pdev_resets = src->pdev_resets; + dst->stateless_tid_alloc_failure = src->stateless_tid_alloc_failure; + dst->phy_underrun = src->phy_underrun; + dst->txop_ovf = src->txop_ovf; +} + +static void ath11k_wmi_pull_pdev_stats_rx(const struct wmi_pdev_stats_rx *src, + struct ath11k_fw_stats_pdev *dst) +{ + dst->mid_ppdu_route_change = src->mid_ppdu_route_change; + dst->status_rcvd = src->status_rcvd; + dst->r0_frags = src->r0_frags; + dst->r1_frags = src->r1_frags; + dst->r2_frags = src->r2_frags; + dst->r3_frags = src->r3_frags; + dst->htt_msdus = src->htt_msdus; + dst->htt_mpdus = src->htt_mpdus; + dst->loc_msdus = src->loc_msdus; + dst->loc_mpdus = src->loc_mpdus; + dst->oversize_amsdu = src->oversize_amsdu; + dst->phy_errs = src->phy_errs; + dst->phy_err_drop = src->phy_err_drop; + dst->mpdu_errs = src->mpdu_errs; +} + +static void +ath11k_wmi_pull_vdev_stats(const struct wmi_vdev_stats *src, + struct ath11k_fw_stats_vdev *dst) +{ + int i; + + dst->vdev_id = src->vdev_id; + dst->beacon_snr = src->beacon_snr; + dst->data_snr = src->data_snr; + dst->num_rx_frames = src->num_rx_frames; + dst->num_rts_fail = src->num_rts_fail; + dst->num_rts_success = src->num_rts_success; + dst->num_rx_err = src->num_rx_err; + dst->num_rx_discard = src->num_rx_discard; + dst->num_tx_not_acked = src->num_tx_not_acked; + + for (i = 0; i < ARRAY_SIZE(src->num_tx_frames); i++) + dst->num_tx_frames[i] = src->num_tx_frames[i]; + + for (i = 0; i < ARRAY_SIZE(src->num_tx_frames_retries); i++) + dst->num_tx_frames_retries[i] = src->num_tx_frames_retries[i]; + + for (i = 0; i < ARRAY_SIZE(src->num_tx_frames_failures); i++) + dst->num_tx_frames_failures[i] = src->num_tx_frames_failures[i]; + + for (i = 0; i < ARRAY_SIZE(src->tx_rate_history); i++) + dst->tx_rate_history[i] = src->tx_rate_history[i]; + + for (i = 0; i < ARRAY_SIZE(src->beacon_rssi_history); i++) + dst->beacon_rssi_history[i] = src->beacon_rssi_history[i]; +} + +static void +ath11k_wmi_pull_bcn_stats(const struct wmi_bcn_stats *src, + struct ath11k_fw_stats_bcn *dst) +{ + dst->vdev_id = src->vdev_id; + dst->tx_bcn_succ_cnt = src->tx_bcn_succ_cnt; + dst->tx_bcn_outage_cnt = src->tx_bcn_outage_cnt; +} + +int ath11k_wmi_pull_fw_stats(struct ath11k_base *ab, struct sk_buff *skb, + struct ath11k_fw_stats *stats) +{ + const void **tb; + const struct wmi_stats_event *ev; + const void *data; + int i, ret; + u32 len = skb->len; + + tb = ath11k_wmi_tlv_parse_alloc(ab, skb->data, len, GFP_ATOMIC); + if (IS_ERR(tb)) { + ret = PTR_ERR(tb); + ath11k_warn(ab, "failed to parse tlv: %d\n", ret); + return ret; + } + + ev = tb[WMI_TAG_STATS_EVENT]; + data = tb[WMI_TAG_ARRAY_BYTE]; + if (!ev || !data) { + ath11k_warn(ab, "failed to fetch update stats ev"); + kfree(tb); + return -EPROTO; + } + + ath11k_dbg(ab, ATH11K_DBG_WMI, + "wmi stats update ev pdev_id %d pdev %i vdev %i bcn %i\n", + ev->pdev_id, + ev->num_pdev_stats, ev->num_vdev_stats, + ev->num_bcn_stats); + + stats->pdev_id = ev->pdev_id; + stats->stats_id = 0; + + for (i = 0; i < ev->num_pdev_stats; i++) { + const struct wmi_pdev_stats *src; + struct ath11k_fw_stats_pdev *dst; + + src = data; + if (len < sizeof(*src)) { + kfree(tb); + return -EPROTO; + } + + stats->stats_id = WMI_REQUEST_PDEV_STAT; + + data += sizeof(*src); + len -= sizeof(*src); + + dst = kzalloc(sizeof(*dst), GFP_ATOMIC); + if (!dst) + continue; + + ath11k_wmi_pull_pdev_stats_base(&src->base, dst); + ath11k_wmi_pull_pdev_stats_tx(&src->tx, dst); + ath11k_wmi_pull_pdev_stats_rx(&src->rx, dst); + list_add_tail(&dst->list, &stats->pdevs); + } + + for (i = 0; i < ev->num_vdev_stats; i++) { + const struct wmi_vdev_stats *src; + struct ath11k_fw_stats_vdev *dst; + + src = data; + if (len < sizeof(*src)) { + kfree(tb); + return -EPROTO; + } + + stats->stats_id = WMI_REQUEST_VDEV_STAT; + + data += sizeof(*src); + len -= sizeof(*src); + + dst = kzalloc(sizeof(*dst), GFP_ATOMIC); + if (!dst) + continue; + + ath11k_wmi_pull_vdev_stats(src, dst); + list_add_tail(&dst->list, &stats->vdevs); + } + + for (i = 0; i < ev->num_bcn_stats; i++) { + const struct wmi_bcn_stats *src; + struct ath11k_fw_stats_bcn *dst; + + src = data; + if (len < sizeof(*src)) { + kfree(tb); + return -EPROTO; + } + + stats->stats_id = WMI_REQUEST_BCN_STAT; + + data += sizeof(*src); + len -= sizeof(*src); + + dst = kzalloc(sizeof(*dst), GFP_ATOMIC); + if (!dst) + continue; + + ath11k_wmi_pull_bcn_stats(src, dst); + list_add_tail(&dst->list, &stats->bcn); + } + + kfree(tb); + return 0; +} + +size_t ath11k_wmi_fw_stats_num_vdevs(struct list_head *head) +{ + struct ath11k_fw_stats_vdev *i; + size_t num = 0; + + list_for_each_entry(i, head, list) + ++num; + + return num; +} + +static size_t ath11k_wmi_fw_stats_num_bcn(struct list_head *head) +{ + struct ath11k_fw_stats_bcn *i; + size_t num = 0; + + list_for_each_entry(i, head, list) + ++num; + + return num; +} + +static void +ath11k_wmi_fw_pdev_base_stats_fill(const struct ath11k_fw_stats_pdev *pdev, + char *buf, u32 *length) +{ + u32 len = *length; + u32 buf_len = ATH11K_FW_STATS_BUF_SIZE; + + len += scnprintf(buf + len, buf_len - len, "\n"); + len += scnprintf(buf + len, buf_len - len, "%30s\n", + "ath11k PDEV stats"); + len += scnprintf(buf + len, buf_len - len, "%30s\n\n", + "================="); + + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "Channel noise floor", pdev->ch_noise_floor); + len += scnprintf(buf + len, buf_len - len, "%30s %10u\n", + "Channel TX power", pdev->chan_tx_power); + len += scnprintf(buf + len, buf_len - len, "%30s %10u\n", + "TX frame count", pdev->tx_frame_count); + len += scnprintf(buf + len, buf_len - len, "%30s %10u\n", + "RX frame count", pdev->rx_frame_count); + len += scnprintf(buf + len, buf_len - len, "%30s %10u\n", + "RX clear count", pdev->rx_clear_count); + len += scnprintf(buf + len, buf_len - len, "%30s %10u\n", + "Cycle count", pdev->cycle_count); + len += scnprintf(buf + len, buf_len - len, "%30s %10u\n", + "PHY error count", pdev->phy_err_count); + + *length = len; +} + +static void +ath11k_wmi_fw_pdev_tx_stats_fill(const struct ath11k_fw_stats_pdev *pdev, + char *buf, u32 *length) +{ + u32 len = *length; + u32 buf_len = ATH11K_FW_STATS_BUF_SIZE; + + len += scnprintf(buf + len, buf_len - len, "\n%30s\n", + "ath11k PDEV TX stats"); + len += scnprintf(buf + len, buf_len - len, "%30s\n\n", + "===================="); + + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "HTT cookies queued", pdev->comp_queued); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "HTT cookies disp.", pdev->comp_delivered); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "MSDU queued", pdev->msdu_enqued); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "MPDU queued", pdev->mpdu_enqued); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "MSDUs dropped", pdev->wmm_drop); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "Local enqued", pdev->local_enqued); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "Local freed", pdev->local_freed); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "HW queued", pdev->hw_queued); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "PPDUs reaped", pdev->hw_reaped); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "Num underruns", pdev->underrun); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "PPDUs cleaned", pdev->tx_abort); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "MPDUs requed", pdev->mpdus_requed); + len += scnprintf(buf + len, buf_len - len, "%30s %10u\n", + "Excessive retries", pdev->tx_ko); + len += scnprintf(buf + len, buf_len - len, "%30s %10u\n", + "HW rate", pdev->data_rc); + len += scnprintf(buf + len, buf_len - len, "%30s %10u\n", + "Sched self triggers", pdev->self_triggers); + len += scnprintf(buf + len, buf_len - len, "%30s %10u\n", + "Dropped due to SW retries", + pdev->sw_retry_failure); + len += scnprintf(buf + len, buf_len - len, "%30s %10u\n", + "Illegal rate phy errors", + pdev->illgl_rate_phy_err); + len += scnprintf(buf + len, buf_len - len, "%30s %10u\n", + "PDEV continuous xretry", pdev->pdev_cont_xretry); + len += scnprintf(buf + len, buf_len - len, "%30s %10u\n", + "TX timeout", pdev->pdev_tx_timeout); + len += scnprintf(buf + len, buf_len - len, "%30s %10u\n", + "PDEV resets", pdev->pdev_resets); + len += scnprintf(buf + len, buf_len - len, "%30s %10u\n", + "Stateless TIDs alloc failures", + pdev->stateless_tid_alloc_failure); + len += scnprintf(buf + len, buf_len - len, "%30s %10u\n", + "PHY underrun", pdev->phy_underrun); + len += scnprintf(buf + len, buf_len - len, "%30s %10u\n", + "MPDU is more than txop limit", pdev->txop_ovf); + *length = len; +} + +static void +ath11k_wmi_fw_pdev_rx_stats_fill(const struct ath11k_fw_stats_pdev *pdev, + char *buf, u32 *length) +{ + u32 len = *length; + u32 buf_len = ATH11K_FW_STATS_BUF_SIZE; + + len += scnprintf(buf + len, buf_len - len, "\n%30s\n", + "ath11k PDEV RX stats"); + len += scnprintf(buf + len, buf_len - len, "%30s\n\n", + "===================="); + + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "Mid PPDU route change", + pdev->mid_ppdu_route_change); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "Tot. number of statuses", pdev->status_rcvd); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "Extra frags on rings 0", pdev->r0_frags); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "Extra frags on rings 1", pdev->r1_frags); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "Extra frags on rings 2", pdev->r2_frags); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "Extra frags on rings 3", pdev->r3_frags); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "MSDUs delivered to HTT", pdev->htt_msdus); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "MPDUs delivered to HTT", pdev->htt_mpdus); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "MSDUs delivered to stack", pdev->loc_msdus); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "MPDUs delivered to stack", pdev->loc_mpdus); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "Oversized AMSUs", pdev->oversize_amsdu); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "PHY errors", pdev->phy_errs); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "PHY errors drops", pdev->phy_err_drop); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "MPDU errors (FCS, MIC, ENC)", pdev->mpdu_errs); + *length = len; +} + +static void +ath11k_wmi_fw_vdev_stats_fill(struct ath11k *ar, + const struct ath11k_fw_stats_vdev *vdev, + char *buf, u32 *length) +{ + u32 len = *length; + u32 buf_len = ATH11K_FW_STATS_BUF_SIZE; + struct ath11k_vif *arvif = ath11k_mac_get_arvif(ar, vdev->vdev_id); + u8 *vif_macaddr; + int i; + + /* VDEV stats has all the active VDEVs of other PDEVs as well, + * ignoring those not part of requested PDEV + */ + if (!arvif) + return; + + vif_macaddr = arvif->vif->addr; + + len += scnprintf(buf + len, buf_len - len, "%30s %u\n", + "VDEV ID", vdev->vdev_id); + len += scnprintf(buf + len, buf_len - len, "%30s %pM\n", + "VDEV MAC address", vif_macaddr); + len += scnprintf(buf + len, buf_len - len, "%30s %u\n", + "beacon snr", vdev->beacon_snr); + len += scnprintf(buf + len, buf_len - len, "%30s %u\n", + "data snr", vdev->data_snr); + len += scnprintf(buf + len, buf_len - len, "%30s %u\n", + "num rx frames", vdev->num_rx_frames); + len += scnprintf(buf + len, buf_len - len, "%30s %u\n", + "num rts fail", vdev->num_rts_fail); + len += scnprintf(buf + len, buf_len - len, "%30s %u\n", + "num rts success", vdev->num_rts_success); + len += scnprintf(buf + len, buf_len - len, "%30s %u\n", + "num rx err", vdev->num_rx_err); + len += scnprintf(buf + len, buf_len - len, "%30s %u\n", + "num rx discard", vdev->num_rx_discard); + len += scnprintf(buf + len, buf_len - len, "%30s %u\n", + "num tx not acked", vdev->num_tx_not_acked); + + for (i = 0 ; i < ARRAY_SIZE(vdev->num_tx_frames); i++) + len += scnprintf(buf + len, buf_len - len, + "%25s [%02d] %u\n", + "num tx frames", i, + vdev->num_tx_frames[i]); + + for (i = 0 ; i < ARRAY_SIZE(vdev->num_tx_frames_retries); i++) + len += scnprintf(buf + len, buf_len - len, + "%25s [%02d] %u\n", + "num tx frames retries", i, + vdev->num_tx_frames_retries[i]); + + for (i = 0 ; i < ARRAY_SIZE(vdev->num_tx_frames_failures); i++) + len += scnprintf(buf + len, buf_len - len, + "%25s [%02d] %u\n", + "num tx frames failures", i, + vdev->num_tx_frames_failures[i]); + + for (i = 0 ; i < ARRAY_SIZE(vdev->tx_rate_history); i++) + len += scnprintf(buf + len, buf_len - len, + "%25s [%02d] 0x%08x\n", + "tx rate history", i, + vdev->tx_rate_history[i]); + + for (i = 0 ; i < ARRAY_SIZE(vdev->beacon_rssi_history); i++) + len += scnprintf(buf + len, buf_len - len, + "%25s [%02d] %u\n", + "beacon rssi history", i, + vdev->beacon_rssi_history[i]); + + len += scnprintf(buf + len, buf_len - len, "\n"); + *length = len; +} + +static void +ath11k_wmi_fw_bcn_stats_fill(struct ath11k *ar, + const struct ath11k_fw_stats_bcn *bcn, + char *buf, u32 *length) +{ + u32 len = *length; + u32 buf_len = ATH11K_FW_STATS_BUF_SIZE; + struct ath11k_vif *arvif = ath11k_mac_get_arvif(ar, bcn->vdev_id); + u8 *vdev_macaddr; + + if (!arvif) { + ath11k_warn(ar->ab, "invalid vdev id %d in bcn stats", + bcn->vdev_id); + return; + } + + vdev_macaddr = arvif->vif->addr; + + len += scnprintf(buf + len, buf_len - len, "%30s %u\n", + "VDEV ID", bcn->vdev_id); + len += scnprintf(buf + len, buf_len - len, "%30s %pM\n", + "VDEV MAC address", vdev_macaddr); + len += scnprintf(buf + len, buf_len - len, "%30s\n\n", + "================"); + len += scnprintf(buf + len, buf_len - len, "%30s %u\n", + "Num of beacon tx success", bcn->tx_bcn_succ_cnt); + len += scnprintf(buf + len, buf_len - len, "%30s %u\n", + "Num of beacon tx failures", bcn->tx_bcn_outage_cnt); + + len += scnprintf(buf + len, buf_len - len, "\n"); + *length = len; +} + +void ath11k_wmi_fw_stats_fill(struct ath11k *ar, + struct ath11k_fw_stats *fw_stats, + u32 stats_id, char *buf) +{ + u32 len = 0; + u32 buf_len = ATH11K_FW_STATS_BUF_SIZE; + const struct ath11k_fw_stats_pdev *pdev; + const struct ath11k_fw_stats_vdev *vdev; + const struct ath11k_fw_stats_bcn *bcn; + size_t num_bcn; + + spin_lock_bh(&ar->data_lock); + + if (stats_id == WMI_REQUEST_PDEV_STAT) { + pdev = list_first_entry_or_null(&fw_stats->pdevs, + struct ath11k_fw_stats_pdev, list); + if (!pdev) { + ath11k_warn(ar->ab, "failed to get pdev stats\n"); + goto unlock; + } + + ath11k_wmi_fw_pdev_base_stats_fill(pdev, buf, &len); + ath11k_wmi_fw_pdev_tx_stats_fill(pdev, buf, &len); + ath11k_wmi_fw_pdev_rx_stats_fill(pdev, buf, &len); + } + + if (stats_id == WMI_REQUEST_VDEV_STAT) { + len += scnprintf(buf + len, buf_len - len, "\n"); + len += scnprintf(buf + len, buf_len - len, "%30s\n", + "ath11k VDEV stats"); + len += scnprintf(buf + len, buf_len - len, "%30s\n\n", + "================="); + + list_for_each_entry(vdev, &fw_stats->vdevs, list) + ath11k_wmi_fw_vdev_stats_fill(ar, vdev, buf, &len); + } + + if (stats_id == WMI_REQUEST_BCN_STAT) { + num_bcn = ath11k_wmi_fw_stats_num_bcn(&fw_stats->bcn); + + len += scnprintf(buf + len, buf_len - len, "\n"); + len += scnprintf(buf + len, buf_len - len, "%30s (%zu)\n", + "ath11k Beacon stats", num_bcn); + len += scnprintf(buf + len, buf_len - len, "%30s\n\n", + "==================="); + + list_for_each_entry(bcn, &fw_stats->bcn, list) + ath11k_wmi_fw_bcn_stats_fill(ar, bcn, buf, &len); + } + +unlock: + spin_unlock_bh(&ar->data_lock); + + if (len >= buf_len) + buf[len - 1] = 0; + else + buf[len] = 0; +} + +static void ath11k_wmi_op_ep_tx_credits(struct ath11k_base *ab) +{ + /* try to send pending beacons first. they take priority */ + wake_up(&ab->wmi_sc.tx_credits_wq); +} + +static void ath11k_wmi_htc_tx_complete(struct ath11k_base *ab, + struct sk_buff *skb) +{ + dev_kfree_skb(skb); +} + +static bool ath11k_reg_is_world_alpha(char *alpha) +{ + return alpha[0] == '0' && alpha[1] == '0'; +} + +static int ath11k_reg_chan_list_event(struct ath11k_base *ab, struct sk_buff *skb) +{ + struct cur_regulatory_info *reg_info = NULL; + struct ieee80211_regdomain *regd = NULL; + bool intersect = false; + int ret = 0, pdev_idx; + struct ath11k *ar; + + reg_info = kzalloc(sizeof(*reg_info), GFP_ATOMIC); + if (!reg_info) { + ret = -ENOMEM; + goto fallback; + } + + ret = ath11k_pull_reg_chan_list_update_ev(ab, skb, reg_info); + if (ret) { + ath11k_warn(ab, "failed to extract regulatory info from received event\n"); + goto fallback; + } + + if (reg_info->status_code != REG_SET_CC_STATUS_PASS) { + /* In case of failure to set the requested ctry, + * fw retains the current regd. We print a failure info + * and return from here. + */ + ath11k_warn(ab, "Failed to set the requested Country regulatory setting\n"); + goto mem_free; + } + + pdev_idx = reg_info->phy_id; + + if (pdev_idx >= ab->num_radios) + goto fallback; + + /* Avoid multiple overwrites to default regd, during core + * stop-start after mac registration. + */ + if (ab->default_regd[pdev_idx] && !ab->new_regd[pdev_idx] && + !memcmp((char *)ab->default_regd[pdev_idx]->alpha2, + (char *)reg_info->alpha2, 2)) + return 0; + + /* Intersect new rules with default regd if a new country setting was + * requested, i.e a default regd was already set during initialization + * and the regd coming from this event has a valid country info. + */ + if (ab->default_regd[pdev_idx] && + !ath11k_reg_is_world_alpha((char *) + ab->default_regd[pdev_idx]->alpha2) && + !ath11k_reg_is_world_alpha((char *)reg_info->alpha2)) + intersect = true; + + regd = ath11k_reg_build_regd(ab, reg_info, intersect); + if (!regd) { + ath11k_warn(ab, "failed to build regd from reg_info\n"); + goto fallback; + } + + spin_lock(&ab->base_lock); + if (test_bit(ATH11K_FLAG_REGISTERED, &ab->dev_flags)) { + /* Once mac is registered, ar is valid and all CC events from + * fw is considered to be received due to user requests + * currently. + * Free previously built regd before assigning the newly + * generated regd to ar. NULL pointer handling will be + * taken care by kfree itself. + */ + ar = ab->pdevs[pdev_idx].ar; + kfree(ab->new_regd[pdev_idx]); + ab->new_regd[pdev_idx] = regd; + ieee80211_queue_work(ar->hw, &ar->regd_update_work); + } else { + /* Multiple events for the same *ar is not expected. But we + * can still clear any previously stored default_regd if we + * are receiving this event for the same radio by mistake. + * NULL pointer handling will be taken care by kfree itself. + */ + kfree(ab->default_regd[pdev_idx]); + /* This regd would be applied during mac registration */ + ab->default_regd[pdev_idx] = regd; + } + ab->dfs_region = reg_info->dfs_region; + spin_unlock(&ab->base_lock); + + goto mem_free; + +fallback: + /* Fallback to older reg (by sending previous country setting + * again if fw has succeded and we failed to process here. + * The Regdomain should be uniform across driver and fw. Since the + * FW has processed the command and sent a success status, we expect + * this function to succeed as well. If it doesn't, CTRY needs to be + * reverted at the fw and the old SCAN_CHAN_LIST cmd needs to be sent. + */ + /* TODO: This is rare, but still should also be handled */ + WARN_ON(1); +mem_free: + if (reg_info) { + kfree(reg_info->reg_rules_2g_ptr); + kfree(reg_info->reg_rules_5g_ptr); + kfree(reg_info); + } + return ret; +} + +static int ath11k_wmi_tlv_rdy_parse(struct ath11k_base *ab, u16 tag, u16 len, + const void *ptr, void *data) +{ + struct wmi_tlv_rdy_parse *rdy_parse = data; + struct wmi_ready_event *fixed_param; + struct wmi_mac_addr *addr_list; + struct ath11k_pdev *pdev; + u32 num_mac_addr; + int i; + + switch (tag) { + case WMI_TAG_READY_EVENT: + fixed_param = (struct wmi_ready_event *)ptr; + ab->wlan_init_status = fixed_param->status; + rdy_parse->num_extra_mac_addr = fixed_param->num_extra_mac_addr; + + ether_addr_copy(ab->mac_addr, fixed_param->mac_addr.addr); + ab->wmi_ready = true; + break; + case WMI_TAG_ARRAY_FIXED_STRUCT: + addr_list = (struct wmi_mac_addr *)ptr; + num_mac_addr = rdy_parse->num_extra_mac_addr; + + if (!(ab->num_radios > 1 && num_mac_addr >= ab->num_radios)) + break; + + for (i = 0; i < ab->num_radios; i++) { + pdev = &ab->pdevs[i]; + ether_addr_copy(pdev->mac_addr, addr_list[i].addr); + } + ab->pdevs_macaddr_valid = true; + break; + default: + break; + } + + return 0; +} + +static int ath11k_ready_event(struct ath11k_base *ab, struct sk_buff *skb) +{ + struct wmi_tlv_rdy_parse rdy_parse = { }; + int ret; + + ret = ath11k_wmi_tlv_iter(ab, skb->data, skb->len, + ath11k_wmi_tlv_rdy_parse, &rdy_parse); + if (ret) { + ath11k_warn(ab, "failed to parse tlv %d\n", ret); + return ret; + } + + complete(&ab->wmi_sc.unified_ready); + return 0; +} + +static void ath11k_peer_delete_resp_event(struct ath11k_base *ab, struct sk_buff *skb) +{ + struct wmi_peer_delete_resp_event peer_del_resp; + + if (ath11k_pull_peer_del_resp_ev(ab, skb, &peer_del_resp) != 0) { + ath11k_warn(ab, "failed to extract peer delete resp"); + return; + } + + /* TODO: Do we need to validate whether ath11k_peer_find() return NULL + * Why this is needed when there is HTT event for peer delete + */ +} + +static inline const char *ath11k_wmi_vdev_resp_print(u32 vdev_resp_status) +{ + switch (vdev_resp_status) { + case WMI_VDEV_START_RESPONSE_INVALID_VDEVID: + return "invalid vdev id"; + case WMI_VDEV_START_RESPONSE_NOT_SUPPORTED: + return "not supported"; + case WMI_VDEV_START_RESPONSE_DFS_VIOLATION: + return "dfs violation"; + case WMI_VDEV_START_RESPONSE_INVALID_REGDOMAIN: + return "invalid regdomain"; + default: + return "unknown"; + } +} + +static void ath11k_vdev_start_resp_event(struct ath11k_base *ab, struct sk_buff *skb) +{ + struct wmi_vdev_start_resp_event vdev_start_resp; + struct ath11k *ar; + u32 status; + + if (ath11k_pull_vdev_start_resp_tlv(ab, skb, &vdev_start_resp) != 0) { + ath11k_warn(ab, "failed to extract vdev start resp"); + return; + } + + rcu_read_lock(); + ar = ath11k_mac_get_ar_by_vdev_id(ab, vdev_start_resp.vdev_id); + if (!ar) { + ath11k_warn(ab, "invalid vdev id in vdev start resp ev %d", + vdev_start_resp.vdev_id); + rcu_read_unlock(); + return; + } + + ar->last_wmi_vdev_start_status = 0; + + status = vdev_start_resp.status; + + if (WARN_ON_ONCE(status)) { + ath11k_warn(ab, "vdev start resp error status %d (%s)\n", + status, ath11k_wmi_vdev_resp_print(status)); + ar->last_wmi_vdev_start_status = status; + } + + complete(&ar->vdev_setup_done); + + rcu_read_unlock(); + + ath11k_dbg(ab, ATH11K_DBG_WMI, "vdev start resp for vdev id %d", + vdev_start_resp.vdev_id); +} + +static void ath11k_bcn_tx_status_event(struct ath11k_base *ab, struct sk_buff *skb) +{ + u32 vdev_id, tx_status; + + if (ath11k_pull_bcn_tx_status_ev(ab, skb->data, skb->len, + &vdev_id, &tx_status) != 0) { + ath11k_warn(ab, "failed to extract bcn tx status"); + return; + } +} + +static void ath11k_vdev_stopped_event(struct ath11k_base *ab, struct sk_buff *skb) +{ + struct ath11k *ar; + u32 vdev_id = 0; + + if (ath11k_pull_vdev_stopped_param_tlv(ab, skb, &vdev_id) != 0) { + ath11k_warn(ab, "failed to extract vdev stopped event"); + return; + } + + rcu_read_lock(); + ar = ath11k_mac_get_ar_vdev_stop_status(ab, vdev_id); + if (!ar) { + ath11k_warn(ab, "invalid vdev id in vdev stopped ev %d", + vdev_id); + rcu_read_unlock(); + return; + } + + complete(&ar->vdev_setup_done); + + rcu_read_unlock(); + + ath11k_dbg(ab, ATH11K_DBG_WMI, "vdev stopped for vdev id %d", vdev_id); +} + +static void ath11k_mgmt_rx_event(struct ath11k_base *ab, struct sk_buff *skb) +{ + struct mgmt_rx_event_params rx_ev = {0}; + struct ath11k *ar; + struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb); + struct ieee80211_hdr *hdr; + u16 fc; + struct ieee80211_supported_band *sband; + + if (ath11k_pull_mgmt_rx_params_tlv(ab, skb, &rx_ev) != 0) { + ath11k_warn(ab, "failed to extract mgmt rx event"); + dev_kfree_skb(skb); + return; + } + + memset(status, 0, sizeof(*status)); + + ath11k_dbg(ab, ATH11K_DBG_MGMT, "mgmt rx event status %08x\n", + rx_ev.status); + + rcu_read_lock(); + ar = ath11k_mac_get_ar_by_pdev_id(ab, rx_ev.pdev_id); + + if (!ar) { + ath11k_warn(ab, "invalid pdev_id %d in mgmt_rx_event\n", + rx_ev.pdev_id); + dev_kfree_skb(skb); + goto exit; + } + + if ((test_bit(ATH11K_CAC_RUNNING, &ar->dev_flags)) || + (rx_ev.status & (WMI_RX_STATUS_ERR_DECRYPT | + WMI_RX_STATUS_ERR_KEY_CACHE_MISS | WMI_RX_STATUS_ERR_CRC))) { + dev_kfree_skb(skb); + goto exit; + } + + if (rx_ev.status & WMI_RX_STATUS_ERR_MIC) + status->flag |= RX_FLAG_MMIC_ERROR; + + if (rx_ev.channel >= 1 && rx_ev.channel <= 14) { + status->band = NL80211_BAND_2GHZ; + } else if (rx_ev.channel >= 36 && rx_ev.channel <= ATH11K_MAX_5G_CHAN) { + status->band = NL80211_BAND_5GHZ; + } else { + /* Shouldn't happen unless list of advertised channels to + * mac80211 has been changed. + */ + WARN_ON_ONCE(1); + dev_kfree_skb(skb); + goto exit; + } + + if (rx_ev.phy_mode == MODE_11B && status->band == NL80211_BAND_5GHZ) + ath11k_dbg(ab, ATH11K_DBG_WMI, + "wmi mgmt rx 11b (CCK) on 5GHz\n"); + + sband = &ar->mac.sbands[status->band]; + + status->freq = ieee80211_channel_to_frequency(rx_ev.channel, + status->band); + status->signal = rx_ev.snr + ATH11K_DEFAULT_NOISE_FLOOR; + status->rate_idx = ath11k_mac_bitrate_to_idx(sband, rx_ev.rate / 100); + + hdr = (struct ieee80211_hdr *)skb->data; + fc = le16_to_cpu(hdr->frame_control); + + /* Firmware is guaranteed to report all essential management frames via + * WMI while it can deliver some extra via HTT. Since there can be + * duplicates split the reporting wrt monitor/sniffing. + */ + status->flag |= RX_FLAG_SKIP_MONITOR; + + /* In case of PMF, FW delivers decrypted frames with Protected Bit set. + * Don't clear that. Also, FW delivers broadcast management frames + * (ex: group privacy action frames in mesh) as encrypted payload. + */ + if (ieee80211_has_protected(hdr->frame_control) && + !is_multicast_ether_addr(ieee80211_get_DA(hdr))) { + status->flag |= RX_FLAG_DECRYPTED; + + if (!ieee80211_is_robust_mgmt_frame(skb)) { + status->flag |= RX_FLAG_IV_STRIPPED | + RX_FLAG_MMIC_STRIPPED; + hdr->frame_control = __cpu_to_le16(fc & + ~IEEE80211_FCTL_PROTECTED); + } + } + + /* TODO: Pending handle beacon implementation + *if (ieee80211_is_beacon(hdr->frame_control)) + * ath11k_mac_handle_beacon(ar, skb); + */ + + ath11k_dbg(ab, ATH11K_DBG_MGMT, + "event mgmt rx skb %pK len %d ftype %02x stype %02x\n", + skb, skb->len, + fc & IEEE80211_FCTL_FTYPE, fc & IEEE80211_FCTL_STYPE); + + ath11k_dbg(ab, ATH11K_DBG_MGMT, + "event mgmt rx freq %d band %d snr %d, rate_idx %d\n", + status->freq, status->band, status->signal, + status->rate_idx); + + ieee80211_rx_ni(ar->hw, skb); + +exit: + rcu_read_unlock(); +} + +static void ath11k_mgmt_tx_compl_event(struct ath11k_base *ab, struct sk_buff *skb) +{ + struct wmi_mgmt_tx_compl_event tx_compl_param = {0}; + struct ath11k *ar; + + if (ath11k_pull_mgmt_tx_compl_param_tlv(ab, skb, &tx_compl_param) != 0) { + ath11k_warn(ab, "failed to extract mgmt tx compl event"); + return; + } + + rcu_read_lock(); + ar = ath11k_mac_get_ar_by_pdev_id(ab, tx_compl_param.pdev_id); + if (!ar) { + ath11k_warn(ab, "invalid pdev id %d in mgmt_tx_compl_event\n", + tx_compl_param.pdev_id); + goto exit; + } + + wmi_process_mgmt_tx_comp(ar, tx_compl_param.desc_id, + tx_compl_param.status); + + ath11k_dbg(ab, ATH11K_DBG_MGMT, + "mgmt tx compl ev pdev_id %d, desc_id %d, status %d", + tx_compl_param.pdev_id, tx_compl_param.desc_id, + tx_compl_param.status); + +exit: + rcu_read_unlock(); +} + +static struct ath11k *ath11k_get_ar_on_scan_abort(struct ath11k_base *ab, + u32 vdev_id) +{ + int i; + struct ath11k_pdev *pdev; + struct ath11k *ar; + + for (i = 0; i < ab->num_radios; i++) { + pdev = rcu_dereference(ab->pdevs_active[i]); + if (pdev && pdev->ar) { + ar = pdev->ar; + + spin_lock_bh(&ar->data_lock); + if (ar->scan.state == ATH11K_SCAN_ABORTING && + ar->scan.vdev_id == vdev_id) { + spin_unlock_bh(&ar->data_lock); + return ar; + } + spin_unlock_bh(&ar->data_lock); + } + } + return NULL; +} + +static void ath11k_scan_event(struct ath11k_base *ab, struct sk_buff *skb) +{ + struct ath11k *ar; + struct wmi_scan_event scan_ev = {0}; + + if (ath11k_pull_scan_ev(ab, skb, &scan_ev) != 0) { + ath11k_warn(ab, "failed to extract scan event"); + return; + } + + rcu_read_lock(); + + /* In case the scan was cancelled, ex. during interface teardown, + * the interface will not be found in active interfaces. + * Rather, in such scenarios, iterate over the active pdev's to + * search 'ar' if the corresponding 'ar' scan is ABORTING and the + * aborting scan's vdev id matches this event info. + */ + if (scan_ev.event_type == WMI_SCAN_EVENT_COMPLETED && + scan_ev.reason == WMI_SCAN_REASON_CANCELLED) + ar = ath11k_get_ar_on_scan_abort(ab, scan_ev.vdev_id); + else + ar = ath11k_mac_get_ar_by_vdev_id(ab, scan_ev.vdev_id); + + if (!ar) { + ath11k_warn(ab, "Received scan event for unknown vdev"); + rcu_read_unlock(); + return; + } + + spin_lock_bh(&ar->data_lock); + + ath11k_dbg(ab, ATH11K_DBG_WMI, + "scan event %s type %d reason %d freq %d req_id %d scan_id %d vdev_id %d state %s (%d)\n", + ath11k_wmi_event_scan_type_str(scan_ev.event_type, scan_ev.reason), + scan_ev.event_type, scan_ev.reason, scan_ev.channel_freq, + scan_ev.scan_req_id, scan_ev.scan_id, scan_ev.vdev_id, + ath11k_scan_state_str(ar->scan.state), ar->scan.state); + + switch (scan_ev.event_type) { + case WMI_SCAN_EVENT_STARTED: + ath11k_wmi_event_scan_started(ar); + break; + case WMI_SCAN_EVENT_COMPLETED: + ath11k_wmi_event_scan_completed(ar); + break; + case WMI_SCAN_EVENT_BSS_CHANNEL: + ath11k_wmi_event_scan_bss_chan(ar); + break; + case WMI_SCAN_EVENT_FOREIGN_CHAN: + ath11k_wmi_event_scan_foreign_chan(ar, scan_ev.channel_freq); + break; + case WMI_SCAN_EVENT_START_FAILED: + ath11k_warn(ab, "received scan start failure event\n"); + ath11k_wmi_event_scan_start_failed(ar); + break; + case WMI_SCAN_EVENT_DEQUEUED: + case WMI_SCAN_EVENT_PREEMPTED: + case WMI_SCAN_EVENT_RESTARTED: + case WMI_SCAN_EVENT_FOREIGN_CHAN_EXIT: + default: + break; + } + + spin_unlock_bh(&ar->data_lock); + + rcu_read_unlock(); +} + +static void ath11k_peer_sta_kickout_event(struct ath11k_base *ab, struct sk_buff *skb) +{ + struct wmi_peer_sta_kickout_arg arg = {}; + struct ieee80211_sta *sta; + struct ath11k_peer *peer; + struct ath11k *ar; + + if (ath11k_pull_peer_sta_kickout_ev(ab, skb, &arg) != 0) { + ath11k_warn(ab, "failed to extract peer sta kickout event"); + return; + } + + rcu_read_lock(); + + spin_lock_bh(&ab->base_lock); + + peer = ath11k_peer_find_by_addr(ab, arg.mac_addr); + + if (!peer) { + ath11k_warn(ab, "peer not found %pM\n", + arg.mac_addr); + goto exit; + } + + ar = ath11k_mac_get_ar_by_vdev_id(ab, peer->vdev_id); + if (!ar) { + ath11k_warn(ab, "invalid vdev id in peer sta kickout ev %d", + peer->vdev_id); + goto exit; + } + + sta = ieee80211_find_sta_by_ifaddr(ar->hw, + arg.mac_addr, NULL); + if (!sta) { + ath11k_warn(ab, "Spurious quick kickout for STA %pM\n", + arg.mac_addr); + goto exit; + } + + ath11k_dbg(ab, ATH11K_DBG_WMI, "peer sta kickout event %pM", + arg.mac_addr); + + ieee80211_report_low_ack(sta, 10); + +exit: + spin_unlock_bh(&ab->base_lock); + rcu_read_unlock(); +} + +static void ath11k_roam_event(struct ath11k_base *ab, struct sk_buff *skb) +{ + struct wmi_roam_event roam_ev = {}; + struct ath11k *ar; + + if (ath11k_pull_roam_ev(ab, skb, &roam_ev) != 0) { + ath11k_warn(ab, "failed to extract roam event"); + return; + } + + ath11k_dbg(ab, ATH11K_DBG_WMI, + "wmi roam event vdev %u reason 0x%08x rssi %d\n", + roam_ev.vdev_id, roam_ev.reason, roam_ev.rssi); + + rcu_read_lock(); + ar = ath11k_mac_get_ar_by_vdev_id(ab, roam_ev.vdev_id); + if (!ar) { + ath11k_warn(ab, "invalid vdev id in roam ev %d", + roam_ev.vdev_id); + rcu_read_unlock(); + return; + } + + if (roam_ev.reason >= WMI_ROAM_REASON_MAX) + ath11k_warn(ab, "ignoring unknown roam event reason %d on vdev %i\n", + roam_ev.reason, roam_ev.vdev_id); + + switch (roam_ev.reason) { + case WMI_ROAM_REASON_BEACON_MISS: + /* TODO: Pending beacon miss and connection_loss_work + * implementation + * ath11k_mac_handle_beacon_miss(ar, vdev_id); + */ + break; + case WMI_ROAM_REASON_BETTER_AP: + case WMI_ROAM_REASON_LOW_RSSI: + case WMI_ROAM_REASON_SUITABLE_AP_FOUND: + case WMI_ROAM_REASON_HO_FAILED: + ath11k_warn(ab, "ignoring not implemented roam event reason %d on vdev %i\n", + roam_ev.reason, roam_ev.vdev_id); + break; + } + + rcu_read_unlock(); +} + +static void ath11k_chan_info_event(struct ath11k_base *ab, struct sk_buff *skb) +{ + struct wmi_chan_info_event ch_info_ev = {0}; + struct ath11k *ar; + struct survey_info *survey; + int idx; + /* HW channel counters frequency value in hertz */ + u32 cc_freq_hz = ab->cc_freq_hz; + + if (ath11k_pull_chan_info_ev(ab, skb->data, skb->len, &ch_info_ev) != 0) { + ath11k_warn(ab, "failed to extract chan info event"); + return; + } + + ath11k_dbg(ab, ATH11K_DBG_WMI, + "chan info vdev_id %d err_code %d freq %d cmd_flags %d noise_floor %d rx_clear_count %d cycle_count %d mac_clk_mhz %d\n", + ch_info_ev.vdev_id, ch_info_ev.err_code, ch_info_ev.freq, + ch_info_ev.cmd_flags, ch_info_ev.noise_floor, + ch_info_ev.rx_clear_count, ch_info_ev.cycle_count, + ch_info_ev.mac_clk_mhz); + + if (ch_info_ev.cmd_flags == WMI_CHAN_INFO_END_RESP) { + ath11k_dbg(ab, ATH11K_DBG_WMI, "chan info report completed\n"); + return; + } + + rcu_read_lock(); + ar = ath11k_mac_get_ar_by_vdev_id(ab, ch_info_ev.vdev_id); + if (!ar) { + ath11k_warn(ab, "invalid vdev id in chan info ev %d", + ch_info_ev.vdev_id); + rcu_read_unlock(); + return; + } + spin_lock_bh(&ar->data_lock); + + switch (ar->scan.state) { + case ATH11K_SCAN_IDLE: + case ATH11K_SCAN_STARTING: + ath11k_warn(ab, "received chan info event without a scan request, ignoring\n"); + goto exit; + case ATH11K_SCAN_RUNNING: + case ATH11K_SCAN_ABORTING: + break; + } + + idx = freq_to_idx(ar, ch_info_ev.freq); + if (idx >= ARRAY_SIZE(ar->survey)) { + ath11k_warn(ab, "chan info: invalid frequency %d (idx %d out of bounds)\n", + ch_info_ev.freq, idx); + goto exit; + } + + /* If FW provides MAC clock frequency in Mhz, overriding the initialized + * HW channel counters frequency value + */ + if (ch_info_ev.mac_clk_mhz) + cc_freq_hz = (ch_info_ev.mac_clk_mhz * 1000); + + if (ch_info_ev.cmd_flags == WMI_CHAN_INFO_START_RESP) { + survey = &ar->survey[idx]; + memset(survey, 0, sizeof(*survey)); + survey->noise = ch_info_ev.noise_floor; + survey->filled = SURVEY_INFO_NOISE_DBM | SURVEY_INFO_TIME | + SURVEY_INFO_TIME_BUSY; + survey->time = div_u64(ch_info_ev.cycle_count, cc_freq_hz); + survey->time_busy = div_u64(ch_info_ev.rx_clear_count, cc_freq_hz); + } +exit: + spin_unlock_bh(&ar->data_lock); + rcu_read_unlock(); +} + +static void +ath11k_pdev_bss_chan_info_event(struct ath11k_base *ab, struct sk_buff *skb) +{ + struct wmi_pdev_bss_chan_info_event bss_ch_info_ev = {}; + struct survey_info *survey; + struct ath11k *ar; + u32 cc_freq_hz = ab->cc_freq_hz; + u64 busy, total, tx, rx, rx_bss; + int idx; + + if (ath11k_pull_pdev_bss_chan_info_ev(ab, skb, &bss_ch_info_ev) != 0) { + ath11k_warn(ab, "failed to extract pdev bss chan info event"); + return; + } + + busy = (u64)(bss_ch_info_ev.rx_clear_count_high) << 32 | + bss_ch_info_ev.rx_clear_count_low; + + total = (u64)(bss_ch_info_ev.cycle_count_high) << 32 | + bss_ch_info_ev.cycle_count_low; + + tx = (u64)(bss_ch_info_ev.tx_cycle_count_high) << 32 | + bss_ch_info_ev.tx_cycle_count_low; + + rx = (u64)(bss_ch_info_ev.rx_cycle_count_high) << 32 | + bss_ch_info_ev.rx_cycle_count_low; + + rx_bss = (u64)(bss_ch_info_ev.rx_bss_cycle_count_high) << 32 | + bss_ch_info_ev.rx_bss_cycle_count_low; + + ath11k_dbg(ab, ATH11K_DBG_WMI, + "pdev bss chan info:\n pdev_id: %d freq: %d noise: %d cycle: busy %llu total %llu tx %llu rx %llu rx_bss %llu\n", + bss_ch_info_ev.pdev_id, bss_ch_info_ev.freq, + bss_ch_info_ev.noise_floor, busy, total, + tx, rx, rx_bss); + + rcu_read_lock(); + ar = ath11k_mac_get_ar_by_pdev_id(ab, bss_ch_info_ev.pdev_id); + + if (!ar) { + ath11k_warn(ab, "invalid pdev id %d in bss_chan_info event\n", + bss_ch_info_ev.pdev_id); + rcu_read_unlock(); + return; + } + + spin_lock_bh(&ar->data_lock); + idx = freq_to_idx(ar, bss_ch_info_ev.freq); + if (idx >= ARRAY_SIZE(ar->survey)) { + ath11k_warn(ab, "bss chan info: invalid frequency %d (idx %d out of bounds)\n", + bss_ch_info_ev.freq, idx); + goto exit; + } + + survey = &ar->survey[idx]; + + survey->noise = bss_ch_info_ev.noise_floor; + survey->time = div_u64(total, cc_freq_hz); + survey->time_busy = div_u64(busy, cc_freq_hz); + survey->time_rx = div_u64(rx_bss, cc_freq_hz); + survey->time_tx = div_u64(tx, cc_freq_hz); + survey->filled |= (SURVEY_INFO_NOISE_DBM | + SURVEY_INFO_TIME | + SURVEY_INFO_TIME_BUSY | + SURVEY_INFO_TIME_RX | + SURVEY_INFO_TIME_TX); +exit: + spin_unlock_bh(&ar->data_lock); + complete(&ar->bss_survey_done); + + rcu_read_unlock(); +} + +static void ath11k_vdev_install_key_compl_event(struct ath11k_base *ab, + struct sk_buff *skb) +{ + struct wmi_vdev_install_key_complete_arg install_key_compl = {0}; + struct ath11k *ar; + + if (ath11k_pull_vdev_install_key_compl_ev(ab, skb, &install_key_compl) != 0) { + ath11k_warn(ab, "failed to extract install key compl event"); + return; + } + + ath11k_dbg(ab, ATH11K_DBG_WMI, + "vdev install key ev idx %d flags %08x macaddr %pM status %d\n", + install_key_compl.key_idx, install_key_compl.key_flags, + install_key_compl.macaddr, install_key_compl.status); + + rcu_read_lock(); + ar = ath11k_mac_get_ar_by_vdev_id(ab, install_key_compl.vdev_id); + if (!ar) { + ath11k_warn(ab, "invalid vdev id in install key compl ev %d", + install_key_compl.vdev_id); + rcu_read_unlock(); + return; + } + + ar->install_key_status = 0; + + if (install_key_compl.status != WMI_VDEV_INSTALL_KEY_COMPL_STATUS_SUCCESS) { + ath11k_warn(ab, "install key failed for %pM status %d\n", + install_key_compl.macaddr, install_key_compl.status); + ar->install_key_status = install_key_compl.status; + } + + complete(&ar->install_key_done); + rcu_read_unlock(); +} + +static void ath11k_service_available_event(struct ath11k_base *ab, struct sk_buff *skb) +{ + const void **tb; + const struct wmi_service_available_event *ev; + int ret; + int i, j; + + tb = ath11k_wmi_tlv_parse_alloc(ab, skb->data, skb->len, GFP_ATOMIC); + if (IS_ERR(tb)) { + ret = PTR_ERR(tb); + ath11k_warn(ab, "failed to parse tlv: %d\n", ret); + return; + } + + ev = tb[WMI_TAG_SERVICE_AVAILABLE_EVENT]; + if (!ev) { + ath11k_warn(ab, "failed to fetch svc available ev"); + kfree(tb); + return; + } + + /* TODO: Use wmi_service_segment_offset information to get the service + * especially when more services are advertised in multiple sevice + * available events. + */ + for (i = 0, j = WMI_MAX_SERVICE; + i < WMI_SERVICE_SEGMENT_BM_SIZE32 && j < WMI_MAX_EXT_SERVICE; + i++) { + do { + if (ev->wmi_service_segment_bitmap[i] & + BIT(j % WMI_AVAIL_SERVICE_BITS_IN_SIZE32)) + set_bit(j, ab->wmi_sc.svc_map); + } while (++j % WMI_AVAIL_SERVICE_BITS_IN_SIZE32); + } + + ath11k_dbg(ab, ATH11K_DBG_WMI, + "wmi_ext_service_bitmap 0:0x%x, 1:0x%x, 2:0x%x, 3:0x%x", + ev->wmi_service_segment_bitmap[0], ev->wmi_service_segment_bitmap[1], + ev->wmi_service_segment_bitmap[2], ev->wmi_service_segment_bitmap[3]); + + kfree(tb); +} + +static void ath11k_peer_assoc_conf_event(struct ath11k_base *ab, struct sk_buff *skb) +{ + struct wmi_peer_assoc_conf_arg peer_assoc_conf = {0}; + struct ath11k *ar; + + if (ath11k_pull_peer_assoc_conf_ev(ab, skb, &peer_assoc_conf) != 0) { + ath11k_warn(ab, "failed to extract peer assoc conf event"); + return; + } + + ath11k_dbg(ab, ATH11K_DBG_WMI, + "peer assoc conf ev vdev id %d macaddr %pM\n", + peer_assoc_conf.vdev_id, peer_assoc_conf.macaddr); + + ar = ath11k_mac_get_ar_by_vdev_id(ab, peer_assoc_conf.vdev_id); + + if (!ar) { + ath11k_warn(ab, "invalid vdev id in peer assoc conf ev %d", + peer_assoc_conf.vdev_id); + return; + } + + complete(&ar->peer_assoc_done); +} + +static void ath11k_update_stats_event(struct ath11k_base *ab, struct sk_buff *skb) +{ + ath11k_debug_fw_stats_process(ab, skb); +} + +/* PDEV_CTL_FAILSAFE_CHECK_EVENT is received from FW when the frequency scanned + * is not part of BDF CTL(Conformance test limits) table entries. + */ +static void ath11k_pdev_ctl_failsafe_check_event(struct ath11k_base *ab, + struct sk_buff *skb) +{ + const void **tb; + const struct wmi_pdev_ctl_failsafe_chk_event *ev; + int ret; + + tb = ath11k_wmi_tlv_parse_alloc(ab, skb->data, skb->len, GFP_ATOMIC); + if (IS_ERR(tb)) { + ret = PTR_ERR(tb); + ath11k_warn(ab, "failed to parse tlv: %d\n", ret); + return; + } + + ev = tb[WMI_TAG_PDEV_CTL_FAILSAFE_CHECK_EVENT]; + if (!ev) { + ath11k_warn(ab, "failed to fetch pdev ctl failsafe check ev"); + kfree(tb); + return; + } + + ath11k_dbg(ab, ATH11K_DBG_WMI, + "pdev ctl failsafe check ev status %d\n", + ev->ctl_failsafe_status); + + /* If ctl_failsafe_status is set to 1 FW will max out the Transmit power + * to 10 dBm else the CTL power entry in the BDF would be picked up. + */ + if (ev->ctl_failsafe_status != 0) + ath11k_warn(ab, "pdev ctl failsafe failure status %d", + ev->ctl_failsafe_status); + + kfree(tb); +} + +static void +ath11k_wmi_process_csa_switch_count_event(struct ath11k_base *ab, + const struct wmi_pdev_csa_switch_ev *ev, + const u32 *vdev_ids) +{ + int i; + struct ath11k_vif *arvif; + + /* Finish CSA once the switch count becomes NULL */ + if (ev->current_switch_count) + return; + + rcu_read_lock(); + for (i = 0; i < ev->num_vdevs; i++) { + arvif = ath11k_mac_get_arvif_by_vdev_id(ab, vdev_ids[i]); + + if (!arvif) { + ath11k_warn(ab, "Recvd csa status for unknown vdev %d", + vdev_ids[i]); + continue; + } + + if (arvif->is_up && arvif->vif->csa_active) + ieee80211_csa_finish(arvif->vif); + } + rcu_read_unlock(); +} + +static void +ath11k_wmi_pdev_csa_switch_count_status_event(struct ath11k_base *ab, + struct sk_buff *skb) +{ + const void **tb; + const struct wmi_pdev_csa_switch_ev *ev; + const u32 *vdev_ids; + int ret; + + tb = ath11k_wmi_tlv_parse_alloc(ab, skb->data, skb->len, GFP_ATOMIC); + if (IS_ERR(tb)) { + ret = PTR_ERR(tb); + ath11k_warn(ab, "failed to parse tlv: %d\n", ret); + return; + } + + ev = tb[WMI_TAG_PDEV_CSA_SWITCH_COUNT_STATUS_EVENT]; + vdev_ids = tb[WMI_TAG_ARRAY_UINT32]; + + if (!ev || !vdev_ids) { + ath11k_warn(ab, "failed to fetch pdev csa switch count ev"); + kfree(tb); + return; + } + + ath11k_dbg(ab, ATH11K_DBG_WMI, + "pdev csa switch count %d for pdev %d, num_vdevs %d", + ev->current_switch_count, ev->pdev_id, + ev->num_vdevs); + + ath11k_wmi_process_csa_switch_count_event(ab, ev, vdev_ids); + + kfree(tb); +} + +static void +ath11k_wmi_pdev_dfs_radar_detected_event(struct ath11k_base *ab, struct sk_buff *skb) +{ + const void **tb; + const struct wmi_pdev_radar_ev *ev; + struct ath11k *ar; + int ret; + + tb = ath11k_wmi_tlv_parse_alloc(ab, skb->data, skb->len, GFP_ATOMIC); + if (IS_ERR(tb)) { + ret = PTR_ERR(tb); + ath11k_warn(ab, "failed to parse tlv: %d\n", ret); + return; + } + + ev = tb[WMI_TAG_PDEV_DFS_RADAR_DETECTION_EVENT]; + + if (!ev) { + ath11k_warn(ab, "failed to fetch pdev dfs radar detected ev"); + kfree(tb); + return; + } + + ath11k_dbg(ab, ATH11K_DBG_WMI, + "pdev dfs radar detected on pdev %d, detection mode %d, chan freq %d, chan_width %d, detector id %d, seg id %d, timestamp %d, chirp %d, freq offset %d, sidx %d", + ev->pdev_id, ev->detection_mode, ev->chan_freq, ev->chan_width, + ev->detector_id, ev->segment_id, ev->timestamp, ev->is_chirp, + ev->freq_offset, ev->sidx); + + ar = ath11k_mac_get_ar_by_pdev_id(ab, ev->pdev_id); + + if (!ar) { + ath11k_warn(ab, "radar detected in invalid pdev %d\n", + ev->pdev_id); + goto exit; + } + + ath11k_dbg(ar->ab, ATH11K_DBG_REG, "DFS Radar Detected in pdev %d\n", + ev->pdev_id); + + if (ar->dfs_block_radar_events) + ath11k_info(ab, "DFS Radar detected, but ignored as requested\n"); + else + ieee80211_radar_detected(ar->hw); + +exit: + kfree(tb); +} + +static void ath11k_wmi_tlv_op_rx(struct ath11k_base *ab, struct sk_buff *skb) +{ + struct wmi_cmd_hdr *cmd_hdr; + enum wmi_tlv_event_id id; + + cmd_hdr = (struct wmi_cmd_hdr *)skb->data; + id = FIELD_GET(WMI_CMD_HDR_CMD_ID, (cmd_hdr->cmd_id)); + + if (skb_pull(skb, sizeof(struct wmi_cmd_hdr)) == NULL) + goto out; + + switch (id) { + /* Process all the WMI events here */ + case WMI_SERVICE_READY_EVENTID: + ath11k_service_ready_event(ab, skb); + break; + case WMI_SERVICE_READY_EXT_EVENTID: + ath11k_service_ready_ext_event(ab, skb); + break; + case WMI_REG_CHAN_LIST_CC_EVENTID: + ath11k_reg_chan_list_event(ab, skb); + break; + case WMI_READY_EVENTID: + ath11k_ready_event(ab, skb); + break; + case WMI_PEER_DELETE_RESP_EVENTID: + ath11k_peer_delete_resp_event(ab, skb); + break; + case WMI_VDEV_START_RESP_EVENTID: + ath11k_vdev_start_resp_event(ab, skb); + break; + case WMI_OFFLOAD_BCN_TX_STATUS_EVENTID: + ath11k_bcn_tx_status_event(ab, skb); + break; + case WMI_VDEV_STOPPED_EVENTID: + ath11k_vdev_stopped_event(ab, skb); + break; + case WMI_MGMT_RX_EVENTID: + ath11k_mgmt_rx_event(ab, skb); + /* mgmt_rx_event() owns the skb now! */ + return; + case WMI_MGMT_TX_COMPLETION_EVENTID: + ath11k_mgmt_tx_compl_event(ab, skb); + break; + case WMI_SCAN_EVENTID: + ath11k_scan_event(ab, skb); + break; + case WMI_PEER_STA_KICKOUT_EVENTID: + ath11k_peer_sta_kickout_event(ab, skb); + break; + case WMI_ROAM_EVENTID: + ath11k_roam_event(ab, skb); + break; + case WMI_CHAN_INFO_EVENTID: + ath11k_chan_info_event(ab, skb); + break; + case WMI_PDEV_BSS_CHAN_INFO_EVENTID: + ath11k_pdev_bss_chan_info_event(ab, skb); + break; + case WMI_VDEV_INSTALL_KEY_COMPLETE_EVENTID: + ath11k_vdev_install_key_compl_event(ab, skb); + break; + case WMI_SERVICE_AVAILABLE_EVENTID: + ath11k_service_available_event(ab, skb); + break; + case WMI_PEER_ASSOC_CONF_EVENTID: + ath11k_peer_assoc_conf_event(ab, skb); + break; + case WMI_UPDATE_STATS_EVENTID: + ath11k_update_stats_event(ab, skb); + break; + case WMI_PDEV_CTL_FAILSAFE_CHECK_EVENTID: + ath11k_pdev_ctl_failsafe_check_event(ab, skb); + break; + case WMI_PDEV_CSA_SWITCH_COUNT_STATUS_EVENTID: + ath11k_wmi_pdev_csa_switch_count_status_event(ab, skb); + break; + /* add Unsupported events here */ + case WMI_TBTTOFFSET_EXT_UPDATE_EVENTID: + case WMI_VDEV_DELETE_RESP_EVENTID: + ath11k_dbg(ab, ATH11K_DBG_WMI, + "ignoring unsupported event 0x%x\n", id); + break; + case WMI_PDEV_DFS_RADAR_DETECTION_EVENTID: + ath11k_wmi_pdev_dfs_radar_detected_event(ab, skb); + break; + /* TODO: Add remaining events */ + default: + ath11k_warn(ab, "Unknown eventid: 0x%x\n", id); + break; + } + +out: + dev_kfree_skb(skb); +} + +static int ath11k_connect_pdev_htc_service(struct ath11k_base *ab, + u32 pdev_idx) +{ + int status; + u32 svc_id[] = { ATH11K_HTC_SVC_ID_WMI_CONTROL, + ATH11K_HTC_SVC_ID_WMI_CONTROL_MAC1, + ATH11K_HTC_SVC_ID_WMI_CONTROL_MAC2 }; + + struct ath11k_htc_svc_conn_req conn_req; + struct ath11k_htc_svc_conn_resp conn_resp; + + memset(&conn_req, 0, sizeof(conn_req)); + memset(&conn_resp, 0, sizeof(conn_resp)); + + /* these fields are the same for all service endpoints */ + conn_req.ep_ops.ep_tx_complete = ath11k_wmi_htc_tx_complete; + conn_req.ep_ops.ep_rx_complete = ath11k_wmi_tlv_op_rx; + conn_req.ep_ops.ep_tx_credits = ath11k_wmi_op_ep_tx_credits; + + /* connect to control service */ + conn_req.service_id = svc_id[pdev_idx]; + + status = ath11k_htc_connect_service(&ab->htc, &conn_req, &conn_resp); + if (status) { + ath11k_warn(ab, "failed to connect to WMI CONTROL service status: %d\n", + status); + return status; + } + + ab->wmi_sc.wmi_endpoint_id[pdev_idx] = conn_resp.eid; + ab->wmi_sc.wmi[pdev_idx].eid = conn_resp.eid; + ab->wmi_sc.max_msg_len[pdev_idx] = conn_resp.max_msg_len; + + return 0; +} + +static int +ath11k_wmi_send_unit_test_cmd(struct ath11k *ar, + struct wmi_unit_test_cmd ut_cmd, + u32 *test_args) +{ + struct ath11k_pdev_wmi *wmi = ar->wmi; + struct wmi_unit_test_cmd *cmd; + struct sk_buff *skb; + struct wmi_tlv *tlv; + void *ptr; + u32 *ut_cmd_args; + int buf_len, arg_len; + int ret; + int i; + + arg_len = sizeof(u32) * ut_cmd.num_args; + buf_len = sizeof(ut_cmd) + arg_len + TLV_HDR_SIZE; + + skb = ath11k_wmi_alloc_skb(wmi->wmi_sc, buf_len); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_unit_test_cmd *)skb->data; + cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_UNIT_TEST_CMD) | + FIELD_PREP(WMI_TLV_LEN, sizeof(ut_cmd) - TLV_HDR_SIZE); + + cmd->vdev_id = ut_cmd.vdev_id; + cmd->module_id = ut_cmd.module_id; + cmd->num_args = ut_cmd.num_args; + cmd->diag_token = ut_cmd.diag_token; + + ptr = skb->data + sizeof(ut_cmd); + + tlv = ptr; + tlv->header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_ARRAY_UINT32) | + FIELD_PREP(WMI_TLV_LEN, arg_len); + + ptr += TLV_HDR_SIZE; + + ut_cmd_args = ptr; + for (i = 0; i < ut_cmd.num_args; i++) + ut_cmd_args[i] = test_args[i]; + + ret = ath11k_wmi_cmd_send(wmi, skb, WMI_UNIT_TEST_CMDID); + + if (ret) { + ath11k_warn(ar->ab, "failed to send WMI_UNIT_TEST CMD :%d\n", + ret); + dev_kfree_skb(skb); + } + + ath11k_dbg(ar->ab, ATH11K_DBG_WMI, + "WMI unit test : module %d vdev %d n_args %d token %d\n", + cmd->module_id, cmd->vdev_id, cmd->num_args, + cmd->diag_token); + + return ret; +} + +int ath11k_wmi_simulate_radar(struct ath11k *ar) +{ + struct ath11k_vif *arvif; + u32 dfs_args[DFS_MAX_TEST_ARGS]; + struct wmi_unit_test_cmd wmi_ut; + bool arvif_found = false; + + list_for_each_entry(arvif, &ar->arvifs, list) { + if (arvif->is_started && arvif->vdev_type == WMI_VDEV_TYPE_AP) { + arvif_found = true; + break; + } + } + + if (!arvif_found) + return -EINVAL; + + dfs_args[DFS_TEST_CMDID] = 0; + dfs_args[DFS_TEST_PDEV_ID] = ar->pdev->pdev_id; + /* Currently we could pass segment_id(b0 - b1), chirp(b2) + * freq offset (b3 - b10) to unit test. For simulation + * purpose this can be set to 0 which is valid. + */ + dfs_args[DFS_TEST_RADAR_PARAM] = 0; + + wmi_ut.vdev_id = arvif->vdev_id; + wmi_ut.module_id = DFS_UNIT_TEST_MODULE; + wmi_ut.num_args = DFS_MAX_TEST_ARGS; + wmi_ut.diag_token = DFS_UNIT_TEST_TOKEN; + + ath11k_dbg(ar->ab, ATH11K_DBG_REG, "Triggering Radar Simulation\n"); + + return ath11k_wmi_send_unit_test_cmd(ar, wmi_ut, dfs_args); +} + +int ath11k_wmi_connect(struct ath11k_base *ab) +{ + u32 i; + u8 wmi_ep_count; + + wmi_ep_count = ab->htc.wmi_ep_count; + if (wmi_ep_count > MAX_RADIOS) + return -1; + + for (i = 0; i < wmi_ep_count; i++) + ath11k_connect_pdev_htc_service(ab, i); + + return 0; +} + +static void ath11k_wmi_pdev_detach(struct ath11k_base *ab, u8 pdev_id) +{ + if (WARN_ON(pdev_id >= MAX_RADIOS)) + return; + + /* TODO: Deinit any pdev specific wmi resource */ +} + +int ath11k_wmi_pdev_attach(struct ath11k_base *ab, + u8 pdev_id) +{ + struct ath11k_pdev_wmi *wmi_handle; + + if (pdev_id >= MAX_RADIOS) + return -EINVAL; + + wmi_handle = &ab->wmi_sc.wmi[pdev_id]; + + wmi_handle->wmi_sc = &ab->wmi_sc; + + ab->wmi_sc.ab = ab; + /* TODO: Init remaining resource specific to pdev */ + + return 0; +} + +int ath11k_wmi_attach(struct ath11k_base *ab) +{ + int ret; + + ret = ath11k_wmi_pdev_attach(ab, 0); + if (ret) + return ret; + + ab->wmi_sc.ab = ab; + ab->wmi_sc.preferred_hw_mode = WMI_HOST_HW_MODE_MAX; + + /* TODO: Init remaining wmi soc resources required */ + init_completion(&ab->wmi_sc.service_ready); + init_completion(&ab->wmi_sc.unified_ready); + + return 0; +} + +void ath11k_wmi_detach(struct ath11k_base *ab) +{ + int i; + + /* TODO: Deinit wmi resource specific to SOC as required */ + + for (i = 0; i < ab->htc.wmi_ep_count; i++) + ath11k_wmi_pdev_detach(ab, i); +} diff --git a/drivers/net/wireless/ath/ath11k/wmi.h b/drivers/net/wireless/ath/ath11k/wmi.h new file mode 100644 index 0000000000000..00c51cc83d479 --- /dev/null +++ b/drivers/net/wireless/ath/ath11k/wmi.h @@ -0,0 +1,4664 @@ +/* SPDX-License-Identifier: BSD-3-Clause-Clear */ +/* + * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. + */ + +#ifndef ATH11K_WMI_H +#define ATH11K_WMI_H + +#include +#include "htc.h" + +struct ath11k_base; +struct ath11k; +struct ath11k_fw_stats; + +#define PSOC_HOST_MAX_NUM_SS (8) + +/* defines to set Packet extension values whic can be 0 us, 8 usec or 16 usec */ +#define MAX_HE_NSS 8 +#define MAX_HE_MODULATION 8 +#define MAX_HE_RU 4 +#define HE_MODULATION_NONE 7 +#define HE_PET_0_USEC 0 +#define HE_PET_8_USEC 1 +#define HE_PET_16_USEC 2 + +#define WMI_MAX_NUM_SS MAX_HE_NSS +#define WMI_MAX_NUM_RU MAX_HE_RU + +#define WMI_TLV_CMD(grp_id) (((grp_id) << 12) | 0x1) +#define WMI_TLV_EV(grp_id) (((grp_id) << 12) | 0x1) +#define WMI_TLV_CMD_UNSUPPORTED 0 +#define WMI_TLV_PDEV_PARAM_UNSUPPORTED 0 +#define WMI_TLV_VDEV_PARAM_UNSUPPORTED 0 + +struct wmi_cmd_hdr { + u32 cmd_id; +} __packed; + +struct wmi_tlv { + u32 header; + u8 value[0]; +} __packed; + +#define WMI_TLV_LEN GENMASK(15, 0) +#define WMI_TLV_TAG GENMASK(31, 16) +#define TLV_HDR_SIZE FIELD_SIZEOF(struct wmi_tlv, header) + +#define WMI_CMD_HDR_CMD_ID GENMASK(23, 0) +#define WMI_MAX_MEM_REQS 32 +#define ATH11K_MAX_HW_LISTEN_INTERVAL 5 + +#define WLAN_SCAN_PARAMS_MAX_SSID 16 +#define WLAN_SCAN_PARAMS_MAX_BSSID 4 +#define WLAN_SCAN_PARAMS_MAX_IE_LEN 256 + +/* + * HW mode config type replicated from FW header + * @WMI_HOST_HW_MODE_SINGLE: Only one PHY is active. + * @WMI_HOST_HW_MODE_DBS: Both PHYs are active in different bands, + * one in 2G and another in 5G. + * @WMI_HOST_HW_MODE_SBS_PASSIVE: Both PHYs are in passive mode (only rx) in + * same band; no tx allowed. + * @WMI_HOST_HW_MODE_SBS: Both PHYs are active in the same band. + * Support for both PHYs within one band is planned + * for 5G only(as indicated in WMI_MAC_PHY_CAPABILITIES), + * but could be extended to other bands in the future. + * The separation of the band between the two PHYs needs + * to be communicated separately. + * @WMI_HOST_HW_MODE_DBS_SBS: 3 PHYs, with 2 on the same band doing SBS + * as in WMI_HW_MODE_SBS, and 3rd on the other band + * @WMI_HOST_HW_MODE_DBS_OR_SBS: Two PHY with one PHY capabale of both 2G and + * 5G. It can support SBS (5G + 5G) OR DBS (5G + 2G). + * @WMI_HOST_HW_MODE_MAX: Max hw_mode_id. Used to indicate invalid mode. + */ +enum wmi_host_hw_mode_config_type { + WMI_HOST_HW_MODE_SINGLE = 0, + WMI_HOST_HW_MODE_DBS = 1, + WMI_HOST_HW_MODE_SBS_PASSIVE = 2, + WMI_HOST_HW_MODE_SBS = 3, + WMI_HOST_HW_MODE_DBS_SBS = 4, + WMI_HOST_HW_MODE_DBS_OR_SBS = 5, + + /* keep last */ + WMI_HOST_HW_MODE_MAX +}; + +/* HW mode priority values used to detect the preferred HW mode + * on the available modes. + */ +enum wmi_host_hw_mode_priority { + WMI_HOST_HW_MODE_DBS_SBS_PRI, + WMI_HOST_HW_MODE_DBS_PRI, + WMI_HOST_HW_MODE_DBS_OR_SBS_PRI, + WMI_HOST_HW_MODE_SBS_PRI, + WMI_HOST_HW_MODE_SBS_PASSIVE_PRI, + WMI_HOST_HW_MODE_SINGLE_PRI, + + /* keep last the lowest priority */ + WMI_HOST_HW_MODE_MAX_PRI +}; + +enum { + WMI_HOST_WLAN_2G_CAP = 0x1, + WMI_HOST_WLAN_5G_CAP = 0x2, + WMI_HOST_WLAN_2G_5G_CAP = 0x3, +}; + +/* + * wmi command groups. + */ +enum wmi_cmd_group { + /* 0 to 2 are reserved */ + WMI_GRP_START = 0x3, + WMI_GRP_SCAN = WMI_GRP_START, + WMI_GRP_PDEV = 0x4, + WMI_GRP_VDEV = 0x5, + WMI_GRP_PEER = 0x6, + WMI_GRP_MGMT = 0x7, + WMI_GRP_BA_NEG = 0x8, + WMI_GRP_STA_PS = 0x9, + WMI_GRP_DFS = 0xa, + WMI_GRP_ROAM = 0xb, + WMI_GRP_OFL_SCAN = 0xc, + WMI_GRP_P2P = 0xd, + WMI_GRP_AP_PS = 0xe, + WMI_GRP_RATE_CTRL = 0xf, + WMI_GRP_PROFILE = 0x10, + WMI_GRP_SUSPEND = 0x11, + WMI_GRP_BCN_FILTER = 0x12, + WMI_GRP_WOW = 0x13, + WMI_GRP_RTT = 0x14, + WMI_GRP_SPECTRAL = 0x15, + WMI_GRP_STATS = 0x16, + WMI_GRP_ARP_NS_OFL = 0x17, + WMI_GRP_NLO_OFL = 0x18, + WMI_GRP_GTK_OFL = 0x19, + WMI_GRP_CSA_OFL = 0x1a, + WMI_GRP_CHATTER = 0x1b, + WMI_GRP_TID_ADDBA = 0x1c, + WMI_GRP_MISC = 0x1d, + WMI_GRP_GPIO = 0x1e, + WMI_GRP_FWTEST = 0x1f, + WMI_GRP_TDLS = 0x20, + WMI_GRP_RESMGR = 0x21, + WMI_GRP_STA_SMPS = 0x22, + WMI_GRP_WLAN_HB = 0x23, + WMI_GRP_RMC = 0x24, + WMI_GRP_MHF_OFL = 0x25, + WMI_GRP_LOCATION_SCAN = 0x26, + WMI_GRP_OEM = 0x27, + WMI_GRP_NAN = 0x28, + WMI_GRP_COEX = 0x29, + WMI_GRP_OBSS_OFL = 0x2a, + WMI_GRP_LPI = 0x2b, + WMI_GRP_EXTSCAN = 0x2c, + WMI_GRP_DHCP_OFL = 0x2d, + WMI_GRP_IPA = 0x2e, + WMI_GRP_MDNS_OFL = 0x2f, + WMI_GRP_SAP_OFL = 0x30, + WMI_GRP_OCB = 0x31, + WMI_GRP_SOC = 0x32, + WMI_GRP_PKT_FILTER = 0x33, + WMI_GRP_MAWC = 0x34, + WMI_GRP_PMF_OFFLOAD = 0x35, + WMI_GRP_BPF_OFFLOAD = 0x36, + WMI_GRP_NAN_DATA = 0x37, + WMI_GRP_PROTOTYPE = 0x38, + WMI_GRP_MONITOR = 0x39, + WMI_GRP_REGULATORY = 0x3a, + WMI_GRP_HW_DATA_FILTER = 0x3b, +}; + +#define WMI_CMD_GRP(grp_id) (((grp_id) << 12) | 0x1) +#define WMI_EVT_GRP_START_ID(grp_id) (((grp_id) << 12) | 0x1) + +#define WMI_CMD_UNSUPPORTED 0 + +enum wmi_tlv_cmd_id { + WMI_INIT_CMDID = 0x1, + WMI_START_SCAN_CMDID = WMI_TLV_CMD(WMI_GRP_SCAN), + WMI_STOP_SCAN_CMDID, + WMI_SCAN_CHAN_LIST_CMDID, + WMI_SCAN_SCH_PRIO_TBL_CMDID, + WMI_SCAN_UPDATE_REQUEST_CMDID, + WMI_SCAN_PROB_REQ_OUI_CMDID, + WMI_SCAN_ADAPTIVE_DWELL_CONFIG_CMDID, + WMI_PDEV_SET_REGDOMAIN_CMDID = WMI_TLV_CMD(WMI_GRP_PDEV), + WMI_PDEV_SET_CHANNEL_CMDID, + WMI_PDEV_SET_PARAM_CMDID, + WMI_PDEV_PKTLOG_ENABLE_CMDID, + WMI_PDEV_PKTLOG_DISABLE_CMDID, + WMI_PDEV_SET_WMM_PARAMS_CMDID, + WMI_PDEV_SET_HT_CAP_IE_CMDID, + WMI_PDEV_SET_VHT_CAP_IE_CMDID, + WMI_PDEV_SET_DSCP_TID_MAP_CMDID, + WMI_PDEV_SET_QUIET_MODE_CMDID, + WMI_PDEV_GREEN_AP_PS_ENABLE_CMDID, + WMI_PDEV_GET_TPC_CONFIG_CMDID, + WMI_PDEV_SET_BASE_MACADDR_CMDID, + WMI_PDEV_DUMP_CMDID, + WMI_PDEV_SET_LED_CONFIG_CMDID, + WMI_PDEV_GET_TEMPERATURE_CMDID, + WMI_PDEV_SET_LED_FLASHING_CMDID, + WMI_PDEV_SMART_ANT_ENABLE_CMDID, + WMI_PDEV_SMART_ANT_SET_RX_ANTENNA_CMDID, + WMI_PDEV_SET_ANTENNA_SWITCH_TABLE_CMDID, + WMI_PDEV_SET_CTL_TABLE_CMDID, + WMI_PDEV_SET_MIMOGAIN_TABLE_CMDID, + WMI_PDEV_FIPS_CMDID, + WMI_PDEV_GET_ANI_CCK_CONFIG_CMDID, + WMI_PDEV_GET_ANI_OFDM_CONFIG_CMDID, + WMI_PDEV_GET_NFCAL_POWER_CMDID, + WMI_PDEV_GET_TPC_CMDID, + WMI_MIB_STATS_ENABLE_CMDID, + WMI_PDEV_SET_PCL_CMDID, + WMI_PDEV_SET_HW_MODE_CMDID, + WMI_PDEV_SET_MAC_CONFIG_CMDID, + WMI_PDEV_SET_ANTENNA_MODE_CMDID, + WMI_SET_PERIODIC_CHANNEL_STATS_CONFIG_CMDID, + WMI_PDEV_WAL_POWER_DEBUG_CMDID, + WMI_PDEV_SET_REORDER_TIMEOUT_VAL_CMDID, + WMI_PDEV_SET_WAKEUP_CONFIG_CMDID, + WMI_PDEV_GET_ANTDIV_STATUS_CMDID, + WMI_PDEV_GET_CHIP_POWER_STATS_CMDID, + WMI_PDEV_SET_STATS_THRESHOLD_CMDID, + WMI_PDEV_MULTIPLE_VDEV_RESTART_REQUEST_CMDID, + WMI_PDEV_UPDATE_PKT_ROUTING_CMDID, + WMI_PDEV_CHECK_CAL_VERSION_CMDID, + WMI_PDEV_SET_DIVERSITY_GAIN_CMDID, + WMI_PDEV_DIV_GET_RSSI_ANTID_CMDID, + WMI_PDEV_BSS_CHAN_INFO_REQUEST_CMDID, + WMI_PDEV_UPDATE_PMK_CACHE_CMDID, + WMI_PDEV_UPDATE_FILS_HLP_PKT_CMDID, + WMI_PDEV_UPDATE_CTLTABLE_REQUEST_CMDID, + WMI_PDEV_CONFIG_VENDOR_OUI_ACTION_CMDID, + WMI_PDEV_SET_AC_TX_QUEUE_OPTIMIZED_CMDID, + WMI_PDEV_SET_RX_FILTER_PROMISCUOUS_CMDID, + WMI_PDEV_DMA_RING_CFG_REQ_CMDID, + WMI_PDEV_HE_TB_ACTION_FRM_CMDID, + WMI_PDEV_PKTLOG_FILTER_CMDID, + WMI_VDEV_CREATE_CMDID = WMI_TLV_CMD(WMI_GRP_VDEV), + WMI_VDEV_DELETE_CMDID, + WMI_VDEV_START_REQUEST_CMDID, + WMI_VDEV_RESTART_REQUEST_CMDID, + WMI_VDEV_UP_CMDID, + WMI_VDEV_STOP_CMDID, + WMI_VDEV_DOWN_CMDID, + WMI_VDEV_SET_PARAM_CMDID, + WMI_VDEV_INSTALL_KEY_CMDID, + WMI_VDEV_WNM_SLEEPMODE_CMDID, + WMI_VDEV_WMM_ADDTS_CMDID, + WMI_VDEV_WMM_DELTS_CMDID, + WMI_VDEV_SET_WMM_PARAMS_CMDID, + WMI_VDEV_SET_GTX_PARAMS_CMDID, + WMI_VDEV_IPSEC_NATKEEPALIVE_FILTER_CMDID, + WMI_VDEV_PLMREQ_START_CMDID, + WMI_VDEV_PLMREQ_STOP_CMDID, + WMI_VDEV_TSF_TSTAMP_ACTION_CMDID, + WMI_VDEV_SET_IE_CMDID, + WMI_VDEV_RATEMASK_CMDID, + WMI_VDEV_ATF_REQUEST_CMDID, + WMI_VDEV_SET_DSCP_TID_MAP_CMDID, + WMI_VDEV_FILTER_NEIGHBOR_RX_PACKETS_CMDID, + WMI_VDEV_SET_QUIET_MODE_CMDID, + WMI_VDEV_SET_CUSTOM_AGGR_SIZE_CMDID, + WMI_VDEV_ENCRYPT_DECRYPT_DATA_REQ_CMDID, + WMI_VDEV_ADD_MAC_ADDR_TO_RX_FILTER_CMDID, + WMI_PEER_CREATE_CMDID = WMI_TLV_CMD(WMI_GRP_PEER), + WMI_PEER_DELETE_CMDID, + WMI_PEER_FLUSH_TIDS_CMDID, + WMI_PEER_SET_PARAM_CMDID, + WMI_PEER_ASSOC_CMDID, + WMI_PEER_ADD_WDS_ENTRY_CMDID, + WMI_PEER_REMOVE_WDS_ENTRY_CMDID, + WMI_PEER_MCAST_GROUP_CMDID, + WMI_PEER_INFO_REQ_CMDID, + WMI_PEER_GET_ESTIMATED_LINKSPEED_CMDID, + WMI_PEER_SET_RATE_REPORT_CONDITION_CMDID, + WMI_PEER_UPDATE_WDS_ENTRY_CMDID, + WMI_PEER_ADD_PROXY_STA_ENTRY_CMDID, + WMI_PEER_SMART_ANT_SET_TX_ANTENNA_CMDID, + WMI_PEER_SMART_ANT_SET_TRAIN_INFO_CMDID, + WMI_PEER_SMART_ANT_SET_NODE_CONFIG_OPS_CMDID, + WMI_PEER_ATF_REQUEST_CMDID, + WMI_PEER_BWF_REQUEST_CMDID, + WMI_PEER_REORDER_QUEUE_SETUP_CMDID, + WMI_PEER_REORDER_QUEUE_REMOVE_CMDID, + WMI_PEER_SET_RX_BLOCKSIZE_CMDID, + WMI_PEER_ANTDIV_INFO_REQ_CMDID, + WMI_PEER_OPER_MODE_CHANGE_EVENTID, + WMI_BCN_TX_CMDID = WMI_TLV_CMD(WMI_GRP_MGMT), + WMI_PDEV_SEND_BCN_CMDID, + WMI_BCN_TMPL_CMDID, + WMI_BCN_FILTER_RX_CMDID, + WMI_PRB_REQ_FILTER_RX_CMDID, + WMI_MGMT_TX_CMDID, + WMI_PRB_TMPL_CMDID, + WMI_MGMT_TX_SEND_CMDID, + WMI_OFFCHAN_DATA_TX_SEND_CMDID, + WMI_PDEV_SEND_FD_CMDID, + WMI_BCN_OFFLOAD_CTRL_CMDID, + WMI_BSS_COLOR_CHANGE_ENABLE_CMDID, + WMI_VDEV_BCN_OFFLOAD_QUIET_CONFIG_CMDID, + WMI_ADDBA_CLEAR_RESP_CMDID = WMI_TLV_CMD(WMI_GRP_BA_NEG), + WMI_ADDBA_SEND_CMDID, + WMI_ADDBA_STATUS_CMDID, + WMI_DELBA_SEND_CMDID, + WMI_ADDBA_SET_RESP_CMDID, + WMI_SEND_SINGLEAMSDU_CMDID, + WMI_STA_POWERSAVE_MODE_CMDID = WMI_TLV_CMD(WMI_GRP_STA_PS), + WMI_STA_POWERSAVE_PARAM_CMDID, + WMI_STA_MIMO_PS_MODE_CMDID, + WMI_PDEV_DFS_ENABLE_CMDID = WMI_TLV_CMD(WMI_GRP_DFS), + WMI_PDEV_DFS_DISABLE_CMDID, + WMI_DFS_PHYERR_FILTER_ENA_CMDID, + WMI_DFS_PHYERR_FILTER_DIS_CMDID, + WMI_PDEV_DFS_PHYERR_OFFLOAD_ENABLE_CMDID, + WMI_PDEV_DFS_PHYERR_OFFLOAD_DISABLE_CMDID, + WMI_VDEV_ADFS_CH_CFG_CMDID, + WMI_VDEV_ADFS_OCAC_ABORT_CMDID, + WMI_ROAM_SCAN_MODE = WMI_TLV_CMD(WMI_GRP_ROAM), + WMI_ROAM_SCAN_RSSI_THRESHOLD, + WMI_ROAM_SCAN_PERIOD, + WMI_ROAM_SCAN_RSSI_CHANGE_THRESHOLD, + WMI_ROAM_AP_PROFILE, + WMI_ROAM_CHAN_LIST, + WMI_ROAM_SCAN_CMD, + WMI_ROAM_SYNCH_COMPLETE, + WMI_ROAM_SET_RIC_REQUEST_CMDID, + WMI_ROAM_INVOKE_CMDID, + WMI_ROAM_FILTER_CMDID, + WMI_ROAM_SUBNET_CHANGE_CONFIG_CMDID, + WMI_ROAM_CONFIGURE_MAWC_CMDID, + WMI_ROAM_SET_MBO_PARAM_CMDID, + WMI_ROAM_PER_CONFIG_CMDID, + WMI_OFL_SCAN_ADD_AP_PROFILE = WMI_TLV_CMD(WMI_GRP_OFL_SCAN), + WMI_OFL_SCAN_REMOVE_AP_PROFILE, + WMI_OFL_SCAN_PERIOD, + WMI_P2P_DEV_SET_DEVICE_INFO = WMI_TLV_CMD(WMI_GRP_P2P), + WMI_P2P_DEV_SET_DISCOVERABILITY, + WMI_P2P_GO_SET_BEACON_IE, + WMI_P2P_GO_SET_PROBE_RESP_IE, + WMI_P2P_SET_VENDOR_IE_DATA_CMDID, + WMI_P2P_DISC_OFFLOAD_CONFIG_CMDID, + WMI_P2P_DISC_OFFLOAD_APPIE_CMDID, + WMI_P2P_DISC_OFFLOAD_PATTERN_CMDID, + WMI_P2P_SET_OPPPS_PARAM_CMDID, + WMI_P2P_LISTEN_OFFLOAD_START_CMDID, + WMI_P2P_LISTEN_OFFLOAD_STOP_CMDID, + WMI_AP_PS_PEER_PARAM_CMDID = WMI_TLV_CMD(WMI_GRP_AP_PS), + WMI_AP_PS_PEER_UAPSD_COEX_CMDID, + WMI_AP_PS_EGAP_PARAM_CMDID, + WMI_PEER_RATE_RETRY_SCHED_CMDID = WMI_TLV_CMD(WMI_GRP_RATE_CTRL), + WMI_WLAN_PROFILE_TRIGGER_CMDID = WMI_TLV_CMD(WMI_GRP_PROFILE), + WMI_WLAN_PROFILE_SET_HIST_INTVL_CMDID, + WMI_WLAN_PROFILE_GET_PROFILE_DATA_CMDID, + WMI_WLAN_PROFILE_ENABLE_PROFILE_ID_CMDID, + WMI_WLAN_PROFILE_LIST_PROFILE_ID_CMDID, + WMI_PDEV_SUSPEND_CMDID = WMI_TLV_CMD(WMI_GRP_SUSPEND), + WMI_PDEV_RESUME_CMDID, + WMI_ADD_BCN_FILTER_CMDID = WMI_TLV_CMD(WMI_GRP_BCN_FILTER), + WMI_RMV_BCN_FILTER_CMDID, + WMI_WOW_ADD_WAKE_PATTERN_CMDID = WMI_TLV_CMD(WMI_GRP_WOW), + WMI_WOW_DEL_WAKE_PATTERN_CMDID, + WMI_WOW_ENABLE_DISABLE_WAKE_EVENT_CMDID, + WMI_WOW_ENABLE_CMDID, + WMI_WOW_HOSTWAKEUP_FROM_SLEEP_CMDID, + WMI_WOW_IOAC_ADD_KEEPALIVE_CMDID, + WMI_WOW_IOAC_DEL_KEEPALIVE_CMDID, + WMI_WOW_IOAC_ADD_WAKE_PATTERN_CMDID, + WMI_WOW_IOAC_DEL_WAKE_PATTERN_CMDID, + WMI_D0_WOW_ENABLE_DISABLE_CMDID, + WMI_EXTWOW_ENABLE_CMDID, + WMI_EXTWOW_SET_APP_TYPE1_PARAMS_CMDID, + WMI_EXTWOW_SET_APP_TYPE2_PARAMS_CMDID, + WMI_WOW_ENABLE_ICMPV6_NA_FLT_CMDID, + WMI_WOW_UDP_SVC_OFLD_CMDID, + WMI_WOW_HOSTWAKEUP_GPIO_PIN_PATTERN_CONFIG_CMDID, + WMI_WOW_SET_ACTION_WAKE_UP_CMDID, + WMI_RTT_MEASREQ_CMDID = WMI_TLV_CMD(WMI_GRP_RTT), + WMI_RTT_TSF_CMDID, + WMI_VDEV_SPECTRAL_SCAN_CONFIGURE_CMDID = WMI_TLV_CMD(WMI_GRP_SPECTRAL), + WMI_VDEV_SPECTRAL_SCAN_ENABLE_CMDID, + WMI_REQUEST_STATS_CMDID = WMI_TLV_CMD(WMI_GRP_STATS), + WMI_MCC_SCHED_TRAFFIC_STATS_CMDID, + WMI_REQUEST_STATS_EXT_CMDID, + WMI_REQUEST_LINK_STATS_CMDID, + WMI_START_LINK_STATS_CMDID, + WMI_CLEAR_LINK_STATS_CMDID, + WMI_GET_FW_MEM_DUMP_CMDID, + WMI_DEBUG_MESG_FLUSH_CMDID, + WMI_DIAG_EVENT_LOG_CONFIG_CMDID, + WMI_REQUEST_WLAN_STATS_CMDID, + WMI_REQUEST_RCPI_CMDID, + WMI_REQUEST_PEER_STATS_INFO_CMDID, + WMI_REQUEST_RADIO_CHAN_STATS_CMDID, + WMI_SET_ARP_NS_OFFLOAD_CMDID = WMI_TLV_CMD(WMI_GRP_ARP_NS_OFL), + WMI_ADD_PROACTIVE_ARP_RSP_PATTERN_CMDID, + WMI_DEL_PROACTIVE_ARP_RSP_PATTERN_CMDID, + WMI_NETWORK_LIST_OFFLOAD_CONFIG_CMDID = WMI_TLV_CMD(WMI_GRP_NLO_OFL), + WMI_APFIND_CMDID, + WMI_PASSPOINT_LIST_CONFIG_CMDID, + WMI_NLO_CONFIGURE_MAWC_CMDID, + WMI_GTK_OFFLOAD_CMDID = WMI_TLV_CMD(WMI_GRP_GTK_OFL), + WMI_CSA_OFFLOAD_ENABLE_CMDID = WMI_TLV_CMD(WMI_GRP_CSA_OFL), + WMI_CSA_OFFLOAD_CHANSWITCH_CMDID, + WMI_CHATTER_SET_MODE_CMDID = WMI_TLV_CMD(WMI_GRP_CHATTER), + WMI_CHATTER_ADD_COALESCING_FILTER_CMDID, + WMI_CHATTER_DELETE_COALESCING_FILTER_CMDID, + WMI_CHATTER_COALESCING_QUERY_CMDID, + WMI_PEER_TID_ADDBA_CMDID = WMI_TLV_CMD(WMI_GRP_TID_ADDBA), + WMI_PEER_TID_DELBA_CMDID, + WMI_STA_DTIM_PS_METHOD_CMDID, + WMI_STA_UAPSD_AUTO_TRIG_CMDID, + WMI_STA_KEEPALIVE_CMDID, + WMI_BA_REQ_SSN_CMDID, + WMI_ECHO_CMDID = WMI_TLV_CMD(WMI_GRP_MISC), + WMI_PDEV_UTF_CMDID, + WMI_DBGLOG_CFG_CMDID, + WMI_PDEV_QVIT_CMDID, + WMI_PDEV_FTM_INTG_CMDID, + WMI_VDEV_SET_KEEPALIVE_CMDID, + WMI_VDEV_GET_KEEPALIVE_CMDID, + WMI_FORCE_FW_HANG_CMDID, + WMI_SET_MCASTBCAST_FILTER_CMDID, + WMI_THERMAL_MGMT_CMDID, + WMI_HOST_AUTO_SHUTDOWN_CFG_CMDID, + WMI_TPC_CHAINMASK_CONFIG_CMDID, + WMI_SET_ANTENNA_DIVERSITY_CMDID, + WMI_OCB_SET_SCHED_CMDID, + WMI_RSSI_BREACH_MONITOR_CONFIG_CMDID, + WMI_LRO_CONFIG_CMDID, + WMI_TRANSFER_DATA_TO_FLASH_CMDID, + WMI_CONFIG_ENHANCED_MCAST_FILTER_CMDID, + WMI_VDEV_WISA_CMDID, + WMI_DBGLOG_TIME_STAMP_SYNC_CMDID, + WMI_SET_MULTIPLE_MCAST_FILTER_CMDID, + WMI_READ_DATA_FROM_FLASH_CMDID, + WMI_GPIO_CONFIG_CMDID = WMI_TLV_CMD(WMI_GRP_GPIO), + WMI_GPIO_OUTPUT_CMDID, + WMI_TXBF_CMDID, + WMI_FWTEST_VDEV_MCC_SET_TBTT_MODE_CMDID = WMI_TLV_CMD(WMI_GRP_FWTEST), + WMI_FWTEST_P2P_SET_NOA_PARAM_CMDID, + WMI_UNIT_TEST_CMDID, + WMI_FWTEST_CMDID, + WMI_QBOOST_CFG_CMDID, + WMI_TDLS_SET_STATE_CMDID = WMI_TLV_CMD(WMI_GRP_TDLS), + WMI_TDLS_PEER_UPDATE_CMDID, + WMI_TDLS_SET_OFFCHAN_MODE_CMDID, + WMI_RESMGR_ADAPTIVE_OCS_EN_DIS_CMDID = WMI_TLV_CMD(WMI_GRP_RESMGR), + WMI_RESMGR_SET_CHAN_TIME_QUOTA_CMDID, + WMI_RESMGR_SET_CHAN_LATENCY_CMDID, + WMI_STA_SMPS_FORCE_MODE_CMDID = WMI_TLV_CMD(WMI_GRP_STA_SMPS), + WMI_STA_SMPS_PARAM_CMDID, + WMI_HB_SET_ENABLE_CMDID = WMI_TLV_CMD(WMI_GRP_WLAN_HB), + WMI_HB_SET_TCP_PARAMS_CMDID, + WMI_HB_SET_TCP_PKT_FILTER_CMDID, + WMI_HB_SET_UDP_PARAMS_CMDID, + WMI_HB_SET_UDP_PKT_FILTER_CMDID, + WMI_RMC_SET_MODE_CMDID = WMI_TLV_CMD(WMI_GRP_RMC), + WMI_RMC_SET_ACTION_PERIOD_CMDID, + WMI_RMC_CONFIG_CMDID, + WMI_RMC_SET_MANUAL_LEADER_CMDID, + WMI_MHF_OFFLOAD_SET_MODE_CMDID = WMI_TLV_CMD(WMI_GRP_MHF_OFL), + WMI_MHF_OFFLOAD_PLUMB_ROUTING_TBL_CMDID, + WMI_BATCH_SCAN_ENABLE_CMDID = WMI_TLV_CMD(WMI_GRP_LOCATION_SCAN), + WMI_BATCH_SCAN_DISABLE_CMDID, + WMI_BATCH_SCAN_TRIGGER_RESULT_CMDID, + WMI_OEM_REQ_CMDID = WMI_TLV_CMD(WMI_GRP_OEM), + WMI_OEM_REQUEST_CMDID, + WMI_LPI_OEM_REQ_CMDID, + WMI_NAN_CMDID = WMI_TLV_CMD(WMI_GRP_NAN), + WMI_MODEM_POWER_STATE_CMDID = WMI_TLV_CMD(WMI_GRP_COEX), + WMI_CHAN_AVOID_UPDATE_CMDID, + WMI_COEX_CONFIG_CMDID, + WMI_CHAN_AVOID_RPT_ALLOW_CMDID, + WMI_COEX_GET_ANTENNA_ISOLATION_CMDID, + WMI_SAR_LIMITS_CMDID, + WMI_OBSS_SCAN_ENABLE_CMDID = WMI_TLV_CMD(WMI_GRP_OBSS_OFL), + WMI_OBSS_SCAN_DISABLE_CMDID, + WMI_LPI_MGMT_SNOOPING_CONFIG_CMDID = WMI_TLV_CMD(WMI_GRP_LPI), + WMI_LPI_START_SCAN_CMDID, + WMI_LPI_STOP_SCAN_CMDID, + WMI_EXTSCAN_START_CMDID = WMI_TLV_CMD(WMI_GRP_EXTSCAN), + WMI_EXTSCAN_STOP_CMDID, + WMI_EXTSCAN_CONFIGURE_WLAN_CHANGE_MONITOR_CMDID, + WMI_EXTSCAN_CONFIGURE_HOTLIST_MONITOR_CMDID, + WMI_EXTSCAN_GET_CACHED_RESULTS_CMDID, + WMI_EXTSCAN_GET_WLAN_CHANGE_RESULTS_CMDID, + WMI_EXTSCAN_SET_CAPABILITIES_CMDID, + WMI_EXTSCAN_GET_CAPABILITIES_CMDID, + WMI_EXTSCAN_CONFIGURE_HOTLIST_SSID_MONITOR_CMDID, + WMI_EXTSCAN_CONFIGURE_MAWC_CMDID, + WMI_SET_DHCP_SERVER_OFFLOAD_CMDID = WMI_TLV_CMD(WMI_GRP_DHCP_OFL), + WMI_IPA_OFFLOAD_ENABLE_DISABLE_CMDID = WMI_TLV_CMD(WMI_GRP_IPA), + WMI_MDNS_OFFLOAD_ENABLE_CMDID = WMI_TLV_CMD(WMI_GRP_MDNS_OFL), + WMI_MDNS_SET_FQDN_CMDID, + WMI_MDNS_SET_RESPONSE_CMDID, + WMI_MDNS_GET_STATS_CMDID, + WMI_SAP_OFL_ENABLE_CMDID = WMI_TLV_CMD(WMI_GRP_SAP_OFL), + WMI_SAP_SET_BLACKLIST_PARAM_CMDID, + WMI_OCB_SET_CONFIG_CMDID = WMI_TLV_CMD(WMI_GRP_OCB), + WMI_OCB_SET_UTC_TIME_CMDID, + WMI_OCB_START_TIMING_ADVERT_CMDID, + WMI_OCB_STOP_TIMING_ADVERT_CMDID, + WMI_OCB_GET_TSF_TIMER_CMDID, + WMI_DCC_GET_STATS_CMDID, + WMI_DCC_CLEAR_STATS_CMDID, + WMI_DCC_UPDATE_NDL_CMDID, + WMI_SOC_SET_PCL_CMDID = WMI_TLV_CMD(WMI_GRP_SOC), + WMI_SOC_SET_HW_MODE_CMDID, + WMI_SOC_SET_DUAL_MAC_CONFIG_CMDID, + WMI_SOC_SET_ANTENNA_MODE_CMDID, + WMI_PACKET_FILTER_CONFIG_CMDID = WMI_TLV_CMD(WMI_GRP_PKT_FILTER), + WMI_PACKET_FILTER_ENABLE_CMDID, + WMI_MAWC_SENSOR_REPORT_IND_CMDID = WMI_TLV_CMD(WMI_GRP_MAWC), + WMI_PMF_OFFLOAD_SET_SA_QUERY_CMDID = WMI_TLV_CMD(WMI_GRP_PMF_OFFLOAD), + WMI_BPF_GET_CAPABILITY_CMDID = WMI_TLV_CMD(WMI_GRP_BPF_OFFLOAD), + WMI_BPF_GET_VDEV_STATS_CMDID, + WMI_BPF_SET_VDEV_INSTRUCTIONS_CMDID, + WMI_BPF_DEL_VDEV_INSTRUCTIONS_CMDID, + WMI_BPF_SET_VDEV_ACTIVE_MODE_CMDID, + WMI_MNT_FILTER_CMDID = WMI_TLV_CMD(WMI_GRP_MONITOR), + WMI_SET_CURRENT_COUNTRY_CMDID = WMI_TLV_CMD(WMI_GRP_REGULATORY), + WMI_11D_SCAN_START_CMDID, + WMI_11D_SCAN_STOP_CMDID, + WMI_SET_INIT_COUNTRY_CMDID, + WMI_NDI_GET_CAP_REQ_CMDID = WMI_TLV_CMD(WMI_GRP_PROTOTYPE), + WMI_NDP_INITIATOR_REQ_CMDID, + WMI_NDP_RESPONDER_REQ_CMDID, + WMI_NDP_END_REQ_CMDID, + WMI_HW_DATA_FILTER_CMDID = WMI_TLV_CMD(WMI_GRP_HW_DATA_FILTER), +}; + +enum wmi_tlv_event_id { + WMI_SERVICE_READY_EVENTID = 0x1, + WMI_READY_EVENTID, + WMI_SERVICE_AVAILABLE_EVENTID, + WMI_SCAN_EVENTID = WMI_EVT_GRP_START_ID(WMI_GRP_SCAN), + WMI_PDEV_TPC_CONFIG_EVENTID = WMI_TLV_CMD(WMI_GRP_PDEV), + WMI_CHAN_INFO_EVENTID, + WMI_PHYERR_EVENTID, + WMI_PDEV_DUMP_EVENTID, + WMI_TX_PAUSE_EVENTID, + WMI_DFS_RADAR_EVENTID, + WMI_PDEV_L1SS_TRACK_EVENTID, + WMI_PDEV_TEMPERATURE_EVENTID, + WMI_SERVICE_READY_EXT_EVENTID, + WMI_PDEV_FIPS_EVENTID, + WMI_PDEV_CHANNEL_HOPPING_EVENTID, + WMI_PDEV_ANI_CCK_LEVEL_EVENTID, + WMI_PDEV_ANI_OFDM_LEVEL_EVENTID, + WMI_PDEV_TPC_EVENTID, + WMI_PDEV_NFCAL_POWER_ALL_CHANNELS_EVENTID, + WMI_PDEV_SET_HW_MODE_RESP_EVENTID, + WMI_PDEV_HW_MODE_TRANSITION_EVENTID, + WMI_PDEV_SET_MAC_CONFIG_RESP_EVENTID, + WMI_PDEV_ANTDIV_STATUS_EVENTID, + WMI_PDEV_CHIP_POWER_STATS_EVENTID, + WMI_PDEV_CHIP_POWER_SAVE_FAILURE_DETECTED_EVENTID, + WMI_PDEV_CSA_SWITCH_COUNT_STATUS_EVENTID, + WMI_PDEV_CHECK_CAL_VERSION_EVENTID, + WMI_PDEV_DIV_RSSI_ANTID_EVENTID, + WMI_PDEV_BSS_CHAN_INFO_EVENTID, + WMI_PDEV_UPDATE_CTLTABLE_EVENTID, + WMI_PDEV_DMA_RING_CFG_RSP_EVENTID, + WMI_PDEV_DMA_RING_BUF_RELEASE_EVENTID, + WMI_PDEV_CTL_FAILSAFE_CHECK_EVENTID, + WMI_VDEV_START_RESP_EVENTID = WMI_TLV_CMD(WMI_GRP_VDEV), + WMI_VDEV_STOPPED_EVENTID, + WMI_VDEV_INSTALL_KEY_COMPLETE_EVENTID, + WMI_VDEV_MCC_BCN_INTERVAL_CHANGE_REQ_EVENTID, + WMI_VDEV_TSF_REPORT_EVENTID, + WMI_VDEV_DELETE_RESP_EVENTID, + WMI_VDEV_ENCRYPT_DECRYPT_DATA_RESP_EVENTID, + WMI_VDEV_ADD_MAC_ADDR_TO_RX_FILTER_STATUS_EVENTID, + WMI_PEER_STA_KICKOUT_EVENTID = WMI_TLV_CMD(WMI_GRP_PEER), + WMI_PEER_INFO_EVENTID, + WMI_PEER_TX_FAIL_CNT_THR_EVENTID, + WMI_PEER_ESTIMATED_LINKSPEED_EVENTID, + WMI_PEER_STATE_EVENTID, + WMI_PEER_ASSOC_CONF_EVENTID, + WMI_PEER_DELETE_RESP_EVENTID, + WMI_PEER_RATECODE_LIST_EVENTID, + WMI_WDS_PEER_EVENTID, + WMI_PEER_STA_PS_STATECHG_EVENTID, + WMI_PEER_ANTDIV_INFO_EVENTID, + WMI_MGMT_RX_EVENTID = WMI_TLV_CMD(WMI_GRP_MGMT), + WMI_HOST_SWBA_EVENTID, + WMI_TBTTOFFSET_UPDATE_EVENTID, + WMI_OFFLOAD_BCN_TX_STATUS_EVENTID, + WMI_OFFLOAD_PROB_RESP_TX_STATUS_EVENTID, + WMI_MGMT_TX_COMPLETION_EVENTID, + WMI_MGMT_TX_BUNDLE_COMPLETION_EVENTID, + WMI_TBTTOFFSET_EXT_UPDATE_EVENTID, + WMI_TX_DELBA_COMPLETE_EVENTID = WMI_TLV_CMD(WMI_GRP_BA_NEG), + WMI_TX_ADDBA_COMPLETE_EVENTID, + WMI_BA_RSP_SSN_EVENTID, + WMI_AGGR_STATE_TRIG_EVENTID, + WMI_ROAM_EVENTID = WMI_TLV_CMD(WMI_GRP_ROAM), + WMI_PROFILE_MATCH, + WMI_ROAM_SYNCH_EVENTID, + WMI_P2P_DISC_EVENTID = WMI_TLV_CMD(WMI_GRP_P2P), + WMI_P2P_NOA_EVENTID, + WMI_P2P_LISTEN_OFFLOAD_STOPPED_EVENTID, + WMI_AP_PS_EGAP_INFO_EVENTID = WMI_TLV_CMD(WMI_GRP_AP_PS), + WMI_PDEV_RESUME_EVENTID = WMI_TLV_CMD(WMI_GRP_SUSPEND), + WMI_WOW_WAKEUP_HOST_EVENTID = WMI_TLV_CMD(WMI_GRP_WOW), + WMI_D0_WOW_DISABLE_ACK_EVENTID, + WMI_WOW_INITIAL_WAKEUP_EVENTID, + WMI_RTT_MEASUREMENT_REPORT_EVENTID = WMI_TLV_CMD(WMI_GRP_RTT), + WMI_TSF_MEASUREMENT_REPORT_EVENTID, + WMI_RTT_ERROR_REPORT_EVENTID, + WMI_STATS_EXT_EVENTID = WMI_TLV_CMD(WMI_GRP_STATS), + WMI_IFACE_LINK_STATS_EVENTID, + WMI_PEER_LINK_STATS_EVENTID, + WMI_RADIO_LINK_STATS_EVENTID, + WMI_UPDATE_FW_MEM_DUMP_EVENTID, + WMI_DIAG_EVENT_LOG_SUPPORTED_EVENTID, + WMI_INST_RSSI_STATS_EVENTID, + WMI_RADIO_TX_POWER_LEVEL_STATS_EVENTID, + WMI_REPORT_STATS_EVENTID, + WMI_UPDATE_RCPI_EVENTID, + WMI_PEER_STATS_INFO_EVENTID, + WMI_RADIO_CHAN_STATS_EVENTID, + WMI_NLO_MATCH_EVENTID = WMI_TLV_CMD(WMI_GRP_NLO_OFL), + WMI_NLO_SCAN_COMPLETE_EVENTID, + WMI_APFIND_EVENTID, + WMI_PASSPOINT_MATCH_EVENTID, + WMI_GTK_OFFLOAD_STATUS_EVENTID = WMI_TLV_CMD(WMI_GRP_GTK_OFL), + WMI_GTK_REKEY_FAIL_EVENTID, + WMI_CSA_HANDLING_EVENTID = WMI_TLV_CMD(WMI_GRP_CSA_OFL), + WMI_CHATTER_PC_QUERY_EVENTID = WMI_TLV_CMD(WMI_GRP_CHATTER), + WMI_PDEV_DFS_RADAR_DETECTION_EVENTID = WMI_TLV_CMD(WMI_GRP_DFS), + WMI_VDEV_DFS_CAC_COMPLETE_EVENTID, + WMI_VDEV_ADFS_OCAC_COMPLETE_EVENTID, + WMI_ECHO_EVENTID = WMI_TLV_CMD(WMI_GRP_MISC), + WMI_PDEV_UTF_EVENTID, + WMI_DEBUG_MESG_EVENTID, + WMI_UPDATE_STATS_EVENTID, + WMI_DEBUG_PRINT_EVENTID, + WMI_DCS_INTERFERENCE_EVENTID, + WMI_PDEV_QVIT_EVENTID, + WMI_WLAN_PROFILE_DATA_EVENTID, + WMI_PDEV_FTM_INTG_EVENTID, + WMI_WLAN_FREQ_AVOID_EVENTID, + WMI_VDEV_GET_KEEPALIVE_EVENTID, + WMI_THERMAL_MGMT_EVENTID, + WMI_DIAG_DATA_CONTAINER_EVENTID, + WMI_HOST_AUTO_SHUTDOWN_EVENTID, + WMI_UPDATE_WHAL_MIB_STATS_EVENTID, + WMI_UPDATE_VDEV_RATE_STATS_EVENTID, + WMI_DIAG_EVENTID, + WMI_OCB_SET_SCHED_EVENTID, + WMI_DEBUG_MESG_FLUSH_COMPLETE_EVENTID, + WMI_RSSI_BREACH_EVENTID, + WMI_TRANSFER_DATA_TO_FLASH_COMPLETE_EVENTID, + WMI_PDEV_UTF_SCPC_EVENTID, + WMI_READ_DATA_FROM_FLASH_EVENTID, + WMI_REPORT_RX_AGGR_FAILURE_EVENTID, + WMI_PKGID_EVENTID, + WMI_GPIO_INPUT_EVENTID = WMI_TLV_CMD(WMI_GRP_GPIO), + WMI_UPLOADH_EVENTID, + WMI_CAPTUREH_EVENTID, + WMI_RFKILL_STATE_CHANGE_EVENTID, + WMI_TDLS_PEER_EVENTID = WMI_TLV_CMD(WMI_GRP_TDLS), + WMI_STA_SMPS_FORCE_MODE_COMPL_EVENTID = WMI_TLV_CMD(WMI_GRP_STA_SMPS), + WMI_BATCH_SCAN_ENABLED_EVENTID = WMI_TLV_CMD(WMI_GRP_LOCATION_SCAN), + WMI_BATCH_SCAN_RESULT_EVENTID, + WMI_OEM_CAPABILITY_EVENTID = WMI_TLV_CMD(WMI_GRP_OEM), + WMI_OEM_MEASUREMENT_REPORT_EVENTID, + WMI_OEM_ERROR_REPORT_EVENTID, + WMI_OEM_RESPONSE_EVENTID, + WMI_NAN_EVENTID = WMI_TLV_CMD(WMI_GRP_NAN), + WMI_NAN_DISC_IFACE_CREATED_EVENTID, + WMI_NAN_DISC_IFACE_DELETED_EVENTID, + WMI_NAN_STARTED_CLUSTER_EVENTID, + WMI_NAN_JOINED_CLUSTER_EVENTID, + WMI_COEX_REPORT_ANTENNA_ISOLATION_EVENTID = WMI_TLV_CMD(WMI_GRP_COEX), + WMI_LPI_RESULT_EVENTID = WMI_TLV_CMD(WMI_GRP_LPI), + WMI_LPI_STATUS_EVENTID, + WMI_LPI_HANDOFF_EVENTID, + WMI_EXTSCAN_START_STOP_EVENTID = WMI_TLV_CMD(WMI_GRP_EXTSCAN), + WMI_EXTSCAN_OPERATION_EVENTID, + WMI_EXTSCAN_TABLE_USAGE_EVENTID, + WMI_EXTSCAN_CACHED_RESULTS_EVENTID, + WMI_EXTSCAN_WLAN_CHANGE_RESULTS_EVENTID, + WMI_EXTSCAN_HOTLIST_MATCH_EVENTID, + WMI_EXTSCAN_CAPABILITIES_EVENTID, + WMI_EXTSCAN_HOTLIST_SSID_MATCH_EVENTID, + WMI_MDNS_STATS_EVENTID = WMI_TLV_CMD(WMI_GRP_MDNS_OFL), + WMI_SAP_OFL_ADD_STA_EVENTID = WMI_TLV_CMD(WMI_GRP_SAP_OFL), + WMI_SAP_OFL_DEL_STA_EVENTID, + WMI_OCB_SET_CONFIG_RESP_EVENTID = WMI_TLV_CMD(WMI_GRP_OCB), + WMI_OCB_GET_TSF_TIMER_RESP_EVENTID, + WMI_DCC_GET_STATS_RESP_EVENTID, + WMI_DCC_UPDATE_NDL_RESP_EVENTID, + WMI_DCC_STATS_EVENTID, + WMI_SOC_SET_HW_MODE_RESP_EVENTID = WMI_TLV_CMD(WMI_GRP_SOC), + WMI_SOC_HW_MODE_TRANSITION_EVENTID, + WMI_SOC_SET_DUAL_MAC_CONFIG_RESP_EVENTID, + WMI_MAWC_ENABLE_SENSOR_EVENTID = WMI_TLV_CMD(WMI_GRP_MAWC), + WMI_BPF_CAPABILIY_INFO_EVENTID = WMI_TLV_CMD(WMI_GRP_BPF_OFFLOAD), + WMI_BPF_VDEV_STATS_INFO_EVENTID, + WMI_RMC_NEW_LEADER_EVENTID = WMI_TLV_CMD(WMI_GRP_RMC), + WMI_REG_CHAN_LIST_CC_EVENTID = WMI_TLV_CMD(WMI_GRP_REGULATORY), + WMI_11D_NEW_COUNTRY_EVENTID, + WMI_NDI_CAP_RSP_EVENTID = WMI_TLV_CMD(WMI_GRP_PROTOTYPE), + WMI_NDP_INITIATOR_RSP_EVENTID, + WMI_NDP_RESPONDER_RSP_EVENTID, + WMI_NDP_END_RSP_EVENTID, + WMI_NDP_INDICATION_EVENTID, + WMI_NDP_CONFIRM_EVENTID, + WMI_NDP_END_INDICATION_EVENTID, +}; + +enum wmi_tlv_pdev_param { + WMI_PDEV_PARAM_TX_CHAIN_MASK = 0x1, + WMI_PDEV_PARAM_RX_CHAIN_MASK, + WMI_PDEV_PARAM_TXPOWER_LIMIT2G, + WMI_PDEV_PARAM_TXPOWER_LIMIT5G, + WMI_PDEV_PARAM_TXPOWER_SCALE, + WMI_PDEV_PARAM_BEACON_GEN_MODE, + WMI_PDEV_PARAM_BEACON_TX_MODE, + WMI_PDEV_PARAM_RESMGR_OFFCHAN_MODE, + WMI_PDEV_PARAM_PROTECTION_MODE, + WMI_PDEV_PARAM_DYNAMIC_BW, + WMI_PDEV_PARAM_NON_AGG_SW_RETRY_TH, + WMI_PDEV_PARAM_AGG_SW_RETRY_TH, + WMI_PDEV_PARAM_STA_KICKOUT_TH, + WMI_PDEV_PARAM_AC_AGGRSIZE_SCALING, + WMI_PDEV_PARAM_LTR_ENABLE, + WMI_PDEV_PARAM_LTR_AC_LATENCY_BE, + WMI_PDEV_PARAM_LTR_AC_LATENCY_BK, + WMI_PDEV_PARAM_LTR_AC_LATENCY_VI, + WMI_PDEV_PARAM_LTR_AC_LATENCY_VO, + WMI_PDEV_PARAM_LTR_AC_LATENCY_TIMEOUT, + WMI_PDEV_PARAM_LTR_SLEEP_OVERRIDE, + WMI_PDEV_PARAM_LTR_RX_OVERRIDE, + WMI_PDEV_PARAM_LTR_TX_ACTIVITY_TIMEOUT, + WMI_PDEV_PARAM_L1SS_ENABLE, + WMI_PDEV_PARAM_DSLEEP_ENABLE, + WMI_PDEV_PARAM_PCIELP_TXBUF_FLUSH, + WMI_PDEV_PARAM_PCIELP_TXBUF_WATERMARK, + WMI_PDEV_PARAM_PCIELP_TXBUF_TMO_EN, + WMI_PDEV_PARAM_PCIELP_TXBUF_TMO_VALUE, + WMI_PDEV_PARAM_PDEV_STATS_UPDATE_PERIOD, + WMI_PDEV_PARAM_VDEV_STATS_UPDATE_PERIOD, + WMI_PDEV_PARAM_PEER_STATS_UPDATE_PERIOD, + WMI_PDEV_PARAM_BCNFLT_STATS_UPDATE_PERIOD, + WMI_PDEV_PARAM_PMF_QOS, + WMI_PDEV_PARAM_ARP_AC_OVERRIDE, + WMI_PDEV_PARAM_DCS, + WMI_PDEV_PARAM_ANI_ENABLE, + WMI_PDEV_PARAM_ANI_POLL_PERIOD, + WMI_PDEV_PARAM_ANI_LISTEN_PERIOD, + WMI_PDEV_PARAM_ANI_OFDM_LEVEL, + WMI_PDEV_PARAM_ANI_CCK_LEVEL, + WMI_PDEV_PARAM_DYNTXCHAIN, + WMI_PDEV_PARAM_PROXY_STA, + WMI_PDEV_PARAM_IDLE_PS_CONFIG, + WMI_PDEV_PARAM_POWER_GATING_SLEEP, + WMI_PDEV_PARAM_RFKILL_ENABLE, + WMI_PDEV_PARAM_BURST_DUR, + WMI_PDEV_PARAM_BURST_ENABLE, + WMI_PDEV_PARAM_HW_RFKILL_CONFIG, + WMI_PDEV_PARAM_LOW_POWER_RF_ENABLE, + WMI_PDEV_PARAM_L1SS_TRACK, + WMI_PDEV_PARAM_HYST_EN, + WMI_PDEV_PARAM_POWER_COLLAPSE_ENABLE, + WMI_PDEV_PARAM_LED_SYS_STATE, + WMI_PDEV_PARAM_LED_ENABLE, + WMI_PDEV_PARAM_AUDIO_OVER_WLAN_LATENCY, + WMI_PDEV_PARAM_AUDIO_OVER_WLAN_ENABLE, + WMI_PDEV_PARAM_WHAL_MIB_STATS_UPDATE_ENABLE, + WMI_PDEV_PARAM_VDEV_RATE_STATS_UPDATE_PERIOD, + WMI_PDEV_PARAM_CTS_CBW, + WMI_PDEV_PARAM_WNTS_CONFIG, + WMI_PDEV_PARAM_ADAPTIVE_EARLY_RX_ENABLE, + WMI_PDEV_PARAM_ADAPTIVE_EARLY_RX_MIN_SLEEP_SLOP, + WMI_PDEV_PARAM_ADAPTIVE_EARLY_RX_INC_DEC_STEP, + WMI_PDEV_PARAM_EARLY_RX_FIX_SLEEP_SLOP, + WMI_PDEV_PARAM_BMISS_BASED_ADAPTIVE_BTO_ENABLE, + WMI_PDEV_PARAM_BMISS_BTO_MIN_BCN_TIMEOUT, + WMI_PDEV_PARAM_BMISS_BTO_INC_DEC_STEP, + WMI_PDEV_PARAM_BTO_FIX_BCN_TIMEOUT, + WMI_PDEV_PARAM_CE_BASED_ADAPTIVE_BTO_ENABLE, + WMI_PDEV_PARAM_CE_BTO_COMBO_CE_VALUE, + WMI_PDEV_PARAM_TX_CHAIN_MASK_2G, + WMI_PDEV_PARAM_RX_CHAIN_MASK_2G, + WMI_PDEV_PARAM_TX_CHAIN_MASK_5G, + WMI_PDEV_PARAM_RX_CHAIN_MASK_5G, + WMI_PDEV_PARAM_TX_CHAIN_MASK_CCK, + WMI_PDEV_PARAM_TX_CHAIN_MASK_1SS, + WMI_PDEV_PARAM_CTS2SELF_FOR_P2P_GO_CONFIG, + WMI_PDEV_PARAM_TXPOWER_DECR_DB, + WMI_PDEV_PARAM_AGGR_BURST, + WMI_PDEV_PARAM_RX_DECAP_MODE, + WMI_PDEV_PARAM_FAST_CHANNEL_RESET, + WMI_PDEV_PARAM_SMART_ANTENNA_DEFAULT_ANTENNA, + WMI_PDEV_PARAM_ANTENNA_GAIN, + WMI_PDEV_PARAM_RX_FILTER, + WMI_PDEV_SET_MCAST_TO_UCAST_TID, + WMI_PDEV_PARAM_PROXY_STA_MODE, + WMI_PDEV_PARAM_SET_MCAST2UCAST_MODE, + WMI_PDEV_PARAM_SET_MCAST2UCAST_BUFFER, + WMI_PDEV_PARAM_REMOVE_MCAST2UCAST_BUFFER, + WMI_PDEV_PEER_STA_PS_STATECHG_ENABLE, + WMI_PDEV_PARAM_IGMPMLD_AC_OVERRIDE, + WMI_PDEV_PARAM_BLOCK_INTERBSS, + WMI_PDEV_PARAM_SET_DISABLE_RESET_CMDID, + WMI_PDEV_PARAM_SET_MSDU_TTL_CMDID, + WMI_PDEV_PARAM_SET_PPDU_DURATION_CMDID, + WMI_PDEV_PARAM_TXBF_SOUND_PERIOD_CMDID, + WMI_PDEV_PARAM_SET_PROMISC_MODE_CMDID, + WMI_PDEV_PARAM_SET_BURST_MODE_CMDID, + WMI_PDEV_PARAM_EN_STATS, + WMI_PDEV_PARAM_MU_GROUP_POLICY, + WMI_PDEV_PARAM_NOISE_DETECTION, + WMI_PDEV_PARAM_NOISE_THRESHOLD, + WMI_PDEV_PARAM_DPD_ENABLE, + WMI_PDEV_PARAM_SET_MCAST_BCAST_ECHO, + WMI_PDEV_PARAM_ATF_STRICT_SCH, + WMI_PDEV_PARAM_ATF_SCHED_DURATION, + WMI_PDEV_PARAM_ANT_PLZN, + WMI_PDEV_PARAM_MGMT_RETRY_LIMIT, + WMI_PDEV_PARAM_SENSITIVITY_LEVEL, + WMI_PDEV_PARAM_SIGNED_TXPOWER_2G, + WMI_PDEV_PARAM_SIGNED_TXPOWER_5G, + WMI_PDEV_PARAM_ENABLE_PER_TID_AMSDU, + WMI_PDEV_PARAM_ENABLE_PER_TID_AMPDU, + WMI_PDEV_PARAM_CCA_THRESHOLD, + WMI_PDEV_PARAM_RTS_FIXED_RATE, + WMI_PDEV_PARAM_PDEV_RESET, + WMI_PDEV_PARAM_WAPI_MBSSID_OFFSET, + WMI_PDEV_PARAM_ARP_DBG_SRCADDR, + WMI_PDEV_PARAM_ARP_DBG_DSTADDR, + WMI_PDEV_PARAM_ATF_OBSS_NOISE_SCH, + WMI_PDEV_PARAM_ATF_OBSS_NOISE_SCALING_FACTOR, + WMI_PDEV_PARAM_CUST_TXPOWER_SCALE, + WMI_PDEV_PARAM_ATF_DYNAMIC_ENABLE, + WMI_PDEV_PARAM_CTRL_RETRY_LIMIT, + WMI_PDEV_PARAM_PROPAGATION_DELAY, + WMI_PDEV_PARAM_ENA_ANT_DIV, + WMI_PDEV_PARAM_FORCE_CHAIN_ANT, + WMI_PDEV_PARAM_ANT_DIV_SELFTEST, + WMI_PDEV_PARAM_ANT_DIV_SELFTEST_INTVL, + WMI_PDEV_PARAM_STATS_OBSERVATION_PERIOD, + WMI_PDEV_PARAM_TX_PPDU_DELAY_BIN_SIZE_MS, + WMI_PDEV_PARAM_TX_PPDU_DELAY_ARRAY_LEN, + WMI_PDEV_PARAM_TX_MPDU_AGGR_ARRAY_LEN, + WMI_PDEV_PARAM_RX_MPDU_AGGR_ARRAY_LEN, + WMI_PDEV_PARAM_TX_SCH_DELAY, + WMI_PDEV_PARAM_ENABLE_RTS_SIFS_BURSTING, + WMI_PDEV_PARAM_MAX_MPDUS_IN_AMPDU, + WMI_PDEV_PARAM_PEER_STATS_INFO_ENABLE, + WMI_PDEV_PARAM_FAST_PWR_TRANSITION, + WMI_PDEV_PARAM_RADIO_CHAN_STATS_ENABLE, + WMI_PDEV_PARAM_RADIO_DIAGNOSIS_ENABLE, + WMI_PDEV_PARAM_MESH_MCAST_ENABLE, +}; + +enum wmi_tlv_vdev_param { + WMI_VDEV_PARAM_RTS_THRESHOLD = 0x1, + WMI_VDEV_PARAM_FRAGMENTATION_THRESHOLD, + WMI_VDEV_PARAM_BEACON_INTERVAL, + WMI_VDEV_PARAM_LISTEN_INTERVAL, + WMI_VDEV_PARAM_MULTICAST_RATE, + WMI_VDEV_PARAM_MGMT_TX_RATE, + WMI_VDEV_PARAM_SLOT_TIME, + WMI_VDEV_PARAM_PREAMBLE, + WMI_VDEV_PARAM_SWBA_TIME, + WMI_VDEV_STATS_UPDATE_PERIOD, + WMI_VDEV_PWRSAVE_AGEOUT_TIME, + WMI_VDEV_HOST_SWBA_INTERVAL, + WMI_VDEV_PARAM_DTIM_PERIOD, + WMI_VDEV_OC_SCHEDULER_AIR_TIME_LIMIT, + WMI_VDEV_PARAM_WDS, + WMI_VDEV_PARAM_ATIM_WINDOW, + WMI_VDEV_PARAM_BMISS_COUNT_MAX, + WMI_VDEV_PARAM_BMISS_FIRST_BCNT, + WMI_VDEV_PARAM_BMISS_FINAL_BCNT, + WMI_VDEV_PARAM_FEATURE_WMM, + WMI_VDEV_PARAM_CHWIDTH, + WMI_VDEV_PARAM_CHEXTOFFSET, + WMI_VDEV_PARAM_DISABLE_HTPROTECTION, + WMI_VDEV_PARAM_STA_QUICKKICKOUT, + WMI_VDEV_PARAM_MGMT_RATE, + WMI_VDEV_PARAM_PROTECTION_MODE, + WMI_VDEV_PARAM_FIXED_RATE, + WMI_VDEV_PARAM_SGI, + WMI_VDEV_PARAM_LDPC, + WMI_VDEV_PARAM_TX_STBC, + WMI_VDEV_PARAM_RX_STBC, + WMI_VDEV_PARAM_INTRA_BSS_FWD, + WMI_VDEV_PARAM_DEF_KEYID, + WMI_VDEV_PARAM_NSS, + WMI_VDEV_PARAM_BCAST_DATA_RATE, + WMI_VDEV_PARAM_MCAST_DATA_RATE, + WMI_VDEV_PARAM_MCAST_INDICATE, + WMI_VDEV_PARAM_DHCP_INDICATE, + WMI_VDEV_PARAM_UNKNOWN_DEST_INDICATE, + WMI_VDEV_PARAM_AP_KEEPALIVE_MIN_IDLE_INACTIVE_TIME_SECS, + WMI_VDEV_PARAM_AP_KEEPALIVE_MAX_IDLE_INACTIVE_TIME_SECS, + WMI_VDEV_PARAM_AP_KEEPALIVE_MAX_UNRESPONSIVE_TIME_SECS, + WMI_VDEV_PARAM_AP_ENABLE_NAWDS, + WMI_VDEV_PARAM_ENABLE_RTSCTS, + WMI_VDEV_PARAM_TXBF, + WMI_VDEV_PARAM_PACKET_POWERSAVE, + WMI_VDEV_PARAM_DROP_UNENCRY, + WMI_VDEV_PARAM_TX_ENCAP_TYPE, + WMI_VDEV_PARAM_AP_DETECT_OUT_OF_SYNC_SLEEPING_STA_TIME_SECS, + WMI_VDEV_PARAM_EARLY_RX_ADJUST_ENABLE, + WMI_VDEV_PARAM_EARLY_RX_TGT_BMISS_NUM, + WMI_VDEV_PARAM_EARLY_RX_BMISS_SAMPLE_CYCLE, + WMI_VDEV_PARAM_EARLY_RX_SLOP_STEP, + WMI_VDEV_PARAM_EARLY_RX_INIT_SLOP, + WMI_VDEV_PARAM_EARLY_RX_ADJUST_PAUSE, + WMI_VDEV_PARAM_TX_PWRLIMIT, + WMI_VDEV_PARAM_SNR_NUM_FOR_CAL, + WMI_VDEV_PARAM_ROAM_FW_OFFLOAD, + WMI_VDEV_PARAM_ENABLE_RMC, + WMI_VDEV_PARAM_IBSS_MAX_BCN_LOST_MS, + WMI_VDEV_PARAM_MAX_RATE, + WMI_VDEV_PARAM_EARLY_RX_DRIFT_SAMPLE, + WMI_VDEV_PARAM_SET_IBSS_TX_FAIL_CNT_THR, + WMI_VDEV_PARAM_EBT_RESYNC_TIMEOUT, + WMI_VDEV_PARAM_AGGR_TRIG_EVENT_ENABLE, + WMI_VDEV_PARAM_IS_IBSS_POWER_SAVE_ALLOWED, + WMI_VDEV_PARAM_IS_POWER_COLLAPSE_ALLOWED, + WMI_VDEV_PARAM_IS_AWAKE_ON_TXRX_ENABLED, + WMI_VDEV_PARAM_INACTIVITY_CNT, + WMI_VDEV_PARAM_TXSP_END_INACTIVITY_TIME_MS, + WMI_VDEV_PARAM_DTIM_POLICY, + WMI_VDEV_PARAM_IBSS_PS_WARMUP_TIME_SECS, + WMI_VDEV_PARAM_IBSS_PS_1RX_CHAIN_IN_ATIM_WINDOW_ENABLE, + WMI_VDEV_PARAM_RX_LEAK_WINDOW, + WMI_VDEV_PARAM_STATS_AVG_FACTOR, + WMI_VDEV_PARAM_DISCONNECT_TH, + WMI_VDEV_PARAM_RTSCTS_RATE, + WMI_VDEV_PARAM_MCC_RTSCTS_PROTECTION_ENABLE, + WMI_VDEV_PARAM_MCC_BROADCAST_PROBE_ENABLE, + WMI_VDEV_PARAM_TXPOWER_SCALE, + WMI_VDEV_PARAM_TXPOWER_SCALE_DECR_DB, + WMI_VDEV_PARAM_MCAST2UCAST_SET, + WMI_VDEV_PARAM_RC_NUM_RETRIES, + WMI_VDEV_PARAM_CABQ_MAXDUR, + WMI_VDEV_PARAM_MFPTEST_SET, + WMI_VDEV_PARAM_RTS_FIXED_RATE, + WMI_VDEV_PARAM_VHT_SGIMASK, + WMI_VDEV_PARAM_VHT80_RATEMASK, + WMI_VDEV_PARAM_PROXY_STA, + WMI_VDEV_PARAM_VIRTUAL_CELL_MODE, + WMI_VDEV_PARAM_RX_DECAP_TYPE, + WMI_VDEV_PARAM_BW_NSS_RATEMASK, + WMI_VDEV_PARAM_SENSOR_AP, + WMI_VDEV_PARAM_BEACON_RATE, + WMI_VDEV_PARAM_DTIM_ENABLE_CTS, + WMI_VDEV_PARAM_STA_KICKOUT, + WMI_VDEV_PARAM_CAPABILITIES, + WMI_VDEV_PARAM_TSF_INCREMENT, + WMI_VDEV_PARAM_AMPDU_PER_AC, + WMI_VDEV_PARAM_RX_FILTER, + WMI_VDEV_PARAM_MGMT_TX_POWER, + WMI_VDEV_PARAM_NON_AGG_SW_RETRY_TH, + WMI_VDEV_PARAM_AGG_SW_RETRY_TH, + WMI_VDEV_PARAM_DISABLE_DYN_BW_RTS, + WMI_VDEV_PARAM_ATF_SSID_SCHED_POLICY, + WMI_VDEV_PARAM_HE_DCM, + WMI_VDEV_PARAM_HE_RANGE_EXT, + WMI_VDEV_PARAM_ENABLE_BCAST_PROBE_RESPONSE, + WMI_VDEV_PARAM_FILS_MAX_CHANNEL_GUARD_TIME, + WMI_VDEV_PARAM_SET_HE_SOUNDING_MODE = 0x87, + WMI_VDEV_PARAM_PROTOTYPE = 0x8000, + WMI_VDEV_PARAM_BSS_COLOR, + WMI_VDEV_PARAM_SET_HEMU_MODE, + WMI_VDEV_PARAM_TX_OFDMA_CPLEN, +}; + +enum wmi_tlv_peer_flags { + WMI_TLV_PEER_AUTH = 0x00000001, + WMI_TLV_PEER_QOS = 0x00000002, + WMI_TLV_PEER_NEED_PTK_4_WAY = 0x00000004, + WMI_TLV_PEER_NEED_GTK_2_WAY = 0x00000010, + WMI_TLV_PEER_APSD = 0x00000800, + WMI_TLV_PEER_HT = 0x00001000, + WMI_TLV_PEER_40MHZ = 0x00002000, + WMI_TLV_PEER_STBC = 0x00008000, + WMI_TLV_PEER_LDPC = 0x00010000, + WMI_TLV_PEER_DYN_MIMOPS = 0x00020000, + WMI_TLV_PEER_STATIC_MIMOPS = 0x00040000, + WMI_TLV_PEER_SPATIAL_MUX = 0x00200000, + WMI_TLV_PEER_VHT = 0x02000000, + WMI_TLV_PEER_80MHZ = 0x04000000, + WMI_TLV_PEER_PMF = 0x08000000, + WMI_PEER_IS_P2P_CAPABLE = 0x20000000, + WMI_PEER_160MHZ = 0x40000000, + WMI_PEER_SAFEMODE_EN = 0x80000000, + +}; + +/** Enum list of TLV Tags for each parameter structure type. */ +enum wmi_tlv_tag { + WMI_TAG_LAST_RESERVED = 15, + WMI_TAG_FIRST_ARRAY_ENUM, + WMI_TAG_ARRAY_UINT32 = WMI_TAG_FIRST_ARRAY_ENUM, + WMI_TAG_ARRAY_BYTE, + WMI_TAG_ARRAY_STRUCT, + WMI_TAG_ARRAY_FIXED_STRUCT, + WMI_TAG_LAST_ARRAY_ENUM = 31, + WMI_TAG_SERVICE_READY_EVENT, + WMI_TAG_HAL_REG_CAPABILITIES, + WMI_TAG_WLAN_HOST_MEM_REQ, + WMI_TAG_READY_EVENT, + WMI_TAG_SCAN_EVENT, + WMI_TAG_PDEV_TPC_CONFIG_EVENT, + WMI_TAG_CHAN_INFO_EVENT, + WMI_TAG_COMB_PHYERR_RX_HDR, + WMI_TAG_VDEV_START_RESPONSE_EVENT, + WMI_TAG_VDEV_STOPPED_EVENT, + WMI_TAG_VDEV_INSTALL_KEY_COMPLETE_EVENT, + WMI_TAG_PEER_STA_KICKOUT_EVENT, + WMI_TAG_MGMT_RX_HDR, + WMI_TAG_TBTT_OFFSET_EVENT, + WMI_TAG_TX_DELBA_COMPLETE_EVENT, + WMI_TAG_TX_ADDBA_COMPLETE_EVENT, + WMI_TAG_ROAM_EVENT, + WMI_TAG_WOW_EVENT_INFO, + WMI_TAG_WOW_EVENT_INFO_SECTION_BITMAP, + WMI_TAG_RTT_EVENT_HEADER, + WMI_TAG_RTT_ERROR_REPORT_EVENT, + WMI_TAG_RTT_MEAS_EVENT, + WMI_TAG_ECHO_EVENT, + WMI_TAG_FTM_INTG_EVENT, + WMI_TAG_VDEV_GET_KEEPALIVE_EVENT, + WMI_TAG_GPIO_INPUT_EVENT, + WMI_TAG_CSA_EVENT, + WMI_TAG_GTK_OFFLOAD_STATUS_EVENT, + WMI_TAG_IGTK_INFO, + WMI_TAG_DCS_INTERFERENCE_EVENT, + WMI_TAG_ATH_DCS_CW_INT, + WMI_TAG_WLAN_DCS_CW_INT = /* ALIAS */ + WMI_TAG_ATH_DCS_CW_INT, + WMI_TAG_ATH_DCS_WLAN_INT_STAT, + WMI_TAG_WLAN_DCS_IM_TGT_STATS_T = /* ALIAS */ + WMI_TAG_ATH_DCS_WLAN_INT_STAT, + WMI_TAG_WLAN_PROFILE_CTX_T, + WMI_TAG_WLAN_PROFILE_T, + WMI_TAG_PDEV_QVIT_EVENT, + WMI_TAG_HOST_SWBA_EVENT, + WMI_TAG_TIM_INFO, + WMI_TAG_P2P_NOA_INFO, + WMI_TAG_STATS_EVENT, + WMI_TAG_AVOID_FREQ_RANGES_EVENT, + WMI_TAG_AVOID_FREQ_RANGE_DESC, + WMI_TAG_GTK_REKEY_FAIL_EVENT, + WMI_TAG_INIT_CMD, + WMI_TAG_RESOURCE_CONFIG, + WMI_TAG_WLAN_HOST_MEMORY_CHUNK, + WMI_TAG_START_SCAN_CMD, + WMI_TAG_STOP_SCAN_CMD, + WMI_TAG_SCAN_CHAN_LIST_CMD, + WMI_TAG_CHANNEL, + WMI_TAG_PDEV_SET_REGDOMAIN_CMD, + WMI_TAG_PDEV_SET_PARAM_CMD, + WMI_TAG_PDEV_SET_WMM_PARAMS_CMD, + WMI_TAG_WMM_PARAMS, + WMI_TAG_PDEV_SET_QUIET_CMD, + WMI_TAG_VDEV_CREATE_CMD, + WMI_TAG_VDEV_DELETE_CMD, + WMI_TAG_VDEV_START_REQUEST_CMD, + WMI_TAG_P2P_NOA_DESCRIPTOR, + WMI_TAG_P2P_GO_SET_BEACON_IE, + WMI_TAG_GTK_OFFLOAD_CMD, + WMI_TAG_VDEV_UP_CMD, + WMI_TAG_VDEV_STOP_CMD, + WMI_TAG_VDEV_DOWN_CMD, + WMI_TAG_VDEV_SET_PARAM_CMD, + WMI_TAG_VDEV_INSTALL_KEY_CMD, + WMI_TAG_PEER_CREATE_CMD, + WMI_TAG_PEER_DELETE_CMD, + WMI_TAG_PEER_FLUSH_TIDS_CMD, + WMI_TAG_PEER_SET_PARAM_CMD, + WMI_TAG_PEER_ASSOC_COMPLETE_CMD, + WMI_TAG_VHT_RATE_SET, + WMI_TAG_BCN_TMPL_CMD, + WMI_TAG_PRB_TMPL_CMD, + WMI_TAG_BCN_PRB_INFO, + WMI_TAG_PEER_TID_ADDBA_CMD, + WMI_TAG_PEER_TID_DELBA_CMD, + WMI_TAG_STA_POWERSAVE_MODE_CMD, + WMI_TAG_STA_POWERSAVE_PARAM_CMD, + WMI_TAG_STA_DTIM_PS_METHOD_CMD, + WMI_TAG_ROAM_SCAN_MODE, + WMI_TAG_ROAM_SCAN_RSSI_THRESHOLD, + WMI_TAG_ROAM_SCAN_PERIOD, + WMI_TAG_ROAM_SCAN_RSSI_CHANGE_THRESHOLD, + WMI_TAG_PDEV_SUSPEND_CMD, + WMI_TAG_PDEV_RESUME_CMD, + WMI_TAG_ADD_BCN_FILTER_CMD, + WMI_TAG_RMV_BCN_FILTER_CMD, + WMI_TAG_WOW_ENABLE_CMD, + WMI_TAG_WOW_HOSTWAKEUP_FROM_SLEEP_CMD, + WMI_TAG_STA_UAPSD_AUTO_TRIG_CMD, + WMI_TAG_STA_UAPSD_AUTO_TRIG_PARAM, + WMI_TAG_SET_ARP_NS_OFFLOAD_CMD, + WMI_TAG_ARP_OFFLOAD_TUPLE, + WMI_TAG_NS_OFFLOAD_TUPLE, + WMI_TAG_FTM_INTG_CMD, + WMI_TAG_STA_KEEPALIVE_CMD, + WMI_TAG_STA_KEEPALVE_ARP_RESPONSE, + WMI_TAG_P2P_SET_VENDOR_IE_DATA_CMD, + WMI_TAG_AP_PS_PEER_CMD, + WMI_TAG_PEER_RATE_RETRY_SCHED_CMD, + WMI_TAG_WLAN_PROFILE_TRIGGER_CMD, + WMI_TAG_WLAN_PROFILE_SET_HIST_INTVL_CMD, + WMI_TAG_WLAN_PROFILE_GET_PROF_DATA_CMD, + WMI_TAG_WLAN_PROFILE_ENABLE_PROFILE_ID_CMD, + WMI_TAG_WOW_DEL_PATTERN_CMD, + WMI_TAG_WOW_ADD_DEL_EVT_CMD, + WMI_TAG_RTT_MEASREQ_HEAD, + WMI_TAG_RTT_MEASREQ_BODY, + WMI_TAG_RTT_TSF_CMD, + WMI_TAG_VDEV_SPECTRAL_CONFIGURE_CMD, + WMI_TAG_VDEV_SPECTRAL_ENABLE_CMD, + WMI_TAG_REQUEST_STATS_CMD, + WMI_TAG_NLO_CONFIG_CMD, + WMI_TAG_NLO_CONFIGURED_PARAMETERS, + WMI_TAG_CSA_OFFLOAD_ENABLE_CMD, + WMI_TAG_CSA_OFFLOAD_CHANSWITCH_CMD, + WMI_TAG_CHATTER_SET_MODE_CMD, + WMI_TAG_ECHO_CMD, + WMI_TAG_VDEV_SET_KEEPALIVE_CMD, + WMI_TAG_VDEV_GET_KEEPALIVE_CMD, + WMI_TAG_FORCE_FW_HANG_CMD, + WMI_TAG_GPIO_CONFIG_CMD, + WMI_TAG_GPIO_OUTPUT_CMD, + WMI_TAG_PEER_ADD_WDS_ENTRY_CMD, + WMI_TAG_PEER_REMOVE_WDS_ENTRY_CMD, + WMI_TAG_BCN_TX_HDR, + WMI_TAG_BCN_SEND_FROM_HOST_CMD, + WMI_TAG_MGMT_TX_HDR, + WMI_TAG_ADDBA_CLEAR_RESP_CMD, + WMI_TAG_ADDBA_SEND_CMD, + WMI_TAG_DELBA_SEND_CMD, + WMI_TAG_ADDBA_SETRESPONSE_CMD, + WMI_TAG_SEND_SINGLEAMSDU_CMD, + WMI_TAG_PDEV_PKTLOG_ENABLE_CMD, + WMI_TAG_PDEV_PKTLOG_DISABLE_CMD, + WMI_TAG_PDEV_SET_HT_IE_CMD, + WMI_TAG_PDEV_SET_VHT_IE_CMD, + WMI_TAG_PDEV_SET_DSCP_TID_MAP_CMD, + WMI_TAG_PDEV_GREEN_AP_PS_ENABLE_CMD, + WMI_TAG_PDEV_GET_TPC_CONFIG_CMD, + WMI_TAG_PDEV_SET_BASE_MACADDR_CMD, + WMI_TAG_PEER_MCAST_GROUP_CMD, + WMI_TAG_ROAM_AP_PROFILE, + WMI_TAG_AP_PROFILE, + WMI_TAG_SCAN_SCH_PRIORITY_TABLE_CMD, + WMI_TAG_PDEV_DFS_ENABLE_CMD, + WMI_TAG_PDEV_DFS_DISABLE_CMD, + WMI_TAG_WOW_ADD_PATTERN_CMD, + WMI_TAG_WOW_BITMAP_PATTERN_T, + WMI_TAG_WOW_IPV4_SYNC_PATTERN_T, + WMI_TAG_WOW_IPV6_SYNC_PATTERN_T, + WMI_TAG_WOW_MAGIC_PATTERN_CMD, + WMI_TAG_SCAN_UPDATE_REQUEST_CMD, + WMI_TAG_CHATTER_PKT_COALESCING_FILTER, + WMI_TAG_CHATTER_COALESCING_ADD_FILTER_CMD, + WMI_TAG_CHATTER_COALESCING_DELETE_FILTER_CMD, + WMI_TAG_CHATTER_COALESCING_QUERY_CMD, + WMI_TAG_TXBF_CMD, + WMI_TAG_DEBUG_LOG_CONFIG_CMD, + WMI_TAG_NLO_EVENT, + WMI_TAG_CHATTER_QUERY_REPLY_EVENT, + WMI_TAG_UPLOAD_H_HDR, + WMI_TAG_CAPTURE_H_EVENT_HDR, + WMI_TAG_VDEV_WNM_SLEEPMODE_CMD, + WMI_TAG_VDEV_IPSEC_NATKEEPALIVE_FILTER_CMD, + WMI_TAG_VDEV_WMM_ADDTS_CMD, + WMI_TAG_VDEV_WMM_DELTS_CMD, + WMI_TAG_VDEV_SET_WMM_PARAMS_CMD, + WMI_TAG_TDLS_SET_STATE_CMD, + WMI_TAG_TDLS_PEER_UPDATE_CMD, + WMI_TAG_TDLS_PEER_EVENT, + WMI_TAG_TDLS_PEER_CAPABILITIES, + WMI_TAG_VDEV_MCC_SET_TBTT_MODE_CMD, + WMI_TAG_ROAM_CHAN_LIST, + WMI_TAG_VDEV_MCC_BCN_INTVL_CHANGE_EVENT, + WMI_TAG_RESMGR_ADAPTIVE_OCS_ENABLE_DISABLE_CMD, + WMI_TAG_RESMGR_SET_CHAN_TIME_QUOTA_CMD, + WMI_TAG_RESMGR_SET_CHAN_LATENCY_CMD, + WMI_TAG_BA_REQ_SSN_CMD, + WMI_TAG_BA_RSP_SSN_EVENT, + WMI_TAG_STA_SMPS_FORCE_MODE_CMD, + WMI_TAG_SET_MCASTBCAST_FILTER_CMD, + WMI_TAG_P2P_SET_OPPPS_CMD, + WMI_TAG_P2P_SET_NOA_CMD, + WMI_TAG_BA_REQ_SSN_CMD_SUB_STRUCT_PARAM, + WMI_TAG_BA_REQ_SSN_EVENT_SUB_STRUCT_PARAM, + WMI_TAG_STA_SMPS_PARAM_CMD, + WMI_TAG_VDEV_SET_GTX_PARAMS_CMD, + WMI_TAG_MCC_SCHED_TRAFFIC_STATS_CMD, + WMI_TAG_MCC_SCHED_STA_TRAFFIC_STATS, + WMI_TAG_OFFLOAD_BCN_TX_STATUS_EVENT, + WMI_TAG_P2P_NOA_EVENT, + WMI_TAG_HB_SET_ENABLE_CMD, + WMI_TAG_HB_SET_TCP_PARAMS_CMD, + WMI_TAG_HB_SET_TCP_PKT_FILTER_CMD, + WMI_TAG_HB_SET_UDP_PARAMS_CMD, + WMI_TAG_HB_SET_UDP_PKT_FILTER_CMD, + WMI_TAG_HB_IND_EVENT, + WMI_TAG_TX_PAUSE_EVENT, + WMI_TAG_RFKILL_EVENT, + WMI_TAG_DFS_RADAR_EVENT, + WMI_TAG_DFS_PHYERR_FILTER_ENA_CMD, + WMI_TAG_DFS_PHYERR_FILTER_DIS_CMD, + WMI_TAG_BATCH_SCAN_RESULT_SCAN_LIST, + WMI_TAG_BATCH_SCAN_RESULT_NETWORK_INFO, + WMI_TAG_BATCH_SCAN_ENABLE_CMD, + WMI_TAG_BATCH_SCAN_DISABLE_CMD, + WMI_TAG_BATCH_SCAN_TRIGGER_RESULT_CMD, + WMI_TAG_BATCH_SCAN_ENABLED_EVENT, + WMI_TAG_BATCH_SCAN_RESULT_EVENT, + WMI_TAG_VDEV_PLMREQ_START_CMD, + WMI_TAG_VDEV_PLMREQ_STOP_CMD, + WMI_TAG_THERMAL_MGMT_CMD, + WMI_TAG_THERMAL_MGMT_EVENT, + WMI_TAG_PEER_INFO_REQ_CMD, + WMI_TAG_PEER_INFO_EVENT, + WMI_TAG_PEER_INFO, + WMI_TAG_PEER_TX_FAIL_CNT_THR_EVENT, + WMI_TAG_RMC_SET_MODE_CMD, + WMI_TAG_RMC_SET_ACTION_PERIOD_CMD, + WMI_TAG_RMC_CONFIG_CMD, + WMI_TAG_MHF_OFFLOAD_SET_MODE_CMD, + WMI_TAG_MHF_OFFLOAD_PLUMB_ROUTING_TABLE_CMD, + WMI_TAG_ADD_PROACTIVE_ARP_RSP_PATTERN_CMD, + WMI_TAG_DEL_PROACTIVE_ARP_RSP_PATTERN_CMD, + WMI_TAG_NAN_CMD_PARAM, + WMI_TAG_NAN_EVENT_HDR, + WMI_TAG_PDEV_L1SS_TRACK_EVENT, + WMI_TAG_DIAG_DATA_CONTAINER_EVENT, + WMI_TAG_MODEM_POWER_STATE_CMD_PARAM, + WMI_TAG_PEER_GET_ESTIMATED_LINKSPEED_CMD, + WMI_TAG_PEER_ESTIMATED_LINKSPEED_EVENT, + WMI_TAG_AGGR_STATE_TRIG_EVENT, + WMI_TAG_MHF_OFFLOAD_ROUTING_TABLE_ENTRY, + WMI_TAG_ROAM_SCAN_CMD, + WMI_TAG_REQ_STATS_EXT_CMD, + WMI_TAG_STATS_EXT_EVENT, + WMI_TAG_OBSS_SCAN_ENABLE_CMD, + WMI_TAG_OBSS_SCAN_DISABLE_CMD, + WMI_TAG_OFFLOAD_PRB_RSP_TX_STATUS_EVENT, + WMI_TAG_PDEV_SET_LED_CONFIG_CMD, + WMI_TAG_HOST_AUTO_SHUTDOWN_CFG_CMD, + WMI_TAG_HOST_AUTO_SHUTDOWN_EVENT, + WMI_TAG_UPDATE_WHAL_MIB_STATS_EVENT, + WMI_TAG_CHAN_AVOID_UPDATE_CMD_PARAM, + WMI_TAG_WOW_IOAC_PKT_PATTERN_T, + WMI_TAG_WOW_IOAC_TMR_PATTERN_T, + WMI_TAG_WOW_IOAC_ADD_KEEPALIVE_CMD, + WMI_TAG_WOW_IOAC_DEL_KEEPALIVE_CMD, + WMI_TAG_WOW_IOAC_KEEPALIVE_T, + WMI_TAG_WOW_IOAC_ADD_PATTERN_CMD, + WMI_TAG_WOW_IOAC_DEL_PATTERN_CMD, + WMI_TAG_START_LINK_STATS_CMD, + WMI_TAG_CLEAR_LINK_STATS_CMD, + WMI_TAG_REQUEST_LINK_STATS_CMD, + WMI_TAG_IFACE_LINK_STATS_EVENT, + WMI_TAG_RADIO_LINK_STATS_EVENT, + WMI_TAG_PEER_STATS_EVENT, + WMI_TAG_CHANNEL_STATS, + WMI_TAG_RADIO_LINK_STATS, + WMI_TAG_RATE_STATS, + WMI_TAG_PEER_LINK_STATS, + WMI_TAG_WMM_AC_STATS, + WMI_TAG_IFACE_LINK_STATS, + WMI_TAG_LPI_MGMT_SNOOPING_CONFIG_CMD, + WMI_TAG_LPI_START_SCAN_CMD, + WMI_TAG_LPI_STOP_SCAN_CMD, + WMI_TAG_LPI_RESULT_EVENT, + WMI_TAG_PEER_STATE_EVENT, + WMI_TAG_EXTSCAN_BUCKET_CMD, + WMI_TAG_EXTSCAN_BUCKET_CHANNEL_EVENT, + WMI_TAG_EXTSCAN_START_CMD, + WMI_TAG_EXTSCAN_STOP_CMD, + WMI_TAG_EXTSCAN_CONFIGURE_WLAN_CHANGE_MONITOR_CMD, + WMI_TAG_EXTSCAN_WLAN_CHANGE_BSSID_PARAM_CMD, + WMI_TAG_EXTSCAN_CONFIGURE_HOTLIST_MONITOR_CMD, + WMI_TAG_EXTSCAN_GET_CACHED_RESULTS_CMD, + WMI_TAG_EXTSCAN_GET_WLAN_CHANGE_RESULTS_CMD, + WMI_TAG_EXTSCAN_SET_CAPABILITIES_CMD, + WMI_TAG_EXTSCAN_GET_CAPABILITIES_CMD, + WMI_TAG_EXTSCAN_OPERATION_EVENT, + WMI_TAG_EXTSCAN_START_STOP_EVENT, + WMI_TAG_EXTSCAN_TABLE_USAGE_EVENT, + WMI_TAG_EXTSCAN_WLAN_DESCRIPTOR_EVENT, + WMI_TAG_EXTSCAN_RSSI_INFO_EVENT, + WMI_TAG_EXTSCAN_CACHED_RESULTS_EVENT, + WMI_TAG_EXTSCAN_WLAN_CHANGE_RESULTS_EVENT, + WMI_TAG_EXTSCAN_WLAN_CHANGE_RESULT_BSSID_EVENT, + WMI_TAG_EXTSCAN_HOTLIST_MATCH_EVENT, + WMI_TAG_EXTSCAN_CAPABILITIES_EVENT, + WMI_TAG_EXTSCAN_CACHE_CAPABILITIES_EVENT, + WMI_TAG_EXTSCAN_WLAN_CHANGE_MONITOR_CAPABILITIES_EVENT, + WMI_TAG_EXTSCAN_HOTLIST_MONITOR_CAPABILITIES_EVENT, + WMI_TAG_D0_WOW_ENABLE_DISABLE_CMD, + WMI_TAG_D0_WOW_DISABLE_ACK_EVENT, + WMI_TAG_UNIT_TEST_CMD, + WMI_TAG_ROAM_OFFLOAD_TLV_PARAM, + WMI_TAG_ROAM_11I_OFFLOAD_TLV_PARAM, + WMI_TAG_ROAM_11R_OFFLOAD_TLV_PARAM, + WMI_TAG_ROAM_ESE_OFFLOAD_TLV_PARAM, + WMI_TAG_ROAM_SYNCH_EVENT, + WMI_TAG_ROAM_SYNCH_COMPLETE, + WMI_TAG_EXTWOW_ENABLE_CMD, + WMI_TAG_EXTWOW_SET_APP_TYPE1_PARAMS_CMD, + WMI_TAG_EXTWOW_SET_APP_TYPE2_PARAMS_CMD, + WMI_TAG_LPI_STATUS_EVENT, + WMI_TAG_LPI_HANDOFF_EVENT, + WMI_TAG_VDEV_RATE_STATS_EVENT, + WMI_TAG_VDEV_RATE_HT_INFO, + WMI_TAG_RIC_REQUEST, + WMI_TAG_PDEV_GET_TEMPERATURE_CMD, + WMI_TAG_PDEV_TEMPERATURE_EVENT, + WMI_TAG_SET_DHCP_SERVER_OFFLOAD_CMD, + WMI_TAG_TPC_CHAINMASK_CONFIG_CMD, + WMI_TAG_RIC_TSPEC, + WMI_TAG_TPC_CHAINMASK_CONFIG, + WMI_TAG_IPA_OFFLOAD_ENABLE_DISABLE_CMD, + WMI_TAG_SCAN_PROB_REQ_OUI_CMD, + WMI_TAG_KEY_MATERIAL, + WMI_TAG_TDLS_SET_OFFCHAN_MODE_CMD, + WMI_TAG_SET_LED_FLASHING_CMD, + WMI_TAG_MDNS_OFFLOAD_CMD, + WMI_TAG_MDNS_SET_FQDN_CMD, + WMI_TAG_MDNS_SET_RESP_CMD, + WMI_TAG_MDNS_GET_STATS_CMD, + WMI_TAG_MDNS_STATS_EVENT, + WMI_TAG_ROAM_INVOKE_CMD, + WMI_TAG_PDEV_RESUME_EVENT, + WMI_TAG_PDEV_SET_ANTENNA_DIVERSITY_CMD, + WMI_TAG_SAP_OFL_ENABLE_CMD, + WMI_TAG_SAP_OFL_ADD_STA_EVENT, + WMI_TAG_SAP_OFL_DEL_STA_EVENT, + WMI_TAG_APFIND_CMD_PARAM, + WMI_TAG_APFIND_EVENT_HDR, + WMI_TAG_OCB_SET_SCHED_CMD, + WMI_TAG_OCB_SET_SCHED_EVENT, + WMI_TAG_OCB_SET_CONFIG_CMD, + WMI_TAG_OCB_SET_CONFIG_RESP_EVENT, + WMI_TAG_OCB_SET_UTC_TIME_CMD, + WMI_TAG_OCB_START_TIMING_ADVERT_CMD, + WMI_TAG_OCB_STOP_TIMING_ADVERT_CMD, + WMI_TAG_OCB_GET_TSF_TIMER_CMD, + WMI_TAG_OCB_GET_TSF_TIMER_RESP_EVENT, + WMI_TAG_DCC_GET_STATS_CMD, + WMI_TAG_DCC_CHANNEL_STATS_REQUEST, + WMI_TAG_DCC_GET_STATS_RESP_EVENT, + WMI_TAG_DCC_CLEAR_STATS_CMD, + WMI_TAG_DCC_UPDATE_NDL_CMD, + WMI_TAG_DCC_UPDATE_NDL_RESP_EVENT, + WMI_TAG_DCC_STATS_EVENT, + WMI_TAG_OCB_CHANNEL, + WMI_TAG_OCB_SCHEDULE_ELEMENT, + WMI_TAG_DCC_NDL_STATS_PER_CHANNEL, + WMI_TAG_DCC_NDL_CHAN, + WMI_TAG_QOS_PARAMETER, + WMI_TAG_DCC_NDL_ACTIVE_STATE_CONFIG, + WMI_TAG_ROAM_SCAN_EXTENDED_THRESHOLD_PARAM, + WMI_TAG_ROAM_FILTER, + WMI_TAG_PASSPOINT_CONFIG_CMD, + WMI_TAG_PASSPOINT_EVENT_HDR, + WMI_TAG_EXTSCAN_CONFIGURE_HOTLIST_SSID_MONITOR_CMD, + WMI_TAG_EXTSCAN_HOTLIST_SSID_MATCH_EVENT, + WMI_TAG_VDEV_TSF_TSTAMP_ACTION_CMD, + WMI_TAG_VDEV_TSF_REPORT_EVENT, + WMI_TAG_GET_FW_MEM_DUMP, + WMI_TAG_UPDATE_FW_MEM_DUMP, + WMI_TAG_FW_MEM_DUMP_PARAMS, + WMI_TAG_DEBUG_MESG_FLUSH, + WMI_TAG_DEBUG_MESG_FLUSH_COMPLETE, + WMI_TAG_PEER_SET_RATE_REPORT_CONDITION, + WMI_TAG_ROAM_SUBNET_CHANGE_CONFIG, + WMI_TAG_VDEV_SET_IE_CMD, + WMI_TAG_RSSI_BREACH_MONITOR_CONFIG, + WMI_TAG_RSSI_BREACH_EVENT, + WMI_TAG_WOW_EVENT_INITIAL_WAKEUP, + WMI_TAG_SOC_SET_PCL_CMD, + WMI_TAG_SOC_SET_HW_MODE_CMD, + WMI_TAG_SOC_SET_HW_MODE_RESPONSE_EVENT, + WMI_TAG_SOC_HW_MODE_TRANSITION_EVENT, + WMI_TAG_VDEV_TXRX_STREAMS, + WMI_TAG_SOC_SET_HW_MODE_RESPONSE_VDEV_MAC_ENTRY, + WMI_TAG_SOC_SET_DUAL_MAC_CONFIG_CMD, + WMI_TAG_SOC_SET_DUAL_MAC_CONFIG_RESPONSE_EVENT, + WMI_TAG_WOW_IOAC_SOCK_PATTERN_T, + WMI_TAG_WOW_ENABLE_ICMPV6_NA_FLT_CMD, + WMI_TAG_DIAG_EVENT_LOG_CONFIG, + WMI_TAG_DIAG_EVENT_LOG_SUPPORTED_EVENT_FIXED_PARAMS, + WMI_TAG_PACKET_FILTER_CONFIG, + WMI_TAG_PACKET_FILTER_ENABLE, + WMI_TAG_SAP_SET_BLACKLIST_PARAM_CMD, + WMI_TAG_MGMT_TX_SEND_CMD, + WMI_TAG_MGMT_TX_COMPL_EVENT, + WMI_TAG_SOC_SET_ANTENNA_MODE_CMD, + WMI_TAG_WOW_UDP_SVC_OFLD_CMD, + WMI_TAG_LRO_INFO_CMD, + WMI_TAG_ROAM_EARLYSTOP_RSSI_THRES_PARAM, + WMI_TAG_SERVICE_READY_EXT_EVENT, + WMI_TAG_MAWC_SENSOR_REPORT_IND_CMD, + WMI_TAG_MAWC_ENABLE_SENSOR_EVENT, + WMI_TAG_ROAM_CONFIGURE_MAWC_CMD, + WMI_TAG_NLO_CONFIGURE_MAWC_CMD, + WMI_TAG_EXTSCAN_CONFIGURE_MAWC_CMD, + WMI_TAG_PEER_ASSOC_CONF_EVENT, + WMI_TAG_WOW_HOSTWAKEUP_GPIO_PIN_PATTERN_CONFIG_CMD, + WMI_TAG_AP_PS_EGAP_PARAM_CMD, + WMI_TAG_AP_PS_EGAP_INFO_EVENT, + WMI_TAG_PMF_OFFLOAD_SET_SA_QUERY_CMD, + WMI_TAG_TRANSFER_DATA_TO_FLASH_CMD, + WMI_TAG_TRANSFER_DATA_TO_FLASH_COMPLETE_EVENT, + WMI_TAG_SCPC_EVENT, + WMI_TAG_AP_PS_EGAP_INFO_CHAINMASK_LIST, + WMI_TAG_STA_SMPS_FORCE_MODE_COMPLETE_EVENT, + WMI_TAG_BPF_GET_CAPABILITY_CMD, + WMI_TAG_BPF_CAPABILITY_INFO_EVT, + WMI_TAG_BPF_GET_VDEV_STATS_CMD, + WMI_TAG_BPF_VDEV_STATS_INFO_EVT, + WMI_TAG_BPF_SET_VDEV_INSTRUCTIONS_CMD, + WMI_TAG_BPF_DEL_VDEV_INSTRUCTIONS_CMD, + WMI_TAG_VDEV_DELETE_RESP_EVENT, + WMI_TAG_PEER_DELETE_RESP_EVENT, + WMI_TAG_ROAM_DENSE_THRES_PARAM, + WMI_TAG_ENLO_CANDIDATE_SCORE_PARAM, + WMI_TAG_PEER_UPDATE_WDS_ENTRY_CMD, + WMI_TAG_VDEV_CONFIG_RATEMASK, + WMI_TAG_PDEV_FIPS_CMD, + WMI_TAG_PDEV_SMART_ANT_ENABLE_CMD, + WMI_TAG_PDEV_SMART_ANT_SET_RX_ANTENNA_CMD, + WMI_TAG_PEER_SMART_ANT_SET_TX_ANTENNA_CMD, + WMI_TAG_PEER_SMART_ANT_SET_TRAIN_ANTENNA_CMD, + WMI_TAG_PEER_SMART_ANT_SET_NODE_CONFIG_OPS_CMD, + WMI_TAG_PDEV_SET_ANT_SWITCH_TBL_CMD, + WMI_TAG_PDEV_SET_CTL_TABLE_CMD, + WMI_TAG_PDEV_SET_MIMOGAIN_TABLE_CMD, + WMI_TAG_FWTEST_SET_PARAM_CMD, + WMI_TAG_PEER_ATF_REQUEST, + WMI_TAG_VDEV_ATF_REQUEST, + WMI_TAG_PDEV_GET_ANI_CCK_CONFIG_CMD, + WMI_TAG_PDEV_GET_ANI_OFDM_CONFIG_CMD, + WMI_TAG_INST_RSSI_STATS_RESP, + WMI_TAG_MED_UTIL_REPORT_EVENT, + WMI_TAG_PEER_STA_PS_STATECHANGE_EVENT, + WMI_TAG_WDS_ADDR_EVENT, + WMI_TAG_PEER_RATECODE_LIST_EVENT, + WMI_TAG_PDEV_NFCAL_POWER_ALL_CHANNELS_EVENT, + WMI_TAG_PDEV_TPC_EVENT, + WMI_TAG_ANI_OFDM_EVENT, + WMI_TAG_ANI_CCK_EVENT, + WMI_TAG_PDEV_CHANNEL_HOPPING_EVENT, + WMI_TAG_PDEV_FIPS_EVENT, + WMI_TAG_ATF_PEER_INFO, + WMI_TAG_PDEV_GET_TPC_CMD, + WMI_TAG_VDEV_FILTER_NRP_CONFIG_CMD, + WMI_TAG_QBOOST_CFG_CMD, + WMI_TAG_PDEV_SMART_ANT_GPIO_HANDLE, + WMI_TAG_PEER_SMART_ANT_SET_TX_ANTENNA_SERIES, + WMI_TAG_PEER_SMART_ANT_SET_TRAIN_ANTENNA_PARAM, + WMI_TAG_PDEV_SET_ANT_CTRL_CHAIN, + WMI_TAG_PEER_CCK_OFDM_RATE_INFO, + WMI_TAG_PEER_MCS_RATE_INFO, + WMI_TAG_PDEV_NFCAL_POWER_ALL_CHANNELS_NFDBR, + WMI_TAG_PDEV_NFCAL_POWER_ALL_CHANNELS_NFDBM, + WMI_TAG_PDEV_NFCAL_POWER_ALL_CHANNELS_FREQNUM, + WMI_TAG_MU_REPORT_TOTAL_MU, + WMI_TAG_VDEV_SET_DSCP_TID_MAP_CMD, + WMI_TAG_ROAM_SET_MBO, + WMI_TAG_MIB_STATS_ENABLE_CMD, + WMI_TAG_NAN_DISC_IFACE_CREATED_EVENT, + WMI_TAG_NAN_DISC_IFACE_DELETED_EVENT, + WMI_TAG_NAN_STARTED_CLUSTER_EVENT, + WMI_TAG_NAN_JOINED_CLUSTER_EVENT, + WMI_TAG_NDI_GET_CAP_REQ, + WMI_TAG_NDP_INITIATOR_REQ, + WMI_TAG_NDP_RESPONDER_REQ, + WMI_TAG_NDP_END_REQ, + WMI_TAG_NDI_CAP_RSP_EVENT, + WMI_TAG_NDP_INITIATOR_RSP_EVENT, + WMI_TAG_NDP_RESPONDER_RSP_EVENT, + WMI_TAG_NDP_END_RSP_EVENT, + WMI_TAG_NDP_INDICATION_EVENT, + WMI_TAG_NDP_CONFIRM_EVENT, + WMI_TAG_NDP_END_INDICATION_EVENT, + WMI_TAG_VDEV_SET_QUIET_CMD, + WMI_TAG_PDEV_SET_PCL_CMD, + WMI_TAG_PDEV_SET_HW_MODE_CMD, + WMI_TAG_PDEV_SET_MAC_CONFIG_CMD, + WMI_TAG_PDEV_SET_ANTENNA_MODE_CMD, + WMI_TAG_PDEV_SET_HW_MODE_RESPONSE_EVENT, + WMI_TAG_PDEV_HW_MODE_TRANSITION_EVENT, + WMI_TAG_PDEV_SET_HW_MODE_RESPONSE_VDEV_MAC_ENTRY, + WMI_TAG_PDEV_SET_MAC_CONFIG_RESPONSE_EVENT, + WMI_TAG_COEX_CONFIG_CMD, + WMI_TAG_CONFIG_ENHANCED_MCAST_FILTER, + WMI_TAG_CHAN_AVOID_RPT_ALLOW_CMD, + WMI_TAG_SET_PERIODIC_CHANNEL_STATS_CONFIG, + WMI_TAG_VDEV_SET_CUSTOM_AGGR_SIZE_CMD, + WMI_TAG_PDEV_WAL_POWER_DEBUG_CMD, + WMI_TAG_MAC_PHY_CAPABILITIES, + WMI_TAG_HW_MODE_CAPABILITIES, + WMI_TAG_SOC_MAC_PHY_HW_MODE_CAPS, + WMI_TAG_HAL_REG_CAPABILITIES_EXT, + WMI_TAG_SOC_HAL_REG_CAPABILITIES, + WMI_TAG_VDEV_WISA_CMD, + WMI_TAG_TX_POWER_LEVEL_STATS_EVT, + WMI_TAG_SCAN_ADAPTIVE_DWELL_PARAMETERS_TLV, + WMI_TAG_SCAN_ADAPTIVE_DWELL_CONFIG, + WMI_TAG_WOW_SET_ACTION_WAKE_UP_CMD, + WMI_TAG_NDP_END_RSP_PER_NDI, + WMI_TAG_PEER_BWF_REQUEST, + WMI_TAG_BWF_PEER_INFO, + WMI_TAG_DBGLOG_TIME_STAMP_SYNC_CMD, + WMI_TAG_RMC_SET_LEADER_CMD, + WMI_TAG_RMC_MANUAL_LEADER_EVENT, + WMI_TAG_PER_CHAIN_RSSI_STATS, + WMI_TAG_RSSI_STATS, + WMI_TAG_P2P_LO_START_CMD, + WMI_TAG_P2P_LO_STOP_CMD, + WMI_TAG_P2P_LO_STOPPED_EVENT, + WMI_TAG_REORDER_QUEUE_SETUP_CMD, + WMI_TAG_REORDER_QUEUE_REMOVE_CMD, + WMI_TAG_SET_MULTIPLE_MCAST_FILTER_CMD, + WMI_TAG_MGMT_TX_COMPL_BUNDLE_EVENT, + WMI_TAG_READ_DATA_FROM_FLASH_CMD, + WMI_TAG_READ_DATA_FROM_FLASH_EVENT, + WMI_TAG_PDEV_SET_REORDER_TIMEOUT_VAL_CMD, + WMI_TAG_PEER_SET_RX_BLOCKSIZE_CMD, + WMI_TAG_PDEV_SET_WAKEUP_CONFIG_CMDID, + WMI_TAG_TLV_BUF_LEN_PARAM, + WMI_TAG_SERVICE_AVAILABLE_EVENT, + WMI_TAG_PEER_ANTDIV_INFO_REQ_CMD, + WMI_TAG_PEER_ANTDIV_INFO_EVENT, + WMI_TAG_PEER_ANTDIV_INFO, + WMI_TAG_PDEV_GET_ANTDIV_STATUS_CMD, + WMI_TAG_PDEV_ANTDIV_STATUS_EVENT, + WMI_TAG_MNT_FILTER_CMD, + WMI_TAG_GET_CHIP_POWER_STATS_CMD, + WMI_TAG_PDEV_CHIP_POWER_STATS_EVENT, + WMI_TAG_COEX_GET_ANTENNA_ISOLATION_CMD, + WMI_TAG_COEX_REPORT_ISOLATION_EVENT, + WMI_TAG_CHAN_CCA_STATS, + WMI_TAG_PEER_SIGNAL_STATS, + WMI_TAG_TX_STATS, + WMI_TAG_PEER_AC_TX_STATS, + WMI_TAG_RX_STATS, + WMI_TAG_PEER_AC_RX_STATS, + WMI_TAG_REPORT_STATS_EVENT, + WMI_TAG_CHAN_CCA_STATS_THRESH, + WMI_TAG_PEER_SIGNAL_STATS_THRESH, + WMI_TAG_TX_STATS_THRESH, + WMI_TAG_RX_STATS_THRESH, + WMI_TAG_PDEV_SET_STATS_THRESHOLD_CMD, + WMI_TAG_REQUEST_WLAN_STATS_CMD, + WMI_TAG_RX_AGGR_FAILURE_EVENT, + WMI_TAG_RX_AGGR_FAILURE_INFO, + WMI_TAG_VDEV_ENCRYPT_DECRYPT_DATA_REQ_CMD, + WMI_TAG_VDEV_ENCRYPT_DECRYPT_DATA_RESP_EVENT, + WMI_TAG_PDEV_BAND_TO_MAC, + WMI_TAG_TBTT_OFFSET_INFO, + WMI_TAG_TBTT_OFFSET_EXT_EVENT, + WMI_TAG_SAR_LIMITS_CMD, + WMI_TAG_SAR_LIMIT_CMD_ROW, + WMI_TAG_PDEV_DFS_PHYERR_OFFLOAD_ENABLE_CMD, + WMI_TAG_PDEV_DFS_PHYERR_OFFLOAD_DISABLE_CMD, + WMI_TAG_VDEV_ADFS_CH_CFG_CMD, + WMI_TAG_VDEV_ADFS_OCAC_ABORT_CMD, + WMI_TAG_PDEV_DFS_RADAR_DETECTION_EVENT, + WMI_TAG_VDEV_ADFS_OCAC_COMPLETE_EVENT, + WMI_TAG_VDEV_DFS_CAC_COMPLETE_EVENT, + WMI_TAG_VENDOR_OUI, + WMI_TAG_REQUEST_RCPI_CMD, + WMI_TAG_UPDATE_RCPI_EVENT, + WMI_TAG_REQUEST_PEER_STATS_INFO_CMD, + WMI_TAG_PEER_STATS_INFO, + WMI_TAG_PEER_STATS_INFO_EVENT, + WMI_TAG_PKGID_EVENT, + WMI_TAG_CONNECTED_NLO_RSSI_PARAMS, + WMI_TAG_SET_CURRENT_COUNTRY_CMD, + WMI_TAG_REGULATORY_RULE_STRUCT, + WMI_TAG_REG_CHAN_LIST_CC_EVENT, + WMI_TAG_11D_SCAN_START_CMD, + WMI_TAG_11D_SCAN_STOP_CMD, + WMI_TAG_11D_NEW_COUNTRY_EVENT, + WMI_TAG_REQUEST_RADIO_CHAN_STATS_CMD, + WMI_TAG_RADIO_CHAN_STATS, + WMI_TAG_RADIO_CHAN_STATS_EVENT, + WMI_TAG_ROAM_PER_CONFIG, + WMI_TAG_VDEV_ADD_MAC_ADDR_TO_RX_FILTER_CMD, + WMI_TAG_VDEV_ADD_MAC_ADDR_TO_RX_FILTER_STATUS_EVENT, + WMI_TAG_BPF_SET_VDEV_ACTIVE_MODE_CMD, + WMI_TAG_HW_DATA_FILTER_CMD, + WMI_TAG_CONNECTED_NLO_BSS_BAND_RSSI_PREF, + WMI_TAG_PEER_OPER_MODE_CHANGE_EVENT, + WMI_TAG_CHIP_POWER_SAVE_FAILURE_DETECTED, + WMI_TAG_PDEV_MULTIPLE_VDEV_RESTART_REQUEST_CMD, + WMI_TAG_PDEV_CSA_SWITCH_COUNT_STATUS_EVENT, + WMI_TAG_PDEV_UPDATE_PKT_ROUTING_CMD, + WMI_TAG_PDEV_CHECK_CAL_VERSION_CMD, + WMI_TAG_PDEV_CHECK_CAL_VERSION_EVENT, + WMI_TAG_PDEV_SET_DIVERSITY_GAIN_CMD, + WMI_TAG_MAC_PHY_CHAINMASK_COMBO, + WMI_TAG_MAC_PHY_CHAINMASK_CAPABILITY, + WMI_TAG_VDEV_SET_ARP_STATS_CMD, + WMI_TAG_VDEV_GET_ARP_STATS_CMD, + WMI_TAG_VDEV_GET_ARP_STATS_EVENT, + WMI_TAG_IFACE_OFFLOAD_STATS, + WMI_TAG_REQUEST_STATS_CMD_SUB_STRUCT_PARAM, + WMI_TAG_RSSI_CTL_EXT, + WMI_TAG_SINGLE_PHYERR_EXT_RX_HDR, + WMI_TAG_COEX_BT_ACTIVITY_EVENT, + WMI_TAG_VDEV_GET_TX_POWER_CMD, + WMI_TAG_VDEV_TX_POWER_EVENT, + WMI_TAG_OFFCHAN_DATA_TX_COMPL_EVENT, + WMI_TAG_OFFCHAN_DATA_TX_SEND_CMD, + WMI_TAG_TX_SEND_PARAMS, + WMI_TAG_HE_RATE_SET, + WMI_TAG_CONGESTION_STATS, + WMI_TAG_SET_INIT_COUNTRY_CMD, + WMI_TAG_SCAN_DBS_DUTY_CYCLE, + WMI_TAG_SCAN_DBS_DUTY_CYCLE_PARAM_TLV, + WMI_TAG_PDEV_DIV_GET_RSSI_ANTID, + WMI_TAG_THERM_THROT_CONFIG_REQUEST, + WMI_TAG_THERM_THROT_LEVEL_CONFIG_INFO, + WMI_TAG_THERM_THROT_STATS_EVENT, + WMI_TAG_THERM_THROT_LEVEL_STATS_INFO, + WMI_TAG_PDEV_DIV_RSSI_ANTID_EVENT, + WMI_TAG_OEM_DMA_RING_CAPABILITIES, + WMI_TAG_OEM_DMA_RING_CFG_REQ, + WMI_TAG_OEM_DMA_RING_CFG_RSP, + WMI_TAG_OEM_INDIRECT_DATA, + WMI_TAG_OEM_DMA_BUF_RELEASE, + WMI_TAG_OEM_DMA_BUF_RELEASE_ENTRY, + WMI_TAG_PDEV_BSS_CHAN_INFO_REQUEST, + WMI_TAG_PDEV_BSS_CHAN_INFO_EVENT, + WMI_TAG_ROAM_LCA_DISALLOW_CONFIG, + WMI_TAG_VDEV_LIMIT_OFFCHAN_CMD, + WMI_TAG_ROAM_RSSI_REJECTION_OCE_CONFIG, + WMI_TAG_UNIT_TEST_EVENT, + WMI_TAG_ROAM_FILS_OFFLOAD, + WMI_TAG_PDEV_UPDATE_PMK_CACHE_CMD, + WMI_TAG_PMK_CACHE, + WMI_TAG_PDEV_UPDATE_FILS_HLP_PKT_CMD, + WMI_TAG_ROAM_FILS_SYNCH, + WMI_TAG_GTK_OFFLOAD_EXTENDED, + WMI_TAG_ROAM_BG_SCAN_ROAMING, + WMI_TAG_OIC_PING_OFFLOAD_PARAMS_CMD, + WMI_TAG_OIC_PING_OFFLOAD_SET_ENABLE_CMD, + WMI_TAG_OIC_PING_HANDOFF_EVENT, + WMI_TAG_DHCP_LEASE_RENEW_OFFLOAD_CMD, + WMI_TAG_DHCP_LEASE_RENEW_EVENT, + WMI_TAG_BTM_CONFIG, + WMI_TAG_DEBUG_MESG_FW_DATA_STALL, + WMI_TAG_WLM_CONFIG_CMD, + WMI_TAG_PDEV_UPDATE_CTLTABLE_REQUEST, + WMI_TAG_PDEV_UPDATE_CTLTABLE_EVENT, + WMI_TAG_ROAM_CND_SCORING_PARAM, + WMI_TAG_PDEV_CONFIG_VENDOR_OUI_ACTION, + WMI_TAG_VENDOR_OUI_EXT, + WMI_TAG_ROAM_SYNCH_FRAME_EVENT, + WMI_TAG_FD_SEND_FROM_HOST_CMD, + WMI_TAG_ENABLE_FILS_CMD, + WMI_TAG_HOST_SWFDA_EVENT, + WMI_TAG_BCN_OFFLOAD_CTRL_CMD, + WMI_TAG_PDEV_SET_AC_TX_QUEUE_OPTIMIZED_CMD, + WMI_TAG_STATS_PERIOD, + WMI_TAG_NDL_SCHEDULE_UPDATE, + WMI_TAG_PEER_TID_MSDUQ_QDEPTH_THRESH_UPDATE_CMD, + WMI_TAG_MSDUQ_QDEPTH_THRESH_UPDATE, + WMI_TAG_PDEV_SET_RX_FILTER_PROMISCUOUS_CMD, + WMI_TAG_SAR2_RESULT_EVENT, + WMI_TAG_SAR_CAPABILITIES, + WMI_TAG_SAP_OBSS_DETECTION_CFG_CMD, + WMI_TAG_SAP_OBSS_DETECTION_INFO_EVT, + WMI_TAG_DMA_RING_CAPABILITIES, + WMI_TAG_DMA_RING_CFG_REQ, + WMI_TAG_DMA_RING_CFG_RSP, + WMI_TAG_DMA_BUF_RELEASE, + WMI_TAG_DMA_BUF_RELEASE_ENTRY, + WMI_TAG_SAR_GET_LIMITS_CMD, + WMI_TAG_SAR_GET_LIMITS_EVENT, + WMI_TAG_SAR_GET_LIMITS_EVENT_ROW, + WMI_TAG_OFFLOAD_11K_REPORT, + WMI_TAG_INVOKE_NEIGHBOR_REPORT, + WMI_TAG_NEIGHBOR_REPORT_OFFLOAD, + WMI_TAG_VDEV_SET_CONNECTIVITY_CHECK_STATS, + WMI_TAG_VDEV_GET_CONNECTIVITY_CHECK_STATS, + WMI_TAG_BPF_SET_VDEV_ENABLE_CMD, + WMI_TAG_BPF_SET_VDEV_WORK_MEMORY_CMD, + WMI_TAG_BPF_GET_VDEV_WORK_MEMORY_CMD, + WMI_TAG_BPF_GET_VDEV_WORK_MEMORY_RESP_EVT, + WMI_TAG_PDEV_GET_NFCAL_POWER, + WMI_TAG_BSS_COLOR_CHANGE_ENABLE, + WMI_TAG_OBSS_COLOR_COLLISION_DET_CONFIG, + WMI_TAG_OBSS_COLOR_COLLISION_EVT, + WMI_TAG_RUNTIME_DPD_RECAL_CMD, + WMI_TAG_TWT_ENABLE_CMD, + WMI_TAG_TWT_DISABLE_CMD, + WMI_TAG_TWT_ADD_DIALOG_CMD, + WMI_TAG_TWT_DEL_DIALOG_CMD, + WMI_TAG_TWT_PAUSE_DIALOG_CMD, + WMI_TAG_TWT_RESUME_DIALOG_CMD, + WMI_TAG_TWT_ENABLE_COMPLETE_EVENT, + WMI_TAG_TWT_DISABLE_COMPLETE_EVENT, + WMI_TAG_TWT_ADD_DIALOG_COMPLETE_EVENT, + WMI_TAG_TWT_DEL_DIALOG_COMPLETE_EVENT, + WMI_TAG_TWT_PAUSE_DIALOG_COMPLETE_EVENT, + WMI_TAG_TWT_RESUME_DIALOG_COMPLETE_EVENT, + WMI_TAG_REQUEST_ROAM_SCAN_STATS_CMD, + WMI_TAG_ROAM_SCAN_STATS_EVENT, + WMI_TAG_PEER_TID_CONFIGURATIONS_CMD, + WMI_TAG_VDEV_SET_CUSTOM_SW_RETRY_TH_CMD, + WMI_TAG_GET_TPC_POWER_CMD, + WMI_TAG_GET_TPC_POWER_EVENT, + WMI_TAG_DMA_BUF_RELEASE_SPECTRAL_META_DATA, + WMI_TAG_MOTION_DET_CONFIG_PARAMS_CMD, + WMI_TAG_MOTION_DET_BASE_LINE_CONFIG_PARAMS_CMD, + WMI_TAG_MOTION_DET_START_STOP_CMD, + WMI_TAG_MOTION_DET_BASE_LINE_START_STOP_CMD, + WMI_TAG_MOTION_DET_EVENT, + WMI_TAG_MOTION_DET_BASE_LINE_EVENT, + WMI_TAG_NDP_TRANSPORT_IP, + WMI_TAG_OBSS_SPATIAL_REUSE_SET_CMD, + WMI_TAG_ESP_ESTIMATE_EVENT, + WMI_TAG_NAN_HOST_CONFIG, + WMI_TAG_SPECTRAL_BIN_SCALING_PARAMS, + WMI_TAG_PEER_CFR_CAPTURE_CMD, + WMI_TAG_PEER_CHAN_WIDTH_SWITCH_CMD, + WMI_TAG_CHAN_WIDTH_PEER_LIST, + WMI_TAG_OBSS_SPATIAL_REUSE_SET_DEF_OBSS_THRESH_CMD, + WMI_TAG_PDEV_HE_TB_ACTION_FRM_CMD, + WMI_TAG_PEER_EXTD2_STATS, + WMI_TAG_HPCS_PULSE_START_CMD, + WMI_TAG_PDEV_CTL_FAILSAFE_CHECK_EVENT, + WMI_TAG_VDEV_CHAINMASK_CONFIG_CMD, + WMI_TAG_VDEV_BCN_OFFLOAD_QUIET_CONFIG_CMD, + WMI_TAG_NAN_EVENT_INFO, + WMI_TAG_NDP_CHANNEL_INFO, + WMI_TAG_NDP_CMD, + WMI_TAG_NDP_EVENT, + /* TODO add all the missing cmds */ + WMI_TAG_PDEV_PEER_PKTLOG_FILTER_CMD = 0x301, + WMI_TAG_PDEV_PEER_PKTLOG_FILTER_INFO, + WMI_TAG_MAX +}; + +enum wmi_tlv_service { + WMI_TLV_SERVICE_BEACON_OFFLOAD = 0, + WMI_TLV_SERVICE_SCAN_OFFLOAD = 1, + WMI_TLV_SERVICE_ROAM_SCAN_OFFLOAD = 2, + WMI_TLV_SERVICE_BCN_MISS_OFFLOAD = 3, + WMI_TLV_SERVICE_STA_PWRSAVE = 4, + WMI_TLV_SERVICE_STA_ADVANCED_PWRSAVE = 5, + WMI_TLV_SERVICE_AP_UAPSD = 6, + WMI_TLV_SERVICE_AP_DFS = 7, + WMI_TLV_SERVICE_11AC = 8, + WMI_TLV_SERVICE_BLOCKACK = 9, + WMI_TLV_SERVICE_PHYERR = 10, + WMI_TLV_SERVICE_BCN_FILTER = 11, + WMI_TLV_SERVICE_RTT = 12, + WMI_TLV_SERVICE_WOW = 13, + WMI_TLV_SERVICE_RATECTRL_CACHE = 14, + WMI_TLV_SERVICE_IRAM_TIDS = 15, + WMI_TLV_SERVICE_ARPNS_OFFLOAD = 16, + WMI_TLV_SERVICE_NLO = 17, + WMI_TLV_SERVICE_GTK_OFFLOAD = 18, + WMI_TLV_SERVICE_SCAN_SCH = 19, + WMI_TLV_SERVICE_CSA_OFFLOAD = 20, + WMI_TLV_SERVICE_CHATTER = 21, + WMI_TLV_SERVICE_COEX_FREQAVOID = 22, + WMI_TLV_SERVICE_PACKET_POWER_SAVE = 23, + WMI_TLV_SERVICE_FORCE_FW_HANG = 24, + WMI_TLV_SERVICE_GPIO = 25, + WMI_TLV_SERVICE_STA_DTIM_PS_MODULATED_DTIM = 26, + WMI_STA_UAPSD_BASIC_AUTO_TRIG = 27, + WMI_STA_UAPSD_VAR_AUTO_TRIG = 28, + WMI_TLV_SERVICE_STA_KEEP_ALIVE = 29, + WMI_TLV_SERVICE_TX_ENCAP = 30, + WMI_TLV_SERVICE_AP_PS_DETECT_OUT_OF_SYNC = 31, + WMI_TLV_SERVICE_EARLY_RX = 32, + WMI_TLV_SERVICE_STA_SMPS = 33, + WMI_TLV_SERVICE_FWTEST = 34, + WMI_TLV_SERVICE_STA_WMMAC = 35, + WMI_TLV_SERVICE_TDLS = 36, + WMI_TLV_SERVICE_BURST = 37, + WMI_TLV_SERVICE_MCC_BCN_INTERVAL_CHANGE = 38, + WMI_TLV_SERVICE_ADAPTIVE_OCS = 39, + WMI_TLV_SERVICE_BA_SSN_SUPPORT = 40, + WMI_TLV_SERVICE_FILTER_IPSEC_NATKEEPALIVE = 41, + WMI_TLV_SERVICE_WLAN_HB = 42, + WMI_TLV_SERVICE_LTE_ANT_SHARE_SUPPORT = 43, + WMI_TLV_SERVICE_BATCH_SCAN = 44, + WMI_TLV_SERVICE_QPOWER = 45, + WMI_TLV_SERVICE_PLMREQ = 46, + WMI_TLV_SERVICE_THERMAL_MGMT = 47, + WMI_TLV_SERVICE_RMC = 48, + WMI_TLV_SERVICE_MHF_OFFLOAD = 49, + WMI_TLV_SERVICE_COEX_SAR = 50, + WMI_TLV_SERVICE_BCN_TXRATE_OVERRIDE = 51, + WMI_TLV_SERVICE_NAN = 52, + WMI_TLV_SERVICE_L1SS_STAT = 53, + WMI_TLV_SERVICE_ESTIMATE_LINKSPEED = 54, + WMI_TLV_SERVICE_OBSS_SCAN = 55, + WMI_TLV_SERVICE_TDLS_OFFCHAN = 56, + WMI_TLV_SERVICE_TDLS_UAPSD_BUFFER_STA = 57, + WMI_TLV_SERVICE_TDLS_UAPSD_SLEEP_STA = 58, + WMI_TLV_SERVICE_IBSS_PWRSAVE = 59, + WMI_TLV_SERVICE_LPASS = 60, + WMI_TLV_SERVICE_EXTSCAN = 61, + WMI_TLV_SERVICE_D0WOW = 62, + WMI_TLV_SERVICE_HSOFFLOAD = 63, + WMI_TLV_SERVICE_ROAM_HO_OFFLOAD = 64, + WMI_TLV_SERVICE_RX_FULL_REORDER = 65, + WMI_TLV_SERVICE_DHCP_OFFLOAD = 66, + WMI_TLV_SERVICE_STA_RX_IPA_OFFLOAD_SUPPORT = 67, + WMI_TLV_SERVICE_MDNS_OFFLOAD = 68, + WMI_TLV_SERVICE_SAP_AUTH_OFFLOAD = 69, + WMI_TLV_SERVICE_DUAL_BAND_SIMULTANEOUS_SUPPORT = 70, + WMI_TLV_SERVICE_OCB = 71, + WMI_TLV_SERVICE_AP_ARPNS_OFFLOAD = 72, + WMI_TLV_SERVICE_PER_BAND_CHAINMASK_SUPPORT = 73, + WMI_TLV_SERVICE_PACKET_FILTER_OFFLOAD = 74, + WMI_TLV_SERVICE_MGMT_TX_HTT = 75, + WMI_TLV_SERVICE_MGMT_TX_WMI = 76, + WMI_TLV_SERVICE_EXT_MSG = 77, + WMI_TLV_SERVICE_MAWC = 78, + WMI_TLV_SERVICE_PEER_ASSOC_CONF = 79, + WMI_TLV_SERVICE_EGAP = 80, + WMI_TLV_SERVICE_STA_PMF_OFFLOAD = 81, + WMI_TLV_SERVICE_UNIFIED_WOW_CAPABILITY = 82, + WMI_TLV_SERVICE_ENHANCED_PROXY_STA = 83, + WMI_TLV_SERVICE_ATF = 84, + WMI_TLV_SERVICE_COEX_GPIO = 85, + WMI_TLV_SERVICE_AUX_SPECTRAL_INTF = 86, + WMI_TLV_SERVICE_AUX_CHAN_LOAD_INTF = 87, + WMI_TLV_SERVICE_BSS_CHANNEL_INFO_64 = 88, + WMI_TLV_SERVICE_ENTERPRISE_MESH = 89, + WMI_TLV_SERVICE_RESTRT_CHNL_SUPPORT = 90, + WMI_TLV_SERVICE_BPF_OFFLOAD = 91, + WMI_TLV_SERVICE_SYNC_DELETE_CMDS = 92, + WMI_TLV_SERVICE_SMART_ANTENNA_SW_SUPPORT = 93, + WMI_TLV_SERVICE_SMART_ANTENNA_HW_SUPPORT = 94, + WMI_TLV_SERVICE_RATECTRL_LIMIT_MAX_MIN_RATES = 95, + WMI_TLV_SERVICE_NAN_DATA = 96, + WMI_TLV_SERVICE_NAN_RTT = 97, + WMI_TLV_SERVICE_11AX = 98, + WMI_TLV_SERVICE_DEPRECATED_REPLACE = 99, + WMI_TLV_SERVICE_TDLS_CONN_TRACKER_IN_HOST_MODE = 100, + WMI_TLV_SERVICE_ENHANCED_MCAST_FILTER = 101, + WMI_TLV_SERVICE_PERIODIC_CHAN_STAT_SUPPORT = 102, + WMI_TLV_SERVICE_MESH_11S = 103, + WMI_TLV_SERVICE_HALF_RATE_QUARTER_RATE_SUPPORT = 104, + WMI_TLV_SERVICE_VDEV_RX_FILTER = 105, + WMI_TLV_SERVICE_P2P_LISTEN_OFFLOAD_SUPPORT = 106, + WMI_TLV_SERVICE_MARK_FIRST_WAKEUP_PACKET = 107, + WMI_TLV_SERVICE_MULTIPLE_MCAST_FILTER_SET = 108, + WMI_TLV_SERVICE_HOST_MANAGED_RX_REORDER = 109, + WMI_TLV_SERVICE_FLASH_RDWR_SUPPORT = 110, + WMI_TLV_SERVICE_WLAN_STATS_REPORT = 111, + WMI_TLV_SERVICE_TX_MSDU_ID_NEW_PARTITION_SUPPORT = 112, + WMI_TLV_SERVICE_DFS_PHYERR_OFFLOAD = 113, + WMI_TLV_SERVICE_RCPI_SUPPORT = 114, + WMI_TLV_SERVICE_FW_MEM_DUMP_SUPPORT = 115, + WMI_TLV_SERVICE_PEER_STATS_INFO = 116, + WMI_TLV_SERVICE_REGULATORY_DB = 117, + WMI_TLV_SERVICE_11D_OFFLOAD = 118, + WMI_TLV_SERVICE_HW_DATA_FILTERING = 119, + WMI_TLV_SERVICE_MULTIPLE_VDEV_RESTART = 120, + WMI_TLV_SERVICE_PKT_ROUTING = 121, + WMI_TLV_SERVICE_CHECK_CAL_VERSION = 122, + WMI_TLV_SERVICE_OFFCHAN_TX_WMI = 123, + WMI_TLV_SERVICE_8SS_TX_BFEE = 124, + WMI_TLV_SERVICE_EXTENDED_NSS_SUPPORT = 125, + WMI_TLV_SERVICE_ACK_TIMEOUT = 126, + WMI_TLV_SERVICE_PDEV_BSS_CHANNEL_INFO_64 = 127, + + WMI_MAX_SERVICE = 128, + + WMI_TLV_SERVICE_CHAN_LOAD_INFO = 128, + WMI_TLV_SERVICE_TX_PPDU_INFO_STATS_SUPPORT = 129, + WMI_TLV_SERVICE_VDEV_LIMIT_OFFCHAN_SUPPORT = 130, + WMI_TLV_SERVICE_FILS_SUPPORT = 131, + WMI_TLV_SERVICE_WLAN_OIC_PING_OFFLOAD = 132, + WMI_TLV_SERVICE_WLAN_DHCP_RENEW = 133, + WMI_TLV_SERVICE_MAWC_SUPPORT = 134, + WMI_TLV_SERVICE_VDEV_LATENCY_CONFIG = 135, + WMI_TLV_SERVICE_PDEV_UPDATE_CTLTABLE_SUPPORT = 136, + WMI_TLV_SERVICE_PKTLOG_SUPPORT_OVER_HTT = 137, + WMI_TLV_SERVICE_VDEV_MULTI_GROUP_KEY_SUPPORT = 138, + WMI_TLV_SERVICE_SCAN_PHYMODE_SUPPORT = 139, + WMI_TLV_SERVICE_THERM_THROT = 140, + WMI_TLV_SERVICE_BCN_OFFLOAD_START_STOP_SUPPORT = 141, + WMI_TLV_SERVICE_WOW_WAKEUP_BY_TIMER_PATTERN = 142, + WMI_TLV_SERVICE_PEER_MAP_UNMAP_V2_SUPPORT = 143, + WMI_TLV_SERVICE_OFFCHAN_DATA_TID_SUPPORT = 144, + WMI_TLV_SERVICE_RX_PROMISC_ENABLE_SUPPORT = 145, + WMI_TLV_SERVICE_SUPPORT_DIRECT_DMA = 146, + WMI_TLV_SERVICE_AP_OBSS_DETECTION_OFFLOAD = 147, + WMI_TLV_SERVICE_11K_NEIGHBOUR_REPORT_SUPPORT = 148, + WMI_TLV_SERVICE_LISTEN_INTERVAL_OFFLOAD_SUPPORT = 149, + WMI_TLV_SERVICE_BSS_COLOR_OFFLOAD = 150, + WMI_TLV_SERVICE_RUNTIME_DPD_RECAL = 151, + WMI_TLV_SERVICE_STA_TWT = 152, + WMI_TLV_SERVICE_AP_TWT = 153, + WMI_TLV_SERVICE_GMAC_OFFLOAD_SUPPORT = 154, + WMI_TLV_SERVICE_SPOOF_MAC_SUPPORT = 155, + WMI_TLV_SERVICE_PEER_TID_CONFIGS_SUPPORT = 156, + WMI_TLV_SERVICE_VDEV_SWRETRY_PER_AC_CONFIG_SUPPORT = 157, + WMI_TLV_SERVICE_DUAL_BEACON_ON_SINGLE_MAC_SCC_SUPPORT = 158, + WMI_TLV_SERVICE_DUAL_BEACON_ON_SINGLE_MAC_MCC_SUPPORT = 159, + WMI_TLV_SERVICE_MOTION_DET = 160, + WMI_TLV_SERVICE_INFRA_MBSSID = 161, + WMI_TLV_SERVICE_OBSS_SPATIAL_REUSE = 162, + WMI_TLV_SERVICE_VDEV_DIFFERENT_BEACON_INTERVAL_SUPPORT = 163, + WMI_TLV_SERVICE_NAN_DBS_SUPPORT = 164, + WMI_TLV_SERVICE_NDI_DBS_SUPPORT = 165, + WMI_TLV_SERVICE_NAN_SAP_SUPPORT = 166, + WMI_TLV_SERVICE_NDI_SAP_SUPPORT = 167, + WMI_TLV_SERVICE_CFR_CAPTURE_SUPPORT = 168, + WMI_TLV_SERVICE_CFR_CAPTURE_IND_MSG_TYPE_1 = 169, + WMI_TLV_SERVICE_ESP_SUPPORT = 170, + WMI_TLV_SERVICE_PEER_CHWIDTH_CHANGE = 171, + WMI_TLV_SERVICE_WLAN_HPCS_PULSE = 172, + WMI_TLV_SERVICE_PER_VDEV_CHAINMASK_CONFIG_SUPPORT = 173, + WMI_TLV_SERVICE_TX_DATA_MGMT_ACK_RSSI = 174, + WMI_TLV_SERVICE_NAN_DISABLE_SUPPORT = 175, + WMI_TLV_SERVICE_HTT_H2T_NO_HTC_HDR_LEN_IN_MSG_LEN = 176, + + WMI_MAX_EXT_SERVICE + +}; + +enum { + WMI_SMPS_FORCED_MODE_NONE = 0, + WMI_SMPS_FORCED_MODE_DISABLED, + WMI_SMPS_FORCED_MODE_STATIC, + WMI_SMPS_FORCED_MODE_DYNAMIC +}; + +#define WMI_TPC_CHAINMASK_CONFIG_BAND_2G 0 +#define WMI_TPC_CHAINMASK_CONFIG_BAND_5G 1 +#define WMI_NUM_SUPPORTED_BAND_MAX 2 + +#define WMI_PEER_MIMO_PS_STATE 0x1 +#define WMI_PEER_AMPDU 0x2 +#define WMI_PEER_AUTHORIZE 0x3 +#define WMI_PEER_CHWIDTH 0x4 +#define WMI_PEER_NSS 0x5 +#define WMI_PEER_USE_4ADDR 0x6 +#define WMI_PEER_MEMBERSHIP 0x7 +#define WMI_PEER_USERPOS 0x8 +#define WMI_PEER_CRIT_PROTO_HINT_ENABLED 0x9 +#define WMI_PEER_TX_FAIL_CNT_THR 0xA +#define WMI_PEER_SET_HW_RETRY_CTS2S 0xB +#define WMI_PEER_IBSS_ATIM_WINDOW_LENGTH 0xC +#define WMI_PEER_PHYMODE 0xD +#define WMI_PEER_USE_FIXED_PWR 0xE +#define WMI_PEER_PARAM_FIXED_RATE 0xF +#define WMI_PEER_SET_MU_WHITELIST 0x10 +#define WMI_PEER_SET_MAX_TX_RATE 0x11 +#define WMI_PEER_SET_MIN_TX_RATE 0x12 +#define WMI_PEER_SET_DEFAULT_ROUTING 0x13 + +/* slot time long */ +#define WMI_VDEV_SLOT_TIME_LONG 0x1 +/* slot time short */ +#define WMI_VDEV_SLOT_TIME_SHORT 0x2 +/* preablbe long */ +#define WMI_VDEV_PREAMBLE_LONG 0x1 +/* preablbe short */ +#define WMI_VDEV_PREAMBLE_SHORT 0x2 + +enum wmi_peer_smps_state { + WMI_PEER_SMPS_PS_NONE = 0x0, + WMI_PEER_SMPS_STATIC = 0x1, + WMI_PEER_SMPS_DYNAMIC = 0x2 +}; + +enum wmi_peer_chwidth { + WMI_PEER_CHWIDTH_20MHZ = 0, + WMI_PEER_CHWIDTH_40MHZ = 1, + WMI_PEER_CHWIDTH_80MHZ = 2, + WMI_PEER_CHWIDTH_160MHZ = 3, +}; + +enum wmi_beacon_gen_mode { + WMI_BEACON_STAGGERED_MODE = 0, + WMI_BEACON_BURST_MODE = 1 +}; + +struct wmi_host_pdev_band_to_mac { + u32 pdev_id; + u32 start_freq; + u32 end_freq; +}; + +struct ath11k_ppe_threshold { + u32 numss_m1; + u32 ru_bit_mask; + u32 ppet16_ppet8_ru3_ru0[PSOC_HOST_MAX_NUM_SS]; +}; + +struct ath11k_service_ext_param { + u32 default_conc_scan_config_bits; + u32 default_fw_config_bits; + struct ath11k_ppe_threshold ppet; + u32 he_cap_info; + u32 mpdu_density; + u32 max_bssid_rx_filters; + u32 num_hw_modes; + u32 num_phy; +}; + +struct ath11k_hw_mode_caps { + u32 hw_mode_id; + u32 phy_id_map; + u32 hw_mode_config_type; +}; + +#define PSOC_HOST_MAX_PHY_SIZE (3) +#define ATH11K_11B_SUPPORT BIT(0) +#define ATH11K_11G_SUPPORT BIT(1) +#define ATH11K_11A_SUPPORT BIT(2) +#define ATH11K_11N_SUPPORT BIT(3) +#define ATH11K_11AC_SUPPORT BIT(4) +#define ATH11K_11AX_SUPPORT BIT(5) + +struct ath11k_hal_reg_capabilities_ext { + u32 phy_id; + u32 eeprom_reg_domain; + u32 eeprom_reg_domain_ext; + u32 regcap1; + u32 regcap2; + u32 wireless_modes; + u32 low_2ghz_chan; + u32 high_2ghz_chan; + u32 low_5ghz_chan; + u32 high_5ghz_chan; +}; + +#define WMI_HOST_MAX_PDEV 3 + +struct wlan_host_mem_chunk { + u32 tlv_header; + u32 req_id; + u32 ptr; + u32 size; +} __packed; + +struct wmi_host_mem_chunk { + void *vaddr; + dma_addr_t paddr; + u32 len; + u32 req_id; +}; + +struct wmi_init_cmd_param { + u32 tlv_header; + struct target_resource_config *res_cfg; + u8 num_mem_chunks; + struct wmi_host_mem_chunk *mem_chunks; + u32 hw_mode_id; + u32 num_band_to_mac; + struct wmi_host_pdev_band_to_mac band_to_mac[WMI_HOST_MAX_PDEV]; +}; + +struct wmi_pdev_band_to_mac { + u32 tlv_header; + u32 pdev_id; + u32 start_freq; + u32 end_freq; +} __packed; + +struct wmi_pdev_set_hw_mode_cmd_param { + u32 tlv_header; + u32 pdev_id; + u32 hw_mode_index; + u32 num_band_to_mac; +} __packed; + +struct wmi_ppe_threshold { + u32 numss_m1; /** NSS - 1*/ + union { + u32 ru_count; + u32 ru_mask; + } __packed; + u32 ppet16_ppet8_ru3_ru0[WMI_MAX_NUM_SS]; +} __packed; + +#define HW_BD_INFO_SIZE 5 + +struct wmi_abi_version { + u32 abi_version_0; + u32 abi_version_1; + u32 abi_version_ns_0; + u32 abi_version_ns_1; + u32 abi_version_ns_2; + u32 abi_version_ns_3; +} __packed; + +struct wmi_init_cmd { + u32 tlv_header; + struct wmi_abi_version host_abi_vers; + u32 num_host_mem_chunks; +} __packed; + +struct wmi_resource_config { + u32 tlv_header; + u32 num_vdevs; + u32 num_peers; + u32 num_offload_peers; + u32 num_offload_reorder_buffs; + u32 num_peer_keys; + u32 num_tids; + u32 ast_skid_limit; + u32 tx_chain_mask; + u32 rx_chain_mask; + u32 rx_timeout_pri[4]; + u32 rx_decap_mode; + u32 scan_max_pending_req; + u32 bmiss_offload_max_vdev; + u32 roam_offload_max_vdev; + u32 roam_offload_max_ap_profiles; + u32 num_mcast_groups; + u32 num_mcast_table_elems; + u32 mcast2ucast_mode; + u32 tx_dbg_log_size; + u32 num_wds_entries; + u32 dma_burst_size; + u32 mac_aggr_delim; + u32 rx_skip_defrag_timeout_dup_detection_check; + u32 vow_config; + u32 gtk_offload_max_vdev; + u32 num_msdu_desc; + u32 max_frag_entries; + u32 num_tdls_vdevs; + u32 num_tdls_conn_table_entries; + u32 beacon_tx_offload_max_vdev; + u32 num_multicast_filter_entries; + u32 num_wow_filters; + u32 num_keep_alive_pattern; + u32 keep_alive_pattern_size; + u32 max_tdls_concurrent_sleep_sta; + u32 max_tdls_concurrent_buffer_sta; + u32 wmi_send_separate; + u32 num_ocb_vdevs; + u32 num_ocb_channels; + u32 num_ocb_schedules; + u32 flag1; + u32 smart_ant_cap; + u32 bk_minfree; + u32 be_minfree; + u32 vi_minfree; + u32 vo_minfree; + u32 alloc_frag_desc_for_data_pkt; + u32 num_ns_ext_tuples_cfg; + u32 bpf_instruction_size; + u32 max_bssid_rx_filters; + u32 use_pdev_id; + u32 max_num_dbs_scan_duty_cycle; + u32 max_num_group_keys; + u32 peer_map_unmap_v2_support; +} __packed; + +struct wmi_service_ready_event { + u32 fw_build_vers; + struct wmi_abi_version fw_abi_vers; + u32 phy_capability; + u32 max_frag_entry; + u32 num_rf_chains; + u32 ht_cap_info; + u32 vht_cap_info; + u32 vht_supp_mcs; + u32 hw_min_tx_power; + u32 hw_max_tx_power; + u32 sys_cap_info; + u32 min_pkt_size_enable; + u32 max_bcn_ie_size; + u32 num_mem_reqs; + u32 max_num_scan_channels; + u32 hw_bd_id; + u32 hw_bd_info[HW_BD_INFO_SIZE]; + u32 max_supported_macs; + u32 wmi_fw_sub_feat_caps; + u32 num_dbs_hw_modes; + /* txrx_chainmask + * [7:0] - 2G band tx chain mask + * [15:8] - 2G band rx chain mask + * [23:16] - 5G band tx chain mask + * [31:24] - 5G band rx chain mask + */ + u32 txrx_chainmask; + u32 default_dbs_hw_mode_index; + u32 num_msdu_desc; +} __packed; + +#define WMI_SERVICE_BM_SIZE ((WMI_MAX_SERVICE + sizeof(u32) - 1) / sizeof(u32)) + +#define WMI_SERVICE_SEGMENT_BM_SIZE32 4 /* 4x u32 = 128 bits */ +#define WMI_SERVICE_EXT_BM_SIZE (WMI_SERVICE_SEGMENT_BM_SIZE32 * sizeof(u32)) +#define WMI_AVAIL_SERVICE_BITS_IN_SIZE32 32 +#define WMI_SERVICE_BITS_IN_SIZE32 4 + +struct wmi_service_ready_ext_event { + u32 default_conc_scan_config_bits; + u32 default_fw_config_bits; + struct wmi_ppe_threshold ppet; + u32 he_cap_info; + u32 mpdu_density; + u32 max_bssid_rx_filters; + u32 fw_build_vers_ext; + u32 max_nlo_ssids; + u32 max_bssid_indicator; + u32 he_cap_info_ext; +} __packed; + +struct wmi_soc_mac_phy_hw_mode_caps { + u32 num_hw_modes; + u32 num_chainmask_tables; +} __packed; + +struct wmi_hw_mode_capabilities { + u32 tlv_header; + u32 hw_mode_id; + u32 phy_id_map; + u32 hw_mode_config_type; +} __packed; + +#define WMI_MAX_HECAP_PHY_SIZE (3) + +struct wmi_mac_phy_capabilities { + u32 tlv_header; + u32 hw_mode_id; + u32 pdev_id; + u32 phy_id; + u32 supported_flags; + u32 supported_bands; + u32 ampdu_density; + u32 max_bw_supported_2g; + u32 ht_cap_info_2g; + u32 vht_cap_info_2g; + u32 vht_supp_mcs_2g; + u32 he_cap_info_2g; + u32 he_supp_mcs_2g; + u32 tx_chain_mask_2g; + u32 rx_chain_mask_2g; + u32 max_bw_supported_5g; + u32 ht_cap_info_5g; + u32 vht_cap_info_5g; + u32 vht_supp_mcs_5g; + u32 he_cap_info_5g; + u32 he_supp_mcs_5g; + u32 tx_chain_mask_5g; + u32 rx_chain_mask_5g; + u32 he_cap_phy_info_2g[WMI_MAX_HECAP_PHY_SIZE]; + u32 he_cap_phy_info_5g[WMI_MAX_HECAP_PHY_SIZE]; + struct wmi_ppe_threshold he_ppet2g; + struct wmi_ppe_threshold he_ppet5g; + u32 chainmask_table_id; + u32 lmac_id; + u32 he_cap_info_2g_ext; + u32 he_cap_info_5g_ext; + u32 he_cap_info_internal; +} __packed; + +struct wmi_hal_reg_capabilities_ext { + u32 tlv_header; + u32 phy_id; + u32 eeprom_reg_domain; + u32 eeprom_reg_domain_ext; + u32 regcap1; + u32 regcap2; + u32 wireless_modes; + u32 low_2ghz_chan; + u32 high_2ghz_chan; + u32 low_5ghz_chan; + u32 high_5ghz_chan; +} __packed; + +struct wmi_soc_hal_reg_capabilities { + u32 num_phy; +} __packed; + +/* 2 word representation of MAC addr */ +struct wmi_mac_addr { + union { + u8 addr[6]; + struct { + u32 word0; + u32 word1; + } __packed; + } __packed; +} __packed; + +struct wmi_ready_event { + struct wmi_abi_version fw_abi_vers; + struct wmi_mac_addr mac_addr; + u32 status; + u32 num_dscp_table; + u32 num_extra_mac_addr; + u32 num_total_peers; + u32 num_extra_peers; +} __packed; + +struct wmi_service_available_event { + u32 wmi_service_segment_offset; + u32 wmi_service_segment_bitmap[WMI_SERVICE_SEGMENT_BM_SIZE32]; +} __packed; + +struct ath11k_pdev_wmi { + struct ath11k_wmi_base *wmi_sc; + enum ath11k_htc_ep_id eid; + const struct wmi_peer_flags_map *peer_flags; + u32 rx_decap_mode; +}; + +struct vdev_create_params { + u8 if_id; + u32 type; + u32 subtype; + struct { + u8 tx; + u8 rx; + } chains[NUM_NL80211_BANDS]; + u32 pdev_id; +}; + +struct wmi_vdev_create_cmd { + u32 tlv_header; + u32 vdev_id; + u32 vdev_type; + u32 vdev_subtype; + struct wmi_mac_addr vdev_macaddr; + u32 num_cfg_txrx_streams; + u32 pdev_id; +} __packed; + +struct wmi_vdev_txrx_streams { + u32 tlv_header; + u32 band; + u32 supported_tx_streams; + u32 supported_rx_streams; +} __packed; + +struct wmi_vdev_delete_cmd { + u32 tlv_header; + u32 vdev_id; +} __packed; + +struct wmi_vdev_up_cmd { + u32 tlv_header; + u32 vdev_id; + u32 vdev_assoc_id; + struct wmi_mac_addr vdev_bssid; + struct wmi_mac_addr trans_bssid; + u32 profile_idx; + u32 profile_num; +} __packed; + +struct wmi_vdev_stop_cmd { + u32 tlv_header; + u32 vdev_id; +} __packed; + +struct wmi_vdev_down_cmd { + u32 tlv_header; + u32 vdev_id; +} __packed; + +#define WMI_VDEV_START_HIDDEN_SSID BIT(0) +#define WMI_VDEV_START_PMF_ENABLED BIT(1) +#define WMI_VDEV_START_LDPC_RX_ENABLED BIT(3) + +struct wmi_ssid { + u32 ssid_len; + u32 ssid[8]; +} __packed; + +#define ATH11K_VDEV_SETUP_TIMEOUT_HZ (1 * HZ) + +struct wmi_vdev_start_request_cmd { + u32 tlv_header; + u32 vdev_id; + u32 requestor_id; + u32 beacon_interval; + u32 dtim_period; + u32 flags; + struct wmi_ssid ssid; + u32 bcn_tx_rate; + u32 bcn_txpower; + u32 num_noa_descriptors; + u32 disable_hw_ack; + u32 preferred_tx_streams; + u32 preferred_rx_streams; + u32 he_ops; + u32 cac_duration_ms; + u32 regdomain; +} __packed; + +#define MGMT_TX_DL_FRM_LEN 64 +#define WMI_MAC_MAX_SSID_LENGTH 32 +struct mac_ssid { + u8 length; + u8 mac_ssid[WMI_MAC_MAX_SSID_LENGTH]; +} __packed; + +struct wmi_p2p_noa_descriptor { + u32 type_count; + u32 duration; + u32 interval; + u32 start_time; +}; + +struct channel_param { + u8 chan_id; + u8 pwr; + u32 mhz; + u32 half_rate:1, + quarter_rate:1, + dfs_set:1, + dfs_set_cfreq2:1, + is_chan_passive:1, + allow_ht:1, + allow_vht:1, + set_agile:1; + u32 phy_mode; + u32 cfreq1; + u32 cfreq2; + char maxpower; + char minpower; + char maxregpower; + u8 antennamax; + u8 reg_class_id; +} __packed; + +enum wmi_phy_mode { + MODE_11A = 0, + MODE_11G = 1, /* 11b/g Mode */ + MODE_11B = 2, /* 11b Mode */ + MODE_11GONLY = 3, /* 11g only Mode */ + MODE_11NA_HT20 = 4, + MODE_11NG_HT20 = 5, + MODE_11NA_HT40 = 6, + MODE_11NG_HT40 = 7, + MODE_11AC_VHT20 = 8, + MODE_11AC_VHT40 = 9, + MODE_11AC_VHT80 = 10, + MODE_11AC_VHT20_2G = 11, + MODE_11AC_VHT40_2G = 12, + MODE_11AC_VHT80_2G = 13, + MODE_11AC_VHT80_80 = 14, + MODE_11AC_VHT160 = 15, + MODE_11AX_HE20 = 16, + MODE_11AX_HE40 = 17, + MODE_11AX_HE80 = 18, + MODE_11AX_HE80_80 = 19, + MODE_11AX_HE160 = 20, + MODE_11AX_HE20_2G = 21, + MODE_11AX_HE40_2G = 22, + MODE_11AX_HE80_2G = 23, + MODE_UNKNOWN = 24, + MODE_MAX = 24 +}; + +static inline const char *ath11k_wmi_phymode_str(enum wmi_phy_mode mode) +{ + switch (mode) { + case MODE_11A: + return "11a"; + case MODE_11G: + return "11g"; + case MODE_11B: + return "11b"; + case MODE_11GONLY: + return "11gonly"; + case MODE_11NA_HT20: + return "11na-ht20"; + case MODE_11NG_HT20: + return "11ng-ht20"; + case MODE_11NA_HT40: + return "11na-ht40"; + case MODE_11NG_HT40: + return "11ng-ht40"; + case MODE_11AC_VHT20: + return "11ac-vht20"; + case MODE_11AC_VHT40: + return "11ac-vht40"; + case MODE_11AC_VHT80: + return "11ac-vht80"; + case MODE_11AC_VHT160: + return "11ac-vht160"; + case MODE_11AC_VHT80_80: + return "11ac-vht80+80"; + case MODE_11AC_VHT20_2G: + return "11ac-vht20-2g"; + case MODE_11AC_VHT40_2G: + return "11ac-vht40-2g"; + case MODE_11AC_VHT80_2G: + return "11ac-vht80-2g"; + case MODE_11AX_HE20: + return "11ax-he20"; + case MODE_11AX_HE40: + return "11ax-he40"; + case MODE_11AX_HE80: + return "11ax-he80"; + case MODE_11AX_HE80_80: + return "11ax-he80+80"; + case MODE_11AX_HE160: + return "11ax-he160"; + case MODE_11AX_HE20_2G: + return "11ax-he20-2g"; + case MODE_11AX_HE40_2G: + return "11ax-he40-2g"; + case MODE_11AX_HE80_2G: + return "11ax-he80-2g"; + case MODE_UNKNOWN: + /* skip */ + break; + + /* no default handler to allow compiler to check that the + * enum is fully handled + */ + }; + + return ""; +} + +struct wmi_channel_arg { + u32 freq; + u32 band_center_freq1; + u32 band_center_freq2; + bool passive; + bool allow_ibss; + bool allow_ht; + bool allow_vht; + bool ht40plus; + bool chan_radar; + bool freq2_radar; + bool allow_he; + u32 min_power; + u32 max_power; + u32 max_reg_power; + u32 max_antenna_gain; + enum wmi_phy_mode mode; +}; + +struct wmi_vdev_start_req_arg { + u32 vdev_id; + struct wmi_channel_arg channel; + u32 bcn_intval; + u32 dtim_period; + u8 *ssid; + u32 ssid_len; + u32 bcn_tx_rate; + u32 bcn_tx_power; + bool disable_hw_ack; + bool hidden_ssid; + bool pmf_enabled; + u32 he_ops; + u32 cac_duration_ms; + u32 regdomain; + u32 pref_rx_streams; + u32 pref_tx_streams; + u32 num_noa_descriptors; +}; + +struct peer_create_params { + const u8 *peer_addr; + u32 peer_type; + u32 vdev_id; +}; + +struct peer_delete_params { + u8 vdev_id; +}; + +struct peer_flush_params { + u32 peer_tid_bitmap; + u8 vdev_id; +}; + +struct pdev_set_regdomain_params { + u16 current_rd_in_use; + u16 current_rd_2g; + u16 current_rd_5g; + u32 ctl_2g; + u32 ctl_5g; + u8 dfs_domain; + u32 pdev_id; +}; + +struct rx_reorder_queue_remove_params { + u8 *peer_macaddr; + u16 vdev_id; + u32 peer_tid_bitmap; +}; + +#define WMI_HOST_PDEV_ID_SOC 0xFF +#define WMI_HOST_PDEV_ID_0 0 +#define WMI_HOST_PDEV_ID_1 1 +#define WMI_HOST_PDEV_ID_2 2 + +#define WMI_PDEV_ID_SOC 0 +#define WMI_PDEV_ID_1ST 1 +#define WMI_PDEV_ID_2ND 2 +#define WMI_PDEV_ID_3RD 3 + +/* Freq units in MHz */ +#define REG_RULE_START_FREQ 0x0000ffff +#define REG_RULE_END_FREQ 0xffff0000 +#define REG_RULE_FLAGS 0x0000ffff +#define REG_RULE_MAX_BW 0x0000ffff +#define REG_RULE_REG_PWR 0x00ff0000 +#define REG_RULE_ANT_GAIN 0xff000000 + +#define WMI_VDEV_PARAM_TXBF_SU_TX_BFEE BIT(0) +#define WMI_VDEV_PARAM_TXBF_MU_TX_BFEE BIT(1) +#define WMI_VDEV_PARAM_TXBF_SU_TX_BFER BIT(2) +#define WMI_VDEV_PARAM_TXBF_MU_TX_BFER BIT(3) + +#define HECAP_PHYDWORD_0 0 +#define HECAP_PHYDWORD_1 1 +#define HECAP_PHYDWORD_2 2 + +#define HECAP_PHY_SU_BFER BIT(31) +#define HECAP_PHY_SU_BFEE BIT(0) +#define HECAP_PHY_MU_BFER BIT(1) +#define HECAP_PHY_UL_MUMIMO BIT(22) +#define HECAP_PHY_UL_MUOFDMA BIT(23) + +#define HECAP_PHY_SUBFMR_GET(hecap_phy) \ + FIELD_GET(HECAP_PHY_SU_BFER, hecap_phy[HECAP_PHYDWORD_0]) + +#define HECAP_PHY_SUBFME_GET(hecap_phy) \ + FIELD_GET(HECAP_PHY_SU_BFEE, hecap_phy[HECAP_PHYDWORD_1]) + +#define HECAP_PHY_MUBFMR_GET(hecap_phy) \ + FIELD_GET(HECAP_PHY_MU_BFER, hecap_phy[HECAP_PHYDWORD_1]) + +#define HECAP_PHY_ULMUMIMO_GET(hecap_phy) \ + FIELD_GET(HECAP_PHY_UL_MUMIMO, hecap_phy[HECAP_PHYDWORD_0]) + +#define HECAP_PHY_ULOFDMA_GET(hecap_phy) \ + FIELD_GET(HECAP_PHY_UL_MUOFDMA, hecap_phy[HECAP_PHYDWORD_0]) + +#define HE_MODE_SU_TX_BFEE BIT(0) +#define HE_MODE_SU_TX_BFER BIT(1) +#define HE_MODE_MU_TX_BFEE BIT(2) +#define HE_MODE_MU_TX_BFER BIT(3) +#define HE_MODE_DL_OFDMA BIT(4) +#define HE_MODE_UL_OFDMA BIT(5) +#define HE_MODE_UL_MUMIMO BIT(6) + +#define HE_DL_MUOFDMA_ENABLE 1 +#define HE_UL_MUOFDMA_ENABLE 1 +#define HE_DL_MUMIMO_ENABLE 1 +#define HE_MU_BFEE_ENABLE 1 +#define HE_SU_BFEE_ENABLE 1 + +#define HE_VHT_SOUNDING_MODE_ENABLE 1 +#define HE_SU_MU_SOUNDING_MODE_ENABLE 1 +#define HE_TRIG_NONTRIG_SOUNDING_MODE_ENABLE 1 + +/* HE or VHT Sounding */ +#define HE_VHT_SOUNDING_MODE BIT(0) +/* SU or MU Sounding */ +#define HE_SU_MU_SOUNDING_MODE BIT(2) +/* Trig or Non-Trig Sounding */ +#define HE_TRIG_NONTRIG_SOUNDING_MODE BIT(3) + +#define WMI_TXBF_STS_CAP_OFFSET_LSB 4 +#define WMI_TXBF_STS_CAP_OFFSET_MASK 0x70 +#define WMI_BF_SOUND_DIM_OFFSET_LSB 8 +#define WMI_BF_SOUND_DIM_OFFSET_MASK 0x700 + +struct pdev_params { + u32 param_id; + u32 param_value; +}; + +enum wmi_peer_type { + WMI_PEER_TYPE_DEFAULT = 0, + WMI_PEER_TYPE_BSS = 1, + WMI_PEER_TYPE_TDLS = 2, +}; + +struct wmi_peer_create_cmd { + u32 tlv_header; + u32 vdev_id; + struct wmi_mac_addr peer_macaddr; + u32 peer_type; +} __packed; + +struct wmi_peer_delete_cmd { + u32 tlv_header; + u32 vdev_id; + struct wmi_mac_addr peer_macaddr; +} __packed; + +struct wmi_peer_reorder_queue_setup_cmd { + u32 tlv_header; + u32 vdev_id; + struct wmi_mac_addr peer_macaddr; + u32 tid; + u32 queue_ptr_lo; + u32 queue_ptr_hi; + u32 queue_no; + u32 ba_window_size_valid; + u32 ba_window_size; +} __packed; + +struct wmi_peer_reorder_queue_remove_cmd { + u32 tlv_header; + u32 vdev_id; + struct wmi_mac_addr peer_macaddr; + u32 tid_mask; +} __packed; + +struct gpio_config_params { + u32 gpio_num; + u32 input; + u32 pull_type; + u32 intr_mode; +}; + +enum wmi_gpio_type { + WMI_GPIO_PULL_NONE, + WMI_GPIO_PULL_UP, + WMI_GPIO_PULL_DOWN +}; + +enum wmi_gpio_intr_type { + WMI_GPIO_INTTYPE_DISABLE, + WMI_GPIO_INTTYPE_RISING_EDGE, + WMI_GPIO_INTTYPE_FALLING_EDGE, + WMI_GPIO_INTTYPE_BOTH_EDGE, + WMI_GPIO_INTTYPE_LEVEL_LOW, + WMI_GPIO_INTTYPE_LEVEL_HIGH +}; + +enum wmi_bss_chan_info_req_type { + WMI_BSS_SURVEY_REQ_TYPE_READ = 1, + WMI_BSS_SURVEY_REQ_TYPE_READ_CLEAR, +}; + +struct wmi_gpio_config_cmd_param { + u32 tlv_header; + u32 gpio_num; + u32 input; + u32 pull_type; + u32 intr_mode; +}; + +struct gpio_output_params { + u32 gpio_num; + u32 set; +}; + +struct wmi_gpio_output_cmd_param { + u32 tlv_header; + u32 gpio_num; + u32 set; +}; + +struct set_fwtest_params { + u32 arg; + u32 value; +}; + +struct wmi_fwtest_set_param_cmd_param { + u32 tlv_header; + u32 param_id; + u32 param_value; +}; + +struct wmi_pdev_set_param_cmd { + u32 tlv_header; + u32 pdev_id; + u32 param_id; + u32 param_value; +} __packed; + +struct wmi_pdev_suspend_cmd { + u32 tlv_header; + u32 pdev_id; + u32 suspend_opt; +} __packed; + +struct wmi_pdev_resume_cmd { + u32 tlv_header; + u32 pdev_id; +} __packed; + +struct wmi_pdev_bss_chan_info_req_cmd { + u32 tlv_header; + /* ref wmi_bss_chan_info_req_type */ + u32 req_type; +} __packed; + +struct wmi_ap_ps_peer_cmd { + u32 tlv_header; + u32 vdev_id; + struct wmi_mac_addr peer_macaddr; + u32 param; + u32 value; +} __packed; + +struct wmi_sta_powersave_param_cmd { + u32 tlv_header; + u32 vdev_id; + u32 param; + u32 value; +} __packed; + +struct wmi_pdev_set_regdomain_cmd { + u32 tlv_header; + u32 pdev_id; + u32 reg_domain; + u32 reg_domain_2g; + u32 reg_domain_5g; + u32 conformance_test_limit_2g; + u32 conformance_test_limit_5g; + u32 dfs_domain; +} __packed; + +struct wmi_peer_set_param_cmd { + u32 tlv_header; + u32 vdev_id; + struct wmi_mac_addr peer_macaddr; + u32 param_id; + u32 param_value; +} __packed; + +struct wmi_peer_flush_tids_cmd { + u32 tlv_header; + u32 vdev_id; + struct wmi_mac_addr peer_macaddr; + u32 peer_tid_bitmap; +} __packed; + +struct wmi_dfs_phyerr_offload_cmd { + u32 tlv_header; + u32 pdev_id; +} __packed; + +struct wmi_bcn_offload_ctrl_cmd { + u32 tlv_header; + u32 vdev_id; + u32 bcn_ctrl_op; +} __packed; + +enum scan_priority { + SCAN_PRIORITY_VERY_LOW, + SCAN_PRIORITY_LOW, + SCAN_PRIORITY_MEDIUM, + SCAN_PRIORITY_HIGH, + SCAN_PRIORITY_VERY_HIGH, + SCAN_PRIORITY_COUNT, +}; + +enum scan_dwelltime_adaptive_mode { + SCAN_DWELL_MODE_DEFAULT = 0, + SCAN_DWELL_MODE_CONSERVATIVE = 1, + SCAN_DWELL_MODE_MODERATE = 2, + SCAN_DWELL_MODE_AGGRESSIVE = 3, + SCAN_DWELL_MODE_STATIC = 4 +}; + +#define WLAN_SCAN_MAX_NUM_SSID 10 +#define WLAN_SCAN_MAX_NUM_BSSID 10 +#define WLAN_SCAN_MAX_NUM_CHANNELS 40 + +#define WLAN_SSID_MAX_LEN 32 + +struct element_info { + u32 len; + u8 *ptr; +}; + +struct wlan_ssid { + u8 length; + u8 ssid[WLAN_SSID_MAX_LEN]; +}; + +#define WMI_IE_BITMAP_SIZE 8 + +#define WMI_SCAN_MAX_NUM_SSID 0x0A +/* prefix used by scan requestor ids on the host */ +#define WMI_HOST_SCAN_REQUESTOR_ID_PREFIX 0xA000 + +/* prefix used by scan request ids generated on the host */ +/* host cycles through the lower 12 bits to generate ids */ +#define WMI_HOST_SCAN_REQ_ID_PREFIX 0xA000 + +#define WLAN_SCAN_PARAMS_MAX_SSID 16 +#define WLAN_SCAN_PARAMS_MAX_BSSID 4 +#define WLAN_SCAN_PARAMS_MAX_IE_LEN 256 + +/* Values lower than this may be refused by some firmware revisions with a scan + * completion with a timedout reason. + */ +#define WMI_SCAN_CHAN_MIN_TIME_MSEC 40 + +/* Scan priority numbers must be sequential, starting with 0 */ +enum wmi_scan_priority { + WMI_SCAN_PRIORITY_VERY_LOW = 0, + WMI_SCAN_PRIORITY_LOW, + WMI_SCAN_PRIORITY_MEDIUM, + WMI_SCAN_PRIORITY_HIGH, + WMI_SCAN_PRIORITY_VERY_HIGH, + WMI_SCAN_PRIORITY_COUNT /* number of priorities supported */ +}; + +enum wmi_scan_event_type { + WMI_SCAN_EVENT_STARTED = BIT(0), + WMI_SCAN_EVENT_COMPLETED = BIT(1), + WMI_SCAN_EVENT_BSS_CHANNEL = BIT(2), + WMI_SCAN_EVENT_FOREIGN_CHAN = BIT(3), + WMI_SCAN_EVENT_DEQUEUED = BIT(4), + /* possibly by high-prio scan */ + WMI_SCAN_EVENT_PREEMPTED = BIT(5), + WMI_SCAN_EVENT_START_FAILED = BIT(6), + WMI_SCAN_EVENT_RESTARTED = BIT(7), + WMI_SCAN_EVENT_FOREIGN_CHAN_EXIT = BIT(8), + WMI_SCAN_EVENT_SUSPENDED = BIT(9), + WMI_SCAN_EVENT_RESUMED = BIT(10), + WMI_SCAN_EVENT_MAX = BIT(15), +}; + +enum wmi_scan_completion_reason { + WMI_SCAN_REASON_COMPLETED, + WMI_SCAN_REASON_CANCELLED, + WMI_SCAN_REASON_PREEMPTED, + WMI_SCAN_REASON_TIMEDOUT, + WMI_SCAN_REASON_INTERNAL_FAILURE, + WMI_SCAN_REASON_MAX, +}; + +struct wmi_start_scan_cmd { + u32 tlv_header; + u32 scan_id; + u32 scan_req_id; + u32 vdev_id; + u32 scan_priority; + u32 notify_scan_events; + u32 dwell_time_active; + u32 dwell_time_passive; + u32 min_rest_time; + u32 max_rest_time; + u32 repeat_probe_time; + u32 probe_spacing_time; + u32 idle_time; + u32 max_scan_time; + u32 probe_delay; + u32 scan_ctrl_flags; + u32 burst_duration; + u32 num_chan; + u32 num_bssid; + u32 num_ssids; + u32 ie_len; + u32 n_probes; + struct wmi_mac_addr mac_addr; + struct wmi_mac_addr mac_mask; + u32 ie_bitmap[WMI_IE_BITMAP_SIZE]; + u32 num_vendor_oui; + u32 scan_ctrl_flags_ext; + u32 dwell_time_active_2g; +} __packed; + +#define WMI_SCAN_FLAG_PASSIVE 0x1 +#define WMI_SCAN_ADD_BCAST_PROBE_REQ 0x2 +#define WMI_SCAN_ADD_CCK_RATES 0x4 +#define WMI_SCAN_ADD_OFDM_RATES 0x8 +#define WMI_SCAN_CHAN_STAT_EVENT 0x10 +#define WMI_SCAN_FILTER_PROBE_REQ 0x20 +#define WMI_SCAN_BYPASS_DFS_CHN 0x40 +#define WMI_SCAN_CONTINUE_ON_ERROR 0x80 +#define WMI_SCAN_FILTER_PROMISCUOS 0x100 +#define WMI_SCAN_FLAG_FORCE_ACTIVE_ON_DFS 0x200 +#define WMI_SCAN_ADD_TPC_IE_IN_PROBE_REQ 0x400 +#define WMI_SCAN_ADD_DS_IE_IN_PROBE_REQ 0x800 +#define WMI_SCAN_ADD_SPOOF_MAC_IN_PROBE_REQ 0x1000 +#define WMI_SCAN_OFFCHAN_MGMT_TX 0x2000 +#define WMI_SCAN_OFFCHAN_DATA_TX 0x4000 +#define WMI_SCAN_CAPTURE_PHY_ERROR 0x8000 +#define WMI_SCAN_FLAG_STRICT_PASSIVE_ON_PCHN 0x10000 +#define WMI_SCAN_FLAG_HALF_RATE_SUPPORT 0x20000 +#define WMI_SCAN_FLAG_QUARTER_RATE_SUPPORT 0x40000 +#define WMI_SCAN_RANDOM_SEQ_NO_IN_PROBE_REQ 0x80000 +#define WMI_SCAN_ENABLE_IE_WHTELIST_IN_PROBE_REQ 0x100000 + +#define WMI_SCAN_DWELL_MODE_MASK 0x00E00000 +#define WMI_SCAN_DWELL_MODE_SHIFT 21 + +enum { + WMI_SCAN_DWELL_MODE_DEFAULT = 0, + WMI_SCAN_DWELL_MODE_CONSERVATIVE = 1, + WMI_SCAN_DWELL_MODE_MODERATE = 2, + WMI_SCAN_DWELL_MODE_AGGRESSIVE = 3, + WMI_SCAN_DWELL_MODE_STATIC = 4, +}; + +#define WMI_SCAN_SET_DWELL_MODE(flag, mode) \ + ((flag) |= (((mode) << WMI_SCAN_DWELL_MODE_SHIFT) & \ + WMI_SCAN_DWELL_MODE_MASK)) + +struct scan_req_params { + u32 scan_id; + u32 scan_req_id; + u32 vdev_id; + u32 pdev_id; + enum scan_priority scan_priority; + union { + struct { + u32 scan_ev_started:1, + scan_ev_completed:1, + scan_ev_bss_chan:1, + scan_ev_foreign_chan:1, + scan_ev_dequeued:1, + scan_ev_preempted:1, + scan_ev_start_failed:1, + scan_ev_restarted:1, + scan_ev_foreign_chn_exit:1, + scan_ev_invalid:1, + scan_ev_gpio_timeout:1, + scan_ev_suspended:1, + scan_ev_resumed:1; + }; + u32 scan_events; + }; + u32 dwell_time_active; + u32 dwell_time_active_2g; + u32 dwell_time_passive; + u32 min_rest_time; + u32 max_rest_time; + u32 repeat_probe_time; + u32 probe_spacing_time; + u32 idle_time; + u32 max_scan_time; + u32 probe_delay; + union { + struct { + u32 scan_f_passive:1, + scan_f_bcast_probe:1, + scan_f_cck_rates:1, + scan_f_ofdm_rates:1, + scan_f_chan_stat_evnt:1, + scan_f_filter_prb_req:1, + scan_f_bypass_dfs_chn:1, + scan_f_continue_on_err:1, + scan_f_offchan_mgmt_tx:1, + scan_f_offchan_data_tx:1, + scan_f_promisc_mode:1, + scan_f_capture_phy_err:1, + scan_f_strict_passive_pch:1, + scan_f_half_rate:1, + scan_f_quarter_rate:1, + scan_f_force_active_dfs_chn:1, + scan_f_add_tpc_ie_in_probe:1, + scan_f_add_ds_ie_in_probe:1, + scan_f_add_spoofed_mac_in_probe:1, + scan_f_add_rand_seq_in_probe:1, + scan_f_en_ie_whitelist_in_probe:1, + scan_f_forced:1, + scan_f_2ghz:1, + scan_f_5ghz:1, + scan_f_80mhz:1; + }; + u32 scan_flags; + }; + enum scan_dwelltime_adaptive_mode adaptive_dwell_time_mode; + u32 burst_duration; + u32 num_chan; + u32 num_bssid; + u32 num_ssids; + u32 n_probes; + u32 chan_list[WLAN_SCAN_MAX_NUM_CHANNELS]; + u32 notify_scan_events; + struct wlan_ssid ssid[WLAN_SCAN_MAX_NUM_SSID]; + struct wmi_mac_addr bssid_list[WLAN_SCAN_MAX_NUM_BSSID]; + struct element_info extraie; + struct element_info htcap; + struct element_info vhtcap; +}; + +struct wmi_ssid_arg { + int len; + const u8 *ssid; +}; + +struct wmi_bssid_arg { + const u8 *bssid; +}; + +struct wmi_start_scan_arg { + u32 scan_id; + u32 scan_req_id; + u32 vdev_id; + u32 scan_priority; + u32 notify_scan_events; + u32 dwell_time_active; + u32 dwell_time_passive; + u32 min_rest_time; + u32 max_rest_time; + u32 repeat_probe_time; + u32 probe_spacing_time; + u32 idle_time; + u32 max_scan_time; + u32 probe_delay; + u32 scan_ctrl_flags; + + u32 ie_len; + u32 n_channels; + u32 n_ssids; + u32 n_bssids; + + u8 ie[WLAN_SCAN_PARAMS_MAX_IE_LEN]; + u32 channels[64]; + struct wmi_ssid_arg ssids[WLAN_SCAN_PARAMS_MAX_SSID]; + struct wmi_bssid_arg bssids[WLAN_SCAN_PARAMS_MAX_BSSID]; +}; + +#define WMI_SCAN_STOP_ONE 0x00000000 +#define WMI_SCN_STOP_VAP_ALL 0x01000000 +#define WMI_SCAN_STOP_ALL 0x04000000 + +/* Prefix 0xA000 indicates that the scan request + * is trigger by HOST + */ +#define ATH11K_SCAN_ID 0xA000 + +enum scan_cancel_req_type { + WLAN_SCAN_CANCEL_SINGLE = 1, + WLAN_SCAN_CANCEL_VDEV_ALL, + WLAN_SCAN_CANCEL_PDEV_ALL, +}; + +struct scan_cancel_param { + u32 requester; + u32 scan_id; + enum scan_cancel_req_type req_type; + u32 vdev_id; + u32 pdev_id; +}; + +struct wmi_bcn_send_from_host_cmd { + u32 tlv_header; + u32 vdev_id; + u32 data_len; + union { + u32 frag_ptr; + u32 frag_ptr_lo; + }; + u32 frame_ctrl; + u32 dtim_flag; + u32 bcn_antenna; + u32 frag_ptr_hi; +}; + +#define WMI_CHAN_INFO_MODE GENMASK(5, 0) +#define WMI_CHAN_INFO_HT40_PLUS BIT(6) +#define WMI_CHAN_INFO_PASSIVE BIT(7) +#define WMI_CHAN_INFO_ADHOC_ALLOWED BIT(8) +#define WMI_CHAN_INFO_AP_DISABLED BIT(9) +#define WMI_CHAN_INFO_DFS BIT(10) +#define WMI_CHAN_INFO_ALLOW_HT BIT(11) +#define WMI_CHAN_INFO_ALLOW_VHT BIT(12) +#define WMI_CHAN_INFO_CHAN_CHANGE_CAUSE_CSA BIT(13) +#define WMI_CHAN_INFO_HALF_RATE BIT(14) +#define WMI_CHAN_INFO_QUARTER_RATE BIT(15) +#define WMI_CHAN_INFO_DFS_FREQ2 BIT(16) +#define WMI_CHAN_INFO_ALLOW_HE BIT(17) + +#define WMI_CHAN_REG_INFO1_MIN_PWR GENMASK(7, 0) +#define WMI_CHAN_REG_INFO1_MAX_PWR GENMASK(15, 8) +#define WMI_CHAN_REG_INFO1_MAX_REG_PWR GENMASK(23, 16) +#define WMI_CHAN_REG_INFO1_REG_CLS GENMASK(31, 24) + +#define WMI_CHAN_REG_INFO2_ANT_MAX GENMASK(7, 0) +#define WMI_CHAN_REG_INFO2_MAX_TX_PWR GENMASK(15, 8) + +struct wmi_channel { + u32 tlv_header; + u32 mhz; + u32 band_center_freq1; + u32 band_center_freq2; + u32 info; + u32 reg_info_1; + u32 reg_info_2; +} __packed; + +struct wmi_mgmt_params { + void *tx_frame; + u16 frm_len; + u8 vdev_id; + u16 chanfreq; + void *pdata; + u16 desc_id; + u8 *macaddr; + void *qdf_ctx; +}; + +enum wmi_sta_ps_mode { + WMI_STA_PS_MODE_DISABLED = 0, + WMI_STA_PS_MODE_ENABLED = 1, +}; + +#define WMI_SMPS_MASK_LOWER_16BITS 0xFF +#define WMI_SMPS_MASK_UPPER_3BITS 0x7 +#define WMI_SMPS_PARAM_VALUE_SHIFT 29 + +#define ATH11K_WMI_FW_HANG_ASSERT_TYPE 1 +#define ATH11K_WMI_FW_HANG_DELAY 0 + +/* type, 0:unused 1: ASSERT 2: not respond detect command + * delay_time_ms, the simulate will delay time + */ + +struct wmi_force_fw_hang_cmd { + u32 tlv_header; + u32 type; + u32 delay_time_ms; +}; + +struct wmi_vdev_set_param_cmd { + u32 tlv_header; + u32 vdev_id; + u32 param_id; + u32 param_value; +} __packed; + +enum wmi_stats_id { + WMI_REQUEST_PEER_STAT = BIT(0), + WMI_REQUEST_AP_STAT = BIT(1), + WMI_REQUEST_PDEV_STAT = BIT(2), + WMI_REQUEST_VDEV_STAT = BIT(3), + WMI_REQUEST_BCNFLT_STAT = BIT(4), + WMI_REQUEST_VDEV_RATE_STAT = BIT(5), + WMI_REQUEST_INST_STAT = BIT(6), + WMI_REQUEST_MIB_STAT = BIT(7), + WMI_REQUEST_RSSI_PER_CHAIN_STAT = BIT(8), + WMI_REQUEST_CONGESTION_STAT = BIT(9), + WMI_REQUEST_PEER_EXTD_STAT = BIT(10), + WMI_REQUEST_BCN_STAT = BIT(11), + WMI_REQUEST_BCN_STAT_RESET = BIT(12), + WMI_REQUEST_PEER_EXTD2_STAT = BIT(13), +}; + +struct wmi_request_stats_cmd { + u32 tlv_header; + enum wmi_stats_id stats_id; + u32 vdev_id; + struct wmi_mac_addr peer_macaddr; + u32 pdev_id; +} __packed; + +#define WMI_BEACON_TX_BUFFER_SIZE 512 + +struct wmi_bcn_tmpl_cmd { + u32 tlv_header; + u32 vdev_id; + u32 tim_ie_offset; + u32 buf_len; + u32 csa_switch_count_offset; + u32 ext_csa_switch_count_offset; + u32 csa_event_bitmap; + u32 mbssid_ie_offset; + u32 esp_ie_offset; +} __packed; + +struct wmi_key_seq_counter { + u32 key_seq_counter_l; + u32 key_seq_counter_h; +} __packed; + +struct wmi_vdev_install_key_cmd { + u32 tlv_header; + u32 vdev_id; + struct wmi_mac_addr peer_macaddr; + u32 key_idx; + u32 key_flags; + u32 key_cipher; + struct wmi_key_seq_counter key_rsc_counter; + struct wmi_key_seq_counter key_global_rsc_counter; + struct wmi_key_seq_counter key_tsc_counter; + u8 wpi_key_rsc_counter[16]; + u8 wpi_key_tsc_counter[16]; + u32 key_len; + u32 key_txmic_len; + u32 key_rxmic_len; + u32 is_group_key_id_valid; + u32 group_key_id; + + /* Followed by key_data containing key followed by + * tx mic and then rx mic + */ +} __packed; + +struct wmi_vdev_install_key_arg { + u32 vdev_id; + const u8 *macaddr; + u32 key_idx; + u32 key_flags; + u32 key_cipher; + u32 key_len; + u32 key_txmic_len; + u32 key_rxmic_len; + u64 key_rsc_counter; + const void *key_data; +}; + +#define WMI_MAX_SUPPORTED_RATES 128 +#define WMI_HOST_MAX_HECAP_PHY_SIZE 3 +#define WMI_HOST_MAX_HE_RATE_SET 1 + +struct wmi_rate_set_arg { + u32 num_rates; + u8 rates[WMI_MAX_SUPPORTED_RATES]; +}; + +struct peer_assoc_params { + struct wmi_mac_addr peer_macaddr; + u32 vdev_id; + u32 peer_new_assoc; + u32 peer_associd; + u32 peer_flags; + u32 peer_caps; + u32 peer_listen_intval; + u32 peer_ht_caps; + u32 peer_max_mpdu; + u32 peer_mpdu_density; + u32 peer_rate_caps; + u32 peer_nss; + u32 peer_vht_caps; + u32 peer_phymode; + u32 peer_ht_info[2]; + struct wmi_rate_set_arg peer_legacy_rates; + struct wmi_rate_set_arg peer_ht_rates; + u32 rx_max_rate; + u32 rx_mcs_set; + u32 tx_max_rate; + u32 tx_mcs_set; + u8 vht_capable; + u32 tx_max_mcs_nss; + u32 peer_bw_rxnss_override; + bool is_pmf_enabled; + bool is_wme_set; + bool qos_flag; + bool apsd_flag; + bool ht_flag; + bool bw_40; + bool bw_80; + bool bw_160; + bool stbc_flag; + bool ldpc_flag; + bool static_mimops_flag; + bool dynamic_mimops_flag; + bool spatial_mux_flag; + bool vht_flag; + bool vht_ng_flag; + bool need_ptk_4_way; + bool need_gtk_2_way; + bool auth_flag; + bool safe_mode_enabled; + bool amsdu_disable; + /* Use common structure */ + u8 peer_mac[ETH_ALEN]; + + bool he_flag; + u32 peer_he_cap_macinfo[2]; + u32 peer_he_cap_macinfo_internal; + u32 peer_he_ops; + u32 peer_he_cap_phyinfo[WMI_HOST_MAX_HECAP_PHY_SIZE]; + u32 peer_he_mcs_count; + u32 peer_he_rx_mcs_set[WMI_HOST_MAX_HE_RATE_SET]; + u32 peer_he_tx_mcs_set[WMI_HOST_MAX_HE_RATE_SET]; + struct ath11k_ppe_threshold peer_ppet; +}; + +struct wmi_peer_assoc_complete_cmd { + u32 tlv_header; + struct wmi_mac_addr peer_macaddr; + u32 vdev_id; + u32 peer_new_assoc; + u32 peer_associd; + u32 peer_flags; + u32 peer_caps; + u32 peer_listen_intval; + u32 peer_ht_caps; + u32 peer_max_mpdu; + u32 peer_mpdu_density; + u32 peer_rate_caps; + u32 peer_nss; + u32 peer_vht_caps; + u32 peer_phymode; + u32 peer_ht_info[2]; + u32 num_peer_legacy_rates; + u32 num_peer_ht_rates; + u32 peer_bw_rxnss_override; + struct wmi_ppe_threshold peer_ppet; + u32 peer_he_cap_info; + u32 peer_he_ops; + u32 peer_he_cap_phy[WMI_MAX_HECAP_PHY_SIZE]; + u32 peer_he_mcs; + u32 peer_he_cap_info_ext; + u32 peer_he_cap_info_internal; +} __packed; + +struct wmi_stop_scan_cmd { + u32 tlv_header; + u32 requestor; + u32 scan_id; + u32 req_type; + u32 vdev_id; + u32 pdev_id; +}; + +struct scan_chan_list_params { + u32 pdev_id; + u16 nallchans; + struct channel_param ch_param[1]; +}; + +struct wmi_scan_chan_list_cmd { + u32 tlv_header; + u32 num_scan_chans; + u32 flags; + u32 pdev_id; +} __packed; + +#define WMI_MGMT_SEND_DOWNLD_LEN 64 + +#define WMI_TX_PARAMS_DWORD0_POWER GENMASK(7, 0) +#define WMI_TX_PARAMS_DWORD0_MCS_MASK GENMASK(19, 8) +#define WMI_TX_PARAMS_DWORD0_NSS_MASK GENMASK(27, 20) +#define WMI_TX_PARAMS_DWORD0_RETRY_LIMIT GENMASK(31, 28) + +#define WMI_TX_PARAMS_DWORD1_CHAIN_MASK GENMASK(7, 0) +#define WMI_TX_PARAMS_DWORD1_BW_MASK GENMASK(14, 8) +#define WMI_TX_PARAMS_DWORD1_PREAMBLE_TYPE GENMASK(19, 15) +#define WMI_TX_PARAMS_DWORD1_FRAME_TYPE BIT(20) +#define WMI_TX_PARAMS_DWORD1_RSVD GENMASK(31, 21) + +struct wmi_mgmt_send_params { + u32 tlv_header; + u32 tx_params_dword0; + u32 tx_params_dword1; +}; + +struct wmi_mgmt_send_cmd { + u32 tlv_header; + u32 vdev_id; + u32 desc_id; + u32 chanfreq; + u32 paddr_lo; + u32 paddr_hi; + u32 frame_len; + u32 buf_len; + u32 tx_params_valid; + + /* This TLV is followed by struct wmi_mgmt_frame */ + + /* Followed by struct wmi_mgmt_send_params */ +} __packed; + +struct wmi_sta_powersave_mode_cmd { + u32 tlv_header; + u32 vdev_id; + u32 sta_ps_mode; +}; + +struct wmi_sta_smps_force_mode_cmd { + u32 tlv_header; + u32 vdev_id; + u32 forced_mode; +}; + +struct wmi_sta_smps_param_cmd { + u32 tlv_header; + u32 vdev_id; + u32 param; + u32 value; +}; + +struct wmi_bcn_prb_info { + u32 tlv_header; + u32 caps; + u32 erp; +} __packed; + +enum { + WMI_PDEV_SUSPEND, + WMI_PDEV_SUSPEND_AND_DISABLE_INTR, +}; + +struct green_ap_ps_params { + u32 value; +}; + +struct wmi_pdev_green_ap_ps_enable_cmd_param { + u32 tlv_header; + u32 pdev_id; + u32 enable; +}; + +struct ap_ps_params { + u32 vdev_id; + u32 param; + u32 value; +}; + +struct vdev_set_params { + u32 if_id; + u32 param_id; + u32 param_value; +}; + +struct stats_request_params { + u32 stats_id; + u32 vdev_id; + u32 pdev_id; +}; + +enum set_init_cc_type { + WMI_COUNTRY_INFO_TYPE_ALPHA, + WMI_COUNTRY_INFO_TYPE_COUNTRY_CODE, + WMI_COUNTRY_INFO_TYPE_REGDOMAIN, +}; + +enum set_init_cc_flags { + INVALID_CC, + CC_IS_SET, + REGDMN_IS_SET, + ALPHA_IS_SET, +}; + +struct wmi_init_country_params { + union { + u16 country_code; + u16 regdom_id; + u8 alpha2[3]; + } cc_info; + enum set_init_cc_flags flags; +}; + +struct wmi_init_country_cmd { + u32 tlv_header; + u32 pdev_id; + u32 init_cc_type; + union { + u32 country_code; + u32 regdom_id; + u32 alpha2; + } cc_info; +} __packed; + +struct wmi_pdev_pktlog_filter_info { + u32 tlv_header; + struct wmi_mac_addr peer_macaddr; +} __packed; + +struct wmi_pdev_pktlog_filter_cmd { + u32 tlv_header; + u32 pdev_id; + u32 enable; + u32 filter_type; + u32 num_mac; +} __packed; + +enum ath11k_wmi_pktlog_enable { + ATH11K_WMI_PKTLOG_ENABLE_AUTO = 0, + ATH11K_WMI_PKTLOG_ENABLE_FORCE = 1, +}; + +struct wmi_pktlog_enable_cmd { + u32 tlv_header; + u32 pdev_id; + u32 evlist; /* WMI_PKTLOG_EVENT */ + u32 enable; +} __packed; + +struct wmi_pktlog_disable_cmd { + u32 tlv_header; + u32 pdev_id; +} __packed; + +#define DFS_PHYERR_UNIT_TEST_CMD 0 +#define DFS_UNIT_TEST_MODULE 0x2b +#define DFS_UNIT_TEST_TOKEN 0xAA + +enum dfs_test_args_idx { + DFS_TEST_CMDID = 0, + DFS_TEST_PDEV_ID, + DFS_TEST_RADAR_PARAM, + DFS_MAX_TEST_ARGS, +}; + +struct wmi_dfs_unit_test_arg { + u32 cmd_id; + u32 pdev_id; + u32 radar_param; +}; + +struct wmi_unit_test_cmd { + u32 tlv_header; + u32 vdev_id; + u32 module_id; + u32 num_args; + u32 diag_token; + /* Followed by test args*/ +} __packed; + +#define MAX_SUPPORTED_RATES 128 + +#define WMI_PEER_AUTH 0x00000001 +#define WMI_PEER_QOS 0x00000002 +#define WMI_PEER_NEED_PTK_4_WAY 0x00000004 +#define WMI_PEER_NEED_GTK_2_WAY 0x00000010 +#define WMI_PEER_HE 0x00000400 +#define WMI_PEER_APSD 0x00000800 +#define WMI_PEER_HT 0x00001000 +#define WMI_PEER_40MHZ 0x00002000 +#define WMI_PEER_STBC 0x00008000 +#define WMI_PEER_LDPC 0x00010000 +#define WMI_PEER_DYN_MIMOPS 0x00020000 +#define WMI_PEER_STATIC_MIMOPS 0x00040000 +#define WMI_PEER_SPATIAL_MUX 0x00200000 +#define WMI_PEER_VHT 0x02000000 +#define WMI_PEER_80MHZ 0x04000000 +#define WMI_PEER_PMF 0x08000000 +/* TODO: Place holder for WLAN_PEER_F_PS_PRESEND_REQUIRED = 0x10000000. + * Need to be cleaned up + */ +#define WMI_PEER_IS_P2P_CAPABLE 0x20000000 +#define WMI_PEER_160MHZ 0x40000000 +#define WMI_PEER_SAFEMODE_EN 0x80000000 + +struct beacon_tmpl_params { + u8 vdev_id; + u32 tim_ie_offset; + u32 tmpl_len; + u32 tmpl_len_aligned; + u32 csa_switch_count_offset; + u32 ext_csa_switch_count_offset; + u8 *frm; +}; + +struct wmi_rate_set { + u32 num_rates; + u32 rates[(MAX_SUPPORTED_RATES / 4) + 1]; +}; + +struct wmi_vht_rate_set { + u32 tlv_header; + u32 rx_max_rate; + u32 rx_mcs_set; + u32 tx_max_rate; + u32 tx_mcs_set; + u32 tx_max_mcs_nss; +} __packed; + +struct wmi_he_rate_set { + u32 tlv_header; + u32 rx_mcs_set; + u32 tx_mcs_set; +} __packed; + +#define MAX_REG_RULES 10 +#define REG_ALPHA2_LEN 2 + +enum wmi_start_event_param { + WMI_VDEV_START_RESP_EVENT = 0, + WMI_VDEV_RESTART_RESP_EVENT, +}; + +struct wmi_vdev_start_resp_event { + u32 vdev_id; + u32 requestor_id; + enum wmi_start_event_param resp_type; + u32 status; + u32 chain_mask; + u32 smps_mode; + union { + u32 mac_id; + u32 pdev_id; + }; + u32 cfgd_tx_streams; + u32 cfgd_rx_streams; +} __packed; + +/* VDEV start response status codes */ +enum wmi_vdev_start_resp_status_code { + WMI_VDEV_START_RESPONSE_STATUS_SUCCESS = 0, + WMI_VDEV_START_RESPONSE_INVALID_VDEVID = 1, + WMI_VDEV_START_RESPONSE_NOT_SUPPORTED = 2, + WMI_VDEV_START_RESPONSE_DFS_VIOLATION = 3, + WMI_VDEV_START_RESPONSE_INVALID_REGDOMAIN = 4, +}; + +; +enum cc_setting_code { + REG_SET_CC_STATUS_PASS = 0, + REG_CURRENT_ALPHA2_NOT_FOUND = 1, + REG_INIT_ALPHA2_NOT_FOUND = 2, + REG_SET_CC_CHANGE_NOT_ALLOWED = 3, + REG_SET_CC_STATUS_NO_MEMORY = 4, + REG_SET_CC_STATUS_FAIL = 5, +}; + +/* Regaulatory Rule Flags Passed by FW */ +#define REGULATORY_CHAN_DISABLED BIT(0) +#define REGULATORY_CHAN_NO_IR BIT(1) +#define REGULATORY_CHAN_RADAR BIT(3) +#define REGULATORY_CHAN_NO_OFDM BIT(6) +#define REGULATORY_CHAN_INDOOR_ONLY BIT(9) + +#define REGULATORY_CHAN_NO_HT40 BIT(4) +#define REGULATORY_CHAN_NO_80MHZ BIT(7) +#define REGULATORY_CHAN_NO_160MHZ BIT(8) +#define REGULATORY_CHAN_NO_20MHZ BIT(11) +#define REGULATORY_CHAN_NO_10MHZ BIT(12) + +enum { + WMI_REG_SET_CC_STATUS_PASS = 0, + WMI_REG_CURRENT_ALPHA2_NOT_FOUND = 1, + WMI_REG_INIT_ALPHA2_NOT_FOUND = 2, + WMI_REG_SET_CC_CHANGE_NOT_ALLOWED = 3, + WMI_REG_SET_CC_STATUS_NO_MEMORY = 4, + WMI_REG_SET_CC_STATUS_FAIL = 5, +}; + +struct cur_reg_rule { + u16 start_freq; + u16 end_freq; + u16 max_bw; + u8 reg_power; + u8 ant_gain; + u16 flags; +}; + +struct cur_regulatory_info { + enum cc_setting_code status_code; + u8 num_phy; + u8 phy_id; + u16 reg_dmn_pair; + u16 ctry_code; + u8 alpha2[REG_ALPHA2_LEN + 1]; + u32 dfs_region; + u32 phybitmap; + u32 min_bw_2g; + u32 max_bw_2g; + u32 min_bw_5g; + u32 max_bw_5g; + u32 num_2g_reg_rules; + u32 num_5g_reg_rules; + struct cur_reg_rule *reg_rules_2g_ptr; + struct cur_reg_rule *reg_rules_5g_ptr; +}; + +struct wmi_reg_chan_list_cc_event { + u32 status_code; + u32 phy_id; + u32 alpha2; + u32 num_phy; + u32 country_id; + u32 domain_code; + u32 dfs_region; + u32 phybitmap; + u32 min_bw_2g; + u32 max_bw_2g; + u32 min_bw_5g; + u32 max_bw_5g; + u32 num_2g_reg_rules; + u32 num_5g_reg_rules; +} __packed; + +struct wmi_regulatory_rule_struct { + u32 tlv_header; + u32 freq_info; + u32 bw_pwr_info; + u32 flag_info; +}; + +struct wmi_peer_delete_resp_event { + u32 vdev_id; + struct wmi_mac_addr peer_macaddr; +} __packed; + +struct wmi_bcn_tx_status_event { + u32 vdev_id; + u32 tx_status; +} __packed; + +struct wmi_vdev_stopped_event { + u32 vdev_id; +} __packed; + +struct wmi_pdev_bss_chan_info_event { + u32 pdev_id; + u32 freq; /* Units in MHz */ + u32 noise_floor; /* units are dBm */ + /* rx clear - how often the channel was unused */ + u32 rx_clear_count_low; + u32 rx_clear_count_high; + /* cycle count - elapsed time during measured period, in clock ticks */ + u32 cycle_count_low; + u32 cycle_count_high; + /* tx cycle count - elapsed time spent in tx, in clock ticks */ + u32 tx_cycle_count_low; + u32 tx_cycle_count_high; + /* rx cycle count - elapsed time spent in rx, in clock ticks */ + u32 rx_cycle_count_low; + u32 rx_cycle_count_high; + /*rx_cycle cnt for my bss in 64bits format */ + u32 rx_bss_cycle_count_low; + u32 rx_bss_cycle_count_high; +} __packed; + +#define WMI_VDEV_INSTALL_KEY_COMPL_STATUS_SUCCESS 0 + +struct wmi_vdev_install_key_compl_event { + u32 vdev_id; + struct wmi_mac_addr peer_macaddr; + u32 key_idx; + u32 key_flags; + u32 status; +} __packed; + +struct wmi_vdev_install_key_complete_arg { + u32 vdev_id; + const u8 *macaddr; + u32 key_idx; + u32 key_flags; + u32 status; +}; + +struct wmi_peer_assoc_conf_event { + u32 vdev_id; + struct wmi_mac_addr peer_macaddr; +} __packed; + +struct wmi_peer_assoc_conf_arg { + u32 vdev_id; + const u8 *macaddr; +}; + +/* + * PDEV statistics + */ +struct wmi_pdev_stats_base { + s32 chan_nf; + u32 tx_frame_count; /* Cycles spent transmitting frames */ + u32 rx_frame_count; /* Cycles spent receiving frames */ + u32 rx_clear_count; /* Total channel busy time, evidently */ + u32 cycle_count; /* Total on-channel time */ + u32 phy_err_count; + u32 chan_tx_pwr; +} __packed; + +struct wmi_pdev_stats_extra { + u32 ack_rx_bad; + u32 rts_bad; + u32 rts_good; + u32 fcs_bad; + u32 no_beacons; + u32 mib_int_count; +} __packed; + +struct wmi_pdev_stats_tx { + /* Num HTT cookies queued to dispatch list */ + s32 comp_queued; + + /* Num HTT cookies dispatched */ + s32 comp_delivered; + + /* Num MSDU queued to WAL */ + s32 msdu_enqued; + + /* Num MPDU queue to WAL */ + s32 mpdu_enqued; + + /* Num MSDUs dropped by WMM limit */ + s32 wmm_drop; + + /* Num Local frames queued */ + s32 local_enqued; + + /* Num Local frames done */ + s32 local_freed; + + /* Num queued to HW */ + s32 hw_queued; + + /* Num PPDU reaped from HW */ + s32 hw_reaped; + + /* Num underruns */ + s32 underrun; + + /* Num PPDUs cleaned up in TX abort */ + s32 tx_abort; + + /* Num MPDUs requed by SW */ + s32 mpdus_requed; + + /* excessive retries */ + u32 tx_ko; + + /* data hw rate code */ + u32 data_rc; + + /* Scheduler self triggers */ + u32 self_triggers; + + /* frames dropped due to excessive sw retries */ + u32 sw_retry_failure; + + /* illegal rate phy errors */ + u32 illgl_rate_phy_err; + + /* wal pdev continuous xretry */ + u32 pdev_cont_xretry; + + /* wal pdev tx timeouts */ + u32 pdev_tx_timeout; + + /* wal pdev resets */ + u32 pdev_resets; + + /* frames dropped due to non-availability of stateless TIDs */ + u32 stateless_tid_alloc_failure; + + /* PhY/BB underrun */ + u32 phy_underrun; + + /* MPDU is more than txop limit */ + u32 txop_ovf; +} __packed; + +struct wmi_pdev_stats_rx { + /* Cnts any change in ring routing mid-ppdu */ + s32 mid_ppdu_route_change; + + /* Total number of statuses processed */ + s32 status_rcvd; + + /* Extra frags on rings 0-3 */ + s32 r0_frags; + s32 r1_frags; + s32 r2_frags; + s32 r3_frags; + + /* MSDUs / MPDUs delivered to HTT */ + s32 htt_msdus; + s32 htt_mpdus; + + /* MSDUs / MPDUs delivered to local stack */ + s32 loc_msdus; + s32 loc_mpdus; + + /* AMSDUs that have more MSDUs than the status ring size */ + s32 oversize_amsdu; + + /* Number of PHY errors */ + s32 phy_errs; + + /* Number of PHY errors drops */ + s32 phy_err_drop; + + /* Number of mpdu errors - FCS, MIC, ENC etc. */ + s32 mpdu_errs; +} __packed; + +struct wmi_pdev_stats { + struct wmi_pdev_stats_base base; + struct wmi_pdev_stats_tx tx; + struct wmi_pdev_stats_rx rx; +} __packed; + +#define WLAN_MAX_AC 4 +#define MAX_TX_RATE_VALUES 10 +#define MAX_TX_RATE_VALUES 10 + +struct wmi_vdev_stats { + u32 vdev_id; + u32 beacon_snr; + u32 data_snr; + u32 num_tx_frames[WLAN_MAX_AC]; + u32 num_rx_frames; + u32 num_tx_frames_retries[WLAN_MAX_AC]; + u32 num_tx_frames_failures[WLAN_MAX_AC]; + u32 num_rts_fail; + u32 num_rts_success; + u32 num_rx_err; + u32 num_rx_discard; + u32 num_tx_not_acked; + u32 tx_rate_history[MAX_TX_RATE_VALUES]; + u32 beacon_rssi_history[MAX_TX_RATE_VALUES]; +} __packed; + +struct wmi_bcn_stats { + u32 vdev_id; + u32 tx_bcn_succ_cnt; + u32 tx_bcn_outage_cnt; +} __packed; + +struct wmi_stats_event { + u32 stats_id; + u32 num_pdev_stats; + u32 num_vdev_stats; + u32 num_peer_stats; + u32 num_bcnflt_stats; + u32 num_chan_stats; + u32 num_mib_stats; + u32 pdev_id; + u32 num_bcn_stats; + u32 num_peer_extd_stats; + u32 num_peer_extd2_stats; +} __packed; + +struct wmi_pdev_ctl_failsafe_chk_event { + u32 pdev_id; + u32 ctl_failsafe_status; +} __packed; + +struct wmi_pdev_csa_switch_ev { + u32 pdev_id; + u32 current_switch_count; + u32 num_vdevs; +} __packed; + +struct wmi_pdev_radar_ev { + u32 pdev_id; + u32 detection_mode; + u32 chan_freq; + u32 chan_width; + u32 detector_id; + u32 segment_id; + u32 timestamp; + u32 is_chirp; + s32 freq_offset; + s32 sidx; +} __packed; + +#define WMI_RX_STATUS_OK 0x00 +#define WMI_RX_STATUS_ERR_CRC 0x01 +#define WMI_RX_STATUS_ERR_DECRYPT 0x08 +#define WMI_RX_STATUS_ERR_MIC 0x10 +#define WMI_RX_STATUS_ERR_KEY_CACHE_MISS 0x20 + +#define WLAN_MGMT_TXRX_HOST_MAX_ANTENNA 4 + +struct mgmt_rx_event_params { + u32 channel; + u32 snr; + u8 rssi_ctl[WLAN_MGMT_TXRX_HOST_MAX_ANTENNA]; + u32 rate; + enum wmi_phy_mode phy_mode; + u32 buf_len; + int status; + u32 flags; + int rssi; + u32 tsf_delta; + u8 pdev_id; +}; + +#define ATH_MAX_ANTENNA 4 + +struct wmi_mgmt_rx_hdr { + u32 channel; + u32 snr; + u32 rate; + u32 phy_mode; + u32 buf_len; + u32 status; + u32 rssi_ctl[ATH_MAX_ANTENNA]; + u32 flags; + int rssi; + u32 tsf_delta; + u32 rx_tsf_l32; + u32 rx_tsf_u32; + u32 pdev_id; +} __packed; + +#define MAX_ANTENNA_EIGHT 8 + +struct wmi_rssi_ctl_ext { + u32 tlv_header; + u32 rssi_ctl_ext[MAX_ANTENNA_EIGHT - ATH_MAX_ANTENNA]; +}; + +struct wmi_mgmt_tx_compl_event { + u32 desc_id; + u32 status; + u32 pdev_id; +} __packed; + +struct wmi_scan_event { + u32 event_type; /* %WMI_SCAN_EVENT_ */ + u32 reason; /* %WMI_SCAN_REASON_ */ + u32 channel_freq; /* only valid for WMI_SCAN_EVENT_FOREIGN_CHANNEL */ + u32 scan_req_id; + u32 scan_id; + u32 vdev_id; + /* TSF Timestamp when the scan event (%WMI_SCAN_EVENT_) is completed + * In case of AP it is TSF of the AP vdev + * In case of STA connected state, this is the TSF of the AP + * In case of STA not connected, it will be the free running HW timer + */ + u32 tsf_timestamp; +} __packed; + +struct wmi_peer_sta_kickout_arg { + const u8 *mac_addr; +}; + +struct wmi_peer_sta_kickout_event { + struct wmi_mac_addr peer_macaddr; +} __packed; + +enum wmi_roam_reason { + WMI_ROAM_REASON_BETTER_AP = 1, + WMI_ROAM_REASON_BEACON_MISS = 2, + WMI_ROAM_REASON_LOW_RSSI = 3, + WMI_ROAM_REASON_SUITABLE_AP_FOUND = 4, + WMI_ROAM_REASON_HO_FAILED = 5, + + /* keep last */ + WMI_ROAM_REASON_MAX, +}; + +struct wmi_roam_event { + u32 vdev_id; + u32 reason; + u32 rssi; +} __packed; + +#define WMI_CHAN_INFO_START_RESP 0 +#define WMI_CHAN_INFO_END_RESP 1 + +struct wmi_chan_info_event { + u32 err_code; + u32 freq; + u32 cmd_flags; + u32 noise_floor; + u32 rx_clear_count; + u32 cycle_count; + u32 chan_tx_pwr_range; + u32 chan_tx_pwr_tp; + u32 rx_frame_count; + u32 my_bss_rx_cycle_count; + u32 rx_11b_mode_data_duration; + u32 tx_frame_cnt; + u32 mac_clk_mhz; + u32 vdev_id; +} __packed; + +struct ath11k_targ_cap { + u32 phy_capability; + u32 max_frag_entry; + u32 num_rf_chains; + u32 ht_cap_info; + u32 vht_cap_info; + u32 vht_supp_mcs; + u32 hw_min_tx_power; + u32 hw_max_tx_power; + u32 sys_cap_info; + u32 min_pkt_size_enable; + u32 max_bcn_ie_size; + u32 max_num_scan_channels; + u32 max_supported_macs; + u32 wmi_fw_sub_feat_caps; + u32 txrx_chainmask; + u32 default_dbs_hw_mode_index; + u32 num_msdu_desc; +}; + +enum wmi_vdev_type { + WMI_VDEV_TYPE_AP = 1, + WMI_VDEV_TYPE_STA = 2, + WMI_VDEV_TYPE_IBSS = 3, + WMI_VDEV_TYPE_MONITOR = 4, +}; + +enum wmi_vdev_subtype { + WMI_VDEV_SUBTYPE_NONE, + WMI_VDEV_SUBTYPE_P2P_DEVICE, + WMI_VDEV_SUBTYPE_P2P_CLIENT, + WMI_VDEV_SUBTYPE_P2P_GO, + WMI_VDEV_SUBTYPE_PROXY_STA, + WMI_VDEV_SUBTYPE_MESH_NON_11S, + WMI_VDEV_SUBTYPE_MESH_11S, +}; + +enum wmi_sta_powersave_param { + WMI_STA_PS_PARAM_RX_WAKE_POLICY = 0, + WMI_STA_PS_PARAM_TX_WAKE_THRESHOLD = 1, + WMI_STA_PS_PARAM_PSPOLL_COUNT = 2, + WMI_STA_PS_PARAM_INACTIVITY_TIME = 3, + WMI_STA_PS_PARAM_UAPSD = 4, +}; + +#define WMI_UAPSD_AC_TYPE_DELI 0 +#define WMI_UAPSD_AC_TYPE_TRIG 1 + +#define WMI_UAPSD_AC_BIT_MASK(ac, type) \ + ((type == WMI_UAPSD_AC_TYPE_DELI) ? \ + (1 << (ac << 1)) : (1 << ((ac << 1) + 1))) + +enum wmi_sta_ps_param_uapsd { + WMI_STA_PS_UAPSD_AC0_DELIVERY_EN = (1 << 0), + WMI_STA_PS_UAPSD_AC0_TRIGGER_EN = (1 << 1), + WMI_STA_PS_UAPSD_AC1_DELIVERY_EN = (1 << 2), + WMI_STA_PS_UAPSD_AC1_TRIGGER_EN = (1 << 3), + WMI_STA_PS_UAPSD_AC2_DELIVERY_EN = (1 << 4), + WMI_STA_PS_UAPSD_AC2_TRIGGER_EN = (1 << 5), + WMI_STA_PS_UAPSD_AC3_DELIVERY_EN = (1 << 6), + WMI_STA_PS_UAPSD_AC3_TRIGGER_EN = (1 << 7), +}; + +#define WMI_STA_UAPSD_MAX_INTERVAL_MSEC UINT_MAX + +struct wmi_sta_uapsd_auto_trig_param { + u32 wmm_ac; + u32 user_priority; + u32 service_interval; + u32 suspend_interval; + u32 delay_interval; +}; + +struct wmi_sta_uapsd_auto_trig_cmd_fixed_param { + u32 vdev_id; + struct wmi_mac_addr peer_macaddr; + u32 num_ac; +}; + +struct wmi_sta_uapsd_auto_trig_arg { + u32 wmm_ac; + u32 user_priority; + u32 service_interval; + u32 suspend_interval; + u32 delay_interval; +}; + +enum wmi_sta_ps_param_tx_wake_threshold { + WMI_STA_PS_TX_WAKE_THRESHOLD_NEVER = 0, + WMI_STA_PS_TX_WAKE_THRESHOLD_ALWAYS = 1, + + /* Values greater than one indicate that many TX attempts per beacon + * interval before the STA will wake up + */ +}; + +/* The maximum number of PS-Poll frames the FW will send in response to + * traffic advertised in TIM before waking up (by sending a null frame with PS + * = 0). Value 0 has a special meaning: there is no maximum count and the FW + * will send as many PS-Poll as are necessary to retrieve buffered BU. This + * parameter is used when the RX wake policy is + * WMI_STA_PS_RX_WAKE_POLICY_POLL_UAPSD and ignored when the RX wake + * policy is WMI_STA_PS_RX_WAKE_POLICY_WAKE. + */ +enum wmi_sta_ps_param_pspoll_count { + WMI_STA_PS_PSPOLL_COUNT_NO_MAX = 0, + /* Values greater than 0 indicate the maximum numer of PS-Poll frames + * FW will send before waking up. + */ +}; + +/* U-APSD configuration of peer station from (re)assoc request and TSPECs */ +enum wmi_ap_ps_param_uapsd { + WMI_AP_PS_UAPSD_AC0_DELIVERY_EN = (1 << 0), + WMI_AP_PS_UAPSD_AC0_TRIGGER_EN = (1 << 1), + WMI_AP_PS_UAPSD_AC1_DELIVERY_EN = (1 << 2), + WMI_AP_PS_UAPSD_AC1_TRIGGER_EN = (1 << 3), + WMI_AP_PS_UAPSD_AC2_DELIVERY_EN = (1 << 4), + WMI_AP_PS_UAPSD_AC2_TRIGGER_EN = (1 << 5), + WMI_AP_PS_UAPSD_AC3_DELIVERY_EN = (1 << 6), + WMI_AP_PS_UAPSD_AC3_TRIGGER_EN = (1 << 7), +}; + +/* U-APSD maximum service period of peer station */ +enum wmi_ap_ps_peer_param_max_sp { + WMI_AP_PS_PEER_PARAM_MAX_SP_UNLIMITED = 0, + WMI_AP_PS_PEER_PARAM_MAX_SP_2 = 1, + WMI_AP_PS_PEER_PARAM_MAX_SP_4 = 2, + WMI_AP_PS_PEER_PARAM_MAX_SP_6 = 3, + MAX_WMI_AP_PS_PEER_PARAM_MAX_SP, +}; + +enum wmi_ap_ps_peer_param { + /** Set uapsd configuration for a given peer. + * + * This include the delivery and trigger enabled state for each AC. + * The host MLME needs to set this based on AP capability and stations + * request Set in the association request received from the station. + * + * Lower 8 bits of the value specify the UAPSD configuration. + * + * (see enum wmi_ap_ps_param_uapsd) + * The default value is 0. + */ + WMI_AP_PS_PEER_PARAM_UAPSD = 0, + + /** + * Set the service period for a UAPSD capable station + * + * The service period from wme ie in the (re)assoc request frame. + * + * (see enum wmi_ap_ps_peer_param_max_sp) + */ + WMI_AP_PS_PEER_PARAM_MAX_SP = 1, + + /** Time in seconds for aging out buffered frames + * for STA in power save + */ + WMI_AP_PS_PEER_PARAM_AGEOUT_TIME = 2, + + /** Specify frame types that are considered SIFS + * RESP trigger frame + */ + WMI_AP_PS_PEER_PARAM_SIFS_RESP_FRMTYPE = 3, + + /** Specifies the trigger state of TID. + * Valid only for UAPSD frame type + */ + WMI_AP_PS_PEER_PARAM_SIFS_RESP_UAPSD = 4, + + /* Specifies the WNM sleep state of a STA */ + WMI_AP_PS_PEER_PARAM_WNM_SLEEP = 5, +}; + +#define DISABLE_SIFS_RESPONSE_TRIGGER 0 + +#define WMI_MAX_KEY_INDEX 3 +#define WMI_MAX_KEY_LEN 32 + +#define WMI_KEY_PAIRWISE 0x00 +#define WMI_KEY_GROUP 0x01 + +#define WMI_CIPHER_NONE 0x0 /* clear key */ +#define WMI_CIPHER_WEP 0x1 +#define WMI_CIPHER_TKIP 0x2 +#define WMI_CIPHER_AES_OCB 0x3 +#define WMI_CIPHER_AES_CCM 0x4 +#define WMI_CIPHER_WAPI 0x5 +#define WMI_CIPHER_CKIP 0x6 +#define WMI_CIPHER_AES_CMAC 0x7 +#define WMI_CIPHER_ANY 0x8 +#define WMI_CIPHER_AES_GCM 0x9 +#define WMI_CIPHER_AES_GMAC 0xa + +/* Value to disable fixed rate setting */ +#define WMI_FIXED_RATE_NONE (0xffff) + +#define ATH11K_RC_VERSION_OFFSET 28 +#define ATH11K_RC_PREAMBLE_OFFSET 8 +#define ATH11K_RC_NSS_OFFSET 5 + +#define ATH11K_HW_RATE_CODE(rate, nss, preamble) \ + ((1 << ATH11K_RC_VERSION_OFFSET) | \ + ((nss) << ATH11K_RC_NSS_OFFSET) | \ + ((preamble) << ATH11K_RC_PREAMBLE_OFFSET) | \ + (rate)) + +/* Preamble types to be used with VDEV fixed rate configuration */ +enum wmi_rate_preamble { + WMI_RATE_PREAMBLE_OFDM, + WMI_RATE_PREAMBLE_CCK, + WMI_RATE_PREAMBLE_HT, + WMI_RATE_PREAMBLE_VHT, + WMI_RATE_PREAMBLE_HE, +}; + +/** + * enum wmi_rtscts_prot_mode - Enable/Disable RTS/CTS and CTS2Self Protection. + * @WMI_RTS_CTS_DISABLED : RTS/CTS protection is disabled. + * @WMI_USE_RTS_CTS : RTS/CTS Enabled. + * @WMI_USE_CTS2SELF : CTS to self protection Enabled. + */ +enum wmi_rtscts_prot_mode { + WMI_RTS_CTS_DISABLED = 0, + WMI_USE_RTS_CTS = 1, + WMI_USE_CTS2SELF = 2, +}; + +/** + * enum wmi_rtscts_profile - Selection of RTS CTS profile along with enabling + * protection mode. + * @WMI_RTSCTS_FOR_NO_RATESERIES - Neither of rate-series should use RTS-CTS + * @WMI_RTSCTS_FOR_SECOND_RATESERIES - Only second rate-series will use RTS-CTS + * @WMI_RTSCTS_ACROSS_SW_RETRIES - Only the second rate-series will use RTS-CTS, + * but if there's a sw retry, both the rate + * series will use RTS-CTS. + * @WMI_RTSCTS_ERP - RTS/CTS used for ERP protection for every PPDU. + * @WMI_RTSCTS_FOR_ALL_RATESERIES - Enable RTS-CTS for all rate series. + */ +enum wmi_rtscts_profile { + WMI_RTSCTS_FOR_NO_RATESERIES = 0, + WMI_RTSCTS_FOR_SECOND_RATESERIES = 1, + WMI_RTSCTS_ACROSS_SW_RETRIES = 2, + WMI_RTSCTS_ERP = 3, + WMI_RTSCTS_FOR_ALL_RATESERIES = 4, +}; + +struct ath11k_hal_reg_cap { + u32 eeprom_rd; + u32 eeprom_rd_ext; + u32 regcap1; + u32 regcap2; + u32 wireless_modes; + u32 low_2ghz_chan; + u32 high_2ghz_chan; + u32 low_5ghz_chan; + u32 high_5ghz_chan; +}; + +struct ath11k_mem_chunk { + void *vaddr; + dma_addr_t paddr; + u32 len; + u32 req_id; +}; + +#define WMI_SKB_HEADROOM sizeof(struct wmi_cmd_hdr) + +enum wmi_sta_ps_param_rx_wake_policy { + WMI_STA_PS_RX_WAKE_POLICY_WAKE = 0, + WMI_STA_PS_RX_WAKE_POLICY_POLL_UAPSD = 1, +}; + +enum ath11k_hw_txrx_mode { + ATH11K_HW_TXRX_RAW = 0, + ATH11K_HW_TXRX_NATIVE_WIFI = 1, + ATH11K_HW_TXRX_ETHERNET = 2, +}; + +struct wmi_wmm_params { + u32 tlv_header; + u32 cwmin; + u32 cwmax; + u32 aifs; + u32 txoplimit; + u32 acm; + u32 no_ack; +} __packed; + +struct wmi_wmm_params_arg { + u8 acm; + u8 aifs; + u8 cwmin; + u8 cwmax; + u16 txop; + u8 no_ack; +}; + +struct wmi_vdev_set_wmm_params_cmd { + u32 tlv_header; + u32 vdev_id; + struct wmi_wmm_params wmm_params[4]; + u32 wmm_param_type; +} __packed; + +struct wmi_wmm_params_all_arg { + struct wmi_wmm_params_arg ac_be; + struct wmi_wmm_params_arg ac_bk; + struct wmi_wmm_params_arg ac_vi; + struct wmi_wmm_params_arg ac_vo; +}; + +struct target_resource_config { + u32 num_vdevs; + u32 num_peers; + u32 num_active_peers; + u32 num_offload_peers; + u32 num_offload_reorder_buffs; + u32 num_peer_keys; + u32 num_tids; + u32 ast_skid_limit; + u32 tx_chain_mask; + u32 rx_chain_mask; + u32 rx_timeout_pri[4]; + u32 rx_decap_mode; + u32 scan_max_pending_req; + u32 bmiss_offload_max_vdev; + u32 roam_offload_max_vdev; + u32 roam_offload_max_ap_profiles; + u32 num_mcast_groups; + u32 num_mcast_table_elems; + u32 mcast2ucast_mode; + u32 tx_dbg_log_size; + u32 num_wds_entries; + u32 dma_burst_size; + u32 mac_aggr_delim; + u32 rx_skip_defrag_timeout_dup_detection_check; + u32 vow_config; + u32 gtk_offload_max_vdev; + u32 num_msdu_desc; + u32 max_frag_entries; + u32 max_peer_ext_stats; + u32 smart_ant_cap; + u32 bk_minfree; + u32 be_minfree; + u32 vi_minfree; + u32 vo_minfree; + u32 rx_batchmode; + u32 tt_support; + u32 atf_config; + u32 iphdr_pad_config; + u32 qwrap_config:16, + alloc_frag_desc_for_data_pkt:16; + u32 num_tdls_vdevs; + u32 num_tdls_conn_table_entries; + u32 beacon_tx_offload_max_vdev; + u32 num_multicast_filter_entries; + u32 num_wow_filters; + u32 num_keep_alive_pattern; + u32 keep_alive_pattern_size; + u32 max_tdls_concurrent_sleep_sta; + u32 max_tdls_concurrent_buffer_sta; + u32 wmi_send_separate; + u32 num_ocb_vdevs; + u32 num_ocb_channels; + u32 num_ocb_schedules; + u32 num_ns_ext_tuples_cfg; + u32 bpf_instruction_size; + u32 max_bssid_rx_filters; + u32 use_pdev_id; + u32 peer_map_unmap_v2_support; +}; + +#define WMI_MAX_MEM_REQS 32 + +#define MAX_RADIOS 3 + +#define WMI_SERVICE_READY_TIMEOUT_HZ (5 * HZ) +#define WMI_SEND_TIMEOUT_HZ (3 * HZ) + +struct ath11k_wmi_base { + struct ath11k_base *ab; + struct ath11k_pdev_wmi wmi[MAX_RADIOS]; + enum ath11k_htc_ep_id wmi_endpoint_id[MAX_RADIOS]; + u32 max_msg_len[MAX_RADIOS]; + + struct completion service_ready; + struct completion unified_ready; + DECLARE_BITMAP(svc_map, WMI_MAX_EXT_SERVICE); + wait_queue_head_t tx_credits_wq; + const struct wmi_peer_flags_map *peer_flags; + u32 num_mem_chunks; + u32 rx_decap_mode; + struct wmi_host_mem_chunk mem_chunks[WMI_MAX_MEM_REQS]; + + enum wmi_host_hw_mode_config_type preferred_hw_mode; + struct target_resource_config wlan_resource_config; + + struct ath11k_targ_cap *targ_cap; +}; + +int ath11k_wmi_cmd_send(struct ath11k_pdev_wmi *wmi, struct sk_buff *skb, + u32 cmd_id); +struct sk_buff *ath11k_wmi_alloc_skb(struct ath11k_wmi_base *wmi_sc, u32 len); +int ath11k_wmi_mgmt_send(struct ath11k *ar, u32 vdev_id, u32 buf_id, + struct sk_buff *frame); +int ath11k_wmi_bcn_tmpl(struct ath11k *ar, u32 vdev_id, + struct ieee80211_mutable_offsets *offs, + struct sk_buff *bcn); +int ath11k_wmi_vdev_down(struct ath11k *ar, u8 vdev_id); +int ath11k_wmi_vdev_up(struct ath11k *ar, u32 vdev_id, u32 aid, + const u8 *bssid); +int ath11k_wmi_vdev_stop(struct ath11k *ar, u8 vdev_id); +int ath11k_wmi_vdev_start(struct ath11k *ar, struct wmi_vdev_start_req_arg *arg, + bool restart); +int ath11k_wmi_set_peer_param(struct ath11k *ar, const u8 *peer_addr, + u32 vdev_id, u32 param_id, u32 param_val); +int ath11k_wmi_pdev_set_param(struct ath11k *ar, u32 param_id, + u32 param_value, u8 pdev_id); +int ath11k_wmi_wait_for_unified_ready(struct ath11k_base *ab); +int ath11k_wmi_cmd_init(struct ath11k_base *ab); +int ath11k_wmi_wait_for_service_ready(struct ath11k_base *ab); +int ath11k_wmi_connect(struct ath11k_base *ab); +int ath11k_wmi_pdev_attach(struct ath11k_base *ab, + u8 pdev_id); +int ath11k_wmi_attach(struct ath11k_base *ab); +void ath11k_wmi_detach(struct ath11k_base *ab); +int ath11k_wmi_vdev_create(struct ath11k *ar, u8 *macaddr, + struct vdev_create_params *param); +int ath11k_wmi_peer_rx_reorder_queue_setup(struct ath11k *ar, int vdev_id, + const u8 *addr, dma_addr_t paddr, + u8 tid, u8 ba_window_size_valid, + u32 ba_window_size); +int ath11k_wmi_send_peer_create_cmd(struct ath11k *ar, + struct peer_create_params *param); +int ath11k_wmi_vdev_set_param_cmd(struct ath11k *ar, u32 vdev_id, + u32 param_id, u32 param_value); + +int ath11k_wmi_set_sta_ps_param(struct ath11k *ar, u32 vdev_id, + u32 param, u32 param_value); +int ath11k_wmi_force_fw_hang_cmd(struct ath11k *ar, u32 type, u32 delay_time_ms); +int ath11k_wmi_send_peer_delete_cmd(struct ath11k *ar, + const u8 *peer_addr, u8 vdev_id); +int ath11k_wmi_vdev_delete(struct ath11k *ar, u8 vdev_id); +void ath11k_wmi_start_scan_init(struct ath11k *ar, struct scan_req_params *arg); +int ath11k_wmi_send_scan_start_cmd(struct ath11k *ar, + struct scan_req_params *params); +int ath11k_wmi_send_scan_stop_cmd(struct ath11k *ar, + struct scan_cancel_param *param); +int ath11k_wmi_send_wmm_update_cmd_tlv(struct ath11k *ar, u32 vdev_id, + struct wmi_wmm_params_all_arg *param); +int ath11k_wmi_pdev_suspend(struct ath11k *ar, u32 suspend_opt, + u32 pdev_id); +int ath11k_wmi_pdev_resume(struct ath11k *ar, u32 pdev_id); + +int ath11k_wmi_send_peer_assoc_cmd(struct ath11k *ar, + struct peer_assoc_params *param); +int ath11k_wmi_vdev_install_key(struct ath11k *ar, + struct wmi_vdev_install_key_arg *arg); +int ath11k_wmi_pdev_bss_chan_info_request(struct ath11k *ar, + enum wmi_bss_chan_info_req_type type); +int ath11k_wmi_send_stats_request_cmd(struct ath11k *ar, + struct stats_request_params *param); +int ath11k_wmi_send_peer_flush_tids_cmd(struct ath11k *ar, + u8 peer_addr[ETH_ALEN], + struct peer_flush_params *param); +int ath11k_wmi_send_set_ap_ps_param_cmd(struct ath11k *ar, u8 *peer_addr, + struct ap_ps_params *param); +int ath11k_wmi_send_scan_chan_list_cmd(struct ath11k *ar, + struct scan_chan_list_params *chan_list); +int ath11k_wmi_send_dfs_phyerr_offload_enable_cmd(struct ath11k *ar, + u32 pdev_id); +int ath11k_wmi_send_bcn_offload_control_cmd(struct ath11k *ar, + u32 vdev_id, u32 bcn_ctrl_op); +int +ath11k_wmi_send_init_country_cmd(struct ath11k *ar, + struct wmi_init_country_params init_cc_param); +int ath11k_wmi_pdev_pktlog_enable(struct ath11k *ar, u32 pktlog_filter); +int ath11k_wmi_pdev_pktlog_disable(struct ath11k *ar); +int ath11k_wmi_pdev_peer_pktlog_filter(struct ath11k *ar, u8 *addr, u8 enable); +int +ath11k_wmi_rx_reord_queue_remove(struct ath11k *ar, + struct rx_reorder_queue_remove_params *param); +int ath11k_wmi_send_pdev_set_regdomain(struct ath11k *ar, + struct pdev_set_regdomain_params *param); +int ath11k_wmi_pull_fw_stats(struct ath11k_base *ab, struct sk_buff *skb, + struct ath11k_fw_stats *stats); +size_t ath11k_wmi_fw_stats_num_peers(struct list_head *head); +size_t ath11k_wmi_fw_stats_num_peers_extd(struct list_head *head); +size_t ath11k_wmi_fw_stats_num_vdevs(struct list_head *head); +void ath11k_wmi_fw_stats_fill(struct ath11k *ar, + struct ath11k_fw_stats *fw_stats, u32 stats_id, + char *buf); +int ath11k_wmi_simulate_radar(struct ath11k *ar); +#endif -- GitLab From 02fdd85da2ce9434dab7d5172284336046afeb17 Mon Sep 17 00:00:00 2001 From: Kalle Valo Date: Sat, 23 Nov 2019 10:01:16 +0200 Subject: [PATCH 0019/1437] MAINTAINERS: add ath11k Add an entry for ath11k to the MAINTAINERS file. Signed-off-by: John Crispin Signed-off-by: Kalle Valo --- MAINTAINERS | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index c0024b2961582..ff08668b1f303 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -13458,6 +13458,13 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/kvalo/ath.git S: Supported F: drivers/net/wireless/ath/ath10k/ +QUALCOMM ATHEROS ATH11K WIRELESS DRIVER +M: Kalle Valo +L: ath11k@lists.infradead.org +T: git git://git.kernel.org/pub/scm/linux/kernel/git/kvalo/ath.git +S: Supported +F: drivers/net/wireless/ath/ath11k/ + QUALCOMM ATHEROS ATH9K WIRELESS DRIVER M: QCA ath9k Development L: linux-wireless@vger.kernel.org -- GitLab From bc094709de0192a756c6946a7c89c543243ae609 Mon Sep 17 00:00:00 2001 From: Shuming Fan Date: Mon, 25 Nov 2019 17:19:40 +0800 Subject: [PATCH 0020/1437] ASoC: rt5682: fix i2c arbitration lost issue This patch modified the HW initial setting to fix i2c arbitration lost issue. Signed-off-by: Shuming Fan Link: https://lore.kernel.org/r/20191125091940.11953-1-shumingf@realtek.com Signed-off-by: Mark Brown --- sound/soc/codecs/rt5682.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/soc/codecs/rt5682.c b/sound/soc/codecs/rt5682.c index 35dcec135c8a6..9feba9a24501f 100644 --- a/sound/soc/codecs/rt5682.c +++ b/sound/soc/codecs/rt5682.c @@ -73,6 +73,7 @@ struct rt5682_priv { static const struct reg_sequence patch_list[] = { {RT5682_HP_IMP_SENS_CTRL_19, 0x1000}, {RT5682_DAC_ADC_DIG_VOL1, 0xa020}, + {RT5682_I2C_CTRL, 0x000f}, }; static const struct reg_default rt5682_reg[] = { @@ -2496,6 +2497,7 @@ static void rt5682_calibrate(struct rt5682_priv *rt5682) mutex_lock(&rt5682->calibrate_mutex); rt5682_reset(rt5682->regmap); + regmap_write(rt5682->regmap, RT5682_I2C_CTRL, 0x000f); regmap_write(rt5682->regmap, RT5682_PWR_ANLG_1, 0xa2af); usleep_range(15000, 20000); regmap_write(rt5682->regmap, RT5682_PWR_ANLG_1, 0xf2af); -- GitLab From 9c7315c9fca5de203538163cf42699bb10328902 Mon Sep 17 00:00:00 2001 From: Jarkko Nikula Date: Mon, 25 Nov 2019 14:51:59 +0200 Subject: [PATCH 0021/1437] spi: pxa2xx: Add support for Intel Jasper Lake LPSS SPI on Intel Jasper Lake is compatible with Intel Ice Lake which follows Intel Cannon Lake. Add PCI IDs of Jasper Lake. Signed-off-by: Jarkko Nikula Link: https://lore.kernel.org/r/20191125125159.15404-1-jarkko.nikula@linux.intel.com Signed-off-by: Mark Brown --- drivers/spi/spi-pxa2xx.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/spi/spi-pxa2xx.c b/drivers/spi/spi-pxa2xx.c index 9bc710c1b9e51..d58791d92c733 100644 --- a/drivers/spi/spi-pxa2xx.c +++ b/drivers/spi/spi-pxa2xx.c @@ -1443,6 +1443,10 @@ static const struct pci_device_id pxa2xx_spi_pci_compound_match[] = { { PCI_VDEVICE(INTEL, 0x4b2a), LPSS_BXT_SSP }, { PCI_VDEVICE(INTEL, 0x4b2b), LPSS_BXT_SSP }, { PCI_VDEVICE(INTEL, 0x4b37), LPSS_BXT_SSP }, + /* JSL */ + { PCI_VDEVICE(INTEL, 0x4daa), LPSS_CNL_SSP }, + { PCI_VDEVICE(INTEL, 0x4dab), LPSS_CNL_SSP }, + { PCI_VDEVICE(INTEL, 0x4dfb), LPSS_CNL_SSP }, /* APL */ { PCI_VDEVICE(INTEL, 0x5ac2), LPSS_BXT_SSP }, { PCI_VDEVICE(INTEL, 0x5ac4), LPSS_BXT_SSP }, -- GitLab From dbaf971c9cdf10843071a60dcafc1aaab3162354 Mon Sep 17 00:00:00 2001 From: Mike Snitzer Date: Tue, 26 Nov 2019 10:08:29 -0500 Subject: [PATCH 0022/1437] dm mpath: remove harmful bio-based optimization Removes the branching for edge-case where no SCSI device handler exists. The __map_bio_fast() method was far too limited, by only selecting a new pathgroup or path IFF there was a path failure, fix this be eliminating it in favor of __map_bio(). __map_bio()'s extra SCSI device handler specific MPATHF_PG_INIT_REQUIRED test is not in the fast path anyway. This change restores full path selector functionality for bio-based configurations that don't haave a SCSI device handler. But it should be noted that the path selectors do have an impact on performance for certain networks that are extremely fast (and don't require frequent switching). Fixes: 8d47e65948dd ("dm mpath: remove unnecessary NVMe branching in favor of scsi_dh checks") Cc: stable@vger.kernel.org Reported-by: Drew Hastings Suggested-by: Martin Wilck Signed-off-by: Mike Snitzer --- drivers/md/dm-mpath.c | 37 +------------------------------------ 1 file changed, 1 insertion(+), 36 deletions(-) diff --git a/drivers/md/dm-mpath.c b/drivers/md/dm-mpath.c index dbcc1e41cd57d..e0c32793c2487 100644 --- a/drivers/md/dm-mpath.c +++ b/drivers/md/dm-mpath.c @@ -599,45 +599,10 @@ static struct pgpath *__map_bio(struct multipath *m, struct bio *bio) return pgpath; } -static struct pgpath *__map_bio_fast(struct multipath *m, struct bio *bio) -{ - struct pgpath *pgpath; - unsigned long flags; - - /* Do we need to select a new pgpath? */ - /* - * FIXME: currently only switching path if no path (due to failure, etc) - * - which negates the point of using a path selector - */ - pgpath = READ_ONCE(m->current_pgpath); - if (!pgpath) - pgpath = choose_pgpath(m, bio->bi_iter.bi_size); - - if (!pgpath) { - if (test_bit(MPATHF_QUEUE_IF_NO_PATH, &m->flags)) { - /* Queue for the daemon to resubmit */ - spin_lock_irqsave(&m->lock, flags); - bio_list_add(&m->queued_bios, bio); - spin_unlock_irqrestore(&m->lock, flags); - queue_work(kmultipathd, &m->process_queued_bios); - - return ERR_PTR(-EAGAIN); - } - return NULL; - } - - return pgpath; -} - static int __multipath_map_bio(struct multipath *m, struct bio *bio, struct dm_mpath_io *mpio) { - struct pgpath *pgpath; - - if (!m->hw_handler_name) - pgpath = __map_bio_fast(m, bio); - else - pgpath = __map_bio(m, bio); + struct pgpath *pgpath = __map_bio(m, bio); if (IS_ERR(pgpath)) return DM_MAPIO_SUBMITTED; -- GitLab From 484e0d2b11e1fdd0d17702b282eb2ed56148385f Mon Sep 17 00:00:00 2001 From: Diego Calleja Date: Tue, 26 Nov 2019 14:00:31 +0200 Subject: [PATCH 0023/1437] dm: add dm-clone to the documentation index Fixes: 7431b7835f554 ("dm: add clone target") Signed-off-by: Diego Calleja Signed-off-by: Nikos Tsironis Signed-off-by: Mike Snitzer --- Documentation/admin-guide/device-mapper/index.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/admin-guide/device-mapper/index.rst b/Documentation/admin-guide/device-mapper/index.rst index c77c58b8f67b7..d8dec8911eb38 100644 --- a/Documentation/admin-guide/device-mapper/index.rst +++ b/Documentation/admin-guide/device-mapper/index.rst @@ -8,6 +8,7 @@ Device Mapper cache-policies cache delay + dm-clone dm-crypt dm-flakey dm-init -- GitLab From 38e1800275d3af607e4df92ff49dc2cf442586a4 Mon Sep 17 00:00:00 2001 From: Israel Rukshin Date: Sun, 24 Nov 2019 18:38:30 +0200 Subject: [PATCH 0024/1437] nvme-rdma: Avoid preallocating big SGL for data nvme_rdma_alloc_tagset() preallocates a big buffer for the IO SGL based on SG_CHUNK_SIZE. Modern DMA engines are often capable of dealing with very big segments so the SG_CHUNK_SIZE is often too big. SG_CHUNK_SIZE results in a static 4KB SGL allocation per command. If a controller has lots of deep queues, preallocation for the sg list can consume substantial amounts of memory. For nvme-rdma, nr_hw_queues can be 128 and each queue's depth 128. This means the resulting preallocation for the data SGL is 128*128*4K = 64MB per controller. Switch to runtime allocation for SGL for lists longer than 2 entries. This is the approach used by NVMe PCI so it should be reasonable for NVMeOF as well. Runtime SGL allocation has always been the case for the legacy I/O path so this is nothing new. The preallocated small SGL depends on SG_CHAIN so if the ARCH doesn't support SG_CHAIN, use only runtime allocation for the SGL. We didn't notice of a performance degradation, since for small IOs we'll use the inline SG and for the bigger IOs the allocation of a bigger SGL from slab is fast enough. Suggested-by: Christoph Hellwig Reviewed-by: Max Gurtovoy Reviewed-by: Christoph Hellwig Signed-off-by: Israel Rukshin Signed-off-by: Keith Busch --- drivers/nvme/host/nvme.h | 6 ++++++ drivers/nvme/host/rdma.c | 10 +++++----- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/drivers/nvme/host/nvme.h b/drivers/nvme/host/nvme.h index 3b9cbe0668fa4..1024fec7914c4 100644 --- a/drivers/nvme/host/nvme.h +++ b/drivers/nvme/host/nvme.h @@ -28,6 +28,12 @@ extern unsigned int admin_timeout; #define NVME_DEFAULT_KATO 5 #define NVME_KATO_GRACE 10 +#ifdef CONFIG_ARCH_NO_SG_CHAIN +#define NVME_INLINE_SG_CNT 0 +#else +#define NVME_INLINE_SG_CNT 2 +#endif + extern struct workqueue_struct *nvme_wq; extern struct workqueue_struct *nvme_reset_wq; extern struct workqueue_struct *nvme_delete_wq; diff --git a/drivers/nvme/host/rdma.c b/drivers/nvme/host/rdma.c index dce59459ed41b..2a47c6c5007e1 100644 --- a/drivers/nvme/host/rdma.c +++ b/drivers/nvme/host/rdma.c @@ -731,7 +731,7 @@ static struct blk_mq_tag_set *nvme_rdma_alloc_tagset(struct nvme_ctrl *nctrl, set->reserved_tags = 2; /* connect + keep-alive */ set->numa_node = nctrl->numa_node; set->cmd_size = sizeof(struct nvme_rdma_request) + - SG_CHUNK_SIZE * sizeof(struct scatterlist); + NVME_INLINE_SG_CNT * sizeof(struct scatterlist); set->driver_data = ctrl; set->nr_hw_queues = 1; set->timeout = ADMIN_TIMEOUT; @@ -745,7 +745,7 @@ static struct blk_mq_tag_set *nvme_rdma_alloc_tagset(struct nvme_ctrl *nctrl, set->numa_node = nctrl->numa_node; set->flags = BLK_MQ_F_SHOULD_MERGE; set->cmd_size = sizeof(struct nvme_rdma_request) + - SG_CHUNK_SIZE * sizeof(struct scatterlist); + NVME_INLINE_SG_CNT * sizeof(struct scatterlist); set->driver_data = ctrl; set->nr_hw_queues = nctrl->queue_count - 1; set->timeout = NVME_IO_TIMEOUT; @@ -1160,7 +1160,7 @@ static void nvme_rdma_unmap_data(struct nvme_rdma_queue *queue, } ib_dma_unmap_sg(ibdev, req->sg_table.sgl, req->nents, rq_dma_dir(rq)); - sg_free_table_chained(&req->sg_table, SG_CHUNK_SIZE); + sg_free_table_chained(&req->sg_table, NVME_INLINE_SG_CNT); } static int nvme_rdma_set_sg_null(struct nvme_command *c) @@ -1276,7 +1276,7 @@ static int nvme_rdma_map_data(struct nvme_rdma_queue *queue, req->sg_table.sgl = req->first_sgl; ret = sg_alloc_table_chained(&req->sg_table, blk_rq_nr_phys_segments(rq), req->sg_table.sgl, - SG_CHUNK_SIZE); + NVME_INLINE_SG_CNT); if (ret) return -ENOMEM; @@ -1314,7 +1314,7 @@ out: out_unmap_sg: ib_dma_unmap_sg(ibdev, req->sg_table.sgl, req->nents, rq_dma_dir(rq)); out_free_table: - sg_free_table_chained(&req->sg_table, SG_CHUNK_SIZE); + sg_free_table_chained(&req->sg_table, NVME_INLINE_SG_CNT); return ret; } -- GitLab From b1ae1a238900474a9f51431c0f7f169ade1faa19 Mon Sep 17 00:00:00 2001 From: Israel Rukshin Date: Sun, 24 Nov 2019 18:38:31 +0200 Subject: [PATCH 0025/1437] nvme-fc: Avoid preallocating big SGL for data nvme_fc_create_io_queues() preallocates a big buffer for the IO SGL based on SG_CHUNK_SIZE. Modern DMA engines are often capable of dealing with very big segments so the SG_CHUNK_SIZE is often too big. SG_CHUNK_SIZE results in a static 4KB SGL allocation per command. If a controller has lots of deep queues, preallocation for the sg list can consume substantial amounts of memory. For nvme-fc, nr_hw_queues can be 128 and each queue's depth 128. This means the resulting preallocation for the data SGL is 128*128*4K = 64MB per controller. Switch to runtime allocation for SGL for lists longer than 2 entries. This is the approach used by NVMe PCI so it should be reasonable for NVMeOF as well. Runtime SGL allocation has always been the case for the legacy I/O path so this is nothing new. Reviewed-by: Max Gurtovoy Reviewed-by: James Smart Signed-off-by: Israel Rukshin Signed-off-by: Keith Busch --- drivers/nvme/host/fc.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/nvme/host/fc.c b/drivers/nvme/host/fc.c index 679a721ae229a..13cb00e56cac3 100644 --- a/drivers/nvme/host/fc.c +++ b/drivers/nvme/host/fc.c @@ -95,7 +95,7 @@ struct nvme_fc_fcp_op { struct nvme_fcp_op_w_sgl { struct nvme_fc_fcp_op op; - struct scatterlist sgl[SG_CHUNK_SIZE]; + struct scatterlist sgl[NVME_INLINE_SG_CNT]; uint8_t priv[0]; }; @@ -2141,7 +2141,7 @@ nvme_fc_map_data(struct nvme_fc_ctrl *ctrl, struct request *rq, freq->sg_table.sgl = freq->first_sgl; ret = sg_alloc_table_chained(&freq->sg_table, blk_rq_nr_phys_segments(rq), freq->sg_table.sgl, - SG_CHUNK_SIZE); + NVME_INLINE_SG_CNT); if (ret) return -ENOMEM; @@ -2150,7 +2150,7 @@ nvme_fc_map_data(struct nvme_fc_ctrl *ctrl, struct request *rq, freq->sg_cnt = fc_dma_map_sg(ctrl->lport->dev, freq->sg_table.sgl, op->nents, rq_dma_dir(rq)); if (unlikely(freq->sg_cnt <= 0)) { - sg_free_table_chained(&freq->sg_table, SG_CHUNK_SIZE); + sg_free_table_chained(&freq->sg_table, NVME_INLINE_SG_CNT); freq->sg_cnt = 0; return -EFAULT; } @@ -2173,7 +2173,7 @@ nvme_fc_unmap_data(struct nvme_fc_ctrl *ctrl, struct request *rq, fc_dma_unmap_sg(ctrl->lport->dev, freq->sg_table.sgl, op->nents, rq_dma_dir(rq)); - sg_free_table_chained(&freq->sg_table, SG_CHUNK_SIZE); + sg_free_table_chained(&freq->sg_table, NVME_INLINE_SG_CNT); freq->sg_cnt = 0; } -- GitLab From 52e6d8ed16fdf9f1d2923a2b036222a5ac834b1d Mon Sep 17 00:00:00 2001 From: Israel Rukshin Date: Sun, 24 Nov 2019 18:38:32 +0200 Subject: [PATCH 0026/1437] nvmet-loop: Avoid preallocating big SGL for data nvme_loop_create_io_queues() preallocates a big buffer for the IO SGL based on SG_CHUNK_SIZE. Modern DMA engines are often capable of dealing with very big segments so the SG_CHUNK_SIZE is often too big. SG_CHUNK_SIZE results in a static 4KB SGL allocation per command. If a controller has lots of deep queues, preallocation for the sg list can consume substantial amounts of memory. For nvmet-loop, nr_hw_queues can be 128 and each queue's depth 128. This means the resulting preallocation for the data SGL is 128*128*4K = 64MB per controller. Switch to runtime allocation for SGL for lists longer than 2 entries. This is the approach used by NVMe PCI so it should be reasonable for NVMeOF as well. Runtime SGL allocation has always been the case for the legacy I/O path so this is nothing new. Tested-by: Chaitanya Kulkarni Reviewed-by: Christoph Hellwig Reviewed-by: Chaitanya Kulkarni Reviewed-by: Max Gurtovoy Signed-off-by: Israel Rukshin Signed-off-by: Keith Busch --- drivers/nvme/target/loop.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/nvme/target/loop.c b/drivers/nvme/target/loop.c index a758bb3d5dd49..4df4ebde208a0 100644 --- a/drivers/nvme/target/loop.c +++ b/drivers/nvme/target/loop.c @@ -76,7 +76,7 @@ static void nvme_loop_complete_rq(struct request *req) { struct nvme_loop_iod *iod = blk_mq_rq_to_pdu(req); - sg_free_table_chained(&iod->sg_table, SG_CHUNK_SIZE); + sg_free_table_chained(&iod->sg_table, NVME_INLINE_SG_CNT); nvme_complete_rq(req); } @@ -156,7 +156,7 @@ static blk_status_t nvme_loop_queue_rq(struct blk_mq_hw_ctx *hctx, iod->sg_table.sgl = iod->first_sgl; if (sg_alloc_table_chained(&iod->sg_table, blk_rq_nr_phys_segments(req), - iod->sg_table.sgl, SG_CHUNK_SIZE)) { + iod->sg_table.sgl, NVME_INLINE_SG_CNT)) { nvme_cleanup_cmd(req); return BLK_STS_RESOURCE; } @@ -342,7 +342,7 @@ static int nvme_loop_configure_admin_queue(struct nvme_loop_ctrl *ctrl) ctrl->admin_tag_set.reserved_tags = 2; /* connect + keep-alive */ ctrl->admin_tag_set.numa_node = NUMA_NO_NODE; ctrl->admin_tag_set.cmd_size = sizeof(struct nvme_loop_iod) + - SG_CHUNK_SIZE * sizeof(struct scatterlist); + NVME_INLINE_SG_CNT * sizeof(struct scatterlist); ctrl->admin_tag_set.driver_data = ctrl; ctrl->admin_tag_set.nr_hw_queues = 1; ctrl->admin_tag_set.timeout = ADMIN_TIMEOUT; @@ -516,7 +516,7 @@ static int nvme_loop_create_io_queues(struct nvme_loop_ctrl *ctrl) ctrl->tag_set.numa_node = NUMA_NO_NODE; ctrl->tag_set.flags = BLK_MQ_F_SHOULD_MERGE; ctrl->tag_set.cmd_size = sizeof(struct nvme_loop_iod) + - SG_CHUNK_SIZE * sizeof(struct scatterlist); + NVME_INLINE_SG_CNT * sizeof(struct scatterlist); ctrl->tag_set.driver_data = ctrl; ctrl->tag_set.nr_hw_queues = ctrl->ctrl.queue_count - 1; ctrl->tag_set.timeout = NVME_IO_TIMEOUT; -- GitLab From 863fbae929c7a5b64e96b8a3ffb34a29eefb9f8f Mon Sep 17 00:00:00 2001 From: James Smart Date: Thu, 14 Nov 2019 15:15:26 -0800 Subject: [PATCH 0027/1437] nvme_fc: add module to ops template to allow module references In nvme-fc: it's possible to have connected active controllers and as no references are taken on the LLDD, the LLDD can be unloaded. The controller would enter a reconnect state and as long as the LLDD resumed within the reconnect timeout, the controller would resume. But if a namespace on the controller is the root device, allowing the driver to unload can be problematic. To reload the driver, it may require new io to the boot device, and as it's no longer connected we get into a catch-22 that eventually fails, and the system locks up. Fix this issue by taking a module reference for every connected controller (which is what the core layer did to the transport module). Reference is cleared when the controller is removed. Acked-by: Himanshu Madhani Reviewed-by: Christoph Hellwig Signed-off-by: James Smart Signed-off-by: Keith Busch --- drivers/nvme/host/fc.c | 14 ++++++++++++-- drivers/nvme/target/fcloop.c | 1 + drivers/scsi/lpfc/lpfc_nvme.c | 2 ++ drivers/scsi/qla2xxx/qla_nvme.c | 1 + include/linux/nvme-fc-driver.h | 4 ++++ 5 files changed, 20 insertions(+), 2 deletions(-) diff --git a/drivers/nvme/host/fc.c b/drivers/nvme/host/fc.c index 13cb00e56cac3..d61439f8f5a9a 100644 --- a/drivers/nvme/host/fc.c +++ b/drivers/nvme/host/fc.c @@ -342,7 +342,8 @@ nvme_fc_register_localport(struct nvme_fc_port_info *pinfo, !template->ls_req || !template->fcp_io || !template->ls_abort || !template->fcp_abort || !template->max_hw_queues || !template->max_sgl_segments || - !template->max_dif_sgl_segments || !template->dma_boundary) { + !template->max_dif_sgl_segments || !template->dma_boundary || + !template->module) { ret = -EINVAL; goto out_reghost_failed; } @@ -2015,6 +2016,7 @@ nvme_fc_ctrl_free(struct kref *ref) { struct nvme_fc_ctrl *ctrl = container_of(ref, struct nvme_fc_ctrl, ref); + struct nvme_fc_lport *lport = ctrl->lport; unsigned long flags; if (ctrl->ctrl.tagset) { @@ -2041,6 +2043,7 @@ nvme_fc_ctrl_free(struct kref *ref) if (ctrl->ctrl.opts) nvmf_free_options(ctrl->ctrl.opts); kfree(ctrl); + module_put(lport->ops->module); } static void @@ -3059,10 +3062,15 @@ nvme_fc_init_ctrl(struct device *dev, struct nvmf_ctrl_options *opts, goto out_fail; } + if (!try_module_get(lport->ops->module)) { + ret = -EUNATCH; + goto out_free_ctrl; + } + idx = ida_simple_get(&nvme_fc_ctrl_cnt, 0, 0, GFP_KERNEL); if (idx < 0) { ret = -ENOSPC; - goto out_free_ctrl; + goto out_mod_put; } ctrl->ctrl.opts = opts; @@ -3215,6 +3223,8 @@ out_free_queues: out_free_ida: put_device(ctrl->dev); ida_simple_remove(&nvme_fc_ctrl_cnt, ctrl->cnum); +out_mod_put: + module_put(lport->ops->module); out_free_ctrl: kfree(ctrl); out_fail: diff --git a/drivers/nvme/target/fcloop.c b/drivers/nvme/target/fcloop.c index b50b53db37462..1c50af6219f32 100644 --- a/drivers/nvme/target/fcloop.c +++ b/drivers/nvme/target/fcloop.c @@ -850,6 +850,7 @@ fcloop_targetport_delete(struct nvmet_fc_target_port *targetport) #define FCLOOP_DMABOUND_4G 0xFFFFFFFF static struct nvme_fc_port_template fctemplate = { + .module = THIS_MODULE, .localport_delete = fcloop_localport_delete, .remoteport_delete = fcloop_remoteport_delete, .create_queue = fcloop_create_queue, diff --git a/drivers/scsi/lpfc/lpfc_nvme.c b/drivers/scsi/lpfc/lpfc_nvme.c index a227e36cbdc2b..8e0f03ef346b6 100644 --- a/drivers/scsi/lpfc/lpfc_nvme.c +++ b/drivers/scsi/lpfc/lpfc_nvme.c @@ -1976,6 +1976,8 @@ out_unlock: /* Declare and initialization an instance of the FC NVME template. */ static struct nvme_fc_port_template lpfc_nvme_template = { + .module = THIS_MODULE, + /* initiator-based functions */ .localport_delete = lpfc_nvme_localport_delete, .remoteport_delete = lpfc_nvme_remoteport_delete, diff --git a/drivers/scsi/qla2xxx/qla_nvme.c b/drivers/scsi/qla2xxx/qla_nvme.c index 6cc19e060afc3..6e4d71302534b 100644 --- a/drivers/scsi/qla2xxx/qla_nvme.c +++ b/drivers/scsi/qla2xxx/qla_nvme.c @@ -610,6 +610,7 @@ static void qla_nvme_remoteport_delete(struct nvme_fc_remote_port *rport) } static struct nvme_fc_port_template qla_nvme_fc_transport = { + .module = THIS_MODULE, .localport_delete = qla_nvme_localport_delete, .remoteport_delete = qla_nvme_remoteport_delete, .create_queue = qla_nvme_alloc_queue, diff --git a/include/linux/nvme-fc-driver.h b/include/linux/nvme-fc-driver.h index 10f81629b9cec..6d0d70f3219c5 100644 --- a/include/linux/nvme-fc-driver.h +++ b/include/linux/nvme-fc-driver.h @@ -270,6 +270,8 @@ struct nvme_fc_remote_port { * * Host/Initiator Transport Entrypoints/Parameters: * + * @module: The LLDD module using the interface + * * @localport_delete: The LLDD initiates deletion of a localport via * nvme_fc_deregister_localport(). However, the teardown is * asynchronous. This routine is called upon the completion of the @@ -383,6 +385,8 @@ struct nvme_fc_remote_port { * Value is Mandatory. Allowed to be zero. */ struct nvme_fc_port_template { + struct module *module; + /* initiator-based functions */ void (*localport_delete)(struct nvme_fc_local_port *); void (*remoteport_delete)(struct nvme_fc_remote_port *); -- GitLab From a8157ff3607eccb21efd4eb2ca46ff0cc488d12d Mon Sep 17 00:00:00 2001 From: James Smart Date: Thu, 21 Nov 2019 09:58:10 -0800 Subject: [PATCH 0028/1437] nvme: add error message on mismatching controller ids We've seen a few devices that return different controller id's to the Fabric Connect command vs the Identify(controller) command. It's currently hard to identify this failure by existing error messages. It comes across as a (re)connect attempt in the transport that fails with a -22 (-EINVAL) status. The issue is compounded by older kernels not having the controller id check or had the identify command overwrite the fabrics controller id value before it checked. Both resulted in cases where the devices appeared fine until more recent kernels. Clarify the reject by adding an error message on controller id mismatches. Reviewed-by: Christoph Hellwig Reviewed-by: Hannes Reinecke Reviewed-by: Ewan D. Milne Signed-off-by: James Smart Signed-off-by: Keith Busch --- drivers/nvme/host/core.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/nvme/host/core.c b/drivers/nvme/host/core.c index 8e8527408db3b..e6ee34376c5ea 100644 --- a/drivers/nvme/host/core.c +++ b/drivers/nvme/host/core.c @@ -2862,6 +2862,10 @@ int nvme_init_identify(struct nvme_ctrl *ctrl) * admin connect */ if (ctrl->cntlid != le16_to_cpu(id->cntlid)) { + dev_err(ctrl->device, + "Mismatching cntlid: Connect %u vs Identify " + "%u, rejecting\n", + ctrl->cntlid, le16_to_cpu(id->cntlid)); ret = -EINVAL; goto out_free; } -- GitLab From c80b36cd9576efa861a080b05382856173a02ae9 Mon Sep 17 00:00:00 2001 From: Edmund Nadolski Date: Mon, 25 Nov 2019 09:06:12 -0700 Subject: [PATCH 0029/1437] nvme: else following return is not needed Remove unnecessary keyword in nvme_create_queue(). Reviewed-by: Christoph Hellwig Signed-off-by: Edmund Nadolski Signed-off-by: Keith Busch --- drivers/nvme/host/pci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/nvme/host/pci.c b/drivers/nvme/host/pci.c index dcaad5831cee7..9d307593b94f8 100644 --- a/drivers/nvme/host/pci.c +++ b/drivers/nvme/host/pci.c @@ -1549,7 +1549,7 @@ static int nvme_create_queue(struct nvme_queue *nvmeq, int qid, bool polled) result = adapter_alloc_sq(dev, qid, nvmeq); if (result < 0) return result; - else if (result) + if (result) goto release_cq; nvmeq->cq_vector = vector; -- GitLab From c869e494ef8b5846d9ba91f1e922c23cd444f0c1 Mon Sep 17 00:00:00 2001 From: James Smart Date: Thu, 21 Nov 2019 09:59:37 -0800 Subject: [PATCH 0030/1437] nvme-fc: fix double-free scenarios on hw queues If an error occurs on one of the ios used for creating an association, the creating routine has error paths that are invoked by the command failure and the error paths will free up the controller resources created to that point. But... the io was ultimately determined by an asynchronous completion routine that detected the error and which unconditionally invokes the error_recovery path which calls delete_association. Delete association deletes all outstanding io then tears down the controller resources. So the create_association thread can be running in parallel with the error_recovery thread. What was seen was the LLDD received a call to delete a queue, causing the LLDD to do a free of a resource, then the transport called the delete queue again causing the driver to repeat the free call. The second free routine corrupted the allocator. The transport shouldn't be making the duplicate call, and the delete queue is just one of the resources being freed. To fix, it is realized that the create_association path is completely serialized with one command at a time. So the failed io completion will always be seen by the create_association path and as of the failure, there are no ios to terminate and there is no reason to be manipulating queue freeze states, etc. The serialized condition stays true until the controller is transitioned to the LIVE state. Thus the fix is to change the error recovery path to check the controller state and only invoke the teardown path if not already in the CONNECTING state. Reviewed-by: Himanshu Madhani Reviewed-by: Ewan D. Milne Signed-off-by: James Smart Signed-off-by: Keith Busch --- drivers/nvme/host/fc.c | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/drivers/nvme/host/fc.c b/drivers/nvme/host/fc.c index d61439f8f5a9a..5a70ac395d53a 100644 --- a/drivers/nvme/host/fc.c +++ b/drivers/nvme/host/fc.c @@ -2913,10 +2913,22 @@ nvme_fc_reconnect_or_delete(struct nvme_fc_ctrl *ctrl, int status) static void __nvme_fc_terminate_io(struct nvme_fc_ctrl *ctrl) { - nvme_stop_keep_alive(&ctrl->ctrl); + /* + * if state is connecting - the error occurred as part of a + * reconnect attempt. The create_association error paths will + * clean up any outstanding io. + * + * if it's a different state - ensure all pending io is + * terminated. Given this can delay while waiting for the + * aborted io to return, we recheck adapter state below + * before changing state. + */ + if (ctrl->ctrl.state != NVME_CTRL_CONNECTING) { + nvme_stop_keep_alive(&ctrl->ctrl); - /* will block will waiting for io to terminate */ - nvme_fc_delete_association(ctrl); + /* will block will waiting for io to terminate */ + nvme_fc_delete_association(ctrl); + } if (ctrl->ctrl.state != NVME_CTRL_CONNECTING && !nvme_change_ctrl_state(&ctrl->ctrl, NVME_CTRL_CONNECTING)) -- GitLab From 756ae8f237e19a014a1c20ad5a51b0686463d1f7 Mon Sep 17 00:00:00 2001 From: Yu-Hsuan Hsu Date: Tue, 26 Nov 2019 15:54:24 +0800 Subject: [PATCH 0031/1437] ASoC: AMD: Enable clk in startup intead of hw_params Some usages only call startup and shutdown without setting hw_params (e.g. arecord --dump-hw-params). If we don't enable clk in startup, it will cause ref count error because the clk will be disabled in shutdown. For this reason, we should move enabling clk from hw_params to startup. In addition, the hw_params is fixed in this driver(48000 rate, 2 channels, S16_LE format) so we don't need to change the clk rate after the hw_params is set. Signed-off-by: Yu-Hsuan Hsu Acked-by: Akshu Agrawal Link: https://lore.kernel.org/r/20191126075424.80668-1-yuhsuan@chromium.org Signed-off-by: Mark Brown --- sound/soc/amd/acp-da7219-max98357a.c | 46 +++++++++------------------- 1 file changed, 14 insertions(+), 32 deletions(-) diff --git a/sound/soc/amd/acp-da7219-max98357a.c b/sound/soc/amd/acp-da7219-max98357a.c index f4ee6798154af..7a5621e5e2330 100644 --- a/sound/soc/amd/acp-da7219-max98357a.c +++ b/sound/soc/amd/acp-da7219-max98357a.c @@ -96,14 +96,19 @@ static int cz_da7219_init(struct snd_soc_pcm_runtime *rtd) return 0; } -static int da7219_clk_enable(struct snd_pcm_substream *substream, - int wclk_rate, int bclk_rate) +static int da7219_clk_enable(struct snd_pcm_substream *substream) { int ret = 0; struct snd_soc_pcm_runtime *rtd = substream->private_data; - clk_set_rate(da7219_dai_wclk, wclk_rate); - clk_set_rate(da7219_dai_bclk, bclk_rate); + /* + * Set wclk to 48000 because the rate constraint of this driver is + * 48000. ADAU7002 spec: "The ADAU7002 requires a BCLK rate that is + * minimum of 64x the LRCLK sample rate." DA7219 is the only clk + * source so for all codecs we have to limit bclk to 64X lrclk. + */ + clk_set_rate(da7219_dai_wclk, 48000); + clk_set_rate(da7219_dai_bclk, 48000 * 64); ret = clk_prepare_enable(da7219_dai_bclk); if (ret < 0) { dev_err(rtd->dev, "can't enable master clock %d\n", ret); @@ -156,7 +161,7 @@ static int cz_da7219_play_startup(struct snd_pcm_substream *substream) &constraints_rates); machine->play_i2s_instance = I2S_SP_INSTANCE; - return 0; + return da7219_clk_enable(substream); } static int cz_da7219_cap_startup(struct snd_pcm_substream *substream) @@ -178,7 +183,7 @@ static int cz_da7219_cap_startup(struct snd_pcm_substream *substream) machine->cap_i2s_instance = I2S_SP_INSTANCE; machine->capture_channel = CAP_CHANNEL1; - return 0; + return da7219_clk_enable(substream); } static int cz_max_startup(struct snd_pcm_substream *substream) @@ -199,7 +204,7 @@ static int cz_max_startup(struct snd_pcm_substream *substream) &constraints_rates); machine->play_i2s_instance = I2S_BT_INSTANCE; - return 0; + return da7219_clk_enable(substream); } static int cz_dmic0_startup(struct snd_pcm_substream *substream) @@ -220,7 +225,7 @@ static int cz_dmic0_startup(struct snd_pcm_substream *substream) &constraints_rates); machine->cap_i2s_instance = I2S_BT_INSTANCE; - return 0; + return da7219_clk_enable(substream); } static int cz_dmic1_startup(struct snd_pcm_substream *substream) @@ -242,25 +247,7 @@ static int cz_dmic1_startup(struct snd_pcm_substream *substream) machine->cap_i2s_instance = I2S_SP_INSTANCE; machine->capture_channel = CAP_CHANNEL0; - return 0; -} - -static int cz_da7219_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params) -{ - int wclk, bclk; - - wclk = params_rate(params); - bclk = wclk * params_channels(params) * - snd_pcm_format_width(params_format(params)); - /* ADAU7002 spec: "The ADAU7002 requires a BCLK rate - * that is minimum of 64x the LRCLK sample rate." - * DA7219 is the only clk source so for all codecs - * we have to limit bclk to 64X lrclk. - */ - if (bclk < (wclk * 64)) - bclk = wclk * 64; - return da7219_clk_enable(substream, wclk, bclk); + return da7219_clk_enable(substream); } static void cz_da7219_shutdown(struct snd_pcm_substream *substream) @@ -271,31 +258,26 @@ static void cz_da7219_shutdown(struct snd_pcm_substream *substream) static const struct snd_soc_ops cz_da7219_play_ops = { .startup = cz_da7219_play_startup, .shutdown = cz_da7219_shutdown, - .hw_params = cz_da7219_params, }; static const struct snd_soc_ops cz_da7219_cap_ops = { .startup = cz_da7219_cap_startup, .shutdown = cz_da7219_shutdown, - .hw_params = cz_da7219_params, }; static const struct snd_soc_ops cz_max_play_ops = { .startup = cz_max_startup, .shutdown = cz_da7219_shutdown, - .hw_params = cz_da7219_params, }; static const struct snd_soc_ops cz_dmic0_cap_ops = { .startup = cz_dmic0_startup, .shutdown = cz_da7219_shutdown, - .hw_params = cz_da7219_params, }; static const struct snd_soc_ops cz_dmic1_cap_ops = { .startup = cz_dmic1_startup, .shutdown = cz_da7219_shutdown, - .hw_params = cz_da7219_params, }; SND_SOC_DAILINK_DEF(designware1, -- GitLab From b81eb73be03ac736f1f8d27d64a372c62c7159e5 Mon Sep 17 00:00:00 2001 From: Keyon Jie Date: Tue, 26 Nov 2019 08:15:33 -0600 Subject: [PATCH 0032/1437] ASoC: SOF: Intel: BYT: fix a copy/paste mistake in byt_dump() The shim registers in BYT/CHT/BSW are 64bits based, correct the copy/paste (from bdw.c where the shim registers are 32bits based) error in byt_dump(). Fixes: 3a9e204d4e36 ("ASoC: SOF: Intel: Add context data to any IPC timeout") Signed-off-by: Keyon Jie Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20191126141533.21601-1-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/intel/byt.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/sound/soc/sof/intel/byt.c b/sound/soc/sof/intel/byt.c index 2abf80b3eb52f..b9061b79a57b1 100644 --- a/sound/soc/sof/intel/byt.c +++ b/sound/soc/sof/intel/byt.c @@ -145,33 +145,33 @@ static void byt_dump(struct snd_sof_dev *sdev, u32 flags) struct sof_ipc_dsp_oops_xtensa xoops; struct sof_ipc_panic_info panic_info; u32 stack[BYT_STACK_DUMP_SIZE]; - u32 status, panic, imrd, imrx; + u64 status, panic, imrd, imrx; /* now try generic SOF status messages */ - status = snd_sof_dsp_read(sdev, BYT_DSP_BAR, SHIM_IPCD); - panic = snd_sof_dsp_read(sdev, BYT_DSP_BAR, SHIM_IPCX); + status = snd_sof_dsp_read64(sdev, BYT_DSP_BAR, SHIM_IPCD); + panic = snd_sof_dsp_read64(sdev, BYT_DSP_BAR, SHIM_IPCX); byt_get_registers(sdev, &xoops, &panic_info, stack, BYT_STACK_DUMP_SIZE); snd_sof_get_status(sdev, status, panic, &xoops, &panic_info, stack, BYT_STACK_DUMP_SIZE); /* provide some context for firmware debug */ - imrx = snd_sof_dsp_read(sdev, BYT_DSP_BAR, SHIM_IMRX); - imrd = snd_sof_dsp_read(sdev, BYT_DSP_BAR, SHIM_IMRD); + imrx = snd_sof_dsp_read64(sdev, BYT_DSP_BAR, SHIM_IMRX); + imrd = snd_sof_dsp_read64(sdev, BYT_DSP_BAR, SHIM_IMRD); dev_err(sdev->dev, - "error: ipc host -> DSP: pending %s complete %s raw 0x%8.8x\n", + "error: ipc host -> DSP: pending %s complete %s raw 0x%llx\n", (panic & SHIM_IPCX_BUSY) ? "yes" : "no", (panic & SHIM_IPCX_DONE) ? "yes" : "no", panic); dev_err(sdev->dev, - "error: mask host: pending %s complete %s raw 0x%8.8x\n", + "error: mask host: pending %s complete %s raw 0x%llx\n", (imrx & SHIM_IMRX_BUSY) ? "yes" : "no", (imrx & SHIM_IMRX_DONE) ? "yes" : "no", imrx); dev_err(sdev->dev, - "error: ipc DSP -> host: pending %s complete %s raw 0x%8.8x\n", + "error: ipc DSP -> host: pending %s complete %s raw 0x%llx\n", (status & SHIM_IPCD_BUSY) ? "yes" : "no", (status & SHIM_IPCD_DONE) ? "yes" : "no", status); dev_err(sdev->dev, - "error: mask DSP: pending %s complete %s raw 0x%8.8x\n", + "error: mask DSP: pending %s complete %s raw 0x%llx\n", (imrd & SHIM_IMRD_BUSY) ? "yes" : "no", (imrd & SHIM_IMRD_DONE) ? "yes" : "no", imrd); -- GitLab From 469b3ad672e27b28c5865c804426f65e69c5e41a Mon Sep 17 00:00:00 2001 From: Daniel Baluta Date: Tue, 26 Nov 2019 08:16:06 -0600 Subject: [PATCH 0033/1437] ASoC: SOF: topology: Fix unload for SAI/ESAI Link unload now fails for ESAI/SAI DAIs with: "error: invalid DAI type 6" because DAI type is not properly handled. Fix this by correctly handling cases where type is ESAI or SAI. Fixes: a4eff5f86c9c5e7 ("ASoC: SOF: imx: Read ESAI parameters and send them to DSP") Signed-off-by: Daniel Baluta Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20191126141606.21650-1-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/topology.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index d82ab981e8408..e20b806ec80f1 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -3132,7 +3132,9 @@ found: case SOF_DAI_INTEL_SSP: case SOF_DAI_INTEL_DMIC: case SOF_DAI_INTEL_ALH: - /* no resource needs to be released for SSP, DMIC and ALH */ + case SOF_DAI_IMX_SAI: + case SOF_DAI_IMX_ESAI: + /* no resource needs to be released for all cases above */ break; case SOF_DAI_INTEL_HDA: ret = sof_link_hda_unload(sdev, link); -- GitLab From fb3194413d1ef79732931a40f54da21a16505a76 Mon Sep 17 00:00:00 2001 From: YueHaibing Date: Wed, 27 Nov 2019 16:21:45 +0800 Subject: [PATCH 0034/1437] ASoC: rt5677: Fix build error without CONFIG_SPI If CONFIG_SPI is n, SND_SOC_RT5677_SPI also is n, building fails: sound/soc/codecs/rt5677.o: In function `rt5677_irq': rt5677.c:(.text+0x2dbf): undefined reference to `rt5677_spi_hotword_detected' sound/soc/codecs/rt5677.o: In function `rt5677_dsp_work': rt5677.c:(.text+0x3709): undefined reference to `rt5677_spi_write' This adds stub helpers to fix this. Reported-by: Hulk Robot Fixes: 461c623270e4 ("ASoC: rt5677: Load firmware via SPI using delayed work") Signed-off-by: YueHaibing Link: https://lore.kernel.org/r/20191127082145.6100-1-yuehaibing@huawei.com Signed-off-by: Mark Brown --- sound/soc/codecs/rt5677-spi.h | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/sound/soc/codecs/rt5677-spi.h b/sound/soc/codecs/rt5677-spi.h index 3af36ec928e93..088b779317276 100644 --- a/sound/soc/codecs/rt5677-spi.h +++ b/sound/soc/codecs/rt5677-spi.h @@ -9,9 +9,25 @@ #ifndef __RT5677_SPI_H__ #define __RT5677_SPI_H__ +#if IS_ENABLED(CONFIG_SND_SOC_RT5677_SPI) int rt5677_spi_read(u32 addr, void *rxbuf, size_t len); int rt5677_spi_write(u32 addr, const void *txbuf, size_t len); int rt5677_spi_write_firmware(u32 addr, const struct firmware *fw); void rt5677_spi_hotword_detected(void); +#else +static inline int rt5677_spi_read(u32 addr, void *rxbuf, size_t len) +{ + return -EINVAL; +} +static inline int rt5677_spi_write(u32 addr, const void *txbuf, size_t len) +{ + return -EINVAL; +} +static inline int rt5677_spi_write_firmware(u32 addr, const struct firmware *fw) +{ + return -EINVAL; +} +static inline void rt5677_spi_hotword_detected(void){} +#endif #endif /* __RT5677_SPI_H__ */ -- GitLab From 2a0bda276c64212e517cc1d65cf65719a9ab1ef6 Mon Sep 17 00:00:00 2001 From: Michael Walle Date: Sat, 23 Nov 2019 00:25:32 +0100 Subject: [PATCH 0035/1437] ASoC: wm8904: fix automatic sysclk configuration The simple-card tries to signal the codec to disable rate constraints, see commit 2458adb8f92a ("SoC: simple-card-utils: set 0Hz to sysclk when shutdown"). This wasn't handled by the codec, instead it would set the FLL frequency to 0Hz which isn't working. Since we don't have any rate constraints just ignore this request. Fixes: 13409d27cb39 ("ASoC: wm8904: configure sysclk/FLL automatically") Signed-off-by: Michael Walle Acked-by: Charles Keepax Link: https://lore.kernel.org/r/20191122232532.22258-1-michael@walle.cc Signed-off-by: Mark Brown --- sound/soc/codecs/wm8904.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/sound/soc/codecs/wm8904.c b/sound/soc/codecs/wm8904.c index 2a7d23a5daa8d..d191d81850ee8 100644 --- a/sound/soc/codecs/wm8904.c +++ b/sound/soc/codecs/wm8904.c @@ -1806,6 +1806,12 @@ static int wm8904_set_sysclk(struct snd_soc_dai *dai, int clk_id, switch (clk_id) { case WM8904_CLK_AUTO: + /* We don't have any rate constraints, so just ignore the + * request to disable constraining. + */ + if (!freq) + return 0; + mclk_freq = clk_get_rate(priv->mclk); /* enable FLL if a different sysclk is desired */ if (mclk_freq != freq) { -- GitLab From 4affd79a125ac91e6a53be843ea3960a8fc00cbb Mon Sep 17 00:00:00 2001 From: Wen Yang Date: Sun, 24 Nov 2019 22:58:35 +0800 Subject: [PATCH 0036/1437] regulator: fix use after free issue This is caused by dereferencing 'rdev' after put_device() in the _regulator_get()/_regulator_put() functions. This patch just moves the put_device() down a bit to avoid the issue. Signed-off-by: Wen Yang Cc: Liam Girdwood Cc: Mark Brown Cc: linux-kernel@vger.kernel.org Link: https://lore.kernel.org/r/20191124145835.25999-1-wenyang@linux.alibaba.com Signed-off-by: Mark Brown --- drivers/regulator/core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 5e6c629806e42..c80f3fd9532dd 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -1937,8 +1937,8 @@ struct regulator *_regulator_get(struct device *dev, const char *id, regulator = create_regulator(rdev, dev, id); if (regulator == NULL) { regulator = ERR_PTR(-ENOMEM); - put_device(&rdev->dev); module_put(rdev->owner); + put_device(&rdev->dev); return regulator; } @@ -2059,13 +2059,13 @@ static void _regulator_put(struct regulator *regulator) rdev->open_count--; rdev->exclusive = 0; - put_device(&rdev->dev); regulator_unlock(rdev); kfree_const(regulator->supply_name); kfree(regulator); module_put(rdev->owner); + put_device(&rdev->dev); } /** -- GitLab From 61acd19f9c56fa0809285346bd0bd4a926ab0da0 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Tue, 26 Nov 2019 16:41:40 +0000 Subject: [PATCH 0037/1437] spi: cadence: Correct handling of native chipselect To fix a regression on the Cadence SPI driver, this patch reverts commit 6046f5407ff0 ("spi: cadence: Fix default polarity of native chipselect"). This patch was not the correct fix for the issue. The SPI framework calls the set_cs line with the logic level it desires on the chip select line, as such the old is_high handling was correct. However, this was broken by the fact that before commit 3e5ec1db8bfe ("spi: Fix SPI_CS_HIGH setting when using native and GPIO CS") all controllers that offered the use of a GPIO chip select had SPI_CS_HIGH applied, even for hardware chip selects. This caused the value passed into the driver to be inverted. Which unfortunately makes it look like a logical enable the chip select value. Since the core was corrected to not unconditionally apply SPI_CS_HIGH, the Cadence driver, whilst using the hardware chip select, will deselect the chip select every time we attempt to communicate with the device, which results in failed communications. Fixes: 3e5ec1db8bfe ("spi: Fix SPI_CS_HIGH setting when using native and GPIO CS") Signed-off-by: Charles Keepax Acked-by: Linus Walleij Link: https://lore.kernel.org/r/20191126164140.6240-1-ckeepax@opensource.cirrus.com Signed-off-by: Mark Brown --- drivers/spi/spi-cadence.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/spi/spi-cadence.c b/drivers/spi/spi-cadence.c index c36587b42e951..82a0ee09cbe14 100644 --- a/drivers/spi/spi-cadence.c +++ b/drivers/spi/spi-cadence.c @@ -168,16 +168,16 @@ static void cdns_spi_init_hw(struct cdns_spi *xspi) /** * cdns_spi_chipselect - Select or deselect the chip select line * @spi: Pointer to the spi_device structure - * @enable: Select (1) or deselect (0) the chip select line + * @is_high: Select(0) or deselect (1) the chip select line */ -static void cdns_spi_chipselect(struct spi_device *spi, bool enable) +static void cdns_spi_chipselect(struct spi_device *spi, bool is_high) { struct cdns_spi *xspi = spi_master_get_devdata(spi->master); u32 ctrl_reg; ctrl_reg = cdns_spi_read(xspi, CDNS_SPI_CR); - if (!enable) { + if (is_high) { /* Deselect the slave */ ctrl_reg |= CDNS_SPI_CR_SSCTRL; } else { -- GitLab From acb874a7c049ec49d8fc66c893170fb42c01bdf7 Mon Sep 17 00:00:00 2001 From: Tzung-Bi Shih Date: Fri, 22 Nov 2019 15:31:12 +0800 Subject: [PATCH 0038/1437] ASoC: max98090: remove msleep in PLL unlocked workaround It was observed Baytrail-based chromebooks could cause continuous PLL unlocked when using playback stream and capture stream simultaneously. Specifically, starting a capture stream after started a playback stream. As a result, the audio data could corrupt or turn completely silent. As the datasheet suggested, the maximum PLL lock time should be 7 msec. The workaround resets the codec softly by toggling SHDN off and on if PLL failed to lock for 10 msec. Notably, there is no suggested hold time for SHDN off. On Baytrail-based chromebooks, it would easily happen continuous PLL unlocked if there is a 10 msec delay between SHDN off and on. Removes the msleep(). Signed-off-by: Tzung-Bi Shih Link: https://lore.kernel.org/r/20191122073114.219945-2-tzungbi@google.com Reviewed-by: Pierre-Louis Bossart Signed-off-by: Mark Brown --- sound/soc/codecs/max98090.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/sound/soc/codecs/max98090.c b/sound/soc/codecs/max98090.c index f6bf4cfbea23f..12cb87c0d4637 100644 --- a/sound/soc/codecs/max98090.c +++ b/sound/soc/codecs/max98090.c @@ -2114,10 +2114,16 @@ static void max98090_pll_work(struct work_struct *work) dev_info_ratelimited(component->dev, "PLL unlocked\n"); + /* + * As the datasheet suggested, the maximum PLL lock time should be + * 7 msec. The workaround resets the codec softly by toggling SHDN + * off and on if PLL failed to lock for 10 msec. Notably, there is + * no suggested hold time for SHDN off. + */ + /* Toggle shutdown OFF then ON */ snd_soc_component_update_bits(component, M98090_REG_DEVICE_SHUTDOWN, M98090_SHDNN_MASK, 0); - msleep(10); snd_soc_component_update_bits(component, M98090_REG_DEVICE_SHUTDOWN, M98090_SHDNN_MASK, M98090_SHDNN_MASK); -- GitLab From 6f49919d11690a9b5614445ba30fde18083fdd63 Mon Sep 17 00:00:00 2001 From: Tzung-Bi Shih Date: Fri, 22 Nov 2019 15:31:13 +0800 Subject: [PATCH 0039/1437] ASoC: max98090: exit workaround earlier if PLL is locked According to the datasheet, PLL lock time typically takes 2 msec and at most takes 7 msec. Check the lock status every 1 msec and exit the workaround if PLL is locked. Signed-off-by: Tzung-Bi Shih Link: https://lore.kernel.org/r/20191122073114.219945-3-tzungbi@google.com Reviewed-by: Pierre-Louis Bossart Signed-off-by: Mark Brown --- sound/soc/codecs/max98090.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/sound/soc/codecs/max98090.c b/sound/soc/codecs/max98090.c index 12cb87c0d4637..f531e5a11bdda 100644 --- a/sound/soc/codecs/max98090.c +++ b/sound/soc/codecs/max98090.c @@ -2108,6 +2108,8 @@ static void max98090_pll_work(struct work_struct *work) struct max98090_priv *max98090 = container_of(work, struct max98090_priv, pll_work); struct snd_soc_component *component = max98090->component; + unsigned int pll; + int i; if (!snd_soc_component_is_active(component)) return; @@ -2127,8 +2129,16 @@ static void max98090_pll_work(struct work_struct *work) snd_soc_component_update_bits(component, M98090_REG_DEVICE_SHUTDOWN, M98090_SHDNN_MASK, M98090_SHDNN_MASK); - /* Give PLL time to lock */ - msleep(10); + for (i = 0; i < 10; ++i) { + /* Give PLL time to lock */ + usleep_range(1000, 1200); + + /* Check lock status */ + pll = snd_soc_component_read32( + component, M98090_REG_DEVICE_STATUS); + if (!(pll & M98090_ULK_MASK)) + break; + } } static void max98090_jack_work(struct work_struct *work) -- GitLab From 45dfbf56975994822cce00b7475732a49f8aefed Mon Sep 17 00:00:00 2001 From: Tzung-Bi Shih Date: Fri, 22 Nov 2019 15:31:14 +0800 Subject: [PATCH 0040/1437] ASoC: max98090: fix possible race conditions max98090_interrupt() and max98090_pll_work() run in 2 different threads. There are 2 possible races: Note: M98090_REG_DEVICE_STATUS = 0x01. Note: ULK == 0, PLL is locked; ULK == 1, PLL is unlocked. max98090_interrupt max98090_pll_work ---------------------------------------------- schedule max98090_pll_work restart max98090 codec receive ULK INT assert ULK == 0 schedule max98090_pll_work (1). In the case (1), the PLL is locked but max98090_interrupt unnecessarily schedules another max98090_pll_work. max98090_interrupt max98090_pll_work max98090 codec ---------------------------------------------------------------------- ULK = 1 receive ULK INT read 0x01 ULK = 0 (clear on read) schedule max98090_pll_work restart max98090 codec ULK = 1 receive ULK INT read 0x01 ULK = 0 (clear on read) read 0x01 assert ULK == 0 (2). In the case (2), both max98090_interrupt and max98090_pll_work read the same clear-on-read register. max98090_pll_work would falsely thought PLL is locked. Note: the case (2) race is introduced by the previous commit ("ASoC: max98090: exit workaround earlier if PLL is locked") to check the status and exit the loop earlier in max98090_pll_work. There are 2 possible solution options: A. turn off ULK interrupt before scheduling max98090_pll_work; and turn on again before exiting max98090_pll_work. B. remove the second thread of execution. Option A cannot fix the case (2) race because it still has 2 threads access the same clear-on-read register simultaneously. Although we could suppose the register is volatile and read the status via I2C could be much slower than the hardware raises the bits. Option B introduces a maximum 10~12 msec penalty delay in the interrupt handler. However, it could only punish the jack detection by extra 10~12 msec. Adopts option B which is the better solution overall. Signed-off-by: Tzung-Bi Shih Link: https://lore.kernel.org/r/20191122073114.219945-4-tzungbi@google.com Reviewed-by: Pierre-Louis Bossart Signed-off-by: Mark Brown --- sound/soc/codecs/max98090.c | 8 ++------ sound/soc/codecs/max98090.h | 1 - 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/sound/soc/codecs/max98090.c b/sound/soc/codecs/max98090.c index f531e5a11bdda..e46b6ada13b1b 100644 --- a/sound/soc/codecs/max98090.c +++ b/sound/soc/codecs/max98090.c @@ -2103,10 +2103,8 @@ static void max98090_pll_det_disable_work(struct work_struct *work) M98090_IULK_MASK, 0); } -static void max98090_pll_work(struct work_struct *work) +static void max98090_pll_work(struct max98090_priv *max98090) { - struct max98090_priv *max98090 = - container_of(work, struct max98090_priv, pll_work); struct snd_soc_component *component = max98090->component; unsigned int pll; int i; @@ -2275,7 +2273,7 @@ static irqreturn_t max98090_interrupt(int irq, void *data) if (active & M98090_ULK_MASK) { dev_dbg(component->dev, "M98090_ULK_MASK\n"); - schedule_work(&max98090->pll_work); + max98090_pll_work(max98090); } if (active & M98090_JDET_MASK) { @@ -2438,7 +2436,6 @@ static int max98090_probe(struct snd_soc_component *component) max98090_pll_det_enable_work); INIT_WORK(&max98090->pll_det_disable_work, max98090_pll_det_disable_work); - INIT_WORK(&max98090->pll_work, max98090_pll_work); /* Enable jack detection */ snd_soc_component_write(component, M98090_REG_JACK_DETECT, @@ -2491,7 +2488,6 @@ static void max98090_remove(struct snd_soc_component *component) cancel_delayed_work_sync(&max98090->jack_work); cancel_delayed_work_sync(&max98090->pll_det_enable_work); cancel_work_sync(&max98090->pll_det_disable_work); - cancel_work_sync(&max98090->pll_work); max98090->component = NULL; } diff --git a/sound/soc/codecs/max98090.h b/sound/soc/codecs/max98090.h index 57965cd678b4e..a197114b0dad3 100644 --- a/sound/soc/codecs/max98090.h +++ b/sound/soc/codecs/max98090.h @@ -1530,7 +1530,6 @@ struct max98090_priv { struct delayed_work jack_work; struct delayed_work pll_det_enable_work; struct work_struct pll_det_disable_work; - struct work_struct pll_work; struct snd_soc_jack *jack; unsigned int dai_fmt; int tdm_slots; -- GitLab From e4eb7b5c335ccfdb5c2a9a2004aca7cb81e0d577 Mon Sep 17 00:00:00 2001 From: John Crispin Date: Mon, 25 Nov 2019 16:36:18 +0000 Subject: [PATCH 0041/1437] ath11k: add RX stats support for radiotap mac80211 expects the definition of what HE rate info is available inside a struct prepended to the skb. Signed-off-by: Shashidhar Lakkavalli Signed-off-by: John Crispin Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath11k/dp_rx.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/net/wireless/ath/ath11k/dp_rx.c b/drivers/net/wireless/ath/ath11k/dp_rx.c index 9491a479fab51..c498c4920e117 100644 --- a/drivers/net/wireless/ath/ath11k/dp_rx.c +++ b/drivers/net/wireless/ath/ath11k/dp_rx.c @@ -2038,11 +2038,21 @@ static char *ath11k_print_get_tid(struct ieee80211_hdr *hdr, char *out, static void ath11k_dp_rx_deliver_msdu(struct ath11k *ar, struct napi_struct *napi, struct sk_buff *msdu) { + static const struct ieee80211_radiotap_he known = { + .data1 = cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA1_DATA_MCS_KNOWN), + .data2 = cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA2_GI_KNOWN), + }; struct ieee80211_rx_status *status; struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)msdu->data; + struct ieee80211_radiotap_he *he = NULL; char tid[32]; status = IEEE80211_SKB_RXCB(msdu); + if (status->encoding == RX_ENC_HE) { + he = skb_push(msdu, sizeof(known)); + memcpy(he, &known, sizeof(known)); + status->flag |= RX_FLAG_RADIOTAP_HE; + } ath11k_dbg(ar->ab, ATH11K_DBG_DATA, "rx skb %pK len %u peer %pM %s %s sn %u %s%s%s%s%s%s%s %srate_idx %u vht_nss %u freq %u band %u flag 0x%x fcs-err %i mic-err %i amsdu-more %i\n", -- GitLab From 9cfbae4632faf26ab43f65f3beaf59a00dc699f6 Mon Sep 17 00:00:00 2001 From: John Crispin Date: Mon, 25 Nov 2019 16:36:19 +0000 Subject: [PATCH 0042/1437] ath11k: ignore event 0x6017 Everytime a new peer gets associated, we see the following message in the log - ath11k c000000.wifi1: Unknown eventid: 0x6017 Ignore this event for now. We probably need to handle the event properly when we add OMI support. Signed-off-by: Shashidhar Lakkavalli Signed-off-by: John Crispin Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath11k/wmi.c | 1 + drivers/net/wireless/ath/ath11k/wmi.h | 13 ++++++++++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath11k/wmi.c b/drivers/net/wireless/ath/ath11k/wmi.c index 3d1d4d3c3f20e..3ec5dd47cdc85 100644 --- a/drivers/net/wireless/ath/ath11k/wmi.c +++ b/drivers/net/wireless/ath/ath11k/wmi.c @@ -5416,6 +5416,7 @@ static void ath11k_wmi_tlv_op_rx(struct ath11k_base *ab, struct sk_buff *skb) /* add Unsupported events here */ case WMI_TBTTOFFSET_EXT_UPDATE_EVENTID: case WMI_VDEV_DELETE_RESP_EVENTID: + case WMI_PEER_OPER_MODE_CHANGE_EVENTID: ath11k_dbg(ab, ATH11K_DBG_WMI, "ignoring unsupported event 0x%x\n", id); break; diff --git a/drivers/net/wireless/ath/ath11k/wmi.h b/drivers/net/wireless/ath/ath11k/wmi.h index 00c51cc83d479..1110da29cfcd7 100644 --- a/drivers/net/wireless/ath/ath11k/wmi.h +++ b/drivers/net/wireless/ath/ath11k/wmi.h @@ -288,7 +288,6 @@ enum wmi_tlv_cmd_id { WMI_PEER_REORDER_QUEUE_REMOVE_CMDID, WMI_PEER_SET_RX_BLOCKSIZE_CMDID, WMI_PEER_ANTDIV_INFO_REQ_CMDID, - WMI_PEER_OPER_MODE_CHANGE_EVENTID, WMI_BCN_TX_CMDID = WMI_TLV_CMD(WMI_GRP_MGMT), WMI_PDEV_SEND_BCN_CMDID, WMI_BCN_TMPL_CMDID, @@ -586,6 +585,18 @@ enum wmi_tlv_event_id { WMI_WDS_PEER_EVENTID, WMI_PEER_STA_PS_STATECHG_EVENTID, WMI_PEER_ANTDIV_INFO_EVENTID, + WMI_PEER_RESERVED0_EVENTID, + WMI_PEER_RESERVED1_EVENTID, + WMI_PEER_RESERVED2_EVENTID, + WMI_PEER_RESERVED3_EVENTID, + WMI_PEER_RESERVED4_EVENTID, + WMI_PEER_RESERVED5_EVENTID, + WMI_PEER_RESERVED6_EVENTID, + WMI_PEER_RESERVED7_EVENTID, + WMI_PEER_RESERVED8_EVENTID, + WMI_PEER_RESERVED9_EVENTID, + WMI_PEER_RESERVED10_EVENTID, + WMI_PEER_OPER_MODE_CHANGE_EVENTID, WMI_MGMT_RX_EVENTID = WMI_TLV_CMD(WMI_GRP_MGMT), WMI_HOST_SWBA_EVENTID, WMI_TBTTOFFSET_UPDATE_EVENTID, -- GitLab From 2dab7d221e88c83f88af5996d811dea62512dff2 Mon Sep 17 00:00:00 2001 From: John Crispin Date: Mon, 25 Nov 2019 16:36:21 +0000 Subject: [PATCH 0043/1437] ath11k: convert message from info to dbg We can regularly see the following message. - "ath11k c000000.wifi1: failed to find the peer with peer_id 4" This happens when the FW starts sending stats for the peer whilst the peer is not fully associated. Convert this info message to a debug one. Signed-off-by: Shashidhar Lakkavalli Signed-off-by: John Crispin Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath11k/dp_rx.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/ath/ath11k/dp_rx.c b/drivers/net/wireless/ath/ath11k/dp_rx.c index c498c4920e117..d5963509419b2 100644 --- a/drivers/net/wireless/ath/ath11k/dp_rx.c +++ b/drivers/net/wireless/ath/ath11k/dp_rx.c @@ -2621,8 +2621,9 @@ int ath11k_dp_rx_process_mon_status(struct ath11k_base *ab, int mac_id, peer = ath11k_peer_find_by_id(ab, ppdu_info.peer_id); if (!peer || !peer->sta) { - ath11k_warn(ab, "failed to find the peer with peer_id %d\n", - ppdu_info.peer_id); + ath11k_dbg(ab, ATH11K_DBG_DATA, + "failed to find the peer with peer_id %d\n", + ppdu_info.peer_id); spin_unlock_bh(&ab->base_lock); rcu_read_unlock(); dev_kfree_skb_any(skb); -- GitLab From 39e81c6a2907eff835247d61c0f467327ab6d6c0 Mon Sep 17 00:00:00 2001 From: Tamizh chelvam Date: Mon, 25 Nov 2019 16:36:23 +0000 Subject: [PATCH 0044/1437] ath11k: fix missed bw conversion in tx completion TX rate stats for the retried packets for a station comes through tx completion events. Assigning hw reported bandwidth information directly to station's txrate bandwidth will cause below warning. Fix this warning by converting the hw reported bandwidth to mac80211 base bandwidth. [ 134.758190] PC is at cfg80211_calculate_bitrate+0x1bc/0x214 [cfg80211] [ 134.765730] LR is at cfg80211_calculate_bitrate+0x1bc/0x214 [cfg80211] [ 134.875014] [] cfg80211_calculate_bitrate+0x1bc/0x214 [cfg80211] [ 134.877192] [] nl80211_put_sta_rate+0x54/0xf24 [cfg80211] [ 134.884829] [] nl80211_put_sta_rate+0x698/0xf24 [cfg80211] [ 134.891687] [] nl80211_put_sta_rate+0xde0/0xf24 [cfg80211] [ 134.898975] [] genl_lock_dumpit+0x30/0x4c [ 134.905998] [] netlink_dump+0xf4/0x248 [ 134.911291] [] __netlink_dump_start+0xe0/0x174 [ 134.916850] [] genl_family_rcv_msg+0x130/0x2c0 Signed-off-by: Tamizh chelvam Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath11k/debugfs_sta.c | 2 +- drivers/net/wireless/ath/ath11k/dp_rx.c | 30 +++---------------- drivers/net/wireless/ath/ath11k/mac.c | 22 ++++++++++++++ drivers/net/wireless/ath/ath11k/mac.h | 1 + 4 files changed, 28 insertions(+), 27 deletions(-) diff --git a/drivers/net/wireless/ath/ath11k/debugfs_sta.c b/drivers/net/wireless/ath/ath11k/debugfs_sta.c index b392117eff3ca..3c5f931e22a9d 100644 --- a/drivers/net/wireless/ath/ath11k/debugfs_sta.c +++ b/drivers/net/wireless/ath/ath11k/debugfs_sta.c @@ -192,7 +192,7 @@ void ath11k_update_per_peer_stats_from_txcompl(struct ath11k *ar, } arsta->txrate.nss = arsta->last_txrate.nss; - arsta->txrate.bw = ts->bw; + arsta->txrate.bw = ath11k_mac_bw_to_mac80211_bw(ts->bw); ath11k_accumulate_per_peer_tx_stats(arsta, peer_stats, rate_idx); spin_unlock_bh(&ab->base_lock); diff --git a/drivers/net/wireless/ath/ath11k/dp_rx.c b/drivers/net/wireless/ath/ath11k/dp_rx.c index d5963509419b2..0ff0ff81adeb5 100644 --- a/drivers/net/wireless/ath/ath11k/dp_rx.c +++ b/drivers/net/wireless/ath/ath11k/dp_rx.c @@ -987,28 +987,6 @@ int ath11k_dp_htt_tlv_iter(struct ath11k_base *ab, const void *ptr, size_t len, return 0; } -static u8 ath11k_bw_to_mac80211_bw(u8 bw) -{ - u8 ret = 0; - - switch (bw) { - case ATH11K_BW_20: - ret = RATE_INFO_BW_20; - break; - case ATH11K_BW_40: - ret = RATE_INFO_BW_40; - break; - case ATH11K_BW_80: - ret = RATE_INFO_BW_80; - break; - case ATH11K_BW_160: - ret = RATE_INFO_BW_160; - break; - } - - return ret; -} - static u32 ath11k_bw_to_mac80211_bwflags(u8 bw) { u32 bwflags = 0; @@ -1157,7 +1135,7 @@ ath11k_update_per_peer_tx_stats(struct ath11k *ar, } arsta->txrate.nss = nss; - arsta->txrate.bw = ath11k_bw_to_mac80211_bw(bw); + arsta->txrate.bw = ath11k_mac_bw_to_mac80211_bw(bw); arsta->tx_info.status.rates[0].flags |= ath11k_bw_to_mac80211_bwflags(bw); memcpy(&arsta->last_txrate, &arsta->txrate, sizeof(struct rate_info)); @@ -1934,7 +1912,7 @@ static void ath11k_dp_rx_h_rate(struct ath11k *ar, struct hal_rx_desc *rx_desc, rx_status->rate_idx = rate_mcs + (8 * (nss - 1)); if (sgi) rx_status->enc_flags |= RX_ENC_FLAG_SHORT_GI; - rx_status->bw = ath11k_bw_to_mac80211_bw(bw); + rx_status->bw = ath11k_mac_bw_to_mac80211_bw(bw); break; case RX_MSDU_START_PKT_TYPE_11AC: rx_status->encoding = RX_ENC_VHT; @@ -1948,7 +1926,7 @@ static void ath11k_dp_rx_h_rate(struct ath11k *ar, struct hal_rx_desc *rx_desc, rx_status->nss = nss; if (sgi) rx_status->enc_flags |= RX_ENC_FLAG_SHORT_GI; - rx_status->bw = ath11k_bw_to_mac80211_bw(bw); + rx_status->bw = ath11k_mac_bw_to_mac80211_bw(bw); break; case RX_MSDU_START_PKT_TYPE_11AX: rx_status->rate_idx = rate_mcs; @@ -1960,7 +1938,7 @@ static void ath11k_dp_rx_h_rate(struct ath11k *ar, struct hal_rx_desc *rx_desc, } rx_status->encoding = RX_ENC_HE; rx_status->nss = nss; - rx_status->bw = ath11k_bw_to_mac80211_bw(bw); + rx_status->bw = ath11k_mac_bw_to_mac80211_bw(bw); break; } } diff --git a/drivers/net/wireless/ath/ath11k/mac.c b/drivers/net/wireless/ath/ath11k/mac.c index cb025a4a5785f..939f5880bce66 100644 --- a/drivers/net/wireless/ath/ath11k/mac.c +++ b/drivers/net/wireless/ath/ath11k/mac.c @@ -156,6 +156,28 @@ static const u32 ath11k_smps_map[] = { [WLAN_HT_CAP_SM_PS_DISABLED] = WMI_PEER_SMPS_PS_NONE, }; +u8 ath11k_mac_bw_to_mac80211_bw(u8 bw) +{ + u8 ret = 0; + + switch (bw) { + case ATH11K_BW_20: + ret = RATE_INFO_BW_20; + break; + case ATH11K_BW_40: + ret = RATE_INFO_BW_40; + break; + case ATH11K_BW_80: + ret = RATE_INFO_BW_80; + break; + case ATH11K_BW_160: + ret = RATE_INFO_BW_160; + break; + } + + return ret; +} + int ath11k_mac_hw_ratecode_to_legacy_rate(u8 hw_rc, u8 preamble, u8 *rateidx, u16 *rate) { diff --git a/drivers/net/wireless/ath/ath11k/mac.h b/drivers/net/wireless/ath/ath11k/mac.h index f84af1fc49527..8c37573ae5dc6 100644 --- a/drivers/net/wireless/ath/ath11k/mac.h +++ b/drivers/net/wireless/ath/ath11k/mac.h @@ -142,4 +142,5 @@ struct ath11k *ath11k_mac_get_ar_vdev_stop_status(struct ath11k_base *ab, void ath11k_mac_drain_tx(struct ath11k *ar); void ath11k_mac_peer_cleanup_all(struct ath11k *ar); int ath11k_mac_tx_mgmt_pending_free(int buf_id, void *skb, void *ctx); +u8 ath11k_mac_bw_to_mac80211_bw(u8 bw); #endif -- GitLab From 33782a3c379ede6ed443873eae69a0989f5a65ba Mon Sep 17 00:00:00 2001 From: Tamizh chelvam Date: Mon, 25 Nov 2019 16:36:25 +0000 Subject: [PATCH 0045/1437] ath11k: Remove dead code while handling amsdu packets Remove unexecuted code while handling amsdu packets. The same logic is done before calling ath11k_dp_rx_msdu_coalesce Signed-off-by: Tamizh chelvam Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath11k/dp_rx.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/drivers/net/wireless/ath/ath11k/dp_rx.c b/drivers/net/wireless/ath/ath11k/dp_rx.c index 0ff0ff81adeb5..7aade0314e614 100644 --- a/drivers/net/wireless/ath/ath11k/dp_rx.c +++ b/drivers/net/wireless/ath/ath11k/dp_rx.c @@ -1349,12 +1349,6 @@ static int ath11k_dp_rx_msdu_coalesce(struct ath11k *ar, int rem_len; int buf_len; - if (!rxcb->is_continuation) { - skb_put(first, HAL_RX_DESC_SIZE + l3pad_bytes + msdu_len); - skb_pull(first, HAL_RX_DESC_SIZE + l3pad_bytes); - return 0; - } - if (WARN_ON_ONCE(msdu_len <= (DP_RX_BUFFER_SIZE - (HAL_RX_DESC_SIZE + l3pad_bytes)))) { skb_put(first, HAL_RX_DESC_SIZE + l3pad_bytes + msdu_len); -- GitLab From 9f056ed8ee01ad6898db49707cdc70ce923be3d0 Mon Sep 17 00:00:00 2001 From: John Crispin Date: Mon, 25 Nov 2019 16:36:27 +0000 Subject: [PATCH 0046/1437] ath11k: add HE support Add basic HE support to the driver. The sband_iftype data is generated from the capabilities read from the FW. Signed-off-by: Shashidhar Lakkavalli Signed-off-by: John Crispin Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath11k/core.h | 2 + drivers/net/wireless/ath/ath11k/mac.c | 265 ++++++++++++++++++++++++- drivers/net/wireless/ath/ath11k/reg.c | 1 + drivers/net/wireless/ath/ath11k/wmi.c | 6 +- drivers/net/wireless/ath/ath11k/wmi.h | 6 +- 5 files changed, 270 insertions(+), 10 deletions(-) diff --git a/drivers/net/wireless/ath/ath11k/core.h b/drivers/net/wireless/ath/ath11k/core.h index 95447d1be1047..065cb9dc880b8 100644 --- a/drivers/net/wireless/ath/ath11k/core.h +++ b/drivers/net/wireless/ath/ath11k/core.h @@ -431,6 +431,8 @@ struct ath11k { struct { struct ieee80211_supported_band sbands[NUM_NL80211_BANDS]; + struct ieee80211_sband_iftype_data + iftype[NUM_NL80211_BANDS][NUM_NL80211_IFTYPES]; } mac; unsigned long dev_flags; unsigned int filter_flags; diff --git a/drivers/net/wireless/ath/ath11k/mac.c b/drivers/net/wireless/ath/ath11k/mac.c index 939f5880bce66..79c22d86e0fe6 100644 --- a/drivers/net/wireless/ath/ath11k/mac.c +++ b/drivers/net/wireless/ath/ath11k/mac.c @@ -1140,7 +1140,86 @@ static void ath11k_peer_assoc_h_he(struct ath11k *ar, struct ieee80211_sta *sta, struct peer_assoc_params *arg) { - /* TODO: Implementation */ + const struct ieee80211_sta_he_cap *he_cap = &sta->he_cap; + u16 v; + + if (!he_cap->has_he) + return; + + arg->he_flag = true; + + memcpy(&arg->peer_he_cap_macinfo, he_cap->he_cap_elem.mac_cap_info, + sizeof(arg->peer_he_cap_macinfo)); + memcpy(&arg->peer_he_cap_phyinfo, he_cap->he_cap_elem.phy_cap_info, + sizeof(arg->peer_he_cap_phyinfo)); + memcpy(&arg->peer_he_ops, &vif->bss_conf.he_operation, + sizeof(arg->peer_he_ops)); + + /* the top most byte is used to indicate BSS color info */ + arg->peer_he_ops &= 0xffffff; + + if (he_cap->he_cap_elem.phy_cap_info[6] & + IEEE80211_HE_PHY_CAP6_PPE_THRESHOLD_PRESENT) { + int bit = 7; + int nss, ru; + + arg->peer_ppet.numss_m1 = he_cap->ppe_thres[0] & + IEEE80211_PPE_THRES_NSS_MASK; + arg->peer_ppet.ru_bit_mask = + (he_cap->ppe_thres[0] & + IEEE80211_PPE_THRES_RU_INDEX_BITMASK_MASK) >> + IEEE80211_PPE_THRES_RU_INDEX_BITMASK_POS; + + for (nss = 0; nss <= arg->peer_ppet.numss_m1; nss++) { + for (ru = 0; ru < 4; ru++) { + u32 val = 0; + int i; + + if ((arg->peer_ppet.ru_bit_mask & BIT(ru)) == 0) + continue; + for (i = 0; i < 6; i++) { + val >>= 1; + val |= ((he_cap->ppe_thres[bit / 8] >> + (bit % 8)) & 0x1) << 5; + bit++; + } + arg->peer_ppet.ppet16_ppet8_ru3_ru0[nss] |= + val << (ru * 6); + } + } + } + + switch (sta->bandwidth) { + case IEEE80211_STA_RX_BW_160: + if (he_cap->he_cap_elem.phy_cap_info[0] & + IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_80PLUS80_MHZ_IN_5G) { + v = le16_to_cpu(he_cap->he_mcs_nss_supp.rx_mcs_80p80); + arg->peer_he_rx_mcs_set[WMI_HECAP_TXRX_MCS_NSS_IDX_80_80] = v; + + v = le16_to_cpu(he_cap->he_mcs_nss_supp.tx_mcs_80p80); + arg->peer_he_tx_mcs_set[WMI_HECAP_TXRX_MCS_NSS_IDX_80_80] = v; + + arg->peer_he_mcs_count++; + } + v = le16_to_cpu(he_cap->he_mcs_nss_supp.rx_mcs_160); + arg->peer_he_rx_mcs_set[WMI_HECAP_TXRX_MCS_NSS_IDX_160] = v; + + v = le16_to_cpu(he_cap->he_mcs_nss_supp.tx_mcs_160); + arg->peer_he_tx_mcs_set[WMI_HECAP_TXRX_MCS_NSS_IDX_160] = v; + + arg->peer_he_mcs_count++; + /* fall through */ + + default: + v = le16_to_cpu(he_cap->he_mcs_nss_supp.rx_mcs_80); + arg->peer_he_rx_mcs_set[WMI_HECAP_TXRX_MCS_NSS_IDX_80] = v; + + v = le16_to_cpu(he_cap->he_mcs_nss_supp.tx_mcs_80); + arg->peer_he_tx_mcs_set[WMI_HECAP_TXRX_MCS_NSS_IDX_80] = v; + + arg->peer_he_mcs_count++; + break; + } } static void ath11k_peer_assoc_h_smps(struct ieee80211_sta *sta, @@ -1307,6 +1386,32 @@ static enum wmi_phy_mode ath11k_mac_get_phymode_vht(struct ath11k *ar, return MODE_UNKNOWN; } +static enum wmi_phy_mode ath11k_mac_get_phymode_he(struct ath11k *ar, + struct ieee80211_sta *sta) +{ + if (sta->bandwidth == IEEE80211_STA_RX_BW_160) { + if (sta->he_cap.he_cap_elem.phy_cap_info[0] & + IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G) + return MODE_11AX_HE160; + else if (sta->he_cap.he_cap_elem.phy_cap_info[0] & + IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_80PLUS80_MHZ_IN_5G) + return MODE_11AX_HE80_80; + /* not sure if this is a valid case? */ + return MODE_11AX_HE160; + } + + if (sta->bandwidth == IEEE80211_STA_RX_BW_80) + return MODE_11AX_HE80; + + if (sta->bandwidth == IEEE80211_STA_RX_BW_40) + return MODE_11AX_HE40; + + if (sta->bandwidth == IEEE80211_STA_RX_BW_20) + return MODE_11AX_HE20; + + return MODE_UNKNOWN; +} + static void ath11k_peer_assoc_h_phymode(struct ath11k *ar, struct ieee80211_vif *vif, struct ieee80211_sta *sta, @@ -1328,7 +1433,14 @@ static void ath11k_peer_assoc_h_phymode(struct ath11k *ar, switch (band) { case NL80211_BAND_2GHZ: - if (sta->vht_cap.vht_supported && + if (sta->he_cap.has_he) { + if (sta->bandwidth == IEEE80211_STA_RX_BW_80) + phymode = MODE_11AX_HE80_2G; + else if (sta->bandwidth == IEEE80211_STA_RX_BW_40) + phymode = MODE_11AX_HE40_2G; + else + phymode = MODE_11AX_HE20_2G; + } else if (sta->vht_cap.vht_supported && !ath11k_peer_assoc_h_vht_masked(vht_mcs_mask)) { if (sta->bandwidth == IEEE80211_STA_RX_BW_40) phymode = MODE_11AC_VHT40; @@ -1345,12 +1457,12 @@ static void ath11k_peer_assoc_h_phymode(struct ath11k *ar, } else { phymode = MODE_11B; } - /* TODO: HE */ - break; case NL80211_BAND_5GHZ: - /* Check VHT first */ - if (sta->vht_cap.vht_supported && + /* Check HE first */ + if (sta->he_cap.has_he) { + phymode = ath11k_mac_get_phymode_he(ar, sta); + } else if (sta->vht_cap.vht_supported && !ath11k_peer_assoc_h_vht_masked(vht_mcs_mask)) { phymode = ath11k_mac_get_phymode_vht(ar, sta); } else if (sta->ht_cap.ht_supported && @@ -1362,7 +1474,6 @@ static void ath11k_peer_assoc_h_phymode(struct ath11k *ar, } else { phymode = MODE_11A; } - /* TODO: HE Phymode */ break; default: break; @@ -3126,6 +3237,142 @@ static int ath11k_check_chain_mask(struct ath11k *ar, u32 ant, bool is_tx_ant) return 0; } +static void ath11k_gen_ppe_thresh(struct ath11k_ppe_threshold *fw_ppet, + u8 *he_ppet) +{ + int nss, ru; + u8 bit = 7; + + he_ppet[0] = fw_ppet->numss_m1 & IEEE80211_PPE_THRES_NSS_MASK; + he_ppet[0] |= (fw_ppet->ru_bit_mask << + IEEE80211_PPE_THRES_RU_INDEX_BITMASK_POS) & + IEEE80211_PPE_THRES_RU_INDEX_BITMASK_MASK; + for (nss = 0; nss <= fw_ppet->numss_m1; nss++) { + for (ru = 0; ru < 4; ru++) { + u8 val; + int i; + + if ((fw_ppet->ru_bit_mask & BIT(ru)) == 0) + continue; + val = (fw_ppet->ppet16_ppet8_ru3_ru0[nss] >> (ru * 6)) & + 0x3f; + val = ((val >> 3) & 0x7) | ((val & 0x7) << 3); + for (i = 5; i >= 0; i--) { + he_ppet[bit / 8] |= + ((val >> i) & 0x1) << ((bit % 8)); + bit++; + } + } + } +} + +static int ath11k_mac_copy_he_cap(struct ath11k *ar, + struct ath11k_pdev_cap *cap, + struct ieee80211_sband_iftype_data *data, + int band) +{ + int i, idx = 0; + + for (i = 0; i < NUM_NL80211_IFTYPES; i++) { + struct ieee80211_sta_he_cap *he_cap = &data[idx].he_cap; + struct ath11k_band_cap *band_cap = &cap->band[band]; + struct ieee80211_he_cap_elem *he_cap_elem = + &he_cap->he_cap_elem; + + switch (i) { + case NL80211_IFTYPE_STATION: + case NL80211_IFTYPE_AP: + break; + + default: + continue; + } + + data[idx].types_mask = BIT(i); + he_cap->has_he = true; + memcpy(he_cap_elem->mac_cap_info, band_cap->he_cap_info, + sizeof(he_cap_elem->mac_cap_info)); + memcpy(he_cap_elem->phy_cap_info, band_cap->he_cap_phy_info, + sizeof(he_cap_elem->phy_cap_info)); + + he_cap_elem->mac_cap_info[1] |= + IEEE80211_HE_MAC_CAP1_TF_MAC_PAD_DUR_MASK; + he_cap_elem->phy_cap_info[4] &= + ~IEEE80211_HE_PHY_CAP4_BEAMFORMEE_MAX_STS_UNDER_80MHZ_MASK; + he_cap_elem->phy_cap_info[4] &= + ~IEEE80211_HE_PHY_CAP4_BEAMFORMEE_MAX_STS_ABOVE_80MHZ_MASK; + he_cap_elem->phy_cap_info[4] |= (ar->num_tx_chains - 1) << 2; + + he_cap_elem->phy_cap_info[5] &= + ~IEEE80211_HE_PHY_CAP5_BEAMFORMEE_NUM_SND_DIM_UNDER_80MHZ_MASK; + he_cap_elem->phy_cap_info[5] &= + ~IEEE80211_HE_PHY_CAP5_BEAMFORMEE_NUM_SND_DIM_ABOVE_80MHZ_MASK; + he_cap_elem->phy_cap_info[5] |= ar->num_tx_chains - 1; + + switch (i) { + case NL80211_IFTYPE_AP: + he_cap_elem->phy_cap_info[9] |= + IEEE80211_HE_PHY_CAP9_RX_1024_QAM_LESS_THAN_242_TONE_RU; + break; + case NL80211_IFTYPE_STATION: + he_cap_elem->mac_cap_info[0] &= + ~IEEE80211_HE_MAC_CAP0_TWT_RES; + he_cap_elem->mac_cap_info[0] |= + IEEE80211_HE_MAC_CAP0_TWT_REQ; + he_cap_elem->phy_cap_info[9] |= + IEEE80211_HE_PHY_CAP9_TX_1024_QAM_LESS_THAN_242_TONE_RU; + break; + } + + he_cap->he_mcs_nss_supp.rx_mcs_80 = + cpu_to_le16(band_cap->he_mcs & 0xffff); + he_cap->he_mcs_nss_supp.tx_mcs_80 = + cpu_to_le16(band_cap->he_mcs & 0xffff); + he_cap->he_mcs_nss_supp.rx_mcs_160 = + cpu_to_le16((band_cap->he_mcs >> 16) & 0xffff); + he_cap->he_mcs_nss_supp.tx_mcs_160 = + cpu_to_le16((band_cap->he_mcs >> 16) & 0xffff); + he_cap->he_mcs_nss_supp.rx_mcs_80p80 = + cpu_to_le16((band_cap->he_mcs >> 16) & 0xffff); + he_cap->he_mcs_nss_supp.tx_mcs_80p80 = + cpu_to_le16((band_cap->he_mcs >> 16) & 0xffff); + + memset(he_cap->ppe_thres, 0, sizeof(he_cap->ppe_thres)); + if (he_cap_elem->phy_cap_info[6] & + IEEE80211_HE_PHY_CAP6_PPE_THRESHOLD_PRESENT) + ath11k_gen_ppe_thresh(&band_cap->he_ppet, + he_cap->ppe_thres); + idx++; + } + + return idx; +} + +static void ath11k_mac_setup_he_cap(struct ath11k *ar, + struct ath11k_pdev_cap *cap) +{ + struct ieee80211_supported_band *band = NULL; + int count = 0; + + if (cap->supported_bands & WMI_HOST_WLAN_2G_CAP) { + count = ath11k_mac_copy_he_cap(ar, cap, + ar->mac.iftype[NL80211_BAND_2GHZ], + NL80211_BAND_2GHZ); + band = &ar->mac.sbands[NL80211_BAND_2GHZ]; + band->iftype_data = ar->mac.iftype[NL80211_BAND_2GHZ]; + } + + if (cap->supported_bands & WMI_HOST_WLAN_5G_CAP) { + count = ath11k_mac_copy_he_cap(ar, cap, + ar->mac.iftype[NL80211_BAND_5GHZ], + NL80211_BAND_5GHZ); + band = &ar->mac.sbands[NL80211_BAND_5GHZ]; + band->iftype_data = ar->mac.iftype[NL80211_BAND_5GHZ]; + } + + band->n_iftype_data = count; +} + static int __ath11k_set_antenna(struct ath11k *ar, u32 tx_ant, u32 rx_ant) { int ret; @@ -3161,8 +3408,9 @@ static int __ath11k_set_antenna(struct ath11k *ar, u32 tx_ant, u32 rx_ant) return ret; } - /* Reload HT/VHT capability */ + /* Reload HT/VHT/HE capability */ ath11k_mac_setup_ht_vht_cap(ar, &ar->pdev->cap, NULL); + ath11k_mac_setup_he_cap(ar, &ar->pdev->cap); return 0; } @@ -5207,6 +5455,7 @@ static int ath11k_mac_register(struct ath11k *ar) goto err_free; ath11k_mac_setup_ht_vht_cap(ar, cap, &ht_cap); + ath11k_mac_setup_he_cap(ar, cap); ar->hw->wiphy->available_antennas_rx = cap->rx_chain_mask; ar->hw->wiphy->available_antennas_tx = cap->tx_chain_mask; diff --git a/drivers/net/wireless/ath/ath11k/reg.c b/drivers/net/wireless/ath/ath11k/reg.c index 810050b033c4f..453aa9c069691 100644 --- a/drivers/net/wireless/ath/ath11k/reg.c +++ b/drivers/net/wireless/ath/ath11k/reg.c @@ -141,6 +141,7 @@ int ath11k_reg_update_chan_list(struct ath11k *ar) /* TODO: Set to true/false based on some condition? */ ch->allow_ht = true; ch->allow_vht = true; + ch->allow_he = true; ch->dfs_set = !!(channel->flags & IEEE80211_CHAN_RADAR); diff --git a/drivers/net/wireless/ath/ath11k/wmi.c b/drivers/net/wireless/ath/ath11k/wmi.c index 3ec5dd47cdc85..d077ea30f3c1f 100644 --- a/drivers/net/wireless/ath/ath11k/wmi.c +++ b/drivers/net/wireless/ath/ath11k/wmi.c @@ -753,6 +753,8 @@ static void ath11k_wmi_put_wmi_channel(struct wmi_channel *chan, chan->info |= WMI_CHAN_INFO_ALLOW_HT; if (arg->channel.allow_vht) chan->info |= WMI_CHAN_INFO_ALLOW_VHT; + if (arg->channel.allow_he) + chan->info |= WMI_CHAN_INFO_ALLOW_HE; if (arg->channel.ht40plus) chan->info |= WMI_CHAN_INFO_HT40_PLUS; if (arg->channel.chan_radar) @@ -2150,7 +2152,9 @@ int ath11k_wmi_send_scan_chan_list_cmd(struct ath11k *ar, if (tchan_info->is_chan_passive) chan_info->info |= WMI_CHAN_INFO_PASSIVE; - if (tchan_info->allow_vht) + if (tchan_info->allow_he) + chan_info->info |= WMI_CHAN_INFO_ALLOW_HE; + else if (tchan_info->allow_vht) chan_info->info |= WMI_CHAN_INFO_ALLOW_VHT; else if (tchan_info->allow_ht) chan_info->info |= WMI_CHAN_INFO_ALLOW_HT; diff --git a/drivers/net/wireless/ath/ath11k/wmi.h b/drivers/net/wireless/ath/ath11k/wmi.h index 1110da29cfcd7..fd1ffa593a412 100644 --- a/drivers/net/wireless/ath/ath11k/wmi.h +++ b/drivers/net/wireless/ath/ath11k/wmi.h @@ -2445,6 +2445,7 @@ struct channel_param { is_chan_passive:1, allow_ht:1, allow_vht:1, + allow_he:1, set_agile:1; u32 phy_mode; u32 cfreq1; @@ -3334,7 +3335,10 @@ struct wmi_vdev_install_key_arg { #define WMI_MAX_SUPPORTED_RATES 128 #define WMI_HOST_MAX_HECAP_PHY_SIZE 3 -#define WMI_HOST_MAX_HE_RATE_SET 1 +#define WMI_HOST_MAX_HE_RATE_SET 3 +#define WMI_HECAP_TXRX_MCS_NSS_IDX_80 0 +#define WMI_HECAP_TXRX_MCS_NSS_IDX_160 1 +#define WMI_HECAP_TXRX_MCS_NSS_IDX_80_80 2 struct wmi_rate_set_arg { u32 num_rates; -- GitLab From 6d293d447670da6325cc9c8fb809878d1930c234 Mon Sep 17 00:00:00 2001 From: John Crispin Date: Mon, 25 Nov 2019 16:36:28 +0000 Subject: [PATCH 0047/1437] ath11k: add TWT support Add target wait time wmi calls to the driver. En/disable the support from when the bss_config changes. We ignore the cmd completion events. Signed-off-by: Shashidhar Lakkavalli Signed-off-by: John Crispin Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath11k/mac.c | 12 ++++ drivers/net/wireless/ath/ath11k/wmi.c | 91 +++++++++++++++++++++++++++ drivers/net/wireless/ath/ath11k/wmi.h | 70 +++++++++++++++++++++ 3 files changed, 173 insertions(+) diff --git a/drivers/net/wireless/ath/ath11k/mac.c b/drivers/net/wireless/ath/ath11k/mac.c index 79c22d86e0fe6..70945d121fbae 100644 --- a/drivers/net/wireless/ath/ath11k/mac.c +++ b/drivers/net/wireless/ath/ath11k/mac.c @@ -1189,6 +1189,11 @@ static void ath11k_peer_assoc_h_he(struct ath11k *ar, } } + if (he_cap->he_cap_elem.mac_cap_info[0] & IEEE80211_HE_MAC_CAP0_TWT_RES) + arg->twt_responder = true; + if (he_cap->he_cap_elem.mac_cap_info[0] & IEEE80211_HE_MAC_CAP0_TWT_REQ) + arg->twt_requester = true; + switch (sta->bandwidth) { case IEEE80211_STA_RX_BW_160: if (he_cap->he_cap_elem.phy_cap_info[0] & @@ -1904,6 +1909,13 @@ static void ath11k_mac_op_bss_info_changed(struct ieee80211_hw *hw, !ath11k_mac_vif_chan(arvif->vif, &def)) ath11k_recalculate_mgmt_rate(ar, vif, &def); + if (changed & BSS_CHANGED_TWT) { + if (info->twt_requester || info->twt_responder) + ath11k_wmi_send_twt_enable_cmd(ar, ar->pdev_idx); + else + ath11k_wmi_send_twt_disable_cmd(ar, ar->pdev_idx); + } + mutex_unlock(&ar->conf_mutex); } diff --git a/drivers/net/wireless/ath/ath11k/wmi.c b/drivers/net/wireless/ath/ath11k/wmi.c index d077ea30f3c1f..7aa66e8eecb6d 100644 --- a/drivers/net/wireless/ath/ath11k/wmi.c +++ b/drivers/net/wireless/ath/ath11k/wmi.c @@ -1622,6 +1622,10 @@ ath11k_wmi_copy_peer_flags(struct wmi_peer_assoc_complete_cmd *cmd, cmd->peer_flags |= WMI_PEER_VHT; if (param->he_flag) cmd->peer_flags |= WMI_PEER_HE; + if (param->twt_requester) + cmd->peer_flags |= WMI_PEER_TWT_REQ; + if (param->twt_responder) + cmd->peer_flags |= WMI_PEER_TWT_RESP; } /* Suppress authorization for all AUTH modes that need 4-way handshake @@ -2457,6 +2461,86 @@ int ath11k_wmi_pdev_pktlog_disable(struct ath11k *ar) return ret; } +int +ath11k_wmi_send_twt_enable_cmd(struct ath11k *ar, u32 pdev_id) +{ + struct ath11k_pdev_wmi *wmi = ar->wmi; + struct ath11k_base *ab = wmi->wmi_sc->ab; + struct wmi_twt_enable_params_cmd *cmd; + struct sk_buff *skb; + int ret, len; + + len = sizeof(*cmd); + + skb = ath11k_wmi_alloc_skb(wmi->wmi_sc, len); + if (!skb) + return -ENOMEM; + + cmd = (void *)skb->data; + cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_TWT_ENABLE_CMD) | + FIELD_PREP(WMI_TLV_LEN, len - TLV_HDR_SIZE); + cmd->pdev_id = pdev_id; + cmd->sta_cong_timer_ms = ATH11K_TWT_DEF_STA_CONG_TIMER_MS; + cmd->default_slot_size = ATH11K_TWT_DEF_DEFAULT_SLOT_SIZE; + cmd->congestion_thresh_setup = ATH11K_TWT_DEF_CONGESTION_THRESH_SETUP; + cmd->congestion_thresh_teardown = + ATH11K_TWT_DEF_CONGESTION_THRESH_TEARDOWN; + cmd->congestion_thresh_critical = + ATH11K_TWT_DEF_CONGESTION_THRESH_CRITICAL; + cmd->interference_thresh_teardown = + ATH11K_TWT_DEF_INTERFERENCE_THRESH_TEARDOWN; + cmd->interference_thresh_setup = + ATH11K_TWT_DEF_INTERFERENCE_THRESH_SETUP; + cmd->min_no_sta_setup = ATH11K_TWT_DEF_MIN_NO_STA_SETUP; + cmd->min_no_sta_teardown = ATH11K_TWT_DEF_MIN_NO_STA_TEARDOWN; + cmd->no_of_bcast_mcast_slots = ATH11K_TWT_DEF_NO_OF_BCAST_MCAST_SLOTS; + cmd->min_no_twt_slots = ATH11K_TWT_DEF_MIN_NO_TWT_SLOTS; + cmd->max_no_sta_twt = ATH11K_TWT_DEF_MAX_NO_STA_TWT; + cmd->mode_check_interval = ATH11K_TWT_DEF_MODE_CHECK_INTERVAL; + cmd->add_sta_slot_interval = ATH11K_TWT_DEF_ADD_STA_SLOT_INTERVAL; + cmd->remove_sta_slot_interval = + ATH11K_TWT_DEF_REMOVE_STA_SLOT_INTERVAL; + /* TODO add MBSSID support */ + cmd->mbss_support = 0; + + ret = ath11k_wmi_cmd_send(wmi, skb, + WMI_TWT_ENABLE_CMDID); + if (ret) { + ath11k_warn(ab, "Failed to send WMI_TWT_ENABLE_CMDID"); + dev_kfree_skb(skb); + } + return ret; +} + +int +ath11k_wmi_send_twt_disable_cmd(struct ath11k *ar, u32 pdev_id) +{ + struct ath11k_pdev_wmi *wmi = ar->wmi; + struct ath11k_base *ab = wmi->wmi_sc->ab; + struct wmi_twt_disable_params_cmd *cmd; + struct sk_buff *skb; + int ret, len; + + len = sizeof(*cmd); + + skb = ath11k_wmi_alloc_skb(wmi->wmi_sc, len); + if (!skb) + return -ENOMEM; + + cmd = (void *)skb->data; + cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_TWT_DISABLE_CMD) | + FIELD_PREP(WMI_TLV_LEN, len - TLV_HDR_SIZE); + cmd->pdev_id = pdev_id; + + ret = ath11k_wmi_cmd_send(wmi, skb, + WMI_TWT_DISABLE_CMDID); + if (ret) { + ath11k_warn(ab, "Failed to send WMI_TWT_DIeABLE_CMDID"); + dev_kfree_skb(skb); + } + return ret; +} + static void ath11k_fill_band_to_mac_param(struct ath11k_base *soc, struct wmi_host_pdev_band_to_mac *band_to_mac) @@ -2548,6 +2632,9 @@ ath11k_wmi_copy_resource_config(struct wmi_resource_config *wmi_cfg, wmi_cfg->use_pdev_id = tg_cfg->use_pdev_id; wmi_cfg->flag1 = tg_cfg->atf_config; wmi_cfg->peer_map_unmap_v2_support = tg_cfg->peer_map_unmap_v2_support; + wmi_cfg->sched_params = tg_cfg->sched_params; + wmi_cfg->twt_ap_pdev_count = tg_cfg->twt_ap_pdev_count; + wmi_cfg->twt_ap_sta_count = tg_cfg->twt_ap_sta_count; } static int ath11k_init_cmd_send(struct ath11k_pdev_wmi *wmi, @@ -2739,6 +2826,8 @@ int ath11k_wmi_cmd_init(struct ath11k_base *ab) config.beacon_tx_offload_max_vdev = ab->num_radios * TARGET_MAX_BCN_OFFLD; config.rx_batchmode = TARGET_RX_BATCHMODE; config.peer_map_unmap_v2_support = 1; + config.twt_ap_pdev_count = 2; + config.twt_ap_sta_count = 1000; memcpy(&wmi_sc->wlan_resource_config, &config, sizeof(config)); @@ -5421,6 +5510,8 @@ static void ath11k_wmi_tlv_op_rx(struct ath11k_base *ab, struct sk_buff *skb) case WMI_TBTTOFFSET_EXT_UPDATE_EVENTID: case WMI_VDEV_DELETE_RESP_EVENTID: case WMI_PEER_OPER_MODE_CHANGE_EVENTID: + case WMI_TWT_ENABLE_EVENTID: + case WMI_TWT_DISABLE_EVENTID: ath11k_dbg(ab, ATH11K_DBG_WMI, "ignoring unsupported event 0x%x\n", id); break; diff --git a/drivers/net/wireless/ath/ath11k/wmi.h b/drivers/net/wireless/ath/ath11k/wmi.h index fd1ffa593a412..b5c53f2ac5ccc 100644 --- a/drivers/net/wireless/ath/ath11k/wmi.h +++ b/drivers/net/wireless/ath/ath11k/wmi.h @@ -169,6 +169,9 @@ enum wmi_cmd_group { WMI_GRP_MONITOR = 0x39, WMI_GRP_REGULATORY = 0x3a, WMI_GRP_HW_DATA_FILTER = 0x3b, + WMI_GRP_WLM = 0x3c, + WMI_GRP_11K_OFFLOAD = 0x3d, + WMI_GRP_TWT = 0x3e, }; #define WMI_CMD_GRP(grp_id) (((grp_id) << 12) | 0x1) @@ -530,6 +533,12 @@ enum wmi_tlv_cmd_id { WMI_NDP_RESPONDER_REQ_CMDID, WMI_NDP_END_REQ_CMDID, WMI_HW_DATA_FILTER_CMDID = WMI_TLV_CMD(WMI_GRP_HW_DATA_FILTER), + WMI_TWT_ENABLE_CMDID = WMI_TLV_CMD(WMI_GRP_TWT), + WMI_TWT_DISABLE_CMDID, + WMI_TWT_ADD_DIALOG_CMDID, + WMI_TWT_DEL_DIALOG_CMDID, + WMI_TWT_PAUSE_DIALOG_CMDID, + WMI_TWT_RESUME_DIALOG_CMDID, }; enum wmi_tlv_event_id { @@ -724,6 +733,13 @@ enum wmi_tlv_event_id { WMI_NDP_INDICATION_EVENTID, WMI_NDP_CONFIRM_EVENTID, WMI_NDP_END_INDICATION_EVENTID, + + WMI_TWT_ENABLE_EVENTID = WMI_TLV_CMD(WMI_GRP_TWT), + WMI_TWT_DISABLE_EVENTID, + WMI_TWT_ADD_DIALOG_EVENTID, + WMI_TWT_DEL_DIALOG_EVENTID, + WMI_TWT_PAUSE_DIALOG_EVENTID, + WMI_TWT_RESUME_DIALOG_EVENTID, }; enum wmi_tlv_pdev_param { @@ -2183,6 +2199,9 @@ struct wmi_resource_config { u32 max_num_dbs_scan_duty_cycle; u32 max_num_group_keys; u32 peer_map_unmap_v2_support; + u32 sched_params; + u32 twt_ap_pdev_count; + u32 twt_ap_sta_count; } __packed; struct wmi_service_ready_event { @@ -3401,6 +3420,8 @@ struct peer_assoc_params { u32 peer_he_mcs_count; u32 peer_he_rx_mcs_set[WMI_HOST_MAX_HE_RATE_SET]; u32 peer_he_tx_mcs_set[WMI_HOST_MAX_HE_RATE_SET]; + bool twt_responder; + bool twt_requester; struct ath11k_ppe_threshold peer_ppet; }; @@ -3652,6 +3673,8 @@ struct wmi_unit_test_cmd { #define WMI_PEER_DYN_MIMOPS 0x00020000 #define WMI_PEER_STATIC_MIMOPS 0x00040000 #define WMI_PEER_SPATIAL_MUX 0x00200000 +#define WMI_PEER_TWT_REQ 0x00400000 +#define WMI_PEER_TWT_RESP 0x00800000 #define WMI_PEER_VHT 0x02000000 #define WMI_PEER_80MHZ 0x04000000 #define WMI_PEER_PMF 0x08000000 @@ -4494,6 +4517,48 @@ struct wmi_wmm_params_all_arg { struct wmi_wmm_params_arg ac_vo; }; +#define ATH11K_TWT_DEF_STA_CONG_TIMER_MS 5000 +#define ATH11K_TWT_DEF_DEFAULT_SLOT_SIZE 10 +#define ATH11K_TWT_DEF_CONGESTION_THRESH_SETUP 50 +#define ATH11K_TWT_DEF_CONGESTION_THRESH_TEARDOWN 20 +#define ATH11K_TWT_DEF_CONGESTION_THRESH_CRITICAL 100 +#define ATH11K_TWT_DEF_INTERFERENCE_THRESH_TEARDOWN 80 +#define ATH11K_TWT_DEF_INTERFERENCE_THRESH_SETUP 50 +#define ATH11K_TWT_DEF_MIN_NO_STA_SETUP 10 +#define ATH11K_TWT_DEF_MIN_NO_STA_TEARDOWN 2 +#define ATH11K_TWT_DEF_NO_OF_BCAST_MCAST_SLOTS 2 +#define ATH11K_TWT_DEF_MIN_NO_TWT_SLOTS 2 +#define ATH11K_TWT_DEF_MAX_NO_STA_TWT 500 +#define ATH11K_TWT_DEF_MODE_CHECK_INTERVAL 10000 +#define ATH11K_TWT_DEF_ADD_STA_SLOT_INTERVAL 1000 +#define ATH11K_TWT_DEF_REMOVE_STA_SLOT_INTERVAL 5000 + +struct wmi_twt_enable_params_cmd { + u32 tlv_header; + u32 pdev_id; + u32 sta_cong_timer_ms; + u32 mbss_support; + u32 default_slot_size; + u32 congestion_thresh_setup; + u32 congestion_thresh_teardown; + u32 congestion_thresh_critical; + u32 interference_thresh_teardown; + u32 interference_thresh_setup; + u32 min_no_sta_setup; + u32 min_no_sta_teardown; + u32 no_of_bcast_mcast_slots; + u32 min_no_twt_slots; + u32 max_no_sta_twt; + u32 mode_check_interval; + u32 add_sta_slot_interval; + u32 remove_sta_slot_interval; +}; + +struct wmi_twt_disable_params_cmd { + u32 tlv_header; + u32 pdev_id; +}; + struct target_resource_config { u32 num_vdevs; u32 num_peers; @@ -4553,6 +4618,9 @@ struct target_resource_config { u32 max_bssid_rx_filters; u32 use_pdev_id; u32 peer_map_unmap_v2_support; + u32 sched_params; + u32 twt_ap_pdev_count; + u32 twt_ap_sta_count; }; #define WMI_MAX_MEM_REQS 32 @@ -4676,4 +4744,6 @@ void ath11k_wmi_fw_stats_fill(struct ath11k *ar, struct ath11k_fw_stats *fw_stats, u32 stats_id, char *buf); int ath11k_wmi_simulate_radar(struct ath11k *ar); +int ath11k_wmi_send_twt_enable_cmd(struct ath11k *ar, u32 pdev_id); +int ath11k_wmi_send_twt_disable_cmd(struct ath11k *ar, u32 pdev_id); #endif -- GitLab From 3f8be640077a33ef3bd717ade8b50bd0ef815ba9 Mon Sep 17 00:00:00 2001 From: John Crispin Date: Mon, 25 Nov 2019 16:36:29 +0000 Subject: [PATCH 0048/1437] ath11k: add spatial reuse support Trigger the WMI call en/disabling OBSS PD when the bss config changes or we assoc to an AP that broadcasts the IE. Signed-off-by: John Crispin Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath11k/mac.c | 10 ++++++++ drivers/net/wireless/ath/ath11k/wmi.c | 35 +++++++++++++++++++++++++++ drivers/net/wireless/ath/ath11k/wmi.h | 16 ++++++++++++ 3 files changed, 61 insertions(+) diff --git a/drivers/net/wireless/ath/ath11k/mac.c b/drivers/net/wireless/ath/ath11k/mac.c index 70945d121fbae..c25817c51d873 100644 --- a/drivers/net/wireless/ath/ath11k/mac.c +++ b/drivers/net/wireless/ath/ath11k/mac.c @@ -1612,6 +1612,12 @@ static void ath11k_bss_assoc(struct ieee80211_hw *hw, 1); if (ret) ath11k_warn(ar->ab, "Unable to authorize BSS peer: %d\n", ret); + + ret = ath11k_wmi_send_obss_spr_cmd(ar, arvif->vdev_id, + &bss_conf->he_obss_pd); + if (ret) + ath11k_warn(ar->ab, "failed to set vdev %i OBSS PD parameters: %d\n", + arvif->vdev_id, ret); } static void ath11k_bss_disassoc(struct ieee80211_hw *hw, @@ -1916,6 +1922,10 @@ static void ath11k_mac_op_bss_info_changed(struct ieee80211_hw *hw, ath11k_wmi_send_twt_disable_cmd(ar, ar->pdev_idx); } + if (changed & BSS_CHANGED_HE_OBSS_PD) + ath11k_wmi_send_obss_spr_cmd(ar, arvif->vdev_id, + &info->he_obss_pd); + mutex_unlock(&ar->conf_mutex); } diff --git a/drivers/net/wireless/ath/ath11k/wmi.c b/drivers/net/wireless/ath/ath11k/wmi.c index 7aa66e8eecb6d..2c3c973f5f7c5 100644 --- a/drivers/net/wireless/ath/ath11k/wmi.c +++ b/drivers/net/wireless/ath/ath11k/wmi.c @@ -2541,6 +2541,41 @@ ath11k_wmi_send_twt_disable_cmd(struct ath11k *ar, u32 pdev_id) return ret; } +int +ath11k_wmi_send_obss_spr_cmd(struct ath11k *ar, u32 vdev_id, + struct ieee80211_he_obss_pd *he_obss_pd) +{ + struct ath11k_pdev_wmi *wmi = ar->wmi; + struct ath11k_base *ab = wmi->wmi_sc->ab; + struct wmi_obss_spatial_reuse_params_cmd *cmd; + struct sk_buff *skb; + int ret, len; + + len = sizeof(*cmd); + + skb = ath11k_wmi_alloc_skb(wmi->wmi_sc, len); + if (!skb) + return -ENOMEM; + + cmd = (void *)skb->data; + cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, + WMI_TAG_OBSS_SPATIAL_REUSE_SET_CMD) | + FIELD_PREP(WMI_TLV_LEN, len - TLV_HDR_SIZE); + cmd->vdev_id = vdev_id; + cmd->enable = he_obss_pd->enable; + cmd->obss_min = he_obss_pd->min_offset; + cmd->obss_max = he_obss_pd->max_offset; + + ret = ath11k_wmi_cmd_send(wmi, skb, + WMI_PDEV_OBSS_PD_SPATIAL_REUSE_CMDID); + if (ret) { + ath11k_warn(ab, + "Failed to send WMI_PDEV_OBSS_PD_SPATIAL_REUSE_CMDID"); + dev_kfree_skb(skb); + } + return ret; +} + static void ath11k_fill_band_to_mac_param(struct ath11k_base *soc, struct wmi_host_pdev_band_to_mac *band_to_mac) diff --git a/drivers/net/wireless/ath/ath11k/wmi.h b/drivers/net/wireless/ath/ath11k/wmi.h index b5c53f2ac5ccc..7d5690c65279b 100644 --- a/drivers/net/wireless/ath/ath11k/wmi.h +++ b/drivers/net/wireless/ath/ath11k/wmi.h @@ -172,6 +172,8 @@ enum wmi_cmd_group { WMI_GRP_WLM = 0x3c, WMI_GRP_11K_OFFLOAD = 0x3d, WMI_GRP_TWT = 0x3e, + WMI_GRP_MOTION_DET = 0x3f, + WMI_GRP_SPATIAL_REUSE = 0x40, }; #define WMI_CMD_GRP(grp_id) (((grp_id) << 12) | 0x1) @@ -539,6 +541,9 @@ enum wmi_tlv_cmd_id { WMI_TWT_DEL_DIALOG_CMDID, WMI_TWT_PAUSE_DIALOG_CMDID, WMI_TWT_RESUME_DIALOG_CMDID, + WMI_PDEV_OBSS_PD_SPATIAL_REUSE_CMDID = + WMI_TLV_CMD(WMI_GRP_SPATIAL_REUSE), + WMI_PDEV_OBSS_PD_SPATIAL_REUSE_SET_DEF_OBSS_THRESH_CMDID, }; enum wmi_tlv_event_id { @@ -4559,6 +4564,15 @@ struct wmi_twt_disable_params_cmd { u32 pdev_id; }; +struct wmi_obss_spatial_reuse_params_cmd { + u32 tlv_header; + u32 pdev_id; + u32 enable; + s32 obss_min; + s32 obss_max; + u32 vdev_id; +}; + struct target_resource_config { u32 num_vdevs; u32 num_peers; @@ -4746,4 +4760,6 @@ void ath11k_wmi_fw_stats_fill(struct ath11k *ar, int ath11k_wmi_simulate_radar(struct ath11k *ar); int ath11k_wmi_send_twt_enable_cmd(struct ath11k *ar, u32 pdev_id); int ath11k_wmi_send_twt_disable_cmd(struct ath11k *ar, u32 pdev_id); +int ath11k_wmi_send_obss_spr_cmd(struct ath11k *ar, u32 vdev_id, + struct ieee80211_he_obss_pd *he_obss_pd); #endif -- GitLab From ef39ac1b0d9f523b66757dfc080ecdbed688d669 Mon Sep 17 00:00:00 2001 From: Govind Singh Date: Mon, 25 Nov 2019 13:10:01 +0000 Subject: [PATCH 0049/1437] ath10k: move non-fatal warn logs to dbg level During driver load below warn logs are printed in the console if firmware doesn't support some optional HTC services, ex:pktlog. It is likely some older fw version may not support PKTLOG HTC service as legacy fw uses HTC DATA service for pktlog. Move this log to debug level to remove un-necessary warn message on console. htc.c:803: ath10k_warn(ar, "unsupported HTC service id: %d\n", htc.c:881: ath10k_warn(ar, "unsupported HTC service id: %d\n", Signed-off-by: Govind Singh Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/htc.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/ath/ath10k/htc.c b/drivers/net/wireless/ath/ath10k/htc.c index 1d4d1a1992fe4..0bbd1415d85c5 100644 --- a/drivers/net/wireless/ath/ath10k/htc.c +++ b/drivers/net/wireless/ath/ath10k/htc.c @@ -800,8 +800,8 @@ setup: &ep->ul_pipe_id, &ep->dl_pipe_id); if (status) { - ath10k_warn(ar, "unsupported HTC service id: %d\n", - ep->service_id); + ath10k_dbg(ar, ATH10K_DBG_BOOT, "unsupported HTC service id: %d\n", + ep->service_id); return status; } @@ -878,8 +878,8 @@ static bool ath10k_htc_pktlog_svc_supported(struct ath10k *ar) &ul_pipe_id, &dl_pipe_id); if (status) { - ath10k_warn(ar, "unsupported HTC service id: %d\n", - ATH10K_HTC_SVC_ID_HTT_LOG_MSG); + ath10k_dbg(ar, ATH10K_DBG_BOOT, "unsupported HTC pktlog service id: %d\n", + ATH10K_HTC_SVC_ID_HTT_LOG_MSG); return false; } -- GitLab From 3d94a4a8373bf5f45cf5f939e88b8354dbf2311b Mon Sep 17 00:00:00 2001 From: Ganapathi Bhat Date: Thu, 21 Nov 2019 21:34:38 +0530 Subject: [PATCH 0050/1437] mwifiex: fix possible heap overflow in mwifiex_process_country_ie() mwifiex_process_country_ie() function parse elements of bss descriptor in beacon packet. When processing WLAN_EID_COUNTRY element, there is no upper limit check for country_ie_len before calling memcpy. The destination buffer domain_info->triplet is an array of length MWIFIEX_MAX_TRIPLET_802_11D(83). The remote attacker can build a fake AP with the same ssid as real AP, and send malicous beacon packet with long WLAN_EID_COUNTRY elemen (country_ie_len > 83). Attacker can force STA connect to fake AP on a different channel. When the victim STA connects to fake AP, will trigger the heap buffer overflow. Fix this by checking for length and if found invalid, don not connect to the AP. This fix addresses CVE-2019-14895. Reported-by: huangwen Signed-off-by: Ganapathi Bhat Signed-off-by: Kalle Valo --- drivers/net/wireless/marvell/mwifiex/sta_ioctl.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/marvell/mwifiex/sta_ioctl.c b/drivers/net/wireless/marvell/mwifiex/sta_ioctl.c index 74e50566db1f2..6dd835f1efc21 100644 --- a/drivers/net/wireless/marvell/mwifiex/sta_ioctl.c +++ b/drivers/net/wireless/marvell/mwifiex/sta_ioctl.c @@ -229,6 +229,14 @@ static int mwifiex_process_country_ie(struct mwifiex_private *priv, "11D: skip setting domain info in FW\n"); return 0; } + + if (country_ie_len > + (IEEE80211_COUNTRY_STRING_LEN + MWIFIEX_MAX_TRIPLET_802_11D)) { + mwifiex_dbg(priv->adapter, ERROR, + "11D: country_ie_len overflow!, deauth AP\n"); + return -EINVAL; + } + memcpy(priv->adapter->country_code, &country_ie[2], 2); domain_info->country_code[0] = country_ie[2]; @@ -272,8 +280,9 @@ int mwifiex_bss_start(struct mwifiex_private *priv, struct cfg80211_bss *bss, priv->scan_block = false; if (bss) { - if (adapter->region_code == 0x00) - mwifiex_process_country_ie(priv, bss); + if (adapter->region_code == 0x00 && + mwifiex_process_country_ie(priv, bss)) + return -EINVAL; /* Allocate and fill new bss descriptor */ bss_desc = kzalloc(sizeof(struct mwifiex_bssdescriptor), -- GitLab From 22d0d5ae7a089967e9295a06694aa3e8a812b15e Mon Sep 17 00:00:00 2001 From: Christian Lamparter Date: Sun, 24 Nov 2019 15:40:59 +0100 Subject: [PATCH 0051/1437] ath9k: use iowrite32 over __raw_writel This patch changes the ath9k_pci_owl_loader to use the same iowrite32 memory accessor that ath9k_pci is using to communicate with the PCI(e) chip. This will fix endian issues that came up during testing with loaned AVM Fritz!Box 7360 (Lantiq MIPS SoCs + AR9287). Fixes: 5a4f2040fd07 ("ath9k: add loader for AR92XX (and older) pci(e)") Signed-off-by: Christian Lamparter Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath9k/ath9k_pci_owl_loader.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath9k/ath9k_pci_owl_loader.c b/drivers/net/wireless/ath/ath9k/ath9k_pci_owl_loader.c index 956fa7828d0c8..56d1a7764b9f8 100644 --- a/drivers/net/wireless/ath/ath9k/ath9k_pci_owl_loader.c +++ b/drivers/net/wireless/ath/ath9k/ath9k_pci_owl_loader.c @@ -83,7 +83,7 @@ static int ath9k_pci_fixup(struct pci_dev *pdev, const u16 *cal_data, val = swahb32(val); } - __raw_writel(val, mem + reg); + iowrite32(val, mem + reg); usleep_range(100, 120); } -- GitLab From ada9e3fcc175db4538f5b5e05abf5dedf626e550 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Wed, 27 Nov 2019 15:39:36 +0000 Subject: [PATCH 0052/1437] spi: dw: Correct handling of native chipselect This patch reverts commit 6e0a32d6f376 ("spi: dw: Fix default polarity of native chipselect"). The SPI framework always called the set_cs callback with the logic level it desired on the chip select line, which is what the drivers original handling supported. commit f3186dd87669 ("spi: Optionally use GPIO descriptors for CS GPIOs") changed these symantics, but only in the case of drivers that also support GPIO chip selects, to true meaning apply slave select rather than logic high. This left things in an odd state where a driver that only supports hardware chip selects, the core would handle polarity but if the driver supported GPIOs as well the driver should handle polarity. At this point the reverted change was applied to change the logic in the driver to match new system. This was then broken by commit 3e5ec1db8bfe ("spi: Fix SPI_CS_HIGH setting when using native and GPIO CS") which reverted the core back to consistently calling set_cs with a logic level. This fix reverts the driver code back to its original state to match the current core code. This is probably a better fix as a) the set_cs callback is always called with consistent symantics and b) the inversion for SPI_CS_HIGH can be handled in the core and doesn't need to be coded in each driver supporting it. Fixes: 3e5ec1db8bfe ("spi: Fix SPI_CS_HIGH setting when using native and GPIO CS") Signed-off-by: Charles Keepax Acked-by: Linus Walleij Link: https://lore.kernel.org/r/20191127153936.29719-1-ckeepax@opensource.cirrus.com Signed-off-by: Mark Brown --- drivers/spi/spi-dw.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/spi/spi-dw.c b/drivers/spi/spi-dw.c index 466f5c67843b2..9387f60eb496e 100644 --- a/drivers/spi/spi-dw.c +++ b/drivers/spi/spi-dw.c @@ -129,10 +129,11 @@ void dw_spi_set_cs(struct spi_device *spi, bool enable) struct dw_spi *dws = spi_controller_get_devdata(spi->controller); struct chip_data *chip = spi_get_ctldata(spi); + /* Chip select logic is inverted from spi_set_cs() */ if (chip && chip->cs_control) - chip->cs_control(enable); + chip->cs_control(!enable); - if (enable) + if (!enable) dw_writel(dws, DW_SPI_SER, BIT(spi->chip_select)); else if (dws->cs_override) dw_writel(dws, DW_SPI_SER, 0); -- GitLab From f106904968e2a075e64653b9b79dda9f0f070ab5 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Thu, 28 Nov 2019 09:37:16 +0100 Subject: [PATCH 0053/1437] spi: fsl: Fix GPIO descriptor support This makes the driver actually support looking up GPIO descriptor. A coding mistake in the initial descriptor support patch was that it was failing to turn on the very feature it was implementing. Mea culpa. Cc: Christophe Leroy Reported-by: Christophe Leroy Fixes: 0f0581b24bd0 ("spi: fsl: Convert to use CS GPIO descriptors") Signed-off-by: Linus Walleij Tested-by: Christophe Leroy Link: https://lore.kernel.org/r/20191128083718.39177-1-linus.walleij@linaro.org Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-spi.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/spi/spi-fsl-spi.c b/drivers/spi/spi-fsl-spi.c index 114801a32371c..c87e9c4506c2f 100644 --- a/drivers/spi/spi-fsl-spi.c +++ b/drivers/spi/spi-fsl-spi.c @@ -611,6 +611,7 @@ static struct spi_master * fsl_spi_probe(struct device *dev, master->setup = fsl_spi_setup; master->cleanup = fsl_spi_cleanup; master->transfer_one_message = fsl_spi_do_one_msg; + master->use_gpio_descriptors = true; mpc8xxx_spi = spi_master_get_devdata(master); mpc8xxx_spi->max_bits_per_word = 32; -- GitLab From 71b8f600b034c7f5780f6fb311dabfe331c64feb Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Thu, 28 Nov 2019 09:37:17 +0100 Subject: [PATCH 0054/1437] gpio: Handle counting of Freescale chipselects We have a special quirk to handle the Freescale nonstandard SPI chipselect GPIOs in the gpiolib-of.c file, but it currently only handles the case where the GPIOs are actually requested (gpiod_*get()). We also need to handle that the SPI core attempts to count the GPIOs before use, and that needs a similar quirk in the OF part of the library. Cc: Christophe Leroy Reported-by: Christophe Leroy Fixes: 0f0581b24bd0 ("spi: fsl: Convert to use CS GPIO descriptors") Signed-off-by: Linus Walleij Tested-by: Christophe Leroy Link: https://lore.kernel.org/r/20191128083718.39177-2-linus.walleij@linaro.org Signed-off-by: Mark Brown --- drivers/gpio/gpiolib-of.c | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/drivers/gpio/gpiolib-of.c b/drivers/gpio/gpiolib-of.c index 1eea2c6c2e1d8..4b19e7e26b908 100644 --- a/drivers/gpio/gpiolib-of.c +++ b/drivers/gpio/gpiolib-of.c @@ -23,6 +23,29 @@ #include "gpiolib.h" #include "gpiolib-of.h" +/** + * of_gpio_spi_cs_get_count() - special GPIO counting for SPI + * Some elder GPIO controllers need special quirks. Currently we handle + * the Freescale GPIO controller with bindings that doesn't use the + * established "cs-gpios" for chip selects but instead rely on + * "gpios" for the chip select lines. If we detect this, we redirect + * the counting of "cs-gpios" to count "gpios" transparent to the + * driver. + */ +int of_gpio_spi_cs_get_count(struct device *dev, const char *con_id) +{ + struct device_node *np = dev->of_node; + + if (!IS_ENABLED(CONFIG_SPI_MASTER)) + return 0; + if (!con_id || strcmp(con_id, "cs")) + return 0; + if (!of_device_is_compatible(np, "fsl,spi") && + !of_device_is_compatible(np, "aeroflexgaisler,spictrl")) + return 0; + return of_gpio_named_count(np, "gpios"); +} + /* * This is used by external users of of_gpio_count() from * @@ -35,6 +58,10 @@ int of_gpio_get_count(struct device *dev, const char *con_id) char propname[32]; unsigned int i; + ret = of_gpio_spi_cs_get_count(dev, con_id); + if (ret > 0) + return ret; + for (i = 0; i < ARRAY_SIZE(gpio_suffixes); i++) { if (con_id) snprintf(propname, sizeof(propname), "%s-%s", -- GitLab From 7251953d784baf7e5416afabe030a0e81de1a938 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Thu, 28 Nov 2019 09:37:18 +0100 Subject: [PATCH 0055/1437] spi: fsl: Handle the single hardwired chipselect case The Freescale MPC8xxx had a special quirk for handling a single hardwired chipselect, the case when we're using neither GPIO nor native chip select: when inspecting the device tree and finding zero "cs-gpios" on the device node the code would assume we have a single hardwired chipselect that leaves the device always selected. This quirk is not handled by the new core code, so we need to check the "cs-gpios" explicitly in the driver and set pdata->max_chipselect = 1 which will later fall through to the SPI master ->num_chipselect. Make sure not to assign the chip select handler in this case: there is no handling needed since the chip is always selected, and this is what the old code did as well. Cc: Christophe Leroy Reported-by: Christophe Leroy Fixes: 0f0581b24bd0 ("spi: fsl: Convert to use CS GPIO descriptors") Signed-off-by: Linus Walleij Tested-by: Christophe Leroy (No tested the Link: https://lore.kernel.org/r/20191128083718.39177-3-linus.walleij@linaro.org Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-spi.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/drivers/spi/spi-fsl-spi.c b/drivers/spi/spi-fsl-spi.c index c87e9c4506c2f..4b70887cf4438 100644 --- a/drivers/spi/spi-fsl-spi.c +++ b/drivers/spi/spi-fsl-spi.c @@ -728,8 +728,18 @@ static int of_fsl_spi_probe(struct platform_device *ofdev) } } #endif - - pdata->cs_control = fsl_spi_cs_control; + /* + * Handle the case where we have one hardwired (always selected) + * device on the first "chipselect". Else we let the core code + * handle any GPIOs or native chip selects and assign the + * appropriate callback for dealing with the CS lines. This isn't + * supported on the GRLIB variant. + */ + ret = gpiod_count(dev, "cs"); + if (ret <= 0) + pdata->max_chipselect = 1; + else + pdata->cs_control = fsl_spi_cs_control; } ret = of_address_to_resource(np, 0, &mem); -- GitLab From 1a365e822372ba24c9da0822bc583894f6f3d821 Mon Sep 17 00:00:00 2001 From: Marco Elver Date: Wed, 20 Nov 2019 16:57:15 +0100 Subject: [PATCH 0056/1437] locking/spinlock/debug: Fix various data races This fixes various data races in spinlock_debug. By testing with KCSAN, it is observable that the console gets spammed with data races reports, suggesting these are extremely frequent. Example data race report: read to 0xffff8ab24f403c48 of 4 bytes by task 221 on cpu 2: debug_spin_lock_before kernel/locking/spinlock_debug.c:85 [inline] do_raw_spin_lock+0x9b/0x210 kernel/locking/spinlock_debug.c:112 __raw_spin_lock include/linux/spinlock_api_smp.h:143 [inline] _raw_spin_lock+0x39/0x40 kernel/locking/spinlock.c:151 spin_lock include/linux/spinlock.h:338 [inline] get_partial_node.isra.0.part.0+0x32/0x2f0 mm/slub.c:1873 get_partial_node mm/slub.c:1870 [inline] write to 0xffff8ab24f403c48 of 4 bytes by task 167 on cpu 3: debug_spin_unlock kernel/locking/spinlock_debug.c:103 [inline] do_raw_spin_unlock+0xc9/0x1a0 kernel/locking/spinlock_debug.c:138 __raw_spin_unlock_irqrestore include/linux/spinlock_api_smp.h:159 [inline] _raw_spin_unlock_irqrestore+0x2d/0x50 kernel/locking/spinlock.c:191 spin_unlock_irqrestore include/linux/spinlock.h:393 [inline] free_debug_processing+0x1b3/0x210 mm/slub.c:1214 __slab_free+0x292/0x400 mm/slub.c:2864 As a side-effect, with KCSAN, this eventually locks up the console, most likely due to deadlock, e.g. .. -> printk lock -> spinlock_debug -> KCSAN detects data race -> kcsan_print_report() -> printk lock -> deadlock. This fix will 1) avoid the data races, and 2) allow using lock debugging together with KCSAN. Reported-by: Qian Cai Signed-off-by: Marco Elver Cc: Andrew Morton Cc: Linus Torvalds Cc: Paul E. McKenney Cc: Peter Zijlstra Cc: Thomas Gleixner Cc: Will Deacon Link: https://lkml.kernel.org/r/20191120155715.28089-1-elver@google.com Signed-off-by: Ingo Molnar --- kernel/locking/spinlock_debug.c | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/kernel/locking/spinlock_debug.c b/kernel/locking/spinlock_debug.c index 399669f7eba8e..472dd462a40ca 100644 --- a/kernel/locking/spinlock_debug.c +++ b/kernel/locking/spinlock_debug.c @@ -51,19 +51,19 @@ EXPORT_SYMBOL(__rwlock_init); static void spin_dump(raw_spinlock_t *lock, const char *msg) { - struct task_struct *owner = NULL; + struct task_struct *owner = READ_ONCE(lock->owner); - if (lock->owner && lock->owner != SPINLOCK_OWNER_INIT) - owner = lock->owner; + if (owner == SPINLOCK_OWNER_INIT) + owner = NULL; printk(KERN_EMERG "BUG: spinlock %s on CPU#%d, %s/%d\n", msg, raw_smp_processor_id(), current->comm, task_pid_nr(current)); printk(KERN_EMERG " lock: %pS, .magic: %08x, .owner: %s/%d, " ".owner_cpu: %d\n", - lock, lock->magic, + lock, READ_ONCE(lock->magic), owner ? owner->comm : "", owner ? task_pid_nr(owner) : -1, - lock->owner_cpu); + READ_ONCE(lock->owner_cpu)); dump_stack(); } @@ -80,16 +80,16 @@ static void spin_bug(raw_spinlock_t *lock, const char *msg) static inline void debug_spin_lock_before(raw_spinlock_t *lock) { - SPIN_BUG_ON(lock->magic != SPINLOCK_MAGIC, lock, "bad magic"); - SPIN_BUG_ON(lock->owner == current, lock, "recursion"); - SPIN_BUG_ON(lock->owner_cpu == raw_smp_processor_id(), + SPIN_BUG_ON(READ_ONCE(lock->magic) != SPINLOCK_MAGIC, lock, "bad magic"); + SPIN_BUG_ON(READ_ONCE(lock->owner) == current, lock, "recursion"); + SPIN_BUG_ON(READ_ONCE(lock->owner_cpu) == raw_smp_processor_id(), lock, "cpu recursion"); } static inline void debug_spin_lock_after(raw_spinlock_t *lock) { - lock->owner_cpu = raw_smp_processor_id(); - lock->owner = current; + WRITE_ONCE(lock->owner_cpu, raw_smp_processor_id()); + WRITE_ONCE(lock->owner, current); } static inline void debug_spin_unlock(raw_spinlock_t *lock) @@ -99,8 +99,8 @@ static inline void debug_spin_unlock(raw_spinlock_t *lock) SPIN_BUG_ON(lock->owner != current, lock, "wrong owner"); SPIN_BUG_ON(lock->owner_cpu != raw_smp_processor_id(), lock, "wrong CPU"); - lock->owner = SPINLOCK_OWNER_INIT; - lock->owner_cpu = -1; + WRITE_ONCE(lock->owner, SPINLOCK_OWNER_INIT); + WRITE_ONCE(lock->owner_cpu, -1); } /* @@ -187,8 +187,8 @@ static inline void debug_write_lock_before(rwlock_t *lock) static inline void debug_write_lock_after(rwlock_t *lock) { - lock->owner_cpu = raw_smp_processor_id(); - lock->owner = current; + WRITE_ONCE(lock->owner_cpu, raw_smp_processor_id()); + WRITE_ONCE(lock->owner, current); } static inline void debug_write_unlock(rwlock_t *lock) @@ -197,8 +197,8 @@ static inline void debug_write_unlock(rwlock_t *lock) RWLOCK_BUG_ON(lock->owner != current, lock, "wrong owner"); RWLOCK_BUG_ON(lock->owner_cpu != raw_smp_processor_id(), lock, "wrong CPU"); - lock->owner = SPINLOCK_OWNER_INIT; - lock->owner_cpu = -1; + WRITE_ONCE(lock->owner, SPINLOCK_OWNER_INIT); + WRITE_ONCE(lock->owner_cpu, -1); } void do_raw_write_lock(rwlock_t *lock) -- GitLab From 86d4def890fdc25cee378c346dc31ca2ee0ef713 Mon Sep 17 00:00:00 2001 From: Ganesh Sesetti Date: Wed, 27 Nov 2019 18:29:45 +0200 Subject: [PATCH 0057/1437] ath11k: Fix htt stats sounding info and pdev cca stats The Previous configuartion of htt stats sounding info and pdev cca stats are invalid due to that getting time out error. Changing htt stats sounding info value from 0xFF to 0x00 and htt pdev cca stats from 0x10 to 0x00 Signed-off-by: Ganesh Sesetti Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath11k/dp.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/ath/ath11k/dp.h b/drivers/net/wireless/ath/ath11k/dp.h index 4db71173db62c..eb0f3df131c5e 100644 --- a/drivers/net/wireless/ath/ath11k/dp.h +++ b/drivers/net/wireless/ath/ath11k/dp.h @@ -1365,8 +1365,8 @@ struct htt_ext_stats_cfg_cmd { #define HTT_STAT_DEFAULT_CFG0_ALL_CMDQS 0xffff #define HTT_STAT_DEFAULT_CFG0_ALL_RINGS 0xffff #define HTT_STAT_DEFAULT_CFG0_ACTIVE_PEERS 0xff -#define HTT_STAT_DEFAULT_CFG0_CCA_CUMULATIVE 0x10 -#define HTT_STAT_DEFAULT_CFG0_ACTIVE_VDEVS 0xff +#define HTT_STAT_DEFAULT_CFG0_CCA_CUMULATIVE 0x00 +#define HTT_STAT_DEFAULT_CFG0_ACTIVE_VDEVS 0x00 /* HTT_DBG_EXT_STATS_PEER_INFO * PARAMS: -- GitLab From 5e97128759b49b32740e1486007f239f2d7a9b8a Mon Sep 17 00:00:00 2001 From: Karthikeyan Periyasamy Date: Wed, 27 Nov 2019 18:29:47 +0200 Subject: [PATCH 0058/1437] ath11k: fix resource leak in ath11k_mac_sta_state Handled the error case with proper resource cleanup and moved the handling into a separate function from ath11k_mac_sta_state. Signed-off-by: Karthikeyan Periyasamy Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath11k/mac.c | 144 +++++++++++++++----------- 1 file changed, 86 insertions(+), 58 deletions(-) diff --git a/drivers/net/wireless/ath/ath11k/mac.c b/drivers/net/wireless/ath/ath11k/mac.c index c25817c51d873..6f82fdbbd358b 100644 --- a/drivers/net/wireless/ath/ath11k/mac.c +++ b/drivers/net/wireless/ath/ath11k/mac.c @@ -2682,6 +2682,87 @@ static void ath11k_mac_dec_num_stations(struct ath11k_vif *arvif, ar->num_stations--; } +static int ath11k_mac_station_add(struct ath11k *ar, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct ath11k_base *ab = ar->ab; + struct ath11k_vif *arvif = ath11k_vif_to_arvif(vif); + struct ath11k_sta *arsta = (struct ath11k_sta *)sta->drv_priv; + struct peer_create_params peer_param; + int ret; + + lockdep_assert_held(&ar->conf_mutex); + + ret = ath11k_mac_inc_num_stations(arvif, sta); + if (ret) { + ath11k_warn(ab, "refusing to associate station: too many connected already (%d)\n", + ar->max_num_stations); + goto exit; + } + + arsta->rx_stats = kzalloc(sizeof(*arsta->rx_stats), GFP_KERNEL); + if (!arsta->rx_stats) { + ret = -ENOMEM; + goto dec_num_station; + } + + peer_param.vdev_id = arvif->vdev_id; + peer_param.peer_addr = sta->addr; + peer_param.peer_type = WMI_PEER_TYPE_DEFAULT; + + ret = ath11k_peer_create(ar, arvif, sta, &peer_param); + if (ret) { + ath11k_warn(ab, "Failed to add peer: %pM for VDEV: %d\n", + sta->addr, arvif->vdev_id); + goto free_rx_stats; + } + + ath11k_dbg(ab, ATH11K_DBG_MAC, "Added peer: %pM for VDEV: %d\n", + sta->addr, arvif->vdev_id); + + if (ath11k_debug_is_extd_tx_stats_enabled(ar)) { + arsta->tx_stats = kzalloc(sizeof(*arsta->tx_stats), GFP_KERNEL); + if (!arsta->tx_stats) { + ret = -ENOMEM; + goto free_peer; + } + } + + if (ieee80211_vif_is_mesh(vif)) { + ret = ath11k_wmi_set_peer_param(ar, sta->addr, + arvif->vdev_id, + WMI_PEER_USE_4ADDR, 1); + if (ret) { + ath11k_warn(ab, "failed to STA %pM 4addr capability: %d\n", + sta->addr, ret); + goto free_tx_stats; + } + } + + ret = ath11k_dp_peer_setup(ar, arvif->vdev_id, sta->addr); + if (ret) { + ath11k_warn(ab, "failed to setup dp for peer %pM on vdev %i (%d)\n", + sta->addr, arvif->vdev_id, ret); + goto free_tx_stats; + } + + return 0; + +free_tx_stats: + kfree(arsta->tx_stats); + arsta->tx_stats = NULL; +free_peer: + ath11k_peer_delete(ar, arvif->vdev_id, sta->addr); +free_rx_stats: + kfree(arsta->rx_stats); + arsta->rx_stats = NULL; +dec_num_station: + ath11k_mac_dec_num_stations(arvif, sta); +exit: + return ret; +} + static int ath11k_mac_op_sta_state(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_sta *sta, @@ -2691,7 +2772,6 @@ static int ath11k_mac_op_sta_state(struct ieee80211_hw *hw, struct ath11k *ar = hw->priv; struct ath11k_vif *arvif = ath11k_vif_to_arvif(vif); struct ath11k_sta *arsta = (struct ath11k_sta *)sta->drv_priv; - struct peer_create_params peer_param; int ret = 0; /* cancel must be done outside the mutex to avoid deadlock */ @@ -2707,60 +2787,10 @@ static int ath11k_mac_op_sta_state(struct ieee80211_hw *hw, arsta->arvif = arvif; INIT_WORK(&arsta->update_wk, ath11k_sta_rc_update_wk); - ret = ath11k_mac_inc_num_stations(arvif, sta); - if (ret) { - ath11k_warn(ar->ab, "refusing to associate station: too many connected already (%d)\n", - ar->max_num_stations); - goto exit; - } - - arsta->rx_stats = kzalloc(sizeof(*arsta->rx_stats), GFP_KERNEL); - if (!arsta->rx_stats) { - ret = -ENOMEM; - goto exit; - } - - peer_param.vdev_id = arvif->vdev_id; - peer_param.peer_addr = sta->addr; - peer_param.peer_type = WMI_PEER_TYPE_DEFAULT; - ret = ath11k_peer_create(ar, arvif, sta, &peer_param); - if (ret) { - ath11k_warn(ar->ab, "Failed to add peer: %pM for VDEV: %d\n", + ret = ath11k_mac_station_add(ar, vif, sta); + if (ret) + ath11k_warn(ar->ab, "Failed to add station: %pM for VDEV: %d\n", sta->addr, arvif->vdev_id); - ath11k_mac_dec_num_stations(arvif, sta); - goto exit; - } - - ath11k_info(ar->ab, "Added peer: %pM for VDEV: %d\n", - sta->addr, arvif->vdev_id); - - if (ath11k_debug_is_extd_tx_stats_enabled(ar)) { - arsta->tx_stats = kzalloc(sizeof(*arsta->tx_stats), - GFP_KERNEL); - if (!arsta->tx_stats) { - ret = -ENOMEM; - goto exit; - } - } - - if (ieee80211_vif_is_mesh(vif)) { - ret = ath11k_wmi_set_peer_param(ar, sta->addr, - arvif->vdev_id, - WMI_PEER_USE_4ADDR, 1); - if (ret) { - ath11k_warn(ar->ab, "failed to STA %pM 4addr capability: %d\n", - sta->addr, ret); - goto exit; - } - } - - ret = ath11k_dp_peer_setup(ar, arvif->vdev_id, sta->addr); - if (ret) { - ath11k_warn(ar->ab, "failed to setup dp for peer %pM on vdev %i (%d)\n", - sta->addr, arvif->vdev_id, ret); - ath11k_peer_delete(ar, arvif->vdev_id, sta->addr); - ath11k_mac_dec_num_stations(arvif, sta); - } } else if ((old_state == IEEE80211_STA_NONE && new_state == IEEE80211_STA_NOTEXIST)) { ath11k_dp_peer_cleanup(ar, arvif->vdev_id, sta->addr); @@ -2770,9 +2800,8 @@ static int ath11k_mac_op_sta_state(struct ieee80211_hw *hw, ath11k_warn(ar->ab, "Failed to delete peer: %pM for VDEV: %d\n", sta->addr, arvif->vdev_id); else - ath11k_info(ar->ab, - "Removed peer: %pM for VDEV: %d\n", - sta->addr, arvif->vdev_id); + ath11k_dbg(ar->ab, ATH11K_DBG_MAC, "Removed peer: %pM for VDEV: %d\n", + sta->addr, arvif->vdev_id); ath11k_mac_dec_num_stations(arvif, sta); @@ -2809,7 +2838,6 @@ static int ath11k_mac_op_sta_state(struct ieee80211_hw *hw, sta->addr); } -exit: mutex_unlock(&ar->conf_mutex); return ret; } -- GitLab From 5b90fc760db5a969ed26d70f8e62c91915f012bd Mon Sep 17 00:00:00 2001 From: Anilkumar Kolli Date: Wed, 27 Nov 2019 18:29:50 +0200 Subject: [PATCH 0059/1437] ath11k: fix wmi service ready ext tlv parsing The current ath11k driver failed to parse wmi_tlv_svc_rdy_ext_parse if there is change in wmi_mac_phy_capabilities length with below error. ath11k c000000.wifi1: failed to extract mac caps, idx :0 ath11k c000000.wifi1: failed to parse tlv -22 This is needed to get firmware version WLAN.HK.2.0.0.1-00240-QCAHKSWPL_SILICONZ-1 working. Signed-off-by: Anilkumar Kolli Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath11k/wmi.c | 14 +++++++++++--- drivers/net/wireless/ath/ath11k/wmi.h | 1 - 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/ath/ath11k/wmi.c b/drivers/net/wireless/ath/ath11k/wmi.c index 2c3c973f5f7c5..ccebf7ce420a2 100644 --- a/drivers/net/wireless/ath/ath11k/wmi.c +++ b/drivers/net/wireless/ath/ath11k/wmi.c @@ -310,7 +310,7 @@ ath11k_pull_mac_phy_cap_svc_ready_ext(struct ath11k_pdev_wmi *wmi_handle, if (phy_id >= hal_reg_caps->num_phy) return -EINVAL; - mac_phy_caps = &wmi_mac_phy_caps[phy_idx]; + mac_phy_caps = wmi_mac_phy_caps + phy_idx; pdev->pdev_id = mac_phy_caps->pdev_id; pdev_cap->supported_bands = mac_phy_caps->supported_bands; @@ -2959,6 +2959,15 @@ static int ath11k_wmi_tlv_mac_phy_caps_parse(struct ath11k_base *soc, if (svc_rdy_ext->n_mac_phy_caps >= svc_rdy_ext->tot_phy_id) return -ENOBUFS; + len = min_t(u16, len, sizeof(struct wmi_mac_phy_capabilities)); + if (!svc_rdy_ext->n_mac_phy_caps) { + svc_rdy_ext->mac_phy_caps = kzalloc((svc_rdy_ext->tot_phy_id) * len, + GFP_ATOMIC); + if (!svc_rdy_ext->mac_phy_caps) + return -ENOMEM; + } + + memcpy(svc_rdy_ext->mac_phy_caps + svc_rdy_ext->n_mac_phy_caps, ptr, len); svc_rdy_ext->n_mac_phy_caps++; return 0; } @@ -3092,8 +3101,6 @@ static int ath11k_wmi_tlv_svc_rdy_ext_parse(struct ath11k_base *ab, svc_rdy_ext->hw_mode_done = true; } else if (!svc_rdy_ext->mac_phy_done) { svc_rdy_ext->n_mac_phy_caps = 0; - svc_rdy_ext->mac_phy_caps = - (struct wmi_mac_phy_capabilities *)ptr; ret = ath11k_wmi_tlv_iter(ab, ptr, len, ath11k_wmi_tlv_mac_phy_caps_parse, svc_rdy_ext); @@ -3134,6 +3141,7 @@ static int ath11k_service_ready_ext_event(struct ath11k_base *ab, return ret; } + kfree(svc_rdy_ext.mac_phy_caps); return 0; } diff --git a/drivers/net/wireless/ath/ath11k/wmi.h b/drivers/net/wireless/ath/ath11k/wmi.h index 7d5690c65279b..6a0468094694e 100644 --- a/drivers/net/wireless/ath/ath11k/wmi.h +++ b/drivers/net/wireless/ath/ath11k/wmi.h @@ -2276,7 +2276,6 @@ struct wmi_hw_mode_capabilities { #define WMI_MAX_HECAP_PHY_SIZE (3) struct wmi_mac_phy_capabilities { - u32 tlv_header; u32 hw_mode_id; u32 pdev_id; u32 phy_id; -- GitLab From 0f37fbf43c3fc39a6c49c5a27846df937d217356 Mon Sep 17 00:00:00 2001 From: Anilkumar Kolli Date: Wed, 27 Nov 2019 18:29:53 +0200 Subject: [PATCH 0060/1437] ath11k: update tcl cmd descriptor parameters for STA mode It is observed that ath11k STA mode UL throughput is low. This is due to packets delivered to FW from TCL instead of TQM. TCL AST search fail causes packet delivered to FW, fix this by properly configuring the TCL address search type and ast_hash. STA UL throughput is improved 10times with 11AC AP. Signed-off-by: Anilkumar Kolli Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath11k/dp.c | 9 ++++----- drivers/net/wireless/ath/ath11k/dp_rx.c | 2 +- drivers/net/wireless/ath/ath11k/hal_desc.h | 5 ++--- drivers/net/wireless/ath/ath11k/hal_tx.c | 6 +++--- drivers/net/wireless/ath/ath11k/hal_tx.h | 4 ++-- 5 files changed, 12 insertions(+), 14 deletions(-) diff --git a/drivers/net/wireless/ath/ath11k/dp.c b/drivers/net/wireless/ath/ath11k/dp.c index aa2b28189cda5..ff510e821a291 100644 --- a/drivers/net/wireless/ath/ath11k/dp.c +++ b/drivers/net/wireless/ath/ath11k/dp.c @@ -758,13 +758,12 @@ int ath11k_dp_htt_connect(struct ath11k_dp *dp) static void ath11k_dp_update_vdev_search(struct ath11k_vif *arvif) { - /* Enable AddrY (SA based search) for STA mode. All other modes it - * is going to be AddrX (DA based search). For STA mode, set search - * type based on AST value. - */ + /* For STA mode, enable address search index, + * tcl uses ast_hash value in the descriptor. + */ switch (arvif->vdev_type) { case WMI_VDEV_TYPE_STA: - arvif->hal_addr_search_flags = HAL_TX_ADDRY_EN; + arvif->hal_addr_search_flags = HAL_TX_ADDRX_EN; arvif->search_type = HAL_TX_ADDR_SEARCH_INDEX; break; case WMI_VDEV_TYPE_AP: diff --git a/drivers/net/wireless/ath/ath11k/dp_rx.c b/drivers/net/wireless/ath/ath11k/dp_rx.c index 7aade0314e614..396658be6f26b 100644 --- a/drivers/net/wireless/ath/ath11k/dp_rx.c +++ b/drivers/net/wireless/ath/ath11k/dp_rx.c @@ -1312,7 +1312,7 @@ void ath11k_dp_htt_htc_t2h_msg_handler(struct ath11k_base *ab, ath11k_dp_get_mac_addr(resp->peer_map_ev.mac_addr_l32, peer_mac_h16, mac_addr); ast_hash = FIELD_GET(HTT_T2H_PEER_MAP_INFO2_AST_HASH_VAL, - resp->peer_map_ev.info1); + resp->peer_map_ev.info2); ath11k_peer_map_event(ab, vdev_id, peer_id, mac_addr, ast_hash); break; case HTT_T2H_MSG_TYPE_PEER_UNMAP: diff --git a/drivers/net/wireless/ath/ath11k/hal_desc.h b/drivers/net/wireless/ath/ath11k/hal_desc.h index 35e191b772718..5e200380cca4e 100644 --- a/drivers/net/wireless/ath/ath11k/hal_desc.h +++ b/drivers/net/wireless/ath/ath11k/hal_desc.h @@ -934,9 +934,8 @@ struct hal_reo_flush_cache { #define HAL_TCL_DATA_CMD_INFO0_ENCRYPT_TYPE GENMASK(7, 4) #define HAL_TCL_DATA_CMD_INFO0_SRC_BUF_SWAP BIT(8) #define HAL_TCL_DATA_CMD_INFO0_LNK_META_SWAP BIT(9) -#define HAL_TCL_DATA_CMD_INFO0_SEARCH_TYPE BIT(12) -#define HAL_TCL_DATA_CMD_INFO0_ADDRX_EN BIT(14) -#define HAL_TCL_DATA_CMD_INFO0_ADDRY_EN BIT(15) +#define HAL_TCL_DATA_CMD_INFO0_SEARCH_TYPE GENMASK(13, 12) +#define HAL_TCL_DATA_CMD_INFO0_ADDR_EN GENMASK(15, 14) #define HAL_TCL_DATA_CMD_INFO0_CMD_NUM GENMASK(31, 16) #define HAL_TCL_DATA_CMD_INFO1_DATA_LEN GENMASK(15, 0) diff --git a/drivers/net/wireless/ath/ath11k/hal_tx.c b/drivers/net/wireless/ath/ath11k/hal_tx.c index bb27591023fe7..cbe5497987620 100644 --- a/drivers/net/wireless/ath/ath11k/hal_tx.c +++ b/drivers/net/wireless/ath/ath11k/hal_tx.c @@ -52,9 +52,9 @@ void ath11k_hal_tx_cmd_desc_setup(struct ath11k_base *ab, void *cmd, FIELD_PREP(HAL_TCL_DATA_CMD_INFO0_ENCAP_TYPE, ti->encap_type) | FIELD_PREP(HAL_TCL_DATA_CMD_INFO0_ENCRYPT_TYPE, ti->encrypt_type) | - FIELD_PREP(HAL_TCL_DATA_CMD_INFO0_ADDRX_EN, - ti->addr_search_flags) | - FIELD_PREP(HAL_TCL_DATA_CMD_INFO0_ADDRY_EN, + FIELD_PREP(HAL_TCL_DATA_CMD_INFO0_SEARCH_TYPE, + ti->search_type) | + FIELD_PREP(HAL_TCL_DATA_CMD_INFO0_ADDR_EN, ti->addr_search_flags) | FIELD_PREP(HAL_TCL_DATA_CMD_INFO0_CMD_NUM, ti->meta_data_flags); diff --git a/drivers/net/wireless/ath/ath11k/hal_tx.h b/drivers/net/wireless/ath/ath11k/hal_tx.h index f955070bcf1e4..5217eaf9da501 100644 --- a/drivers/net/wireless/ath/ath11k/hal_tx.h +++ b/drivers/net/wireless/ath/ath11k/hal_tx.h @@ -11,8 +11,8 @@ #define HAL_TX_ADDRX_EN 1 #define HAL_TX_ADDRY_EN 2 -#define HAL_TX_ADDR_SEARCH_INDEX 0 -#define HAL_TX_ADDR_SEARCH_DEFAULT 1 +#define HAL_TX_ADDR_SEARCH_DEFAULT 0 +#define HAL_TX_ADDR_SEARCH_INDEX 1 struct hal_tx_info { u16 meta_data_flags; /* %HAL_TCL_DATA_CMD_INFO0_META_ */ -- GitLab From 293cb5839729b186f951a82289aa5e0257c6d1b8 Mon Sep 17 00:00:00 2001 From: John Crispin Date: Wed, 27 Nov 2019 18:29:56 +0200 Subject: [PATCH 0061/1437] ath11k: optimize RX path latency This patch drops ath11k_hal_rx_parse_dst_ring_desc(). This function was creating a huge amount of load, which lead to a signifcant latency delay when processing data in the RX path. Pegging the processing on a specific core and running perf --top we get the following output when running HE80 at a fixed bandwidth of 1gbit. with patch 19.19% [ath11k] [k] ath11k_dp_process_rx 5.02% [ath11k] [k] ath11k_dp_rx_tid_del_func 4.39% [kernel] [k] v7_dma_inv_range 4.15% [kernel] [k] __slab_alloc.constprop.1 4.03% [kernel] [k] dev_gro_receive 3.86% [kernel] [k] tcp_gro_receive 3.07% [ip_tables] [k] ipt_do_table 2.96% [kernel] [k] dma_cache_maint_page without patch 21.64% [ath11k] [k] ath11k_hal_rx_parse_dst_ring_desc 10.80% [ath11k] [k] ath11k_dp_process_rx 3.77% [kernel] [k] v7_dma_inv_range 3.48% [kernel] [k] dev_gro_receive 3.32% [ath11k] [k] ath11k_dp_rx_tid_del_func 3.17% [mac80211] [k] ieee80211_rx_napi 2.70% [kernel] [k] dma_cache_maint_page 2.65% [mac80211] [k] ieee80211_sta_ps_transition When removing the the bandwidth limit and rerunning the test we see an overall throughput improvement of 3-400mbit when running 4x4 HE80. Signed-off-by: Shashidhar Lakkavalli Signed-off-by: John Crispin Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath11k/dp_rx.c | 47 +++++++++++++---------- drivers/net/wireless/ath/ath11k/hal_rx.c | 49 ++---------------------- drivers/net/wireless/ath/ath11k/hal_rx.h | 28 +------------- 3 files changed, 31 insertions(+), 93 deletions(-) diff --git a/drivers/net/wireless/ath/ath11k/dp_rx.c b/drivers/net/wireless/ath/ath11k/dp_rx.c index 396658be6f26b..321a2bb657e8b 100644 --- a/drivers/net/wireless/ath/ath11k/dp_rx.c +++ b/drivers/net/wireless/ath/ath11k/dp_rx.c @@ -2124,7 +2124,6 @@ int ath11k_dp_process_rx(struct ath11k_base *ab, int mac_id, struct ieee80211_rx_status *rx_status = &dp->rx_status; struct dp_rxdma_ring *rx_ring = &dp->rx_refill_buf_ring; struct hal_srng *srng; - struct hal_rx_meta_info meta_info; struct sk_buff *msdu; struct sk_buff_head msdu_list; struct sk_buff_head amsdu_list; @@ -2160,11 +2159,14 @@ int ath11k_dp_process_rx(struct ath11k_base *ab, int mac_id, try_again: while ((rx_desc = ath11k_hal_srng_dst_get_next_entry(ab, srng))) { - memset(&meta_info, 0, sizeof(meta_info)); - ath11k_hal_rx_parse_dst_ring_desc(ab, rx_desc, &meta_info); + struct hal_reo_dest_ring *desc = (struct hal_reo_dest_ring *)rx_desc; + enum hal_reo_dest_ring_push_reason push_reason; + u32 cookie; + cookie = FIELD_GET(BUFFER_ADDR_INFO1_SW_COOKIE, + desc->buf_addr_info.info1); buf_id = FIELD_GET(DP_RXDMA_BUF_COOKIE_BUF_ID, - meta_info.msdu_meta.cookie); + cookie); spin_lock_bh(&rx_ring->idr_lock); msdu = idr_find(&rx_ring->bufs_idr, buf_id); if (!msdu) { @@ -2184,7 +2186,9 @@ try_again: num_buffs_reaped++; - if (meta_info.push_reason != + push_reason = FIELD_GET(HAL_REO_DEST_RING_INFO0_PUSH_REASON, + desc->info0); + if (push_reason != HAL_REO_DEST_RING_PUSH_REASON_ROUTING_INSTRUCTION) { /* TODO: Check if the msdu can be sent up for processing */ dev_kfree_skb_any(msdu); @@ -2192,9 +2196,12 @@ try_again: continue; } - rxcb->is_first_msdu = meta_info.msdu_meta.first; - rxcb->is_last_msdu = meta_info.msdu_meta.last; - rxcb->is_continuation = meta_info.msdu_meta.continuation; + rxcb->is_first_msdu = !!(desc->rx_msdu_info.info0 & + RX_MSDU_DESC_INFO0_FIRST_MSDU_IN_MPDU); + rxcb->is_last_msdu = !!(desc->rx_msdu_info.info0 & + RX_MSDU_DESC_INFO0_LAST_MSDU_IN_MPDU); + rxcb->is_continuation = !!(desc->rx_msdu_info.info0 & + RX_MSDU_DESC_INFO0_MSDU_CONTINUATION); rxcb->mac_id = mac_id; __skb_queue_tail(&msdu_list, msdu); @@ -2770,12 +2777,11 @@ exit: int ath11k_dp_process_rx_err(struct ath11k_base *ab, struct napi_struct *napi, int budget) { - struct hal_rx_msdu_meta meta[HAL_NUM_RX_MSDUS_PER_LINK_DESC]; + u32 msdu_cookies[HAL_NUM_RX_MSDUS_PER_LINK_DESC]; struct dp_link_desc_bank *link_desc_banks; enum hal_rx_buf_return_buf_manager rbm; int tot_n_bufs_reaped, quota, ret, i; int n_bufs_reaped[MAX_RADIOS] = {0}; - struct hal_rx_meta_info meta_info; struct dp_rxdma_ring *rx_ring; struct dp_srng *reo_except; u32 desc_bank, num_msdus; @@ -2803,6 +2809,8 @@ int ath11k_dp_process_rx_err(struct ath11k_base *ab, struct napi_struct *napi, while (budget && (desc = ath11k_hal_srng_dst_get_next_entry(ab, srng))) { + struct hal_reo_dest_ring *reo_desc = (struct hal_reo_dest_ring *)desc; + ab->soc_stats.err_ring_pkts++; ret = ath11k_hal_desc_reo_parse_err(ab, desc, &paddr, &desc_bank); @@ -2813,7 +2821,7 @@ int ath11k_dp_process_rx_err(struct ath11k_base *ab, struct napi_struct *napi, } link_desc_va = link_desc_banks[desc_bank].vaddr + (paddr - link_desc_banks[desc_bank].paddr); - ath11k_hal_rx_msdu_link_info_get(link_desc_va, &num_msdus, meta, + ath11k_hal_rx_msdu_link_info_get(link_desc_va, &num_msdus, msdu_cookies, &rbm); if (rbm != HAL_RX_BUF_RBM_WBM_IDLE_DESC_LIST && rbm != HAL_RX_BUF_RBM_SW3_BM) { @@ -2824,10 +2832,7 @@ int ath11k_dp_process_rx_err(struct ath11k_base *ab, struct napi_struct *napi, continue; } - memset(&meta_info, 0, sizeof(meta_info)); - ath11k_hal_rx_parse_dst_ring_desc(ab, desc, &meta_info); - - is_frag = meta_info.mpdu_meta.frag; + is_frag = !!(reo_desc->rx_mpdu_info.info0 & RX_MPDU_DESC_INFO0_FRAG_FLAG); /* Return the link desc back to wbm idle list */ ath11k_dp_rx_link_desc_return(ab, desc, @@ -2835,10 +2840,10 @@ int ath11k_dp_process_rx_err(struct ath11k_base *ab, struct napi_struct *napi, for (i = 0; i < num_msdus; i++) { buf_id = FIELD_GET(DP_RXDMA_BUF_COOKIE_BUF_ID, - meta[i].cookie); + msdu_cookies[i]); mac_id = FIELD_GET(DP_RXDMA_BUF_COOKIE_PDEV_ID, - meta[i].cookie); + msdu_cookies[i]); ar = ab->pdevs[mac_id].ar; @@ -3192,7 +3197,7 @@ int ath11k_dp_process_rxdma_err(struct ath11k_base *ab, int mac_id, int budget) struct dp_rxdma_ring *rx_ring = &ar->dp.rx_refill_buf_ring; struct dp_link_desc_bank *link_desc_banks = ab->dp.link_desc_banks; struct hal_srng *srng; - struct hal_rx_msdu_meta meta[HAL_NUM_RX_MSDUS_PER_LINK_DESC]; + u32 msdu_cookies[HAL_NUM_RX_MSDUS_PER_LINK_DESC]; enum hal_rx_buf_return_buf_manager rbm; enum hal_reo_entr_rxdma_ecode rxdma_err_code; struct ath11k_skb_rxcb *rxcb; @@ -3226,12 +3231,12 @@ int ath11k_dp_process_rxdma_err(struct ath11k_base *ab, int mac_id, int budget) link_desc_va = link_desc_banks[desc_bank].vaddr + (paddr - link_desc_banks[desc_bank].paddr); - ath11k_hal_rx_msdu_link_info_get(link_desc_va, &num_msdus, meta, - &rbm); + ath11k_hal_rx_msdu_link_info_get(link_desc_va, &num_msdus, + msdu_cookies, &rbm); for (i = 0; i < num_msdus; i++) { buf_id = FIELD_GET(DP_RXDMA_BUF_COOKIE_BUF_ID, - meta[i].cookie); + msdu_cookies[i]); spin_lock_bh(&rx_ring->idr_lock); skb = idr_find(&rx_ring->bufs_idr, buf_id); diff --git a/drivers/net/wireless/ath/ath11k/hal_rx.c b/drivers/net/wireless/ath/ath11k/hal_rx.c index 2de4b388db20c..6168a4eb0c0bb 100644 --- a/drivers/net/wireless/ath/ath11k/hal_rx.c +++ b/drivers/net/wireless/ath/ath11k/hal_rx.c @@ -290,7 +290,7 @@ void ath11k_hal_rx_buf_addr_info_get(void *desc, dma_addr_t *paddr, } void ath11k_hal_rx_msdu_link_info_get(void *link_desc, u32 *num_msdus, - struct hal_rx_msdu_meta *meta, + u32 *msdu_cookies, enum hal_rx_buf_return_buf_manager *rbm) { struct hal_rx_msdu_link *link = (struct hal_rx_msdu_link *)link_desc; @@ -311,17 +311,9 @@ void ath11k_hal_rx_msdu_link_info_get(void *link_desc, u32 *num_msdus, *num_msdus = i; break; } - meta->msdu_len = FIELD_GET(RX_MSDU_DESC_INFO0_MSDU_LENGTH, - msdu->rx_msdu_info.info0); - meta->first = !!(msdu->rx_msdu_info.info0 & - RX_MSDU_DESC_INFO0_FIRST_MSDU_IN_MPDU); - meta->last = !!(msdu->rx_msdu_info.info0 & - RX_MSDU_DESC_INFO0_LAST_MSDU_IN_MPDU); - meta->continuation = !!(msdu->rx_msdu_info.info0 & - RX_MSDU_DESC_INFO0_MSDU_CONTINUATION); - meta->cookie = FIELD_GET(BUFFER_ADDR_INFO1_SW_COOKIE, - msdu->buf_addr_info.info1); - meta++; + *msdu_cookies = FIELD_GET(BUFFER_ADDR_INFO1_SW_COOKIE, + msdu->buf_addr_info.info1); + msdu_cookies++; } } @@ -356,39 +348,6 @@ int ath11k_hal_desc_reo_parse_err(struct ath11k_base *ab, u32 *rx_desc, return 0; } -void ath11k_hal_rx_parse_dst_ring_desc(struct ath11k_base *ab, u32 *rx_desc, - struct hal_rx_meta_info *meta_info) -{ - struct hal_reo_dest_ring *desc = (struct hal_reo_dest_ring *)rx_desc; - struct rx_mpdu_desc *mpdu = &desc->rx_mpdu_info; - struct rx_msdu_desc *msdu = &desc->rx_msdu_info; - struct hal_rx_mpdu_meta *meta_mpdu = &meta_info->mpdu_meta; - struct hal_rx_msdu_meta *meta_msdu = &meta_info->msdu_meta; - - meta_info->push_reason = FIELD_GET(HAL_REO_DEST_RING_INFO0_PUSH_REASON, - desc->info0); - - meta_mpdu->msdu_cnt = FIELD_GET(RX_MPDU_DESC_INFO0_MSDU_COUNT, - mpdu->info0); - meta_mpdu->seq_num = FIELD_GET(RX_MPDU_DESC_INFO0_SEQ_NUM, mpdu->info0); - meta_mpdu->frag = !!(mpdu->info0 & RX_MPDU_DESC_INFO0_FRAG_FLAG); - meta_mpdu->retry = !!(mpdu->info0 & RX_MPDU_DESC_INFO0_MPDU_RETRY); - meta_mpdu->ampdu = !!(mpdu->info0 & RX_MPDU_DESC_INFO0_AMPDU_FLAG); - meta_mpdu->raw = !!(mpdu->info0 & RX_MPDU_DESC_INFO0_RAW_MPDU); - meta_mpdu->peer_meta = mpdu->meta_data; - - meta_msdu->cookie = FIELD_GET(BUFFER_ADDR_INFO1_SW_COOKIE, - desc->buf_addr_info.info1); - meta_msdu->msdu_len = FIELD_GET(RX_MSDU_DESC_INFO0_MSDU_LENGTH, - msdu->info0); - meta_msdu->first = - !!(msdu->info0 & RX_MSDU_DESC_INFO0_FIRST_MSDU_IN_MPDU); - meta_msdu->last = - !!(msdu->info0 & RX_MSDU_DESC_INFO0_LAST_MSDU_IN_MPDU); - meta_msdu->continuation = - !!(msdu->info0 & RX_MSDU_DESC_INFO0_MSDU_CONTINUATION); -} - int ath11k_hal_wbm_desc_parse_err(struct ath11k_base *ab, void *desc, struct hal_rx_wbm_rel_info *rel_info) { diff --git a/drivers/net/wireless/ath/ath11k/hal_rx.h b/drivers/net/wireless/ath/ath11k/hal_rx.h index 96555e4f4ef31..2271bf56183bd 100644 --- a/drivers/net/wireless/ath/ath11k/hal_rx.h +++ b/drivers/net/wireless/ath/ath11k/hal_rx.h @@ -6,30 +6,6 @@ #ifndef ATH11K_HAL_RX_H #define ATH11K_HAL_RX_H -struct hal_rx_mpdu_meta { - u32 peer_meta; - u16 msdu_cnt; - u16 seq_num; - bool frag; - bool retry; - bool ampdu; - bool raw; -}; - -struct hal_rx_msdu_meta { - u32 cookie; - u32 msdu_len; - bool first; - bool last; - bool continuation; -}; - -struct hal_rx_meta_info { - enum hal_reo_dest_ring_push_reason push_reason; - struct hal_rx_mpdu_meta mpdu_meta; - struct hal_rx_msdu_meta msdu_meta; -}; - struct hal_rx_wbm_rel_info { u32 cookie; enum hal_wbm_rel_src_module err_rel_src; @@ -320,7 +296,7 @@ void ath11k_hal_reo_update_rx_reo_queue_status(struct ath11k_base *ab, struct hal_reo_status *status); int ath11k_hal_reo_process_status(u8 *reo_desc, u8 *status); void ath11k_hal_rx_msdu_link_info_get(void *link_desc, u32 *num_msdus, - struct hal_rx_msdu_meta *meta, + u32 *msdu_cookies, enum hal_rx_buf_return_buf_manager *rbm); void ath11k_hal_rx_msdu_link_desc_set(struct ath11k_base *ab, void *desc, void *link_desc, @@ -331,8 +307,6 @@ void ath11k_hal_rx_buf_addr_info_get(void *desc, dma_addr_t *paddr, u32 *cookie, u8 *rbm); int ath11k_hal_desc_reo_parse_err(struct ath11k_base *ab, u32 *rx_desc, dma_addr_t *paddr, u32 *desc_bank); -void ath11k_hal_rx_parse_dst_ring_desc(struct ath11k_base *ab, u32 *rx_desc, - struct hal_rx_meta_info *meta_info); int ath11k_hal_wbm_desc_parse_err(struct ath11k_base *ab, void *desc, struct hal_rx_wbm_rel_info *rel_info); void ath11k_hal_rx_reo_ent_paddr_get(struct ath11k_base *ab, void *desc, -- GitLab From f1d34a01ed547793c223eb3df4849bdb19ff1d22 Mon Sep 17 00:00:00 2001 From: Karthikeyan Periyasamy Date: Wed, 27 Nov 2019 18:29:58 +0200 Subject: [PATCH 0062/1437] ath11k: avoid WMM param truncation In conf_tx() mac operation callback, we are truncating the tx params cw_min and cw_max due to lower data type cast. so modified the data type of cwmin and cwmax to avoid the trucation issue. Signed-off-by: Karthikeyan Periyasamy Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath11k/wmi.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/ath/ath11k/wmi.h b/drivers/net/wireless/ath/ath11k/wmi.h index 6a0468094694e..4a518d406bc52 100644 --- a/drivers/net/wireless/ath/ath11k/wmi.h +++ b/drivers/net/wireless/ath/ath11k/wmi.h @@ -4501,8 +4501,8 @@ struct wmi_wmm_params { struct wmi_wmm_params_arg { u8 acm; u8 aifs; - u8 cwmin; - u8 cwmax; + u16 cwmin; + u16 cwmax; u16 txop; u8 no_ack; }; -- GitLab From d12ac6c47a32b720d77181b8c2915be43c5be2d0 Mon Sep 17 00:00:00 2001 From: Pradeep Kumar Chitrapu Date: Wed, 27 Nov 2019 18:30:01 +0200 Subject: [PATCH 0063/1437] ath11k: remove unused tx ring counters remove unused counter to avoid taking locks inorder to optimize cpu load. Using Flamegraph, cpu usage of ath11k_dp_tx() observed to be decreased from 5.58% to 3.74% with iperf traffic running with 80MHz bandwidth ap mode. Signed-off-by: Pradeep Kumar Chitrapu Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath11k/dp.h | 1 - drivers/net/wireless/ath/ath11k/dp_tx.c | 7 ------- 2 files changed, 8 deletions(-) diff --git a/drivers/net/wireless/ath/ath11k/dp.h b/drivers/net/wireless/ath/ath11k/dp.h index eb0f3df131c5e..db216f055c425 100644 --- a/drivers/net/wireless/ath/ath11k/dp.h +++ b/drivers/net/wireless/ath/ath11k/dp.h @@ -63,7 +63,6 @@ struct dp_tx_ring { struct dp_srng tcl_data_ring; struct dp_srng tcl_comp_ring; struct idr txbuf_idr; - u32 num_tx_pending; /* Protects txbuf_idr and num_pending */ spinlock_t tx_idr_lock; DECLARE_KFIFO_PTR(tx_status_fifo, struct hal_wbm_release_ring); diff --git a/drivers/net/wireless/ath/ath11k/dp_tx.c b/drivers/net/wireless/ath/ath11k/dp_tx.c index 51a02b8e66ccf..a8b9557c2346f 100644 --- a/drivers/net/wireless/ath/ath11k/dp_tx.c +++ b/drivers/net/wireless/ath/ath11k/dp_tx.c @@ -196,10 +196,6 @@ int ath11k_dp_tx(struct ath11k *ar, struct ath11k_vif *arvif, spin_unlock_bh(&tcl_ring->lock); - spin_lock_bh(&tx_ring->tx_idr_lock); - tx_ring->num_tx_pending++; - spin_unlock_bh(&tx_ring->tx_idr_lock); - atomic_inc(&ar->dp.num_tx_pending); return 0; @@ -236,7 +232,6 @@ static void ath11k_dp_tx_free_txbuf(struct ath11k_base *ab, u8 mac_id, skb_cb = ATH11K_SKB_CB(msdu); idr_remove(&tx_ring->txbuf_idr, msdu_id); - tx_ring->num_tx_pending--; spin_unlock_bh(&tx_ring->tx_idr_lock); dma_unmap_single(ab->dev, skb_cb->paddr, msdu->len, DMA_TO_DEVICE); @@ -272,7 +267,6 @@ ath11k_dp_tx_htt_tx_complete_buf(struct ath11k_base *ab, ar = skb_cb->ar; idr_remove(&tx_ring->txbuf_idr, ts->msdu_id); - tx_ring->num_tx_pending--; spin_unlock_bh(&tx_ring->tx_idr_lock); if (atomic_dec_and_test(&ar->dp.num_tx_pending)) @@ -495,7 +489,6 @@ void ath11k_dp_tx_completion_handler(struct ath11k_base *ab, int ring_id) continue; } idr_remove(&tx_ring->txbuf_idr, msdu_id); - tx_ring->num_tx_pending--; spin_unlock_bh(&tx_ring->tx_idr_lock); ar = ab->pdevs[mac_id].ar; -- GitLab From d2f510fa0103a789b7fd310dbf14e0effa2f1fdf Mon Sep 17 00:00:00 2001 From: Sriram R Date: Wed, 27 Nov 2019 18:30:04 +0200 Subject: [PATCH 0064/1437] ath11k: Fix skb_panic observed during msdu coalescing skb_panic is hit during msdu coalescing whenever enough tailroom is not allocated based on the remaining msdu length which is spread across in different rx buffers. Compute the extra length for resizing the skb based on the total msdu length and the msdu length of the first buffer. Signed-off-by: Sriram R Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath11k/dp_rx.c | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/drivers/net/wireless/ath/ath11k/dp_rx.c b/drivers/net/wireless/ath/ath11k/dp_rx.c index 321a2bb657e8b..acad74658e640 100644 --- a/drivers/net/wireless/ath/ath11k/dp_rx.c +++ b/drivers/net/wireless/ath/ath11k/dp_rx.c @@ -1344,15 +1344,22 @@ static int ath11k_dp_rx_msdu_coalesce(struct ath11k *ar, { struct sk_buff *skb; struct ath11k_skb_rxcb *rxcb = ATH11K_SKB_RXCB(first); + int buf_first_hdr_len, buf_first_len; struct hal_rx_desc *ldesc; int space_extra; int rem_len; int buf_len; - if (WARN_ON_ONCE(msdu_len <= (DP_RX_BUFFER_SIZE - - (HAL_RX_DESC_SIZE + l3pad_bytes)))) { - skb_put(first, HAL_RX_DESC_SIZE + l3pad_bytes + msdu_len); - skb_pull(first, HAL_RX_DESC_SIZE + l3pad_bytes); + /* As the msdu is spread across multiple rx buffers, + * find the offset to the start of msdu for computing + * the length of the msdu in the first buffer. + */ + buf_first_hdr_len = HAL_RX_DESC_SIZE + l3pad_bytes; + buf_first_len = DP_RX_BUFFER_SIZE - buf_first_hdr_len; + + if (WARN_ON_ONCE(msdu_len <= buf_first_len)) { + skb_put(first, buf_first_hdr_len + msdu_len); + skb_pull(first, buf_first_hdr_len); return 0; } @@ -1365,9 +1372,9 @@ static int ath11k_dp_rx_msdu_coalesce(struct ath11k *ar, * in the first buf is of length DP_RX_BUFFER_SIZE - HAL_RX_DESC_SIZE. */ skb_put(first, DP_RX_BUFFER_SIZE); - skb_pull(first, HAL_RX_DESC_SIZE + l3pad_bytes); + skb_pull(first, buf_first_hdr_len); - space_extra = msdu_len - (DP_RX_BUFFER_SIZE + skb_tailroom(first)); + space_extra = msdu_len - (buf_first_len + skb_tailroom(first)); if (space_extra > 0 && (pskb_expand_head(first, 0, space_extra, GFP_ATOMIC) < 0)) { /* Free up all buffers of the MSDU */ @@ -1387,8 +1394,7 @@ static int ath11k_dp_rx_msdu_coalesce(struct ath11k *ar, */ ath11k_dp_rx_desc_end_tlv_copy(rxcb->rx_desc, ldesc); - rem_len = msdu_len - - (DP_RX_BUFFER_SIZE - HAL_RX_DESC_SIZE - l3pad_bytes); + rem_len = msdu_len - buf_first_len; while ((skb = __skb_dequeue(msdu_list)) != NULL && rem_len > 0) { rxcb = ATH11K_SKB_RXCB(skb); if (rxcb->is_continuation) -- GitLab From 79c647a3c59ddc1a1acf68e82e555201c10d3c18 Mon Sep 17 00:00:00 2001 From: Vikas Patel Date: Wed, 27 Nov 2019 18:30:07 +0200 Subject: [PATCH 0065/1437] ath11k: Fixing TLV length issue in peer pktlog WMI command TLV length was 0 for TLV tag 'WMI_TAG_ARRAY_STRUCT' causing Q6 to crash when trying to configure pktlog filter via debugfs. Signed-off-by: Vikas Patel Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath11k/wmi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath11k/wmi.c b/drivers/net/wireless/ath/ath11k/wmi.c index ccebf7ce420a2..aae6e76330dac 100644 --- a/drivers/net/wireless/ath/ath11k/wmi.c +++ b/drivers/net/wireless/ath/ath11k/wmi.c @@ -2329,7 +2329,7 @@ int ath11k_wmi_pdev_peer_pktlog_filter(struct ath11k *ar, u8 *addr, u8 enable) tlv = ptr; tlv->header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_ARRAY_STRUCT) | - FIELD_PREP(WMI_TLV_LEN, 0); + FIELD_PREP(WMI_TLV_LEN, sizeof(*info)); ptr += TLV_HDR_SIZE; info = ptr; -- GitLab From 051cefa4466744cb669d7eef9399cbf850b2a6d4 Mon Sep 17 00:00:00 2001 From: Vasanthakumar Thiagarajan Date: Wed, 27 Nov 2019 18:30:09 +0200 Subject: [PATCH 0066/1437] ath11k: Fix target crash due to WBM_IDLE_LINK ring desc shortage Make sure the number of WBM_IDLE_LINK ring descriptors is power of 2. This increases the number of descriptors to 32k from the current ~18k to fix the target assert because of the shortage in the descriptors in WBM_IDLE_LINK ring. Remove unnecessary power of 2 calculation in ath11k_dp_link_desc_setup() as it is not required after this change. Signed-off-by: Vasanthakumar Thiagarajan Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath11k/dp.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/ath/ath11k/dp.c b/drivers/net/wireless/ath/ath11k/dp.c index ff510e821a291..72c21cf6a3522 100644 --- a/drivers/net/wireless/ath/ath11k/dp.c +++ b/drivers/net/wireless/ath/ath11k/dp.c @@ -478,6 +478,9 @@ static int ath11k_wbm_idle_ring_setup(struct ath11k_base *ab, u32 *n_link_desc) *n_link_desc = n_mpdu_link_desc + n_mpdu_queue_desc + n_tx_msdu_link_desc + n_rx_msdu_link_desc; + if (*n_link_desc & (*n_link_desc - 1)) + *n_link_desc = 1 << fls(*n_link_desc); + ret = ath11k_dp_srng_setup(ab, &dp->wbm_idle_ring, HAL_WBM_IDLE_LINK, 0, 0, *n_link_desc); if (ret) { @@ -499,9 +502,6 @@ int ath11k_dp_link_desc_setup(struct ath11k_base *ab, u32 *desc; int i, ret; - if (n_link_desc & (n_link_desc - 1)) - n_link_desc = 1 << fls(n_link_desc); - tot_mem_sz = n_link_desc * HAL_LINK_DESC_SIZE; tot_mem_sz += HAL_LINK_DESC_ALIGN; -- GitLab From 7cbf4c96d7159e4abe762f4bafa9911fc1f7d339 Mon Sep 17 00:00:00 2001 From: Wen Gong Date: Fri, 8 Nov 2019 17:19:14 +0800 Subject: [PATCH 0067/1437] ath10k: enable firmware log by default for sdio On SDIO chips the firmware log does not impact performance. To make it easier to debug firmware problems keep it enabled on the firmware. Tested with QCA6174 SDIO with firmware WLAN.RMH.4.4.1-00029. Signed-off-by: Wen Gong Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/core.c | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c index 0d345852c04a9..6da377317d1fa 100644 --- a/drivers/net/wireless/ath/ath10k/core.c +++ b/drivers/net/wireless/ath/ath10k/core.c @@ -715,18 +715,6 @@ static int ath10k_init_sdio(struct ath10k *ar, enum ath10k_firmware_mode mode) if (ret) return ret; - /* Explicitly set fwlog prints to zero as target may turn it on - * based on scratch registers. - */ - ret = ath10k_bmi_read32(ar, hi_option_flag, ¶m); - if (ret) - return ret; - - param |= HI_OPTION_DISABLE_DBGLOG; - ret = ath10k_bmi_write32(ar, hi_option_flag, param); - if (ret) - return ret; - return 0; } -- GitLab From 376a30c7c9a05382d2cad09e9ff9b2998d0d53c3 Mon Sep 17 00:00:00 2001 From: Wen Gong Date: Wed, 27 Nov 2019 02:59:29 +0000 Subject: [PATCH 0068/1437] ath10k: set max mtu to 1500 for sdio chip For sdio chip, the max credit size in firmware is 1556, the 1556 include payload, ieee80211 header, htt header, htc header. So it need to set the max mtu to 1500 to forbidden TX packet which exceed 1500 form application. Tested with QCA6174 SDIO with firmware WLAN.RMH.4.4.1-00017-QCARMSWP-1. Signed-off-by: Wen Gong Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/sdio.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/wireless/ath/ath10k/sdio.c b/drivers/net/wireless/ath/ath10k/sdio.c index 3e63adfe3e9c4..2e155bba2aa3f 100644 --- a/drivers/net/wireless/ath/ath10k/sdio.c +++ b/drivers/net/wireless/ath/ath10k/sdio.c @@ -2137,6 +2137,8 @@ static int ath10k_sdio_probe(struct sdio_func *func, bus_params.chip_id = 0; bus_params.hl_msdu_ids = true; + ar->hw->max_mtu = ETH_DATA_LEN; + ret = ath10k_core_register(ar, &bus_params); if (ret) { ath10k_err(ar, "failed to register driver core: %d\n", ret); -- GitLab From 382e51c139ef9d0b8e65ff5c249771f864b411fd Mon Sep 17 00:00:00 2001 From: Wen Gong Date: Wed, 27 Nov 2019 03:19:24 +0000 Subject: [PATCH 0069/1437] ath10k: set WMI_PEER_AUTHORIZE after a firmware crash After the firmware crashes ath10k recovers via ieee80211_reconfig(), which eventually leads to firmware configuration and including the encryption keys. However, because there is no new auth/assoc and 4-way-handshake, and firmware set the authorize flag after 4-way-handshake, so the authorize flag in firmware is not set in firmware without 4-way-handshake. This will lead to a failure of data transmission after recovery done when using encrypted connections like WPA-PSK. Set authorize flag after installing keys to firmware will fix the issue. This was noticed by testing firmware crashing using simulate_fw_crash debugfs file. Tested with QCA6174 SDIO with firmware WLAN.RMH.4.4.1-00007-QCARMSWP-1. Signed-off-by: Wen Gong Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/mac.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index 6c6eb9341e6d4..767c7bf169758 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -6329,6 +6329,9 @@ static int ath10k_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, if (sta && sta->tdls) ath10k_wmi_peer_set_param(ar, arvif->vdev_id, sta->addr, ar->wmi.peer_param->authorize, 1); + else if (sta && cmd == SET_KEY && (key->flags & IEEE80211_KEY_FLAG_PAIRWISE)) + ath10k_wmi_peer_set_param(ar, arvif->vdev_id, peer_addr, + ar->wmi.peer_param->authorize, 1); exit: mutex_unlock(&ar->conf_mutex); -- GitLab From 5d2467299a17a2c13c2a1a608b72ec032f559914 Mon Sep 17 00:00:00 2001 From: Wen Gong Date: Wed, 27 Nov 2019 03:33:14 +0000 Subject: [PATCH 0070/1437] ath10k: change log level for mpdu status of sdio chip Change log level from warn to dbg level of mpdu status of sdio chip. Tested with QCA6174 SDIO with firmware WLAN.RMH.4.4.1-00007-QCARMSWP-1. Signed-off-by: Wen Gong Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/htt_rx.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/ath/ath10k/htt_rx.c b/drivers/net/wireless/ath/ath10k/htt_rx.c index 9faecfa5f9d7d..bbcfa728917d1 100644 --- a/drivers/net/wireless/ath/ath10k/htt_rx.c +++ b/drivers/net/wireless/ath/ath10k/htt_rx.c @@ -2203,8 +2203,8 @@ static bool ath10k_htt_rx_proc_rx_ind_hl(struct ath10k_htt *htt, HTT_RX_IND_MPDU_STATUS_OK && mpdu_ranges->mpdu_range_status != HTT_RX_IND_MPDU_STATUS_TKIP_MIC_ERR) { - ath10k_warn(ar, "MPDU range status: %d\n", - mpdu_ranges->mpdu_range_status); + ath10k_dbg(ar, ATH10K_DBG_HTT, "htt mpdu_range_status %d\n", + mpdu_ranges->mpdu_range_status); goto err; } -- GitLab From 7321095ca39c8bb4da5e86f7a3fb8c22b47e5063 Mon Sep 17 00:00:00 2001 From: Wen Gong Date: Wed, 27 Nov 2019 06:04:13 +0000 Subject: [PATCH 0071/1437] ath10k: enable wow feature for sdio chip sdio does not support wow, this patch is to enable it. When system enter sleep state, if wowlan is enabled, then sdio chip will keep power if platform support keep power, after resume, it will not need to re-load firmware again. Tested with QCA6174 SDIO with firmware WLAN.RMH.4.4.1-00029. Signed-off-by: Wen Gong Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/sdio.c | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/ath/ath10k/sdio.c b/drivers/net/wireless/ath/ath10k/sdio.c index 2e155bba2aa3f..b8e2499096991 100644 --- a/drivers/net/wireless/ath/ath10k/sdio.c +++ b/drivers/net/wireless/ath/ath10k/sdio.c @@ -1859,7 +1859,7 @@ static void ath10k_sdio_hif_stop(struct ath10k *ar) static int ath10k_sdio_hif_suspend(struct ath10k *ar) { - return -EOPNOTSUPP; + return 0; } static int ath10k_sdio_hif_resume(struct ath10k *ar) @@ -2009,7 +2009,26 @@ static const struct ath10k_hif_ops ath10k_sdio_hif_ops = { */ static int ath10k_sdio_pm_suspend(struct device *device) { - return 0; + struct sdio_func *func = dev_to_sdio_func(device); + struct ath10k_sdio *ar_sdio = sdio_get_drvdata(func); + struct ath10k *ar = ar_sdio->ar; + mmc_pm_flag_t pm_flag, pm_caps; + int ret; + + if (!device_may_wakeup(ar->dev)) + return 0; + + pm_flag = MMC_PM_KEEP_POWER; + + ret = sdio_set_host_pm_flags(func, pm_flag); + if (ret) { + pm_caps = sdio_get_host_pm_caps(func); + ath10k_warn(ar, "failed to set sdio host pm flags (0x%x, 0x%x): %d\n", + pm_flag, pm_caps, ret); + return ret; + } + + return ret; } static int ath10k_sdio_pm_resume(struct device *device) -- GitLab From e8da3986570eff40e02bebcffd62fd3ccdf485db Mon Sep 17 00:00:00 2001 From: Anilkumar Kolli Date: Wed, 27 Nov 2019 14:08:45 +0000 Subject: [PATCH 0072/1437] ath11k: tracing: fix ath11k tracing Add missing tracing subsystem define. Signed-off-by: Anilkumar Kolli Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath11k/trace.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/wireless/ath/ath11k/trace.h b/drivers/net/wireless/ath/ath11k/trace.h index 400f759568e14..8700a622be7bd 100644 --- a/drivers/net/wireless/ath/ath11k/trace.h +++ b/drivers/net/wireless/ath/ath11k/trace.h @@ -17,6 +17,9 @@ static inline void trace_ ## name(proto) {} #endif /* !CONFIG_ATH11K_TRACING || __CHECKER__ */ +#undef TRACE_SYSTEM +#define TRACE_SYSTEM ath11k + TRACE_EVENT(ath11k_htt_pktlog, TP_PROTO(struct ath11k *ar, const void *buf, u16 buf_len), -- GitLab From d6af906d8307a804a1af488564def0264ea57282 Mon Sep 17 00:00:00 2001 From: Anilkumar Kolli Date: Wed, 27 Nov 2019 14:08:47 +0000 Subject: [PATCH 0073/1437] ath11k: qmi clean up ce and HTC service config update Copy CE and htc service configs for all pipes. Signed-off-by: Anilkumar Kolli Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath11k/ahb.c | 9 ++++----- drivers/net/wireless/ath/ath11k/qmi.h | 4 ++-- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/drivers/net/wireless/ath/ath11k/ahb.c b/drivers/net/wireless/ath/ath11k/ahb.c index f80173b8afc6b..e7e3e64c07aa7 100644 --- a/drivers/net/wireless/ath/ath11k/ahb.c +++ b/drivers/net/wireless/ath/ath11k/ahb.c @@ -635,11 +635,10 @@ static void ath11k_ahb_init_qmi_ce_config(struct ath11k_base *ab) { struct ath11k_qmi_ce_cfg *cfg = &ab->qmi.ce_cfg; - cfg->tgt_ce = (u8 *)target_ce_config_wlan; - cfg->tgt_ce_len = sizeof(target_ce_config_wlan); - - cfg->svc_to_ce_map = (u8 *)target_service_to_ce_map_wlan; - cfg->svc_to_ce_map_len = sizeof(target_service_to_ce_map_wlan); + cfg->tgt_ce_len = ARRAY_SIZE(target_ce_config_wlan) - 1; + cfg->tgt_ce = target_ce_config_wlan; + cfg->svc_to_ce_map_len = ARRAY_SIZE(target_service_to_ce_map_wlan); + cfg->svc_to_ce_map = target_service_to_ce_map_wlan; } static void ath11k_ahb_free_ext_irq(struct ath11k_base *ab) diff --git a/drivers/net/wireless/ath/ath11k/qmi.h b/drivers/net/wireless/ath/ath11k/qmi.h index df0685564e21c..3f7db642d8693 100644 --- a/drivers/net/wireless/ath/ath11k/qmi.h +++ b/drivers/net/wireless/ath/ath11k/qmi.h @@ -66,9 +66,9 @@ struct ath11k_qmi_driver_event { }; struct ath11k_qmi_ce_cfg { - const u8 *tgt_ce; + const struct ce_pipe_config *tgt_ce; int tgt_ce_len; - const u8 *svc_to_ce_map; + const struct service_to_pipe *svc_to_ce_map; int svc_to_ce_map_len; const u8 *shadow_reg; int shadow_reg_len; -- GitLab From 0c408515cd3b6fb980ec31213c0e55bbbe74d975 Mon Sep 17 00:00:00 2001 From: Anilkumar Kolli Date: Wed, 27 Nov 2019 14:08:50 +0000 Subject: [PATCH 0074/1437] ath11k: qmi clean up in ath11k_qmi_wlanfw_wlan_cfg_send() Use pipe_id id instead of ret in for loop. Signed-off-by: Anilkumar Kolli Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath11k/ce.h | 9 +++++++-- drivers/net/wireless/ath/ath11k/qmi.c | 28 +++++++++++++-------------- 2 files changed, 20 insertions(+), 17 deletions(-) diff --git a/drivers/net/wireless/ath/ath11k/ce.h b/drivers/net/wireless/ath/ath11k/ce.h index 6174eac84410a..e355dfda48bf1 100644 --- a/drivers/net/wireless/ath/ath11k/ce.h +++ b/drivers/net/wireless/ath/ath11k/ce.h @@ -57,7 +57,12 @@ void ath11k_ce_byte_swap(void *mem, u32 len); struct ath11k_base; -/* Establish a mapping between a service/direction and a pipe. */ +/* + * Establish a mapping between a service/direction and a pipe. + * Configuration information for a Copy Engine pipe and services. + * Passed from Host to Target through QMI message and must be in + * little endian format. + */ struct service_to_pipe { __le32 service_id; __le32 pipedir; @@ -66,7 +71,7 @@ struct service_to_pipe { /* * Configuration information for a Copy Engine pipe. - * Passed from Host to Target during startup (one per CE). + * Passed from Host to Target through QMI message during startup (one per CE). * * NOTE: Structure is shared between Host software and Target firmware! */ diff --git a/drivers/net/wireless/ath/ath11k/qmi.c b/drivers/net/wireless/ath/ath11k/qmi.c index adf41f28a2318..2377895a58ecf 100644 --- a/drivers/net/wireless/ath/ath11k/qmi.c +++ b/drivers/net/wireless/ath/ath11k/qmi.c @@ -2016,7 +2016,7 @@ static int ath11k_qmi_wlanfw_wlan_cfg_send(struct ath11k_base *ab) struct ce_pipe_config *ce_cfg; struct service_to_pipe *svc_cfg; struct qmi_txn txn = {}; - int ret = 0; + int ret = 0, pipe_num; ce_cfg = (struct ce_pipe_config *)ab->qmi.ce_cfg.tgt_ce; svc_cfg = (struct service_to_pipe *)ab->qmi.ce_cfg.svc_to_ce_map; @@ -2033,24 +2033,22 @@ static int ath11k_qmi_wlanfw_wlan_cfg_send(struct ath11k_base *ab) req->tgt_cfg_valid = 1; /* This is number of CE configs */ - req->tgt_cfg_len = ((ab->qmi.ce_cfg.tgt_ce_len) / - (sizeof(struct ce_pipe_config))) - 1; - for (ret = 0; ret <= req->tgt_cfg_len ; ret++) { - req->tgt_cfg[ret].pipe_num = ce_cfg[ret].pipenum; - req->tgt_cfg[ret].pipe_dir = ce_cfg[ret].pipedir; - req->tgt_cfg[ret].nentries = ce_cfg[ret].nentries; - req->tgt_cfg[ret].nbytes_max = ce_cfg[ret].nbytes_max; - req->tgt_cfg[ret].flags = ce_cfg[ret].flags; + req->tgt_cfg_len = ab->qmi.ce_cfg.tgt_ce_len; + for (pipe_num = 0; pipe_num <= req->tgt_cfg_len ; pipe_num++) { + req->tgt_cfg[pipe_num].pipe_num = ce_cfg[pipe_num].pipenum; + req->tgt_cfg[pipe_num].pipe_dir = ce_cfg[pipe_num].pipedir; + req->tgt_cfg[pipe_num].nentries = ce_cfg[pipe_num].nentries; + req->tgt_cfg[pipe_num].nbytes_max = ce_cfg[pipe_num].nbytes_max; + req->tgt_cfg[pipe_num].flags = ce_cfg[pipe_num].flags; } req->svc_cfg_valid = 1; /* This is number of Service/CE configs */ - req->svc_cfg_len = (ab->qmi.ce_cfg.svc_to_ce_map_len) / - (sizeof(struct service_to_pipe)); - for (ret = 0; ret < req->svc_cfg_len; ret++) { - req->svc_cfg[ret].service_id = svc_cfg[ret].service_id; - req->svc_cfg[ret].pipe_dir = svc_cfg[ret].pipedir; - req->svc_cfg[ret].pipe_num = svc_cfg[ret].pipenum; + req->svc_cfg_len = ab->qmi.ce_cfg.svc_to_ce_map_len; + for (pipe_num = 0; pipe_num < req->svc_cfg_len; pipe_num++) { + req->svc_cfg[pipe_num].service_id = svc_cfg[pipe_num].service_id; + req->svc_cfg[pipe_num].pipe_dir = svc_cfg[pipe_num].pipedir; + req->svc_cfg[pipe_num].pipe_num = svc_cfg[pipe_num].pipenum; } req->shadow_reg_valid = 0; req->shadow_reg_v2_valid = 0; -- GitLab From d0f390eae07284939b10fe34edd45d4c59226783 Mon Sep 17 00:00:00 2001 From: Anilkumar Kolli Date: Wed, 27 Nov 2019 14:08:50 +0000 Subject: [PATCH 0075/1437] ath11k: pktlog: fix sending/using the pdev id Fixes sending the pdev id(0,1,2 for mac0, mac1, mac2) to FW in wmi cmd pktlog enable/disable. Signed-off-by: Anilkumar Kolli Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath11k/dp_rx.c | 8 +++++--- drivers/net/wireless/ath/ath11k/wmi.c | 6 +++--- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/drivers/net/wireless/ath/ath11k/dp_rx.c b/drivers/net/wireless/ath/ath11k/dp_rx.c index acad74658e640..67efa247bf656 100644 --- a/drivers/net/wireless/ath/ath11k/dp_rx.c +++ b/drivers/net/wireless/ath/ath11k/dp_rx.c @@ -1265,7 +1265,6 @@ static void ath11k_htt_pktlog(struct ath11k_base *ab, struct sk_buff *skb) u8 pdev_id; len = FIELD_GET(HTT_T2H_PPDU_STATS_INFO_PAYLOAD_SIZE, data->hdr); - if (len > ATH11K_HTT_PKTLOG_MAX_SIZE) { ath11k_warn(ab, "htt pktlog buffer size %d, expected < %d\n", len, @@ -1274,8 +1273,11 @@ static void ath11k_htt_pktlog(struct ath11k_base *ab, struct sk_buff *skb) } pdev_id = FIELD_GET(HTT_T2H_PPDU_STATS_INFO_PDEV_ID, data->hdr); - pdev_id = DP_HW2SW_MACID(pdev_id); - ar = ab->pdevs[pdev_id].ar; + ar = ath11k_mac_get_ar_by_pdev_id(ab, pdev_id); + if (!ar) { + ath11k_warn(ab, "invalid pdev id %d on htt pktlog\n", pdev_id); + return; + } trace_ath11k_htt_pktlog(ar, data->payload, len); } diff --git a/drivers/net/wireless/ath/ath11k/wmi.c b/drivers/net/wireless/ath/ath11k/wmi.c index aae6e76330dac..f349bf2c1ee8a 100644 --- a/drivers/net/wireless/ath/ath11k/wmi.c +++ b/drivers/net/wireless/ath/ath11k/wmi.c @@ -2321,7 +2321,7 @@ int ath11k_wmi_pdev_peer_pktlog_filter(struct ath11k *ar, u8 *addr, u8 enable) cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_PDEV_PEER_PKTLOG_FILTER_CMD) | FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE); - cmd->pdev_id = ar->pdev->pdev_id; + cmd->pdev_id = DP_HW2SW_MACID(ar->pdev->pdev_id); cmd->num_mac = 1; cmd->enable = enable; @@ -2419,7 +2419,7 @@ int ath11k_wmi_pdev_pktlog_enable(struct ath11k *ar, u32 pktlog_filter) cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_PDEV_PKTLOG_ENABLE_CMD) | FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE); - cmd->pdev_id = ar->pdev->pdev_id; + cmd->pdev_id = DP_HW2SW_MACID(ar->pdev->pdev_id); cmd->evlist = pktlog_filter; cmd->enable = ATH11K_WMI_PKTLOG_ENABLE_FORCE; @@ -2449,7 +2449,7 @@ int ath11k_wmi_pdev_pktlog_disable(struct ath11k *ar) cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_PDEV_PKTLOG_DISABLE_CMD) | FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE); - cmd->pdev_id = ar->pdev->pdev_id; + cmd->pdev_id = DP_HW2SW_MACID(ar->pdev->pdev_id); ret = ath11k_wmi_cmd_send(wmi, skb, WMI_PDEV_PKTLOG_DISABLE_CMDID); -- GitLab From f425078b449f90793c73423e4bbc44da6aad48d6 Mon Sep 17 00:00:00 2001 From: Karthikeyan Periyasamy Date: Wed, 27 Nov 2019 14:08:52 +0000 Subject: [PATCH 0076/1437] ath11k: avoid burst time conversion logic WMI_VDEV_SET_WMM_PARAMS commmand expects the txoplimit param in the units of 32 microseconds. convert the txop unit from 32 microseconds to absolute microseconds leads to the higher burst values which is incorrect. so no need to convert the txop unit from 32 microseconds to absolute microseconds. Signed-off-by: Karthikeyan Periyasamy Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath11k/mac.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/drivers/net/wireless/ath/ath11k/mac.c b/drivers/net/wireless/ath/ath11k/mac.c index 6f82fdbbd358b..412c258143caf 100644 --- a/drivers/net/wireless/ath/ath11k/mac.c +++ b/drivers/net/wireless/ath/ath11k/mac.c @@ -3023,12 +3023,7 @@ static int ath11k_mac_op_conf_tx(struct ieee80211_hw *hw, p->cwmin = params->cw_min; p->cwmax = params->cw_max; p->aifs = params->aifs; - - /* The channel time duration programmed in the HW is in absolute - * microseconds, while mac80211 gives the txop in units of - * 32 microseconds. - */ - p->txop = params->txop * 32; + p->txop = params->txop; ret = ath11k_wmi_send_wmm_update_cmd_tlv(ar, arvif->vdev_id, &arvif->wmm_params); -- GitLab From 30679ec409189de89f55552a623c264091d72b66 Mon Sep 17 00:00:00 2001 From: Karthikeyan Periyasamy Date: Wed, 27 Nov 2019 14:08:53 +0000 Subject: [PATCH 0077/1437] ath11k: avoid use_after_free in ath11k_dp_rx_msdu_coalesce API Accessing already stored first msdu data after the skb expand trigger use_after_free, since first msdu got deleted. so do the descriptor copy operation before the skb expand operation. Signed-off-by: Karthikeyan Periyasamy Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath11k/dp_rx.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/ath/ath11k/dp_rx.c b/drivers/net/wireless/ath/ath11k/dp_rx.c index 67efa247bf656..f87bd327b082b 100644 --- a/drivers/net/wireless/ath/ath11k/dp_rx.c +++ b/drivers/net/wireless/ath/ath11k/dp_rx.c @@ -1376,6 +1376,11 @@ static int ath11k_dp_rx_msdu_coalesce(struct ath11k *ar, skb_put(first, DP_RX_BUFFER_SIZE); skb_pull(first, buf_first_hdr_len); + /* When an MSDU spread over multiple buffers attention, MSDU_END and + * MPDU_END tlvs are valid only in the last buffer. Copy those tlvs. + */ + ath11k_dp_rx_desc_end_tlv_copy(rxcb->rx_desc, ldesc); + space_extra = msdu_len - (buf_first_len + skb_tailroom(first)); if (space_extra > 0 && (pskb_expand_head(first, 0, space_extra, GFP_ATOMIC) < 0)) { @@ -1391,11 +1396,6 @@ static int ath11k_dp_rx_msdu_coalesce(struct ath11k *ar, return -ENOMEM; } - /* When an MSDU spread over multiple buffers attention, MSDU_END and - * MPDU_END tlvs are valid only in the last buffer. Copy those tlvs. - */ - ath11k_dp_rx_desc_end_tlv_copy(rxcb->rx_desc, ldesc); - rem_len = msdu_len - buf_first_len; while ((skb = __skb_dequeue(msdu_list)) != NULL && rem_len > 0) { rxcb = ATH11K_SKB_RXCB(skb); -- GitLab From fe201947f8bd4aed649a69e5faf0934652327804 Mon Sep 17 00:00:00 2001 From: Venkateswara Naralasetty Date: Wed, 27 Nov 2019 14:08:57 +0000 Subject: [PATCH 0078/1437] ath11k: update bawindow size in delba process Currenly in delba process calling ath11k_peer_rx_tid_delete() updates reo with desc invalid and add tid queue to the flush list. If station send data traffic without addba req and before tid flush, hw gives those packets as invalid desc reo error. Since we are dropping these invalid desc packets results in traffic stall. This patch fix this issue by updating the reo queue with bawindow size 1 instead of tid removal in delba process. Signed-off-by: Venkateswara Naralasetty Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath11k/dp_rx.c | 28 +++++++++++++++++-------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/drivers/net/wireless/ath/ath11k/dp_rx.c b/drivers/net/wireless/ath/ath11k/dp_rx.c index f87bd327b082b..8c21925a522a7 100644 --- a/drivers/net/wireless/ath/ath11k/dp_rx.c +++ b/drivers/net/wireless/ath/ath11k/dp_rx.c @@ -641,7 +641,8 @@ void ath11k_peer_rx_tid_cleanup(struct ath11k *ar, struct ath11k_peer *peer) static int ath11k_peer_rx_tid_reo_update(struct ath11k *ar, struct ath11k_peer *peer, struct dp_rx_tid *rx_tid, - u32 ba_win_sz, u16 ssn) + u32 ba_win_sz, u16 ssn, + bool update_ssn) { struct ath11k_hal_reo_cmd cmd = {0}; int ret; @@ -649,10 +650,13 @@ static int ath11k_peer_rx_tid_reo_update(struct ath11k *ar, cmd.addr_lo = lower_32_bits(rx_tid->paddr); cmd.addr_hi = upper_32_bits(rx_tid->paddr); cmd.flag = HAL_REO_CMD_FLG_NEED_STATUS; - cmd.upd0 = HAL_REO_CMD_UPD0_BA_WINDOW_SIZE | - HAL_REO_CMD_UPD0_SSN; + cmd.upd0 = HAL_REO_CMD_UPD0_BA_WINDOW_SIZE; cmd.ba_window_size = ba_win_sz; - cmd.upd2 = FIELD_PREP(HAL_REO_CMD_UPD2_SSN, ssn); + + if (update_ssn) { + cmd.upd0 |= HAL_REO_CMD_UPD0_SSN; + cmd.upd2 = FIELD_PREP(HAL_REO_CMD_UPD2_SSN, ssn); + } ret = ath11k_dp_tx_send_reo_cmd(ar->ab, rx_tid, HAL_REO_CMD_UPDATE_RX_QUEUE, &cmd, @@ -722,7 +726,7 @@ int ath11k_peer_rx_tid_setup(struct ath11k *ar, const u8 *peer_mac, int vdev_id, if (rx_tid->active) { paddr = rx_tid->paddr; ret = ath11k_peer_rx_tid_reo_update(ar, peer, rx_tid, - ba_win_sz, ssn); + ba_win_sz, ssn, true); spin_unlock_bh(&ab->base_lock); if (ret) { ath11k_warn(ab, "failed to update reo for rx tid %d\n", tid); @@ -832,12 +836,18 @@ int ath11k_dp_rx_ampdu_stop(struct ath11k *ar, paddr = peer->rx_tid[params->tid].paddr; active = peer->rx_tid[params->tid].active; - ath11k_peer_rx_tid_delete(ar, peer, params->tid); + if (!active) { + spin_unlock_bh(&ab->base_lock); + return 0; + } + ret = ath11k_peer_rx_tid_reo_update(ar, peer, peer->rx_tid, 1, 0, false); spin_unlock_bh(&ab->base_lock); - - if (!active) - return 0; + if (ret) { + ath11k_warn(ab, "failed to update reo for rx tid %d: %d\n", + params->tid, ret); + return ret; + } ret = ath11k_wmi_peer_rx_reorder_queue_setup(ar, vdev_id, params->sta->addr, paddr, -- GitLab From 64f1d7e94daaaf53208e9505448406b5c8fbbd8a Mon Sep 17 00:00:00 2001 From: Maharaja Kennadyrajan Date: Wed, 27 Nov 2019 14:08:57 +0000 Subject: [PATCH 0079/1437] ath11k: add support for controlling tx power to a station This patch will add the support to control the transmit power for traffic to a station associated with the AP. Underlying firmware will enforce that the maximum tx power will be based on the regulatory requirements. If the user given transmit power is greater than the allowed tx power in the given channel, then the firmware will use the maximum tx power in the same channel. Max and Min tx power values will depends on number of tx chain masks. The allowed tx power range values are from 6 to 23. When 0 is sent to the firmware as tx power, it will revert to the default tx power for the station. Signed-off-by: Maharaja Kennadyrajan Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath11k/debug.h | 3 ++ drivers/net/wireless/ath/ath11k/mac.c | 37 +++++++++++++++++++++++++ 2 files changed, 40 insertions(+) diff --git a/drivers/net/wireless/ath/ath11k/debug.h b/drivers/net/wireless/ath/ath11k/debug.h index aef33f83c9b16..a317a7bdb9a23 100644 --- a/drivers/net/wireless/ath/ath11k/debug.h +++ b/drivers/net/wireless/ath/ath11k/debug.h @@ -9,6 +9,9 @@ #include "hal_tx.h" #include "trace.h" +#define ATH11K_TX_POWER_MAX_VAL 70 +#define ATH11K_TX_POWER_MIN_VAL 0 + enum ath11k_debug_mask { ATH11K_DBG_AHB = 0x00000001, ATH11K_DBG_WMI = 0x00000002, diff --git a/drivers/net/wireless/ath/ath11k/mac.c b/drivers/net/wireless/ath/ath11k/mac.c index 412c258143caf..5231c4fa0e38f 100644 --- a/drivers/net/wireless/ath/ath11k/mac.c +++ b/drivers/net/wireless/ath/ath11k/mac.c @@ -2842,6 +2842,41 @@ static int ath11k_mac_op_sta_state(struct ieee80211_hw *hw, return ret; } +static int ath11k_mac_op_sta_set_txpwr(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct ath11k *ar = hw->priv; + struct ath11k_vif *arvif = (void *)vif->drv_priv; + int ret = 0; + s16 txpwr; + + if (sta->txpwr.type == NL80211_TX_POWER_AUTOMATIC) { + txpwr = 0; + } else { + txpwr = sta->txpwr.power; + if (!txpwr) + return -EINVAL; + } + + if (txpwr > ATH11K_TX_POWER_MAX_VAL || txpwr < ATH11K_TX_POWER_MIN_VAL) + return -EINVAL; + + mutex_lock(&ar->conf_mutex); + + ret = ath11k_wmi_set_peer_param(ar, sta->addr, arvif->vdev_id, + WMI_PEER_USE_FIXED_PWR, txpwr); + if (ret) { + ath11k_warn(ar->ab, "failed to set tx power for station ret: %d\n", + ret); + goto out; + } + +out: + mutex_unlock(&ar->conf_mutex); + return ret; +} + static void ath11k_mac_op_sta_rc_update(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_sta *sta, @@ -5311,6 +5346,7 @@ static const struct ieee80211_ops ath11k_ops = { .cancel_hw_scan = ath11k_mac_op_cancel_hw_scan, .set_key = ath11k_mac_op_set_key, .sta_state = ath11k_mac_op_sta_state, + .sta_set_txpwr = ath11k_mac_op_sta_set_txpwr, .sta_rc_update = ath11k_mac_op_sta_rc_update, .conf_tx = ath11k_mac_op_conf_tx, .set_antenna = ath11k_mac_op_set_antenna, @@ -5571,6 +5607,7 @@ static int ath11k_mac_register(struct ath11k *ar) ar->hw->wiphy->n_iface_combinations = ARRAY_SIZE(ath11k_if_comb); wiphy_ext_feature_set(ar->hw->wiphy, NL80211_EXT_FEATURE_CQM_RSSI_LIST); + wiphy_ext_feature_set(ar->hw->wiphy, NL80211_EXT_FEATURE_STA_TX_PWR); ar->hw->wiphy->cipher_suites = cipher_suites; ar->hw->wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites); -- GitLab From ba47923974fb67bf612b7a7c07c5884e95c5518b Mon Sep 17 00:00:00 2001 From: Govindaraj Saminathan Date: Wed, 27 Nov 2019 14:08:58 +0000 Subject: [PATCH 0080/1437] ath11k: unlock mutex during failure in qmi fw ready qmi firmware ready event start to initialize the core modules and the sequence executed with mutex lock. In case of any failure mutex should be unlocked otherwise it will hang during the recovery. Signed-off-by: Govindaraj Saminathan Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath11k/core.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/wireless/ath/ath11k/core.c b/drivers/net/wireless/ath/ath11k/core.c index b0c71b5879b1a..37d4ecb458db5 100644 --- a/drivers/net/wireless/ath/ath11k/core.c +++ b/drivers/net/wireless/ath/ath11k/core.c @@ -539,6 +539,7 @@ err_core_stop: ath11k_core_stop(ab); err_dp_free: ath11k_dp_free(ab); + mutex_unlock(&ab->core_lock); return ret; } -- GitLab From 1cb616a3b497d6f8b8160a3fee97e822cdb38f39 Mon Sep 17 00:00:00 2001 From: Sriram R Date: Wed, 27 Nov 2019 14:09:00 +0000 Subject: [PATCH 0081/1437] ath11k: add necessary peer assoc params in wmi dbg Add necessary peer assoc params in WMI debug message while sending the peer assoc command to firmware to aid in debugging. Signed-off-by: Sriram R Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath11k/wmi.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/ath/ath11k/wmi.c b/drivers/net/wireless/ath/ath11k/wmi.c index f349bf2c1ee8a..b05642617b78c 100644 --- a/drivers/net/wireless/ath/ath11k/wmi.c +++ b/drivers/net/wireless/ath/ath11k/wmi.c @@ -1804,8 +1804,17 @@ int ath11k_wmi_send_peer_assoc_cmd(struct ath11k *ar, } ath11k_dbg(ar->ab, ATH11K_DBG_WMI, - "WMI peer assoc vdev id %d assoc id %d peer mac %pM\n", - param->vdev_id, param->peer_associd, param->peer_mac); + "wmi peer assoc vdev id %d assoc id %d peer mac %pM peer_flags %x rate_caps %x peer_caps %x listen_intval %d ht_caps %x max_mpdu %d nss %d phymode %d peer_mpdu_density %d vht_caps %x he cap_info %x he ops %x he cap_info_ext %x he phy %x %x %x peer_bw_rxnss_override %x\n", + cmd->vdev_id, cmd->peer_associd, param->peer_mac, + cmd->peer_flags, cmd->peer_rate_caps, cmd->peer_caps, + cmd->peer_listen_intval, cmd->peer_ht_caps, + cmd->peer_max_mpdu, cmd->peer_nss, cmd->peer_phymode, + cmd->peer_mpdu_density, + cmd->peer_vht_caps, cmd->peer_he_cap_info, + cmd->peer_he_ops, cmd->peer_he_cap_info_ext, + cmd->peer_he_cap_phy[0], cmd->peer_he_cap_phy[1], + cmd->peer_he_cap_phy[2], + cmd->peer_bw_rxnss_override); return ret; } -- GitLab From a3c5195a97afdfff1f9ae0569c3a4cd98ebd5696 Mon Sep 17 00:00:00 2001 From: Sriram R Date: Thu, 28 Nov 2019 08:21:45 +0000 Subject: [PATCH 0082/1437] ath11k: Update tx and rx chain count properly on drv_set_antenna Set the number of tx and rx chains properly on drv_set_antenna(). This will ensure the related ht/vht/he caps are properly recalculated based on the tx/rx chains set. Signed-off-by: Sriram R Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath11k/mac.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/net/wireless/ath/ath11k/mac.c b/drivers/net/wireless/ath/ath11k/mac.c index 5231c4fa0e38f..5ee7c2a1c6116 100644 --- a/drivers/net/wireless/ath/ath11k/mac.c +++ b/drivers/net/wireless/ath/ath11k/mac.c @@ -3480,6 +3480,8 @@ static int __ath11k_set_antenna(struct ath11k *ar, u32 tx_ant, u32 rx_ant) return ret; } + ar->num_tx_chains = get_num_chains(tx_ant); + ret = ath11k_wmi_pdev_set_param(ar, WMI_PDEV_PARAM_RX_CHAIN_MASK, rx_ant, ar->pdev->pdev_id); if (ret) { @@ -3488,6 +3490,8 @@ static int __ath11k_set_antenna(struct ath11k *ar, u32 tx_ant, u32 rx_ant) return ret; } + ar->num_rx_chains = get_num_chains(rx_ant); + /* Reload HT/VHT/HE capability */ ath11k_mac_setup_ht_vht_cap(ar, &ar->pdev->cap, NULL); ath11k_mac_setup_he_cap(ar, &ar->pdev->cap); -- GitLab From c000e56ee6b0b84e1ff33f6ee540e3b7be263ffa Mon Sep 17 00:00:00 2001 From: Venkateswara Naralasetty Date: Thu, 28 Nov 2019 08:21:47 +0000 Subject: [PATCH 0083/1437] ath11k: Advertise MPDU start spacing as no restriction Adverise MPDU start spacing as no restriction in ht capabilities, Since IPQ8074 hw support all sorts of mpdu start spcing. With this observed minor uplink performance improvement in lower data frame size case with Veriwave clients. Signed-off-by: Venkateswara Naralasetty Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath11k/mac.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath11k/mac.c b/drivers/net/wireless/ath/ath11k/mac.c index 5ee7c2a1c6116..b048057b398f0 100644 --- a/drivers/net/wireless/ath/ath11k/mac.c +++ b/drivers/net/wireless/ath/ath11k/mac.c @@ -3089,7 +3089,7 @@ ath11k_create_ht_cap(struct ath11k *ar, u32 ar_ht_cap, u32 rate_cap_rx_chainmask ht_cap.ht_supported = 1; ht_cap.ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K; - ht_cap.ampdu_density = IEEE80211_HT_MPDU_DENSITY_8; + ht_cap.ampdu_density = IEEE80211_HT_MPDU_DENSITY_NONE; ht_cap.cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40; ht_cap.cap |= IEEE80211_HT_CAP_DSSSCCK40; ht_cap.cap |= WLAN_HT_CAP_SM_PS_STATIC << IEEE80211_HT_CAP_SM_PS_SHIFT; -- GitLab From 5e02bc7354e1339ad801c7a2e31eb7f7bbeb7b22 Mon Sep 17 00:00:00 2001 From: Miles Hu Date: Thu, 28 Nov 2019 08:21:49 +0000 Subject: [PATCH 0084/1437] ath11k: fix memory leak in monitor mode remove tail check to avoid last amsdu leak. recycle skb in ppdu id wrap around case Signed-off-by: Miles Hu Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath11k/dp_rx.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/ath/ath11k/dp_rx.c b/drivers/net/wireless/ath/ath11k/dp_rx.c index 8c21925a522a7..85e999c7a2388 100644 --- a/drivers/net/wireless/ath/ath11k/dp_rx.c +++ b/drivers/net/wireless/ath/ath11k/dp_rx.c @@ -3735,8 +3735,15 @@ ath11k_dp_rx_mon_mpdu_pop(struct ath11k *ar, if (ath11k_dp_rx_mon_comp_ppduid(msdu_ppdu_id, ppdu_id, - &rx_bufs_used)) + &rx_bufs_used)) { + if (rx_bufs_used) { + drop_mpdu = true; + dev_kfree_skb_any(msdu); + msdu = NULL; + goto next_msdu; + } return rx_bufs_used; + } pmon->mon_last_linkdesc_paddr = paddr; is_first_msdu = false; } @@ -3938,7 +3945,7 @@ static int ath11k_dp_rx_mon_deliver(struct ath11k *ar, u32 mac_id, ath11k_dp_rx_deliver_msdu(ar, napi, mon_skb); mon_skb = skb_next; - } while (mon_skb && (mon_skb != tail_msdu)); + } while (mon_skb); rxs->flag = 0; return 0; -- GitLab From 28dee8ef7645cdc3e19814ac909c7eff781b66a5 Mon Sep 17 00:00:00 2001 From: Manikanta Pubbisetty Date: Thu, 28 Nov 2019 08:21:50 +0000 Subject: [PATCH 0085/1437] ath11k: fix vht guard interval mapping Guard interval value which comes from VHT_SIG_A TLV has a mapping where value 0 corresponds to LGI, 1 and 3 corresponds to SGI. Value 3 which is SGI(0.4us) in VHT was incorrectly mapped to a GI of 3.2us(only applicable in HE) resulting in incorrect rx GI stats. Fixing the mapping. Signed-off-by: Manikanta Pubbisetty Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath11k/hal_rx.c | 16 +++++++++++++--- drivers/net/wireless/ath/ath11k/hal_rx.h | 6 ++++++ 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/ath/ath11k/hal_rx.c b/drivers/net/wireless/ath/ath11k/hal_rx.c index 6168a4eb0c0bb..9e0f8064e4272 100644 --- a/drivers/net/wireless/ath/ath11k/hal_rx.c +++ b/drivers/net/wireless/ath/ath11k/hal_rx.c @@ -920,6 +920,7 @@ ath11k_hal_rx_parse_mon_status_tlv(struct ath11k_base *ab, (struct hal_rx_vht_sig_a_info *)tlv_data; u32 nsts; u32 group_id; + u8 gi_setting; info0 = __le32_to_cpu(vht_sig->info0); info1 = __le32_to_cpu(vht_sig->info1); @@ -928,9 +929,18 @@ ath11k_hal_rx_parse_mon_status_tlv(struct ath11k_base *ab, info0); ppdu_info->mcs = FIELD_GET(HAL_RX_VHT_SIG_A_INFO_INFO1_MCS, info1); - ppdu_info->gi = - FIELD_GET(HAL_RX_VHT_SIG_A_INFO_INFO1_GI_SETTING, - info1); + gi_setting = FIELD_GET(HAL_RX_VHT_SIG_A_INFO_INFO1_GI_SETTING, + info1); + switch (gi_setting) { + case HAL_RX_VHT_SIG_A_NORMAL_GI: + ppdu_info->gi = HAL_RX_GI_0_8_US; + break; + case HAL_RX_VHT_SIG_A_SHORT_GI: + case HAL_RX_VHT_SIG_A_SHORT_GI_AMBIGUITY: + ppdu_info->gi = HAL_RX_GI_0_4_US; + break; + } + ppdu_info->is_stbc = info0 & HAL_RX_VHT_SIG_A_INFO_INFO0_STBC; nsts = FIELD_GET(HAL_RX_VHT_SIG_A_INFO_INFO0_NSTS, info0); if (ppdu_info->is_stbc && nsts > 0) diff --git a/drivers/net/wireless/ath/ath11k/hal_rx.h b/drivers/net/wireless/ath/ath11k/hal_rx.h index 2271bf56183bd..bb022c781c488 100644 --- a/drivers/net/wireless/ath/ath11k/hal_rx.h +++ b/drivers/net/wireless/ath/ath11k/hal_rx.h @@ -189,6 +189,12 @@ struct hal_rx_vht_sig_a_info { __le32 info1; } __packed; +enum hal_rx_vht_sig_a_gi_setting { + HAL_RX_VHT_SIG_A_NORMAL_GI = 0, + HAL_RX_VHT_SIG_A_SHORT_GI = 1, + HAL_RX_VHT_SIG_A_SHORT_GI_AMBIGUITY = 3, +}; + #define HAL_RX_HE_SIG_A_SU_INFO_INFO0_TRANSMIT_MCS GENMASK(6, 3) #define HAL_RX_HE_SIG_A_SU_INFO_INFO0_DCM BIT(7) #define HAL_RX_HE_SIG_A_SU_INFO_INFO0_TRANSMIT_BW GENMASK(20, 19) -- GitLab From a9e945eadf1759fd2255e952a0b57ac7bf04b4b0 Mon Sep 17 00:00:00 2001 From: Venkateswara Naralasetty Date: Thu, 28 Nov 2019 08:21:51 +0000 Subject: [PATCH 0086/1437] ath11k: update tx duration in station info Update tx duration in station info form PPDU stats so that users can dump tx duration of the station. Signed-off-by: Venkateswara Naralasetty Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath11k/core.h | 1 + drivers/net/wireless/ath/ath11k/dp_rx.c | 2 +- drivers/net/wireless/ath/ath11k/mac.c | 3 +++ 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath11k/core.h b/drivers/net/wireless/ath/ath11k/core.h index 065cb9dc880b8..06482a5c102d8 100644 --- a/drivers/net/wireless/ath/ath11k/core.h +++ b/drivers/net/wireless/ath/ath11k/core.h @@ -335,6 +335,7 @@ struct ath11k_sta { struct rate_info txrate; struct rate_info last_txrate; u64 rx_duration; + u64 tx_duration; u8 rssi_comb; struct ath11k_htt_tx_stats *tx_stats; struct ath11k_rx_peer_stats *rx_stats; diff --git a/drivers/net/wireless/ath/ath11k/dp_rx.c b/drivers/net/wireless/ath/ath11k/dp_rx.c index 85e999c7a2388..8f69de594ada2 100644 --- a/drivers/net/wireless/ath/ath11k/dp_rx.c +++ b/drivers/net/wireless/ath/ath11k/dp_rx.c @@ -1147,7 +1147,7 @@ ath11k_update_per_peer_tx_stats(struct ath11k *ar, arsta->txrate.nss = nss; arsta->txrate.bw = ath11k_mac_bw_to_mac80211_bw(bw); arsta->tx_info.status.rates[0].flags |= ath11k_bw_to_mac80211_bwflags(bw); - + arsta->tx_duration += tx_duration; memcpy(&arsta->last_txrate, &arsta->txrate, sizeof(struct rate_info)); if (succ_pkts) { diff --git a/drivers/net/wireless/ath/ath11k/mac.c b/drivers/net/wireless/ath/ath11k/mac.c index b048057b398f0..624534d4c33ad 100644 --- a/drivers/net/wireless/ath/ath11k/mac.c +++ b/drivers/net/wireless/ath/ath11k/mac.c @@ -5316,6 +5316,9 @@ static void ath11k_mac_op_sta_statistics(struct ieee80211_hw *hw, sinfo->rx_duration = arsta->rx_duration; sinfo->filled |= BIT_ULL(NL80211_STA_INFO_RX_DURATION); + sinfo->tx_duration = arsta->tx_duration; + sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_DURATION); + if (!arsta->txrate.legacy && !arsta->txrate.nss) return; -- GitLab From b9269a078ae609f1315726f1ac4c7d3d8425384f Mon Sep 17 00:00:00 2001 From: Venkateswara Naralasetty Date: Thu, 28 Nov 2019 08:21:54 +0000 Subject: [PATCH 0087/1437] ath11k: Skip update peer stats for management packets Currently HTT_PPDU_STATS_TAG_USR_COMPLTN_ACK_BA_STATUS tag of PPDU stats doesn't have valid success bytes info of management frames. So skip update peer stats for management packets. Signed-off-by: Venkateswara Naralasetty Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath11k/dp.h | 4 ++- drivers/net/wireless/ath/ath11k/dp_rx.c | 33 +++++++++++++++---------- 2 files changed, 23 insertions(+), 14 deletions(-) diff --git a/drivers/net/wireless/ath/ath11k/dp.h b/drivers/net/wireless/ath/ath11k/dp.h index db216f055c425..82ddd70177220 100644 --- a/drivers/net/wireless/ath/ath11k/dp.h +++ b/drivers/net/wireless/ath/ath11k/dp.h @@ -1176,7 +1176,9 @@ struct htt_ppdu_stats_usr_cmpltn_cmn { #define HTT_PPDU_STATS_ACK_BA_INFO_NUM_MPDU_M GENMASK(8, 0) #define HTT_PPDU_STATS_ACK_BA_INFO_NUM_MSDU_M GENMASK(24, 9) -#define HTT_PPDU_STATS_ACK_BA_INFO_TID_NUM GENMASK(3, 0) +#define HTT_PPDU_STATS_ACK_BA_INFO_TID_NUM GENMASK(31, 25) + +#define HTT_PPDU_STATS_NON_QOS_TID 16 struct htt_ppdu_stats_usr_cmpltn_ack_ba_status { u32 ppdu_id; diff --git a/drivers/net/wireless/ath/ath11k/dp_rx.c b/drivers/net/wireless/ath/ath11k/dp_rx.c index 8f69de594ada2..a380efb9caa69 100644 --- a/drivers/net/wireless/ath/ath11k/dp_rx.c +++ b/drivers/net/wireless/ath/ath11k/dp_rx.c @@ -1034,6 +1034,7 @@ ath11k_update_per_peer_tx_stats(struct ath11k *ar, u32 succ_bytes = 0; u16 rate = 0, succ_pkts = 0; u32 tx_duration = 0; + u8 tid = HTT_PPDU_STATS_NON_QOS_TID; bool is_ampdu = false; if (!usr_stats) @@ -1051,6 +1052,8 @@ ath11k_update_per_peer_tx_stats(struct ath11k *ar, succ_bytes = usr_stats->ack_ba.success_bytes; succ_pkts = FIELD_GET(HTT_PPDU_STATS_ACK_BA_INFO_NUM_MSDU_M, usr_stats->ack_ba.info); + tid = FIELD_GET(HTT_PPDU_STATS_ACK_BA_INFO_TID_NUM, + usr_stats->ack_ba.info); } if (common->fes_duration_us) @@ -1156,19 +1159,23 @@ ath11k_update_per_peer_tx_stats(struct ath11k *ar, ieee80211_tx_rate_update(ar->hw, sta, &arsta->tx_info); } - memset(peer_stats, 0, sizeof(*peer_stats)); - - peer_stats->succ_pkts = succ_pkts; - peer_stats->succ_bytes = succ_bytes; - peer_stats->is_ampdu = is_ampdu; - peer_stats->duration = tx_duration; - peer_stats->ba_fails = - HTT_USR_CMPLTN_LONG_RETRY(usr_stats->cmpltn_cmn.flags) + - HTT_USR_CMPLTN_SHORT_RETRY(usr_stats->cmpltn_cmn.flags); - - if (ath11k_debug_is_extd_tx_stats_enabled(ar)) - ath11k_accumulate_per_peer_tx_stats(arsta, - peer_stats, rate_idx); + /* PPDU stats reported for mgmt packet doesn't have valid tx bytes. + * So skip peer stats update for mgmt packets. + */ + if (tid < HTT_PPDU_STATS_NON_QOS_TID) { + memset(peer_stats, 0, sizeof(*peer_stats)); + peer_stats->succ_pkts = succ_pkts; + peer_stats->succ_bytes = succ_bytes; + peer_stats->is_ampdu = is_ampdu; + peer_stats->duration = tx_duration; + peer_stats->ba_fails = + HTT_USR_CMPLTN_LONG_RETRY(usr_stats->cmpltn_cmn.flags) + + HTT_USR_CMPLTN_SHORT_RETRY(usr_stats->cmpltn_cmn.flags); + + if (ath11k_debug_is_extd_tx_stats_enabled(ar)) + ath11k_accumulate_per_peer_tx_stats(arsta, + peer_stats, rate_idx); + } spin_unlock_bh(&ab->base_lock); rcu_read_unlock(); -- GitLab From 0366f42640a4100198edcf03bf7625ee1c46f42b Mon Sep 17 00:00:00 2001 From: Vasanthakumar Thiagarajan Date: Thu, 28 Nov 2019 08:21:57 +0000 Subject: [PATCH 0088/1437] ath11k: Move mac80211 hw allocation before wmi_init command This is to prepare REO ring setup before sending wmi_init command. Firmware expects all the required REO rings to be setup while processing wmi_init command. But as per the current initialization sequence, REO ring configurations are done only after wmi_init command is sent. Also refactoring ath11k_mac_create() into ath11k_mac_alloc() and ath11k_mac_register() to it mac80211 hw structure available before sending wmi_init command. Signed-off-by: Vasanthakumar Thiagarajan Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath11k/core.c | 27 ++++--- drivers/net/wireless/ath/ath11k/mac.c | 97 ++++++++++++++++---------- drivers/net/wireless/ath/ath11k/mac.h | 3 +- 3 files changed, 81 insertions(+), 46 deletions(-) diff --git a/drivers/net/wireless/ath/ath11k/core.c b/drivers/net/wireless/ath/ath11k/core.c index 37d4ecb458db5..c9c9e4b5ef6bd 100644 --- a/drivers/net/wireless/ath/ath11k/core.c +++ b/drivers/net/wireless/ath/ath11k/core.c @@ -378,23 +378,22 @@ static int ath11k_core_pdev_create(struct ath11k_base *ab) return ret; } - ret = ath11k_mac_create(ab); + ret = ath11k_mac_register(ab); if (ret) { - ath11k_err(ab, "failed to create new hw device with mac80211 :%d\n", - ret); + ath11k_err(ab, "failed register the radio with mac80211: %d\n", ret); goto err_pdev_debug; } ret = ath11k_dp_pdev_alloc(ab); if (ret) { ath11k_err(ab, "failed to attach DP pdev: %d\n", ret); - goto err_mac_destroy; + goto err_mac_unregister; } return 0; -err_mac_destroy: - ath11k_mac_destroy(ab); +err_mac_unregister: + ath11k_mac_unregister(ab); err_pdev_debug: ath11k_debug_pdev_destroy(ab); @@ -470,28 +469,37 @@ static int ath11k_core_start(struct ath11k_base *ab, goto err_hif_stop; } + ret = ath11k_mac_allocate(ab); + if (ret) { + ath11k_err(ab, "failed to create new hw device with mac80211 :%d\n", + ret); + goto err_hif_stop; + } + ret = ath11k_wmi_cmd_init(ab); if (ret) { ath11k_err(ab, "failed to send wmi init cmd: %d\n", ret); - goto err_hif_stop; + goto err_mac_destroy; } ret = ath11k_wmi_wait_for_unified_ready(ab); if (ret) { ath11k_err(ab, "failed to receive wmi unified ready event: %d\n", ret); - goto err_hif_stop; + goto err_mac_destroy; } ret = ath11k_dp_tx_htt_h2t_ver_req_msg(ab); if (ret) { ath11k_err(ab, "failed to send htt version request message: %d\n", ret); - goto err_hif_stop; + goto err_mac_destroy; } return 0; +err_mac_destroy: + ath11k_mac_destroy(ab); err_hif_stop: ath11k_ahb_stop(ab); err_wmi_detach: @@ -537,6 +545,7 @@ int ath11k_core_qmi_firmware_ready(struct ath11k_base *ab) err_core_stop: ath11k_core_stop(ab); + ath11k_mac_destroy(ab); err_dp_free: ath11k_dp_free(ab); mutex_unlock(&ab->core_lock); diff --git a/drivers/net/wireless/ath/ath11k/mac.c b/drivers/net/wireless/ath/ath11k/mac.c index 624534d4c33ad..e4dfce6b63f04 100644 --- a/drivers/net/wireless/ath/ath11k/mac.c +++ b/drivers/net/wireless/ath/ath11k/mac.c @@ -5513,7 +5513,38 @@ static const struct wiphy_iftype_ext_capab ath11k_iftypes_ext_capa[] = { }, }; -static int ath11k_mac_register(struct ath11k *ar) +static void __ath11k_mac_unregister(struct ath11k *ar) +{ + cancel_work_sync(&ar->regd_update_work); + + ieee80211_unregister_hw(ar->hw); + + idr_for_each(&ar->txmgmt_idr, ath11k_mac_tx_mgmt_pending_free, ar); + idr_destroy(&ar->txmgmt_idr); + + kfree(ar->mac.sbands[NL80211_BAND_2GHZ].channels); + kfree(ar->mac.sbands[NL80211_BAND_5GHZ].channels); + + SET_IEEE80211_DEV(ar->hw, NULL); +} + +void ath11k_mac_unregister(struct ath11k_base *ab) +{ + struct ath11k *ar; + struct ath11k_pdev *pdev; + int i; + + for (i = 0; i < ab->num_radios; i++) { + pdev = &ab->pdevs[i]; + ar = pdev->ar; + if (!ar) + continue; + + __ath11k_mac_unregister(ar); + } +} + +static int __ath11k_mac_register(struct ath11k *ar) { struct ath11k_base *ab = ar->ab; struct ath11k_pdev_cap *cap = &ar->pdev->cap; @@ -5657,32 +5688,48 @@ err_free: return ret; } -void ath11k_mac_unregister(struct ath11k_base *ab) +int ath11k_mac_register(struct ath11k_base *ab) { struct ath11k *ar; struct ath11k_pdev *pdev; int i; + int ret; for (i = 0; i < ab->num_radios; i++) { pdev = &ab->pdevs[i]; ar = pdev->ar; - if (!ar) - continue; - cancel_work_sync(&ar->regd_update_work); + if (ab->pdevs_macaddr_valid) { + ether_addr_copy(ar->mac_addr, pdev->mac_addr); + } else { + ether_addr_copy(ar->mac_addr, ab->mac_addr); + ar->mac_addr[4] += i; + } - ieee80211_unregister_hw(ar->hw); + ret = __ath11k_mac_register(ar); + if (ret) + goto err_cleanup; - idr_for_each(&ar->txmgmt_idr, ath11k_mac_tx_mgmt_pending_free, ar); - idr_destroy(&ar->txmgmt_idr); + idr_init(&ar->txmgmt_idr); + spin_lock_init(&ar->txmgmt_idr_lock); + } - kfree(ar->mac.sbands[NL80211_BAND_2GHZ].channels); - kfree(ar->mac.sbands[NL80211_BAND_5GHZ].channels); + /* Initialize channel counters frequency value in hertz */ + ab->cc_freq_hz = IPQ8074_CC_FREQ_HERTZ; + ab->free_vdev_map = (1LL << (ab->num_radios * TARGET_NUM_VDEVS)) - 1; + + return 0; - SET_IEEE80211_DEV(ar->hw, NULL); +err_cleanup: + for (i = i - 1; i >= 0; i--) { + pdev = &ab->pdevs[i]; + ar = pdev->ar; + __ath11k_mac_unregister(ar); } + + return ret; } -int ath11k_mac_create(struct ath11k_base *ab) +int ath11k_mac_allocate(struct ath11k_base *ab) { struct ieee80211_hw *hw; struct ath11k *ar; @@ -5699,7 +5746,7 @@ int ath11k_mac_create(struct ath11k_base *ab) if (!hw) { ath11k_warn(ab, "failed to allocate mac80211 hw device\n"); ret = -ENOMEM; - goto err_destroy_mac; + goto err_free_mac; } ar = hw->priv; @@ -5720,13 +5767,6 @@ int ath11k_mac_create(struct ath11k_base *ab) ar->num_tx_chains = get_num_chains(pdev->cap.tx_chain_mask); ar->num_rx_chains = get_num_chains(pdev->cap.rx_chain_mask); - if (ab->pdevs_macaddr_valid) { - ether_addr_copy(ar->mac_addr, pdev->mac_addr); - } else { - ether_addr_copy(ar->mac_addr, ab->mac_addr); - ar->mac_addr[4] += i; - } - pdev->ar = ar; spin_lock_init(&ar->data_lock); INIT_LIST_HEAD(&ar->arvifs); @@ -5744,26 +5784,11 @@ int ath11k_mac_create(struct ath11k_base *ab) INIT_WORK(&ar->wmi_mgmt_tx_work, ath11k_mgmt_over_wmi_tx_work); skb_queue_head_init(&ar->wmi_mgmt_tx_queue); clear_bit(ATH11K_FLAG_MONITOR_ENABLED, &ar->monitor_flags); - - ret = ath11k_mac_register(ar); - if (ret) { - ath11k_warn(ab, "failed to register hw device\n"); - pdev->ar = NULL; - ieee80211_free_hw(hw); - goto err_destroy_mac; - } - - idr_init(&ar->txmgmt_idr); - spin_lock_init(&ar->txmgmt_idr_lock); } - /* Initialize channel counters frequency value in hertz */ - ab->cc_freq_hz = IPQ8074_CC_FREQ_HERTZ; - ab->free_vdev_map = (1LL << (ab->num_radios * TARGET_NUM_VDEVS)) - 1; - return 0; -err_destroy_mac: +err_free_mac: ath11k_mac_destroy(ab); return ret; diff --git a/drivers/net/wireless/ath/ath11k/mac.h b/drivers/net/wireless/ath/ath11k/mac.h index 8c37573ae5dc6..f286531cdd78a 100644 --- a/drivers/net/wireless/ath/ath11k/mac.h +++ b/drivers/net/wireless/ath/ath11k/mac.h @@ -118,9 +118,10 @@ struct ath11k_generic_iter { extern const struct htt_rx_ring_tlv_filter ath11k_mac_mon_status_filter_default; -int ath11k_mac_create(struct ath11k_base *ab); void ath11k_mac_destroy(struct ath11k_base *ab); void ath11k_mac_unregister(struct ath11k_base *ab); +int ath11k_mac_register(struct ath11k_base *ab); +int ath11k_mac_allocate(struct ath11k_base *ab); int ath11k_mac_hw_ratecode_to_legacy_rate(u8 hw_rc, u8 preamble, u8 *rateidx, u16 *rate); u8 ath11k_mac_bitrate_to_idx(const struct ieee80211_supported_band *sband, -- GitLab From 9c57d7e3b4882c29bbcf3e555a1f4163d6e55646 Mon Sep 17 00:00:00 2001 From: Vasanthakumar Thiagarajan Date: Thu, 28 Nov 2019 08:21:58 +0000 Subject: [PATCH 0089/1437] ath11k: Setup REO destination ring before sending wmi_init command Firmware expects all the required REO destination rings setup while processing wmi_init command. Not doing this causes connected stations getting disconnected and not able to connect back. Signed-off-by: Vasanthakumar Thiagarajan Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath11k/core.c | 19 ++++++++-- drivers/net/wireless/ath/ath11k/dp.c | 10 ++++- drivers/net/wireless/ath/ath11k/dp.h | 1 + drivers/net/wireless/ath/ath11k/dp_rx.c | 49 ++++++++++++++++++++----- drivers/net/wireless/ath/ath11k/dp_rx.h | 2 + 5 files changed, 67 insertions(+), 14 deletions(-) diff --git a/drivers/net/wireless/ath/ath11k/core.c b/drivers/net/wireless/ath/ath11k/core.c index c9c9e4b5ef6bd..5cdc9b2aee51a 100644 --- a/drivers/net/wireless/ath/ath11k/core.c +++ b/drivers/net/wireless/ath/ath11k/core.c @@ -10,6 +10,7 @@ #include "ahb.h" #include "core.h" #include "dp_tx.h" +#include "dp_rx.h" #include "debug.h" unsigned int ath11k_debug_mask; @@ -325,6 +326,7 @@ static void ath11k_core_stop(struct ath11k_base *ab) ath11k_qmi_firmware_stop(ab); ath11k_ahb_stop(ab); ath11k_wmi_detach(ab); + ath11k_dp_pdev_reo_cleanup(ab); /* De-Init of components as needed */ } @@ -476,28 +478,38 @@ static int ath11k_core_start(struct ath11k_base *ab, goto err_hif_stop; } + ath11k_dp_pdev_pre_alloc(ab); + + ret = ath11k_dp_pdev_reo_setup(ab); + if (ret) { + ath11k_err(ab, "failed to initialize reo destination rings: %d\n", ret); + goto err_mac_destroy; + } + ret = ath11k_wmi_cmd_init(ab); if (ret) { ath11k_err(ab, "failed to send wmi init cmd: %d\n", ret); - goto err_mac_destroy; + goto err_reo_cleanup; } ret = ath11k_wmi_wait_for_unified_ready(ab); if (ret) { ath11k_err(ab, "failed to receive wmi unified ready event: %d\n", ret); - goto err_mac_destroy; + goto err_reo_cleanup; } ret = ath11k_dp_tx_htt_h2t_ver_req_msg(ab); if (ret) { ath11k_err(ab, "failed to send htt version request message: %d\n", ret); - goto err_mac_destroy; + goto err_reo_cleanup; } return 0; +err_reo_cleanup: + ath11k_dp_pdev_reo_cleanup(ab); err_mac_destroy: ath11k_mac_destroy(ab); err_hif_stop: @@ -561,6 +573,7 @@ static int ath11k_core_reconfigure_on_crash(struct ath11k_base *ab) ath11k_dp_pdev_free(ab); ath11k_ahb_stop(ab); ath11k_wmi_detach(ab); + ath11k_dp_pdev_reo_cleanup(ab); mutex_unlock(&ab->core_lock); ath11k_dp_free(ab); diff --git a/drivers/net/wireless/ath/ath11k/dp.c b/drivers/net/wireless/ath/ath11k/dp.c index 72c21cf6a3522..b966a16a930fd 100644 --- a/drivers/net/wireless/ath/ath11k/dp.c +++ b/drivers/net/wireless/ath/ath11k/dp.c @@ -684,11 +684,10 @@ void ath11k_dp_pdev_free(struct ath11k_base *ab) } } -int ath11k_dp_pdev_alloc(struct ath11k_base *ab) +void ath11k_dp_pdev_pre_alloc(struct ath11k_base *ab) { struct ath11k *ar; struct ath11k_pdev_dp *dp; - int ret; int i; for (i = 0; i < ab->num_radios; i++) { @@ -704,6 +703,13 @@ int ath11k_dp_pdev_alloc(struct ath11k_base *ab) idr_init(&dp->rxdma_mon_buf_ring.bufs_idr); spin_lock_init(&dp->rxdma_mon_buf_ring.idr_lock); } +} + +int ath11k_dp_pdev_alloc(struct ath11k_base *ab) +{ + struct ath11k *ar; + int ret; + int i; /* TODO:Per-pdev rx ring unlike tx ring which is mapped to different AC's */ for (i = 0; i < ab->num_radios; i++) { diff --git a/drivers/net/wireless/ath/ath11k/dp.h b/drivers/net/wireless/ath/ath11k/dp.h index 82ddd70177220..f7e53509ae074 100644 --- a/drivers/net/wireless/ath/ath11k/dp.h +++ b/drivers/net/wireless/ath/ath11k/dp.h @@ -1507,6 +1507,7 @@ void ath11k_dp_vdev_tx_attach(struct ath11k *ar, struct ath11k_vif *arvif); void ath11k_dp_free(struct ath11k_base *ab); int ath11k_dp_alloc(struct ath11k_base *ab); int ath11k_dp_pdev_alloc(struct ath11k_base *ab); +void ath11k_dp_pdev_pre_alloc(struct ath11k_base *ab); void ath11k_dp_pdev_free(struct ath11k_base *ab); int ath11k_dp_tx_htt_srng_setup(struct ath11k_base *ab, u32 ring_id, int mac_id, enum hal_ring_type ring_type); diff --git a/drivers/net/wireless/ath/ath11k/dp_rx.c b/drivers/net/wireless/ath/ath11k/dp_rx.c index a380efb9caa69..1b3b65c0038c8 100644 --- a/drivers/net/wireless/ath/ath11k/dp_rx.c +++ b/drivers/net/wireless/ath/ath11k/dp_rx.c @@ -395,12 +395,51 @@ static void ath11k_dp_rx_pdev_srng_free(struct ath11k *ar) struct ath11k_pdev_dp *dp = &ar->dp; ath11k_dp_srng_cleanup(ar->ab, &dp->rx_refill_buf_ring.refill_buf_ring); - ath11k_dp_srng_cleanup(ar->ab, &dp->reo_dst_ring); ath11k_dp_srng_cleanup(ar->ab, &dp->rxdma_err_dst_ring); ath11k_dp_srng_cleanup(ar->ab, &dp->rx_mon_status_refill_ring.refill_buf_ring); ath11k_dp_srng_cleanup(ar->ab, &dp->rxdma_mon_buf_ring.refill_buf_ring); } +void ath11k_dp_pdev_reo_cleanup(struct ath11k_base *ab) +{ + struct ath11k_pdev_dp *dp; + struct ath11k *ar; + int i; + + for (i = 0; i < ab->num_radios; i++) { + ar = ab->pdevs[i].ar; + dp = &ar->dp; + ath11k_dp_srng_cleanup(ab, &dp->reo_dst_ring); + } +} + +int ath11k_dp_pdev_reo_setup(struct ath11k_base *ab) +{ + struct ath11k *ar; + struct ath11k_pdev_dp *dp; + int ret; + int i; + + for (i = 0; i < ab->num_radios; i++) { + ar = ab->pdevs[i].ar; + dp = &ar->dp; + ret = ath11k_dp_srng_setup(ab, &dp->reo_dst_ring, HAL_REO_DST, + dp->mac_id, dp->mac_id, + DP_REO_DST_RING_SIZE); + if (ret) { + ath11k_warn(ar->ab, "failed to setup reo_dst_ring\n"); + goto err_reo_cleanup; + } + } + + return 0; + +err_reo_cleanup: + ath11k_dp_pdev_reo_cleanup(ab); + + return ret; +} + static int ath11k_dp_rx_pdev_srng_alloc(struct ath11k *ar) { struct ath11k_pdev_dp *dp = &ar->dp; @@ -416,14 +455,6 @@ static int ath11k_dp_rx_pdev_srng_alloc(struct ath11k *ar) return ret; } - ret = ath11k_dp_srng_setup(ar->ab, &dp->reo_dst_ring, HAL_REO_DST, - dp->mac_id, dp->mac_id, - DP_REO_DST_RING_SIZE); - if (ret) { - ath11k_warn(ar->ab, "failed to setup reo_dst_ring\n"); - return ret; - } - ret = ath11k_dp_srng_setup(ar->ab, &dp->rxdma_err_dst_ring, HAL_RXDMA_DST, 0, dp->mac_id, DP_RXDMA_ERR_DST_RING_SIZE); diff --git a/drivers/net/wireless/ath/ath11k/dp_rx.h b/drivers/net/wireless/ath/ath11k/dp_rx.h index 026820fda5689..eec5deaa59ad7 100644 --- a/drivers/net/wireless/ath/ath11k/dp_rx.h +++ b/drivers/net/wireless/ath/ath11k/dp_rx.h @@ -48,6 +48,8 @@ int ath11k_peer_rx_tid_setup(struct ath11k *ar, const u8 *peer_mac, int vdev_id, u8 tid, u32 ba_win_sz, u16 ssn); void ath11k_dp_htt_htc_t2h_msg_handler(struct ath11k_base *ab, struct sk_buff *skb); +int ath11k_dp_pdev_reo_setup(struct ath11k_base *ab); +void ath11k_dp_pdev_reo_cleanup(struct ath11k_base *ab); int ath11k_dp_rx_pdev_alloc(struct ath11k_base *ab, int pdev_idx); void ath11k_dp_rx_pdev_free(struct ath11k_base *ab, int pdev_idx); void ath11k_dp_reo_cmd_list_cleanup(struct ath11k_base *ab); -- GitLab From fcaf49d0f2dce08208dfb545571ea02c8da6d97a Mon Sep 17 00:00:00 2001 From: John Crispin Date: Thu, 28 Nov 2019 08:22:00 +0000 Subject: [PATCH 0090/1437] ath11k: fix indentation in ath11k_mac_prepare_he_mode() Signed-off-by: John Crispin Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath11k/mac.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/ath/ath11k/mac.c b/drivers/net/wireless/ath/ath11k/mac.c index e4dfce6b63f04..978d8768d68a2 100644 --- a/drivers/net/wireless/ath/ath11k/mac.c +++ b/drivers/net/wireless/ath/ath11k/mac.c @@ -3883,15 +3883,15 @@ ath11k_mac_prepare_he_mode(struct ath11k_pdev *pdev, u32 viftype) hecap_phy_ptr = &cap_band->he_cap_phy_info[0]; hemode = FIELD_PREP(HE_MODE_SU_TX_BFEE, HE_SU_BFEE_ENABLE) | - FIELD_PREP(HE_MODE_SU_TX_BFER, HECAP_PHY_SUBFMR_GET(hecap_phy_ptr)) | - FIELD_PREP(HE_MODE_UL_MUMIMO, HECAP_PHY_ULMUMIMO_GET(hecap_phy_ptr)); + FIELD_PREP(HE_MODE_SU_TX_BFER, HECAP_PHY_SUBFMR_GET(hecap_phy_ptr)) | + FIELD_PREP(HE_MODE_UL_MUMIMO, HECAP_PHY_ULMUMIMO_GET(hecap_phy_ptr)); /* TODO WDS and other modes */ if (viftype == NL80211_IFTYPE_AP) { hemode |= FIELD_PREP(HE_MODE_MU_TX_BFER, - HECAP_PHY_MUBFMR_GET(hecap_phy_ptr)) | - FIELD_PREP(HE_MODE_DL_OFDMA, HE_DL_MUOFDMA_ENABLE) | - FIELD_PREP(HE_MODE_UL_OFDMA, HE_UL_MUOFDMA_ENABLE); + HECAP_PHY_MUBFMR_GET(hecap_phy_ptr)) | + FIELD_PREP(HE_MODE_DL_OFDMA, HE_DL_MUOFDMA_ENABLE) | + FIELD_PREP(HE_MODE_UL_OFDMA, HE_UL_MUOFDMA_ENABLE); } else { hemode |= FIELD_PREP(HE_MODE_MU_TX_BFEE, HE_MU_BFEE_ENABLE); } -- GitLab From 18a110b022a5c02e7dc9f6109d0bd93e58ac6ebb Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Fri, 15 Nov 2019 12:39:23 +0100 Subject: [PATCH 0091/1437] netfilter: ctnetlink: netns exit must wait for callbacks Curtis Taylor and Jon Maxwell reported and debugged a crash on 3.10 based kernel. Crash occurs in ctnetlink_conntrack_events because net->nfnl socket is NULL. The nfnl socket was set to NULL by netns destruction running on another cpu. The exiting network namespace calls the relevant destructors in the following order: 1. ctnetlink_net_exit_batch This nulls out the event callback pointer in struct netns. 2. nfnetlink_net_exit_batch This nulls net->nfnl socket and frees it. 3. nf_conntrack_cleanup_net_list This removes all remaining conntrack entries. This is order is correct. The only explanation for the crash so ar is: cpu1: conntrack is dying, eviction occurs: -> nf_ct_delete() -> nf_conntrack_event_report \ -> nf_conntrack_eventmask_report -> notify->fcn() (== ctnetlink_conntrack_events). cpu1: a. fetches rcu protected pointer to obtain ctnetlink event callback. b. gets interrupted. cpu2: runs netns exit handlers: a runs ctnetlink destructor, event cb pointer set to NULL. b runs nfnetlink destructor, nfnl socket is closed and set to NULL. cpu1: c. resumes and trips over NULL net->nfnl. Problem appears to be that ctnetlink_net_exit_batch only prevents future callers of nf_conntrack_eventmask_report() from obtaining the callback. It doesn't wait of other cpus that might have already obtained the callbacks address. I don't see anything in upstream kernels that would prevent similar crash: We need to wait for all cpus to have exited the event callback. Fixes: 9592a5c01e79dbc59eb56fa ("netfilter: ctnetlink: netns support") Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- net/netfilter/nf_conntrack_netlink.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c index d8d33ef52ce09..6a1c8f1f61718 100644 --- a/net/netfilter/nf_conntrack_netlink.c +++ b/net/netfilter/nf_conntrack_netlink.c @@ -3626,6 +3626,9 @@ static void __net_exit ctnetlink_net_exit_batch(struct list_head *net_exit_list) list_for_each_entry(net, net_exit_list, exit_list) ctnetlink_net_exit(net); + + /* wait for other cpus until they are done with ctnl_notifiers */ + synchronize_rcu(); } static struct pernet_operations ctnetlink_net_ops = { -- GitLab From f8edbde885bbcab6a2b4a1b5ca614e6ccb807577 Mon Sep 17 00:00:00 2001 From: Kai-Heng Feng Date: Fri, 29 Nov 2019 14:23:02 +0800 Subject: [PATCH 0092/1437] x86/intel: Disable HPET on Intel Coffee Lake H platforms Coffee Lake H SoC has similar behavior as Coffee Lake, skewed HPET timer once the SoCs entered PC10. So let's disable HPET on CFL-H platforms. Signed-off-by: Kai-Heng Feng Cc: Linus Torvalds Cc: Peter Zijlstra Cc: Thomas Gleixner Cc: bp@alien8.de Cc: feng.tang@intel.com Cc: harry.pan@intel.com Cc: hpa@zytor.com Link: https://lkml.kernel.org/r/20191129062303.18982-1-kai.heng.feng@canonical.com Signed-off-by: Ingo Molnar --- arch/x86/kernel/early-quirks.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/x86/kernel/early-quirks.c b/arch/x86/kernel/early-quirks.c index 4cba91ec80492..606711f5ebf8b 100644 --- a/arch/x86/kernel/early-quirks.c +++ b/arch/x86/kernel/early-quirks.c @@ -710,6 +710,8 @@ static struct chipset early_qrk[] __initdata = { */ { PCI_VENDOR_ID_INTEL, 0x0f00, PCI_CLASS_BRIDGE_HOST, PCI_ANY_ID, 0, force_disable_hpet}, + { PCI_VENDOR_ID_INTEL, 0x3e20, + PCI_CLASS_BRIDGE_HOST, PCI_ANY_ID, 0, force_disable_hpet}, { PCI_VENDOR_ID_INTEL, 0x3ec4, PCI_CLASS_BRIDGE_HOST, PCI_ANY_ID, 0, force_disable_hpet}, { PCI_VENDOR_ID_BROADCOM, 0x4331, -- GitLab From e0748539e3d594dd26f0d27a270f14720b22a406 Mon Sep 17 00:00:00 2001 From: Kai-Heng Feng Date: Fri, 29 Nov 2019 14:23:03 +0800 Subject: [PATCH 0093/1437] x86/intel: Disable HPET on Intel Ice Lake platforms Like CFL and CFL-H, ICL SoC has skewed HPET timer once it hits PC10. So let's disable HPET on ICL. Signed-off-by: Kai-Heng Feng Cc: Linus Torvalds Cc: Peter Zijlstra Cc: Thomas Gleixner Cc: bp@alien8.de Cc: feng.tang@intel.com Cc: harry.pan@intel.com Cc: hpa@zytor.com Link: https://lkml.kernel.org/r/20191129062303.18982-2-kai.heng.feng@canonical.com Signed-off-by: Ingo Molnar --- arch/x86/kernel/early-quirks.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/x86/kernel/early-quirks.c b/arch/x86/kernel/early-quirks.c index 606711f5ebf8b..2f9ec14be3b11 100644 --- a/arch/x86/kernel/early-quirks.c +++ b/arch/x86/kernel/early-quirks.c @@ -714,6 +714,8 @@ static struct chipset early_qrk[] __initdata = { PCI_CLASS_BRIDGE_HOST, PCI_ANY_ID, 0, force_disable_hpet}, { PCI_VENDOR_ID_INTEL, 0x3ec4, PCI_CLASS_BRIDGE_HOST, PCI_ANY_ID, 0, force_disable_hpet}, + { PCI_VENDOR_ID_INTEL, 0x8a12, + PCI_CLASS_BRIDGE_HOST, PCI_ANY_ID, 0, force_disable_hpet}, { PCI_VENDOR_ID_BROADCOM, 0x4331, PCI_CLASS_NETWORK_OTHER, PCI_ANY_ID, 0, apple_airport_reset}, {} -- GitLab From 7074674e7338863e6404909c9761d4d3a610a379 Mon Sep 17 00:00:00 2001 From: Andi Kleen Date: Wed, 20 Nov 2019 16:15:13 -0800 Subject: [PATCH 0094/1437] perf cpumap: Maintain cpumaps ordered and without dups Enforce this in _trim() Needed for followon change. Signed-off-by: Andi Kleen Acked-by: Jiri Olsa Link: http://lore.kernel.org/lkml/20191121001522.180827-4-andi@firstfloor.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/lib/cpumap.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/tools/perf/lib/cpumap.c b/tools/perf/lib/cpumap.c index 2ca1fafa620df..d81656b4635eb 100644 --- a/tools/perf/lib/cpumap.c +++ b/tools/perf/lib/cpumap.c @@ -68,14 +68,28 @@ static struct perf_cpu_map *cpu_map__default_new(void) return cpus; } +static int cmp_int(const void *a, const void *b) +{ + return *(const int *)a - *(const int*)b; +} + static struct perf_cpu_map *cpu_map__trim_new(int nr_cpus, int *tmp_cpus) { size_t payload_size = nr_cpus * sizeof(int); struct perf_cpu_map *cpus = malloc(sizeof(*cpus) + payload_size); + int i, j; if (cpus != NULL) { - cpus->nr = nr_cpus; memcpy(cpus->map, tmp_cpus, payload_size); + qsort(cpus->map, nr_cpus, sizeof(int), cmp_int); + /* Remove dups */ + j = 0; + for (i = 0; i < nr_cpus; i++) { + if (i == 0 || cpus->map[i] != cpus->map[i - 1]) + cpus->map[j++] = cpus->map[i]; + } + cpus->nr = j; + assert(j <= nr_cpus); refcount_set(&cpus->refcnt, 1); } -- GitLab From a2408a70368ade9c99de27da78d49416313b8833 Mon Sep 17 00:00:00 2001 From: Andi Kleen Date: Wed, 20 Nov 2019 16:15:14 -0800 Subject: [PATCH 0095/1437] perf evlist: Maintain evlist->all_cpus Maintain a cpumap in the evlist that is the union of all the cpus of the events. This needs a cpumap merge operation, which is added together with tests. v2: Add tests for cpu map merge Fix handling of duplicates Rename _update to _merge Factor out sorting. Fix handling of NULL maps in merge v3: Add comments and empty lines to _merge Committer testing: # perf test "Merge cpu map" 52: Merge cpu map : Ok # Signed-off-by: Andi Kleen Acked-by: Jiri Olsa Tested-by: Arnaldo Carvalho de Melo Link: http://lore.kernel.org/lkml/20191121001522.180827-5-andi@firstfloor.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/lib/cpumap.c | 57 ++++++++++++++++++++++++ tools/perf/lib/evlist.c | 1 + tools/perf/lib/include/internal/evlist.h | 1 + tools/perf/lib/include/perf/cpumap.h | 2 + tools/perf/tests/builtin-test.c | 5 +++ tools/perf/tests/cpumap.c | 16 +++++++ tools/perf/tests/tests.h | 1 + 7 files changed, 83 insertions(+) diff --git a/tools/perf/lib/cpumap.c b/tools/perf/lib/cpumap.c index d81656b4635eb..f93f4e703e4c7 100644 --- a/tools/perf/lib/cpumap.c +++ b/tools/perf/lib/cpumap.c @@ -286,3 +286,60 @@ int perf_cpu_map__max(struct perf_cpu_map *map) return max; } + +/* + * Merge two cpumaps + * + * orig either gets freed and replaced with a new map, or reused + * with no reference count change (similar to "realloc") + * other has its reference count increased. + */ + +struct perf_cpu_map *perf_cpu_map__merge(struct perf_cpu_map *orig, + struct perf_cpu_map *other) +{ + int *tmp_cpus; + int tmp_len; + int i, j, k; + struct perf_cpu_map *merged; + + if (!orig && !other) + return NULL; + if (!orig) { + perf_cpu_map__get(other); + return other; + } + if (!other) + return orig; + if (orig->nr == other->nr && + !memcmp(orig->map, other->map, orig->nr * sizeof(int))) + return orig; + + tmp_len = orig->nr + other->nr; + tmp_cpus = malloc(tmp_len * sizeof(int)); + if (!tmp_cpus) + return NULL; + + /* Standard merge algorithm from wikipedia */ + i = j = k = 0; + while (i < orig->nr && j < other->nr) { + if (orig->map[i] <= other->map[j]) { + if (orig->map[i] == other->map[j]) + j++; + tmp_cpus[k++] = orig->map[i++]; + } else + tmp_cpus[k++] = other->map[j++]; + } + + while (i < orig->nr) + tmp_cpus[k++] = orig->map[i++]; + + while (j < other->nr) + tmp_cpus[k++] = other->map[j++]; + assert(k <= tmp_len); + + merged = cpu_map__trim_new(k, tmp_cpus); + free(tmp_cpus); + perf_cpu_map__put(orig); + return merged; +} diff --git a/tools/perf/lib/evlist.c b/tools/perf/lib/evlist.c index 205ddbb80bc1f..ae9e65aa24918 100644 --- a/tools/perf/lib/evlist.c +++ b/tools/perf/lib/evlist.c @@ -54,6 +54,7 @@ static void __perf_evlist__propagate_maps(struct perf_evlist *evlist, perf_thread_map__put(evsel->threads); evsel->threads = perf_thread_map__get(evlist->threads); + evlist->all_cpus = perf_cpu_map__merge(evlist->all_cpus, evsel->cpus); } static void perf_evlist__propagate_maps(struct perf_evlist *evlist) diff --git a/tools/perf/lib/include/internal/evlist.h b/tools/perf/lib/include/internal/evlist.h index a2fbccf1922f2..74dc8c3f0b667 100644 --- a/tools/perf/lib/include/internal/evlist.h +++ b/tools/perf/lib/include/internal/evlist.h @@ -18,6 +18,7 @@ struct perf_evlist { int nr_entries; bool has_user_cpus; struct perf_cpu_map *cpus; + struct perf_cpu_map *all_cpus; struct perf_thread_map *threads; int nr_mmaps; size_t mmap_len; diff --git a/tools/perf/lib/include/perf/cpumap.h b/tools/perf/lib/include/perf/cpumap.h index ac9aa497f84ab..6a17ad730cbc1 100644 --- a/tools/perf/lib/include/perf/cpumap.h +++ b/tools/perf/lib/include/perf/cpumap.h @@ -12,6 +12,8 @@ LIBPERF_API struct perf_cpu_map *perf_cpu_map__dummy_new(void); LIBPERF_API struct perf_cpu_map *perf_cpu_map__new(const char *cpu_list); LIBPERF_API struct perf_cpu_map *perf_cpu_map__read(FILE *file); LIBPERF_API struct perf_cpu_map *perf_cpu_map__get(struct perf_cpu_map *map); +LIBPERF_API struct perf_cpu_map *perf_cpu_map__merge(struct perf_cpu_map *orig, + struct perf_cpu_map *other); LIBPERF_API void perf_cpu_map__put(struct perf_cpu_map *map); LIBPERF_API int perf_cpu_map__cpu(const struct perf_cpu_map *cpus, int idx); LIBPERF_API int perf_cpu_map__nr(const struct perf_cpu_map *cpus); diff --git a/tools/perf/tests/builtin-test.c b/tools/perf/tests/builtin-test.c index 7115aa32a51ee..82d19a8fcac7c 100644 --- a/tools/perf/tests/builtin-test.c +++ b/tools/perf/tests/builtin-test.c @@ -259,6 +259,11 @@ static struct test generic_tests[] = { .desc = "Print cpu map", .func = test__cpu_map_print, }, + { + .desc = "Merge cpu map", + .func = test__cpu_map_merge, + }, + { .desc = "Probe SDT events", .func = test__sdt_event, diff --git a/tools/perf/tests/cpumap.c b/tools/perf/tests/cpumap.c index 8a0d236202b05..4ac56741ac5fe 100644 --- a/tools/perf/tests/cpumap.c +++ b/tools/perf/tests/cpumap.c @@ -120,3 +120,19 @@ int test__cpu_map_print(struct test *test __maybe_unused, int subtest __maybe_un TEST_ASSERT_VAL("failed to convert map", cpu_map_print("1-10,12-20,22-30,32-40")); return 0; } + +int test__cpu_map_merge(struct test *test __maybe_unused, int subtest __maybe_unused) +{ + struct perf_cpu_map *a = perf_cpu_map__new("4,2,1"); + struct perf_cpu_map *b = perf_cpu_map__new("4,5,7"); + struct perf_cpu_map *c = perf_cpu_map__merge(a, b); + char buf[100]; + + TEST_ASSERT_VAL("failed to merge map: bad nr", c->nr == 5); + cpu_map__snprint(c, buf, sizeof(buf)); + TEST_ASSERT_VAL("failed to merge map: bad result", !strcmp(buf, "1-2,4-5,7")); + perf_cpu_map__put(a); + perf_cpu_map__put(b); + perf_cpu_map__put(c); + return 0; +} diff --git a/tools/perf/tests/tests.h b/tools/perf/tests/tests.h index 25aea387e2bf0..4f9ae6af78ecb 100644 --- a/tools/perf/tests/tests.h +++ b/tools/perf/tests/tests.h @@ -98,6 +98,7 @@ int test__event_update(struct test *test, int subtest); int test__event_times(struct test *test, int subtest); int test__backward_ring_buffer(struct test *test, int subtest); int test__cpu_map_print(struct test *test, int subtest); +int test__cpu_map_merge(struct test *test, int subtest); int test__sdt_event(struct test *test, int subtest); int test__is_printable_array(struct test *test, int subtest); int test__bitmap_print(struct test *test, int subtest); -- GitLab From a8cbe40fe9f4ba499cc60b8b6a6851c2c1963797 Mon Sep 17 00:00:00 2001 From: Andi Kleen Date: Wed, 20 Nov 2019 16:15:15 -0800 Subject: [PATCH 0096/1437] perf evsel: Add iterator to iterate over events ordered by CPU Add some common code that is needed to iterate over all events in CPU order. Used in followon patches Signed-off-by: Andi Kleen Acked-by: Jiri Olsa Link: http://lore.kernel.org/lkml/20191121001522.180827-6-andi@firstfloor.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/cpumap.h | 1 + tools/perf/util/evlist.c | 32 ++++++++++++++++++++++++++++++++ tools/perf/util/evlist.h | 8 ++++++++ tools/perf/util/evsel.h | 1 + 4 files changed, 42 insertions(+) diff --git a/tools/perf/util/cpumap.h b/tools/perf/util/cpumap.h index 57943f3685f8c..3a442f0214684 100644 --- a/tools/perf/util/cpumap.h +++ b/tools/perf/util/cpumap.h @@ -63,4 +63,5 @@ int cpu_map__build_map(struct perf_cpu_map *cpus, struct perf_cpu_map **res, int cpu_map__cpu(struct perf_cpu_map *cpus, int idx); bool cpu_map__has(struct perf_cpu_map *cpus, int cpu); + #endif /* __PERF_CPUMAP_H */ diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c index fdce590d22786..dae6e846b2f8b 100644 --- a/tools/perf/util/evlist.c +++ b/tools/perf/util/evlist.c @@ -342,6 +342,38 @@ static int perf_evlist__nr_threads(struct evlist *evlist, return perf_thread_map__nr(evlist->core.threads); } +void evlist__cpu_iter_start(struct evlist *evlist) +{ + struct evsel *pos; + + /* + * Reset the per evsel cpu_iter. This is needed because + * each evsel's cpumap may have a different index space, + * and some operations need the index to modify + * the FD xyarray (e.g. open, close) + */ + evlist__for_each_entry(evlist, pos) + pos->cpu_iter = 0; +} + +bool evsel__cpu_iter_skip_no_inc(struct evsel *ev, int cpu) +{ + if (ev->cpu_iter >= ev->core.cpus->nr) + return true; + if (cpu >= 0 && ev->core.cpus->map[ev->cpu_iter] != cpu) + return true; + return false; +} + +bool evsel__cpu_iter_skip(struct evsel *ev, int cpu) +{ + if (!evsel__cpu_iter_skip_no_inc(ev, cpu)) { + ev->cpu_iter++; + return false; + } + return true; +} + void evlist__disable(struct evlist *evlist) { struct evsel *pos; diff --git a/tools/perf/util/evlist.h b/tools/perf/util/evlist.h index 3655b9ebb1473..22e2f58eabead 100644 --- a/tools/perf/util/evlist.h +++ b/tools/perf/util/evlist.h @@ -334,9 +334,17 @@ void perf_evlist__to_front(struct evlist *evlist, #define evlist__for_each_entry_safe(evlist, tmp, evsel) \ __evlist__for_each_entry_safe(&(evlist)->core.entries, tmp, evsel) +#define evlist__for_each_cpu(evlist, index, cpu) \ + evlist__cpu_iter_start(evlist); \ + perf_cpu_map__for_each_cpu (cpu, index, (evlist)->core.all_cpus) + void perf_evlist__set_tracking_event(struct evlist *evlist, struct evsel *tracking_evsel); +void evlist__cpu_iter_start(struct evlist *evlist); +bool evsel__cpu_iter_skip(struct evsel *ev, int cpu); +bool evsel__cpu_iter_skip_no_inc(struct evsel *ev, int cpu); + struct evsel * perf_evlist__find_evsel_by_str(struct evlist *evlist, const char *str); diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h index ddc5ee6f6592b..b10d5ba219662 100644 --- a/tools/perf/util/evsel.h +++ b/tools/perf/util/evsel.h @@ -95,6 +95,7 @@ struct evsel { bool collect_stat; bool weak_group; bool percore; + int cpu_iter; const char *pmu_name; struct { perf_evsel__sb_cb_t *cb; -- GitLab From 99d6141d677a8cd0b35390a29527c8def42538b1 Mon Sep 17 00:00:00 2001 From: Andi Kleen Date: Wed, 20 Nov 2019 16:15:16 -0800 Subject: [PATCH 0097/1437] perf evsel: Add functions to close evsel on a CPU Refactor the existing all CPU function to use the per CPU close internally. Export APIs to close per CPU. Signed-off-by: Andi Kleen Acked-by: Jiri Olsa Link: http://lore.kernel.org/lkml/20191121001522.180827-7-andi@firstfloor.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/lib/evsel.c | 27 +++++++++++++++++++++------ tools/perf/lib/include/perf/evsel.h | 1 + 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/tools/perf/lib/evsel.c b/tools/perf/lib/evsel.c index 5a89857b0381c..ea775dacbd2d2 100644 --- a/tools/perf/lib/evsel.c +++ b/tools/perf/lib/evsel.c @@ -114,16 +114,23 @@ int perf_evsel__open(struct perf_evsel *evsel, struct perf_cpu_map *cpus, return err; } +static void perf_evsel__close_fd_cpu(struct perf_evsel *evsel, int cpu) +{ + int thread; + + for (thread = 0; thread < xyarray__max_y(evsel->fd); ++thread) { + if (FD(evsel, cpu, thread) >= 0) + close(FD(evsel, cpu, thread)); + FD(evsel, cpu, thread) = -1; + } +} + void perf_evsel__close_fd(struct perf_evsel *evsel) { - int cpu, thread; + int cpu; for (cpu = 0; cpu < xyarray__max_x(evsel->fd); cpu++) - for (thread = 0; thread < xyarray__max_y(evsel->fd); ++thread) { - if (FD(evsel, cpu, thread) >= 0) - close(FD(evsel, cpu, thread)); - FD(evsel, cpu, thread) = -1; - } + perf_evsel__close_fd_cpu(evsel, cpu); } void perf_evsel__free_fd(struct perf_evsel *evsel) @@ -141,6 +148,14 @@ void perf_evsel__close(struct perf_evsel *evsel) perf_evsel__free_fd(evsel); } +void perf_evsel__close_cpu(struct perf_evsel *evsel, int cpu) +{ + if (evsel->fd == NULL) + return; + + perf_evsel__close_fd_cpu(evsel, cpu); +} + int perf_evsel__read_size(struct perf_evsel *evsel) { u64 read_format = evsel->attr.read_format; diff --git a/tools/perf/lib/include/perf/evsel.h b/tools/perf/lib/include/perf/evsel.h index 557f5815a9c96..e7add554f861c 100644 --- a/tools/perf/lib/include/perf/evsel.h +++ b/tools/perf/lib/include/perf/evsel.h @@ -26,6 +26,7 @@ LIBPERF_API void perf_evsel__delete(struct perf_evsel *evsel); LIBPERF_API int perf_evsel__open(struct perf_evsel *evsel, struct perf_cpu_map *cpus, struct perf_thread_map *threads); LIBPERF_API void perf_evsel__close(struct perf_evsel *evsel); +LIBPERF_API void perf_evsel__close_cpu(struct perf_evsel *evsel, int cpu); LIBPERF_API int perf_evsel__read(struct perf_evsel *evsel, int cpu, int thread, struct perf_counts_values *count); LIBPERF_API int perf_evsel__enable(struct perf_evsel *evsel); -- GitLab From 7736627b865defff2430e95df235b4aa2450bc37 Mon Sep 17 00:00:00 2001 From: Andi Kleen Date: Wed, 20 Nov 2019 16:15:17 -0800 Subject: [PATCH 0098/1437] perf stat: Use affinity for closing file descriptors Closing a perf fd can also trigger an IPI to the target CPU. Use the same affinity technique as we use for reading/enabling events to closing to optimize the CPU transitions. Before on a large test case with 94 CPUs: % time seconds usecs/call calls errors syscall ------ ----------- ----------- --------- --------- ---------------- 32.56 3.085463 50 61483 close After: 10.54 0.735704 11 61485 close Signed-off-by: Andi Kleen Acked-by: Jiri Olsa Link: http://lore.kernel.org/lkml/20191121001522.180827-8-andi@firstfloor.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/evlist.c | 31 +++++++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c index dae6e846b2f8b..2e8d38a324be9 100644 --- a/tools/perf/util/evlist.c +++ b/tools/perf/util/evlist.c @@ -18,6 +18,7 @@ #include "debug.h" #include "units.h" #include // page_size +#include "affinity.h" #include "../perf.h" #include "asm/bug.h" #include "bpf-event.h" @@ -1169,9 +1170,35 @@ void perf_evlist__set_selected(struct evlist *evlist, void evlist__close(struct evlist *evlist) { struct evsel *evsel; + struct affinity affinity; + int cpu, i; - evlist__for_each_entry_reverse(evlist, evsel) - evsel__close(evsel); + /* + * With perf record core.cpus is usually NULL. + * Use the old method to handle this for now. + */ + if (!evlist->core.cpus) { + evlist__for_each_entry_reverse(evlist, evsel) + evsel__close(evsel); + return; + } + + if (affinity__setup(&affinity) < 0) + return; + evlist__for_each_cpu(evlist, i, cpu) { + affinity__set(&affinity, cpu); + + evlist__for_each_entry_reverse(evlist, evsel) { + if (evsel__cpu_iter_skip(evsel, cpu)) + continue; + perf_evsel__close_cpu(&evsel->core, evsel->cpu_iter - 1); + } + } + affinity__cleanup(&affinity); + evlist__for_each_entry_reverse(evlist, evsel) { + perf_evsel__free_fd(&evsel->core); + perf_evsel__free_id(&evsel->core); + } } static int perf_evlist__create_syswide_maps(struct evlist *evlist) -- GitLab From e0e6a6ca3ac211cc07486330a2b89f41ea31b4dd Mon Sep 17 00:00:00 2001 From: Andi Kleen Date: Wed, 20 Nov 2019 16:15:18 -0800 Subject: [PATCH 0099/1437] perf stat: Factor out open error handling Factor out the open error handling into a separate function. This is useful for followon patches who need to duplicate this. No behavior change intended. Signed-off-by: Andi Kleen Acked-by: Jiri Olsa Link: http://lore.kernel.org/lkml/20191121001522.180827-9-andi@firstfloor.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-stat.c | 100 +++++++++++++++++++++++--------------- 1 file changed, 60 insertions(+), 40 deletions(-) diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index 0a15253b438c5..1d9d7161815e5 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c @@ -420,6 +420,57 @@ static bool is_target_alive(struct target *_target, return false; } +enum counter_recovery { + COUNTER_SKIP, + COUNTER_RETRY, + COUNTER_FATAL, +}; + +static enum counter_recovery stat_handle_error(struct evsel *counter) +{ + char msg[BUFSIZ]; + /* + * PPC returns ENXIO for HW counters until 2.6.37 + * (behavior changed with commit b0a873e). + */ + if (errno == EINVAL || errno == ENOSYS || + errno == ENOENT || errno == EOPNOTSUPP || + errno == ENXIO) { + if (verbose > 0) + ui__warning("%s event is not supported by the kernel.\n", + perf_evsel__name(counter)); + counter->supported = false; + + if ((counter->leader != counter) || + !(counter->leader->core.nr_members > 1)) + return COUNTER_SKIP; + } else if (perf_evsel__fallback(counter, errno, msg, sizeof(msg))) { + if (verbose > 0) + ui__warning("%s\n", msg); + return COUNTER_RETRY; + } else if (target__has_per_thread(&target) && + evsel_list->core.threads && + evsel_list->core.threads->err_thread != -1) { + /* + * For global --per-thread case, skip current + * error thread. + */ + if (!thread_map__remove(evsel_list->core.threads, + evsel_list->core.threads->err_thread)) { + evsel_list->core.threads->err_thread = -1; + return COUNTER_RETRY; + } + } + + perf_evsel__open_strerror(counter, &target, + errno, msg, sizeof(msg)); + ui__error("%s\n", msg); + + if (child_pid != -1) + kill(child_pid, SIGTERM); + return COUNTER_FATAL; +} + static int __run_perf_stat(int argc, const char **argv, int run_idx) { int interval = stat_config.interval; @@ -469,47 +520,16 @@ try_again: goto try_again; } - /* - * PPC returns ENXIO for HW counters until 2.6.37 - * (behavior changed with commit b0a873e). - */ - if (errno == EINVAL || errno == ENOSYS || - errno == ENOENT || errno == EOPNOTSUPP || - errno == ENXIO) { - if (verbose > 0) - ui__warning("%s event is not supported by the kernel.\n", - perf_evsel__name(counter)); - counter->supported = false; - - if ((counter->leader != counter) || - !(counter->leader->core.nr_members > 1)) - continue; - } else if (perf_evsel__fallback(counter, errno, msg, sizeof(msg))) { - if (verbose > 0) - ui__warning("%s\n", msg); - goto try_again; - } else if (target__has_per_thread(&target) && - evsel_list->core.threads && - evsel_list->core.threads->err_thread != -1) { - /* - * For global --per-thread case, skip current - * error thread. - */ - if (!thread_map__remove(evsel_list->core.threads, - evsel_list->core.threads->err_thread)) { - evsel_list->core.threads->err_thread = -1; - goto try_again; - } + switch (stat_handle_error(counter)) { + case COUNTER_FATAL: + return -1; + case COUNTER_RETRY: + goto try_again; + case COUNTER_SKIP: + continue; + default: + break; } - - perf_evsel__open_strerror(counter, &target, - errno, msg, sizeof(msg)); - ui__error("%s\n", msg); - - if (child_pid != -1) - kill(child_pid, SIGTERM); - - return -1; } counter->supported = true; -- GitLab From 4804e0111662d7d89edf4e767a64c6f7e4778bb1 Mon Sep 17 00:00:00 2001 From: Andi Kleen Date: Wed, 20 Nov 2019 16:15:19 -0800 Subject: [PATCH 0100/1437] perf stat: Use affinity for opening events Restructure the event opening in perf stat to cycle through the events by CPU after setting affinity to that CPU. This eliminates IPI overhead in the perf API. We have to loop through the CPU in the outter builtin-stat code instead of leaving that to low level functions. It has to change the weak group fallback strategy slightly. Since we cannot easily undo the opens for other CPUs move the weak group retry to a separate loop. Before with a large test case with 94 CPUs: % time seconds usecs/call calls errors syscall ------ ----------- ----------- --------- --------- ---------------- 42.75 4.050910 67 60046 110 perf_event_open After: 26.86 0.944396 16 58069 110 perf_event_open (the number changes slightly because the weak group retries work differently and the test case relies on weak groups) Committer notes: Added one of the hunks in a patch provided by Andi after I noticed that the "event times" 'perf test' entry was segfaulting. Signed-off-by: Andi Kleen Acked-by: Jiri Olsa Link: http://lore.kernel.org/lkml/20191121001522.180827-10-andi@firstfloor.org Link: http://lore.kernel.org/lkml/20191127232657.GL84886@tassilo.jf.intel.com # Fix Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-record.c | 2 +- tools/perf/builtin-stat.c | 121 +++++++++++++++++++++++++++------ tools/perf/tests/event-times.c | 4 +- tools/perf/util/evlist.c | 10 ++- tools/perf/util/evlist.h | 3 +- tools/perf/util/evsel.c | 22 ++++-- tools/perf/util/evsel.h | 5 +- tools/perf/util/stat.c | 5 +- tools/perf/util/stat.h | 3 +- 9 files changed, 141 insertions(+), 34 deletions(-) diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index b5063d3b6fd07..fb19ef63cc35f 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c @@ -832,7 +832,7 @@ try_again: if ((errno == EINVAL || errno == EBADF) && pos->leader != pos && pos->weak_group) { - pos = perf_evlist__reset_weak_group(evlist, pos); + pos = perf_evlist__reset_weak_group(evlist, pos, true); goto try_again; } rc = -errno; diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index 1d9d7161815e5..cf8516e701e2c 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c @@ -65,6 +65,7 @@ #include "util/target.h" #include "util/time-utils.h" #include "util/top.h" +#include "util/affinity.h" #include "asm/bug.h" #include @@ -440,6 +441,11 @@ static enum counter_recovery stat_handle_error(struct evsel *counter) ui__warning("%s event is not supported by the kernel.\n", perf_evsel__name(counter)); counter->supported = false; + /* + * errored is a sticky flag that means one of the counter's + * cpu event had a problem and needs to be reexamined. + */ + counter->errored = true; if ((counter->leader != counter) || !(counter->leader->core.nr_members > 1)) @@ -484,6 +490,9 @@ static int __run_perf_stat(int argc, const char **argv, int run_idx) int status = 0; const bool forks = (argc > 0); bool is_pipe = STAT_RECORD ? perf_stat.data.is_pipe : false; + struct affinity affinity; + int i, cpu; + bool second_pass = false; if (interval) { ts.tv_sec = interval / USEC_PER_MSEC; @@ -508,30 +517,104 @@ static int __run_perf_stat(int argc, const char **argv, int run_idx) if (group) perf_evlist__set_leader(evsel_list); - evlist__for_each_entry(evsel_list, counter) { + if (affinity__setup(&affinity) < 0) + return -1; + + evlist__for_each_cpu (evsel_list, i, cpu) { + affinity__set(&affinity, cpu); + + evlist__for_each_entry(evsel_list, counter) { + if (evsel__cpu_iter_skip(counter, cpu)) + continue; + if (counter->reset_group || counter->errored) + continue; try_again: - if (create_perf_stat_counter(counter, &stat_config, &target) < 0) { - - /* Weak group failed. Reset the group. */ - if ((errno == EINVAL || errno == EBADF) && - counter->leader != counter && - counter->weak_group) { - counter = perf_evlist__reset_weak_group(evsel_list, counter); - goto try_again; + if (create_perf_stat_counter(counter, &stat_config, &target, + counter->cpu_iter - 1) < 0) { + + /* + * Weak group failed. We cannot just undo this here + * because earlier CPUs might be in group mode, and the kernel + * doesn't support mixing group and non group reads. Defer + * it to later. + * Don't close here because we're in the wrong affinity. + */ + if ((errno == EINVAL || errno == EBADF) && + counter->leader != counter && + counter->weak_group) { + perf_evlist__reset_weak_group(evsel_list, counter, false); + assert(counter->reset_group); + second_pass = true; + continue; + } + + switch (stat_handle_error(counter)) { + case COUNTER_FATAL: + return -1; + case COUNTER_RETRY: + goto try_again; + case COUNTER_SKIP: + continue; + default: + break; + } + } + counter->supported = true; + } + } - switch (stat_handle_error(counter)) { - case COUNTER_FATAL: - return -1; - case COUNTER_RETRY: - goto try_again; - case COUNTER_SKIP: - continue; - default: - break; + if (second_pass) { + /* + * Now redo all the weak group after closing them, + * and also close errored counters. + */ + + evlist__for_each_cpu(evsel_list, i, cpu) { + affinity__set(&affinity, cpu); + /* First close errored or weak retry */ + evlist__for_each_entry(evsel_list, counter) { + if (!counter->reset_group && !counter->errored) + continue; + if (evsel__cpu_iter_skip_no_inc(counter, cpu)) + continue; + perf_evsel__close_cpu(&counter->core, counter->cpu_iter); + } + /* Now reopen weak */ + evlist__for_each_entry(evsel_list, counter) { + if (!counter->reset_group && !counter->errored) + continue; + if (evsel__cpu_iter_skip(counter, cpu)) + continue; + if (!counter->reset_group) + continue; +try_again_reset: + pr_debug2("reopening weak %s\n", perf_evsel__name(counter)); + if (create_perf_stat_counter(counter, &stat_config, &target, + counter->cpu_iter - 1) < 0) { + + switch (stat_handle_error(counter)) { + case COUNTER_FATAL: + return -1; + case COUNTER_RETRY: + goto try_again_reset; + case COUNTER_SKIP: + continue; + default: + break; + } + } + counter->supported = true; } } - counter->supported = true; + } + affinity__cleanup(&affinity); + + evlist__for_each_entry(evsel_list, counter) { + if (!counter->supported) { + perf_evsel__free_fd(&counter->core); + continue; + } l = strlen(counter->unit); if (l > stat_config.unit_width) diff --git a/tools/perf/tests/event-times.c b/tools/perf/tests/event-times.c index 1ee8704e22849..1e8a9f5c356dd 100644 --- a/tools/perf/tests/event-times.c +++ b/tools/perf/tests/event-times.c @@ -125,7 +125,7 @@ static int attach__cpu_disabled(struct evlist *evlist) evsel->core.attr.disabled = 1; - err = perf_evsel__open_per_cpu(evsel, cpus); + err = perf_evsel__open_per_cpu(evsel, cpus, -1); if (err) { if (err == -EACCES) return TEST_SKIP; @@ -152,7 +152,7 @@ static int attach__cpu_enabled(struct evlist *evlist) return -1; } - err = perf_evsel__open_per_cpu(evsel, cpus); + err = perf_evsel__open_per_cpu(evsel, cpus, -1); if (err == -EACCES) return TEST_SKIP; diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c index 2e8d38a324be9..096a4ea65b1ba 100644 --- a/tools/perf/util/evlist.c +++ b/tools/perf/util/evlist.c @@ -1636,7 +1636,8 @@ void perf_evlist__force_leader(struct evlist *evlist) } struct evsel *perf_evlist__reset_weak_group(struct evlist *evsel_list, - struct evsel *evsel) + struct evsel *evsel, + bool close) { struct evsel *c2, *leader; bool is_open = true; @@ -1653,10 +1654,15 @@ struct evsel *perf_evlist__reset_weak_group(struct evlist *evsel_list, if (c2 == evsel) is_open = false; if (c2->leader == leader) { - if (is_open) + if (is_open && close) perf_evsel__close(&c2->core); c2->leader = c2; c2->core.nr_members = 0; + /* + * Set this for all former members of the group + * to indicate they get reopened. + */ + c2->reset_group = true; } } return leader; diff --git a/tools/perf/util/evlist.h b/tools/perf/util/evlist.h index 22e2f58eabead..f5bd5c386df11 100644 --- a/tools/perf/util/evlist.h +++ b/tools/perf/util/evlist.h @@ -356,5 +356,6 @@ bool perf_evlist__exclude_kernel(struct evlist *evlist); void perf_evlist__force_leader(struct evlist *evlist); struct evsel *perf_evlist__reset_weak_group(struct evlist *evlist, - struct evsel *evsel); + struct evsel *evsel, + bool close); #endif /* __PERF_EVLIST_H */ diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index f4dea055b0808..aa180d1df50f7 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c @@ -1587,8 +1587,9 @@ static int perf_event_open(struct evsel *evsel, return fd; } -int evsel__open(struct evsel *evsel, struct perf_cpu_map *cpus, - struct perf_thread_map *threads) +static int evsel__open_cpu(struct evsel *evsel, struct perf_cpu_map *cpus, + struct perf_thread_map *threads, + int start_cpu, int end_cpu) { int cpu, thread, nthreads; unsigned long flags = PERF_FLAG_FD_CLOEXEC; @@ -1665,7 +1666,7 @@ retry_sample_id: display_attr(&evsel->core.attr); - for (cpu = 0; cpu < cpus->nr; cpu++) { + for (cpu = start_cpu; cpu < end_cpu; cpu++) { for (thread = 0; thread < nthreads; thread++) { int fd, group_fd; @@ -1843,6 +1844,12 @@ out_close: return err; } +int evsel__open(struct evsel *evsel, struct perf_cpu_map *cpus, + struct perf_thread_map *threads) +{ + return evsel__open_cpu(evsel, cpus, threads, 0, cpus ? cpus->nr : 1); +} + void evsel__close(struct evsel *evsel) { perf_evsel__close(&evsel->core); @@ -1850,9 +1857,14 @@ void evsel__close(struct evsel *evsel) } int perf_evsel__open_per_cpu(struct evsel *evsel, - struct perf_cpu_map *cpus) + struct perf_cpu_map *cpus, + int cpu) { - return evsel__open(evsel, cpus, NULL); + if (cpu == -1) + return evsel__open_cpu(evsel, cpus, NULL, 0, + cpus ? cpus->nr : 1); + + return evsel__open_cpu(evsel, cpus, NULL, cpu, cpu + 1); } int perf_evsel__open_per_thread(struct evsel *evsel, diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h index b10d5ba219662..ca82a93960cd7 100644 --- a/tools/perf/util/evsel.h +++ b/tools/perf/util/evsel.h @@ -94,6 +94,8 @@ struct evsel { struct evsel *metric_leader; bool collect_stat; bool weak_group; + bool reset_group; + bool errored; bool percore; int cpu_iter; const char *pmu_name; @@ -223,7 +225,8 @@ int evsel__enable(struct evsel *evsel); int evsel__disable(struct evsel *evsel); int perf_evsel__open_per_cpu(struct evsel *evsel, - struct perf_cpu_map *cpus); + struct perf_cpu_map *cpus, + int cpu); int perf_evsel__open_per_thread(struct evsel *evsel, struct perf_thread_map *threads); int evsel__open(struct evsel *evsel, struct perf_cpu_map *cpus, diff --git a/tools/perf/util/stat.c b/tools/perf/util/stat.c index 332cb730785bd..5f26137b8d602 100644 --- a/tools/perf/util/stat.c +++ b/tools/perf/util/stat.c @@ -464,7 +464,8 @@ size_t perf_event__fprintf_stat_config(union perf_event *event, FILE *fp) int create_perf_stat_counter(struct evsel *evsel, struct perf_stat_config *config, - struct target *target) + struct target *target, + int cpu) { struct perf_event_attr *attr = &evsel->core.attr; struct evsel *leader = evsel->leader; @@ -518,7 +519,7 @@ int create_perf_stat_counter(struct evsel *evsel, } if (target__has_cpu(target) && !target__has_per_thread(target)) - return perf_evsel__open_per_cpu(evsel, evsel__cpus(evsel)); + return perf_evsel__open_per_cpu(evsel, evsel__cpus(evsel), cpu); return perf_evsel__open_per_thread(evsel, evsel->core.threads); } diff --git a/tools/perf/util/stat.h b/tools/perf/util/stat.h index bfa9aaf36ce6f..fb990efa54a8a 100644 --- a/tools/perf/util/stat.h +++ b/tools/perf/util/stat.h @@ -214,7 +214,8 @@ size_t perf_event__fprintf_stat_config(union perf_event *event, FILE *fp); int create_perf_stat_counter(struct evsel *evsel, struct perf_stat_config *config, - struct target *target); + struct target *target, + int cpu); void perf_evlist__print_counters(struct evlist *evlist, struct perf_stat_config *config, -- GitLab From 4b49ab708d1804bc8b2fcdde79844b8bc98f7ef6 Mon Sep 17 00:00:00 2001 From: Andi Kleen Date: Wed, 20 Nov 2019 16:15:20 -0800 Subject: [PATCH 0101/1437] perf stat: Use affinity for reading Restructure event reading to use affinity to minimize the number of IPIs needed. Before on a large test case with 94 CPUs: % time seconds usecs/call calls errors syscall ------ ----------- ----------- --------- --------- ---------------- 3.16 0.106079 4 22082 read After: 3.43 0.081295 3 22082 read Signed-off-by: Andi Kleen Acked-by: Jiri Olsa Link: http://lore.kernel.org/lkml/20191121001522.180827-11-andi@firstfloor.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-stat.c | 97 ++++++++++++++++++++++----------------- tools/perf/util/evsel.h | 1 + 2 files changed, 57 insertions(+), 41 deletions(-) diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index cf8516e701e2c..a098c2ebf4eac 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c @@ -266,15 +266,10 @@ static int read_single_counter(struct evsel *counter, int cpu, * Read out the results of a single counter: * do not aggregate counts across CPUs in system-wide mode */ -static int read_counter(struct evsel *counter, struct timespec *rs) +static int read_counter_cpu(struct evsel *counter, struct timespec *rs, int cpu) { int nthreads = perf_thread_map__nr(evsel_list->core.threads); - int ncpus, cpu, thread; - - if (target__has_cpu(&target) && !target__has_per_thread(&target)) - ncpus = perf_evsel__nr_cpus(counter); - else - ncpus = 1; + int thread; if (!counter->supported) return -ENOENT; @@ -283,40 +278,38 @@ static int read_counter(struct evsel *counter, struct timespec *rs) nthreads = 1; for (thread = 0; thread < nthreads; thread++) { - for (cpu = 0; cpu < ncpus; cpu++) { - struct perf_counts_values *count; - - count = perf_counts(counter->counts, cpu, thread); - - /* - * The leader's group read loads data into its group members - * (via perf_evsel__read_counter) and sets threir count->loaded. - */ - if (!perf_counts__is_loaded(counter->counts, cpu, thread) && - read_single_counter(counter, cpu, thread, rs)) { - counter->counts->scaled = -1; - perf_counts(counter->counts, cpu, thread)->ena = 0; - perf_counts(counter->counts, cpu, thread)->run = 0; - return -1; - } + struct perf_counts_values *count; - perf_counts__set_loaded(counter->counts, cpu, thread, false); + count = perf_counts(counter->counts, cpu, thread); - if (STAT_RECORD) { - if (perf_evsel__write_stat_event(counter, cpu, thread, count)) { - pr_err("failed to write stat event\n"); - return -1; - } - } + /* + * The leader's group read loads data into its group members + * (via perf_evsel__read_counter()) and sets their count->loaded. + */ + if (!perf_counts__is_loaded(counter->counts, cpu, thread) && + read_single_counter(counter, cpu, thread, rs)) { + counter->counts->scaled = -1; + perf_counts(counter->counts, cpu, thread)->ena = 0; + perf_counts(counter->counts, cpu, thread)->run = 0; + return -1; + } + + perf_counts__set_loaded(counter->counts, cpu, thread, false); - if (verbose > 1) { - fprintf(stat_config.output, - "%s: %d: %" PRIu64 " %" PRIu64 " %" PRIu64 "\n", - perf_evsel__name(counter), - cpu, - count->val, count->ena, count->run); + if (STAT_RECORD) { + if (perf_evsel__write_stat_event(counter, cpu, thread, count)) { + pr_err("failed to write stat event\n"); + return -1; } } + + if (verbose > 1) { + fprintf(stat_config.output, + "%s: %d: %" PRIu64 " %" PRIu64 " %" PRIu64 "\n", + perf_evsel__name(counter), + cpu, + count->val, count->ena, count->run); + } } return 0; @@ -325,15 +318,37 @@ static int read_counter(struct evsel *counter, struct timespec *rs) static void read_counters(struct timespec *rs) { struct evsel *counter; - int ret; + struct affinity affinity; + int i, ncpus, cpu; + + if (affinity__setup(&affinity) < 0) + return; + + ncpus = perf_cpu_map__nr(evsel_list->core.all_cpus); + if (!target__has_cpu(&target) || target__has_per_thread(&target)) + ncpus = 1; + evlist__for_each_cpu(evsel_list, i, cpu) { + if (i >= ncpus) + break; + affinity__set(&affinity, cpu); + + evlist__for_each_entry(evsel_list, counter) { + if (evsel__cpu_iter_skip(counter, cpu)) + continue; + if (!counter->err) { + counter->err = read_counter_cpu(counter, rs, + counter->cpu_iter - 1); + } + } + } + affinity__cleanup(&affinity); evlist__for_each_entry(evsel_list, counter) { - ret = read_counter(counter, rs); - if (ret) + if (counter->err) pr_debug("failed to read counter %s\n", counter->name); - - if (ret == 0 && perf_stat_process_counter(&stat_config, counter)) + if (counter->err == 0 && perf_stat_process_counter(&stat_config, counter)) pr_warning("failed to process counter %s\n", counter->name); + counter->err = 0; } } diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h index ca82a93960cd7..c8af4bc23f8ff 100644 --- a/tools/perf/util/evsel.h +++ b/tools/perf/util/evsel.h @@ -86,6 +86,7 @@ struct evsel { struct list_head config_terms; struct bpf_object *bpf_obj; int bpf_fd; + int err; bool auto_merge_stats; bool merged_stat; const char * metric_expr; -- GitLab From 363fb12189d58ebc60456561b8540d68013782a6 Mon Sep 17 00:00:00 2001 From: Andi Kleen Date: Wed, 20 Nov 2019 16:15:21 -0800 Subject: [PATCH 0102/1437] perf evsel: Add functions to enable/disable for a specific CPU Refactor the existing functions to use these functions internally. Used in the next patch. Signed-off-by: Andi Kleen Acked-by: Jiri Olsa Link: http://lore.kernel.org/lkml/20191121001522.180827-12-andi@firstfloor.org Link: http://lore.kernel.org/lkml/20191127232657.GL84886@tassilo.jf.intel.com # Fix Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/lib/evsel.c | 49 +++++++++++++++++++++-------- tools/perf/lib/include/perf/evsel.h | 2 ++ tools/perf/util/evsel.c | 13 +++++++- tools/perf/util/evsel.h | 2 ++ 4 files changed, 52 insertions(+), 14 deletions(-) diff --git a/tools/perf/lib/evsel.c b/tools/perf/lib/evsel.c index ea775dacbd2d2..4dc06289f4c75 100644 --- a/tools/perf/lib/evsel.c +++ b/tools/perf/lib/evsel.c @@ -198,38 +198,61 @@ int perf_evsel__read(struct perf_evsel *evsel, int cpu, int thread, } static int perf_evsel__run_ioctl(struct perf_evsel *evsel, - int ioc, void *arg) + int ioc, void *arg, + int cpu) { - int cpu, thread; + int thread; - for (cpu = 0; cpu < xyarray__max_x(evsel->fd); cpu++) { - for (thread = 0; thread < xyarray__max_y(evsel->fd); thread++) { - int fd = FD(evsel, cpu, thread), - err = ioctl(fd, ioc, arg); + for (thread = 0; thread < xyarray__max_y(evsel->fd); thread++) { + int fd = FD(evsel, cpu, thread), + err = ioctl(fd, ioc, arg); - if (err) - return err; - } + if (err) + return err; } return 0; } +int perf_evsel__enable_cpu(struct perf_evsel *evsel, int cpu) +{ + return perf_evsel__run_ioctl(evsel, PERF_EVENT_IOC_ENABLE, NULL, cpu); +} + int perf_evsel__enable(struct perf_evsel *evsel) { - return perf_evsel__run_ioctl(evsel, PERF_EVENT_IOC_ENABLE, 0); + int i; + int err = 0; + + for (i = 0; i < xyarray__max_x(evsel->fd) && !err; i++) + err = perf_evsel__run_ioctl(evsel, PERF_EVENT_IOC_ENABLE, NULL, i); + return err; +} + +int perf_evsel__disable_cpu(struct perf_evsel *evsel, int cpu) +{ + return perf_evsel__run_ioctl(evsel, PERF_EVENT_IOC_DISABLE, NULL, cpu); } int perf_evsel__disable(struct perf_evsel *evsel) { - return perf_evsel__run_ioctl(evsel, PERF_EVENT_IOC_DISABLE, 0); + int i; + int err = 0; + + for (i = 0; i < xyarray__max_x(evsel->fd) && !err; i++) + err = perf_evsel__run_ioctl(evsel, PERF_EVENT_IOC_DISABLE, NULL, i); + return err; } int perf_evsel__apply_filter(struct perf_evsel *evsel, const char *filter) { - return perf_evsel__run_ioctl(evsel, + int err = 0, i; + + for (i = 0; i < evsel->cpus->nr && !err; i++) + err = perf_evsel__run_ioctl(evsel, PERF_EVENT_IOC_SET_FILTER, - (void *)filter); + (void *)filter, i); + return err; } struct perf_cpu_map *perf_evsel__cpus(struct perf_evsel *evsel) diff --git a/tools/perf/lib/include/perf/evsel.h b/tools/perf/lib/include/perf/evsel.h index e7add554f861c..c82ec39a4ad06 100644 --- a/tools/perf/lib/include/perf/evsel.h +++ b/tools/perf/lib/include/perf/evsel.h @@ -30,7 +30,9 @@ LIBPERF_API void perf_evsel__close_cpu(struct perf_evsel *evsel, int cpu); LIBPERF_API int perf_evsel__read(struct perf_evsel *evsel, int cpu, int thread, struct perf_counts_values *count); LIBPERF_API int perf_evsel__enable(struct perf_evsel *evsel); +LIBPERF_API int perf_evsel__enable_cpu(struct perf_evsel *evsel, int cpu); LIBPERF_API int perf_evsel__disable(struct perf_evsel *evsel); +LIBPERF_API int perf_evsel__disable_cpu(struct perf_evsel *evsel, int cpu); LIBPERF_API struct perf_cpu_map *perf_evsel__cpus(struct perf_evsel *evsel); LIBPERF_API struct perf_thread_map *perf_evsel__threads(struct perf_evsel *evsel); LIBPERF_API struct perf_event_attr *perf_evsel__attr(struct perf_evsel *evsel); diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index aa180d1df50f7..a69e64236120a 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c @@ -1223,16 +1223,27 @@ int perf_evsel__append_addr_filter(struct evsel *evsel, const char *filter) return perf_evsel__append_filter(evsel, "%s,%s", filter); } +/* Caller has to clear disabled after going through all CPUs. */ +int evsel__enable_cpu(struct evsel *evsel, int cpu) +{ + return perf_evsel__enable_cpu(&evsel->core, cpu); +} + int evsel__enable(struct evsel *evsel) { int err = perf_evsel__enable(&evsel->core); if (!err) evsel->disabled = false; - return err; } +/* Caller has to set disabled after going through all CPUs. */ +int evsel__disable_cpu(struct evsel *evsel, int cpu) +{ + return perf_evsel__disable_cpu(&evsel->core, cpu); +} + int evsel__disable(struct evsel *evsel) { int err = perf_evsel__disable(&evsel->core); diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h index c8af4bc23f8ff..dc14f4a823cd6 100644 --- a/tools/perf/util/evsel.h +++ b/tools/perf/util/evsel.h @@ -222,8 +222,10 @@ int perf_evsel__set_filter(struct evsel *evsel, const char *filter); int perf_evsel__append_tp_filter(struct evsel *evsel, const char *filter); int perf_evsel__append_addr_filter(struct evsel *evsel, const char *filter); +int evsel__enable_cpu(struct evsel *evsel, int cpu); int evsel__enable(struct evsel *evsel); int evsel__disable(struct evsel *evsel); +int evsel__disable_cpu(struct evsel *evsel, int cpu); int perf_evsel__open_per_cpu(struct evsel *evsel, struct perf_cpu_map *cpus, -- GitLab From 704e2f5b700da4c912635cf161c3e982737eb89e Mon Sep 17 00:00:00 2001 From: Andi Kleen Date: Wed, 20 Nov 2019 16:15:22 -0800 Subject: [PATCH 0103/1437] perf stat: Use affinity for enabling/disabling events Restructure event enabling/disabling to use affinity, which minimizes the number of IPIs needed. Before on a large test case with 94 CPUs: % time seconds usecs/call calls errors syscall ------ ----------- ----------- --------- --------- ---------------- 54.65 1.899986 22 84812 660 ioctl after: 39.21 0.930451 10 84796 644 ioctl Signed-off-by: Andi Kleen Acked-by: Jiri Olsa Link: http://lore.kernel.org/lkml/20191121001522.180827-13-andi@firstfloor.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/evlist.c | 40 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 37 insertions(+), 3 deletions(-) diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c index 096a4ea65b1ba..1548237b6558d 100644 --- a/tools/perf/util/evlist.c +++ b/tools/perf/util/evlist.c @@ -378,11 +378,28 @@ bool evsel__cpu_iter_skip(struct evsel *ev, int cpu) void evlist__disable(struct evlist *evlist) { struct evsel *pos; + struct affinity affinity; + int cpu, i; + + if (affinity__setup(&affinity) < 0) + return; + + evlist__for_each_cpu(evlist, i, cpu) { + affinity__set(&affinity, cpu); + evlist__for_each_entry(evlist, pos) { + if (evsel__cpu_iter_skip(pos, cpu)) + continue; + if (pos->disabled || !perf_evsel__is_group_leader(pos) || !pos->core.fd) + continue; + evsel__disable_cpu(pos, pos->cpu_iter - 1); + } + } + affinity__cleanup(&affinity); evlist__for_each_entry(evlist, pos) { - if (pos->disabled || !perf_evsel__is_group_leader(pos) || !pos->core.fd) + if (!perf_evsel__is_group_leader(pos) || !pos->core.fd) continue; - evsel__disable(pos); + pos->disabled = true; } evlist->enabled = false; @@ -391,11 +408,28 @@ void evlist__disable(struct evlist *evlist) void evlist__enable(struct evlist *evlist) { struct evsel *pos; + struct affinity affinity; + int cpu, i; + if (affinity__setup(&affinity) < 0) + return; + + evlist__for_each_cpu(evlist, i, cpu) { + affinity__set(&affinity, cpu); + + evlist__for_each_entry(evlist, pos) { + if (evsel__cpu_iter_skip(pos, cpu)) + continue; + if (!perf_evsel__is_group_leader(pos) || !pos->core.fd) + continue; + evsel__enable_cpu(pos, pos->cpu_iter - 1); + } + } + affinity__cleanup(&affinity); evlist__for_each_entry(evlist, pos) { if (!perf_evsel__is_group_leader(pos) || !pos->core.fd) continue; - evsel__enable(pos); + pos->disabled = false; } evlist->enabled = true; -- GitLab From fa7f7e7354957422b43ea950b672d3e731f27e68 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Tue, 26 Nov 2019 15:59:13 -0800 Subject: [PATCH 0104/1437] perf jit: Move test functionality in to a test Adds a test for minimal jit_write_elf functionality. Committer testing: # perf test jit 61: Test jit_write_elf : Ok # # perf test -v jit 61: Test jit_write_elf : --- start --- test child forked, pid 10460 Writing jit code to: /tmp/perf-test-KqxURR test child finished with 0 ---- end ---- Test jit_write_elf: Ok # Committer notes: Fix up the case where HAVE_JITDUMP is no defined. Signed-off-by: Ian Rogers Tested-by: Arnaldo Carvalho de Melo Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Alexios Zavras Cc: Allison Randal Cc: Florian Fainelli Cc: Greg Kroah-Hartman Cc: Jiri Olsa Cc: Kate Stewart Cc: Leo Yan Cc: Mark Rutland Cc: Michael Petlan Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Song Liu Cc: Stephane Eranian Cc: Thomas Gleixner Link: http://lore.kernel.org/lkml/20191126235913.41855-1-irogers@google.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/Build | 1 + tools/perf/tests/builtin-test.c | 4 +++ tools/perf/tests/genelf.c | 51 +++++++++++++++++++++++++++++++++ tools/perf/tests/tests.h | 1 + tools/perf/util/genelf.c | 46 ----------------------------- 5 files changed, 57 insertions(+), 46 deletions(-) create mode 100644 tools/perf/tests/genelf.c diff --git a/tools/perf/tests/Build b/tools/perf/tests/Build index a3c595fba9434..1692529639b0d 100644 --- a/tools/perf/tests/Build +++ b/tools/perf/tests/Build @@ -54,6 +54,7 @@ perf-y += unit_number__scnprintf.o perf-y += mem2node.o perf-y += maps.o perf-y += time-utils-test.o +perf-y += genelf.o $(OUTPUT)tests/llvm-src-base.c: tests/bpf-script-example.c tests/Build $(call rule_mkdir) diff --git a/tools/perf/tests/builtin-test.c b/tools/perf/tests/builtin-test.c index 82d19a8fcac7c..5f05db75cdd86 100644 --- a/tools/perf/tests/builtin-test.c +++ b/tools/perf/tests/builtin-test.c @@ -301,6 +301,10 @@ static struct test generic_tests[] = { .desc = "time utils", .func = test__time_utils, }, + { + .desc = "Test jit_write_elf", + .func = test__jit_write_elf, + }, { .desc = "maps__merge_in", .func = test__maps__merge_in, diff --git a/tools/perf/tests/genelf.c b/tools/perf/tests/genelf.c new file mode 100644 index 0000000000000..f797f9823e891 --- /dev/null +++ b/tools/perf/tests/genelf.c @@ -0,0 +1,51 @@ +// SPDX-License-Identifier: GPL-2.0-only +#include +#include +#include +#include +#include +#include + +#include "debug.h" +#include "tests.h" + +#ifdef HAVE_JITDUMP +#include +#include "../util/genelf.h" +#endif + +#define TEMPL "/tmp/perf-test-XXXXXX" + +int test__jit_write_elf(struct test *test __maybe_unused, + int subtest __maybe_unused) +{ +#ifdef HAVE_JITDUMP + static unsigned char x86_code[] = { + 0xBB, 0x2A, 0x00, 0x00, 0x00, /* movl $42, %ebx */ + 0xB8, 0x01, 0x00, 0x00, 0x00, /* movl $1, %eax */ + 0xCD, 0x80 /* int $0x80 */ + }; + char path[PATH_MAX]; + int fd, ret; + + strcpy(path, TEMPL); + + fd = mkstemp(path); + if (fd < 0) { + perror("mkstemp failed"); + return TEST_FAIL; + } + + pr_info("Writing jit code to: %s\n", path); + + ret = jit_write_elf(fd, 0, "main", x86_code, sizeof(x86_code), + NULL, 0, NULL, 0, 0); + close(fd); + + unlink(path); + + return ret ? TEST_FAIL : 0; +#else + return TEST_SKIP; +#endif +} diff --git a/tools/perf/tests/tests.h b/tools/perf/tests/tests.h index 4f9ae6af78ecb..9a160fef47c9c 100644 --- a/tools/perf/tests/tests.h +++ b/tools/perf/tests/tests.h @@ -110,6 +110,7 @@ int test__unit_number__scnprint(struct test *test, int subtest); int test__mem2node(struct test *t, int subtest); int test__maps__merge_in(struct test *t, int subtest); int test__time_utils(struct test *t, int subtest); +int test__jit_write_elf(struct test *test, int subtest); bool test__bp_signal_is_supported(void); bool test__bp_account_is_supported(void); diff --git a/tools/perf/util/genelf.c b/tools/perf/util/genelf.c index f9f18b8b1df9a..aed49806a09ba 100644 --- a/tools/perf/util/genelf.c +++ b/tools/perf/util/genelf.c @@ -8,15 +8,12 @@ */ #include -#include -#include #include #include #include #include #include #include -#include #include #include #ifdef HAVE_DWARF_SUPPORT @@ -31,8 +28,6 @@ #define NT_GNU_BUILD_ID 3 #endif -#define JVMTI - #define BUILD_ID_URANDOM /* different uuid for each run */ #ifdef HAVE_LIBCRYPTO @@ -511,44 +506,3 @@ error: return retval; } - -#ifndef JVMTI - -static unsigned char x86_code[] = { - 0xBB, 0x2A, 0x00, 0x00, 0x00, /* movl $42, %ebx */ - 0xB8, 0x01, 0x00, 0x00, 0x00, /* movl $1, %eax */ - 0xCD, 0x80 /* int $0x80 */ -}; - -static struct options options; - -int main(int argc, char **argv) -{ - int c, fd, ret; - - while ((c = getopt(argc, argv, "o:h")) != -1) { - switch (c) { - case 'o': - options.output = optarg; - break; - case 'h': - printf("Usage: genelf -o output_file [-h]\n"); - return 0; - default: - errx(1, "unknown option"); - } - } - - fd = open(options.output, O_CREAT|O_TRUNC|O_RDWR, 0666); - if (fd == -1) - err(1, "cannot create file %s", options.output); - - ret = jit_write_elf(fd, "main", x86_code, sizeof(x86_code)); - close(fd); - - if (ret != 0) - unlink(options.output); - - return ret; -} -#endif -- GitLab From 77b91c1a525d84cb560a4baef6f5f548b5c23f80 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Fri, 29 Nov 2019 15:47:51 -0300 Subject: [PATCH 0105/1437] perf machine: Fill map_symbol->maps in append_inlines() to fix segfault I forgot to fill in the map_symbol->maps field in append_inlines() which then makes code down the line segfault when trying to deref it. It doesn't make any sense to have an addr_location with its 'map' member not NULL while its 'maps' is NULL, after all al->maps is where al->map is in. It is done that way so that we don't have to have in each 'struct map' a pointer to the 'struct maps' it is in, as we had in the past when we would have 'map->mg', before 'struct maps' was combined with 'struct map_groups', because there was always a one-to-one relationship for these structs. This fixes a segfault when processing DWARF callgraphs in 'perf report'. Reported-by: Jiri Olsa Cc: Adrian Hunter Cc: Namhyung Kim Fixes: 08f6680e627e ("perf tools: Add a 'struct map_groups' pointer to 'struct map_symbol'") Link: http://lore.kernel.org/lkml/20191129160631.GD26963@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/machine.c | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c index 416d174d223c5..c8c5410315e81 100644 --- a/tools/perf/util/machine.c +++ b/tools/perf/util/machine.c @@ -2446,6 +2446,7 @@ static int append_inlines(struct callchain_cursor *cursor, struct map_symbol *ms list_for_each_entry(ilist, &inline_node->val, list) { struct map_symbol ilist_ms = { + .maps = ms->maps, .map = map, .sym = ilist->symbol, }; -- GitLab From ee1bcfe01251e8958b53de45d2b0c85e09dd2719 Mon Sep 17 00:00:00 2001 From: wenxu Date: Wed, 20 Nov 2019 12:30:42 +0800 Subject: [PATCH 0106/1437] netfilter: nf_flow_table_offload: Fix block setup as TC_SETUP_FT cmd Set up block through TC_SETUP_FT command. Fixes: c29f74e0df7a ("netfilter: nf_flow_table: hardware offload support") Signed-off-by: wenxu Signed-off-by: Pablo Neira Ayuso --- net/netfilter/nf_flow_table_offload.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/netfilter/nf_flow_table_offload.c b/net/netfilter/nf_flow_table_offload.c index c54c9a6cc981b..6067268ab9bcc 100644 --- a/net/netfilter/nf_flow_table_offload.c +++ b/net/netfilter/nf_flow_table_offload.c @@ -822,7 +822,7 @@ int nf_flow_table_offload_setup(struct nf_flowtable *flowtable, bo.extack = &extack; INIT_LIST_HEAD(&bo.cb_list); - err = dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_BLOCK, &bo); + err = dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_FT, &bo); if (err < 0) return err; -- GitLab From e052901991ae21e15851bfc89c682bfcb39a4dcf Mon Sep 17 00:00:00 2001 From: wenxu Date: Wed, 20 Nov 2019 13:12:22 +0800 Subject: [PATCH 0107/1437] netfilter: nf_flow_table_offload: Fix block_cb tc_setup_type as TC_SETUP_CLSFLOWER Add/del/stats flows through block_cb call must set the tc_setup_type as TC_SETUP_CLSFLOWER. Fixes: c29f74e0df7a ("netfilter: nf_flow_table: hardware offload support") Signed-off-by: wenxu Signed-off-by: Pablo Neira Ayuso --- net/netfilter/nf_flow_table_offload.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/net/netfilter/nf_flow_table_offload.c b/net/netfilter/nf_flow_table_offload.c index 6067268ab9bcc..b3ad285e057df 100644 --- a/net/netfilter/nf_flow_table_offload.c +++ b/net/netfilter/nf_flow_table_offload.c @@ -574,7 +574,7 @@ static int flow_offload_tuple_add(struct flow_offload_work *offload, cls_flow.rule = flow_rule->rule; list_for_each_entry(block_cb, &flowtable->flow_block.cb_list, list) { - err = block_cb->cb(TC_SETUP_FT, &cls_flow, + err = block_cb->cb(TC_SETUP_CLSFLOWER, &cls_flow, block_cb->cb_priv); if (err < 0) continue; @@ -599,7 +599,7 @@ static void flow_offload_tuple_del(struct flow_offload_work *offload, &offload->flow->tuplehash[dir].tuple, &extack); list_for_each_entry(block_cb, &flowtable->flow_block.cb_list, list) - block_cb->cb(TC_SETUP_FT, &cls_flow, block_cb->cb_priv); + block_cb->cb(TC_SETUP_CLSFLOWER, &cls_flow, block_cb->cb_priv); offload->flow->flags |= FLOW_OFFLOAD_HW_DEAD; } @@ -656,7 +656,7 @@ static void flow_offload_tuple_stats(struct flow_offload_work *offload, &offload->flow->tuplehash[dir].tuple, &extack); list_for_each_entry(block_cb, &flowtable->flow_block.cb_list, list) - block_cb->cb(TC_SETUP_FT, &cls_flow, block_cb->cb_priv); + block_cb->cb(TC_SETUP_CLSFLOWER, &cls_flow, block_cb->cb_priv); memcpy(stats, &cls_flow.stats, sizeof(*stats)); } -- GitLab From dc4d3f2e12a6e8a22d13b0d648a6529e403b65a6 Mon Sep 17 00:00:00 2001 From: Nathan Chancellor Date: Tue, 26 Nov 2019 13:12:26 -0700 Subject: [PATCH 0108/1437] netfilter: nf_flow_table_offload: Don't use offset uninitialized in flow_offload_port_{d,s}nat Clang warns (trimmed the second warning for brevity): ../net/netfilter/nf_flow_table_offload.c:342:2: warning: variable 'offset' is used uninitialized whenever switch default is taken [-Wsometimes-uninitialized] default: ^~~~~~~ ../net/netfilter/nf_flow_table_offload.c:346:57: note: uninitialized use occurs here flow_offload_mangle(entry, flow_offload_l4proto(flow), offset, ^~~~~~ ../net/netfilter/nf_flow_table_offload.c:331:12: note: initialize the variable 'offset' to silence this warning u32 offset; ^ = 0 Match what was done in the flow_offload_ipv{4,6}_{d,s}nat functions and just return in the default case, since port would also be uninitialized. Fixes: c29f74e0df7a ("netfilter: nf_flow_table: hardware offload support") Link: https://github.com/ClangBuiltLinux/linux/issues/780 Signed-off-by: Nathan Chancellor Reported-by: kernelci.org bot Reviewed-by: Nick Desaulniers Signed-off-by: Pablo Neira Ayuso --- net/netfilter/nf_flow_table_offload.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/netfilter/nf_flow_table_offload.c b/net/netfilter/nf_flow_table_offload.c index b3ad285e057df..dd78ae5441e97 100644 --- a/net/netfilter/nf_flow_table_offload.c +++ b/net/netfilter/nf_flow_table_offload.c @@ -340,7 +340,7 @@ static void flow_offload_port_snat(struct net *net, offset = 0; /* offsetof(struct tcphdr, dest); */ break; default: - break; + return; } flow_offload_mangle(entry, flow_offload_l4proto(flow), offset, @@ -367,7 +367,7 @@ static void flow_offload_port_dnat(struct net *net, offset = 0; /* offsetof(struct tcphdr, dest); */ break; default: - break; + return; } flow_offload_mangle(entry, flow_offload_l4proto(flow), offset, -- GitLab From c7c17e6a03e08860f7a40095643e72c24c3f896b Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Thu, 28 Nov 2019 13:25:48 +0100 Subject: [PATCH 0109/1437] netfilter: conntrack: tell compiler to not inline nf_ct_resolve_clash At this time compiler inlines it, but this code will not be executed under normal conditions. Also, no inlining allows to use "nf_ct_resolve_clash%return" perf probe. Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- net/netfilter/nf_conntrack_core.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c index 0af1898af2b80..f475fec84536a 100644 --- a/net/netfilter/nf_conntrack_core.c +++ b/net/netfilter/nf_conntrack_core.c @@ -895,9 +895,10 @@ static void nf_ct_acct_merge(struct nf_conn *ct, enum ip_conntrack_info ctinfo, } /* Resolve race on insertion if this protocol allows this. */ -static int nf_ct_resolve_clash(struct net *net, struct sk_buff *skb, - enum ip_conntrack_info ctinfo, - struct nf_conntrack_tuple_hash *h) +static __cold noinline int +nf_ct_resolve_clash(struct net *net, struct sk_buff *skb, + enum ip_conntrack_info ctinfo, + struct nf_conntrack_tuple_hash *h) { /* This is the conntrack entry already in hashes that won race. */ struct nf_conn *ct = nf_ct_tuplehash_to_ctrack(h); -- GitLab From d50264f1fef92967e2d2c9d91bdb05de214ddacf Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Fri, 29 Nov 2019 10:07:01 +0100 Subject: [PATCH 0110/1437] netfilter: nf_flow_table_offload: add IPv6 match description Add missing IPv6 matching description to flow_rule object. Fixes: 5c27d8d76ce8 ("netfilter: nf_flow_table_offload: add IPv6 support") Signed-off-by: Pablo Neira Ayuso --- net/netfilter/nf_flow_table_offload.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/net/netfilter/nf_flow_table_offload.c b/net/netfilter/nf_flow_table_offload.c index dd78ae5441e97..c94ebad78c5c4 100644 --- a/net/netfilter/nf_flow_table_offload.c +++ b/net/netfilter/nf_flow_table_offload.c @@ -28,6 +28,7 @@ struct nf_flow_key { struct flow_dissector_key_basic basic; union { struct flow_dissector_key_ipv4_addrs ipv4; + struct flow_dissector_key_ipv6_addrs ipv6; }; struct flow_dissector_key_tcp tcp; struct flow_dissector_key_ports tp; @@ -57,6 +58,7 @@ static int nf_flow_rule_match(struct nf_flow_match *match, NF_FLOW_DISSECTOR(match, FLOW_DISSECTOR_KEY_CONTROL, control); NF_FLOW_DISSECTOR(match, FLOW_DISSECTOR_KEY_BASIC, basic); NF_FLOW_DISSECTOR(match, FLOW_DISSECTOR_KEY_IPV4_ADDRS, ipv4); + NF_FLOW_DISSECTOR(match, FLOW_DISSECTOR_KEY_IPV6_ADDRS, ipv6); NF_FLOW_DISSECTOR(match, FLOW_DISSECTOR_KEY_TCP, tcp); NF_FLOW_DISSECTOR(match, FLOW_DISSECTOR_KEY_PORTS, tp); @@ -69,9 +71,18 @@ static int nf_flow_rule_match(struct nf_flow_match *match, key->ipv4.dst = tuple->dst_v4.s_addr; mask->ipv4.dst = 0xffffffff; break; + case AF_INET6: + key->control.addr_type = FLOW_DISSECTOR_KEY_IPV6_ADDRS; + key->basic.n_proto = htons(ETH_P_IPV6); + key->ipv6.src = tuple->src_v6; + memset(&mask->ipv6.src, 0xff, sizeof(mask->ipv6.src)); + key->ipv6.dst = tuple->dst_v6; + memset(&mask->ipv6.dst, 0xff, sizeof(mask->ipv6.dst)); + break; default: return -EOPNOTSUPP; } + match->dissector.used_keys |= BIT(key->control.addr_type); mask->basic.n_proto = 0xffff; switch (tuple->l4proto) { @@ -96,7 +107,6 @@ static int nf_flow_rule_match(struct nf_flow_match *match, match->dissector.used_keys |= BIT(FLOW_DISSECTOR_KEY_CONTROL) | BIT(FLOW_DISSECTOR_KEY_BASIC) | - BIT(FLOW_DISSECTOR_KEY_IPV4_ADDRS) | BIT(FLOW_DISSECTOR_KEY_PORTS); return 0; } -- GitLab From d1f4c966475c6dd2545c6625022cb24e878bee11 Mon Sep 17 00:00:00 2001 From: wenxu Date: Wed, 13 Nov 2019 12:21:07 +0800 Subject: [PATCH 0111/1437] netfilter: nf_tables_offload: Check for the NETDEV_UNREGISTER event Check for the NETDEV_UNREGISTER event from the nft_offload_netdev_event function, which is the event that actually triggers the clean up. Fixes: 06d392cbe3db ("netfilter: nf_tables_offload: remove rules when the device unregisters") Signed-off-by: wenxu Signed-off-by: Pablo Neira Ayuso --- net/netfilter/nf_tables_offload.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/netfilter/nf_tables_offload.c b/net/netfilter/nf_tables_offload.c index 68f17a6921d82..d7a35da008ef7 100644 --- a/net/netfilter/nf_tables_offload.c +++ b/net/netfilter/nf_tables_offload.c @@ -577,6 +577,9 @@ static int nft_offload_netdev_event(struct notifier_block *this, struct net *net = dev_net(dev); struct nft_chain *chain; + if (event != NETDEV_UNREGISTER) + return NOTIFY_DONE; + mutex_lock(&net->nft.commit_mutex); chain = __nft_offload_get_chain(dev); if (chain) -- GitLab From cfee8793a74dc3afabb08fc9ed5bbe2045709dbb Mon Sep 17 00:00:00 2001 From: Wen Gong Date: Fri, 29 Nov 2019 09:59:24 +0200 Subject: [PATCH 0112/1437] ath10k: enable napi on RX path for sdio For tcp RX, the quantity of tcp acks to remote is 1/2 of the quantity of tcp data from remote, then it will have many small length packets on TX path of sdio bus, then it reduce the RX packets's bandwidth of tcp. This patch enable napi on RX path, then the RX packet of tcp will not feed to tcp stack immeditely from mac80211 since GRO is enabled by default, it will feed to tcp stack after napi complete, if rx bundle is enabled, then it will feed to tcp stack one time for each bundle of RX. For example, RX bundle size is 32, then tcp stack will receive one large length packet, its length is neary 1500*32, then tcp stack will send a tcp ack for this large packet, this will reduce the tcp acks ratio from 1/2 to 1/32. This results in significant performance improvement for tcp RX. Tcp rx throughout is 240Mbps without this patch, and it arrive 390Mbps with this patch. The cpu usage has no obvious difference with and without NAPI. call stack for each RX packet on GRO path: (skb length is about 1500 bytes) skb_gro_receive ([kernel.kallsyms]) tcp4_gro_receive ([kernel.kallsyms]) inet_gro_receive ([kernel.kallsyms]) dev_gro_receive ([kernel.kallsyms]) napi_gro_receive ([kernel.kallsyms]) ieee80211_deliver_skb ([mac80211]) ieee80211_rx_handlers ([mac80211]) ieee80211_prepare_and_rx_handle ([mac80211]) ieee80211_rx_napi ([mac80211]) ath10k_htt_rx_proc_rx_ind_hl ([ath10k_core]) ath10k_htt_rx_pktlog_completion_handler ([ath10k_core]) ath10k_sdio_napi_poll ([ath10k_sdio]) net_rx_action ([kernel.kallsyms]) softirqentry_text_start ([kernel.kallsyms]) do_softirq ([kernel.kallsyms]) call stack for napi complete and send tcp ack from tcp stack: (skb length is about 1500*32 bytes) _tcp_ack_snd_check ([kernel.kallsyms]) tcp_v4_do_rcv ([kernel.kallsyms]) tcp_v4_rcv ([kernel.kallsyms]) local_deliver_finish ([kernel.kallsyms]) ip_local_deliver ([kernel.kallsyms]) ip_rcv_finish ([kernel.kallsyms]) ip_rcv ([kernel.kallsyms]) netif_receive_skb_core ([kernel.kallsyms]) netif_receive_skb_one_core([kernel.kallsyms]) netif_receive_skb ([kernel.kallsyms]) netif_receive_skb_internal ([kernel.kallsyms]) napi_gro_complete ([kernel.kallsyms]) napi_gro_flush ([kernel.kallsyms]) napi_complete_done ([kernel.kallsyms]) ath10k_sdio_napi_poll ([ath10k_sdio]) net_rx_action ([kernel.kallsyms]) __softirqentry_text_start ([kernel.kallsyms]) do_softirq ([kernel.kallsyms]) Tested with QCA6174 SDIO with firmware WLAN.RMH.4.4.1-00017-QCARMSWP-1. Signed-off-by: Wen Gong Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/core.c | 2 + drivers/net/wireless/ath/ath10k/htt.h | 3 ++ drivers/net/wireless/ath/ath10k/htt_rx.c | 48 ++++++++++++++++++++---- drivers/net/wireless/ath/ath10k/sdio.c | 28 ++++++++++++++ 4 files changed, 73 insertions(+), 8 deletions(-) diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c index 6da377317d1fa..5ec16ce19b695 100644 --- a/drivers/net/wireless/ath/ath10k/core.c +++ b/drivers/net/wireless/ath/ath10k/core.c @@ -3220,6 +3220,8 @@ struct ath10k *ath10k_core_create(size_t priv_size, struct device *dev, init_waitqueue_head(&ar->htt.empty_tx_wq); init_waitqueue_head(&ar->wmi.tx_credits_wq); + skb_queue_head_init(&ar->htt.rx_indication_head); + init_completion(&ar->offchan_tx_completed); INIT_WORK(&ar->offchan_tx_work, ath10k_offchan_tx_work); skb_queue_head_init(&ar->offchan_tx_queue); diff --git a/drivers/net/wireless/ath/ath10k/htt.h b/drivers/net/wireless/ath/ath10k/htt.h index 30c080094af14..4a12564fc30e2 100644 --- a/drivers/net/wireless/ath/ath10k/htt.h +++ b/drivers/net/wireless/ath/ath10k/htt.h @@ -1869,6 +1869,8 @@ struct ath10k_htt { struct ath10k *ar; enum ath10k_htc_ep_id eid; + struct sk_buff_head rx_indication_head; + u8 target_version_major; u8 target_version_minor; struct completion target_version_received; @@ -2283,6 +2285,7 @@ int ath10k_htt_mgmt_tx(struct ath10k_htt *htt, struct sk_buff *msdu); void ath10k_htt_rx_pktlog_completion_handler(struct ath10k *ar, struct sk_buff *skb); int ath10k_htt_txrx_compl_task(struct ath10k *ar, int budget); +int ath10k_htt_rx_hl_indication(struct ath10k *ar, int budget); void ath10k_htt_set_tx_ops(struct ath10k_htt *htt); void ath10k_htt_set_rx_ops(struct ath10k_htt *htt); #endif diff --git a/drivers/net/wireless/ath/ath10k/htt_rx.c b/drivers/net/wireless/ath/ath10k/htt_rx.c index bbcfa728917d1..7fb15180c6859 100644 --- a/drivers/net/wireless/ath/ath10k/htt_rx.c +++ b/drivers/net/wireless/ath/ath10k/htt_rx.c @@ -2359,7 +2359,10 @@ static bool ath10k_htt_rx_proc_rx_ind_hl(struct ath10k_htt *htt, memcpy(skb->data + offset, &qos_ctrl, IEEE80211_QOS_CTL_LEN); } - ieee80211_rx_ni(ar->hw, skb); + if (ar->napi.dev) + ieee80211_rx_napi(ar->hw, NULL, skb, &ar->napi); + else + ieee80211_rx_ni(ar->hw, skb); /* We have delivered the skb to the upper layers (mac80211) so we * must not free it. @@ -3760,14 +3763,12 @@ bool ath10k_htt_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb) break; } case HTT_T2H_MSG_TYPE_RX_IND: - if (ar->bus_param.dev_type == ATH10K_DEV_TYPE_HL) - return ath10k_htt_rx_proc_rx_ind_hl(htt, - &resp->rx_ind_hl, - skb, - HTT_RX_PN_CHECK, - HTT_RX_NON_TKIP_MIC); - else + if (ar->bus_param.dev_type != ATH10K_DEV_TYPE_HL) { ath10k_htt_rx_proc_rx_ind_ll(htt, &resp->rx_ind); + } else { + skb_queue_tail(&htt->rx_indication_head, skb); + return false; + } break; case HTT_T2H_MSG_TYPE_PEER_MAP: { struct htt_peer_map_event ev = { @@ -3957,6 +3958,37 @@ static int ath10k_htt_rx_deliver_msdu(struct ath10k *ar, int quota, int budget) return quota; } +int ath10k_htt_rx_hl_indication(struct ath10k *ar, int budget) +{ + struct htt_resp *resp; + struct ath10k_htt *htt = &ar->htt; + struct sk_buff *skb; + bool release; + int quota; + + for (quota = 0; quota < budget; quota++) { + skb = skb_dequeue(&htt->rx_indication_head); + if (!skb) + break; + + resp = (struct htt_resp *)skb->data; + + release = ath10k_htt_rx_proc_rx_ind_hl(htt, + &resp->rx_ind_hl, + skb, + HTT_RX_PN_CHECK, + HTT_RX_NON_TKIP_MIC); + + if (release) + dev_kfree_skb_any(skb); + + ath10k_dbg(ar, ATH10K_DBG_HTT, "rx indication poll pending count:%d\n", + skb_queue_len(&htt->rx_indication_head)); + } + return quota; +} +EXPORT_SYMBOL(ath10k_htt_rx_hl_indication); + int ath10k_htt_txrx_compl_task(struct ath10k *ar, int budget) { struct ath10k_htt *htt = &ar->htt; diff --git a/drivers/net/wireless/ath/ath10k/sdio.c b/drivers/net/wireless/ath/ath10k/sdio.c index b8e2499096991..e36deda23644d 100644 --- a/drivers/net/wireless/ath/ath10k/sdio.c +++ b/drivers/net/wireless/ath/ath10k/sdio.c @@ -1339,6 +1339,9 @@ static void ath10k_rx_indication_async_work(struct work_struct *work) ep = &ar->htc.endpoint[cb->eid]; ep->ep_ops.ep_rx_complete(ar, skb); } + + if (test_bit(ATH10K_FLAG_CORE_REGISTERED, &ar->dev_flags)) + napi_schedule(&ar->napi); } static void ath10k_sdio_write_async_work(struct work_struct *work) @@ -1729,6 +1732,8 @@ static int ath10k_sdio_hif_start(struct ath10k *ar) struct ath10k_sdio *ar_sdio = ath10k_sdio_priv(ar); int ret; + napi_enable(&ar->napi); + /* Sleep 20 ms before HIF interrupts are disabled. * This will give target plenty of time to process the BMI done * request before interrupts are disabled. @@ -1853,6 +1858,9 @@ static void ath10k_sdio_hif_stop(struct ath10k *ar) } spin_unlock_bh(&ar_sdio->wr_async_lock); + + napi_synchronize(&ar->napi); + napi_disable(&ar->napi); } #ifdef CONFIG_PM @@ -2047,6 +2055,20 @@ static SIMPLE_DEV_PM_OPS(ath10k_sdio_pm_ops, ath10k_sdio_pm_suspend, #endif /* CONFIG_PM_SLEEP */ +static int ath10k_sdio_napi_poll(struct napi_struct *ctx, int budget) +{ + struct ath10k *ar = container_of(ctx, struct ath10k, napi); + int done; + + done = ath10k_htt_rx_hl_indication(ar, budget); + ath10k_dbg(ar, ATH10K_DBG_SDIO, "napi poll: done: %d, budget:%d\n", done, budget); + + if (done < budget) + napi_complete_done(ctx, done); + + return done; +} + static int ath10k_sdio_probe(struct sdio_func *func, const struct sdio_device_id *id) { @@ -2072,6 +2094,9 @@ static int ath10k_sdio_probe(struct sdio_func *func, return -ENOMEM; } + netif_napi_add(&ar->napi_dev, &ar->napi, ath10k_sdio_napi_poll, + ATH10K_NAPI_BUDGET); + ath10k_dbg(ar, ATH10K_DBG_BOOT, "sdio new func %d vendor 0x%x device 0x%x block 0x%x/0x%x\n", func->num, func->vendor, func->device, @@ -2184,6 +2209,9 @@ static void ath10k_sdio_remove(struct sdio_func *func) func->num, func->vendor, func->device); ath10k_core_unregister(ar); + + netif_napi_del(&ar->napi); + ath10k_core_destroy(ar); flush_workqueue(ar_sdio->workqueue); -- GitLab From 4a99124516307e0fc4ed7517c0af796091a159d5 Mon Sep 17 00:00:00 2001 From: Wen Gong Date: Fri, 29 Nov 2019 09:59:59 +0200 Subject: [PATCH 0113/1437] ath10k: change bundle count for max rx bundle for sdio For max bundle size 32, the bundle mask is not same with 8/16. Change it to match the max bundle size of htc. Otherwise it will not match with firmware, for example, when bundle count is 17, then flags of ath10k_htc_hdr is 0x4, if without this patch, it will be considered as non-bundled packet because it does not have mask 0xF0, then trigger error message later: payload length 56747 exceeds max htc length: 4088. htc->max_msgs_per_htc_bundle is the min value of HTC_HOST_MAX_MSG_PER_RX_BUNDLE and msg->ready_ext.max_msgs_per_htc_bundle of ath10k_htc_wait_target, it will be sent to firmware later in ath10k_htc_start, then firmware will use it as the final max rx bundle count, in WLAN.RMH.4.4.1-00029, msg->ready_ext.max_msgs_per_htc_bundle is 32, it is same with HTC_HOST_MAX_MSG_PER_RX_BUNDLE, so the final max rx bundle count will be set to 32 in firmware. This patch only effect sdio chips. Tested with QCA6174 SDIO with firmware WLAN.RMH.4.4.1-00029. Signed-off-by: Wen Gong Fixes: 224776520ead69e ("ath10k: change max RX bundle size from 8 to 32 for sdio") Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/htc.c | 2 +- drivers/net/wireless/ath/ath10k/htc.h | 10 ++++++---- drivers/net/wireless/ath/ath10k/sdio.c | 10 ++++++---- 3 files changed, 13 insertions(+), 9 deletions(-) diff --git a/drivers/net/wireless/ath/ath10k/htc.c b/drivers/net/wireless/ath/ath10k/htc.c index 0bbd1415d85c5..2248d6c022f48 100644 --- a/drivers/net/wireless/ath/ath10k/htc.c +++ b/drivers/net/wireless/ath/ath10k/htc.c @@ -270,7 +270,7 @@ ath10k_htc_process_lookahead_bundle(struct ath10k_htc *htc, struct ath10k *ar = htc->ar; int bundle_cnt = len / sizeof(*report); - if (!bundle_cnt || (bundle_cnt > HTC_HOST_MAX_MSG_PER_RX_BUNDLE)) { + if (!bundle_cnt || (bundle_cnt > htc->max_msgs_per_htc_bundle)) { ath10k_warn(ar, "Invalid lookahead bundle count: %d\n", bundle_cnt); return -EINVAL; diff --git a/drivers/net/wireless/ath/ath10k/htc.h b/drivers/net/wireless/ath/ath10k/htc.h index 16102175e07a0..065c82d9d689f 100644 --- a/drivers/net/wireless/ath/ath10k/htc.h +++ b/drivers/net/wireless/ath/ath10k/htc.h @@ -58,13 +58,15 @@ enum ath10k_htc_rx_flags { #define ATH10K_HTC_BUNDLE_EXTRA_MASK GENMASK(3, 2) #define ATH10K_HTC_BUNDLE_EXTRA_SHIFT 4 -static inline unsigned int ath10k_htc_get_bundle_count(u8 flags) +static inline unsigned int ath10k_htc_get_bundle_count(u8 max_msgs, u8 flags) { - unsigned int count, extra_count; + unsigned int count, extra_count = 0; count = FIELD_GET(ATH10K_HTC_FLAG_BUNDLE_MASK, flags); - extra_count = FIELD_GET(ATH10K_HTC_BUNDLE_EXTRA_MASK, flags) << - ATH10K_HTC_BUNDLE_EXTRA_SHIFT; + + if (max_msgs > 16) + extra_count = FIELD_GET(ATH10K_HTC_BUNDLE_EXTRA_MASK, flags) << + ATH10K_HTC_BUNDLE_EXTRA_SHIFT; return count + extra_count; } diff --git a/drivers/net/wireless/ath/ath10k/sdio.c b/drivers/net/wireless/ath/ath10k/sdio.c index e36deda23644d..6fdf71b8b6763 100644 --- a/drivers/net/wireless/ath/ath10k/sdio.c +++ b/drivers/net/wireless/ath/ath10k/sdio.c @@ -500,14 +500,15 @@ static int ath10k_sdio_mbox_alloc_bundle(struct ath10k *ar, size_t *bndl_cnt) { int ret, i; + u8 max_msgs = ar->htc.max_msgs_per_htc_bundle; - *bndl_cnt = ath10k_htc_get_bundle_count(htc_hdr->flags); + *bndl_cnt = ath10k_htc_get_bundle_count(max_msgs, htc_hdr->flags); - if (*bndl_cnt > HTC_HOST_MAX_MSG_PER_RX_BUNDLE) { + if (*bndl_cnt > max_msgs) { ath10k_warn(ar, "HTC bundle length %u exceeds maximum %u\n", le16_to_cpu(htc_hdr->len), - HTC_HOST_MAX_MSG_PER_RX_BUNDLE); + max_msgs); return -ENOMEM; } @@ -570,7 +571,8 @@ static int ath10k_sdio_mbox_rx_alloc(struct ath10k *ar, goto err; } - if (htc_hdr->flags & ATH10K_HTC_FLAG_BUNDLE_MASK) { + if (ath10k_htc_get_bundle_count( + ar->htc.max_msgs_per_htc_bundle, htc_hdr->flags)) { /* HTC header indicates that every packet to follow * has the same padded length so that it can be * optimally fetched as a full bundle. -- GitLab From 2dc016599cfa9672a147528ca26d70c3654a5423 Mon Sep 17 00:00:00 2001 From: Wen Gong Date: Fri, 29 Nov 2019 07:34:09 +0000 Subject: [PATCH 0114/1437] ath: add support for special 0x0 regulatory domain Some sdio chips of rome QCA6174's regulatory domain code of EEPROM is empty, then ath_is_world_regd will return false for this case, and it will lead function __ath_reg_dyn_country not work, thus the regdomain will not update for NL80211_REGDOM_SET_BY_COUNTRY_IE type, it result ath10k set the same regdomain/reg_5ghz_ctl/reg_2ghz_ctl to firmware, then the tx power will not changed with different regdomain's AP. The regulatory domain code of EEPROM of some QCA6174 PCIE chip is 0x6c, it means world wide regdomain, for this chip, it does not have the issue. For empty reulatory domain code chip, set it to world regulatory domain in functio ath_regd_sanitize, then it will fix the issue. Tested with QCA6174 SDIO with firmware WLAN.RMH.4.4.1-00029. Signed-off-by: Wen Gong Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/regd.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/ath/regd.c b/drivers/net/wireless/ath/regd.c index 20f4f8ea9f894..bee9110b91f38 100644 --- a/drivers/net/wireless/ath/regd.c +++ b/drivers/net/wireless/ath/regd.c @@ -666,14 +666,14 @@ ath_regd_init_wiphy(struct ath_regulatory *reg, /* * Some users have reported their EEPROM programmed with - * 0x8000 set, this is not a supported regulatory domain - * but since we have more than one user with it we need - * a solution for them. We default to 0x64, which is the - * default Atheros world regulatory domain. + * 0x8000 or 0x0 set, this is not a supported regulatory + * domain but since we have more than one user with it we + * need a solution for them. We default to 0x64, which is + * the default Atheros world regulatory domain. */ static void ath_regd_sanitize(struct ath_regulatory *reg) { - if (reg->current_rd != COUNTRY_ERD_FLAG) + if (reg->current_rd != COUNTRY_ERD_FLAG && reg->current_rd != 0) return; printk(KERN_DEBUG "ath: EEPROM regdomain sanitized\n"); reg->current_rd = 0x64; -- GitLab From bd5c6b81dd6025bd4c6ca7800a580b217d9899b9 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Mon, 2 Dec 2019 11:40:57 -0300 Subject: [PATCH 0115/1437] perf bench: Update the copies of x86's mem{cpy,set}_64.S And update linux/linkage.h, which requires in turn that we make these files switch from ENTRY()/ENDPROC() to SYM_FUNC_START()/SYM_FUNC_END(): tools/perf/arch/arm64/tests/regs_load.S tools/perf/arch/arm/tests/regs_load.S tools/perf/arch/powerpc/tests/regs_load.S tools/perf/arch/x86/tests/regs_load.S We also need to switch SYM_FUNC_START_LOCAL() to SYM_FUNC_START() for the functions used directly by 'perf bench', and update tools/perf/check_headers.sh to ignore those changes when checking if the kernel original files drifted from the copies we carry. This is to get the changes from: 6dcc5627f6ae ("x86/asm: Change all ENTRY+ENDPROC to SYM_FUNC_*") ef1e03152cb0 ("x86/asm: Make some functions local") e9b9d020c487 ("x86/asm: Annotate aliases") And address these tools/perf build warnings: Warning: Kernel ABI header at 'tools/arch/x86/lib/memcpy_64.S' differs from latest version at 'arch/x86/lib/memcpy_64.S' diff -u tools/arch/x86/lib/memcpy_64.S arch/x86/lib/memcpy_64.S Warning: Kernel ABI header at 'tools/arch/x86/lib/memset_64.S' differs from latest version at 'arch/x86/lib/memset_64.S' diff -u tools/arch/x86/lib/memset_64.S arch/x86/lib/memset_64.S Cc: Adrian Hunter Cc: Borislav Petkov Cc: Jiri Olsa Cc: Jiri Slaby Cc: Namhyung Kim Link: https://lkml.kernel.org/n/tip-tay3l8x8k11p7y3qcpqh9qh5@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/arch/x86/lib/memcpy_64.S | 20 +++--- tools/arch/x86/lib/memset_64.S | 16 ++--- tools/perf/arch/arm/tests/regs_load.S | 4 +- tools/perf/arch/arm64/tests/regs_load.S | 4 +- tools/perf/arch/x86/tests/regs_load.S | 8 +-- tools/perf/check-headers.sh | 4 +- tools/perf/util/include/linux/linkage.h | 89 ++++++++++++++++++++++++- 7 files changed, 114 insertions(+), 31 deletions(-) diff --git a/tools/arch/x86/lib/memcpy_64.S b/tools/arch/x86/lib/memcpy_64.S index 92748660ba512..df767afc690fe 100644 --- a/tools/arch/x86/lib/memcpy_64.S +++ b/tools/arch/x86/lib/memcpy_64.S @@ -28,8 +28,8 @@ * Output: * rax original destination */ -ENTRY(__memcpy) -ENTRY(memcpy) +SYM_FUNC_START_ALIAS(__memcpy) +SYM_FUNC_START_LOCAL(memcpy) ALTERNATIVE_2 "jmp memcpy_orig", "", X86_FEATURE_REP_GOOD, \ "jmp memcpy_erms", X86_FEATURE_ERMS @@ -41,8 +41,8 @@ ENTRY(memcpy) movl %edx, %ecx rep movsb ret -ENDPROC(memcpy) -ENDPROC(__memcpy) +SYM_FUNC_END(memcpy) +SYM_FUNC_END_ALIAS(__memcpy) EXPORT_SYMBOL(memcpy) EXPORT_SYMBOL(__memcpy) @@ -50,14 +50,14 @@ EXPORT_SYMBOL(__memcpy) * memcpy_erms() - enhanced fast string memcpy. This is faster and * simpler than memcpy. Use memcpy_erms when possible. */ -ENTRY(memcpy_erms) +SYM_FUNC_START(memcpy_erms) movq %rdi, %rax movq %rdx, %rcx rep movsb ret -ENDPROC(memcpy_erms) +SYM_FUNC_END(memcpy_erms) -ENTRY(memcpy_orig) +SYM_FUNC_START(memcpy_orig) movq %rdi, %rax cmpq $0x20, %rdx @@ -182,7 +182,7 @@ ENTRY(memcpy_orig) .Lend: retq -ENDPROC(memcpy_orig) +SYM_FUNC_END(memcpy_orig) #ifndef CONFIG_UML @@ -193,7 +193,7 @@ MCSAFE_TEST_CTL * Note that we only catch machine checks when reading the source addresses. * Writes to target are posted and don't generate machine checks. */ -ENTRY(__memcpy_mcsafe) +SYM_FUNC_START(__memcpy_mcsafe) cmpl $8, %edx /* Less than 8 bytes? Go to byte copy loop */ jb .L_no_whole_words @@ -260,7 +260,7 @@ ENTRY(__memcpy_mcsafe) xorl %eax, %eax .L_done: ret -ENDPROC(__memcpy_mcsafe) +SYM_FUNC_END(__memcpy_mcsafe) EXPORT_SYMBOL_GPL(__memcpy_mcsafe) .section .fixup, "ax" diff --git a/tools/arch/x86/lib/memset_64.S b/tools/arch/x86/lib/memset_64.S index f8f3dc0a6690e..fd5d25a474b7c 100644 --- a/tools/arch/x86/lib/memset_64.S +++ b/tools/arch/x86/lib/memset_64.S @@ -18,8 +18,8 @@ * * rax original destination */ -ENTRY(memset) -ENTRY(__memset) +SYM_FUNC_START_ALIAS(memset) +SYM_FUNC_START(__memset) /* * Some CPUs support enhanced REP MOVSB/STOSB feature. It is recommended * to use it when possible. If not available, use fast string instructions. @@ -42,8 +42,8 @@ ENTRY(__memset) rep stosb movq %r9,%rax ret -ENDPROC(memset) -ENDPROC(__memset) +SYM_FUNC_END(__memset) +SYM_FUNC_END_ALIAS(memset) /* * ISO C memset - set a memory block to a byte value. This function uses @@ -56,16 +56,16 @@ ENDPROC(__memset) * * rax original destination */ -ENTRY(memset_erms) +SYM_FUNC_START(memset_erms) movq %rdi,%r9 movb %sil,%al movq %rdx,%rcx rep stosb movq %r9,%rax ret -ENDPROC(memset_erms) +SYM_FUNC_END(memset_erms) -ENTRY(memset_orig) +SYM_FUNC_START(memset_orig) movq %rdi,%r10 /* expand byte value */ @@ -136,4 +136,4 @@ ENTRY(memset_orig) subq %r8,%rdx jmp .Lafter_bad_alignment .Lfinal: -ENDPROC(memset_orig) +SYM_FUNC_END(memset_orig) diff --git a/tools/perf/arch/arm/tests/regs_load.S b/tools/perf/arch/arm/tests/regs_load.S index 6e2495cc45171..4284307d78226 100644 --- a/tools/perf/arch/arm/tests/regs_load.S +++ b/tools/perf/arch/arm/tests/regs_load.S @@ -37,7 +37,7 @@ .text .type perf_regs_load,%function -ENTRY(perf_regs_load) +SYM_FUNC_START(perf_regs_load) str r0, [r0, #R0] str r1, [r0, #R1] str r2, [r0, #R2] @@ -56,4 +56,4 @@ ENTRY(perf_regs_load) str lr, [r0, #PC] // store pc as lr in order to skip the call // to this function mov pc, lr -ENDPROC(perf_regs_load) +SYM_FUNC_END(perf_regs_load) diff --git a/tools/perf/arch/arm64/tests/regs_load.S b/tools/perf/arch/arm64/tests/regs_load.S index 07042511dca92..d49de40b68180 100644 --- a/tools/perf/arch/arm64/tests/regs_load.S +++ b/tools/perf/arch/arm64/tests/regs_load.S @@ -7,7 +7,7 @@ #define LDR_REG(r) ldr x##r, [x0, 8 * r] #define SP (8 * 31) #define PC (8 * 32) -ENTRY(perf_regs_load) +SYM_FUNC_START(perf_regs_load) STR_REG(0) STR_REG(1) STR_REG(2) @@ -44,4 +44,4 @@ ENTRY(perf_regs_load) str x30, [x0, #PC] LDR_REG(1) ret -ENDPROC(perf_regs_load) +SYM_FUNC_END(perf_regs_load) diff --git a/tools/perf/arch/x86/tests/regs_load.S b/tools/perf/arch/x86/tests/regs_load.S index bbe5a0d16e510..80f14f52e3f60 100644 --- a/tools/perf/arch/x86/tests/regs_load.S +++ b/tools/perf/arch/x86/tests/regs_load.S @@ -28,7 +28,7 @@ .text #ifdef HAVE_ARCH_X86_64_SUPPORT -ENTRY(perf_regs_load) +SYM_FUNC_START(perf_regs_load) movq %rax, AX(%rdi) movq %rbx, BX(%rdi) movq %rcx, CX(%rdi) @@ -60,9 +60,9 @@ ENTRY(perf_regs_load) movq %r14, R14(%rdi) movq %r15, R15(%rdi) ret -ENDPROC(perf_regs_load) +SYM_FUNC_END(perf_regs_load) #else -ENTRY(perf_regs_load) +SYM_FUNC_START(perf_regs_load) push %edi movl 8(%esp), %edi movl %eax, AX(%edi) @@ -88,7 +88,7 @@ ENTRY(perf_regs_load) movl $0, FS(%edi) movl $0, GS(%edi) ret -ENDPROC(perf_regs_load) +SYM_FUNC_END(perf_regs_load) #endif /* diff --git a/tools/perf/check-headers.sh b/tools/perf/check-headers.sh index a1dc167243520..68039a96c1dca 100755 --- a/tools/perf/check-headers.sh +++ b/tools/perf/check-headers.sh @@ -110,8 +110,8 @@ for i in $FILES; do done # diff with extra ignore lines -check arch/x86/lib/memcpy_64.S '-I "^EXPORT_SYMBOL" -I "^#include "' -check arch/x86/lib/memset_64.S '-I "^EXPORT_SYMBOL" -I "^#include "' +check arch/x86/lib/memcpy_64.S '-I "^EXPORT_SYMBOL" -I "^#include " -I"^SYM_FUNC_START\(_LOCAL\)*(memcpy_\(erms\|orig\))"' +check arch/x86/lib/memset_64.S '-I "^EXPORT_SYMBOL" -I "^#include " -I"^SYM_FUNC_START\(_LOCAL\)*(memset_\(erms\|orig\))"' check include/uapi/asm-generic/mman.h '-I "^#include <\(uapi/\)*asm-generic/mman-common\(-tools\)*.h>"' check include/uapi/linux/mman.h '-I "^#include <\(uapi/\)*asm/mman.h>"' check include/linux/ctype.h '-I "isdigit("' diff --git a/tools/perf/util/include/linux/linkage.h b/tools/perf/util/include/linux/linkage.h index f01d48a8d7079..b8a5159361b41 100644 --- a/tools/perf/util/include/linux/linkage.h +++ b/tools/perf/util/include/linux/linkage.h @@ -5,10 +5,93 @@ /* linkage.h ... for including arch/x86/lib/memcpy_64.S */ -#define ENTRY(name) \ - .globl name; \ +/* Some toolchains use other characters (e.g. '`') to mark new line in macro */ +#ifndef ASM_NL +#define ASM_NL ; +#endif + +#ifndef __ALIGN +#define __ALIGN .align 4,0x90 +#define __ALIGN_STR ".align 4,0x90" +#endif + +/* SYM_T_FUNC -- type used by assembler to mark functions */ +#ifndef SYM_T_FUNC +#define SYM_T_FUNC STT_FUNC +#endif + +/* SYM_A_* -- align the symbol? */ +#define SYM_A_ALIGN ALIGN + +/* SYM_L_* -- linkage of symbols */ +#define SYM_L_GLOBAL(name) .globl name +#define SYM_L_LOCAL(name) /* nothing */ + +#define ALIGN __ALIGN + +/* === generic annotations === */ + +/* SYM_ENTRY -- use only if you have to for non-paired symbols */ +#ifndef SYM_ENTRY +#define SYM_ENTRY(name, linkage, align...) \ + linkage(name) ASM_NL \ + align ASM_NL \ name: +#endif + +/* SYM_START -- use only if you have to */ +#ifndef SYM_START +#define SYM_START(name, linkage, align...) \ + SYM_ENTRY(name, linkage, align) +#endif + +/* SYM_END -- use only if you have to */ +#ifndef SYM_END +#define SYM_END(name, sym_type) \ + .type name sym_type ASM_NL \ + .size name, .-name +#endif + +/* + * SYM_FUNC_START_ALIAS -- use where there are two global names for one + * function + */ +#ifndef SYM_FUNC_START_ALIAS +#define SYM_FUNC_START_ALIAS(name) \ + SYM_START(name, SYM_L_GLOBAL, SYM_A_ALIGN) +#endif + +/* SYM_FUNC_START -- use for global functions */ +#ifndef SYM_FUNC_START +/* + * The same as SYM_FUNC_START_ALIAS, but we will need to distinguish these two + * later. + */ +#define SYM_FUNC_START(name) \ + SYM_START(name, SYM_L_GLOBAL, SYM_A_ALIGN) +#endif + +/* SYM_FUNC_START_LOCAL -- use for local functions */ +#ifndef SYM_FUNC_START_LOCAL +/* the same as SYM_FUNC_START_LOCAL_ALIAS, see comment near SYM_FUNC_START */ +#define SYM_FUNC_START_LOCAL(name) \ + SYM_START(name, SYM_L_LOCAL, SYM_A_ALIGN) +#endif + +/* SYM_FUNC_END_ALIAS -- the end of LOCAL_ALIASed or ALIASed function */ +#ifndef SYM_FUNC_END_ALIAS +#define SYM_FUNC_END_ALIAS(name) \ + SYM_END(name, SYM_T_FUNC) +#endif -#define ENDPROC(name) +/* + * SYM_FUNC_END -- the end of SYM_FUNC_START_LOCAL, SYM_FUNC_START, + * SYM_FUNC_START_WEAK, ... + */ +#ifndef SYM_FUNC_END +/* the same as SYM_FUNC_END_ALIAS, see comment near SYM_FUNC_START */ +#define SYM_FUNC_END(name) \ + SYM_END(name, SYM_T_FUNC) +#endif #endif /* PERF_LINUX_LINKAGE_H_ */ -- GitLab From 693d060536abca7b16d2fb8e66aa56be2a2443a2 Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Sun, 24 Nov 2019 00:42:22 +0200 Subject: [PATCH 0116/1437] mt76: mt76x0: fix default mac address overwrite Current implementation always use default eeprom mac address to configure device registers even if it is updated using mt76_eeprom_override. Fix it initializing macaddr filed of mt76_dev data structure with eeprom mac address and running mt76_eeprom_override before mt76x02_mac_setaddr Fixes: d1bc9bf2072c ("mt76: mt76x0: eeprom: add support for MAC address from OF") Tested-by: Kevin Schmidt Signed-off-by: Lorenzo Bianconi Signed-off-by: Kalle Valo --- drivers/net/wireless/mediatek/mt76/mt76x0/eeprom.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt76x0/eeprom.c b/drivers/net/wireless/mediatek/mt76/mt76x0/eeprom.c index a03e2d01fba7c..d1405528b5042 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x0/eeprom.c +++ b/drivers/net/wireless/mediatek/mt76/mt76x0/eeprom.c @@ -342,8 +342,11 @@ int mt76x0_eeprom_init(struct mt76x02_dev *dev) dev_info(dev->mt76.dev, "EEPROM ver:%02hhx fae:%02hhx\n", version, fae); - mt76x02_mac_setaddr(dev, dev->mt76.eeprom.data + MT_EE_MAC_ADDR); + memcpy(dev->mt76.macaddr, (u8 *)dev->mt76.eeprom.data + MT_EE_MAC_ADDR, + ETH_ALEN); mt76_eeprom_override(&dev->mt76); + mt76x02_mac_setaddr(dev, dev->mt76.macaddr); + mt76x0_set_chip_cap(dev); mt76x0_set_freq_offset(dev); mt76x0_set_temp_offset(dev); -- GitLab From 1e58252e334dc3f3756f424a157d1b7484464c40 Mon Sep 17 00:00:00 2001 From: qize wang Date: Fri, 29 Nov 2019 18:10:54 +0800 Subject: [PATCH 0117/1437] mwifiex: Fix heap overflow in mmwifiex_process_tdls_action_frame() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit mwifiex_process_tdls_action_frame() without checking the incoming tdls infomation element's vality before use it, this may cause multi heap buffer overflows. Fix them by putting vality check before use it. IE is TLV struct, but ht_cap and ht_oper aren’t TLV struct. the origin marvell driver code is wrong: memcpy(&sta_ptr->tdls_cap.ht_oper, pos,.... memcpy((u8 *)&sta_ptr->tdls_cap.ht_capb, pos,... Fix the bug by changing pos(the address of IE) to pos+2 ( the address of IE value ). Signed-off-by: qize wang Signed-off-by: Kalle Valo --- drivers/net/wireless/marvell/mwifiex/tdls.c | 70 +++++++++++++++++++-- 1 file changed, 64 insertions(+), 6 deletions(-) diff --git a/drivers/net/wireless/marvell/mwifiex/tdls.c b/drivers/net/wireless/marvell/mwifiex/tdls.c index 09313047beedd..7caf1d26124a2 100644 --- a/drivers/net/wireless/marvell/mwifiex/tdls.c +++ b/drivers/net/wireless/marvell/mwifiex/tdls.c @@ -953,59 +953,117 @@ void mwifiex_process_tdls_action_frame(struct mwifiex_private *priv, switch (*pos) { case WLAN_EID_SUPP_RATES: + if (pos[1] > 32) + return; sta_ptr->tdls_cap.rates_len = pos[1]; for (i = 0; i < pos[1]; i++) sta_ptr->tdls_cap.rates[i] = pos[i + 2]; break; case WLAN_EID_EXT_SUPP_RATES: + if (pos[1] > 32) + return; basic = sta_ptr->tdls_cap.rates_len; + if (pos[1] > 32 - basic) + return; for (i = 0; i < pos[1]; i++) sta_ptr->tdls_cap.rates[basic + i] = pos[i + 2]; sta_ptr->tdls_cap.rates_len += pos[1]; break; case WLAN_EID_HT_CAPABILITY: - memcpy((u8 *)&sta_ptr->tdls_cap.ht_capb, pos, + if (pos > end - sizeof(struct ieee80211_ht_cap) - 2) + return; + if (pos[1] != sizeof(struct ieee80211_ht_cap)) + return; + /* copy the ie's value into ht_capb*/ + memcpy((u8 *)&sta_ptr->tdls_cap.ht_capb, pos + 2, sizeof(struct ieee80211_ht_cap)); sta_ptr->is_11n_enabled = 1; break; case WLAN_EID_HT_OPERATION: - memcpy(&sta_ptr->tdls_cap.ht_oper, pos, + if (pos > end - + sizeof(struct ieee80211_ht_operation) - 2) + return; + if (pos[1] != sizeof(struct ieee80211_ht_operation)) + return; + /* copy the ie's value into ht_oper*/ + memcpy(&sta_ptr->tdls_cap.ht_oper, pos + 2, sizeof(struct ieee80211_ht_operation)); break; case WLAN_EID_BSS_COEX_2040: + if (pos > end - 3) + return; + if (pos[1] != 1) + return; sta_ptr->tdls_cap.coex_2040 = pos[2]; break; case WLAN_EID_EXT_CAPABILITY: + if (pos > end - sizeof(struct ieee_types_header)) + return; + if (pos[1] < sizeof(struct ieee_types_header)) + return; + if (pos[1] > 8) + return; memcpy((u8 *)&sta_ptr->tdls_cap.extcap, pos, sizeof(struct ieee_types_header) + min_t(u8, pos[1], 8)); break; case WLAN_EID_RSN: + if (pos > end - sizeof(struct ieee_types_header)) + return; + if (pos[1] < sizeof(struct ieee_types_header)) + return; + if (pos[1] > IEEE_MAX_IE_SIZE - + sizeof(struct ieee_types_header)) + return; memcpy((u8 *)&sta_ptr->tdls_cap.rsn_ie, pos, sizeof(struct ieee_types_header) + min_t(u8, pos[1], IEEE_MAX_IE_SIZE - sizeof(struct ieee_types_header))); break; case WLAN_EID_QOS_CAPA: + if (pos > end - 3) + return; + if (pos[1] != 1) + return; sta_ptr->tdls_cap.qos_info = pos[2]; break; case WLAN_EID_VHT_OPERATION: - if (priv->adapter->is_hw_11ac_capable) - memcpy(&sta_ptr->tdls_cap.vhtoper, pos, + if (priv->adapter->is_hw_11ac_capable) { + if (pos > end - + sizeof(struct ieee80211_vht_operation) - 2) + return; + if (pos[1] != + sizeof(struct ieee80211_vht_operation)) + return; + /* copy the ie's value into vhtoper*/ + memcpy(&sta_ptr->tdls_cap.vhtoper, pos + 2, sizeof(struct ieee80211_vht_operation)); + } break; case WLAN_EID_VHT_CAPABILITY: if (priv->adapter->is_hw_11ac_capable) { - memcpy((u8 *)&sta_ptr->tdls_cap.vhtcap, pos, + if (pos > end - + sizeof(struct ieee80211_vht_cap) - 2) + return; + if (pos[1] != sizeof(struct ieee80211_vht_cap)) + return; + /* copy the ie's value into vhtcap*/ + memcpy((u8 *)&sta_ptr->tdls_cap.vhtcap, pos + 2, sizeof(struct ieee80211_vht_cap)); sta_ptr->is_11ac_enabled = 1; } break; case WLAN_EID_AID: - if (priv->adapter->is_hw_11ac_capable) + if (priv->adapter->is_hw_11ac_capable) { + if (pos > end - 4) + return; + if (pos[1] != 2) + return; sta_ptr->tdls_cap.aid = get_unaligned_le16((pos + 2)); + } + break; default: break; } -- GitLab From 8122b047dd18ef6e7e1c564e28f3c7067c5a2d71 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Mon, 2 Dec 2019 12:03:49 -0300 Subject: [PATCH 0118/1437] tools arch x86: Sync the msr-index.h copy with the kernel sources To pick up the changes from these csets: 3f3c8be973af Merge tag 'for-linus-5.5a-rc1-tag' of git://git.kernel.org/pub/scm/linux/kernel/git/xen/tip 4e3f77d8419b ("xen/mcelog: add PPIN to record when available") db4d30fbb71b ("x86/bugs: Add ITLB_MULTIHIT bug infrastructure") 1b42f017415b ("x86/speculation/taa: Add mitigation for TSX Async Abort") c2955f270a84 ("x86/msr: Add the IA32_TSX_CTRL MSR") These are the changes in tooling that this udpate ensues: $ tools/perf/trace/beauty/tracepoints/x86_msr.sh > /tmp/before $ $ cp arch/x86/include/asm/msr-index.h tools/arch/x86/include/asm/msr-index.h $ $ tools/perf/trace/beauty/tracepoints/x86_msr.sh > /tmp/after $ diff -u /tmp/before /tmp/after --- /tmp/before 2019-12-02 11:54:44.371035723 -0300 +++ /tmp/after 2019-12-02 11:55:31.847859784 -0300 @@ -48,6 +48,7 @@ [0x00000119] = "IA32_BBL_CR_CTL", [0x0000011e] = "IA32_BBL_CR_CTL3", [0x00000120] = "IDT_MCR_CTRL", + [0x00000122] = "IA32_TSX_CTRL", [0x00000140] = "MISC_FEATURES_ENABLES", [0x00000174] = "IA32_SYSENTER_CS", [0x00000175] = "IA32_SYSENTER_ESP", @@ -283,4 +284,6 @@ [0xc0010240 - x86_AMD_V_KVM_MSRs_offset] = "F15H_NB_PERF_CTL", [0xc0010241 - x86_AMD_V_KVM_MSRs_offset] = "F15H_NB_PERF_CTR", [0xc0010280 - x86_AMD_V_KVM_MSRs_offset] = "F15H_PTSC", + [0xc00102f0 - x86_AMD_V_KVM_MSRs_offset] = "AMD_PPIN_CTL", + [0xc00102f1 - x86_AMD_V_KVM_MSRs_offset] = "AMD_PPIN", }; $ CC /tmp/build/perf/trace/beauty/tracepoints/x86_msr.o LD /tmp/build/perf/trace/beauty/tracepoints/perf-in.o LD /tmp/build/perf/trace/beauty/perf-in.o LD /tmp/build/perf/perf-in.o Now it is possible to use these strings when setting up filters for the msr:* tracepoints, like: # perf trace -e msr:* --filter=msr==IA32_TSX_CTRL ^C[root@quaco ~]# If we use an invalid operator we can check what is the filter that is put in place: # perf trace -e msr:* --filter=msr=IA32_TSX_CTRL Failed to set filter "(msr=0x122) && (common_pid != 25976 && common_pid != 25860)" on event msr:read_msr with 22 (Invalid argument) One can as well use -v to see the tracepoints and its filters: # perf trace -v -e msr:* --filter=msr==IA32_TSX_CTRL Using CPUID GenuineIntel-6-8E-A New filter for msr:read_msr: (msr==0x122) && (common_pid != 26110 && common_pid != 25860) New filter for msr:write_msr: (msr==0x122) && (common_pid != 26110 && common_pid != 25860) New filter for msr:rdpmc: (msr==0x122) && (common_pid != 26110 && common_pid != 25860) mmap size 528384B ^C# Better than keep looking up those numbers, works with callchains as well, e.g. for something more common: # perf trace -e msr:*/max-stack=16/ --filter="msr==IA32_SPEC_CTRL" --max-events=2 0.000 SCTP timer/6158 msr:write_msr(msr: IA32_SPEC_CTRL, val: 6) do_trace_write_msr ([kernel.kallsyms]) do_trace_write_msr ([kernel.kallsyms]) __switch_to_xtra ([kernel.kallsyms]) __switch_to ([kernel.kallsyms]) __sched_text_start ([kernel.kallsyms]) schedule ([kernel.kallsyms]) schedule_hrtimeout_range_clock ([kernel.kallsyms]) poll_schedule_timeout.constprop.0 ([kernel.kallsyms]) do_select ([kernel.kallsyms]) core_sys_select ([kernel.kallsyms]) kern_select ([kernel.kallsyms]) __x64_sys_select ([kernel.kallsyms]) do_syscall_64 ([kernel.kallsyms]) entry_SYSCALL_64 ([kernel.kallsyms]) __select (/usr/lib64/libc-2.29.so) [0] ([unknown]) 0.024 :0/0 msr:write_msr(msr: IA32_SPEC_CTRL) do_trace_write_msr ([kernel.kallsyms]) do_trace_write_msr ([kernel.kallsyms]) __switch_to_xtra ([kernel.kallsyms]) __switch_to ([kernel.kallsyms]) __sched_text_start ([kernel.kallsyms]) schedule_idle ([kernel.kallsyms]) do_idle ([kernel.kallsyms]) cpu_startup_entry ([kernel.kallsyms]) start_secondary ([kernel.kallsyms]) [0x2000d4] ([kernel.kallsyms]) # Cc: Adrian Hunter Cc: Jan Beulich Cc: Jiri Olsa Cc: Juergen Gross Cc: Namhyung Kim Cc: Pawan Gupta Cc: Thomas Gleixner Cc: Vineela Tummalapalli Link: https://lkml.kernel.org/n/tip-n1xd78fpd5lxn4q1brqi2jl6@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/arch/x86/include/asm/msr-index.h | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/tools/arch/x86/include/asm/msr-index.h b/tools/arch/x86/include/asm/msr-index.h index 20ce682a2540f..084e98da04a7e 100644 --- a/tools/arch/x86/include/asm/msr-index.h +++ b/tools/arch/x86/include/asm/msr-index.h @@ -93,6 +93,18 @@ * Microarchitectural Data * Sampling (MDS) vulnerabilities. */ +#define ARCH_CAP_PSCHANGE_MC_NO BIT(6) /* + * The processor is not susceptible to a + * machine check error due to modifying the + * code page size along with either the + * physical address or cache type + * without TLB invalidation. + */ +#define ARCH_CAP_TSX_CTRL_MSR BIT(7) /* MSR for TSX control is available. */ +#define ARCH_CAP_TAA_NO BIT(8) /* + * Not susceptible to + * TSX Async Abort (TAA) vulnerabilities. + */ #define MSR_IA32_FLUSH_CMD 0x0000010b #define L1D_FLUSH BIT(0) /* @@ -103,6 +115,10 @@ #define MSR_IA32_BBL_CR_CTL 0x00000119 #define MSR_IA32_BBL_CR_CTL3 0x0000011e +#define MSR_IA32_TSX_CTRL 0x00000122 +#define TSX_CTRL_RTM_DISABLE BIT(0) /* Disable RTM feature */ +#define TSX_CTRL_CPUID_CLEAR BIT(1) /* Disable TSX enumeration */ + #define MSR_IA32_SYSENTER_CS 0x00000174 #define MSR_IA32_SYSENTER_ESP 0x00000175 #define MSR_IA32_SYSENTER_EIP 0x00000176 @@ -393,6 +409,8 @@ #define MSR_AMD_PSTATE_DEF_BASE 0xc0010064 #define MSR_AMD64_OSVW_ID_LENGTH 0xc0010140 #define MSR_AMD64_OSVW_STATUS 0xc0010141 +#define MSR_AMD_PPIN_CTL 0xc00102f0 +#define MSR_AMD_PPIN 0xc00102f1 #define MSR_AMD64_LS_CFG 0xc0011020 #define MSR_AMD64_DC_CFG 0xc0011022 #define MSR_AMD64_BU_CFG2 0xc001102a -- GitLab From ad46f35cca45e3164137271cd7f06d7e66dae6be Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Mon, 2 Dec 2019 12:19:24 -0300 Subject: [PATCH 0119/1437] tools headers uapi: Sync linux/fscrypt.h with the kernel sources To pick the changes from: b103fb7653ff ("fscrypt: add support for IV_INO_LBLK_64 policies") That don't trigger any changes in tooling. This silences this perf build warning: Warning: Kernel ABI header at 'tools/include/uapi/linux/fscrypt.h' differs from latest version at 'include/uapi/linux/fscrypt.h' diff -u tools/include/uapi/linux/fscrypt.h include/uapi/linux/fscrypt.h Cc: Adrian Hunter Cc: Eric Biggers Cc: Jiri Olsa Cc: Namhyung Kim Link: https://lkml.kernel.org/n/tip-cgfz3ffe07pw2m8hmstvkudl@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/include/uapi/linux/fscrypt.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tools/include/uapi/linux/fscrypt.h b/tools/include/uapi/linux/fscrypt.h index 39ccfe9311c38..1beb174ad9505 100644 --- a/tools/include/uapi/linux/fscrypt.h +++ b/tools/include/uapi/linux/fscrypt.h @@ -17,7 +17,8 @@ #define FSCRYPT_POLICY_FLAGS_PAD_32 0x03 #define FSCRYPT_POLICY_FLAGS_PAD_MASK 0x03 #define FSCRYPT_POLICY_FLAG_DIRECT_KEY 0x04 -#define FSCRYPT_POLICY_FLAGS_VALID 0x07 +#define FSCRYPT_POLICY_FLAG_IV_INO_LBLK_64 0x08 +#define FSCRYPT_POLICY_FLAGS_VALID 0x0F /* Encryption algorithms */ #define FSCRYPT_MODE_AES_256_XTS 1 -- GitLab From c66f2566db340b04c113ea1d930651246d9d4049 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Mon, 2 Dec 2019 12:24:52 -0300 Subject: [PATCH 0120/1437] tools headers uapi: Sync linux/stat.h with the kernel sources To pick the changes from: 3ad2522c64cf ("statx: define STATX_ATTR_VERITY") That don't trigger any changes in tooling. This silences this perf build warning: Warning: Kernel ABI header at 'tools/include/uapi/linux/stat.h' differs from latest version at 'include/uapi/linux/stat.h' diff -u tools/include/uapi/linux/stat.h include/uapi/linux/stat.h At some point we wi'll beautify structs passed in pointers to syscalls and then we'll need to have tables for these defines, for now update the file to silence the warning as this file is used for doing this type of number -> string translations for other defines found in these file. Cc: Adrian Hunter Cc: Eric Biggers Cc: Jiri Olsa Cc: Namhyung Kim Link: https://lkml.kernel.org/n/tip-thcy60dpry5qrpn7nmc58bwg@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/include/uapi/linux/stat.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/include/uapi/linux/stat.h b/tools/include/uapi/linux/stat.h index 7b35e98d3c58b..ad80a5c885d59 100644 --- a/tools/include/uapi/linux/stat.h +++ b/tools/include/uapi/linux/stat.h @@ -167,8 +167,8 @@ struct statx { #define STATX_ATTR_APPEND 0x00000020 /* [I] File is append-only */ #define STATX_ATTR_NODUMP 0x00000040 /* [I] File is not to be dumped */ #define STATX_ATTR_ENCRYPTED 0x00000800 /* [I] File requires key to decrypt in fs */ - #define STATX_ATTR_AUTOMOUNT 0x00001000 /* Dir: Automount trigger */ +#define STATX_ATTR_VERITY 0x00100000 /* [I] Verity protected file */ #endif /* _UAPI_LINUX_STAT_H */ -- GitLab From 1fc3d0ee242de68267769afcaf5520439de42f64 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Mon, 2 Dec 2019 12:44:24 -0300 Subject: [PATCH 0121/1437] tools headers kvm: Sync kvm headers with the kernel sources To pick up the changes from: 14edff88315a Merge tag 'kvmarm-5.5' of git://git.kernel.org/pub/scm/linux/kernel/git/kvmarm/kvmarm into HEAD a4b28f5c6798 Merge remote-tracking branch 'kvmarm/kvm-arm64/stolen-time' into kvmarm-master/next 58772e9a3db7 ("KVM: arm64: Provide VCPU attributes for stolen time") da345174ceca ("KVM: arm/arm64: Allow user injection of external data aborts") c726200dd106 ("KVM: arm/arm64: Allow reporting non-ISV data aborts to userspace") efe5ddcae496 ("KVM: PPC: Book3S HV: XIVE: Allow userspace to set the # of VPs") No tools changes are caused by this, as the only defines so far used from these files are for syscall arg pretty printing are: $ grep KVM tools/perf/trace/beauty/*.sh tools/perf/trace/beauty/kvm_ioctl.sh:regex='^#[[:space:]]*define[[:space:]]+KVM_(\w+)[[:space:]]+_IO[RW]*\([[:space:]]*KVMIO[[:space:]]*,[[:space:]]*(0x[[:xdigit:]]+).*' $ Some are also include by: tools/perf/arch/x86/util/kvm-stat.c tools/perf/arch/powerpc/util/kvm-stat.c This addresses these tools/perf build warnings: Warning: Kernel ABI header at 'tools/include/uapi/linux/kvm.h' differs from latest version at 'include/uapi/linux/kvm.h' diff -u tools/include/uapi/linux/kvm.h include/uapi/linux/kvm.h Warning: Kernel ABI header at 'tools/arch/powerpc/include/uapi/asm/kvm.h' differs from latest version at 'arch/powerpc/include/uapi/asm/kvm.h' diff -u tools/arch/powerpc/include/uapi/asm/kvm.h arch/powerpc/include/uapi/asm/kvm.h Warning: Kernel ABI header at 'tools/arch/arm/include/uapi/asm/kvm.h' differs from latest version at 'arch/arm/include/uapi/asm/kvm.h' diff -u tools/arch/arm/include/uapi/asm/kvm.h arch/arm/include/uapi/asm/kvm.h Warning: Kernel ABI header at 'tools/arch/arm64/include/uapi/asm/kvm.h' differs from latest version at 'arch/arm64/include/uapi/asm/kvm.h' diff -u tools/arch/arm64/include/uapi/asm/kvm.h arch/arm64/include/uapi/asm/kvm.h Cc: Adrian Hunter Cc: Christoffer Dall Cc: Fabiano Rosas Cc: Greg Kurz Cc: Jiri Olsa Cc: Marc Zyngier Cc: Namhyung Kim Cc: Paul Mackerras Cc: Steven Price Link: https://lkml.kernel.org/n/tip-qrjdudhq25mk5bfnhveofbm4@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/arch/arm/include/uapi/asm/kvm.h | 3 ++- tools/arch/arm64/include/uapi/asm/kvm.h | 5 ++++- tools/arch/powerpc/include/uapi/asm/kvm.h | 3 +++ tools/include/uapi/linux/kvm.h | 11 +++++++++++ 4 files changed, 20 insertions(+), 2 deletions(-) diff --git a/tools/arch/arm/include/uapi/asm/kvm.h b/tools/arch/arm/include/uapi/asm/kvm.h index 2769360f195ca..03cd7c19a683b 100644 --- a/tools/arch/arm/include/uapi/asm/kvm.h +++ b/tools/arch/arm/include/uapi/asm/kvm.h @@ -131,8 +131,9 @@ struct kvm_vcpu_events { struct { __u8 serror_pending; __u8 serror_has_esr; + __u8 ext_dabt_pending; /* Align it to 8 bytes */ - __u8 pad[6]; + __u8 pad[5]; __u64 serror_esr; } exception; __u32 reserved[12]; diff --git a/tools/arch/arm64/include/uapi/asm/kvm.h b/tools/arch/arm64/include/uapi/asm/kvm.h index 67c21f9bdbad2..820e5751ada71 100644 --- a/tools/arch/arm64/include/uapi/asm/kvm.h +++ b/tools/arch/arm64/include/uapi/asm/kvm.h @@ -164,8 +164,9 @@ struct kvm_vcpu_events { struct { __u8 serror_pending; __u8 serror_has_esr; + __u8 ext_dabt_pending; /* Align it to 8 bytes */ - __u8 pad[6]; + __u8 pad[5]; __u64 serror_esr; } exception; __u32 reserved[12]; @@ -323,6 +324,8 @@ struct kvm_vcpu_events { #define KVM_ARM_VCPU_TIMER_CTRL 1 #define KVM_ARM_VCPU_TIMER_IRQ_VTIMER 0 #define KVM_ARM_VCPU_TIMER_IRQ_PTIMER 1 +#define KVM_ARM_VCPU_PVTIME_CTRL 2 +#define KVM_ARM_VCPU_PVTIME_IPA 0 /* KVM_IRQ_LINE irq field index values */ #define KVM_ARM_IRQ_VCPU2_SHIFT 28 diff --git a/tools/arch/powerpc/include/uapi/asm/kvm.h b/tools/arch/powerpc/include/uapi/asm/kvm.h index b0f72dea8b11a..264e266a85bf6 100644 --- a/tools/arch/powerpc/include/uapi/asm/kvm.h +++ b/tools/arch/powerpc/include/uapi/asm/kvm.h @@ -667,6 +667,8 @@ struct kvm_ppc_cpu_char { /* PPC64 eXternal Interrupt Controller Specification */ #define KVM_DEV_XICS_GRP_SOURCES 1 /* 64-bit source attributes */ +#define KVM_DEV_XICS_GRP_CTRL 2 +#define KVM_DEV_XICS_NR_SERVERS 1 /* Layout of 64-bit source attribute values */ #define KVM_XICS_DESTINATION_SHIFT 0 @@ -683,6 +685,7 @@ struct kvm_ppc_cpu_char { #define KVM_DEV_XIVE_GRP_CTRL 1 #define KVM_DEV_XIVE_RESET 1 #define KVM_DEV_XIVE_EQ_SYNC 2 +#define KVM_DEV_XIVE_NR_SERVERS 3 #define KVM_DEV_XIVE_GRP_SOURCE 2 /* 64-bit source identifier */ #define KVM_DEV_XIVE_GRP_SOURCE_CONFIG 3 /* 64-bit source identifier */ #define KVM_DEV_XIVE_GRP_EQ_CONFIG 4 /* 64-bit EQ identifier */ diff --git a/tools/include/uapi/linux/kvm.h b/tools/include/uapi/linux/kvm.h index 52641d8ca9e83..e6f17c8e2dbac 100644 --- a/tools/include/uapi/linux/kvm.h +++ b/tools/include/uapi/linux/kvm.h @@ -235,6 +235,7 @@ struct kvm_hyperv_exit { #define KVM_EXIT_S390_STSI 25 #define KVM_EXIT_IOAPIC_EOI 26 #define KVM_EXIT_HYPERV 27 +#define KVM_EXIT_ARM_NISV 28 /* For KVM_EXIT_INTERNAL_ERROR */ /* Emulate instruction failed. */ @@ -394,6 +395,11 @@ struct kvm_run { } eoi; /* KVM_EXIT_HYPERV */ struct kvm_hyperv_exit hyperv; + /* KVM_EXIT_ARM_NISV */ + struct { + __u64 esr_iss; + __u64 fault_ipa; + } arm_nisv; /* Fix the size of the union. */ char padding[256]; }; @@ -1000,6 +1006,9 @@ struct kvm_ppc_resize_hpt { #define KVM_CAP_PMU_EVENT_FILTER 173 #define KVM_CAP_ARM_IRQ_LINE_LAYOUT_2 174 #define KVM_CAP_HYPERV_DIRECT_TLBFLUSH 175 +#define KVM_CAP_PPC_GUEST_DEBUG_SSTEP 176 +#define KVM_CAP_ARM_NISV_TO_USER 177 +#define KVM_CAP_ARM_INJECT_EXT_DABT 178 #ifdef KVM_CAP_IRQ_ROUTING @@ -1227,6 +1236,8 @@ enum kvm_device_type { #define KVM_DEV_TYPE_ARM_VGIC_ITS KVM_DEV_TYPE_ARM_VGIC_ITS KVM_DEV_TYPE_XIVE, #define KVM_DEV_TYPE_XIVE KVM_DEV_TYPE_XIVE + KVM_DEV_TYPE_ARM_PV_TIME, +#define KVM_DEV_TYPE_ARM_PV_TIME KVM_DEV_TYPE_ARM_PV_TIME KVM_DEV_TYPE_MAX, }; -- GitLab From 2603a4903bf96d2fe15b7d9e2d03b7efdffbd99a Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Mon, 2 Dec 2019 12:56:39 -0300 Subject: [PATCH 0122/1437] tools headers UAPI: Sync sched.h with the kernel To get the changes in: 0acefef58451 ("Merge tag 'threads-v5.5' of git://git.kernel.org/pub/scm/linux/kernel/git/brauner/linux") 49cb2fc42ce4 ("fork: extend clone3() to support setting a PID") fa729c4df558 ("clone3: validate stack arguments") b612e5df4587 ("clone3: add CLONE_CLEAR_SIGHAND") This file gets rebuilt, but no changes ensues: CC /tmp/build/perf/trace/beauty/clone.o This addresses this perf build warning: Warning: Kernel ABI header at 'tools/include/uapi/linux/sched.h' differs from latest version at 'include/uapi/linux/sched.h' diff -u tools/include/uapi/linux/sched.h include/uapi/linux/sched. The CLONE_CLEAR_SIGHAND one will be used in tools/perf/trace/beauty/clone.c in a followup patch to show that string when this bit is set in the syscall arg. Keeping a copy of this file allows us to build this in older systems and have the binary support printing that flag whenever that system gets its kernel updated to one where this feature is present. Cc: Adrian Hunter Cc: Adrian Reber Cc: Christian Brauner Cc: Jiri Olsa Cc: Namhyung Kim Link: https://lkml.kernel.org/n/tip-nprqsvvzbhzoy64cbvos6c5b@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/include/uapi/linux/sched.h | 60 ++++++++++++++++++++++---------- 1 file changed, 42 insertions(+), 18 deletions(-) diff --git a/tools/include/uapi/linux/sched.h b/tools/include/uapi/linux/sched.h index 99335e1f4a275..4a02178324641 100644 --- a/tools/include/uapi/linux/sched.h +++ b/tools/include/uapi/linux/sched.h @@ -33,27 +33,48 @@ #define CLONE_NEWNET 0x40000000 /* New network namespace */ #define CLONE_IO 0x80000000 /* Clone io context */ +/* Flags for the clone3() syscall. */ +#define CLONE_CLEAR_SIGHAND 0x100000000ULL /* Clear any signal handler and reset to SIG_DFL. */ + #ifndef __ASSEMBLY__ /** * struct clone_args - arguments for the clone3 syscall - * @flags: Flags for the new process as listed above. - * All flags are valid except for CSIGNAL and - * CLONE_DETACHED. - * @pidfd: If CLONE_PIDFD is set, a pidfd will be - * returned in this argument. - * @child_tid: If CLONE_CHILD_SETTID is set, the TID of the - * child process will be returned in the child's - * memory. - * @parent_tid: If CLONE_PARENT_SETTID is set, the TID of - * the child process will be returned in the - * parent's memory. - * @exit_signal: The exit_signal the parent process will be - * sent when the child exits. - * @stack: Specify the location of the stack for the - * child process. - * @stack_size: The size of the stack for the child process. - * @tls: If CLONE_SETTLS is set, the tls descriptor - * is set to tls. + * @flags: Flags for the new process as listed above. + * All flags are valid except for CSIGNAL and + * CLONE_DETACHED. + * @pidfd: If CLONE_PIDFD is set, a pidfd will be + * returned in this argument. + * @child_tid: If CLONE_CHILD_SETTID is set, the TID of the + * child process will be returned in the child's + * memory. + * @parent_tid: If CLONE_PARENT_SETTID is set, the TID of + * the child process will be returned in the + * parent's memory. + * @exit_signal: The exit_signal the parent process will be + * sent when the child exits. + * @stack: Specify the location of the stack for the + * child process. + * Note, @stack is expected to point to the + * lowest address. The stack direction will be + * determined by the kernel and set up + * appropriately based on @stack_size. + * @stack_size: The size of the stack for the child process. + * @tls: If CLONE_SETTLS is set, the tls descriptor + * is set to tls. + * @set_tid: Pointer to an array of type *pid_t. The size + * of the array is defined using @set_tid_size. + * This array is used to select PIDs/TIDs for + * newly created processes. The first element in + * this defines the PID in the most nested PID + * namespace. Each additional element in the array + * defines the PID in the parent PID namespace of + * the original PID namespace. If the array has + * less entries than the number of currently + * nested PID namespaces only the PIDs in the + * corresponding namespaces are set. + * @set_tid_size: This defines the size of the array referenced + * in @set_tid. This cannot be larger than the + * kernel's limit of nested PID namespaces. * * The structure is versioned by size and thus extensible. * New struct members must go at the end of the struct and @@ -68,10 +89,13 @@ struct clone_args { __aligned_u64 stack; __aligned_u64 stack_size; __aligned_u64 tls; + __aligned_u64 set_tid; + __aligned_u64 set_tid_size; }; #endif #define CLONE_ARGS_SIZE_VER0 64 /* sizeof first published struct */ +#define CLONE_ARGS_SIZE_VER1 80 /* sizeof second published struct */ /* * Scheduling policies -- GitLab From f6661125ff41e27b488f36422226653baad3c382 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Mon, 2 Dec 2019 13:02:25 -0300 Subject: [PATCH 0123/1437] perf beauty: Add CLEAR_SIGHAND support for clone's flags arg Add support for the recently added CLONE_CLEAR_SIGHAND flag. This takes advantage of the copy of the uapi/linux/sched.h we have in tools/include, which allows us to build tools/perf in older systems and have the binary support printing that flag whenever that system gets its kernel updated to one where this feature is present. Cc: Adrian Hunter Cc: Adrian Reber Cc: Christian Brauner Cc: Jiri Olsa Cc: Namhyung Kim --- tools/perf/trace/beauty/clone.c | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/perf/trace/beauty/clone.c b/tools/perf/trace/beauty/clone.c index 1a8d3be2030e9..062ca849c8fda 100644 --- a/tools/perf/trace/beauty/clone.c +++ b/tools/perf/trace/beauty/clone.c @@ -45,6 +45,7 @@ static size_t clone__scnprintf_flags(unsigned long flags, char *bf, size_t size, P_FLAG(NEWPID); P_FLAG(NEWNET); P_FLAG(IO); + P_FLAG(CLEAR_SIGHAND); #undef P_FLAG if (flags) -- GitLab From a717ab38a51404a3f7069987f77676cde82139ac Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Mon, 2 Dec 2019 15:11:51 -0300 Subject: [PATCH 0124/1437] tools arch x86: Sync asm/cpufeatures.h with the kernel sources To pick up the changes from: a25bbc2644f0 ("Merge branches 'x86-cpu-for-linus' and 'x86-fpu-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip") db4d30fbb71b ("x86/bugs: Add ITLB_MULTIHIT bug infrastructure") 1b42f017415b ("x86/speculation/taa: Add mitigation for TSX Async Abort") 9d40b85bb46a ("x86/cpufeatures: Add feature bit RDPRU on AMD") These don't cause any changes in tooling, just silences this perf build warning: Warning: Kernel ABI header at 'tools/arch/x86/include/asm/cpufeatures.h' differs from latest version at 'arch/x86/include/asm/cpufeatures.h' diff -u tools/arch/x86/include/asm/cpufeatures.h arch/x86/include/asm/cpufeatures.h Cc: Adrian Hunter Cc: Babu Moger Cc: Borislav Petkov Cc: Jiri Olsa Cc: Namhyung Kim Cc: Pawan Gupta Cc: Thomas Gleixner Cc: Vineela Tummalapalli Link: https://lkml.kernel.org/n/tip-yufg9yt2nbkh45r9xvxnnscq@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/arch/x86/include/asm/cpufeatures.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tools/arch/x86/include/asm/cpufeatures.h b/tools/arch/x86/include/asm/cpufeatures.h index 0652d3eed9bda..e9b62498fe75a 100644 --- a/tools/arch/x86/include/asm/cpufeatures.h +++ b/tools/arch/x86/include/asm/cpufeatures.h @@ -292,6 +292,7 @@ #define X86_FEATURE_CLZERO (13*32+ 0) /* CLZERO instruction */ #define X86_FEATURE_IRPERF (13*32+ 1) /* Instructions Retired Count */ #define X86_FEATURE_XSAVEERPTR (13*32+ 2) /* Always save/restore FP error pointers */ +#define X86_FEATURE_RDPRU (13*32+ 4) /* Read processor register at user level */ #define X86_FEATURE_WBNOINVD (13*32+ 9) /* WBNOINVD instruction */ #define X86_FEATURE_AMD_IBPB (13*32+12) /* "" Indirect Branch Prediction Barrier */ #define X86_FEATURE_AMD_IBRS (13*32+14) /* "" Indirect Branch Restricted Speculation */ @@ -399,5 +400,7 @@ #define X86_BUG_MDS X86_BUG(19) /* CPU is affected by Microarchitectural data sampling */ #define X86_BUG_MSBDS_ONLY X86_BUG(20) /* CPU is only affected by the MSDBS variant of BUG_MDS */ #define X86_BUG_SWAPGS X86_BUG(21) /* CPU is affected by speculation through SWAPGS */ +#define X86_BUG_TAA X86_BUG(22) /* CPU is affected by TSX Async Abort(TAA) */ +#define X86_BUG_ITLB_MULTIHIT X86_BUG(23) /* CPU may incur MCE during certain page attribute changes */ #endif /* _ASM_X86_CPUFEATURES_H */ -- GitLab From 9974406884459c9301597c2c9f7def6c38099ab4 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Mon, 2 Dec 2019 15:38:59 -0300 Subject: [PATCH 0125/1437] perf kvm: Clarify the 'perf kvm' -i and -o command line options The 'perf kvm' subcommand has options that it in turn passes to other perf subcommands such as 'report' and 'record', particularly -i and -o end up setting the same variable that will then be used for 'record's -o and report '-i', which ends up being confusing, leading some to think that both -i and -o can be used with 'report'. Improve the man page to state that -i is used with the post-processing subcommands while -o is used just with 'record' and that to save the output of 'report' one should simply redirect its output to a file. Noticed while reading the https://www.linux-kvm.org/page/Perf_events page. Cc: Adrian Hunter Cc: Jiri Olsa Cc: Namhyung Kim Cc: Steve Dickson Cc: William Cohen Link: https://lkml.kernel.org/n/tip-tclbttvmgtm525fvmh85f7d9@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/Documentation/perf-kvm.txt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tools/perf/Documentation/perf-kvm.txt b/tools/perf/Documentation/perf-kvm.txt index 6a5bb2b170391..cf95baef7b61c 100644 --- a/tools/perf/Documentation/perf-kvm.txt +++ b/tools/perf/Documentation/perf-kvm.txt @@ -68,10 +68,11 @@ OPTIONS ------- -i:: --input=:: - Input file name. + Input file name, for the 'report', 'diff' and 'buildid-list' subcommands. -o:: --output=:: - Output file name. + Output file name, for the 'record' subcommand. Doesn't work with 'report', + just redirect the output to a file when using 'report'. --host:: Collect host side performance profile. --guest:: -- GitLab From 22802bf742c25b1e2473c70b3b99da98af65ef4d Mon Sep 17 00:00:00 2001 From: Keith Busch Date: Tue, 3 Dec 2019 00:44:59 +0900 Subject: [PATCH 0126/1437] nvme: Namepace identification descriptor list is optional Despite NVM Express specification 1.3 requires a controller claiming to be 1.3 or higher implement Identify CNS 03h (Namespace Identification Descriptor list), the driver doesn't really need this identification in order to use a namespace. The code had already documented in comments that we're not to consider an error to this command. Return success if the controller provided any response to an namespace identification descriptors command. Fixes: 538af88ea7d9de24 ("nvme: make nvme_report_ns_ids propagate error back") Link: https://bugzilla.kernel.org/show_bug.cgi?id=205679 Reported-by: Ingo Brunberg Cc: Sagi Grimberg Cc: stable@vger.kernel.org # 5.4+ Reviewed-by: Christoph Hellwig Signed-off-by: Keith Busch --- drivers/nvme/host/core.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/nvme/host/core.c b/drivers/nvme/host/core.c index e6ee34376c5ea..2a84e14022446 100644 --- a/drivers/nvme/host/core.c +++ b/drivers/nvme/host/core.c @@ -1735,6 +1735,8 @@ static int nvme_report_ns_ids(struct nvme_ctrl *ctrl, unsigned int nsid, if (ret) dev_warn(ctrl->device, "Identify Descriptors failed (%d)\n", ret); + if (ret > 0) + ret = 0; } return ret; } -- GitLab From 587db8ebdac2c5eb3a8851e16b26f2e2711ab797 Mon Sep 17 00:00:00 2001 From: Sudip Mukherjee Date: Fri, 15 Nov 2019 11:36:09 +0000 Subject: [PATCH 0127/1437] libtraceevent: Fix lib installation with O= When we use 'O=' with make to build libtraceevent in a separate folder it fails to install libtraceevent.a and libtraceevent.so.1.1.0 with the error: INSTALL /home/sudip/linux/obj-trace/libtraceevent.a INSTALL /home/sudip/linux/obj-trace/libtraceevent.so.1.1.0 cp: cannot stat 'libtraceevent.a': No such file or directory Makefile:225: recipe for target 'install_lib' failed make: *** [install_lib] Error 1 I used the command: make O=../../../obj-trace DESTDIR=~/test prefix==/usr install It turns out libtraceevent Makefile, even though it builds in a separate folder, searches for libtraceevent.a and libtraceevent.so.1.1.0 in its source folder. So, add the 'OUTPUT' prefix to the source path so that 'make' looks for the files in the correct place. Signed-off-by: Sudipm Mukherjee Reviewed-by: Steven Rostedt (VMware) Cc: linux-trace-devel@vger.kernel.org Link: http://lore.kernel.org/lkml/20191115113610.21493-1-sudipm.mukherjee@gmail.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/lib/traceevent/Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/lib/traceevent/Makefile b/tools/lib/traceevent/Makefile index cbb429f550625..83446fe2cf011 100644 --- a/tools/lib/traceevent/Makefile +++ b/tools/lib/traceevent/Makefile @@ -97,6 +97,7 @@ EVENT_PARSE_VERSION = $(EP_VERSION).$(EP_PATCHLEVEL).$(EP_EXTRAVERSION) LIB_TARGET = libtraceevent.a libtraceevent.so.$(EVENT_PARSE_VERSION) LIB_INSTALL = libtraceevent.a libtraceevent.so* +LIB_INSTALL := $(addprefix $(OUTPUT),$(LIB_INSTALL)) INCLUDES = -I. -I $(srctree)/tools/include $(CONFIG_INCLUDES) -- GitLab From 15b3904f8e884e0d34d5f09906cf6526d0b889a2 Mon Sep 17 00:00:00 2001 From: Sudip Mukherjee Date: Fri, 15 Nov 2019 11:36:10 +0000 Subject: [PATCH 0128/1437] libtraceevent: Copy pkg-config file to output folder when using O= When we use 'O=' with make to build libtraceevent in a separate folder it still copies 'libtraceevent.pc' to its source folder. Modify the Makefile so that it uses the output folder to copy the pkg-config file and install from there. Signed-off-by: Sudipm Mukherjee Reviewed-by: Steven Rostedt (VMware) Cc: linux-trace-devel@vger.kernel.org Link: http://lore.kernel.org/lkml/20191115113610.21493-2-sudipm.mukherjee@gmail.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/lib/traceevent/Makefile | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tools/lib/traceevent/Makefile b/tools/lib/traceevent/Makefile index 83446fe2cf011..c5a03356a9995 100644 --- a/tools/lib/traceevent/Makefile +++ b/tools/lib/traceevent/Makefile @@ -208,10 +208,11 @@ define do_install $(INSTALL) $(if $3,-m $3,) $1 '$(DESTDIR_SQ)$2' endef -PKG_CONFIG_FILE = libtraceevent.pc +PKG_CONFIG_SOURCE_FILE = libtraceevent.pc +PKG_CONFIG_FILE := $(addprefix $(OUTPUT),$(PKG_CONFIG_SOURCE_FILE)) define do_install_pkgconfig_file if [ -n "${pkgconfig_dir}" ]; then \ - cp -f ${PKG_CONFIG_FILE}.template ${PKG_CONFIG_FILE}; \ + cp -f ${PKG_CONFIG_SOURCE_FILE}.template ${PKG_CONFIG_FILE}; \ sed -i "s|INSTALL_PREFIX|${1}|g" ${PKG_CONFIG_FILE}; \ sed -i "s|LIB_VERSION|${EVENT_PARSE_VERSION}|g" ${PKG_CONFIG_FILE}; \ sed -i "s|LIB_DIR|${libdir}|g" ${PKG_CONFIG_FILE}; \ -- GitLab From d5ee9108adacfbed140e0ac2371941ce7ca1fc54 Mon Sep 17 00:00:00 2001 From: YueHaibing Date: Thu, 28 Nov 2019 21:58:53 +0800 Subject: [PATCH 0129/1437] ASoC: Intel: sst: Add missing include Fix build error: sound/soc/intel/atom/sst/sst.c: In function intel_sst_interrupt_mrfld: sound/soc/intel/atom/sst/sst.c:93:5: error: implicit declaration of function memcpy_fromio; did you mean memcpy32_fromio? [-Werror=implicit-function-declaration] memcpy_fromio(msg->mailbox_data, ^~~~~~~~~~~~~ memcpy32_fromio Reported-by: Hulk Robot Signed-off-by: YueHaibing Acked-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20191128135853.8360-1-yuehaibing@huawei.com Signed-off-by: Mark Brown --- sound/soc/intel/atom/sst/sst.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/soc/intel/atom/sst/sst.c b/sound/soc/intel/atom/sst/sst.c index fbecbb74350b5..68bcec5241f74 100644 --- a/sound/soc/intel/atom/sst/sst.c +++ b/sound/soc/intel/atom/sst/sst.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include -- GitLab From a3cde9534ebdafe18a9bbab208df724c57e6c8e8 Mon Sep 17 00:00:00 2001 From: Wen Yang Date: Sun, 1 Dec 2019 11:02:50 +0800 Subject: [PATCH 0130/1437] regulator: core: fix regulator_register() error paths to properly release rdev There are several issues with the error handling code of the regulator_register() function: ret = device_register(&rdev->dev); if (ret != 0) { put_device(&rdev->dev); --> rdev released goto unset_supplies; } ... unset_supplies: ... unset_regulator_supplies(rdev); --> use-after-free ... clean: if (dangling_of_gpiod) gpiod_put(config->ena_gpiod); kfree(rdev); --> double free We add a variable to record the failure of device_register() and move put_device() down a bit to avoid the above issues. Fixes: c438b9d01736 ("regulator: core: Move registration of regulator device") Signed-off-by: Wen Yang Cc: Liam Girdwood Cc: Mark Brown Cc: linux-kernel@vger.kernel.org Link: https://lore.kernel.org/r/20191201030250.38074-1-wenyang@linux.alibaba.com Signed-off-by: Mark Brown --- drivers/regulator/core.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index c80f3fd9532dd..2c3a03cfd381d 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -4998,6 +4998,7 @@ regulator_register(const struct regulator_desc *regulator_desc, struct regulator_dev *rdev; bool dangling_cfg_gpiod = false; bool dangling_of_gpiod = false; + bool reg_device_fail = false; struct device *dev; int ret, i; @@ -5183,7 +5184,7 @@ regulator_register(const struct regulator_desc *regulator_desc, dev_set_drvdata(&rdev->dev, rdev); ret = device_register(&rdev->dev); if (ret != 0) { - put_device(&rdev->dev); + reg_device_fail = true; goto unset_supplies; } @@ -5213,7 +5214,10 @@ wash: clean: if (dangling_of_gpiod) gpiod_put(config->ena_gpiod); - kfree(rdev); + if (reg_device_fail) + put_device(&rdev->dev); + else + kfree(rdev); kfree(config); rinse: if (dangling_cfg_gpiod) -- GitLab From a1f4c96b799b80118b50bd47320deb9013b7ff40 Mon Sep 17 00:00:00 2001 From: YueHaibing Date: Sat, 30 Nov 2019 09:28:28 +0800 Subject: [PATCH 0131/1437] gpiolib: of: Make of_gpio_spi_cs_get_count static Fix sparse warning: drivers/gpio/gpiolib-of.c:35:5: warning: symbol 'of_gpio_spi_cs_get_count' was not declared. Should it be static? Reported-by: Hulk Robot Signed-off-by: YueHaibing Reviewed-by: Linus Walleij Link: https://lore.kernel.org/r/20191130012828.14504-1-yuehaibing@huawei.com Signed-off-by: Mark Brown --- drivers/gpio/gpiolib-of.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpio/gpiolib-of.c b/drivers/gpio/gpiolib-of.c index 4b19e7e26b908..f90a161ca97a9 100644 --- a/drivers/gpio/gpiolib-of.c +++ b/drivers/gpio/gpiolib-of.c @@ -32,7 +32,7 @@ * the counting of "cs-gpios" to count "gpios" transparent to the * driver. */ -int of_gpio_spi_cs_get_count(struct device *dev, const char *con_id) +static int of_gpio_spi_cs_get_count(struct device *dev, const char *con_id) { struct device_node *np = dev->of_node; -- GitLab From f6c4d97b0d82ed1240690c91f06214018b1531ef Mon Sep 17 00:00:00 2001 From: Keith Busch Date: Tue, 3 Dec 2019 05:53:30 +0900 Subject: [PATCH 0132/1437] nvme/pci: Remove last_cq_head We had been saving the last_cq_head seen from an interrupt so that a polled queue wouldn't mistakenly trigger spruious interrupt detection. We don't poll interrupt driven queues any more, so saving this value is pointless. Reviewed-by: Christoph Hellwig Signed-off-by: Keith Busch --- drivers/nvme/host/pci.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/nvme/host/pci.c b/drivers/nvme/host/pci.c index 9d307593b94f8..0590640ba62ca 100644 --- a/drivers/nvme/host/pci.c +++ b/drivers/nvme/host/pci.c @@ -176,7 +176,6 @@ struct nvme_queue { u16 sq_tail; u16 last_sq_tail; u16 cq_head; - u16 last_cq_head; u16 qid; u8 cq_phase; u8 sqes; @@ -1026,10 +1025,7 @@ static irqreturn_t nvme_irq(int irq, void *data) * the irq handler, even if that was on another CPU. */ rmb(); - if (nvmeq->cq_head != nvmeq->last_cq_head) - ret = IRQ_HANDLED; nvme_process_cq(nvmeq, &start, &end, -1); - nvmeq->last_cq_head = nvmeq->cq_head; wmb(); if (start != end) { -- GitLab From af164898482817a1d487964b68f3c21bae7a1beb Mon Sep 17 00:00:00 2001 From: Dave Young Date: Wed, 4 Dec 2019 15:52:33 +0800 Subject: [PATCH 0133/1437] x86/efi: Update e820 with reserved EFI boot services data to fix kexec breakage Michael Weiser reported that he got this error during a kexec rebooting: esrt: Unsupported ESRT version 2904149718861218184. The ESRT memory stays in EFI boot services data, and it was reserved in kernel via efi_mem_reserve(). The initial purpose of the reservation is to reuse the EFI boot services data across kexec reboot. For example the BGRT image data and some ESRT memory like Michael reported. But although the memory is reserved it is not updated in the X86 E820 table, and kexec_file_load() iterates system RAM in the IO resource list to find places for kernel, initramfs and other stuff. In Michael's case the kexec loaded initramfs overwrote the ESRT memory and then the failure happened. Since kexec_file_load() depends on the E820 table being updated, just fix this by updating the reserved EFI boot services memory as reserved type in E820. Originally any memory descriptors with EFI_MEMORY_RUNTIME attribute are bypassed in the reservation code path because they are assumed as reserved. But the reservation is still needed for multiple kexec reboots, and it is the only possible case we come here thus just drop the code chunk, then everything works without side effects. On my machine the ESRT memory sits in an EFI runtime data range, it does not trigger the problem, but I successfully tested with BGRT instead. both kexec_load() and kexec_file_load() work and kdump works as well. [ mingo: Edited the changelog. ] Reported-by: Michael Weiser Tested-by: Michael Weiser Signed-off-by: Dave Young Cc: Ard Biesheuvel Cc: Borislav Petkov Cc: Eric W. Biederman Cc: H. Peter Anvin Cc: Linus Torvalds Cc: Peter Zijlstra Cc: Thomas Gleixner Cc: kexec@lists.infradead.org Cc: linux-efi@vger.kernel.org Link: https://lkml.kernel.org/r/20191204075233.GA10520@dhcp-128-65.nay.redhat.com Signed-off-by: Ingo Molnar --- arch/x86/platform/efi/quirks.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/arch/x86/platform/efi/quirks.c b/arch/x86/platform/efi/quirks.c index 7675cf754d909..f8f0220b6a665 100644 --- a/arch/x86/platform/efi/quirks.c +++ b/arch/x86/platform/efi/quirks.c @@ -260,10 +260,6 @@ void __init efi_arch_mem_reserve(phys_addr_t addr, u64 size) return; } - /* No need to reserve regions that will never be freed. */ - if (md.attribute & EFI_MEMORY_RUNTIME) - return; - size += addr % EFI_PAGE_SIZE; size = round_up(size, EFI_PAGE_SIZE); addr = round_down(addr, EFI_PAGE_SIZE); @@ -293,6 +289,8 @@ void __init efi_arch_mem_reserve(phys_addr_t addr, u64 size) early_memunmap(new, new_size); efi_memmap_install(new_phys, num_entries); + e820__range_update(addr, size, E820_TYPE_RAM, E820_TYPE_RESERVED); + e820__update_table(e820_table); } /* -- GitLab From 4bf2e385aa59c2fae5f880aa25cfd2b470109093 Mon Sep 17 00:00:00 2001 From: Curtis Malainey Date: Tue, 3 Dec 2019 09:30:07 -0800 Subject: [PATCH 0134/1437] ASoC: core: Init pcm runtime work early to avoid warnings There are cases where we fail before we reach soc_new_pcm which would init the workqueue. When we fail we attempt to flush the queue which generates warnings from the workqueue subsystem when we have not inited the queue. Solution is to use a proxy function to get around this issue. Signed-off-by: Curtis Malainey Reviewed-by: Takashi Iwai Link: https://lore.kernel.org/r/20191203173007.46504-1-cujomalainey@chromium.org Signed-off-by: Mark Brown --- include/sound/soc.h | 1 + sound/soc/soc-compress.c | 6 ++---- sound/soc/soc-core.c | 10 ++++++++++ sound/soc/soc-pcm.c | 11 ++++------- 4 files changed, 17 insertions(+), 11 deletions(-) diff --git a/include/sound/soc.h b/include/sound/soc.h index c28a1ed5e8df9..2628967998264 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -1150,6 +1150,7 @@ struct snd_soc_pcm_runtime { unsigned int num_codecs; struct delayed_work delayed_work; + void (*close_delayed_work_func)(struct snd_soc_pcm_runtime *rtd); #ifdef CONFIG_DEBUG_FS struct dentry *debugfs_dpcm_root; #endif diff --git a/sound/soc/soc-compress.c b/sound/soc/soc-compress.c index 61f230324164d..6615ef64c7f55 100644 --- a/sound/soc/soc-compress.c +++ b/sound/soc/soc-compress.c @@ -214,10 +214,8 @@ be_err: * This is to ensure there are no pops or clicks in between any music tracks * due to DAPM power cycling. */ -static void close_delayed_work(struct work_struct *work) +static void close_delayed_work(struct snd_soc_pcm_runtime *rtd) { - struct snd_soc_pcm_runtime *rtd = - container_of(work, struct snd_soc_pcm_runtime, delayed_work.work); struct snd_soc_dai *codec_dai = rtd->codec_dai; mutex_lock_nested(&rtd->card->pcm_mutex, rtd->card->pcm_subclass); @@ -929,7 +927,7 @@ int snd_soc_new_compress(struct snd_soc_pcm_runtime *rtd, int num) } /* DAPM dai link stream work */ - INIT_DELAYED_WORK(&rtd->delayed_work, close_delayed_work); + rtd->close_delayed_work_func = close_delayed_work; rtd->compr = compr; compr->private_data = rtd; diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 062653ab03a37..0e2e628302f1d 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -435,6 +435,15 @@ static void soc_free_pcm_runtime(struct snd_soc_pcm_runtime *rtd) device_unregister(rtd->dev); } +static void close_delayed_work(struct work_struct *work) { + struct snd_soc_pcm_runtime *rtd = + container_of(work, struct snd_soc_pcm_runtime, + delayed_work.work); + + if (rtd->close_delayed_work_func) + rtd->close_delayed_work_func(rtd); +} + static struct snd_soc_pcm_runtime *soc_new_pcm_runtime( struct snd_soc_card *card, struct snd_soc_dai_link *dai_link) { @@ -470,6 +479,7 @@ static struct snd_soc_pcm_runtime *soc_new_pcm_runtime( rtd->dev = dev; dev_set_drvdata(dev, rtd); + INIT_DELAYED_WORK(&rtd->delayed_work, close_delayed_work); /* * for rtd->codec_dais diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index 01eb8700c3de5..b78f6ff2b1d3f 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -637,10 +637,8 @@ out: * This is to ensure there are no pops or clicks in between any music tracks * due to DAPM power cycling. */ -static void close_delayed_work(struct work_struct *work) +static void close_delayed_work(struct snd_soc_pcm_runtime *rtd) { - struct snd_soc_pcm_runtime *rtd = - container_of(work, struct snd_soc_pcm_runtime, delayed_work.work); struct snd_soc_dai *codec_dai = rtd->codec_dais[0]; mutex_lock_nested(&rtd->card->pcm_mutex, rtd->card->pcm_subclass); @@ -660,7 +658,7 @@ static void close_delayed_work(struct work_struct *work) mutex_unlock(&rtd->card->pcm_mutex); } -static void codec2codec_close_delayed_work(struct work_struct *work) +static void codec2codec_close_delayed_work(struct snd_soc_pcm_runtime *rtd) { /* * Currently nothing to do for c2c links @@ -2974,10 +2972,9 @@ int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num) /* DAPM dai link stream work */ if (rtd->dai_link->params) - INIT_DELAYED_WORK(&rtd->delayed_work, - codec2codec_close_delayed_work); + rtd->close_delayed_work_func = codec2codec_close_delayed_work; else - INIT_DELAYED_WORK(&rtd->delayed_work, close_delayed_work); + rtd->close_delayed_work_func = close_delayed_work; pcm->nonatomic = rtd->dai_link->nonatomic; rtd->pcm = pcm; -- GitLab From 7eccc05c7101f34cc36afe9405d15de6d4099fb4 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 3 Dec 2019 23:14:42 +0100 Subject: [PATCH 0135/1437] ASoC: Intel: bytcr_rt5640: Update quirk for Teclast X89 When the Teclast X89 quirk was added we did not have jack-detection support yet. Note the over-current detection limit is set to 2mA instead of the usual 1.5mA because this tablet tends to give false-positive button-presses when it is set to 1.5mA. Signed-off-by: Hans de Goede Acked-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20191203221442.2657-1-hdegoede@redhat.com Signed-off-by: Mark Brown --- sound/soc/intel/boards/bytcr_rt5640.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/sound/soc/intel/boards/bytcr_rt5640.c b/sound/soc/intel/boards/bytcr_rt5640.c index 9c1aa4ec9cba4..cb511ea3b7710 100644 --- a/sound/soc/intel/boards/bytcr_rt5640.c +++ b/sound/soc/intel/boards/bytcr_rt5640.c @@ -705,13 +705,17 @@ static const struct dmi_system_id byt_rt5640_quirk_table[] = { BYT_RT5640_MCLK_EN), }, { + /* Teclast X89 */ .matches = { DMI_MATCH(DMI_BOARD_VENDOR, "TECLAST"), DMI_MATCH(DMI_BOARD_NAME, "tPAD"), }, .driver_data = (void *)(BYT_RT5640_IN3_MAP | - BYT_RT5640_MCLK_EN | - BYT_RT5640_SSP0_AIF1), + BYT_RT5640_JD_SRC_JD1_IN4P | + BYT_RT5640_OVCD_TH_2000UA | + BYT_RT5640_OVCD_SF_1P0 | + BYT_RT5640_SSP0_AIF1 | + BYT_RT5640_MCLK_EN), }, { /* Toshiba Satellite Click Mini L9W-B */ .matches = { -- GitLab From f3c7f7b636ef5061906aa119eccc2b8dbbaf7199 Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Tue, 3 Dec 2019 22:48:38 +0100 Subject: [PATCH 0136/1437] regulator: s5m8767: Fix a warning message Axe a duplicated word ("property") in a warning message. Signed-off-by: Christophe JAILLET Reviewed-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20191203214838.9680-1-christophe.jaillet@wanadoo.fr Signed-off-by: Mark Brown --- drivers/regulator/s5m8767.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/regulator/s5m8767.c b/drivers/regulator/s5m8767.c index bdc07739e9a22..12d6b8d2e97e8 100644 --- a/drivers/regulator/s5m8767.c +++ b/drivers/regulator/s5m8767.c @@ -588,7 +588,7 @@ static int s5m8767_pmic_dt_parse_pdata(struct platform_device *pdev, if (of_property_read_u32(reg_np, "op_mode", &rmode->mode)) { dev_warn(iodev->dev, - "no op_mode property property at %pOF\n", + "no op_mode property at %pOF\n", reg_np); rmode->mode = S5M8767_OPMODE_NORMAL_MODE; -- GitLab From 5e9c5236b7b86779b53b762f7e66240c3f18314b Mon Sep 17 00:00:00 2001 From: Huanpeng Xin Date: Wed, 4 Dec 2019 15:13:59 +0800 Subject: [PATCH 0137/1437] spi: sprd: Fix the incorrect SPI register The original code used an incorrect SPI register to initialize the SPI controller in sprd_spi_init_hw(), thus fix it. Fixes: e7d973a31c24 ("spi: sprd: Add SPI driver for Spreadtrum SC9860") Signed-off-by: Huanpeng Xin Signed-off-by: Baolin Wang Link: https://lore.kernel.org/r/b4f7f89ec0fdc595335687bfbd9f962213bc4a1d.1575443510.git.baolin.wang7@gmail.com Signed-off-by: Mark Brown --- drivers/spi/spi-sprd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/spi/spi-sprd.c b/drivers/spi/spi-sprd.c index 2ee1feb416812..6678f1cbc5660 100644 --- a/drivers/spi/spi-sprd.c +++ b/drivers/spi/spi-sprd.c @@ -678,7 +678,7 @@ static int sprd_spi_init_hw(struct sprd_spi *ss, struct spi_transfer *t) if (d->unit != SPI_DELAY_UNIT_SCK) return -EINVAL; - val = readl_relaxed(ss->base + SPRD_SPI_CTL7); + val = readl_relaxed(ss->base + SPRD_SPI_CTL0); val &= ~(SPRD_SPI_SCK_REV | SPRD_SPI_NG_TX | SPRD_SPI_NG_RX); /* Set default chip selection, clock phase and clock polarity */ val |= ss->hw_mode & SPI_CPHA ? SPRD_SPI_NG_RX : SPRD_SPI_NG_TX; -- GitLab From 926d1650176448d7684b991fbe1a5b1a8289e97c Mon Sep 17 00:00:00 2001 From: Gao Xiang Date: Sun, 1 Dec 2019 16:01:09 +0800 Subject: [PATCH 0138/1437] erofs: zero out when listxattr is called with no xattr As David reported [1], ENODATA returns when attempting to modify files by using EROFS as an overlayfs lower layer. The root cause is that listxattr could return unexpected -ENODATA by mistake for inodes without xattr. That breaks listxattr return value convention and it can cause copy up failure when used with overlayfs. Resolve by zeroing out if no xattr is found for listxattr. [1] https://lore.kernel.org/r/CAEvUa7nxnby+rxK-KRMA46=exeOMApkDMAV08AjMkkPnTPV4CQ@mail.gmail.com Link: https://lore.kernel.org/r/20191201084040.29275-1-hsiangkao@aol.com Fixes: cadf1ccf1b00 ("staging: erofs: add error handling for xattr submodule") Cc: # 4.19+ Reviewed-by: Chao Yu Signed-off-by: Gao Xiang --- fs/erofs/xattr.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fs/erofs/xattr.c b/fs/erofs/xattr.c index a13a78725c571..b766c3ee5fa8c 100644 --- a/fs/erofs/xattr.c +++ b/fs/erofs/xattr.c @@ -649,6 +649,8 @@ ssize_t erofs_listxattr(struct dentry *dentry, struct listxattr_iter it; ret = init_inode_xattrs(d_inode(dentry)); + if (ret == -ENOATTR) + return 0; if (ret) return ret; -- GitLab From ae87405fb511d6220ce86b9a60807fef92e1a934 Mon Sep 17 00:00:00 2001 From: Ravi Bangoria Date: Thu, 14 Nov 2019 18:52:11 +0530 Subject: [PATCH 0139/1437] perf report/top TUI: Replace pr_err() with ui__error() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit pr_err() in TUI mode does not print anyting on the screen and just quits. Replace such pr_err() with ui__error(). Before: $ perf report -s + $ After: $ perf report -s + ┌─Error:────────────────┐ │Invalid --sort key: `+'│ │ │ │Press any key... │ └───────────────────────┘ Signed-off-by: Ravi Bangoria Acked-by: Jiri Olsa Tested-by: Arnaldo Carvalho de Melo Cc: Alexander Shishkin Cc: Andi Kleen Cc: Jin Yao Cc: Kan Liang Cc: Mark Rutland Cc: Namhyung Kim Link: http://lore.kernel.org/lkml/20191114132213.5419-2-ravi.bangoria@linux.ibm.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/sort.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c index 345b5ccc90f68..106d795574ba1 100644 --- a/tools/perf/util/sort.c +++ b/tools/perf/util/sort.c @@ -2681,12 +2681,12 @@ static int setup_sort_list(struct perf_hpp_list *list, char *str, ret = sort_dimension__add(list, tok, evlist, level); if (ret == -EINVAL) { if (!cacheline_size() && !strncasecmp(tok, "dcacheline", strlen(tok))) - pr_err("The \"dcacheline\" --sort key needs to know the cacheline size and it couldn't be determined on this system"); + ui__error("The \"dcacheline\" --sort key needs to know the cacheline size and it couldn't be determined on this system"); else - pr_err("Invalid --sort key: `%s'", tok); + ui__error("Invalid --sort key: `%s'", tok); break; } else if (ret == -ESRCH) { - pr_err("Unknown --sort key: `%s'", tok); + ui__error("Unknown --sort key: `%s'", tok); break; } } @@ -2743,7 +2743,7 @@ static int setup_sort_order(struct evlist *evlist) return 0; if (sort_order[1] == '\0') { - pr_err("Invalid --sort key: `+'"); + ui__error("Invalid --sort key: `+'"); return -EINVAL; } @@ -3034,7 +3034,7 @@ static int __setup_output_field(void) strp++; if (!strlen(strp)) { - pr_err("Invalid --fields key: `+'"); + ui__error("Invalid --fields key: `+'"); goto out; } -- GitLab From aa6b3c99236b49a3e842d7272efa2529f15f7d8a Mon Sep 17 00:00:00 2001 From: Ravi Bangoria Date: Thu, 14 Nov 2019 18:52:12 +0530 Subject: [PATCH 0140/1437] perf report: Make -F more strict like -s Currently -F allows branch-mode / mem-mode fields with -F even when perf report is not running in that mode. Don't allow that. Suggested-by: Arnaldo Carvalho de Melo Signed-off-by: Ravi Bangoria Acked-by: Jiri Olsa Cc: Alexander Shishkin Cc: Andi Kleen Cc: Jin Yao Cc: Kan Liang Cc: Mark Rutland Cc: Namhyung Kim Link: http://lore.kernel.org/lkml/20191114132213.5419-3-ravi.bangoria@linux.ibm.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/sort.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c index 106d795574ba1..9fcba2872130b 100644 --- a/tools/perf/util/sort.c +++ b/tools/perf/util/sort.c @@ -2959,6 +2959,9 @@ int output_field_add(struct perf_hpp_list *list, char *tok) if (strncasecmp(tok, sd->name, strlen(tok))) continue; + if (sort__mode != SORT_MODE__MEMORY) + return -EINVAL; + return __sort_dimension__add_output(list, sd); } @@ -2968,6 +2971,9 @@ int output_field_add(struct perf_hpp_list *list, char *tok) if (strncasecmp(tok, sd->name, strlen(tok))) continue; + if (sort__mode != SORT_MODE__BRANCH) + return -EINVAL; + return __sort_dimension__add_output(list, sd); } -- GitLab From bb30acae4c4dacfa2622387c5ad5563246810583 Mon Sep 17 00:00:00 2001 From: Ravi Bangoria Date: Thu, 14 Nov 2019 18:52:13 +0530 Subject: [PATCH 0141/1437] perf report: Bail out --mem-mode if mem info is not available If perf.data is recorded without -d, don't allow user to use --mem-mode with 'perf report'. symbol_daddr and phys_daddr can be recorded separately and may be present in the perf.data but at the report time they are associated with mem-mode fields and thus this restriction applies to them as well. Before: $ perf record ls $ perf report --mem-mode --stdio # Overhead Local Weight Memory access Symbol # ........ ............ ............. ....................... 55.56% 0 N/A [k] 0xffffffff81a00ae7 After: $ perf report --mem-mode --stdio Error: Selected --mem-mode but no mem data. Did you call perf record without -d? Suggested-by: Arnaldo Carvalho de Melo Signed-off-by: Ravi Bangoria Acked-by: Jiri Olsa Tested-by: Arnaldo Carvalho de Melo Cc: Alexander Shishkin Cc: Andi Kleen Cc: Jin Yao Cc: Kan Liang Cc: Mark Rutland Cc: Namhyung Kim Link: http://lore.kernel.org/lkml/20191114132213.5419-4-ravi.bangoria@linux.ibm.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-report.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index 830d563de8898..387311c672640 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -388,6 +388,14 @@ static int report__setup_sample_type(struct report *rep) } } + if (sort__mode == SORT_MODE__MEMORY) { + if (!is_pipe && !(sample_type & PERF_SAMPLE_DATA_SRC)) { + ui__error("Selected --mem-mode but no mem data. " + "Did you call perf record without -d?\n"); + return -1; + } + } + if (symbol_conf.use_callchain || symbol_conf.cumulate_callchain) { if ((sample_type & PERF_SAMPLE_REGS_USER) && (sample_type & PERF_SAMPLE_STACK_USER)) { -- GitLab From 29f6eeca0e14b301d9c03a3164b852c318d6348a Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Wed, 4 Dec 2019 14:08:00 +0200 Subject: [PATCH 0142/1437] perf inject: Fix processing of ID index for injected instruction tracing The ID index event is used when decoding, but can result in the following error: $ perf record --aux-sample -e '{intel_pt//,branch-misses}:u' ls $ perf inject -i perf.data -o perf.data.inj --itrace=be $ perf script -i perf.data.inj 0x1020 [0x410]: failed to process type: 69 [No such file or directory] Fix by having 'perf inject' drop the ID index event. Fixes: c0a6de06c446 ("perf record: Add support for AUX area sampling") Signed-off-by: Adrian Hunter Cc: Jiri Olsa Link: http://lore.kernel.org/lkml/20191204120800.8138-1-adrian.hunter@intel.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-inject.c | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/tools/perf/builtin-inject.c b/tools/perf/builtin-inject.c index 9664a72a089da..7e124a7b8bfdc 100644 --- a/tools/perf/builtin-inject.c +++ b/tools/perf/builtin-inject.c @@ -403,17 +403,6 @@ static int perf_event__repipe_tracing_data(struct perf_session *session, return err; } -static int perf_event__repipe_id_index(struct perf_session *session, - union perf_event *event) -{ - int err; - - perf_event__repipe_synth(session->tool, event); - err = perf_event__process_id_index(session, event); - - return err; -} - static int dso__read_build_id(struct dso *dso) { if (dso->has_build_id) @@ -651,7 +640,7 @@ static int __cmd_inject(struct perf_inject *inject) inject->tool.comm = perf_event__repipe_comm; inject->tool.namespaces = perf_event__repipe_namespaces; inject->tool.exit = perf_event__repipe_exit; - inject->tool.id_index = perf_event__repipe_id_index; + inject->tool.id_index = perf_event__process_id_index; inject->tool.auxtrace_info = perf_event__process_auxtrace_info; inject->tool.auxtrace = perf_event__process_auxtrace; inject->tool.aux = perf_event__drop_aux; -- GitLab From 0b3fca6ad3283866e9d2376554b3e4fbf23bfd5d Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Wed, 4 Dec 2019 12:49:43 -0300 Subject: [PATCH 0143/1437] tools headers UAPI: Sync drm/i915_drm.h with the kernel sources To pick the change in: a0e047156cde ("drm/i915/gem: Make context persistence optional") 9cd20ef7803c ("drm/i915/perf: allow holding preemption on filtered ctx") 7831e9a965ea ("drm/i915/perf: Allow dynamic reconfiguration of the OA stream") 4f6ccc74a85c ("drm/i915: add support for perf configuration queries") b8d49f28aa03 ("drm/i915/perf: introduce a versioning of the i915-perf uapi") 601734f7aabd ("drm/i915/tgl: s/ss/eu fuse reading support") That don't result in any changes in tooling, just silences this perf build warning: Warning: Kernel ABI header at 'tools/include/uapi/drm/i915_drm.h' differs from latest version at 'include/uapi/drm/i915_drm.h' diff -u tools/include/uapi/drm/i915_drm.h include/uapi/drm/i915_drm.h Cc: Adrian Hunter Cc: Chris Wilson Cc: Daniele Ceraolo Spurio Cc: Jiri Olsa Cc: Lionel Landwerlin Cc: Namhyung Kim Link: https://lkml.kernel.org/n/tip-qwzjrgwj55y3g6rjdf9spkpr@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/include/uapi/drm/i915_drm.h | 128 +++++++++++++++++++++++++++++- 1 file changed, 125 insertions(+), 3 deletions(-) diff --git a/tools/include/uapi/drm/i915_drm.h b/tools/include/uapi/drm/i915_drm.h index 469dc512cca35..5400d7e057f14 100644 --- a/tools/include/uapi/drm/i915_drm.h +++ b/tools/include/uapi/drm/i915_drm.h @@ -611,6 +611,13 @@ typedef struct drm_i915_irq_wait { * See I915_EXEC_FENCE_OUT and I915_EXEC_FENCE_SUBMIT. */ #define I915_PARAM_HAS_EXEC_SUBMIT_FENCE 53 + +/* + * Revision of the i915-perf uAPI. The value returned helps determine what + * i915-perf features are available. See drm_i915_perf_property_id. + */ +#define I915_PARAM_PERF_REVISION 54 + /* Must be kept compact -- no holes and well documented */ typedef struct drm_i915_getparam { @@ -1565,6 +1572,21 @@ struct drm_i915_gem_context_param { * i915_context_engines_bond (I915_CONTEXT_ENGINES_EXT_BOND) */ #define I915_CONTEXT_PARAM_ENGINES 0xa + +/* + * I915_CONTEXT_PARAM_PERSISTENCE: + * + * Allow the context and active rendering to survive the process until + * completion. Persistence allows fire-and-forget clients to queue up a + * bunch of work, hand the output over to a display server and then quit. + * If the context is marked as not persistent, upon closing (either via + * an explicit DRM_I915_GEM_CONTEXT_DESTROY or implicitly from file closure + * or process termination), the context and any outstanding requests will be + * cancelled (and exported fences for cancelled requests marked as -EIO). + * + * By default, new contexts allow persistence. + */ +#define I915_CONTEXT_PARAM_PERSISTENCE 0xb /* Must be kept compact -- no holes and well documented */ __u64 value; @@ -1844,23 +1866,31 @@ enum drm_i915_perf_property_id { * Open the stream for a specific context handle (as used with * execbuffer2). A stream opened for a specific context this way * won't typically require root privileges. + * + * This property is available in perf revision 1. */ DRM_I915_PERF_PROP_CTX_HANDLE = 1, /** * A value of 1 requests the inclusion of raw OA unit reports as * part of stream samples. + * + * This property is available in perf revision 1. */ DRM_I915_PERF_PROP_SAMPLE_OA, /** * The value specifies which set of OA unit metrics should be * be configured, defining the contents of any OA unit reports. + * + * This property is available in perf revision 1. */ DRM_I915_PERF_PROP_OA_METRICS_SET, /** * The value specifies the size and layout of OA unit reports. + * + * This property is available in perf revision 1. */ DRM_I915_PERF_PROP_OA_FORMAT, @@ -1870,9 +1900,22 @@ enum drm_i915_perf_property_id { * from this exponent as follows: * * 80ns * 2^(period_exponent + 1) + * + * This property is available in perf revision 1. */ DRM_I915_PERF_PROP_OA_EXPONENT, + /** + * Specifying this property is only valid when specify a context to + * filter with DRM_I915_PERF_PROP_CTX_HANDLE. Specifying this property + * will hold preemption of the particular context we want to gather + * performance data about. The execbuf2 submissions must include a + * drm_i915_gem_execbuffer_ext_perf parameter for this to apply. + * + * This property is available in perf revision 3. + */ + DRM_I915_PERF_PROP_HOLD_PREEMPTION, + DRM_I915_PERF_PROP_MAX /* non-ABI */ }; @@ -1901,6 +1944,8 @@ struct drm_i915_perf_open_param { * to close and re-open a stream with the same configuration. * * It's undefined whether any pending data for the stream will be lost. + * + * This ioctl is available in perf revision 1. */ #define I915_PERF_IOCTL_ENABLE _IO('i', 0x0) @@ -1908,9 +1953,24 @@ struct drm_i915_perf_open_param { * Disable data capture for a stream. * * It is an error to try and read a stream that is disabled. + * + * This ioctl is available in perf revision 1. */ #define I915_PERF_IOCTL_DISABLE _IO('i', 0x1) +/** + * Change metrics_set captured by a stream. + * + * If the stream is bound to a specific context, the configuration change + * will performed inline with that context such that it takes effect before + * the next execbuf submission. + * + * Returns the previously bound metrics set id, or a negative error code. + * + * This ioctl is available in perf revision 2. + */ +#define I915_PERF_IOCTL_CONFIG _IO('i', 0x2) + /** * Common to all i915 perf records */ @@ -1984,6 +2044,7 @@ struct drm_i915_query_item { __u64 query_id; #define DRM_I915_QUERY_TOPOLOGY_INFO 1 #define DRM_I915_QUERY_ENGINE_INFO 2 +#define DRM_I915_QUERY_PERF_CONFIG 3 /* Must be kept compact -- no holes and well documented */ /* @@ -1995,9 +2056,18 @@ struct drm_i915_query_item { __s32 length; /* - * Unused for now. Must be cleared to zero. + * When query_id == DRM_I915_QUERY_TOPOLOGY_INFO, must be 0. + * + * When query_id == DRM_I915_QUERY_PERF_CONFIG, must be one of the + * following : + * - DRM_I915_QUERY_PERF_CONFIG_LIST + * - DRM_I915_QUERY_PERF_CONFIG_DATA_FOR_UUID + * - DRM_I915_QUERY_PERF_CONFIG_FOR_UUID */ __u32 flags; +#define DRM_I915_QUERY_PERF_CONFIG_LIST 1 +#define DRM_I915_QUERY_PERF_CONFIG_DATA_FOR_UUID 2 +#define DRM_I915_QUERY_PERF_CONFIG_DATA_FOR_ID 3 /* * Data will be written at the location pointed by data_ptr when the @@ -2033,8 +2103,10 @@ struct drm_i915_query { * (data[X / 8] >> (X % 8)) & 1 * * - the subslice mask for each slice with one bit per subslice telling - * whether a subslice is available. The availability of subslice Y in slice - * X can be queried with the following formula : + * whether a subslice is available. Gen12 has dual-subslices, which are + * similar to two gen11 subslices. For gen12, this array represents dual- + * subslices. The availability of subslice Y in slice X can be queried + * with the following formula : * * (data[subslice_offset + * X * subslice_stride + @@ -2123,6 +2195,56 @@ struct drm_i915_query_engine_info { struct drm_i915_engine_info engines[]; }; +/* + * Data written by the kernel with query DRM_I915_QUERY_PERF_CONFIG. + */ +struct drm_i915_query_perf_config { + union { + /* + * When query_item.flags == DRM_I915_QUERY_PERF_CONFIG_LIST, i915 sets + * this fields to the number of configurations available. + */ + __u64 n_configs; + + /* + * When query_id == DRM_I915_QUERY_PERF_CONFIG_DATA_FOR_ID, + * i915 will use the value in this field as configuration + * identifier to decide what data to write into config_ptr. + */ + __u64 config; + + /* + * When query_id == DRM_I915_QUERY_PERF_CONFIG_DATA_FOR_UUID, + * i915 will use the value in this field as configuration + * identifier to decide what data to write into config_ptr. + * + * String formatted like "%08x-%04x-%04x-%04x-%012x" + */ + char uuid[36]; + }; + + /* + * Unused for now. Must be cleared to zero. + */ + __u32 flags; + + /* + * When query_item.flags == DRM_I915_QUERY_PERF_CONFIG_LIST, i915 will + * write an array of __u64 of configuration identifiers. + * + * When query_item.flags == DRM_I915_QUERY_PERF_CONFIG_DATA, i915 will + * write a struct drm_i915_perf_oa_config. If the following fields of + * drm_i915_perf_oa_config are set not set to 0, i915 will write into + * the associated pointers the values of submitted when the + * configuration was created : + * + * - n_mux_regs + * - n_boolean_regs + * - n_flex_regs + */ + __u8 data[]; +}; + #if defined(__cplusplus) } #endif -- GitLab From fd9bee5e24141d00e23b66d1b51bc759efa7e3fe Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Wed, 4 Dec 2019 12:53:16 -0300 Subject: [PATCH 0144/1437] tools headers UAPI: Update tools's copy of drm.h headers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Picking the changes from: 2093dea3def9 ("drm/syncobj: extend syncobj query ability v3") Which doesn't affect tooling, just silences this perf build warning: Warning: Kernel ABI header at 'tools/include/uapi/drm/drm.h' differs from latest version at 'include/uapi/drm/drm.h' diff -u tools/include/uapi/drm/drm.h include/uapi/drm/drm.h Cc: Adrian Hunter Cc: Christian König Cc: Chunming Zhou Cc: Jiri Olsa Cc: Namhyung Kim Link: https://lkml.kernel.org/n/tip-t1xqmjffo4rxdw395dsnu34j@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/include/uapi/drm/drm.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tools/include/uapi/drm/drm.h b/tools/include/uapi/drm/drm.h index 8a5b2f8f8eb98..868bf7996c0f8 100644 --- a/tools/include/uapi/drm/drm.h +++ b/tools/include/uapi/drm/drm.h @@ -778,11 +778,12 @@ struct drm_syncobj_array { __u32 pad; }; +#define DRM_SYNCOBJ_QUERY_FLAGS_LAST_SUBMITTED (1 << 0) /* last available point on timeline syncobj */ struct drm_syncobj_timeline_array { __u64 handles; __u64 points; __u32 count_handles; - __u32 pad; + __u32 flags; }; -- GitLab From 474e559567fa631dea8fb8407ab1b6090c903755 Mon Sep 17 00:00:00 2001 From: Hou Tao Date: Tue, 3 Dec 2019 19:42:58 +0800 Subject: [PATCH 0145/1437] dm btree: increase rebalance threshold in __rebalance2() We got the following warnings from thin_check during thin-pool setup: $ thin_check /dev/vdb examining superblock examining devices tree missing devices: [1, 84] too few entries in btree_node: 41, expected at least 42 (block 138, max_entries = 126) examining mapping tree The phenomenon is the number of entries in one node of details_info tree is less than (max_entries / 3). And it can be easily reproduced by the following procedures: $ new a thin pool $ presume the max entries of details_info tree is 126 $ new 127 thin devices (e.g. 1~127) to make the root node being full and then split $ remove the first 43 (e.g. 1~43) thin devices to make the children reblance repeatedly $ stop the thin pool $ thin_check The root cause is that the B-tree removal procedure in __rebalance2() doesn't guarantee the invariance: the minimal number of entries in non-root node should be >= (max_entries / 3). Simply fix the problem by increasing the rebalance threshold to make sure the number of entries in each child will be greater than or equal to (max_entries / 3 + 1), so no matter which child is used for removal, the number will still be valid. Cc: stable@vger.kernel.org Signed-off-by: Hou Tao Acked-by: Joe Thornber Signed-off-by: Mike Snitzer --- drivers/md/persistent-data/dm-btree-remove.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/md/persistent-data/dm-btree-remove.c b/drivers/md/persistent-data/dm-btree-remove.c index 21ea537bd55e9..eff04fa23dfad 100644 --- a/drivers/md/persistent-data/dm-btree-remove.c +++ b/drivers/md/persistent-data/dm-btree-remove.c @@ -203,7 +203,13 @@ static void __rebalance2(struct dm_btree_info *info, struct btree_node *parent, struct btree_node *right = r->n; uint32_t nr_left = le32_to_cpu(left->header.nr_entries); uint32_t nr_right = le32_to_cpu(right->header.nr_entries); - unsigned threshold = 2 * merge_threshold(left) + 1; + /* + * Ensure the number of entries in each child will be greater + * than or equal to (max_entries / 3 + 1), so no matter which + * child is used for removal, the number will still be not + * less than (max_entries / 3). + */ + unsigned int threshold = 2 * (merge_threshold(left) + 1); if (nr_left + nr_right < threshold) { /* -- GitLab From e6a505f3f9fae572fb3ab3bc486e755ac9cef32c Mon Sep 17 00:00:00 2001 From: Nikos Tsironis Date: Wed, 4 Dec 2019 16:06:52 +0200 Subject: [PATCH 0146/1437] dm clone metadata: Track exact changes per transaction Extend struct dirty_map with a second bitmap which tracks the exact regions that were hydrated during the current metadata transaction. Moreover, fix __flush_dmap() to only commit the metadata of the regions that were hydrated during the current transaction. This is required by the following commits to fix a data corruption bug. Fixes: 7431b7835f55 ("dm: add clone target") Cc: stable@vger.kernel.org # v5.4+ Signed-off-by: Nikos Tsironis Signed-off-by: Mike Snitzer --- drivers/md/dm-clone-metadata.c | 90 +++++++++++++++++++++++----------- 1 file changed, 62 insertions(+), 28 deletions(-) diff --git a/drivers/md/dm-clone-metadata.c b/drivers/md/dm-clone-metadata.c index 08c552e5e41b2..ee870a425ab8b 100644 --- a/drivers/md/dm-clone-metadata.c +++ b/drivers/md/dm-clone-metadata.c @@ -67,23 +67,34 @@ struct superblock_disk { * To save constantly doing look ups on disk we keep an in core copy of the * on-disk bitmap, the region_map. * - * To further reduce metadata I/O overhead we use a second bitmap, the dmap - * (dirty bitmap), which tracks the dirty words, i.e. longs, of the region_map. + * In order to track which regions are hydrated during a metadata transaction, + * we use a second set of bitmaps, the dmap (dirty bitmap), which includes two + * bitmaps, namely dirty_regions and dirty_words. The dirty_regions bitmap + * tracks the regions that got hydrated during the current metadata + * transaction. The dirty_words bitmap tracks the dirty words, i.e. longs, of + * the dirty_regions bitmap. + * + * This allows us to precisely track the regions that were hydrated during the + * current metadata transaction and update the metadata accordingly, when we + * commit the current transaction. This is important because dm-clone should + * only commit the metadata of regions that were properly flushed to the + * destination device beforehand. Otherwise, in case of a crash, we could end + * up with a corrupted dm-clone device. * * When a region finishes hydrating dm-clone calls * dm_clone_set_region_hydrated(), or for discard requests * dm_clone_cond_set_range(), which sets the corresponding bits in region_map * and dmap. * - * During a metadata commit we scan the dmap for dirty region_map words (longs) - * and update accordingly the on-disk metadata. Thus, we don't have to flush to - * disk the whole region_map. We can just flush the dirty region_map words. + * During a metadata commit we scan dmap->dirty_words and dmap->dirty_regions + * and update the on-disk metadata accordingly. Thus, we don't have to flush to + * disk the whole region_map. We can just flush the dirty region_map bits. * - * We use a dirty bitmap, which is smaller than the original region_map, to - * reduce the amount of memory accesses during a metadata commit. As dm-bitset - * accesses the on-disk bitmap in 64-bit word granularity, there is no - * significant benefit in tracking the dirty region_map bits with a smaller - * granularity. + * We use the helper dmap->dirty_words bitmap, which is smaller than the + * original region_map, to reduce the amount of memory accesses during a + * metadata commit. Moreover, as dm-bitset also accesses the on-disk bitmap in + * 64-bit word granularity, the dirty_words bitmap helps us avoid useless disk + * accesses. * * We could update directly the on-disk bitmap, when dm-clone calls either * dm_clone_set_region_hydrated() or dm_clone_cond_set_range(), buts this @@ -92,12 +103,13 @@ struct superblock_disk { * e.g., in a hooked overwrite bio's completion routine, and further reduce the * I/O completion latency. * - * We maintain two dirty bitmaps. During a metadata commit we atomically swap - * the currently used dmap with the unused one. This allows the metadata update - * functions to run concurrently with an ongoing commit. + * We maintain two dirty bitmap sets. During a metadata commit we atomically + * swap the currently used dmap with the unused one. This allows the metadata + * update functions to run concurrently with an ongoing commit. */ struct dirty_map { unsigned long *dirty_words; + unsigned long *dirty_regions; unsigned int changed; }; @@ -461,22 +473,40 @@ static size_t bitmap_size(unsigned long nr_bits) return BITS_TO_LONGS(nr_bits) * sizeof(long); } -static int dirty_map_init(struct dm_clone_metadata *cmd) +static int __dirty_map_init(struct dirty_map *dmap, unsigned long nr_words, + unsigned long nr_regions) { - cmd->dmap[0].changed = 0; - cmd->dmap[0].dirty_words = kvzalloc(bitmap_size(cmd->nr_words), GFP_KERNEL); + dmap->changed = 0; - if (!cmd->dmap[0].dirty_words) { - DMERR("Failed to allocate dirty bitmap"); + dmap->dirty_words = kvzalloc(bitmap_size(nr_words), GFP_KERNEL); + if (!dmap->dirty_words) + return -ENOMEM; + + dmap->dirty_regions = kvzalloc(bitmap_size(nr_regions), GFP_KERNEL); + if (!dmap->dirty_regions) { + kvfree(dmap->dirty_words); return -ENOMEM; } - cmd->dmap[1].changed = 0; - cmd->dmap[1].dirty_words = kvzalloc(bitmap_size(cmd->nr_words), GFP_KERNEL); + return 0; +} - if (!cmd->dmap[1].dirty_words) { +static void __dirty_map_exit(struct dirty_map *dmap) +{ + kvfree(dmap->dirty_words); + kvfree(dmap->dirty_regions); +} + +static int dirty_map_init(struct dm_clone_metadata *cmd) +{ + if (__dirty_map_init(&cmd->dmap[0], cmd->nr_words, cmd->nr_regions)) { DMERR("Failed to allocate dirty bitmap"); - kvfree(cmd->dmap[0].dirty_words); + return -ENOMEM; + } + + if (__dirty_map_init(&cmd->dmap[1], cmd->nr_words, cmd->nr_regions)) { + DMERR("Failed to allocate dirty bitmap"); + __dirty_map_exit(&cmd->dmap[0]); return -ENOMEM; } @@ -487,8 +517,8 @@ static int dirty_map_init(struct dm_clone_metadata *cmd) static void dirty_map_exit(struct dm_clone_metadata *cmd) { - kvfree(cmd->dmap[0].dirty_words); - kvfree(cmd->dmap[1].dirty_words); + __dirty_map_exit(&cmd->dmap[0]); + __dirty_map_exit(&cmd->dmap[1]); } static int __load_bitset_in_core(struct dm_clone_metadata *cmd) @@ -633,21 +663,23 @@ unsigned long dm_clone_find_next_unhydrated_region(struct dm_clone_metadata *cmd return find_next_zero_bit(cmd->region_map, cmd->nr_regions, start); } -static int __update_metadata_word(struct dm_clone_metadata *cmd, unsigned long word) +static int __update_metadata_word(struct dm_clone_metadata *cmd, + unsigned long *dirty_regions, + unsigned long word) { int r; unsigned long index = word * BITS_PER_LONG; unsigned long max_index = min(cmd->nr_regions, (word + 1) * BITS_PER_LONG); while (index < max_index) { - if (test_bit(index, cmd->region_map)) { + if (test_bit(index, dirty_regions)) { r = dm_bitset_set_bit(&cmd->bitset_info, cmd->bitset_root, index, &cmd->bitset_root); - if (r) { DMERR("dm_bitset_set_bit failed"); return r; } + __clear_bit(index, dirty_regions); } index++; } @@ -721,7 +753,7 @@ static int __flush_dmap(struct dm_clone_metadata *cmd, struct dirty_map *dmap) if (word == cmd->nr_words) break; - r = __update_metadata_word(cmd, word); + r = __update_metadata_word(cmd, dmap->dirty_regions, word); if (r) return r; @@ -802,6 +834,7 @@ int dm_clone_set_region_hydrated(struct dm_clone_metadata *cmd, unsigned long re dmap = cmd->current_dmap; __set_bit(word, dmap->dirty_words); + __set_bit(region_nr, dmap->dirty_regions); __set_bit(region_nr, cmd->region_map); dmap->changed = 1; @@ -830,6 +863,7 @@ int dm_clone_cond_set_range(struct dm_clone_metadata *cmd, unsigned long start, if (!test_bit(region_nr, cmd->region_map)) { word = region_nr / BITS_PER_LONG; __set_bit(word, dmap->dirty_words); + __set_bit(region_nr, dmap->dirty_regions); __set_bit(region_nr, cmd->region_map); dmap->changed = 1; } -- GitLab From 8fdbfe8d1690e8a38d497d83a30607d0d90cc15a Mon Sep 17 00:00:00 2001 From: Nikos Tsironis Date: Wed, 4 Dec 2019 16:06:53 +0200 Subject: [PATCH 0147/1437] dm clone metadata: Use a two phase commit Split the metadata commit in two parts: 1. dm_clone_metadata_pre_commit(): Prepare the current transaction for committing. After this is called, all subsequent metadata updates, done through either dm_clone_set_region_hydrated() or dm_clone_cond_set_range(), will be part of the next transaction. 2. dm_clone_metadata_commit(): Actually commit the current transaction to disk and start a new transaction. This is required by the following commit. It allows dm-clone to flush the destination device after step (1) to ensure that all freshly hydrated regions, for which we are updating the metadata, are properly written to non-volatile storage and won't be lost in case of a crash. Fixes: 7431b7835f55 ("dm: add clone target") Cc: stable@vger.kernel.org # v5.4+ Signed-off-by: Nikos Tsironis Signed-off-by: Mike Snitzer --- drivers/md/dm-clone-metadata.c | 46 +++++++++++++++++++++++++++------- drivers/md/dm-clone-metadata.h | 17 +++++++++++++ drivers/md/dm-clone-target.c | 7 +++++- 3 files changed, 60 insertions(+), 10 deletions(-) diff --git a/drivers/md/dm-clone-metadata.c b/drivers/md/dm-clone-metadata.c index ee870a425ab8b..c05b121104561 100644 --- a/drivers/md/dm-clone-metadata.c +++ b/drivers/md/dm-clone-metadata.c @@ -127,6 +127,9 @@ struct dm_clone_metadata { struct dirty_map dmap[2]; struct dirty_map *current_dmap; + /* Protected by lock */ + struct dirty_map *committing_dmap; + /* * In core copy of the on-disk bitmap to save constantly doing look ups * on disk. @@ -511,6 +514,7 @@ static int dirty_map_init(struct dm_clone_metadata *cmd) } cmd->current_dmap = &cmd->dmap[0]; + cmd->committing_dmap = NULL; return 0; } @@ -775,15 +779,17 @@ static int __flush_dmap(struct dm_clone_metadata *cmd, struct dirty_map *dmap) return 0; } -int dm_clone_metadata_commit(struct dm_clone_metadata *cmd) +int dm_clone_metadata_pre_commit(struct dm_clone_metadata *cmd) { - int r = -EPERM; + int r = 0; struct dirty_map *dmap, *next_dmap; down_write(&cmd->lock); - if (cmd->fail_io || dm_bm_is_read_only(cmd->bm)) + if (cmd->fail_io || dm_bm_is_read_only(cmd->bm)) { + r = -EPERM; goto out; + } /* Get current dirty bitmap */ dmap = cmd->current_dmap; @@ -795,7 +801,7 @@ int dm_clone_metadata_commit(struct dm_clone_metadata *cmd) * The last commit failed, so we don't have a clean dirty-bitmap to * use. */ - if (WARN_ON(next_dmap->changed)) { + if (WARN_ON(next_dmap->changed || cmd->committing_dmap)) { r = -EINVAL; goto out; } @@ -805,11 +811,33 @@ int dm_clone_metadata_commit(struct dm_clone_metadata *cmd) cmd->current_dmap = next_dmap; spin_unlock_irq(&cmd->bitmap_lock); - /* - * No one is accessing the old dirty bitmap anymore, so we can flush - * it. - */ - r = __flush_dmap(cmd, dmap); + /* Set old dirty bitmap as currently committing */ + cmd->committing_dmap = dmap; +out: + up_write(&cmd->lock); + + return r; +} + +int dm_clone_metadata_commit(struct dm_clone_metadata *cmd) +{ + int r = -EPERM; + + down_write(&cmd->lock); + + if (cmd->fail_io || dm_bm_is_read_only(cmd->bm)) + goto out; + + if (WARN_ON(!cmd->committing_dmap)) { + r = -EINVAL; + goto out; + } + + r = __flush_dmap(cmd, cmd->committing_dmap); + if (!r) { + /* Clear committing dmap */ + cmd->committing_dmap = NULL; + } out: up_write(&cmd->lock); diff --git a/drivers/md/dm-clone-metadata.h b/drivers/md/dm-clone-metadata.h index 3fe50a781c116..14af1ebd853fd 100644 --- a/drivers/md/dm-clone-metadata.h +++ b/drivers/md/dm-clone-metadata.h @@ -75,7 +75,23 @@ void dm_clone_metadata_close(struct dm_clone_metadata *cmd); /* * Commit dm-clone metadata to disk. + * + * We use a two phase commit: + * + * 1. dm_clone_metadata_pre_commit(): Prepare the current transaction for + * committing. After this is called, all subsequent metadata updates, done + * through either dm_clone_set_region_hydrated() or + * dm_clone_cond_set_range(), will be part of the **next** transaction. + * + * 2. dm_clone_metadata_commit(): Actually commit the current transaction to + * disk and start a new transaction. + * + * This allows dm-clone to flush the destination device after step (1) to + * ensure that all freshly hydrated regions, for which we are updating the + * metadata, are properly written to non-volatile storage and won't be lost in + * case of a crash. */ +int dm_clone_metadata_pre_commit(struct dm_clone_metadata *cmd); int dm_clone_metadata_commit(struct dm_clone_metadata *cmd); /* @@ -112,6 +128,7 @@ int dm_clone_metadata_abort(struct dm_clone_metadata *cmd); * Switches metadata to a read only mode. Once read-only mode has been entered * the following functions will return -EPERM: * + * dm_clone_metadata_pre_commit() * dm_clone_metadata_commit() * dm_clone_set_region_hydrated() * dm_clone_cond_set_range() diff --git a/drivers/md/dm-clone-target.c b/drivers/md/dm-clone-target.c index b3d89072d21c6..613c913c296c7 100644 --- a/drivers/md/dm-clone-target.c +++ b/drivers/md/dm-clone-target.c @@ -1122,8 +1122,13 @@ static int commit_metadata(struct clone *clone) goto out; } - r = dm_clone_metadata_commit(clone->cmd); + r = dm_clone_metadata_pre_commit(clone->cmd); + if (unlikely(r)) { + __metadata_operation_failed(clone, "dm_clone_metadata_pre_commit", r); + goto out; + } + r = dm_clone_metadata_commit(clone->cmd); if (unlikely(r)) { __metadata_operation_failed(clone, "dm_clone_metadata_commit", r); goto out; -- GitLab From 8b3fd1f53af3591d5624ab9df718369b14d09ed1 Mon Sep 17 00:00:00 2001 From: Nikos Tsironis Date: Wed, 4 Dec 2019 16:06:54 +0200 Subject: [PATCH 0148/1437] dm clone: Flush destination device before committing metadata dm-clone maintains an on-disk bitmap which records which regions are valid in the destination device, i.e., which regions have already been hydrated, or have been written to directly, via user I/O. Setting a bit in the on-disk bitmap meas the corresponding region is valid in the destination device and we redirect all I/O regarding it to the destination device. Suppose the destination device has a volatile write-back cache and the following sequence of events occur: 1. A region gets hydrated, either through the background hydration or because it was written to directly, via user I/O. 2. The commit timeout expires and we commit the metadata, marking that region as valid in the destination device. 3. The system crashes and the destination device's cache has not been flushed, meaning the region's data are lost. The next time we read that region we read it from the destination device, since the metadata have been successfully committed, but the data are lost due to the crash, so we read garbage instead of the old data. This has several implications: 1. In case of background hydration or of writes with size smaller than the region size (which means we first copy the whole region and then issue the smaller write), we corrupt data that the user never touched. 2. In case of writes with size equal to the device's logical block size, we fail to provide atomic sector writes. When the system recovers the user will read garbage from the sector instead of the old data or the new data. 3. In case of writes without the FUA flag set, after the system recovers, the written sectors will contain garbage instead of a random mix of sectors containing either old data or new data, thus we fail again to provide atomic sector writes. 4. Even when the user flushes the dm-clone device, because we first commit the metadata and then pass down the flush, the same risk for corruption exists (if the system crashes after the metadata have been committed but before the flush is passed down). The only case which is unaffected is that of writes with size equal to the region size and with the FUA flag set. But, because FUA writes trigger metadata commits, this case can trigger the corruption indirectly. To solve this and avoid the potential data corruption we flush the destination device **before** committing the metadata. This ensures that any freshly hydrated regions, for which we commit the metadata, are properly written to non-volatile storage and won't be lost in case of a crash. Fixes: 7431b7835f55 ("dm: add clone target") Cc: stable@vger.kernel.org # v5.4+ Signed-off-by: Nikos Tsironis Signed-off-by: Mike Snitzer --- drivers/md/dm-clone-target.c | 46 +++++++++++++++++++++++++++++++----- 1 file changed, 40 insertions(+), 6 deletions(-) diff --git a/drivers/md/dm-clone-target.c b/drivers/md/dm-clone-target.c index 613c913c296c7..d1e1b5b56b1bb 100644 --- a/drivers/md/dm-clone-target.c +++ b/drivers/md/dm-clone-target.c @@ -86,6 +86,12 @@ struct clone { struct dm_clone_metadata *cmd; + /* + * bio used to flush the destination device, before committing the + * metadata. + */ + struct bio flush_bio; + /* Region hydration hash table */ struct hash_table_bucket *ht; @@ -1108,10 +1114,13 @@ static bool need_commit_due_to_time(struct clone *clone) /* * A non-zero return indicates read-only or fail mode. */ -static int commit_metadata(struct clone *clone) +static int commit_metadata(struct clone *clone, bool *dest_dev_flushed) { int r = 0; + if (dest_dev_flushed) + *dest_dev_flushed = false; + mutex_lock(&clone->commit_lock); if (!dm_clone_changed_this_transaction(clone->cmd)) @@ -1128,6 +1137,19 @@ static int commit_metadata(struct clone *clone) goto out; } + bio_reset(&clone->flush_bio); + bio_set_dev(&clone->flush_bio, clone->dest_dev->bdev); + clone->flush_bio.bi_opf = REQ_OP_WRITE | REQ_PREFLUSH; + + r = submit_bio_wait(&clone->flush_bio); + if (unlikely(r)) { + __metadata_operation_failed(clone, "flush destination device", r); + goto out; + } + + if (dest_dev_flushed) + *dest_dev_flushed = true; + r = dm_clone_metadata_commit(clone->cmd); if (unlikely(r)) { __metadata_operation_failed(clone, "dm_clone_metadata_commit", r); @@ -1199,6 +1221,7 @@ static void process_deferred_bios(struct clone *clone) static void process_deferred_flush_bios(struct clone *clone) { struct bio *bio; + bool dest_dev_flushed; struct bio_list bios = BIO_EMPTY_LIST; struct bio_list bio_completions = BIO_EMPTY_LIST; @@ -1218,7 +1241,7 @@ static void process_deferred_flush_bios(struct clone *clone) !(dm_clone_changed_this_transaction(clone->cmd) && need_commit_due_to_time(clone))) return; - if (commit_metadata(clone)) { + if (commit_metadata(clone, &dest_dev_flushed)) { bio_list_merge(&bios, &bio_completions); while ((bio = bio_list_pop(&bios))) @@ -1232,8 +1255,17 @@ static void process_deferred_flush_bios(struct clone *clone) while ((bio = bio_list_pop(&bio_completions))) bio_endio(bio); - while ((bio = bio_list_pop(&bios))) - generic_make_request(bio); + while ((bio = bio_list_pop(&bios))) { + if ((bio->bi_opf & REQ_PREFLUSH) && dest_dev_flushed) { + /* We just flushed the destination device as part of + * the metadata commit, so there is no reason to send + * another flush. + */ + bio_endio(bio); + } else { + generic_make_request(bio); + } + } } static void do_worker(struct work_struct *work) @@ -1405,7 +1437,7 @@ static void clone_status(struct dm_target *ti, status_type_t type, /* Commit to ensure statistics aren't out-of-date */ if (!(status_flags & DM_STATUS_NOFLUSH_FLAG) && !dm_suspended(ti)) - (void) commit_metadata(clone); + (void) commit_metadata(clone, NULL); r = dm_clone_get_free_metadata_block_count(clone->cmd, &nr_free_metadata_blocks); @@ -1839,6 +1871,7 @@ static int clone_ctr(struct dm_target *ti, unsigned int argc, char **argv) bio_list_init(&clone->deferred_flush_completions); clone->hydration_offset = 0; atomic_set(&clone->hydrations_in_flight, 0); + bio_init(&clone->flush_bio, NULL, 0); clone->wq = alloc_workqueue("dm-" DM_MSG_PREFIX, WQ_MEM_RECLAIM, 0); if (!clone->wq) { @@ -1912,6 +1945,7 @@ static void clone_dtr(struct dm_target *ti) struct clone *clone = ti->private; mutex_destroy(&clone->commit_lock); + bio_uninit(&clone->flush_bio); for (i = 0; i < clone->nr_ctr_args; i++) kfree(clone->ctr_args[i]); @@ -1966,7 +2000,7 @@ static void clone_postsuspend(struct dm_target *ti) wait_event(clone->hydration_stopped, !atomic_read(&clone->hydrations_in_flight)); flush_workqueue(clone->wq); - (void) commit_metadata(clone); + (void) commit_metadata(clone, NULL); } static void clone_resume(struct dm_target *ti) -- GitLab From ecda7c0280e6b3398459dc589b9a41c1adb45529 Mon Sep 17 00:00:00 2001 From: Nikos Tsironis Date: Wed, 4 Dec 2019 16:07:41 +0200 Subject: [PATCH 0149/1437] dm thin metadata: Add support for a pre-commit callback Add support for one pre-commit callback which is run right before the metadata are committed. This allows the thin provisioning target to run a callback before the metadata are committed and is required by the next commit. Cc: stable@vger.kernel.org Signed-off-by: Nikos Tsironis Acked-by: Joe Thornber Signed-off-by: Mike Snitzer --- drivers/md/dm-thin-metadata.c | 29 +++++++++++++++++++++++++++++ drivers/md/dm-thin-metadata.h | 7 +++++++ 2 files changed, 36 insertions(+) diff --git a/drivers/md/dm-thin-metadata.c b/drivers/md/dm-thin-metadata.c index 4c68a7b93d5ed..b88d6d701f5bb 100644 --- a/drivers/md/dm-thin-metadata.c +++ b/drivers/md/dm-thin-metadata.c @@ -188,6 +188,15 @@ struct dm_pool_metadata { unsigned long flags; sector_t data_block_size; + /* + * Pre-commit callback. + * + * This allows the thin provisioning target to run a callback before + * the metadata are committed. + */ + dm_pool_pre_commit_fn pre_commit_fn; + void *pre_commit_context; + /* * We reserve a section of the metadata for commit overhead. * All reported space does *not* include this. @@ -826,6 +835,14 @@ static int __commit_transaction(struct dm_pool_metadata *pmd) if (unlikely(!pmd->in_service)) return 0; + if (pmd->pre_commit_fn) { + r = pmd->pre_commit_fn(pmd->pre_commit_context); + if (r < 0) { + DMERR("pre-commit callback failed"); + return r; + } + } + r = __write_changed_details(pmd); if (r < 0) return r; @@ -892,6 +909,8 @@ struct dm_pool_metadata *dm_pool_metadata_open(struct block_device *bdev, pmd->in_service = false; pmd->bdev = bdev; pmd->data_block_size = data_block_size; + pmd->pre_commit_fn = NULL; + pmd->pre_commit_context = NULL; r = __create_persistent_data_objects(pmd, format_device); if (r) { @@ -2044,6 +2063,16 @@ int dm_pool_register_metadata_threshold(struct dm_pool_metadata *pmd, return r; } +void dm_pool_register_pre_commit_callback(struct dm_pool_metadata *pmd, + dm_pool_pre_commit_fn fn, + void *context) +{ + pmd_write_lock_in_core(pmd); + pmd->pre_commit_fn = fn; + pmd->pre_commit_context = context; + pmd_write_unlock(pmd); +} + int dm_pool_metadata_set_needs_check(struct dm_pool_metadata *pmd) { int r = -EINVAL; diff --git a/drivers/md/dm-thin-metadata.h b/drivers/md/dm-thin-metadata.h index f6be0d733c202..7ef56bd2a7e33 100644 --- a/drivers/md/dm-thin-metadata.h +++ b/drivers/md/dm-thin-metadata.h @@ -230,6 +230,13 @@ bool dm_pool_metadata_needs_check(struct dm_pool_metadata *pmd); */ void dm_pool_issue_prefetches(struct dm_pool_metadata *pmd); +/* Pre-commit callback */ +typedef int (*dm_pool_pre_commit_fn)(void *context); + +void dm_pool_register_pre_commit_callback(struct dm_pool_metadata *pmd, + dm_pool_pre_commit_fn fn, + void *context); + /*----------------------------------------------------------------*/ #endif -- GitLab From 6e807535dae5dbbd53bcc5e81047a20bf5eb08ea Mon Sep 17 00:00:00 2001 From: Guchun Chen Date: Wed, 4 Dec 2019 15:51:16 +0800 Subject: [PATCH 0150/1437] drm/amdgpu: add check before enabling/disabling broadcast mode When security violation from new vbios happens, data fabric is risky to stop working. So prevent the direct access to DF mmFabricConfigAccessControl from the new vbios and onwards. Signed-off-by: Guchun Chen Reviewed-by: Hawking Zhang Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdgpu/df_v3_6.c | 38 ++++++++++++++++------------ 1 file changed, 22 insertions(+), 16 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/df_v3_6.c b/drivers/gpu/drm/amd/amdgpu/df_v3_6.c index 16fbd2bc8ad1e..4043ebcea5de6 100644 --- a/drivers/gpu/drm/amd/amdgpu/df_v3_6.c +++ b/drivers/gpu/drm/amd/amdgpu/df_v3_6.c @@ -268,23 +268,29 @@ static void df_v3_6_update_medium_grain_clock_gating(struct amdgpu_device *adev, { u32 tmp; - /* Put DF on broadcast mode */ - adev->df_funcs->enable_broadcast_mode(adev, true); - - if (enable && (adev->cg_flags & AMD_CG_SUPPORT_DF_MGCG)) { - tmp = RREG32_SOC15(DF, 0, mmDF_PIE_AON0_DfGlobalClkGater); - tmp &= ~DF_PIE_AON0_DfGlobalClkGater__MGCGMode_MASK; - tmp |= DF_V3_6_MGCG_ENABLE_15_CYCLE_DELAY; - WREG32_SOC15(DF, 0, mmDF_PIE_AON0_DfGlobalClkGater, tmp); - } else { - tmp = RREG32_SOC15(DF, 0, mmDF_PIE_AON0_DfGlobalClkGater); - tmp &= ~DF_PIE_AON0_DfGlobalClkGater__MGCGMode_MASK; - tmp |= DF_V3_6_MGCG_DISABLE; - WREG32_SOC15(DF, 0, mmDF_PIE_AON0_DfGlobalClkGater, tmp); - } + if (adev->cg_flags & AMD_CG_SUPPORT_DF_MGCG) { + /* Put DF on broadcast mode */ + adev->df_funcs->enable_broadcast_mode(adev, true); + + if (enable) { + tmp = RREG32_SOC15(DF, 0, + mmDF_PIE_AON0_DfGlobalClkGater); + tmp &= ~DF_PIE_AON0_DfGlobalClkGater__MGCGMode_MASK; + tmp |= DF_V3_6_MGCG_ENABLE_15_CYCLE_DELAY; + WREG32_SOC15(DF, 0, + mmDF_PIE_AON0_DfGlobalClkGater, tmp); + } else { + tmp = RREG32_SOC15(DF, 0, + mmDF_PIE_AON0_DfGlobalClkGater); + tmp &= ~DF_PIE_AON0_DfGlobalClkGater__MGCGMode_MASK; + tmp |= DF_V3_6_MGCG_DISABLE; + WREG32_SOC15(DF, 0, + mmDF_PIE_AON0_DfGlobalClkGater, tmp); + } - /* Exit broadcast mode */ - adev->df_funcs->enable_broadcast_mode(adev, false); + /* Exit broadcast mode */ + adev->df_funcs->enable_broadcast_mode(adev, false); + } } static void df_v3_6_get_clockgating_state(struct amdgpu_device *adev, -- GitLab From 14891c316ca7e15d81dba78f30fb630e3f9ee2c9 Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Wed, 4 Dec 2019 22:07:49 -0500 Subject: [PATCH 0151/1437] drm/amdgpu: add header line for power profile on Arcturus So the output is consistent with other asics. Reviewed-by: Evan Quan Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/powerplay/arcturus_ppt.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/gpu/drm/amd/powerplay/arcturus_ppt.c b/drivers/gpu/drm/amd/powerplay/arcturus_ppt.c index ce3566ca3e24b..cc71a1078a7a6 100644 --- a/drivers/gpu/drm/amd/powerplay/arcturus_ppt.c +++ b/drivers/gpu/drm/amd/powerplay/arcturus_ppt.c @@ -1313,12 +1313,17 @@ static int arcturus_get_power_profile_mode(struct smu_context *smu, "VR", "COMPUTE", "CUSTOM"}; + static const char *title[] = { + "PROFILE_INDEX(NAME)"}; uint32_t i, size = 0; int16_t workload_type = 0; if (!smu->pm_enabled || !buf) return -EINVAL; + size += sprintf(buf + size, "%16s\n", + title[0]); + for (i = 0; i <= PP_SMC_POWER_PROFILE_CUSTOM; i++) { /* * Conv PP_SMC_POWER_PROFILE* to WORKLOAD_PPLIB_*_BIT -- GitLab From bf26da927a1cd57c9deb2db29ae8cf276ba8b17b Mon Sep 17 00:00:00 2001 From: Pierre-Eric Pelloux-Prayer Date: Thu, 28 Nov 2019 12:08:58 +0100 Subject: [PATCH 0152/1437] drm/amdgpu: add cache flush workaround to gfx8 emit_fence The same workaround is used for gfx7. Both PAL and Mesa use it for gfx8 too, so port this commit to gfx_v8_0_ring_emit_fence_gfx. Signed-off-by: Pierre-Eric Pelloux-Prayer Reviewed-by: Alex Deucher Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdgpu/gfx_v8_0.c | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v8_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v8_0.c index 983db77999e7a..52a647d7022d2 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfx_v8_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gfx_v8_0.c @@ -6146,7 +6146,23 @@ static void gfx_v8_0_ring_emit_fence_gfx(struct amdgpu_ring *ring, u64 addr, bool write64bit = flags & AMDGPU_FENCE_FLAG_64BIT; bool int_sel = flags & AMDGPU_FENCE_FLAG_INT; - /* EVENT_WRITE_EOP - flush caches, send int */ + /* Workaround for cache flush problems. First send a dummy EOP + * event down the pipe with seq one below. + */ + amdgpu_ring_write(ring, PACKET3(PACKET3_EVENT_WRITE_EOP, 4)); + amdgpu_ring_write(ring, (EOP_TCL1_ACTION_EN | + EOP_TC_ACTION_EN | + EOP_TC_WB_ACTION_EN | + EVENT_TYPE(CACHE_FLUSH_AND_INV_TS_EVENT) | + EVENT_INDEX(5))); + amdgpu_ring_write(ring, addr & 0xfffffffc); + amdgpu_ring_write(ring, (upper_32_bits(addr) & 0xffff) | + DATA_SEL(1) | INT_SEL(0)); + amdgpu_ring_write(ring, lower_32_bits(seq - 1)); + amdgpu_ring_write(ring, upper_32_bits(seq - 1)); + + /* Then send the real EOP event down the pipe: + * EVENT_WRITE_EOP - flush caches, send int */ amdgpu_ring_write(ring, PACKET3(PACKET3_EVENT_WRITE_EOP, 4)); amdgpu_ring_write(ring, (EOP_TCL1_ACTION_EN | EOP_TC_ACTION_EN | @@ -6888,7 +6904,7 @@ static const struct amdgpu_ring_funcs gfx_v8_0_ring_funcs_gfx = { 5 + /* COND_EXEC */ 7 + /* PIPELINE_SYNC */ VI_FLUSH_GPU_TLB_NUM_WREG * 5 + 9 + /* VM_FLUSH */ - 8 + /* FENCE for VM_FLUSH */ + 12 + /* FENCE for VM_FLUSH */ 20 + /* GDS switch */ 4 + /* double SWITCH_BUFFER, the first COND_EXEC jump to the place just @@ -6900,7 +6916,7 @@ static const struct amdgpu_ring_funcs gfx_v8_0_ring_funcs_gfx = { 31 + /* DE_META */ 3 + /* CNTX_CTRL */ 5 + /* HDP_INVL */ - 8 + 8 + /* FENCE x2 */ + 12 + 12 + /* FENCE x2 */ 2, /* SWITCH_BUFFER */ .emit_ib_size = 4, /* gfx_v8_0_ring_emit_ib_gfx */ .emit_ib = gfx_v8_0_ring_emit_ib_gfx, -- GitLab From 9d9de889ff8eb941e4daefff610e170386b61962 Mon Sep 17 00:00:00 2001 From: Eric Yang Date: Tue, 5 Nov 2019 11:59:38 -0500 Subject: [PATCH 0153/1437] drm/amd/display: update sr and pstate latencies for Renoir [Why] DF team has produced more optimized latency numbers. [How] Add sr latencies to the wm table, use different latencies for different wm sets. Also fix bb override from registery key for these latencies. Signed-off-by: Eric Yang Reviewed-by: Tony Cheng Acked-by: Leo Li Signed-off-by: Alex Deucher --- .../amd/display/dc/clk_mgr/dcn21/rn_clk_mgr.c | 16 ++++++++++++---- .../drm/amd/display/dc/dcn21/dcn21_resource.c | 15 ++++++++++++--- drivers/gpu/drm/amd/display/dc/inc/hw/clk_mgr.h | 2 ++ 3 files changed, 26 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn21/rn_clk_mgr.c b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn21/rn_clk_mgr.c index 790a2d211bd6d..841095d09d3c2 100644 --- a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn21/rn_clk_mgr.c +++ b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn21/rn_clk_mgr.c @@ -523,25 +523,33 @@ struct clk_bw_params rn_bw_params = { { .wm_inst = WM_A, .wm_type = WM_TYPE_PSTATE_CHG, - .pstate_latency_us = 23.84, + .pstate_latency_us = 11.72, + .sr_exit_time_us = 6.09, + .sr_enter_plus_exit_time_us = 7.14, .valid = true, }, { .wm_inst = WM_B, .wm_type = WM_TYPE_PSTATE_CHG, - .pstate_latency_us = 23.84, + .pstate_latency_us = 11.72, + .sr_exit_time_us = 10.12, + .sr_enter_plus_exit_time_us = 11.48, .valid = true, }, { .wm_inst = WM_C, .wm_type = WM_TYPE_PSTATE_CHG, - .pstate_latency_us = 23.84, + .pstate_latency_us = 11.72, + .sr_exit_time_us = 10.12, + .sr_enter_plus_exit_time_us = 11.48, .valid = true, }, { .wm_inst = WM_D, .wm_type = WM_TYPE_PSTATE_CHG, - .pstate_latency_us = 23.84, + .pstate_latency_us = 11.72, + .sr_exit_time_us = 10.12, + .sr_enter_plus_exit_time_us = 11.48, .valid = true, }, }, diff --git a/drivers/gpu/drm/amd/display/dc/dcn21/dcn21_resource.c b/drivers/gpu/drm/amd/display/dc/dcn21/dcn21_resource.c index 459bd9a5caed5..9d83b405e8b89 100644 --- a/drivers/gpu/drm/amd/display/dc/dcn21/dcn21_resource.c +++ b/drivers/gpu/drm/amd/display/dc/dcn21/dcn21_resource.c @@ -1000,6 +1000,8 @@ static void calculate_wm_set_for_vlevel( pipes[0].clks_cfg.socclk_mhz = dml->soc.clock_limits[vlevel].socclk_mhz; dml->soc.dram_clock_change_latency_us = table_entry->pstate_latency_us; + dml->soc.sr_exit_time_us = table_entry->sr_exit_time_us; + dml->soc.sr_enter_plus_exit_time_us = table_entry->sr_enter_plus_exit_time_us; wm_set->urgent_ns = get_wm_urgent(dml, pipes, pipe_cnt) * 1000; wm_set->cstate_pstate.cstate_enter_plus_exit_ns = get_wm_stutter_enter_exit(dml, pipes, pipe_cnt) * 1000; @@ -1017,14 +1019,21 @@ static void calculate_wm_set_for_vlevel( static void patch_bounding_box(struct dc *dc, struct _vcs_dpi_soc_bounding_box_st *bb) { + int i; + kernel_fpu_begin(); if (dc->bb_overrides.sr_exit_time_ns) { - bb->sr_exit_time_us = dc->bb_overrides.sr_exit_time_ns / 1000.0; + for (i = 0; i < WM_SET_COUNT; i++) { + dc->clk_mgr->bw_params->wm_table.entries[i].sr_exit_time_us = + dc->bb_overrides.sr_exit_time_ns / 1000.0; + } } if (dc->bb_overrides.sr_enter_plus_exit_time_ns) { - bb->sr_enter_plus_exit_time_us = - dc->bb_overrides.sr_enter_plus_exit_time_ns / 1000.0; + for (i = 0; i < WM_SET_COUNT; i++) { + dc->clk_mgr->bw_params->wm_table.entries[i].sr_enter_plus_exit_time_us = + dc->bb_overrides.sr_enter_plus_exit_time_ns / 1000.0; + } } if (dc->bb_overrides.urgent_latency_ns) { diff --git a/drivers/gpu/drm/amd/display/dc/inc/hw/clk_mgr.h b/drivers/gpu/drm/amd/display/dc/inc/hw/clk_mgr.h index 4e18e77dcf422..026e6a2a2c44c 100644 --- a/drivers/gpu/drm/amd/display/dc/inc/hw/clk_mgr.h +++ b/drivers/gpu/drm/amd/display/dc/inc/hw/clk_mgr.h @@ -69,6 +69,8 @@ struct wm_range_table_entry { unsigned int wm_inst; unsigned int wm_type; double pstate_latency_us; + double sr_exit_time_us; + double sr_enter_plus_exit_time_us; bool valid; }; -- GitLab From a1fc44b609b4e9c0941f0e4a1fc69d367af5ab69 Mon Sep 17 00:00:00 2001 From: Nikola Cornij Date: Thu, 7 Nov 2019 13:06:48 -0500 Subject: [PATCH 0154/1437] drm/amd/display: Map DSC resources 1-to-1 if numbers of OPPs and DSCs are equal [why] On ASICs where number of DSCs is the same as OPPs there's no need for DSC resource management. Mappping 1-to-1 fixes mode-set- or S3- -related issues for such platforms. [how] Map DSC resources 1-to-1 to pipes only if number of OPPs is the same as number of DSCs. This will still keep other ASICs working. A follow-up patch to fix mode-set issues on those ASICs will be required if testing shows issues with mode set. Signed-off-by: Nikola Cornij Reviewed-by: Dmytro Laktyushkin Acked-by: Leo Li Signed-off-by: Alex Deucher --- .../gpu/drm/amd/display/dc/dcn20/dcn20_resource.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/amd/display/dc/dcn20/dcn20_resource.c b/drivers/gpu/drm/amd/display/dc/dcn20/dcn20_resource.c index 09793336d84f6..06f9b71591f1c 100644 --- a/drivers/gpu/drm/amd/display/dc/dcn20/dcn20_resource.c +++ b/drivers/gpu/drm/amd/display/dc/dcn20/dcn20_resource.c @@ -1536,13 +1536,20 @@ enum dc_status dcn20_build_mapped_resource(const struct dc *dc, struct dc_state static void acquire_dsc(struct resource_context *res_ctx, const struct resource_pool *pool, - struct display_stream_compressor **dsc) + struct display_stream_compressor **dsc, + int pipe_idx) { int i; ASSERT(*dsc == NULL); *dsc = NULL; + if (pool->res_cap->num_dsc == pool->res_cap->num_opp) { + *dsc = pool->dscs[pipe_idx]; + res_ctx->is_dsc_acquired[pipe_idx] = true; + return; + } + /* Find first free DSC */ for (i = 0; i < pool->res_cap->num_dsc; i++) if (!res_ctx->is_dsc_acquired[i]) { @@ -1585,7 +1592,7 @@ static enum dc_status add_dsc_to_stream_resource(struct dc *dc, if (pipe_ctx->stream != dc_stream) continue; - acquire_dsc(&dc_ctx->res_ctx, pool, &pipe_ctx->stream_res.dsc); + acquire_dsc(&dc_ctx->res_ctx, pool, &pipe_ctx->stream_res.dsc, i); /* The number of DSCs can be less than the number of pipes */ if (!pipe_ctx->stream_res.dsc) { @@ -1785,7 +1792,7 @@ bool dcn20_split_stream_for_odm( next_odm_pipe->stream_res.opp = pool->opps[next_odm_pipe->pipe_idx]; #ifdef CONFIG_DRM_AMD_DC_DSC_SUPPORT if (next_odm_pipe->stream->timing.flags.DSC == 1) { - acquire_dsc(res_ctx, pool, &next_odm_pipe->stream_res.dsc); + acquire_dsc(res_ctx, pool, &next_odm_pipe->stream_res.dsc, next_odm_pipe->pipe_idx); ASSERT(next_odm_pipe->stream_res.dsc); if (next_odm_pipe->stream_res.dsc == NULL) return false; -- GitLab From aa4febf5098572649846b4f28a2609233388c412 Mon Sep 17 00:00:00 2001 From: Joseph Gravenor Date: Mon, 4 Nov 2019 16:39:35 -0500 Subject: [PATCH 0155/1437] drm/amd/display: fix DalDramClockChangeLatencyNs override [why] pstate_latency_us never gets updated from the hard coded value in rn_clk_mgr.c [how] update the wm table's values before we do calculations with them Signed-off-by: Joseph Gravenor Reviewed-by: Eric Yang Acked-by: Leo Li Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/display/dc/dcn21/dcn21_resource.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/display/dc/dcn21/dcn21_resource.c b/drivers/gpu/drm/amd/display/dc/dcn21/dcn21_resource.c index 9d83b405e8b89..6d99b47606950 100644 --- a/drivers/gpu/drm/amd/display/dc/dcn21/dcn21_resource.c +++ b/drivers/gpu/drm/amd/display/dc/dcn21/dcn21_resource.c @@ -1041,9 +1041,12 @@ static void patch_bounding_box(struct dc *dc, struct _vcs_dpi_soc_bounding_box_s } if (dc->bb_overrides.dram_clock_change_latency_ns) { - bb->dram_clock_change_latency_us = + for (i = 0; i < WM_SET_COUNT; i++) { + dc->clk_mgr->bw_params->wm_table.entries[i].pstate_latency_us = dc->bb_overrides.dram_clock_change_latency_ns / 1000.0; + } } + kernel_fpu_end(); } -- GitLab From 1653acd566a846647adc293209f823b3e552e974 Mon Sep 17 00:00:00 2001 From: Joseph Gravenor Date: Thu, 7 Nov 2019 19:20:00 -0500 Subject: [PATCH 0156/1437] drm/amd/display: populate bios integrated info for renoir [Why] When video_memory_type bw_params->vram_type is assigned, wedistinguish between Ddr4MemType and LpDdr4MemType. Because of this we will never report that we are using LpDdr4MemType and never re-purpose WM set D [How] populate bios integrated info for renoir by adding the revision number for renoir and use that integrated info table instead of of asic_id to get the vram type Signed-off-by: Joseph Gravenor Reviewed-by: Tony Cheng Acked-by: Leo Li Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/display/dc/bios/bios_parser2.c | 1 + .../gpu/drm/amd/display/dc/clk_mgr/dcn21/rn_clk_mgr.c | 10 ++++++---- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/amd/display/dc/bios/bios_parser2.c b/drivers/gpu/drm/amd/display/dc/bios/bios_parser2.c index 7873abea4112b..5c3fcaa474109 100644 --- a/drivers/gpu/drm/amd/display/dc/bios/bios_parser2.c +++ b/drivers/gpu/drm/amd/display/dc/bios/bios_parser2.c @@ -1625,6 +1625,7 @@ static enum bp_result construct_integrated_info( /* Don't need to check major revision as they are all 1 */ switch (revision.minor) { case 11: + case 12: result = get_integrated_info_v11(bp, info); break; default: diff --git a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn21/rn_clk_mgr.c b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn21/rn_clk_mgr.c index 841095d09d3c2..9f0381c68844b 100644 --- a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn21/rn_clk_mgr.c +++ b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn21/rn_clk_mgr.c @@ -569,7 +569,7 @@ static unsigned int find_dcfclk_for_voltage(struct dpm_clocks *clock_table, unsi return 0; } -static void rn_clk_mgr_helper_populate_bw_params(struct clk_bw_params *bw_params, struct dpm_clocks *clock_table, struct hw_asic_id *asic_id) +static void rn_clk_mgr_helper_populate_bw_params(struct clk_bw_params *bw_params, struct dpm_clocks *clock_table, struct integrated_info *bios_info) { int i, j = 0; @@ -601,8 +601,8 @@ static void rn_clk_mgr_helper_populate_bw_params(struct clk_bw_params *bw_params bw_params->clk_table.entries[i].dcfclk_mhz = find_dcfclk_for_voltage(clock_table, clock_table->FClocks[j].Vol); } - bw_params->vram_type = asic_id->vram_type; - bw_params->num_channels = asic_id->vram_width / DDR4_DRAM_WIDTH; + bw_params->vram_type = bios_info->memory_type; + bw_params->num_channels = bios_info->ma_channel_number; for (i = 0; i < WM_SET_COUNT; i++) { bw_params->wm_table.entries[i].wm_inst = i; @@ -685,7 +685,9 @@ void rn_clk_mgr_construct( if (pp_smu && pp_smu->rn_funcs.get_dpm_clock_table) { pp_smu->rn_funcs.get_dpm_clock_table(&pp_smu->rn_funcs.pp_smu, &clock_table); - rn_clk_mgr_helper_populate_bw_params(clk_mgr->base.bw_params, &clock_table, &ctx->asic_id); + if (ctx->dc_bios && ctx->dc_bios->integrated_info) { + rn_clk_mgr_helper_populate_bw_params (clk_mgr->base.bw_params, &clock_table, ctx->dc_bios->integrated_info); + } } if (!IS_FPGA_MAXIMUS_DC(ctx->dce_environment) && clk_mgr->smu_ver >= 0x00371500) { -- GitLab From a51d9f8fe756beac51ce26ef54195da00a260d13 Mon Sep 17 00:00:00 2001 From: David Galiffi Date: Thu, 7 Nov 2019 17:18:20 -0500 Subject: [PATCH 0157/1437] drm/amd/display: Fixed kernel panic when booting with DP-to-HDMI dongle [Why] In dc_link_is_dp_sink_present, if dal_ddc_open fails, then dal_gpio_destroy_ddc is called, destroying pin_data and pin_clock. They are created only on dc_construct, and next aux access will cause a panic. [How] Instead of calling dal_gpio_destroy_ddc, call dal_ddc_close. Signed-off-by: David Galiffi Reviewed-by: Tony Cheng Acked-by: Leo Li Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/display/dc/core/dc_link.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/display/dc/core/dc_link.c b/drivers/gpu/drm/amd/display/dc/core/dc_link.c index 12ba6fdf89b73..62d8289abb4ef 100644 --- a/drivers/gpu/drm/amd/display/dc/core/dc_link.c +++ b/drivers/gpu/drm/amd/display/dc/core/dc_link.c @@ -372,7 +372,7 @@ bool dc_link_is_dp_sink_present(struct dc_link *link) if (GPIO_RESULT_OK != dal_ddc_open( ddc, GPIO_MODE_INPUT, GPIO_DDC_CONFIG_TYPE_MODE_I2C)) { - dal_gpio_destroy_ddc(&ddc); + dal_ddc_close(ddc); return present; } -- GitLab From 10bce710fa6feb13ec270d5420aad3b81f6bf7ea Mon Sep 17 00:00:00 2001 From: Joseph Gravenor Date: Fri, 8 Nov 2019 14:30:34 -0500 Subject: [PATCH 0158/1437] drm/amd/display: have two different sr and pstate latency tables for renoir [Why] new sr and pstate latencies are optimized for the case when we are not using lpddr4 memory [How] have two different wm tables, one for the lpddr case and one for non lpddr case Signed-off-by: Joseph Gravenor Reviewed-by: Eric Yang Acked-by: Leo Li Signed-off-by: Alex Deucher --- .../amd/display/dc/clk_mgr/dcn21/rn_clk_mgr.c | 114 ++++++++++++------ 1 file changed, 80 insertions(+), 34 deletions(-) diff --git a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn21/rn_clk_mgr.c b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn21/rn_clk_mgr.c index 9f0381c68844b..89ed230cdb267 100644 --- a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn21/rn_clk_mgr.c +++ b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn21/rn_clk_mgr.c @@ -518,44 +518,83 @@ struct clk_bw_params rn_bw_params = { .num_entries = 4, }, - .wm_table = { - .entries = { - { - .wm_inst = WM_A, - .wm_type = WM_TYPE_PSTATE_CHG, - .pstate_latency_us = 11.72, - .sr_exit_time_us = 6.09, - .sr_enter_plus_exit_time_us = 7.14, - .valid = true, - }, - { - .wm_inst = WM_B, - .wm_type = WM_TYPE_PSTATE_CHG, - .pstate_latency_us = 11.72, - .sr_exit_time_us = 10.12, - .sr_enter_plus_exit_time_us = 11.48, - .valid = true, - }, - { - .wm_inst = WM_C, - .wm_type = WM_TYPE_PSTATE_CHG, - .pstate_latency_us = 11.72, - .sr_exit_time_us = 10.12, - .sr_enter_plus_exit_time_us = 11.48, - .valid = true, - }, - { - .wm_inst = WM_D, - .wm_type = WM_TYPE_PSTATE_CHG, - .pstate_latency_us = 11.72, - .sr_exit_time_us = 10.12, - .sr_enter_plus_exit_time_us = 11.48, - .valid = true, - }, +}; + +struct wm_table ddr4_wm_table = { + .entries = { + { + .wm_inst = WM_A, + .wm_type = WM_TYPE_PSTATE_CHG, + .pstate_latency_us = 11.72, + .sr_exit_time_us = 6.09, + .sr_enter_plus_exit_time_us = 7.14, + .valid = true, + }, + { + .wm_inst = WM_B, + .wm_type = WM_TYPE_PSTATE_CHG, + .pstate_latency_us = 11.72, + .sr_exit_time_us = 10.12, + .sr_enter_plus_exit_time_us = 11.48, + .valid = true, + }, + { + .wm_inst = WM_C, + .wm_type = WM_TYPE_PSTATE_CHG, + .pstate_latency_us = 11.72, + .sr_exit_time_us = 10.12, + .sr_enter_plus_exit_time_us = 11.48, + .valid = true, + }, + { + .wm_inst = WM_D, + .wm_type = WM_TYPE_PSTATE_CHG, + .pstate_latency_us = 11.72, + .sr_exit_time_us = 10.12, + .sr_enter_plus_exit_time_us = 11.48, + .valid = true, }, } }; +struct wm_table lpddr4_wm_table = { + .entries = { + { + .wm_inst = WM_A, + .wm_type = WM_TYPE_PSTATE_CHG, + .pstate_latency_us = 23.84, + .sr_exit_time_us = 12.5, + .sr_enter_plus_exit_time_us = 17.0, + .valid = true, + }, + { + .wm_inst = WM_B, + .wm_type = WM_TYPE_PSTATE_CHG, + .pstate_latency_us = 23.84, + .sr_exit_time_us = 12.5, + .sr_enter_plus_exit_time_us = 17.0, + .valid = true, + }, + { + .wm_inst = WM_C, + .wm_type = WM_TYPE_PSTATE_CHG, + .pstate_latency_us = 23.84, + .sr_exit_time_us = 12.5, + .sr_enter_plus_exit_time_us = 17.0, + .valid = true, + }, + { + .wm_inst = WM_D, + .wm_type = WM_TYPE_PSTATE_CHG, + .pstate_latency_us = 23.84, + .sr_exit_time_us = 12.5, + .sr_enter_plus_exit_time_us = 17.0, + .valid = true, + }, + } +}; + + static unsigned int find_dcfclk_for_voltage(struct dpm_clocks *clock_table, unsigned int voltage) { int i; @@ -677,10 +716,17 @@ void rn_clk_mgr_construct( ASSERT(clk_mgr->base.dprefclk_khz == 600000); clk_mgr->base.dprefclk_khz = 600000; } + + if (ctx->dc_bios->integrated_info->memory_type == LpDdr4MemType) { + rn_bw_params.wm_table = lpddr4_wm_table; + } else { + rn_bw_params.wm_table = ddr4_wm_table; + } } dce_clock_read_ss_info(clk_mgr); + clk_mgr->base.bw_params = &rn_bw_params; if (pp_smu && pp_smu->rn_funcs.get_dpm_clock_table) { -- GitLab From 28fa24ad14e8f7d23c62283eaf9c79b4fd165c16 Mon Sep 17 00:00:00 2001 From: "Leo (Hanghong) Ma" Date: Thu, 7 Nov 2019 16:30:04 -0500 Subject: [PATCH 0159/1437] drm/amd/display: Change the delay time before enabling FEC [why] DP spec requires 1000 symbols delay between the end of link training and enabling FEC in the stream. Currently we are using 1 miliseconds delay which is not accurate. [how] One lane RBR should have the maximum time for transmitting 1000 LL codes which is 6.173 us. So using 7 microseconds delay instead of 1 miliseconds. Signed-off-by: Leo (Hanghong) Ma Reviewed-by: Harry Wentland Reviewed-by: Nikola Cornij Acked-by: Leo Li Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/display/dc/core/dc_link_dp.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/display/dc/core/dc_link_dp.c b/drivers/gpu/drm/amd/display/dc/core/dc_link_dp.c index 0f59b68aa4c24..504055fc70e89 100644 --- a/drivers/gpu/drm/amd/display/dc/core/dc_link_dp.c +++ b/drivers/gpu/drm/amd/display/dc/core/dc_link_dp.c @@ -3522,7 +3522,14 @@ void dp_set_fec_enable(struct dc_link *link, bool enable) if (link_enc->funcs->fec_set_enable && link->dpcd_caps.fec_cap.bits.FEC_CAPABLE) { if (link->fec_state == dc_link_fec_ready && enable) { - msleep(1); + /* Accord to DP spec, FEC enable sequence can first + * be transmitted anytime after 1000 LL codes have + * been transmitted on the link after link training + * completion. Using 1 lane RBR should have the maximum + * time for transmitting 1000 LL codes which is 6.173 us. + * So use 7 microseconds delay instead. + */ + udelay(7); link_enc->funcs->fec_set_enable(link_enc, true); link->fec_state = dc_link_fec_enabled; } else if (link->fec_state == dc_link_fec_enabled && !enable) { -- GitLab From a2a1f1eb06d73a45a60970c77403687918966fc6 Mon Sep 17 00:00:00 2001 From: Brandon Syu Date: Fri, 8 Nov 2019 11:26:06 +0800 Subject: [PATCH 0160/1437] drm/amd/display: fixed that I2C over AUX didn't read data issue [Why] The variable mismatch assignment error. [How] To use uint32_t replace it. Signed-off-by: Brandon Syu Reviewed-by: Charlene Liu Acked-by: Leo Li Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/display/dc/core/dc_link_ddc.c | 2 +- drivers/gpu/drm/amd/display/include/i2caux_interface.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/amd/display/dc/core/dc_link_ddc.c b/drivers/gpu/drm/amd/display/dc/core/dc_link_ddc.c index 7f904d55c1bce..81789191d4ec0 100644 --- a/drivers/gpu/drm/amd/display/dc/core/dc_link_ddc.c +++ b/drivers/gpu/drm/amd/display/dc/core/dc_link_ddc.c @@ -586,7 +586,7 @@ bool dal_ddc_service_query_ddc_data( bool dal_ddc_submit_aux_command(struct ddc_service *ddc, struct aux_payload *payload) { - uint8_t retrieved = 0; + uint32_t retrieved = 0; bool ret = 0; if (!ddc) diff --git a/drivers/gpu/drm/amd/display/include/i2caux_interface.h b/drivers/gpu/drm/amd/display/include/i2caux_interface.h index bb012cb1a9f58..c7fbb9c3ad6b3 100644 --- a/drivers/gpu/drm/amd/display/include/i2caux_interface.h +++ b/drivers/gpu/drm/amd/display/include/i2caux_interface.h @@ -42,7 +42,7 @@ struct aux_payload { bool write; bool mot; uint32_t address; - uint8_t length; + uint32_t length; uint8_t *data; /* * used to return the reply type of the transaction -- GitLab From 87de6cb2f28153bc74d0a001ca099c29453e145f Mon Sep 17 00:00:00 2001 From: Nikola Cornij Date: Mon, 11 Nov 2019 18:03:59 -0500 Subject: [PATCH 0161/1437] drm/amd/display: Reset steer fifo before unblanking the stream [why] During mode transition steer fifo could overflow. Quite often it recovers by itself, but sometimes it doesn't. [how] Add steer fifo reset before unblanking the stream. Also add a short delay when resetting dig resync fifo to make sure register writes don't end up back-to-back, in which case the HW might miss the reset request. Signed-off-by: Nikola Cornij Reviewed-by: Tony Cheng Acked-by: Leo Li Signed-off-by: Alex Deucher --- .../drm/amd/display/dc/dcn20/dcn20_stream_encoder.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/amd/display/dc/dcn20/dcn20_stream_encoder.c b/drivers/gpu/drm/amd/display/dc/dcn20/dcn20_stream_encoder.c index 4b34016164348..fcb3877b4fcb2 100644 --- a/drivers/gpu/drm/amd/display/dc/dcn20/dcn20_stream_encoder.c +++ b/drivers/gpu/drm/amd/display/dc/dcn20/dcn20_stream_encoder.c @@ -492,15 +492,23 @@ void enc2_stream_encoder_dp_unblank( DP_VID_N_MUL, n_multiply); } - /* set DIG_START to 0x1 to reset FIFO */ + /* make sure stream is disabled before resetting steer fifo */ + REG_UPDATE(DP_VID_STREAM_CNTL, DP_VID_STREAM_ENABLE, false); + REG_WAIT(DP_VID_STREAM_CNTL, DP_VID_STREAM_STATUS, 0, 10, 5000); + /* set DIG_START to 0x1 to reset FIFO */ REG_UPDATE(DIG_FE_CNTL, DIG_START, 1); + udelay(1); /* write 0 to take the FIFO out of reset */ REG_UPDATE(DIG_FE_CNTL, DIG_START, 0); - /* switch DP encoder to CRTC data */ + /* switch DP encoder to CRTC data, but reset it the fifo first. It may happen + * that it overflows during mode transition, and sometimes doesn't recover. + */ + REG_UPDATE(DP_STEER_FIFO, DP_STEER_FIFO_RESET, 1); + udelay(10); REG_UPDATE(DP_STEER_FIFO, DP_STEER_FIFO_RESET, 0); -- GitLab From dd0b162fd00915728860a360c97752988782b8cc Mon Sep 17 00:00:00 2001 From: Amanda Liu Date: Fri, 15 Nov 2019 17:07:27 -0500 Subject: [PATCH 0162/1437] drm/amd/display: Fix screen tearing on vrr tests [Why] Screen tearing is present in tests when setting the frame rate to certain fps [How] Revert previous optimizations for low frame rates. Signed-off-by: Amanda Liu Reviewed-by: Aric Cyr Acked-by: Leo Li Signed-off-by: Alex Deucher --- .../amd/display/modules/freesync/freesync.c | 32 ++++++++----------- .../amd/display/modules/inc/mod_freesync.h | 1 - 2 files changed, 13 insertions(+), 20 deletions(-) diff --git a/drivers/gpu/drm/amd/display/modules/freesync/freesync.c b/drivers/gpu/drm/amd/display/modules/freesync/freesync.c index 16e69bbc69aaa..5437b50e9f90d 100644 --- a/drivers/gpu/drm/amd/display/modules/freesync/freesync.c +++ b/drivers/gpu/drm/amd/display/modules/freesync/freesync.c @@ -37,8 +37,8 @@ #define STATIC_SCREEN_RAMP_DELTA_REFRESH_RATE_PER_FRAME ((1000 / 60) * 65) /* Number of elements in the render times cache array */ #define RENDER_TIMES_MAX_COUNT 10 -/* Threshold to exit/exit BTR (to avoid frequent enter-exits at the lower limit) */ -#define BTR_MAX_MARGIN 2500 +/* Threshold to exit BTR (to avoid frequent enter-exits at the lower limit) */ +#define BTR_EXIT_MARGIN 2000 /* Threshold to change BTR multiplier (to avoid frequent changes) */ #define BTR_DRIFT_MARGIN 2000 /*Threshold to exit fixed refresh rate*/ @@ -254,22 +254,24 @@ static void apply_below_the_range(struct core_freesync *core_freesync, unsigned int delta_from_mid_point_in_us_1 = 0xFFFFFFFF; unsigned int delta_from_mid_point_in_us_2 = 0xFFFFFFFF; unsigned int frames_to_insert = 0; + unsigned int min_frame_duration_in_ns = 0; + unsigned int max_render_time_in_us = in_out_vrr->max_duration_in_us; unsigned int delta_from_mid_point_delta_in_us; - unsigned int max_render_time_in_us = - in_out_vrr->max_duration_in_us - in_out_vrr->btr.margin_in_us; + + min_frame_duration_in_ns = ((unsigned int) (div64_u64( + (1000000000ULL * 1000000), + in_out_vrr->max_refresh_in_uhz))); /* Program BTR */ - if ((last_render_time_in_us + in_out_vrr->btr.margin_in_us / 2) < max_render_time_in_us) { + if (last_render_time_in_us + BTR_EXIT_MARGIN < max_render_time_in_us) { /* Exit Below the Range */ if (in_out_vrr->btr.btr_active) { in_out_vrr->btr.frame_counter = 0; in_out_vrr->btr.btr_active = false; } - } else if (last_render_time_in_us > (max_render_time_in_us + in_out_vrr->btr.margin_in_us / 2)) { + } else if (last_render_time_in_us > max_render_time_in_us) { /* Enter Below the Range */ - if (!in_out_vrr->btr.btr_active) { - in_out_vrr->btr.btr_active = true; - } + in_out_vrr->btr.btr_active = true; } /* BTR set to "not active" so disengage */ @@ -325,9 +327,7 @@ static void apply_below_the_range(struct core_freesync *core_freesync, /* Choose number of frames to insert based on how close it * can get to the mid point of the variable range. */ - if ((frame_time_in_us / mid_point_frames_ceil) > in_out_vrr->min_duration_in_us && - (delta_from_mid_point_in_us_1 < delta_from_mid_point_in_us_2 || - mid_point_frames_floor < 2)) { + if (delta_from_mid_point_in_us_1 < delta_from_mid_point_in_us_2) { frames_to_insert = mid_point_frames_ceil; delta_from_mid_point_delta_in_us = delta_from_mid_point_in_us_2 - delta_from_mid_point_in_us_1; @@ -343,7 +343,7 @@ static void apply_below_the_range(struct core_freesync *core_freesync, if (in_out_vrr->btr.frames_to_insert != 0 && delta_from_mid_point_delta_in_us < BTR_DRIFT_MARGIN) { if (((last_render_time_in_us / in_out_vrr->btr.frames_to_insert) < - max_render_time_in_us) && + in_out_vrr->max_duration_in_us) && ((last_render_time_in_us / in_out_vrr->btr.frames_to_insert) > in_out_vrr->min_duration_in_us)) frames_to_insert = in_out_vrr->btr.frames_to_insert; @@ -796,11 +796,6 @@ void mod_freesync_build_vrr_params(struct mod_freesync *mod_freesync, refresh_range = in_out_vrr->max_refresh_in_uhz - in_out_vrr->min_refresh_in_uhz; - in_out_vrr->btr.margin_in_us = in_out_vrr->max_duration_in_us - - 2 * in_out_vrr->min_duration_in_us; - if (in_out_vrr->btr.margin_in_us > BTR_MAX_MARGIN) - in_out_vrr->btr.margin_in_us = BTR_MAX_MARGIN; - in_out_vrr->supported = true; } @@ -816,7 +811,6 @@ void mod_freesync_build_vrr_params(struct mod_freesync *mod_freesync, in_out_vrr->btr.inserted_duration_in_us = 0; in_out_vrr->btr.frames_to_insert = 0; in_out_vrr->btr.frame_counter = 0; - in_out_vrr->btr.mid_point_in_us = (in_out_vrr->min_duration_in_us + in_out_vrr->max_duration_in_us) / 2; diff --git a/drivers/gpu/drm/amd/display/modules/inc/mod_freesync.h b/drivers/gpu/drm/amd/display/modules/inc/mod_freesync.h index dbe7835aabcf7..dc187844d10b1 100644 --- a/drivers/gpu/drm/amd/display/modules/inc/mod_freesync.h +++ b/drivers/gpu/drm/amd/display/modules/inc/mod_freesync.h @@ -92,7 +92,6 @@ struct mod_vrr_params_btr { uint32_t inserted_duration_in_us; uint32_t frames_to_insert; uint32_t frame_counter; - uint32_t margin_in_us; }; struct mod_vrr_params_fixed_refresh { -- GitLab From 44ce6c3dc8479bb3ed68df13b502b0901675e7d6 Mon Sep 17 00:00:00 2001 From: Eric Yang Date: Fri, 15 Nov 2019 12:04:25 -0500 Subject: [PATCH 0163/1437] drm/amd/display: update dispclk and dppclk vco frequency Value obtained from DV is not allowing 8k60 CTA mode with DSC to pass, after checking real value being used in hw, find out that correct value is 3600, which will allow that mode. Signed-off-by: Eric Yang Reviewed-by: Tony Cheng Acked-by: Leo Li Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/display/dc/dcn21/dcn21_resource.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/display/dc/dcn21/dcn21_resource.c b/drivers/gpu/drm/amd/display/dc/dcn21/dcn21_resource.c index 6d99b47606950..c28cb5d50cf46 100644 --- a/drivers/gpu/drm/amd/display/dc/dcn21/dcn21_resource.c +++ b/drivers/gpu/drm/amd/display/dc/dcn21/dcn21_resource.c @@ -257,7 +257,7 @@ struct _vcs_dpi_soc_bounding_box_st dcn2_1_soc = { .vmm_page_size_bytes = 4096, .dram_clock_change_latency_us = 23.84, .return_bus_width_bytes = 64, - .dispclk_dppclk_vco_speed_mhz = 3550, + .dispclk_dppclk_vco_speed_mhz = 3600, .xfc_bus_transport_time_us = 4, .xfc_xbuf_latency_tolerance_us = 4, .use_urgent_burst_bw = 1, -- GitLab From 0df62823630d03d58f6eedb99fab285f847e4a1b Mon Sep 17 00:00:00 2001 From: George Shen Date: Fri, 15 Nov 2019 18:56:57 -0500 Subject: [PATCH 0164/1437] drm/amd/display: Increase the number of retries after AUX DEFER [Why] When a timeout occurs after a DEFER, some devices require more retries than in the case of a regular timeout. [How] In a timeout occurrence, check whether a DEFER has occurred before the timeout and retry MAX_DEFER_RETRIES retries times instead of MAX_TIMEOUT_RETRIES. Signed-off-by: George Shen Reviewed-by: Tony Cheng Acked-by: Abdoulaye Berthe Acked-by: Leo Li Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/display/dc/dce/dce_aux.c | 32 ++++++++++++++------ 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/drivers/gpu/drm/amd/display/dc/dce/dce_aux.c b/drivers/gpu/drm/amd/display/dc/dce/dce_aux.c index e472608faf335..46a603bfa4bd0 100644 --- a/drivers/gpu/drm/amd/display/dc/dce/dce_aux.c +++ b/drivers/gpu/drm/amd/display/dc/dce/dce_aux.c @@ -583,6 +583,8 @@ bool dce_aux_transfer_with_retries(struct ddc_service *ddc, uint8_t reply; bool payload_reply = true; enum aux_channel_operation_result operation_result; + bool retry_on_defer = false; + int aux_ack_retries = 0, aux_defer_retries = 0, aux_i2c_defer_retries = 0, @@ -613,8 +615,9 @@ bool dce_aux_transfer_with_retries(struct ddc_service *ddc, break; case AUX_TRANSACTION_REPLY_AUX_DEFER: - case AUX_TRANSACTION_REPLY_I2C_OVER_AUX_NACK: case AUX_TRANSACTION_REPLY_I2C_OVER_AUX_DEFER: + retry_on_defer = true; + case AUX_TRANSACTION_REPLY_I2C_OVER_AUX_NACK: if (++aux_defer_retries >= AUX_MAX_DEFER_RETRIES) { goto fail; } else { @@ -647,15 +650,24 @@ bool dce_aux_transfer_with_retries(struct ddc_service *ddc, break; case AUX_CHANNEL_OPERATION_FAILED_TIMEOUT: - if (++aux_timeout_retries >= AUX_MAX_TIMEOUT_RETRIES) - goto fail; - else { - /* - * DP 1.4, 2.8.2: AUX Transaction Response/Reply Timeouts - * According to the DP spec there should be 3 retries total - * with a 400us wait inbetween each. Hardware already waits - * for 550us therefore no wait is required here. - */ + // Check whether a DEFER had occurred before the timeout. + // If so, treat timeout as a DEFER. + if (retry_on_defer) { + if (++aux_defer_retries >= AUX_MAX_DEFER_RETRIES) + goto fail; + else if (payload->defer_delay > 0) + msleep(payload->defer_delay); + } else { + if (++aux_timeout_retries >= AUX_MAX_TIMEOUT_RETRIES) + goto fail; + else { + /* + * DP 1.4, 2.8.2: AUX Transaction Response/Reply Timeouts + * According to the DP spec there should be 3 retries total + * with a 400us wait inbetween each. Hardware already waits + * for 550us therefore no wait is required here. + */ + } } break; -- GitLab From 7a02d2985f41539ad2196775085d05f98f4a6040 Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Thu, 5 Dec 2019 16:38:01 -0500 Subject: [PATCH 0165/1437] drm/amdgpu/display: add fallthrough comment To avoid a compiler warning. Reviewed-by: Zhan Liu Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/display/dc/dce/dce_aux.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpu/drm/amd/display/dc/dce/dce_aux.c b/drivers/gpu/drm/amd/display/dc/dce/dce_aux.c index 46a603bfa4bd0..793c0cec407f9 100644 --- a/drivers/gpu/drm/amd/display/dc/dce/dce_aux.c +++ b/drivers/gpu/drm/amd/display/dc/dce/dce_aux.c @@ -617,6 +617,7 @@ bool dce_aux_transfer_with_retries(struct ddc_service *ddc, case AUX_TRANSACTION_REPLY_AUX_DEFER: case AUX_TRANSACTION_REPLY_I2C_OVER_AUX_DEFER: retry_on_defer = true; + /* fall through */ case AUX_TRANSACTION_REPLY_I2C_OVER_AUX_NACK: if (++aux_defer_retries >= AUX_MAX_DEFER_RETRIES) { goto fail; -- GitLab From e62dbcdb8cbcdc476175dc5a43ea7285c488fbbe Mon Sep 17 00:00:00 2001 From: Yongqiang Sun Date: Mon, 18 Nov 2019 13:45:50 -0500 Subject: [PATCH 0166/1437] drm/amd/display: Compare clock state member to determine optimization. [Why] It seems always request passive flip on RN due to incorrect compare clock state to determine optization. [How] Instead of calling memcmp, compare clock state member to determine the condition. Signed-off-by: Yongqiang Sun Reviewed-by: Tony Cheng Acked-by: Leo Li Signed-off-by: Alex Deucher --- .../amd/display/dc/clk_mgr/dcn21/rn_clk_mgr.c | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn21/rn_clk_mgr.c b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn21/rn_clk_mgr.c index 89ed230cdb267..35c55e54eac01 100644 --- a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn21/rn_clk_mgr.c +++ b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn21/rn_clk_mgr.c @@ -471,12 +471,28 @@ static void rn_notify_wm_ranges(struct clk_mgr *clk_mgr_base) } +static bool rn_are_clock_states_equal(struct dc_clocks *a, + struct dc_clocks *b) +{ + if (a->dispclk_khz != b->dispclk_khz) + return false; + else if (a->dppclk_khz != b->dppclk_khz) + return false; + else if (a->dcfclk_khz != b->dcfclk_khz) + return false; + else if (a->dcfclk_deep_sleep_khz != b->dcfclk_deep_sleep_khz) + return false; + + return true; +} + + static struct clk_mgr_funcs dcn21_funcs = { .get_dp_ref_clk_frequency = dce12_get_dp_ref_freq_khz, .update_clocks = rn_update_clocks, .init_clocks = rn_init_clocks, .enable_pme_wa = rn_enable_pme_wa, - /* .dump_clk_registers = rn_dump_clk_registers, */ + .are_clock_states_equal = rn_are_clock_states_equal, .notify_wm_ranges = rn_notify_wm_ranges }; -- GitLab From 1d9a6159bd04b676cb7d9b13245888fa450cec10 Mon Sep 17 00:00:00 2001 From: Kefeng Wang Date: Thu, 28 Nov 2019 08:47:49 +0800 Subject: [PATCH 0167/1437] workqueue: Use pr_warn instead of pr_warning Use pr_warn() instead of the remaining pr_warning() calls. Link: http://lkml.kernel.org/r/20191128004752.35268-2-wangkefeng.wang@huawei.com To: joe@perches.com To: linux-kernel@vger.kernel.org Cc: gregkh@linuxfoundation.org Cc: tj@kernel.org Cc: arnd@arndb.de Cc: sergey.senozhatsky@gmail.com Cc: rostedt@goodmis.org Signed-off-by: Kefeng Wang Acked-by: Tejun Heo Signed-off-by: Petr Mladek --- kernel/workqueue.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kernel/workqueue.c b/kernel/workqueue.c index bc88fd939f4e7..cfc923558e04d 100644 --- a/kernel/workqueue.c +++ b/kernel/workqueue.c @@ -4374,8 +4374,8 @@ void destroy_workqueue(struct workqueue_struct *wq) for_each_pwq(pwq, wq) { spin_lock_irq(&pwq->pool->lock); if (WARN_ON(pwq_busy(pwq))) { - pr_warning("%s: %s has the following busy pwq\n", - __func__, wq->name); + pr_warn("%s: %s has the following busy pwq\n", + __func__, wq->name); show_pwq(pwq); spin_unlock_irq(&pwq->pool->lock); mutex_unlock(&wq->mutex); -- GitLab From ee19545220a8663f0ca58c3427bc08fd6a104a42 Mon Sep 17 00:00:00 2001 From: Stephen Rothwell Date: Fri, 6 Dec 2019 09:25:03 +1100 Subject: [PATCH 0168/1437] Fix up for "printk: Drop pr_warning definition" Link: http://lkml.kernel.org/r/20191206092503.303d6a57@canb.auug.org.au Cc: Linux Next Mailing List Cc: Linux Kernel Mailing List Cc: "Steven Rostedt (VMware)" Cc: Kefeng Wang Cc: Linus Torvalds Signed-off-by: Stephen Rothwell Signed-off-by: Petr Mladek --- kernel/trace/ring_buffer.c | 2 +- kernel/trace/trace.c | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c index 4bf050fcfe3be..3f655371eaf6b 100644 --- a/kernel/trace/ring_buffer.c +++ b/kernel/trace/ring_buffer.c @@ -5070,7 +5070,7 @@ static __init int test_ringbuffer(void) int ret = 0; if (security_locked_down(LOCKDOWN_TRACEFS)) { - pr_warning("Lockdown is enabled, skipping ring buffer tests\n"); + pr_warn("Lockdown is enabled, skipping ring buffer tests\n"); return 0; } diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index 23459d53d5769..6c75410f96988 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c @@ -1889,7 +1889,7 @@ int __init register_tracer(struct tracer *type) } if (security_locked_down(LOCKDOWN_TRACEFS)) { - pr_warning("Can not register tracer %s due to lockdown\n", + pr_warn("Can not register tracer %s due to lockdown\n", type->name); return -EPERM; } @@ -8796,7 +8796,7 @@ struct dentry *tracing_init_dentry(void) struct trace_array *tr = &global_trace; if (security_locked_down(LOCKDOWN_TRACEFS)) { - pr_warning("Tracing disabled due to lockdown\n"); + pr_warn("Tracing disabled due to lockdown\n"); return ERR_PTR(-EPERM); } @@ -9244,7 +9244,7 @@ __init static int tracer_alloc_buffers(void) if (security_locked_down(LOCKDOWN_TRACEFS)) { - pr_warning("Tracing disabled due to lockdown\n"); + pr_warn("Tracing disabled due to lockdown\n"); return -EPERM; } -- GitLab From 61ff72f4016804b99d28988a57e65c217f01769d Mon Sep 17 00:00:00 2001 From: Kefeng Wang Date: Thu, 28 Nov 2019 08:47:51 +0800 Subject: [PATCH 0169/1437] printk: Drop pr_warning definition With all pr_warning are removed, saftely drop pr_warning definition. Link: http://lkml.kernel.org/r/20191128004752.35268-4-wangkefeng.wang@huawei.com To: joe@perches.com To: linux-kernel@vger.kernel.org Cc: gregkh@linuxfoundation.org Cc: tj@kernel.org Cc: arnd@arndb.de Cc: sergey.senozhatsky@gmail.com Cc: rostedt@goodmis.org Signed-off-by: Kefeng Wang Signed-off-by: Petr Mladek --- include/linux/printk.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/include/linux/printk.h b/include/linux/printk.h index c09d67edda3a1..1e6108b8d15fc 100644 --- a/include/linux/printk.h +++ b/include/linux/printk.h @@ -302,9 +302,8 @@ extern int kptr_restrict; printk(KERN_CRIT pr_fmt(fmt), ##__VA_ARGS__) #define pr_err(fmt, ...) \ printk(KERN_ERR pr_fmt(fmt), ##__VA_ARGS__) -#define pr_warning(fmt, ...) \ +#define pr_warn(fmt, ...) \ printk(KERN_WARNING pr_fmt(fmt), ##__VA_ARGS__) -#define pr_warn pr_warning #define pr_notice(fmt, ...) \ printk(KERN_NOTICE pr_fmt(fmt), ##__VA_ARGS__) #define pr_info(fmt, ...) \ -- GitLab From 969bea5e4d8b96d903637b100640acb92158c4e3 Mon Sep 17 00:00:00 2001 From: Kefeng Wang Date: Thu, 28 Nov 2019 08:47:52 +0800 Subject: [PATCH 0170/1437] checkpatch: Drop pr_warning check All pr_warning are removed from kernel, let's cleanup pr_warning check in checkpatch. Link: http://lkml.kernel.org/r/20191128004752.35268-5-wangkefeng.wang@huawei.com To: linux-kernel@vger.kernel.org Cc: gregkh@linuxfoundation.org Cc: tj@kernel.org Cc: arnd@arndb.de Cc: sergey.senozhatsky@gmail.com Cc: rostedt@goodmis.org Cc: Andy Whitcroft Signed-off-by: Kefeng Wang Acked-by: Joe Perches Signed-off-by: Petr Mladek --- scripts/checkpatch.pl | 9 --------- 1 file changed, 9 deletions(-) diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl index 592911a2f06ca..87831c535acb0 100755 --- a/scripts/checkpatch.pl +++ b/scripts/checkpatch.pl @@ -4121,15 +4121,6 @@ sub process { "Prefer [subsystem eg: netdev]_$level2([subsystem]dev, ... then dev_$level2(dev, ... then pr_$level(... to printk(KERN_$orig ...\n" . $herecurr); } - if ($line =~ /\bpr_warning\s*\(/) { - if (WARN("PREFER_PR_LEVEL", - "Prefer pr_warn(... to pr_warning(...\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ - s/\bpr_warning\b/pr_warn/; - } - } - if ($line =~ /\bdev_printk\s*\(\s*KERN_([A-Z]+)/) { my $orig = $1; my $level = lc($orig); -- GitLab From 7e0befd521b6d40af1d08fcdb29d4dbf30236624 Mon Sep 17 00:00:00 2001 From: Miaohe Lin Date: Thu, 21 Nov 2019 15:15:59 +0800 Subject: [PATCH 0171/1437] KVM: arm/arm64: Get rid of unused arg in cpu_init_hyp_mode() As arg dummy is not really needed, there's no need to pass NULL when calling cpu_init_hyp_mode(). So clean it up. Fixes: 67f691976662 ("arm64: kvm: allows kvm cpu hotplug") Reviewed-by: Steven Price Signed-off-by: Miaohe Lin Signed-off-by: Marc Zyngier Link: https://lore.kernel.org/r/1574320559-5662-1-git-send-email-linmiaohe@huawei.com --- virt/kvm/arm/arm.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/virt/kvm/arm/arm.c b/virt/kvm/arm/arm.c index 12e0280291cee..8de4daf25097d 100644 --- a/virt/kvm/arm/arm.c +++ b/virt/kvm/arm/arm.c @@ -1352,7 +1352,7 @@ long kvm_arch_vm_ioctl(struct file *filp, } } -static void cpu_init_hyp_mode(void *dummy) +static void cpu_init_hyp_mode(void) { phys_addr_t pgd_ptr; unsigned long hyp_stack_ptr; @@ -1386,7 +1386,7 @@ static void cpu_hyp_reinit(void) if (is_kernel_in_hyp_mode()) kvm_timer_init_vhe(); else - cpu_init_hyp_mode(NULL); + cpu_init_hyp_mode(); kvm_arm_init_debug(); -- GitLab From 0bda9498dd45280e334bfe88b815ebf519602cc3 Mon Sep 17 00:00:00 2001 From: Miaohe Lin Date: Thu, 28 Nov 2019 14:38:48 +0800 Subject: [PATCH 0172/1437] KVM: arm/arm64: vgic: Fix potential double free dist->spis in __kvm_vgic_destroy() In kvm_vgic_dist_init() called from kvm_vgic_map_resources(), if dist->vgic_model is invalid, dist->spis will be freed without set dist->spis = NULL. And in vgicv2 resources clean up path, __kvm_vgic_destroy() will be called to free allocated resources. And dist->spis will be freed again in clean up chain because we forget to set dist->spis = NULL in kvm_vgic_dist_init() failed path. So double free would happen. Signed-off-by: Miaohe Lin Signed-off-by: Marc Zyngier Reviewed-by: Eric Auger Link: https://lore.kernel.org/r/1574923128-19956-1-git-send-email-linmiaohe@huawei.com --- virt/kvm/arm/vgic/vgic-init.c | 1 + 1 file changed, 1 insertion(+) diff --git a/virt/kvm/arm/vgic/vgic-init.c b/virt/kvm/arm/vgic/vgic-init.c index b3c5de48064c9..7c58112ae67ca 100644 --- a/virt/kvm/arm/vgic/vgic-init.c +++ b/virt/kvm/arm/vgic/vgic-init.c @@ -177,6 +177,7 @@ static int kvm_vgic_dist_init(struct kvm *kvm, unsigned int nr_spis) break; default: kfree(dist->spis); + dist->spis = NULL; return -EINVAL; } } -- GitLab From 72a610f32e1f1664803218ec654a8e3b8fb2081a Mon Sep 17 00:00:00 2001 From: Miaohe Lin Date: Sat, 30 Nov 2019 10:45:18 +0800 Subject: [PATCH 0173/1437] KVM: arm/arm64: vgic: Use wrapper function to lock/unlock all vcpus in kvm_vgic_create() Use wrapper function lock_all_vcpus()/unlock_all_vcpus() in kvm_vgic_create() to remove duplicated code dealing with locking and unlocking all vcpus in a vm. Signed-off-by: Miaohe Lin Signed-off-by: Marc Zyngier Reviewed-by: Eric Auger Reviewed-by: Steven Price Link: https://lore.kernel.org/r/1575081918-11401-1-git-send-email-linmiaohe@huawei.com --- virt/kvm/arm/vgic/vgic-init.c | 19 ++++--------------- 1 file changed, 4 insertions(+), 15 deletions(-) diff --git a/virt/kvm/arm/vgic/vgic-init.c b/virt/kvm/arm/vgic/vgic-init.c index 7c58112ae67ca..a963b9d766b73 100644 --- a/virt/kvm/arm/vgic/vgic-init.c +++ b/virt/kvm/arm/vgic/vgic-init.c @@ -70,7 +70,7 @@ void kvm_vgic_early_init(struct kvm *kvm) */ int kvm_vgic_create(struct kvm *kvm, u32 type) { - int i, vcpu_lock_idx = -1, ret; + int i, ret; struct kvm_vcpu *vcpu; if (irqchip_in_kernel(kvm)) @@ -86,17 +86,9 @@ int kvm_vgic_create(struct kvm *kvm, u32 type) !kvm_vgic_global_state.can_emulate_gicv2) return -ENODEV; - /* - * Any time a vcpu is run, vcpu_load is called which tries to grab the - * vcpu->mutex. By grabbing the vcpu->mutex of all VCPUs we ensure - * that no other VCPUs are run while we create the vgic. - */ ret = -EBUSY; - kvm_for_each_vcpu(i, vcpu, kvm) { - if (!mutex_trylock(&vcpu->mutex)) - goto out_unlock; - vcpu_lock_idx = i; - } + if (!lock_all_vcpus(kvm)) + return ret; kvm_for_each_vcpu(i, vcpu, kvm) { if (vcpu->arch.has_run_once) @@ -125,10 +117,7 @@ int kvm_vgic_create(struct kvm *kvm, u32 type) INIT_LIST_HEAD(&kvm->arch.vgic.rd_regions); out_unlock: - for (; vcpu_lock_idx >= 0; vcpu_lock_idx--) { - vcpu = kvm_get_vcpu(kvm, vcpu_lock_idx); - mutex_unlock(&vcpu->mutex); - } + unlock_all_vcpus(kvm); return ret; } -- GitLab From bf4b96bb542ee3fe913d0b9eb4d9c1046c429ac6 Mon Sep 17 00:00:00 2001 From: Mark Rutland Date: Thu, 5 Dec 2019 18:06:51 +0000 Subject: [PATCH 0174/1437] KVM: arm64: Sanely ratelimit sysreg messages Currently kvm_pr_unimpl() is ratelimited, so print_sys_reg_instr() won't spam the console. However, someof its callers try to print some contextual information with kvm_err(), which is not ratelimited. This means that in some cases the context may be printed without the sysreg encoding, which isn't all that useful. Let's ensure that both are consistently printed together and ratelimited, by refactoring print_sys_reg_instr() so that some callers can provide it with an arbitrary format string. Signed-off-by: Mark Rutland Signed-off-by: Marc Zyngier Link: https://lore.kernel.org/r/20191205180652.18671-2-mark.rutland@arm.com --- arch/arm64/kvm/sys_regs.c | 12 ++++++------ arch/arm64/kvm/sys_regs.h | 17 +++++++++++++++-- 2 files changed, 21 insertions(+), 8 deletions(-) diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c index 2071260a275bd..e8bf08e09f783 100644 --- a/arch/arm64/kvm/sys_regs.c +++ b/arch/arm64/kvm/sys_regs.c @@ -2094,9 +2094,9 @@ static void unhandled_cp_access(struct kvm_vcpu *vcpu, WARN_ON(1); } - kvm_err("Unsupported guest CP%d access at: %08lx [%08lx]\n", - cp, *vcpu_pc(vcpu), *vcpu_cpsr(vcpu)); - print_sys_reg_instr(params); + print_sys_reg_msg(params, + "Unsupported guest CP%d access at: %08lx [%08lx]\n", + cp, *vcpu_pc(vcpu), *vcpu_cpsr(vcpu)); kvm_inject_undefined(vcpu); } @@ -2245,9 +2245,9 @@ static int emulate_sys_reg(struct kvm_vcpu *vcpu, if (likely(r)) { perform_access(vcpu, params, r); } else { - kvm_err("Unsupported guest sys_reg access at: %lx [%08lx]\n", - *vcpu_pc(vcpu), *vcpu_cpsr(vcpu)); - print_sys_reg_instr(params); + print_sys_reg_msg(params, + "Unsupported guest sys_reg access at: %lx [%08lx]\n", + *vcpu_pc(vcpu), *vcpu_cpsr(vcpu)); kvm_inject_undefined(vcpu); } return 1; diff --git a/arch/arm64/kvm/sys_regs.h b/arch/arm64/kvm/sys_regs.h index 9bca0312d7982..5a6fc30f59894 100644 --- a/arch/arm64/kvm/sys_regs.h +++ b/arch/arm64/kvm/sys_regs.h @@ -62,11 +62,24 @@ struct sys_reg_desc { #define REG_HIDDEN_USER (1 << 0) /* hidden from userspace ioctls */ #define REG_HIDDEN_GUEST (1 << 1) /* hidden from guest */ -static inline void print_sys_reg_instr(const struct sys_reg_params *p) +static __printf(2, 3) +inline void print_sys_reg_msg(const struct sys_reg_params *p, + char *fmt, ...) { + va_list va; + + va_start(va, fmt); /* Look, we even formatted it for you to paste into the table! */ - kvm_pr_unimpl(" { Op0(%2u), Op1(%2u), CRn(%2u), CRm(%2u), Op2(%2u), func_%s },\n", + kvm_pr_unimpl("%pV { Op0(%2u), Op1(%2u), CRn(%2u), CRm(%2u), Op2(%2u), func_%s },\n", + &(struct va_format){ fmt, &va }, p->Op0, p->Op1, p->CRn, p->CRm, p->Op2, p->is_write ? "write" : "read"); + va_end(va); +} + +static inline void print_sys_reg_instr(const struct sys_reg_params *p) +{ + /* GCC warns on an empty format string */ + print_sys_reg_msg(p, "%s", ""); } static inline bool ignore_write(struct kvm_vcpu *vcpu, -- GitLab From 4adf0b49eea926a55fd956ef7d86750f771435ff Mon Sep 17 00:00:00 2001 From: Thomas Zimmermann Date: Fri, 6 Dec 2019 09:19:01 +0100 Subject: [PATCH 0175/1437] drm/mgag200: Flag all G200 SE A machines as broken wrt MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Several MGA G200 SE machines don't respect the value of the startadd register field. After more feedback on affected machines, neither PCI subvendor ID nor the internal ID seem to hint towards the bug. All affected machines have a PCI ID of 0x0522 (i.e., G200 SE A). It was decided to flag all G200 SE A machines as broken. Signed-off-by: Thomas Zimmermann Acked-by: Gerd Hoffmann Fixes: 1591fadf857c ("drm/mgag200: Add workaround for HW that does not support 'startadd'") Cc: Thomas Zimmermann Cc: John Donnelly Cc: Daniel Vetter Cc: Gerd Hoffmann Cc: Dave Airlie Cc: Maarten Lankhorst Cc: Maxime Ripard Cc: David Airlie Cc: Sam Ravnborg Cc: "Y.C. Chen" Cc: Neil Armstrong Cc: Thomas Gleixner Cc: "José Roberto de Souza" Cc: Andrzej Pietrasiewicz Cc: dri-devel@lists.freedesktop.org Cc: # v5.3+ Cc: Greg Kroah-Hartman Cc: Allison Randal Cc: Alex Deucher Cc: "Noralf Trønnes" Link: https://patchwork.freedesktop.org/patch/msgid/20191206081901.9938-1-tzimmermann@suse.de --- drivers/gpu/drm/mgag200/mgag200_drv.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/gpu/drm/mgag200/mgag200_drv.c b/drivers/gpu/drm/mgag200/mgag200_drv.c index d43951caeea02..b113876c24283 100644 --- a/drivers/gpu/drm/mgag200/mgag200_drv.c +++ b/drivers/gpu/drm/mgag200/mgag200_drv.c @@ -30,9 +30,8 @@ module_param_named(modeset, mgag200_modeset, int, 0400); static struct drm_driver driver; static const struct pci_device_id pciidlist[] = { - { PCI_VENDOR_ID_MATROX, 0x522, PCI_VENDOR_ID_SUN, 0x4852, 0, 0, + { PCI_VENDOR_ID_MATROX, 0x522, PCI_ANY_ID, PCI_ANY_ID, 0, 0, G200_SE_A | MGAG200_FLAG_HW_BUG_NO_STARTADD}, - { PCI_VENDOR_ID_MATROX, 0x522, PCI_ANY_ID, PCI_ANY_ID, 0, 0, G200_SE_A }, { PCI_VENDOR_ID_MATROX, 0x524, PCI_ANY_ID, PCI_ANY_ID, 0, 0, G200_SE_B }, { PCI_VENDOR_ID_MATROX, 0x530, PCI_ANY_ID, PCI_ANY_ID, 0, 0, G200_EV }, { PCI_VENDOR_ID_MATROX, 0x532, PCI_ANY_ID, PCI_ANY_ID, 0, 0, G200_WB }, -- GitLab From 694cfe7f31db36912725e63a38a5179c8628a496 Mon Sep 17 00:00:00 2001 From: Nikos Tsironis Date: Wed, 4 Dec 2019 16:07:42 +0200 Subject: [PATCH 0176/1437] dm thin: Flush data device before committing metadata The thin provisioning target maintains per thin device mappings that map virtual blocks to data blocks in the data device. When we write to a shared block, in case of internal snapshots, or provision a new block, in case of external snapshots, we copy the shared block to a new data block (COW), update the mapping for the relevant virtual block and then issue the write to the new data block. Suppose the data device has a volatile write-back cache and the following sequence of events occur: 1. We write to a shared block 2. A new data block is allocated 3. We copy the shared block to the new data block using kcopyd (COW) 4. We insert the new mapping for the virtual block in the btree for that thin device. 5. The commit timeout expires and we commit the metadata, that now includes the new mapping from step (4). 6. The system crashes and the data device's cache has not been flushed, meaning that the COWed data are lost. The next time we read that virtual block of the thin device we read it from the data block allocated in step (2), since the metadata have been successfully committed. The data are lost due to the crash, so we read garbage instead of the old, shared data. This has the following implications: 1. In case of writes to shared blocks, with size smaller than the pool's block size (which means we first copy the whole block and then issue the smaller write), we corrupt data that the user never touched. 2. In case of writes to shared blocks, with size equal to the device's logical block size, we fail to provide atomic sector writes. When the system recovers the user will read garbage from that sector instead of the old data or the new data. 3. Even for writes to shared blocks, with size equal to the pool's block size (overwrites), after the system recovers, the written sectors will contain garbage instead of a random mix of sectors containing either old data or new data, thus we fail again to provide atomic sectors writes. 4. Even when the user flushes the thin device, because we first commit the metadata and then pass down the flush, the same risk for corruption exists (if the system crashes after the metadata have been committed but before the flush is passed down to the data device.) The only case which is unaffected is that of writes with size equal to the pool's block size and with the FUA flag set. But, because FUA writes trigger metadata commits, this case can trigger the corruption indirectly. Moreover, apart from internal and external snapshots, the same issue exists for newly provisioned blocks, when block zeroing is enabled. After the system recovers the provisioned blocks might contain garbage instead of zeroes. To solve this and avoid the potential data corruption we flush the pool's data device **before** committing its metadata. This ensures that the data blocks of any newly inserted mappings are properly written to non-volatile storage and won't be lost in case of a crash. Cc: stable@vger.kernel.org Signed-off-by: Nikos Tsironis Acked-by: Joe Thornber Signed-off-by: Mike Snitzer --- drivers/md/dm-thin.c | 42 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 40 insertions(+), 2 deletions(-) diff --git a/drivers/md/dm-thin.c b/drivers/md/dm-thin.c index 5a2c494cb5528..57626c27a54bb 100644 --- a/drivers/md/dm-thin.c +++ b/drivers/md/dm-thin.c @@ -328,6 +328,7 @@ struct pool_c { dm_block_t low_water_blocks; struct pool_features requested_pf; /* Features requested during table load */ struct pool_features adjusted_pf; /* Features used after adjusting for constituent devices */ + struct bio flush_bio; }; /* @@ -2383,8 +2384,16 @@ static void process_deferred_bios(struct pool *pool) while ((bio = bio_list_pop(&bio_completions))) bio_endio(bio); - while ((bio = bio_list_pop(&bios))) - generic_make_request(bio); + while ((bio = bio_list_pop(&bios))) { + /* + * The data device was flushed as part of metadata commit, + * so complete redundant flushes immediately. + */ + if (bio->bi_opf & REQ_PREFLUSH) + bio_endio(bio); + else + generic_make_request(bio); + } } static void do_worker(struct work_struct *ws) @@ -3115,6 +3124,7 @@ static void pool_dtr(struct dm_target *ti) __pool_dec(pt->pool); dm_put_device(ti, pt->metadata_dev); dm_put_device(ti, pt->data_dev); + bio_uninit(&pt->flush_bio); kfree(pt); mutex_unlock(&dm_thin_pool_table.mutex); @@ -3180,6 +3190,29 @@ static void metadata_low_callback(void *context) dm_table_event(pool->ti->table); } +/* + * We need to flush the data device **before** committing the metadata. + * + * This ensures that the data blocks of any newly inserted mappings are + * properly written to non-volatile storage and won't be lost in case of a + * crash. + * + * Failure to do so can result in data corruption in the case of internal or + * external snapshots and in the case of newly provisioned blocks, when block + * zeroing is enabled. + */ +static int metadata_pre_commit_callback(void *context) +{ + struct pool_c *pt = context; + struct bio *flush_bio = &pt->flush_bio; + + bio_reset(flush_bio); + bio_set_dev(flush_bio, pt->data_dev->bdev); + flush_bio->bi_opf = REQ_OP_WRITE | REQ_PREFLUSH; + + return submit_bio_wait(flush_bio); +} + static sector_t get_dev_size(struct block_device *bdev) { return i_size_read(bdev->bd_inode) >> SECTOR_SHIFT; @@ -3348,6 +3381,7 @@ static int pool_ctr(struct dm_target *ti, unsigned argc, char **argv) pt->data_dev = data_dev; pt->low_water_blocks = low_water_blocks; pt->adjusted_pf = pt->requested_pf = pf; + bio_init(&pt->flush_bio, NULL, 0); ti->num_flush_bios = 1; /* @@ -3374,6 +3408,10 @@ static int pool_ctr(struct dm_target *ti, unsigned argc, char **argv) if (r) goto out_flags_changed; + dm_pool_register_pre_commit_callback(pt->pool->pmd, + metadata_pre_commit_callback, + pt); + pt->callbacks.congested_fn = pool_is_congested; dm_table_add_target_callbacks(ti->table, &pt->callbacks); -- GitLab From 22bd4df9dadf46f56f2eb16859439a7a600d106a Mon Sep 17 00:00:00 2001 From: Steven Price Date: Mon, 18 Nov 2019 17:30:02 +0000 Subject: [PATCH 0177/1437] drm/panfrost: devfreq: Round frequencies to OPPs Currently when setting a frequency in panfrost_devfreq_target the returned frequency is the actual frequency that the clock driver reports (the return of clk_get_rate()). However, where the provided OPPs don't precisely match the frequencies that the clock actually achieves devfreq will then complain (repeatedly): devfreq devfreq0: Couldn't update frequency transition information. To avoid this change panfrost_devfreq_target() to fetch the opp using devfreq_recommened_opp() and not actually query the clock for the frequency. A similar problem exists with panfrost_devfreq_get_cur_freq(), but in this case because the function is optional we can just remove it and devfreq will fall back to using the previously set frequency. Fixes: 221bc77914cb ("drm/panfrost: Use generic code for devfreq") Signed-off-by: Steven Price Reviewed-by: Alyssa Rosenzweig Signed-off-by: Rob Herring Link: https://patchwork.freedesktop.org/patch/msgid/20191118173002.32015-1-steven.price@arm.com --- drivers/gpu/drm/panfrost/panfrost_devfreq.c | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/drivers/gpu/drm/panfrost/panfrost_devfreq.c b/drivers/gpu/drm/panfrost/panfrost_devfreq.c index 4c4e8a30a1ac3..536ba93b0f463 100644 --- a/drivers/gpu/drm/panfrost/panfrost_devfreq.c +++ b/drivers/gpu/drm/panfrost/panfrost_devfreq.c @@ -18,15 +18,18 @@ static void panfrost_devfreq_update_utilization(struct panfrost_device *pfdev); static int panfrost_devfreq_target(struct device *dev, unsigned long *freq, u32 flags) { - struct panfrost_device *pfdev = dev_get_drvdata(dev); + struct dev_pm_opp *opp; int err; + opp = devfreq_recommended_opp(dev, freq, flags); + if (IS_ERR(opp)) + return PTR_ERR(opp); + dev_pm_opp_put(opp); + err = dev_pm_opp_set_rate(dev, *freq); if (err) return err; - *freq = clk_get_rate(pfdev->clock); - return 0; } @@ -60,20 +63,10 @@ static int panfrost_devfreq_get_dev_status(struct device *dev, return 0; } -static int panfrost_devfreq_get_cur_freq(struct device *dev, unsigned long *freq) -{ - struct panfrost_device *pfdev = platform_get_drvdata(to_platform_device(dev)); - - *freq = clk_get_rate(pfdev->clock); - - return 0; -} - static struct devfreq_dev_profile panfrost_devfreq_profile = { .polling_ms = 50, /* ~3 frames */ .target = panfrost_devfreq_target, .get_dev_status = panfrost_devfreq_get_dev_status, - .get_cur_freq = panfrost_devfreq_get_cur_freq, }; int panfrost_devfreq_init(struct panfrost_device *pfdev) -- GitLab From 70cc77952efebf6722d483cb83cfb563ac9768db Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Fri, 29 Nov 2019 14:59:02 +0100 Subject: [PATCH 0178/1437] drm/panfrost: Fix a race in panfrost_ioctl_madvise() If 2 threads change the MADVISE property of the same BO in parallel we might end up with an shmem->madv value that's inconsistent with the presence of the BO in the shrinker list. The easiest solution to fix that is to protect the drm_gem_shmem_madvise() call with the shrinker lock. Fixes: 013b65101315 ("drm/panfrost: Add madvise and shrinker support") Cc: Signed-off-by: Boris Brezillon Reviewed-by: Steven Price Acked-by: Alyssa Rosenzweig Signed-off-by: Rob Herring Link: https://patchwork.freedesktop.org/patch/msgid/20191129135908.2439529-3-boris.brezillon@collabora.com --- drivers/gpu/drm/panfrost/panfrost_drv.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/panfrost/panfrost_drv.c b/drivers/gpu/drm/panfrost/panfrost_drv.c index 9d086133a84ed..8b8d6546cfac2 100644 --- a/drivers/gpu/drm/panfrost/panfrost_drv.c +++ b/drivers/gpu/drm/panfrost/panfrost_drv.c @@ -347,20 +347,19 @@ static int panfrost_ioctl_madvise(struct drm_device *dev, void *data, return -ENOENT; } + mutex_lock(&pfdev->shrinker_lock); args->retained = drm_gem_shmem_madvise(gem_obj, args->madv); if (args->retained) { struct panfrost_gem_object *bo = to_panfrost_bo(gem_obj); - mutex_lock(&pfdev->shrinker_lock); - if (args->madv == PANFROST_MADV_DONTNEED) - list_add_tail(&bo->base.madv_list, &pfdev->shrinker_list); + list_add_tail(&bo->base.madv_list, + &pfdev->shrinker_list); else if (args->madv == PANFROST_MADV_WILLNEED) list_del_init(&bo->base.madv_list); - - mutex_unlock(&pfdev->shrinker_lock); } + mutex_unlock(&pfdev->shrinker_lock); drm_gem_object_put_unlocked(gem_obj); return 0; -- GitLab From 3bb69dbcb9e8430e0cc9990cff427ca3ae25ffdc Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Fri, 29 Nov 2019 14:59:03 +0100 Subject: [PATCH 0179/1437] drm/panfrost: Fix a BO leak in panfrost_ioctl_mmap_bo() We should release the reference we grabbed when an error occurs. Fixes: 187d2929206e ("drm/panfrost: Add support for GPU heap allocations") Cc: Signed-off-by: Boris Brezillon Reviewed-by: Steven Price Acked-by: Alyssa Rosenzweig Signed-off-by: Rob Herring Link: https://patchwork.freedesktop.org/patch/msgid/20191129135908.2439529-4-boris.brezillon@collabora.com --- drivers/gpu/drm/panfrost/panfrost_drv.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/panfrost/panfrost_drv.c b/drivers/gpu/drm/panfrost/panfrost_drv.c index 8b8d6546cfac2..0a5d82078bc85 100644 --- a/drivers/gpu/drm/panfrost/panfrost_drv.c +++ b/drivers/gpu/drm/panfrost/panfrost_drv.c @@ -303,14 +303,17 @@ static int panfrost_ioctl_mmap_bo(struct drm_device *dev, void *data, } /* Don't allow mmapping of heap objects as pages are not pinned. */ - if (to_panfrost_bo(gem_obj)->is_heap) - return -EINVAL; + if (to_panfrost_bo(gem_obj)->is_heap) { + ret = -EINVAL; + goto out; + } ret = drm_gem_create_mmap_offset(gem_obj); if (ret == 0) args->offset = drm_vma_node_offset_addr(&gem_obj->vma_node); - drm_gem_object_put_unlocked(gem_obj); +out: + drm_gem_object_put_unlocked(gem_obj); return ret; } -- GitLab From aed44cbeae2b7674cd155ba5cc6506aafe46a94e Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Fri, 29 Nov 2019 14:59:04 +0100 Subject: [PATCH 0180/1437] drm/panfrost: Fix a race in panfrost_gem_free_object() panfrost_gem_shrinker_scan() might purge a BO (release the sgt and kill the GPU mapping) that's being freed by panfrost_gem_free_object() if we don't remove the BO from the shrinker list at the beginning of panfrost_gem_free_object(). Fixes: 013b65101315 ("drm/panfrost: Add madvise and shrinker support") Cc: Signed-off-by: Boris Brezillon Reviewed-by: Steven Price Acked-by: Alyssa Rosenzweig Signed-off-by: Rob Herring Link: https://patchwork.freedesktop.org/patch/msgid/20191129135908.2439529-5-boris.brezillon@collabora.com --- drivers/gpu/drm/panfrost/panfrost_gem.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/panfrost/panfrost_gem.c b/drivers/gpu/drm/panfrost/panfrost_gem.c index deca0c30bbd43..fa1b2732a3a8a 100644 --- a/drivers/gpu/drm/panfrost/panfrost_gem.c +++ b/drivers/gpu/drm/panfrost/panfrost_gem.c @@ -19,6 +19,16 @@ static void panfrost_gem_free_object(struct drm_gem_object *obj) struct panfrost_gem_object *bo = to_panfrost_bo(obj); struct panfrost_device *pfdev = obj->dev->dev_private; + /* + * Make sure the BO is no longer inserted in the shrinker list before + * taking care of the destruction itself. If we don't do that we have a + * race condition between this function and what's done in + * panfrost_gem_shrinker_scan(). + */ + mutex_lock(&pfdev->shrinker_lock); + list_del_init(&bo->base.madv_list); + mutex_unlock(&pfdev->shrinker_lock); + if (bo->sgts) { int i; int n_sgt = bo->base.base.size / SZ_2M; @@ -33,11 +43,6 @@ static void panfrost_gem_free_object(struct drm_gem_object *obj) kfree(bo->sgts); } - mutex_lock(&pfdev->shrinker_lock); - if (!list_empty(&bo->base.madv_list)) - list_del(&bo->base.madv_list); - mutex_unlock(&pfdev->shrinker_lock); - drm_gem_shmem_free_object(obj); } -- GitLab From 0a5239985a3bc084738851afdf3fceb7d5651b0c Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Fri, 29 Nov 2019 14:59:05 +0100 Subject: [PATCH 0181/1437] drm/panfrost: Open/close the perfcnt BO Commit a5efb4c9a562 ("drm/panfrost: Restructure the GEM object creation") moved the drm_mm_insert_node_generic() call to the gem->open() hook, but forgot to update perfcnt accordingly. Patch the perfcnt logic to call panfrost_gem_open/close() where appropriate. Fixes: a5efb4c9a562 ("drm/panfrost: Restructure the GEM object creation") Cc: Signed-off-by: Boris Brezillon Reviewed-by: Steven Price Acked-by: Alyssa Rosenzweig Signed-off-by: Rob Herring Link: https://patchwork.freedesktop.org/patch/msgid/20191129135908.2439529-6-boris.brezillon@collabora.com --- drivers/gpu/drm/panfrost/panfrost_drv.c | 2 +- drivers/gpu/drm/panfrost/panfrost_gem.c | 4 ++-- drivers/gpu/drm/panfrost/panfrost_gem.h | 4 ++++ drivers/gpu/drm/panfrost/panfrost_perfcnt.c | 23 +++++++++++++-------- drivers/gpu/drm/panfrost/panfrost_perfcnt.h | 2 +- 5 files changed, 22 insertions(+), 13 deletions(-) diff --git a/drivers/gpu/drm/panfrost/panfrost_drv.c b/drivers/gpu/drm/panfrost/panfrost_drv.c index 0a5d82078bc85..ca9ba7534eb7d 100644 --- a/drivers/gpu/drm/panfrost/panfrost_drv.c +++ b/drivers/gpu/drm/panfrost/panfrost_drv.c @@ -445,7 +445,7 @@ panfrost_postclose(struct drm_device *dev, struct drm_file *file) { struct panfrost_file_priv *panfrost_priv = file->driver_priv; - panfrost_perfcnt_close(panfrost_priv); + panfrost_perfcnt_close(file); panfrost_job_close(panfrost_priv); panfrost_mmu_pgtable_free(panfrost_priv); diff --git a/drivers/gpu/drm/panfrost/panfrost_gem.c b/drivers/gpu/drm/panfrost/panfrost_gem.c index fa1b2732a3a8a..fd766b1395fb1 100644 --- a/drivers/gpu/drm/panfrost/panfrost_gem.c +++ b/drivers/gpu/drm/panfrost/panfrost_gem.c @@ -46,7 +46,7 @@ static void panfrost_gem_free_object(struct drm_gem_object *obj) drm_gem_shmem_free_object(obj); } -static int panfrost_gem_open(struct drm_gem_object *obj, struct drm_file *file_priv) +int panfrost_gem_open(struct drm_gem_object *obj, struct drm_file *file_priv) { int ret; size_t size = obj->size; @@ -85,7 +85,7 @@ static int panfrost_gem_open(struct drm_gem_object *obj, struct drm_file *file_p return ret; } -static void panfrost_gem_close(struct drm_gem_object *obj, struct drm_file *file_priv) +void panfrost_gem_close(struct drm_gem_object *obj, struct drm_file *file_priv) { struct panfrost_gem_object *bo = to_panfrost_bo(obj); struct panfrost_file_priv *priv = file_priv->driver_priv; diff --git a/drivers/gpu/drm/panfrost/panfrost_gem.h b/drivers/gpu/drm/panfrost/panfrost_gem.h index 50920819cc169..4b17e73087643 100644 --- a/drivers/gpu/drm/panfrost/panfrost_gem.h +++ b/drivers/gpu/drm/panfrost/panfrost_gem.h @@ -45,6 +45,10 @@ panfrost_gem_create_with_handle(struct drm_file *file_priv, u32 flags, uint32_t *handle); +int panfrost_gem_open(struct drm_gem_object *obj, struct drm_file *file_priv); +void panfrost_gem_close(struct drm_gem_object *obj, + struct drm_file *file_priv); + void panfrost_gem_shrinker_init(struct drm_device *dev); void panfrost_gem_shrinker_cleanup(struct drm_device *dev); diff --git a/drivers/gpu/drm/panfrost/panfrost_perfcnt.c b/drivers/gpu/drm/panfrost/panfrost_perfcnt.c index 83c57d325ca87..33054b7524ea4 100644 --- a/drivers/gpu/drm/panfrost/panfrost_perfcnt.c +++ b/drivers/gpu/drm/panfrost/panfrost_perfcnt.c @@ -66,9 +66,10 @@ static int panfrost_perfcnt_dump_locked(struct panfrost_device *pfdev) } static int panfrost_perfcnt_enable_locked(struct panfrost_device *pfdev, - struct panfrost_file_priv *user, + struct drm_file *file_priv, unsigned int counterset) { + struct panfrost_file_priv *user = file_priv->driver_priv; struct panfrost_perfcnt *perfcnt = pfdev->perfcnt; struct drm_gem_shmem_object *bo; u32 cfg; @@ -90,14 +91,14 @@ static int panfrost_perfcnt_enable_locked(struct panfrost_device *pfdev, perfcnt->bo = to_panfrost_bo(&bo->base); /* Map the perfcnt buf in the address space attached to file_priv. */ - ret = panfrost_mmu_map(perfcnt->bo); + ret = panfrost_gem_open(&perfcnt->bo->base.base, file_priv); if (ret) goto err_put_bo; perfcnt->buf = drm_gem_shmem_vmap(&bo->base); if (IS_ERR(perfcnt->buf)) { ret = PTR_ERR(perfcnt->buf); - goto err_put_bo; + goto err_close_bo; } /* @@ -156,14 +157,17 @@ static int panfrost_perfcnt_enable_locked(struct panfrost_device *pfdev, err_vunmap: drm_gem_shmem_vunmap(&perfcnt->bo->base.base, perfcnt->buf); +err_close_bo: + panfrost_gem_close(&perfcnt->bo->base.base, file_priv); err_put_bo: drm_gem_object_put_unlocked(&bo->base); return ret; } static int panfrost_perfcnt_disable_locked(struct panfrost_device *pfdev, - struct panfrost_file_priv *user) + struct drm_file *file_priv) { + struct panfrost_file_priv *user = file_priv->driver_priv; struct panfrost_perfcnt *perfcnt = pfdev->perfcnt; if (user != perfcnt->user) @@ -179,6 +183,7 @@ static int panfrost_perfcnt_disable_locked(struct panfrost_device *pfdev, perfcnt->user = NULL; drm_gem_shmem_vunmap(&perfcnt->bo->base.base, perfcnt->buf); perfcnt->buf = NULL; + panfrost_gem_close(&perfcnt->bo->base.base, file_priv); drm_gem_object_put_unlocked(&perfcnt->bo->base.base); perfcnt->bo = NULL; pm_runtime_mark_last_busy(pfdev->dev); @@ -190,7 +195,6 @@ static int panfrost_perfcnt_disable_locked(struct panfrost_device *pfdev, int panfrost_ioctl_perfcnt_enable(struct drm_device *dev, void *data, struct drm_file *file_priv) { - struct panfrost_file_priv *pfile = file_priv->driver_priv; struct panfrost_device *pfdev = dev->dev_private; struct panfrost_perfcnt *perfcnt = pfdev->perfcnt; struct drm_panfrost_perfcnt_enable *req = data; @@ -206,10 +210,10 @@ int panfrost_ioctl_perfcnt_enable(struct drm_device *dev, void *data, mutex_lock(&perfcnt->lock); if (req->enable) - ret = panfrost_perfcnt_enable_locked(pfdev, pfile, + ret = panfrost_perfcnt_enable_locked(pfdev, file_priv, req->counterset); else - ret = panfrost_perfcnt_disable_locked(pfdev, pfile); + ret = panfrost_perfcnt_disable_locked(pfdev, file_priv); mutex_unlock(&perfcnt->lock); return ret; @@ -247,15 +251,16 @@ out: return ret; } -void panfrost_perfcnt_close(struct panfrost_file_priv *pfile) +void panfrost_perfcnt_close(struct drm_file *file_priv) { + struct panfrost_file_priv *pfile = file_priv->driver_priv; struct panfrost_device *pfdev = pfile->pfdev; struct panfrost_perfcnt *perfcnt = pfdev->perfcnt; pm_runtime_get_sync(pfdev->dev); mutex_lock(&perfcnt->lock); if (perfcnt->user == pfile) - panfrost_perfcnt_disable_locked(pfdev, pfile); + panfrost_perfcnt_disable_locked(pfdev, file_priv); mutex_unlock(&perfcnt->lock); pm_runtime_mark_last_busy(pfdev->dev); pm_runtime_put_autosuspend(pfdev->dev); diff --git a/drivers/gpu/drm/panfrost/panfrost_perfcnt.h b/drivers/gpu/drm/panfrost/panfrost_perfcnt.h index 13b8fdaa1b432..8bbcf5f5fb339 100644 --- a/drivers/gpu/drm/panfrost/panfrost_perfcnt.h +++ b/drivers/gpu/drm/panfrost/panfrost_perfcnt.h @@ -9,7 +9,7 @@ void panfrost_perfcnt_sample_done(struct panfrost_device *pfdev); void panfrost_perfcnt_clean_cache_done(struct panfrost_device *pfdev); int panfrost_perfcnt_init(struct panfrost_device *pfdev); void panfrost_perfcnt_fini(struct panfrost_device *pfdev); -void panfrost_perfcnt_close(struct panfrost_file_priv *pfile); +void panfrost_perfcnt_close(struct drm_file *file_priv); int panfrost_ioctl_perfcnt_enable(struct drm_device *dev, void *data, struct drm_file *file_priv); int panfrost_ioctl_perfcnt_dump(struct drm_device *dev, void *data, -- GitLab From 3f68baf706ec68c4120867c25bc439c845fe3e17 Mon Sep 17 00:00:00 2001 From: Keith Busch Date: Sat, 7 Dec 2019 01:51:54 +0900 Subject: [PATCH 0182/1437] nvme/pci: Fix write and poll queue types The number of poll or write queues should never be negative. Use unsigned types so that it's not possible to break have the driver not allocate any queues. Reviewed-by: Jens Axboe Signed-off-by: Keith Busch --- drivers/nvme/host/pci.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/nvme/host/pci.c b/drivers/nvme/host/pci.c index 0590640ba62ca..446ea9c175fe8 100644 --- a/drivers/nvme/host/pci.c +++ b/drivers/nvme/host/pci.c @@ -68,14 +68,14 @@ static int io_queue_depth = 1024; module_param_cb(io_queue_depth, &io_queue_depth_ops, &io_queue_depth, 0644); MODULE_PARM_DESC(io_queue_depth, "set io queue depth, should >= 2"); -static int write_queues; -module_param(write_queues, int, 0644); +static unsigned int write_queues; +module_param(write_queues, uint, 0644); MODULE_PARM_DESC(write_queues, "Number of queues to use for writes. If not set, reads and writes " "will share a queue set."); -static int poll_queues; -module_param(poll_queues, int, 0644); +static unsigned int poll_queues; +module_param(poll_queues, uint, 0644); MODULE_PARM_DESC(poll_queues, "Number of queues to use for polled IO."); struct nvme_dev; -- GitLab From 17c3316734ae2e51f74078cd955ab855caea7d73 Mon Sep 17 00:00:00 2001 From: Keith Busch Date: Sat, 7 Dec 2019 01:16:59 +0900 Subject: [PATCH 0183/1437] nvme/pci Limit write queue sizes to possible cpus The driver can never use more queues of any type than the number of possible CPUs, so a higher value causes the driver to allocate more memory for IO queues than it could ever use. Limit the parameter at module load time to the number of possible cpus. Reviewed-by: Jens Axboe Signed-off-by: Keith Busch --- drivers/nvme/host/pci.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/nvme/host/pci.c b/drivers/nvme/host/pci.c index 446ea9c175fe8..fe81e1581e5f3 100644 --- a/drivers/nvme/host/pci.c +++ b/drivers/nvme/host/pci.c @@ -3138,6 +3138,9 @@ static int __init nvme_init(void) BUILD_BUG_ON(sizeof(struct nvme_create_sq) != 64); BUILD_BUG_ON(sizeof(struct nvme_delete_queue) != 64); BUILD_BUG_ON(IRQ_AFFINITY_MAX_SETS < 2); + + write_queues = min(write_queues, num_possible_cpus()); + poll_queues = min(poll_queues, num_possible_cpus()); return pci_register_driver(&nvme_driver); } -- GitLab From 7e4c6b9a5d22485acf009b3c3510a370f096dd54 Mon Sep 17 00:00:00 2001 From: Keith Busch Date: Fri, 6 Dec 2019 08:11:17 +0900 Subject: [PATCH 0184/1437] nvme/pci: Fix read queue count If nvme.write_queues equals the number of CPUs, the driver had decreased the number of interrupts available such that there could only be one read queue even if the controller could support more. Remove the interrupt count reduction in this case. The driver wouldn't request more IRQs than it wants queues anyway. Reviewed-by: Jens Axboe Signed-off-by: Keith Busch --- drivers/nvme/host/pci.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/drivers/nvme/host/pci.c b/drivers/nvme/host/pci.c index fe81e1581e5f3..365a2ddbeaa76 100644 --- a/drivers/nvme/host/pci.c +++ b/drivers/nvme/host/pci.c @@ -2054,7 +2054,6 @@ static int nvme_setup_irqs(struct nvme_dev *dev, unsigned int nr_io_queues) .priv = dev, }; unsigned int irq_queues, this_p_queues; - unsigned int nr_cpus = num_possible_cpus(); /* * Poll queues don't need interrupts, but we need at least one IO @@ -2065,10 +2064,7 @@ static int nvme_setup_irqs(struct nvme_dev *dev, unsigned int nr_io_queues) this_p_queues = nr_io_queues - 1; irq_queues = 1; } else { - if (nr_cpus < nr_io_queues - this_p_queues) - irq_queues = nr_cpus + 1; - else - irq_queues = nr_io_queues - this_p_queues + 1; + irq_queues = nr_io_queues - this_p_queues + 1; } dev->io_queues[HCTX_TYPE_POLL] = this_p_queues; -- GitLab From 54ad68b7776043a77c9bf0a67d49cd9cb261a8e7 Mon Sep 17 00:00:00 2001 From: Mark Rutland Date: Thu, 5 Dec 2019 18:06:52 +0000 Subject: [PATCH 0185/1437] KVM: arm64: Don't log IMP DEF sysreg traps We don't intend to support IMPLEMENATION DEFINED system registers, but have to trap them (and emulate them as UNDEFINED). These traps aren't interesting to the system administrator or to the KVM developers, so let's not bother logging when we do so. Signed-off-by: Mark Rutland Signed-off-by: Marc Zyngier Link: https://lore.kernel.org/r/20191205180652.18671-3-mark.rutland@arm.com --- arch/arm64/kvm/sys_regs.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c index e8bf08e09f783..bd2ac3796d8d0 100644 --- a/arch/arm64/kvm/sys_regs.c +++ b/arch/arm64/kvm/sys_regs.c @@ -2229,6 +2229,12 @@ int kvm_handle_cp14_32(struct kvm_vcpu *vcpu, struct kvm_run *run) NULL, 0); } +static bool is_imp_def_sys_reg(struct sys_reg_params *params) +{ + // See ARM DDI 0487E.a, section D12.3.2 + return params->Op0 == 3 && (params->CRn & 0b1011) == 0b1011; +} + static int emulate_sys_reg(struct kvm_vcpu *vcpu, struct sys_reg_params *params) { @@ -2244,6 +2250,8 @@ static int emulate_sys_reg(struct kvm_vcpu *vcpu, if (likely(r)) { perform_access(vcpu, params, r); + } else if (is_imp_def_sys_reg(params)) { + kvm_inject_undefined(vcpu); } else { print_sys_reg_msg(params, "Unsupported guest sys_reg access at: %lx [%08lx]\n", -- GitLab From 97418e968b01ba8e3ad41c38b42106c48bc19544 Mon Sep 17 00:00:00 2001 From: Jia He Date: Fri, 6 Dec 2019 10:08:02 +0800 Subject: [PATCH 0186/1437] KVM: arm/arm64: Remove excessive permission check in kvm_arch_prepare_memory_region In kvm_arch_prepare_memory_region, arm kvm regards the memory region as writable if the flag has no KVM_MEM_READONLY, and the vm is readonly if !VM_WRITE. But there is common usage for setting kvm memory region as follows: e.g. qemu side (see the PROT_NONE flag) 1. mmap(NULL, size, PROT_NONE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); memory_region_init_ram_ptr() 2. re mmap the above area with read/write authority. Such example is used in virtio-fs qemu codes which hasn't been upstreamed [1]. But seems we can't forbid this example. Without this patch, it will cause an EPERM during kvm_set_memory_region() and cause qemu boot crash. As told by Ard, "the underlying assumption is incorrect, i.e., that the value of vm_flags at this point in time defines how the VMA is used during its lifetime. There may be other cases where a VMA is created with VM_READ vm_flags that are changed to VM_READ|VM_WRITE later, and we are currently rejecting this use case as well." [1] https://gitlab.com/virtio-fs/qemu/blob/5a356e/hw/virtio/vhost-user-fs.c#L488 Suggested-by: Ard Biesheuvel Signed-off-by: Jia He Signed-off-by: Marc Zyngier Reviewed-by: Christoffer Dall Link: https://lore.kernel.org/r/20191206020802.196108-1-justin.he@arm.com --- virt/kvm/arm/mmu.c | 9 --------- 1 file changed, 9 deletions(-) diff --git a/virt/kvm/arm/mmu.c b/virt/kvm/arm/mmu.c index 38b4c910b6c38..a48994af70b87 100644 --- a/virt/kvm/arm/mmu.c +++ b/virt/kvm/arm/mmu.c @@ -2301,15 +2301,6 @@ int kvm_arch_prepare_memory_region(struct kvm *kvm, if (!vma || vma->vm_start >= reg_end) break; - /* - * Mapping a read-only VMA is only allowed if the - * memory region is configured as read-only. - */ - if (writable && !(vma->vm_flags & VM_WRITE)) { - ret = -EPERM; - break; - } - /* * Take the intersection of this VMA with the memory region */ -- GitLab From 50260614245b09887c197c58732298f02052d61b Mon Sep 17 00:00:00 2001 From: YueHaibing Date: Wed, 13 Nov 2019 18:53:13 +0800 Subject: [PATCH 0187/1437] thermal: power_allocator: Fix Kconfig warning When do randbuiding, we got this: WARNING: unmet direct dependencies detected for THERMAL_GOV_POWER_ALLOCATOR Depends on [n]: THERMAL [=y] && ENERGY_MODEL [=n] Selected by [y]: - THERMAL_DEFAULT_GOV_POWER_ALLOCATOR [=y] && The Kconfig option THERMAL_DEFAULT_GOV_POWER_ALLOCATOR selects the THERMAL_GOV_POWER_ALLOCATOR but this one depends on the ENERGY_MODEL which is not enabled. Make THERMAL_DEFAULT_GOV_POWER_ALLOCATOR depend on THERMAL_GOV_POWER_ALLOCATOR to fix this warning. Suggested-by: Quentin Perret Fixes: a4e893e802e6 ("thermal: cpu_cooling: Migrate to using the EM framework") Signed-off-by: YueHaibing Signed-off-by: Daniel Lezcano Signed-off-by: Zhang Rui Link: https://lore.kernel.org/r/20191113105313.41616-1-yuehaibing@huawei.com --- drivers/thermal/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig index 59b79fc48266e..79b27865c6f42 100644 --- a/drivers/thermal/Kconfig +++ b/drivers/thermal/Kconfig @@ -108,7 +108,7 @@ config THERMAL_DEFAULT_GOV_USER_SPACE config THERMAL_DEFAULT_GOV_POWER_ALLOCATOR bool "power_allocator" - select THERMAL_GOV_POWER_ALLOCATOR + depends on THERMAL_GOV_POWER_ALLOCATOR help Select this if you want to control temperature based on system and device power allocation. This governor can only -- GitLab From 87587a57b33342fe3c3f9f4a01a1118f9a5bb75e Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Sat, 23 Nov 2019 07:43:03 -0800 Subject: [PATCH 0188/1437] MAINTAINERS: thermal: Eduardo's email is bouncing The last two emails to Eduardo were returned with: 452 4.2.2 The email account that you tried to reach is over quota. Please direct the recipient to https://support.google.com/mail/?p=OverQuotaTemp j17sor626162wrq.49 - gsmtp Signed-off-by: Florian Fainelli Signed-off-by: Zhang Rui Signed-off-by: Daniel Lezcano Link: https://lore.kernel.org/r/20191123154303.2202-1-f.fainelli@gmail.com --- MAINTAINERS | 1 - 1 file changed, 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index ec1565dffa12f..c8abdb50af792 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -16287,7 +16287,6 @@ F: drivers/media/radio/radio-raremono.c THERMAL M: Zhang Rui -M: Eduardo Valentin R: Daniel Lezcano R: Amit Kucheria L: linux-pm@vger.kernel.org -- GitLab From 9f1fb8046bba73f4a3574286da69814e493c2b94 Mon Sep 17 00:00:00 2001 From: Zhang Rui Date: Thu, 5 Dec 2019 19:51:50 +0800 Subject: [PATCH 0189/1437] MAINTAINERS: thermal: Add Daniel Lezcano as the thermal maintainer Add Daniel Lezcano as the co-maintainer of thermal subsystem. Signed-off-by: Zhang Rui Signed-off-by: Daniel Lezcano Link: https://lore.kernel.org/r/20191205115150.18836-1-rui.zhang@intel.com --- MAINTAINERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index c8abdb50af792..0efadb61fe8b3 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -16287,7 +16287,7 @@ F: drivers/media/radio/radio-raremono.c THERMAL M: Zhang Rui -R: Daniel Lezcano +M: Daniel Lezcano R: Amit Kucheria L: linux-pm@vger.kernel.org T: git git://git.kernel.org/pub/scm/linux/kernel/git/rzhang/linux.git -- GitLab From 6e456dca47c5e3b8d8c6bbc4edd1377985d793b5 Mon Sep 17 00:00:00 2001 From: Daniel Lezcano Date: Thu, 5 Dec 2019 13:12:27 +0100 Subject: [PATCH 0190/1437] MAINTAINERS: thermal: Change the git tree location The thermal trees were merged into a single one shared with the maintainer of the subsystem. Update the location of this group git tree. Signed-off-by: Daniel Lezcano Signed-off-by: Zhang Rui Link: https://lore.kernel.org/r/20191205121227.19203-1-daniel.lezcano@linaro.org --- MAINTAINERS | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/MAINTAINERS b/MAINTAINERS index 0efadb61fe8b3..091c1c4cd32b0 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -16290,8 +16290,7 @@ M: Zhang Rui M: Daniel Lezcano R: Amit Kucheria L: linux-pm@vger.kernel.org -T: git git://git.kernel.org/pub/scm/linux/kernel/git/rzhang/linux.git -T: git git://git.kernel.org/pub/scm/linux/kernel/git/evalenti/linux-soc-thermal.git +T: git git://git.kernel.org/pub/scm/linux/kernel/git/thermal/linux.git Q: https://patchwork.kernel.org/project/linux-pm/list/ S: Supported F: drivers/thermal/ -- GitLab From 5a2e6af81807d4616f9839ad0ae7d1313b45c64d Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Mon, 2 Dec 2019 18:35:40 +0100 Subject: [PATCH 0191/1437] selftests: netfilter: use randomized netns names Using ns0, ns1, etc. isn't a good idea, they might exist already. Use a random suffix. Also, older nft versions don't support "-" as alias for stdin, so use /dev/stdin instead. Signed-off-by: Florian Westphal Acked-by: Phil Sutter Signed-off-by: Pablo Neira Ayuso --- tools/testing/selftests/netfilter/nft_nat.sh | 332 ++++++++++--------- 1 file changed, 176 insertions(+), 156 deletions(-) diff --git a/tools/testing/selftests/netfilter/nft_nat.sh b/tools/testing/selftests/netfilter/nft_nat.sh index 1be55e7057800..d7e07f4c3d7fc 100755 --- a/tools/testing/selftests/netfilter/nft_nat.sh +++ b/tools/testing/selftests/netfilter/nft_nat.sh @@ -8,9 +8,14 @@ ksft_skip=4 ret=0 test_inet_nat=true +sfx=$(mktemp -u "XXXXXXXX") +ns0="ns0-$sfx" +ns1="ns1-$sfx" +ns2="ns2-$sfx" + cleanup() { - for i in 0 1 2; do ip netns del ns$i;done + for i in 0 1 2; do ip netns del ns$i-"$sfx";done } nft --version > /dev/null 2>&1 @@ -25,40 +30,49 @@ if [ $? -ne 0 ];then exit $ksft_skip fi -ip netns add ns0 +ip netns add "$ns0" if [ $? -ne 0 ];then - echo "SKIP: Could not create net namespace" + echo "SKIP: Could not create net namespace $ns0" exit $ksft_skip fi trap cleanup EXIT -ip netns add ns1 -ip netns add ns2 +ip netns add "$ns1" +if [ $? -ne 0 ];then + echo "SKIP: Could not create net namespace $ns1" + exit $ksft_skip +fi + +ip netns add "$ns2" +if [ $? -ne 0 ];then + echo "SKIP: Could not create net namespace $ns2" + exit $ksft_skip +fi -ip link add veth0 netns ns0 type veth peer name eth0 netns ns1 > /dev/null 2>&1 +ip link add veth0 netns "$ns0" type veth peer name eth0 netns "$ns1" > /dev/null 2>&1 if [ $? -ne 0 ];then echo "SKIP: No virtual ethernet pair device support in kernel" exit $ksft_skip fi -ip link add veth1 netns ns0 type veth peer name eth0 netns ns2 +ip link add veth1 netns "$ns0" type veth peer name eth0 netns "$ns2" -ip -net ns0 link set lo up -ip -net ns0 link set veth0 up -ip -net ns0 addr add 10.0.1.1/24 dev veth0 -ip -net ns0 addr add dead:1::1/64 dev veth0 +ip -net "$ns0" link set lo up +ip -net "$ns0" link set veth0 up +ip -net "$ns0" addr add 10.0.1.1/24 dev veth0 +ip -net "$ns0" addr add dead:1::1/64 dev veth0 -ip -net ns0 link set veth1 up -ip -net ns0 addr add 10.0.2.1/24 dev veth1 -ip -net ns0 addr add dead:2::1/64 dev veth1 +ip -net "$ns0" link set veth1 up +ip -net "$ns0" addr add 10.0.2.1/24 dev veth1 +ip -net "$ns0" addr add dead:2::1/64 dev veth1 for i in 1 2; do - ip -net ns$i link set lo up - ip -net ns$i link set eth0 up - ip -net ns$i addr add 10.0.$i.99/24 dev eth0 - ip -net ns$i route add default via 10.0.$i.1 - ip -net ns$i addr add dead:$i::99/64 dev eth0 - ip -net ns$i route add default via dead:$i::1 + ip -net ns$i-$sfx link set lo up + ip -net ns$i-$sfx link set eth0 up + ip -net ns$i-$sfx addr add 10.0.$i.99/24 dev eth0 + ip -net ns$i-$sfx route add default via 10.0.$i.1 + ip -net ns$i-$sfx addr add dead:$i::99/64 dev eth0 + ip -net ns$i-$sfx route add default via dead:$i::1 done bad_counter() @@ -66,8 +80,9 @@ bad_counter() local ns=$1 local counter=$2 local expect=$3 + local tag=$4 - echo "ERROR: $counter counter in $ns has unexpected value (expected $expect)" 1>&2 + echo "ERROR: $counter counter in $ns has unexpected value (expected $expect) at $tag" 1>&2 ip netns exec $ns nft list counter inet filter $counter 1>&2 } @@ -78,24 +93,24 @@ check_counters() cnt=$(ip netns exec $ns nft list counter inet filter ns0in | grep -q "packets 1 bytes 84") if [ $? -ne 0 ]; then - bad_counter $ns ns0in "packets 1 bytes 84" + bad_counter $ns ns0in "packets 1 bytes 84" "check_counters 1" lret=1 fi cnt=$(ip netns exec $ns nft list counter inet filter ns0out | grep -q "packets 1 bytes 84") if [ $? -ne 0 ]; then - bad_counter $ns ns0out "packets 1 bytes 84" + bad_counter $ns ns0out "packets 1 bytes 84" "check_counters 2" lret=1 fi expect="packets 1 bytes 104" cnt=$(ip netns exec $ns nft list counter inet filter ns0in6 | grep -q "$expect") if [ $? -ne 0 ]; then - bad_counter $ns ns0in6 "$expect" + bad_counter $ns ns0in6 "$expect" "check_counters 3" lret=1 fi cnt=$(ip netns exec $ns nft list counter inet filter ns0out6 | grep -q "$expect") if [ $? -ne 0 ]; then - bad_counter $ns ns0out6 "$expect" + bad_counter $ns ns0out6 "$expect" "check_counters 4" lret=1 fi @@ -107,41 +122,41 @@ check_ns0_counters() local ns=$1 local lret=0 - cnt=$(ip netns exec ns0 nft list counter inet filter ns0in | grep -q "packets 0 bytes 0") + cnt=$(ip netns exec "$ns0" nft list counter inet filter ns0in | grep -q "packets 0 bytes 0") if [ $? -ne 0 ]; then - bad_counter ns0 ns0in "packets 0 bytes 0" + bad_counter "$ns0" ns0in "packets 0 bytes 0" "check_ns0_counters 1" lret=1 fi - cnt=$(ip netns exec ns0 nft list counter inet filter ns0in6 | grep -q "packets 0 bytes 0") + cnt=$(ip netns exec "$ns0" nft list counter inet filter ns0in6 | grep -q "packets 0 bytes 0") if [ $? -ne 0 ]; then - bad_counter ns0 ns0in6 "packets 0 bytes 0" + bad_counter "$ns0" ns0in6 "packets 0 bytes 0" lret=1 fi - cnt=$(ip netns exec ns0 nft list counter inet filter ns0out | grep -q "packets 0 bytes 0") + cnt=$(ip netns exec "$ns0" nft list counter inet filter ns0out | grep -q "packets 0 bytes 0") if [ $? -ne 0 ]; then - bad_counter ns0 ns0out "packets 0 bytes 0" + bad_counter "$ns0" ns0out "packets 0 bytes 0" "check_ns0_counters 2" lret=1 fi - cnt=$(ip netns exec ns0 nft list counter inet filter ns0out6 | grep -q "packets 0 bytes 0") + cnt=$(ip netns exec "$ns0" nft list counter inet filter ns0out6 | grep -q "packets 0 bytes 0") if [ $? -ne 0 ]; then - bad_counter ns0 ns0out6 "packets 0 bytes 0" + bad_counter "$ns0" ns0out6 "packets 0 bytes 0" "check_ns0_counters3 " lret=1 fi for dir in "in" "out" ; do expect="packets 1 bytes 84" - cnt=$(ip netns exec ns0 nft list counter inet filter ${ns}${dir} | grep -q "$expect") + cnt=$(ip netns exec "$ns0" nft list counter inet filter ${ns}${dir} | grep -q "$expect") if [ $? -ne 0 ]; then - bad_counter ns0 $ns$dir "$expect" + bad_counter "$ns0" $ns$dir "$expect" "check_ns0_counters 4" lret=1 fi expect="packets 1 bytes 104" - cnt=$(ip netns exec ns0 nft list counter inet filter ${ns}${dir}6 | grep -q "$expect") + cnt=$(ip netns exec "$ns0" nft list counter inet filter ${ns}${dir}6 | grep -q "$expect") if [ $? -ne 0 ]; then - bad_counter ns0 $ns$dir6 "$expect" + bad_counter "$ns0" $ns$dir6 "$expect" "check_ns0_counters 5" lret=1 fi done @@ -152,7 +167,7 @@ check_ns0_counters() reset_counters() { for i in 0 1 2;do - ip netns exec ns$i nft reset counters inet > /dev/null + ip netns exec ns$i-$sfx nft reset counters inet > /dev/null done } @@ -166,7 +181,7 @@ test_local_dnat6() IPF="ip6" fi -ip netns exec ns0 nft -f - < /dev/null + ip netns exec "$ns0" ping -q -c 1 dead:1::99 > /dev/null if [ $? -ne 0 ]; then lret=1 echo "ERROR: ping6 failed" @@ -189,18 +204,18 @@ EOF expect="packets 0 bytes 0" for dir in "in6" "out6" ; do - cnt=$(ip netns exec ns0 nft list counter inet filter ns1${dir} | grep -q "$expect") + cnt=$(ip netns exec "$ns0" nft list counter inet filter ns1${dir} | grep -q "$expect") if [ $? -ne 0 ]; then - bad_counter ns0 ns1$dir "$expect" + bad_counter "$ns0" ns1$dir "$expect" "test_local_dnat6 1" lret=1 fi done expect="packets 1 bytes 104" for dir in "in6" "out6" ; do - cnt=$(ip netns exec ns0 nft list counter inet filter ns2${dir} | grep -q "$expect") + cnt=$(ip netns exec "$ns0" nft list counter inet filter ns2${dir} | grep -q "$expect") if [ $? -ne 0 ]; then - bad_counter ns0 ns2$dir "$expect" + bad_counter "$ns0" ns2$dir "$expect" "test_local_dnat6 2" lret=1 fi done @@ -208,9 +223,9 @@ EOF # expect 0 count in ns1 expect="packets 0 bytes 0" for dir in "in6" "out6" ; do - cnt=$(ip netns exec ns1 nft list counter inet filter ns0${dir} | grep -q "$expect") + cnt=$(ip netns exec "$ns1" nft list counter inet filter ns0${dir} | grep -q "$expect") if [ $? -ne 0 ]; then - bad_counter ns1 ns0$dir "$expect" + bad_counter "$ns1" ns0$dir "$expect" "test_local_dnat6 3" lret=1 fi done @@ -218,15 +233,15 @@ EOF # expect 1 packet in ns2 expect="packets 1 bytes 104" for dir in "in6" "out6" ; do - cnt=$(ip netns exec ns2 nft list counter inet filter ns0${dir} | grep -q "$expect") + cnt=$(ip netns exec "$ns2" nft list counter inet filter ns0${dir} | grep -q "$expect") if [ $? -ne 0 ]; then - bad_counter ns2 ns0$dir "$expect" + bad_counter "$ns2" ns0$dir "$expect" "test_local_dnat6 4" lret=1 fi done - test $lret -eq 0 && echo "PASS: ipv6 ping to ns1 was $family NATted to ns2" - ip netns exec ns0 nft flush chain ip6 nat output + test $lret -eq 0 && echo "PASS: ipv6 ping to $ns1 was $family NATted to $ns2" + ip netns exec "$ns0" nft flush chain ip6 nat output return $lret } @@ -241,7 +256,7 @@ test_local_dnat() IPF="ip" fi -ip netns exec ns0 nft -f - </dev/null +ip netns exec "$ns0" nft -f /dev/stdin </dev/null table $family nat { chain output { type nat hook output priority 0; policy accept; @@ -260,7 +275,7 @@ EOF fi # ping netns1, expect rewrite to netns2 - ip netns exec ns0 ping -q -c 1 10.0.1.99 > /dev/null + ip netns exec "$ns0" ping -q -c 1 10.0.1.99 > /dev/null if [ $? -ne 0 ]; then lret=1 echo "ERROR: ping failed" @@ -269,18 +284,18 @@ EOF expect="packets 0 bytes 0" for dir in "in" "out" ; do - cnt=$(ip netns exec ns0 nft list counter inet filter ns1${dir} | grep -q "$expect") + cnt=$(ip netns exec "$ns0" nft list counter inet filter ns1${dir} | grep -q "$expect") if [ $? -ne 0 ]; then - bad_counter ns0 ns1$dir "$expect" + bad_counter "$ns0" ns1$dir "$expect" "test_local_dnat 1" lret=1 fi done expect="packets 1 bytes 84" for dir in "in" "out" ; do - cnt=$(ip netns exec ns0 nft list counter inet filter ns2${dir} | grep -q "$expect") + cnt=$(ip netns exec "$ns0" nft list counter inet filter ns2${dir} | grep -q "$expect") if [ $? -ne 0 ]; then - bad_counter ns0 ns2$dir "$expect" + bad_counter "$ns0" ns2$dir "$expect" "test_local_dnat 2" lret=1 fi done @@ -288,9 +303,9 @@ EOF # expect 0 count in ns1 expect="packets 0 bytes 0" for dir in "in" "out" ; do - cnt=$(ip netns exec ns1 nft list counter inet filter ns0${dir} | grep -q "$expect") + cnt=$(ip netns exec "$ns1" nft list counter inet filter ns0${dir} | grep -q "$expect") if [ $? -ne 0 ]; then - bad_counter ns1 ns0$dir "$expect" + bad_counter "$ns1" ns0$dir "$expect" "test_local_dnat 3" lret=1 fi done @@ -298,19 +313,19 @@ EOF # expect 1 packet in ns2 expect="packets 1 bytes 84" for dir in "in" "out" ; do - cnt=$(ip netns exec ns2 nft list counter inet filter ns0${dir} | grep -q "$expect") + cnt=$(ip netns exec "$ns2" nft list counter inet filter ns0${dir} | grep -q "$expect") if [ $? -ne 0 ]; then - bad_counter ns2 ns0$dir "$expect" + bad_counter "$ns2" ns0$dir "$expect" "test_local_dnat 4" lret=1 fi done - test $lret -eq 0 && echo "PASS: ping to ns1 was $family NATted to ns2" + test $lret -eq 0 && echo "PASS: ping to $ns1 was $family NATted to $ns2" - ip netns exec ns0 nft flush chain $family nat output + ip netns exec "$ns0" nft flush chain $family nat output reset_counters - ip netns exec ns0 ping -q -c 1 10.0.1.99 > /dev/null + ip netns exec "$ns0" ping -q -c 1 10.0.1.99 > /dev/null if [ $? -ne 0 ]; then lret=1 echo "ERROR: ping failed" @@ -319,17 +334,17 @@ EOF expect="packets 1 bytes 84" for dir in "in" "out" ; do - cnt=$(ip netns exec ns0 nft list counter inet filter ns1${dir} | grep -q "$expect") + cnt=$(ip netns exec "$ns0" nft list counter inet filter ns1${dir} | grep -q "$expect") if [ $? -ne 0 ]; then - bad_counter ns1 ns1$dir "$expect" + bad_counter "$ns1" ns1$dir "$expect" "test_local_dnat 5" lret=1 fi done expect="packets 0 bytes 0" for dir in "in" "out" ; do - cnt=$(ip netns exec ns0 nft list counter inet filter ns2${dir} | grep -q "$expect") + cnt=$(ip netns exec "$ns0" nft list counter inet filter ns2${dir} | grep -q "$expect") if [ $? -ne 0 ]; then - bad_counter ns0 ns2$dir "$expect" + bad_counter "$ns0" ns2$dir "$expect" "test_local_dnat 6" lret=1 fi done @@ -337,9 +352,9 @@ EOF # expect 1 count in ns1 expect="packets 1 bytes 84" for dir in "in" "out" ; do - cnt=$(ip netns exec ns1 nft list counter inet filter ns0${dir} | grep -q "$expect") + cnt=$(ip netns exec "$ns1" nft list counter inet filter ns0${dir} | grep -q "$expect") if [ $? -ne 0 ]; then - bad_counter ns0 ns0$dir "$expect" + bad_counter "$ns0" ns0$dir "$expect" "test_local_dnat 7" lret=1 fi done @@ -347,14 +362,14 @@ EOF # expect 0 packet in ns2 expect="packets 0 bytes 0" for dir in "in" "out" ; do - cnt=$(ip netns exec ns2 nft list counter inet filter ns0${dir} | grep -q "$expect") + cnt=$(ip netns exec "$ns2" nft list counter inet filter ns0${dir} | grep -q "$expect") if [ $? -ne 0 ]; then - bad_counter ns2 ns2$dir "$expect" + bad_counter "$ns2" ns0$dir "$expect" "test_local_dnat 8" lret=1 fi done - test $lret -eq 0 && echo "PASS: ping to ns1 OK after $family nat output chain flush" + test $lret -eq 0 && echo "PASS: ping to $ns1 OK after $family nat output chain flush" return $lret } @@ -366,26 +381,26 @@ test_masquerade6() local natflags=$2 local lret=0 - ip netns exec ns0 sysctl net.ipv6.conf.all.forwarding=1 > /dev/null + ip netns exec "$ns0" sysctl net.ipv6.conf.all.forwarding=1 > /dev/null - ip netns exec ns2 ping -q -c 1 dead:1::99 > /dev/null # ping ns2->ns1 + ip netns exec "$ns2" ping -q -c 1 dead:1::99 > /dev/null # ping ns2->ns1 if [ $? -ne 0 ] ; then - echo "ERROR: cannot ping ns1 from ns2 via ipv6" + echo "ERROR: cannot ping $ns1 from $ns2 via ipv6" return 1 lret=1 fi expect="packets 1 bytes 104" for dir in "in6" "out6" ; do - cnt=$(ip netns exec ns1 nft list counter inet filter ns2${dir} | grep -q "$expect") + cnt=$(ip netns exec "$ns1" nft list counter inet filter ns2${dir} | grep -q "$expect") if [ $? -ne 0 ]; then - bad_counter ns1 ns2$dir "$expect" + bad_counter "$ns1" ns2$dir "$expect" "test_masquerade6 1" lret=1 fi - cnt=$(ip netns exec ns2 nft list counter inet filter ns1${dir} | grep -q "$expect") + cnt=$(ip netns exec "$ns2" nft list counter inet filter ns1${dir} | grep -q "$expect") if [ $? -ne 0 ]; then - bad_counter ns2 ns1$dir "$expect" + bad_counter "$ns2" ns1$dir "$expect" "test_masquerade6 2" lret=1 fi done @@ -393,7 +408,7 @@ test_masquerade6() reset_counters # add masquerading rule -ip netns exec ns0 nft -f - < /dev/null # ping ns2->ns1 + ip netns exec "$ns2" ping -q -c 1 dead:1::99 > /dev/null # ping ns2->ns1 if [ $? -ne 0 ] ; then - echo "ERROR: cannot ping ns1 from ns2 with active $family masquerade $natflags" + echo "ERROR: cannot ping $ns1 from $ns2 with active $family masquerade $natflags" lret=1 fi # ns1 should have seen packets from ns0, due to masquerade expect="packets 1 bytes 104" for dir in "in6" "out6" ; do - cnt=$(ip netns exec ns1 nft list counter inet filter ns0${dir} | grep -q "$expect") + cnt=$(ip netns exec "$ns1" nft list counter inet filter ns0${dir} | grep -q "$expect") if [ $? -ne 0 ]; then - bad_counter ns1 ns0$dir "$expect" + bad_counter "$ns1" ns0$dir "$expect" "test_masquerade6 3" lret=1 fi - cnt=$(ip netns exec ns2 nft list counter inet filter ns1${dir} | grep -q "$expect") + cnt=$(ip netns exec "$ns2" nft list counter inet filter ns1${dir} | grep -q "$expect") if [ $? -ne 0 ]; then - bad_counter ns2 ns1$dir "$expect" + bad_counter "$ns2" ns1$dir "$expect" "test_masquerade6 4" lret=1 fi done @@ -431,32 +446,32 @@ EOF # ns1 should not have seen packets from ns2, due to masquerade expect="packets 0 bytes 0" for dir in "in6" "out6" ; do - cnt=$(ip netns exec ns1 nft list counter inet filter ns2${dir} | grep -q "$expect") + cnt=$(ip netns exec "$ns1" nft list counter inet filter ns2${dir} | grep -q "$expect") if [ $? -ne 0 ]; then - bad_counter ns1 ns0$dir "$expect" + bad_counter "$ns1" ns0$dir "$expect" "test_masquerade6 5" lret=1 fi - cnt=$(ip netns exec ns1 nft list counter inet filter ns2${dir} | grep -q "$expect") + cnt=$(ip netns exec "$ns0" nft list counter inet filter ns1${dir} | grep -q "$expect") if [ $? -ne 0 ]; then - bad_counter ns2 ns1$dir "$expect" + bad_counter "$ns0" ns1$dir "$expect" "test_masquerade6 6" lret=1 fi done - ip netns exec ns2 ping -q -c 1 dead:1::99 > /dev/null # ping ns2->ns1 + ip netns exec "$ns2" ping -q -c 1 dead:1::99 > /dev/null # ping ns2->ns1 if [ $? -ne 0 ] ; then - echo "ERROR: cannot ping ns1 from ns2 with active ipv6 masquerade $natflags (attempt 2)" + echo "ERROR: cannot ping $ns1 from $ns2 with active ipv6 masquerade $natflags (attempt 2)" lret=1 fi - ip netns exec ns0 nft flush chain $family nat postrouting + ip netns exec "$ns0" nft flush chain $family nat postrouting if [ $? -ne 0 ]; then echo "ERROR: Could not flush $family nat postrouting" 1>&2 lret=1 fi - test $lret -eq 0 && echo "PASS: $family IPv6 masquerade $natflags for ns2" + test $lret -eq 0 && echo "PASS: $family IPv6 masquerade $natflags for $ns2" return $lret } @@ -467,26 +482,26 @@ test_masquerade() local natflags=$2 local lret=0 - ip netns exec ns0 sysctl net.ipv4.conf.veth0.forwarding=1 > /dev/null - ip netns exec ns0 sysctl net.ipv4.conf.veth1.forwarding=1 > /dev/null + ip netns exec "$ns0" sysctl net.ipv4.conf.veth0.forwarding=1 > /dev/null + ip netns exec "$ns0" sysctl net.ipv4.conf.veth1.forwarding=1 > /dev/null - ip netns exec ns2 ping -q -c 1 10.0.1.99 > /dev/null # ping ns2->ns1 + ip netns exec "$ns2" ping -q -c 1 10.0.1.99 > /dev/null # ping ns2->ns1 if [ $? -ne 0 ] ; then - echo "ERROR: cannot ping ns1 from ns2 $natflags" + echo "ERROR: cannot ping $ns1 from "$ns2" $natflags" lret=1 fi expect="packets 1 bytes 84" for dir in "in" "out" ; do - cnt=$(ip netns exec ns1 nft list counter inet filter ns2${dir} | grep -q "$expect") + cnt=$(ip netns exec "$ns1" nft list counter inet filter ns2${dir} | grep -q "$expect") if [ $? -ne 0 ]; then - bad_counter ns1 ns2$dir "$expect" + bad_counter "$ns1" ns2$dir "$expect" "test_masquerade 1" lret=1 fi - cnt=$(ip netns exec ns2 nft list counter inet filter ns1${dir} | grep -q "$expect") + cnt=$(ip netns exec "$ns2" nft list counter inet filter ns1${dir} | grep -q "$expect") if [ $? -ne 0 ]; then - bad_counter ns2 ns1$dir "$expect" + bad_counter "$ns2" ns1$dir "$expect" "test_masquerade 2" lret=1 fi done @@ -494,7 +509,7 @@ test_masquerade() reset_counters # add masquerading rule -ip netns exec ns0 nft -f - < /dev/null # ping ns2->ns1 + ip netns exec "$ns2" ping -q -c 1 10.0.1.99 > /dev/null # ping ns2->ns1 if [ $? -ne 0 ] ; then - echo "ERROR: cannot ping ns1 from ns2 with active $family masquerade $natflags" + echo "ERROR: cannot ping $ns1 from $ns2 with active $family masquerade $natflags" lret=1 fi # ns1 should have seen packets from ns0, due to masquerade expect="packets 1 bytes 84" for dir in "in" "out" ; do - cnt=$(ip netns exec ns1 nft list counter inet filter ns0${dir} | grep -q "$expect") + cnt=$(ip netns exec "$ns1" nft list counter inet filter ns0${dir} | grep -q "$expect") if [ $? -ne 0 ]; then - bad_counter ns1 ns0$dir "$expect" + bad_counter "$ns1" ns0$dir "$expect" "test_masquerade 3" lret=1 fi - cnt=$(ip netns exec ns2 nft list counter inet filter ns1${dir} | grep -q "$expect") + cnt=$(ip netns exec "$ns2" nft list counter inet filter ns1${dir} | grep -q "$expect") if [ $? -ne 0 ]; then - bad_counter ns2 ns1$dir "$expect" + bad_counter "$ns2" ns1$dir "$expect" "test_masquerade 4" lret=1 fi done @@ -532,32 +547,32 @@ EOF # ns1 should not have seen packets from ns2, due to masquerade expect="packets 0 bytes 0" for dir in "in" "out" ; do - cnt=$(ip netns exec ns1 nft list counter inet filter ns2${dir} | grep -q "$expect") + cnt=$(ip netns exec "$ns1" nft list counter inet filter ns2${dir} | grep -q "$expect") if [ $? -ne 0 ]; then - bad_counter ns1 ns0$dir "$expect" + bad_counter "$ns1" ns0$dir "$expect" "test_masquerade 5" lret=1 fi - cnt=$(ip netns exec ns1 nft list counter inet filter ns2${dir} | grep -q "$expect") + cnt=$(ip netns exec "$ns0" nft list counter inet filter ns1${dir} | grep -q "$expect") if [ $? -ne 0 ]; then - bad_counter ns2 ns1$dir "$expect" + bad_counter "$ns0" ns1$dir "$expect" "test_masquerade 6" lret=1 fi done - ip netns exec ns2 ping -q -c 1 10.0.1.99 > /dev/null # ping ns2->ns1 + ip netns exec "$ns2" ping -q -c 1 10.0.1.99 > /dev/null # ping ns2->ns1 if [ $? -ne 0 ] ; then - echo "ERROR: cannot ping ns1 from ns2 with active ip masquerade $natflags (attempt 2)" + echo "ERROR: cannot ping $ns1 from $ns2 with active ip masquerade $natflags (attempt 2)" lret=1 fi - ip netns exec ns0 nft flush chain $family nat postrouting + ip netns exec "$ns0" nft flush chain $family nat postrouting if [ $? -ne 0 ]; then echo "ERROR: Could not flush $family nat postrouting" 1>&2 lret=1 fi - test $lret -eq 0 && echo "PASS: $family IP masquerade $natflags for ns2" + test $lret -eq 0 && echo "PASS: $family IP masquerade $natflags for $ns2" return $lret } @@ -567,25 +582,25 @@ test_redirect6() local family=$1 local lret=0 - ip netns exec ns0 sysctl net.ipv6.conf.all.forwarding=1 > /dev/null + ip netns exec "$ns0" sysctl net.ipv6.conf.all.forwarding=1 > /dev/null - ip netns exec ns2 ping -q -c 1 dead:1::99 > /dev/null # ping ns2->ns1 + ip netns exec "$ns2" ping -q -c 1 dead:1::99 > /dev/null # ping ns2->ns1 if [ $? -ne 0 ] ; then - echo "ERROR: cannnot ping ns1 from ns2 via ipv6" + echo "ERROR: cannnot ping $ns1 from $ns2 via ipv6" lret=1 fi expect="packets 1 bytes 104" for dir in "in6" "out6" ; do - cnt=$(ip netns exec ns1 nft list counter inet filter ns2${dir} | grep -q "$expect") + cnt=$(ip netns exec "$ns1" nft list counter inet filter ns2${dir} | grep -q "$expect") if [ $? -ne 0 ]; then - bad_counter ns1 ns2$dir "$expect" + bad_counter "$ns1" ns2$dir "$expect" "test_redirect6 1" lret=1 fi - cnt=$(ip netns exec ns2 nft list counter inet filter ns1${dir} | grep -q "$expect") + cnt=$(ip netns exec "$ns2" nft list counter inet filter ns1${dir} | grep -q "$expect") if [ $? -ne 0 ]; then - bad_counter ns2 ns1$dir "$expect" + bad_counter "$ns2" ns1$dir "$expect" "test_redirect6 2" lret=1 fi done @@ -593,7 +608,7 @@ test_redirect6() reset_counters # add redirect rule -ip netns exec ns0 nft -f - < /dev/null # ping ns2->ns1 + ip netns exec "$ns2" ping -q -c 1 dead:1::99 > /dev/null # ping ns2->ns1 if [ $? -ne 0 ] ; then - echo "ERROR: cannot ping ns1 from ns2 via ipv6 with active $family redirect" + echo "ERROR: cannot ping $ns1 from $ns2 via ipv6 with active $family redirect" lret=1 fi # ns1 should have seen no packets from ns2, due to redirection expect="packets 0 bytes 0" for dir in "in6" "out6" ; do - cnt=$(ip netns exec ns1 nft list counter inet filter ns2${dir} | grep -q "$expect") + cnt=$(ip netns exec "$ns1" nft list counter inet filter ns2${dir} | grep -q "$expect") if [ $? -ne 0 ]; then - bad_counter ns1 ns0$dir "$expect" + bad_counter "$ns1" ns0$dir "$expect" "test_redirect6 3" lret=1 fi done @@ -625,20 +640,20 @@ EOF # ns0 should have seen packets from ns2, due to masquerade expect="packets 1 bytes 104" for dir in "in6" "out6" ; do - cnt=$(ip netns exec ns0 nft list counter inet filter ns2${dir} | grep -q "$expect") + cnt=$(ip netns exec "$ns0" nft list counter inet filter ns2${dir} | grep -q "$expect") if [ $? -ne 0 ]; then - bad_counter ns1 ns0$dir "$expect" + bad_counter "$ns1" ns0$dir "$expect" "test_redirect6 4" lret=1 fi done - ip netns exec ns0 nft delete table $family nat + ip netns exec "$ns0" nft delete table $family nat if [ $? -ne 0 ]; then echo "ERROR: Could not delete $family nat table" 1>&2 lret=1 fi - test $lret -eq 0 && echo "PASS: $family IPv6 redirection for ns2" + test $lret -eq 0 && echo "PASS: $family IPv6 redirection for $ns2" return $lret } @@ -648,26 +663,26 @@ test_redirect() local family=$1 local lret=0 - ip netns exec ns0 sysctl net.ipv4.conf.veth0.forwarding=1 > /dev/null - ip netns exec ns0 sysctl net.ipv4.conf.veth1.forwarding=1 > /dev/null + ip netns exec "$ns0" sysctl net.ipv4.conf.veth0.forwarding=1 > /dev/null + ip netns exec "$ns0" sysctl net.ipv4.conf.veth1.forwarding=1 > /dev/null - ip netns exec ns2 ping -q -c 1 10.0.1.99 > /dev/null # ping ns2->ns1 + ip netns exec "$ns2" ping -q -c 1 10.0.1.99 > /dev/null # ping ns2->ns1 if [ $? -ne 0 ] ; then - echo "ERROR: cannot ping ns1 from ns2" + echo "ERROR: cannot ping $ns1 from $ns2" lret=1 fi expect="packets 1 bytes 84" for dir in "in" "out" ; do - cnt=$(ip netns exec ns1 nft list counter inet filter ns2${dir} | grep -q "$expect") + cnt=$(ip netns exec "$ns1" nft list counter inet filter ns2${dir} | grep -q "$expect") if [ $? -ne 0 ]; then - bad_counter ns1 ns2$dir "$expect" + bad_counter "$ns1" $ns2$dir "$expect" "test_redirect 1" lret=1 fi - cnt=$(ip netns exec ns2 nft list counter inet filter ns1${dir} | grep -q "$expect") + cnt=$(ip netns exec "$ns2" nft list counter inet filter ns1${dir} | grep -q "$expect") if [ $? -ne 0 ]; then - bad_counter ns2 ns1$dir "$expect" + bad_counter "$ns2" ns1$dir "$expect" "test_redirect 2" lret=1 fi done @@ -675,7 +690,7 @@ test_redirect() reset_counters # add redirect rule -ip netns exec ns0 nft -f - < /dev/null # ping ns2->ns1 + ip netns exec "$ns2" ping -q -c 1 10.0.1.99 > /dev/null # ping ns2->ns1 if [ $? -ne 0 ] ; then - echo "ERROR: cannot ping ns1 from ns2 with active $family ip redirect" + echo "ERROR: cannot ping $ns1 from $ns2 with active $family ip redirect" lret=1 fi @@ -698,9 +713,9 @@ EOF expect="packets 0 bytes 0" for dir in "in" "out" ; do - cnt=$(ip netns exec ns1 nft list counter inet filter ns2${dir} | grep -q "$expect") + cnt=$(ip netns exec "$ns1" nft list counter inet filter ns2${dir} | grep -q "$expect") if [ $? -ne 0 ]; then - bad_counter ns1 ns0$dir "$expect" + bad_counter "$ns1" ns0$dir "$expect" "test_redirect 3" lret=1 fi done @@ -708,28 +723,28 @@ EOF # ns0 should have seen packets from ns2, due to masquerade expect="packets 1 bytes 84" for dir in "in" "out" ; do - cnt=$(ip netns exec ns0 nft list counter inet filter ns2${dir} | grep -q "$expect") + cnt=$(ip netns exec "$ns0" nft list counter inet filter ns2${dir} | grep -q "$expect") if [ $? -ne 0 ]; then - bad_counter ns1 ns0$dir "$expect" + bad_counter "$ns0" ns0$dir "$expect" "test_redirect 4" lret=1 fi done - ip netns exec ns0 nft delete table $family nat + ip netns exec "$ns0" nft delete table $family nat if [ $? -ne 0 ]; then echo "ERROR: Could not delete $family nat table" 1>&2 lret=1 fi - test $lret -eq 0 && echo "PASS: $family IP redirection for ns2" + test $lret -eq 0 && echo "PASS: $family IP redirection for $ns2" return $lret } -# ip netns exec ns0 ping -c 1 -q 10.0.$i.99 +# ip netns exec "$ns0" ping -c 1 -q 10.0.$i.99 for i in 0 1 2; do -ip netns exec ns$i nft -f - < /dev/null + ip netns exec "$ns0" ping -c 1 -q 10.0.$i.99 > /dev/null if [ $? -ne 0 ];then echo "ERROR: Could not reach other namespace(s)" 1>&2 ret=1 fi - ip netns exec ns0 ping -c 1 -q dead:$i::99 > /dev/null + ip netns exec "$ns0" ping -c 1 -q dead:$i::99 > /dev/null if [ $? -ne 0 ];then echo "ERROR: Could not reach other namespace(s) via ipv6" 1>&2 ret=1 fi - check_counters ns$i + check_counters ns$i-$sfx if [ $? -ne 0 ]; then ret=1 fi @@ -820,7 +835,7 @@ for i in 1 2; do done if [ $ret -eq 0 ];then - echo "PASS: netns routing/connectivity: ns0 can reach ns1 and ns2" + echo "PASS: netns routing/connectivity: $ns0 can reach $ns1 and $ns2" fi reset_counters @@ -846,4 +861,9 @@ reset_counters $test_inet_nat && test_redirect inet $test_inet_nat && test_redirect6 inet +if [ $ret -ne 0 ];then + echo -n "FAIL: " + nft --version +fi + exit $ret -- GitLab From 0b9173f4688dfa7c5d723426be1d979c24ce3d51 Mon Sep 17 00:00:00 2001 From: Marco Oliverio Date: Mon, 2 Dec 2019 19:54:30 +0100 Subject: [PATCH 0192/1437] netfilter: nf_queue: enqueue skbs with NULL dst Bridge packets that are forwarded have skb->dst == NULL and get dropped by the check introduced by b60a77386b1d4868f72f6353d35dabe5fbe981f2 (net: make skb_dst_force return true when dst is refcounted). To fix this we check skb_dst() before skb_dst_force(), so we don't drop skb packet with dst == NULL. This holds also for skb at the PRE_ROUTING hook so we remove the second check. Fixes: b60a77386b1d ("net: make skb_dst_force return true when dst is refcounted") Signed-off-by: Marco Oliverio Signed-off-by: Rocco Folino Acked-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- net/netfilter/nf_queue.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/netfilter/nf_queue.c b/net/netfilter/nf_queue.c index a2b58de826007..f8f52ff99cfb0 100644 --- a/net/netfilter/nf_queue.c +++ b/net/netfilter/nf_queue.c @@ -189,7 +189,7 @@ static int __nf_queue(struct sk_buff *skb, const struct nf_hook_state *state, goto err; } - if (!skb_dst_force(skb) && state->hook != NF_INET_PRE_ROUTING) { + if (skb_dst(skb) && !skb_dst_force(skb)) { status = -ENETDOWN; goto err; } -- GitLab From c08f0a92f4bef9467810551a2db0201d1b95d01e Mon Sep 17 00:00:00 2001 From: Olof Johansson Date: Sat, 7 Dec 2019 14:49:53 -0800 Subject: [PATCH 0193/1437] ALSA: echoaudio: simplify get_audio_levels The loop optimizer seems to go astray here, and produces some warnings that don't seem valid. Still, the code can be simplified -- just clear the whole array at the beginning, and fill in whatever values are valid on the platform. Warnings before this change (GCC 8.2.0 ARM allmodconfig): In file included from ../sound/pci/echoaudio/gina24.c:115: ../sound/pci/echoaudio/echoaudio.c: In function 'snd_echo_vumeters_get': ../sound/pci/echoaudio/echoaudio_dsp.c:647:9: warning: iteration 1073741824 invokes undefined behavior [-Waggressive-loop-optimizations] In file included from ../sound/pci/echoaudio/layla24.c:112: ../sound/pci/echoaudio/echoaudio.c: In function 'snd_echo_vumeters_get': ../sound/pci/echoaudio/echoaudio_dsp.c:658:9: warning: iteration 1073741824 invokes undefined behavior [-Waggressive-loop-optimizations] ../sound/pci/echoaudio/echoaudio_dsp.c:647:9: warning: iteration 1073741824 invokes undefined behavior [-Waggressive-loop-optimizations] Signed-off-by: Olof Johansson Link: https://lore.kernel.org/r/20191207224953.25944-1-olof@lixom.net Signed-off-by: Takashi Iwai --- sound/pci/echoaudio/echoaudio_dsp.c | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/sound/pci/echoaudio/echoaudio_dsp.c b/sound/pci/echoaudio/echoaudio_dsp.c index 50d4a87a6bb34..f02f5b1568dee 100644 --- a/sound/pci/echoaudio/echoaudio_dsp.c +++ b/sound/pci/echoaudio/echoaudio_dsp.c @@ -635,36 +635,30 @@ This function assumes there are no more than 16 in/out busses or pipes Meters is an array [3][16][2] of long. */ static void get_audio_meters(struct echoaudio *chip, long *meters) { - int i, m, n; + unsigned int i, m, n; - m = 0; - n = 0; - for (i = 0; i < num_busses_out(chip); i++, m++) { + for (i = 0 ; i < 96; i++) + meters[i] = 0; + + for (m = 0, n = 0, i = 0; i < num_busses_out(chip); i++, m++) { meters[n++] = chip->comm_page->vu_meter[m]; meters[n++] = chip->comm_page->peak_meter[m]; } - for (; n < 32; n++) - meters[n] = 0; #ifdef ECHOCARD_ECHO3G m = E3G_MAX_OUTPUTS; /* Skip unused meters */ #endif - for (i = 0; i < num_busses_in(chip); i++, m++) { + for (n = 32, i = 0; i < num_busses_in(chip); i++, m++) { meters[n++] = chip->comm_page->vu_meter[m]; meters[n++] = chip->comm_page->peak_meter[m]; } - for (; n < 64; n++) - meters[n] = 0; - #ifdef ECHOCARD_HAS_VMIXER - for (i = 0; i < num_pipes_out(chip); i++, m++) { + for (n = 64, i = 0; i < num_pipes_out(chip); i++, m++) { meters[n++] = chip->comm_page->vu_meter[m]; meters[n++] = chip->comm_page->peak_meter[m]; } #endif - for (; n < 96; n++) - meters[n] = 0; } -- GitLab From fd230ffaa48b28954cde1bf1121aedcbb8db3883 Mon Sep 17 00:00:00 2001 From: Dan Murphy Date: Thu, 5 Dec 2019 11:57:15 -0600 Subject: [PATCH 0194/1437] MAINTAINERS: Add myself as a maintainer for MMIO m_can Since I refactored the code to create a m_can framework and we have a MMIO MCAN IP as well add myself to help maintain the code. Signed-off-by: Dan Murphy Signed-off-by: Marc Kleine-Budde --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index ecc354f4b6928..1d50632f7662a 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -10095,6 +10095,7 @@ S: Maintained F: drivers/media/radio/radio-maxiradio* MCAN MMIO DEVICE DRIVER +M: Dan Murphy M: Sriram Dash L: linux-can@vger.kernel.org S: Maintained -- GitLab From 1a2e9d2f794e8789d8b4503340ea3465163db2f8 Mon Sep 17 00:00:00 2001 From: Dan Murphy Date: Thu, 5 Dec 2019 11:57:16 -0600 Subject: [PATCH 0195/1437] MAINTAINERS: Add myself as a maintainer for TCAN4x5x Adding myself to support the TI TCAN4X5X SPI CAN device. Signed-off-by: Dan Murphy Signed-off-by: Marc Kleine-Budde --- MAINTAINERS | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 1d50632f7662a..cdeabd4ee1a60 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -16498,6 +16498,13 @@ L: alsa-devel@alsa-project.org (moderated for non-subscribers) S: Odd Fixes F: sound/soc/codecs/tas571x* +TI TCAN4X5X DEVICE DRIVER +M: Dan Murphy +L: linux-can@vger.kernel.org +S: Maintained +F: Documentation/devicetree/bindings/net/can/tcan4x5x.txt +F: drivers/net/can/m_can/tcan4x5x.c + TI TRF7970A NFC DRIVER M: Mark Greer L: linux-wireless@vger.kernel.org -- GitLab From 00d4e14d2e4caf5f7254a505fee5eeca8cd37bd4 Mon Sep 17 00:00:00 2001 From: Oleksij Rempel Date: Fri, 6 Dec 2019 15:18:35 +0100 Subject: [PATCH 0196/1437] can: j1939: j1939_sk_bind(): take priv after lock is held syzbot reproduced following crash: =============================================================================== kasan: CONFIG_KASAN_INLINE enabled kasan: GPF could be caused by NULL-ptr deref or user memory access general protection fault: 0000 [#1] PREEMPT SMP KASAN CPU: 0 PID: 9844 Comm: syz-executor.0 Not tainted 5.4.0-syzkaller #0 Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 01/01/2011 RIP: 0010:__lock_acquire+0x1254/0x4a00 kernel/locking/lockdep.c:3828 Code: 00 0f 85 96 24 00 00 48 81 c4 f0 00 00 00 5b 41 5c 41 5d 41 5e 41 5f 5d c3 48 b8 00 00 00 00 00 fc ff df 4c 89 f2 48 c1 ea 03 <80> 3c 02 00 0f 85 0b 28 00 00 49 81 3e 20 19 78 8a 0f 84 5f ee ff RSP: 0018:ffff888099c3fb48 EFLAGS: 00010006 RAX: dffffc0000000000 RBX: 0000000000000000 RCX: 0000000000000000 RDX: 0000000000000218 RSI: 0000000000000000 RDI: 0000000000000001 RBP: ffff888099c3fc60 R08: 0000000000000001 R09: 0000000000000001 R10: fffffbfff146e1d0 R11: ffff888098720400 R12: 00000000000010c0 R13: 0000000000000000 R14: 00000000000010c0 R15: 0000000000000000 FS: 00007f0559e98700(0000) GS:ffff8880ae800000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 00007fe4d89e0000 CR3: 0000000099606000 CR4: 00000000001406f0 DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400 Call Trace: lock_acquire+0x190/0x410 kernel/locking/lockdep.c:4485 __raw_spin_lock_bh include/linux/spinlock_api_smp.h:135 [inline] _raw_spin_lock_bh+0x33/0x50 kernel/locking/spinlock.c:175 spin_lock_bh include/linux/spinlock.h:343 [inline] j1939_jsk_del+0x32/0x210 net/can/j1939/socket.c:89 j1939_sk_bind+0x2ea/0x8f0 net/can/j1939/socket.c:448 __sys_bind+0x239/0x290 net/socket.c:1648 __do_sys_bind net/socket.c:1659 [inline] __se_sys_bind net/socket.c:1657 [inline] __x64_sys_bind+0x73/0xb0 net/socket.c:1657 do_syscall_64+0xfa/0x790 arch/x86/entry/common.c:294 entry_SYSCALL_64_after_hwframe+0x49/0xbe RIP: 0033:0x45a679 Code: ad b6 fb ff c3 66 2e 0f 1f 84 00 00 00 00 00 66 90 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d 01 f0 ff ff 0f 83 7b b6 fb ff c3 66 2e 0f 1f 84 00 00 00 00 RSP: 002b:00007f0559e97c78 EFLAGS: 00000246 ORIG_RAX: 0000000000000031 RAX: ffffffffffffffda RBX: 0000000000000003 RCX: 000000000045a679 RDX: 0000000000000018 RSI: 0000000020000240 RDI: 0000000000000003 RBP: 000000000075bf20 R08: 0000000000000000 R09: 0000000000000000 R10: 0000000000000000 R11: 0000000000000246 R12: 00007f0559e986d4 R13: 00000000004c09e9 R14: 00000000004d37d0 R15: 00000000ffffffff Modules linked in: ------------[ cut here ]------------ WARNING: CPU: 0 PID: 9844 at kernel/locking/mutex.c:1419 mutex_trylock+0x279/0x2f0 kernel/locking/mutex.c:1427 =============================================================================== This issues was caused by null pointer deference. Where j1939_sk_bind() was using currently not existing priv. Possible scenario may look as following: cpu0 cpu1 bind() bind() j1939_sk_bind() j1939_sk_bind() priv = jsk->priv; priv = jsk->priv; lock_sock(sock->sk); priv = j1939_netdev_start(ndev); j1939_jsk_add(priv, jsk); jsk->priv = priv; relase_sock(sock->sk); lock_sock(sock->sk); j1939_jsk_del(priv, jsk); ..... ooops ...... With this patch we move "priv = jsk->priv;" after the lock, to avoid assigning of wrong priv pointer. Reported-by: syzbot+99e9e1b200a1e363237d@syzkaller.appspotmail.com Fixes: 9d71dd0c7009 ("can: add support of SAE J1939 protocol") Signed-off-by: Oleksij Rempel Cc: linux-stable # >= v5.4 Signed-off-by: Marc Kleine-Budde --- net/can/j1939/socket.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/net/can/j1939/socket.c b/net/can/j1939/socket.c index de09b0a65791d..f7587428febdd 100644 --- a/net/can/j1939/socket.c +++ b/net/can/j1939/socket.c @@ -423,9 +423,9 @@ static int j1939_sk_bind(struct socket *sock, struct sockaddr *uaddr, int len) { struct sockaddr_can *addr = (struct sockaddr_can *)uaddr; struct j1939_sock *jsk = j1939_sk(sock->sk); - struct j1939_priv *priv = jsk->priv; - struct sock *sk = sock->sk; - struct net *net = sock_net(sk); + struct j1939_priv *priv; + struct sock *sk; + struct net *net; int ret = 0; ret = j1939_sk_sanity_check(addr, len); @@ -434,6 +434,10 @@ static int j1939_sk_bind(struct socket *sock, struct sockaddr *uaddr, int len) lock_sock(sock->sk); + priv = jsk->priv; + sk = sock->sk; + net = sock_net(sk); + /* Already bound to an interface? */ if (jsk->state & J1939_SOCK_BOUND) { /* A re-bind() to a different interface is not -- GitLab From 8ac9d71d601374222a230804e419cd40c4492e1c Mon Sep 17 00:00:00 2001 From: Marc Kleine-Budde Date: Thu, 21 Nov 2019 10:47:50 +0100 Subject: [PATCH 0197/1437] can: j1939: fix address claim code example During development the define J1939_PGN_ADDRESS_REQUEST was renamed to J1939_PGN_REQUEST. It was forgotten to adjust the documentation accordingly. This patch fixes the name of the symbol. Reported-by: https://github.com/linux-can/can-utils/issues/159#issuecomment-556538798 Fixes: 9d71dd0c7009 ("can: add support of SAE J1939 protocol") Cc: Oleksij Rempel Signed-off-by: Marc Kleine-Budde --- Documentation/networking/j1939.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/networking/j1939.rst b/Documentation/networking/j1939.rst index dc60b13fcd096..f5be243d250a4 100644 --- a/Documentation/networking/j1939.rst +++ b/Documentation/networking/j1939.rst @@ -339,7 +339,7 @@ To claim an address following code example can be used: .pgn = J1939_PGN_ADDRESS_CLAIMED, .pgn_mask = J1939_PGN_PDU1_MAX, }, { - .pgn = J1939_PGN_ADDRESS_REQUEST, + .pgn = J1939_PGN_REQUEST, .pgn_mask = J1939_PGN_PDU1_MAX, }, { .pgn = J1939_PGN_ADDRESS_COMMANDED, -- GitLab From 9ab79b06ddf3cdf6484d60b3e5fe113e733145c8 Mon Sep 17 00:00:00 2001 From: Srinivas Neeli Date: Tue, 3 Dec 2019 17:46:36 +0530 Subject: [PATCH 0198/1437] can: xilinx_can: Fix missing Rx can packets on CANFD2.0 CANFD2.0 core uses BRAM for storing acceptance filter ID(AFID) and MASK (AFMASK)registers. So by default AFID and AFMASK registers contain random data. Due to random data, we are not able to receive all CAN ids. Initializing AFID and AFMASK registers with Zero before enabling acceptance filter to receive all packets irrespective of ID and Mask. Fixes: 0db9071353a0 ("can: xilinx: add can 2.0 support") Signed-off-by: Michal Simek Signed-off-by: Srinivas Neeli Reviewed-by: Naga Sureshkumar Relli Cc: linux-stable # >= v5.0 Signed-off-by: Marc Kleine-Budde --- drivers/net/can/xilinx_can.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/net/can/xilinx_can.c b/drivers/net/can/xilinx_can.c index 464af939cd8af..c1dbab8c896d5 100644 --- a/drivers/net/can/xilinx_can.c +++ b/drivers/net/can/xilinx_can.c @@ -60,6 +60,8 @@ enum xcan_reg { XCAN_TXMSG_BASE_OFFSET = 0x0100, /* TX Message Space */ XCAN_RXMSG_BASE_OFFSET = 0x1100, /* RX Message Space */ XCAN_RXMSG_2_BASE_OFFSET = 0x2100, /* RX Message Space */ + XCAN_AFR_2_MASK_OFFSET = 0x0A00, /* Acceptance Filter MASK */ + XCAN_AFR_2_ID_OFFSET = 0x0A04, /* Acceptance Filter ID */ }; #define XCAN_FRAME_ID_OFFSET(frame_base) ((frame_base) + 0x00) @@ -1809,6 +1811,11 @@ static int xcan_probe(struct platform_device *pdev) pm_runtime_put(&pdev->dev); + if (priv->devtype.flags & XCAN_FLAG_CANFD_2) { + priv->write_reg(priv, XCAN_AFR_2_ID_OFFSET, 0x00000000); + priv->write_reg(priv, XCAN_AFR_2_MASK_OFFSET, 0x00000000); + } + netdev_dbg(ndev, "reg_base=0x%p irq=%d clock=%d, tx buffers: actual %d, using %d\n", priv->reg_base, ndev->irq, priv->can.clock.freq, hw_tx_max, priv->tx_max); -- GitLab From e707180ae2df1c87e26ec7a6fd70d07483bde7fd Mon Sep 17 00:00:00 2001 From: Sean Nyekjaer Date: Wed, 4 Dec 2019 11:36:06 +0000 Subject: [PATCH 0199/1437] can: flexcan: fix possible deadlock and out-of-order reception after wakeup When suspending, and there is still CAN traffic on the interfaces the flexcan immediately wakes the platform again. As it should :-). But it throws this error msg: [ 3169.378661] PM: noirq suspend of devices failed On the way down to suspend the interface that throws the error message calls flexcan_suspend() but fails to call flexcan_noirq_suspend(). That means flexcan_enter_stop_mode() is called, but on the way out of suspend the driver only calls flexcan_resume() and skips flexcan_noirq_resume(), thus it doesn't call flexcan_exit_stop_mode(). This leaves the flexcan in stop mode, and with the current driver it can't recover from this even with a soft reboot, it requires a hard reboot. This patch fixes the deadlock when using self wakeup, by calling flexcan_exit_stop_mode() from flexcan_resume() instead of flexcan_noirq_resume(). This also fixes another issue: CAN frames are received out-of-order in first IRQ handler run after wakeup. The problem is that the wakeup latency from frame reception to the IRQ handler (where the CAN frames are sorted by timestamp) is much bigger than the time stamp counter wrap around time. This means it's impossible to sort the CAN frames by timestamp. The reason is that the controller exits stop mode during noirq resume, which means it receives frames immediately, but interrupt handling is still not possible. So exit stop mode during resume stage instead of noirq resume fixes this issue. Fixes: de3578c198c6 ("can: flexcan: add self wakeup support") Signed-off-by: Sean Nyekjaer Tested-by: Sean Nyekjaer Signed-off-by: Joakim Zhang Cc: linux-stable # >= v5.0 Signed-off-by: Marc Kleine-Budde --- drivers/net/can/flexcan.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/drivers/net/can/flexcan.c b/drivers/net/can/flexcan.c index a929cdda9ab23..b6f675a5e2d9d 100644 --- a/drivers/net/can/flexcan.c +++ b/drivers/net/can/flexcan.c @@ -1722,6 +1722,9 @@ static int __maybe_unused flexcan_resume(struct device *device) netif_start_queue(dev); if (device_may_wakeup(device)) { disable_irq_wake(dev->irq); + err = flexcan_exit_stop_mode(priv); + if (err) + return err; } else { err = pm_runtime_force_resume(device); if (err) @@ -1767,14 +1770,9 @@ static int __maybe_unused flexcan_noirq_resume(struct device *device) { struct net_device *dev = dev_get_drvdata(device); struct flexcan_priv *priv = netdev_priv(dev); - int err; - if (netif_running(dev) && device_may_wakeup(device)) { + if (netif_running(dev) && device_may_wakeup(device)) flexcan_enable_wakeup_irq(priv, false); - err = flexcan_exit_stop_mode(priv); - if (err) - return err; - } return 0; } -- GitLab From b7603d080ffcf8689ec91ca300caf84d8dbed317 Mon Sep 17 00:00:00 2001 From: Joakim Zhang Date: Wed, 4 Dec 2019 11:36:11 +0000 Subject: [PATCH 0200/1437] can: flexcan: add low power enter/exit acknowledgment helper The MCR[LPMACK] read-only bit indicates that FlexCAN is in a lower-power mode (Disabled mode, Doze mode, Stop mode). The CPU can poll this bit to know when FlexCAN has actually entered low power mode. The low power enter/exit acknowledgment helper will reduce code duplication for disabled mode, doze mode and stop mode. Tested-by: Sean Nyekjaer Signed-off-by: Joakim Zhang Signed-off-by: Marc Kleine-Budde --- drivers/net/can/flexcan.c | 46 +++++++++++++++++++++++++-------------- 1 file changed, 30 insertions(+), 16 deletions(-) diff --git a/drivers/net/can/flexcan.c b/drivers/net/can/flexcan.c index b6f675a5e2d9d..9f3a5e56fc377 100644 --- a/drivers/net/can/flexcan.c +++ b/drivers/net/can/flexcan.c @@ -389,6 +389,34 @@ static struct flexcan_mb __iomem *flexcan_get_mb(const struct flexcan_priv *priv (&priv->regs->mb[bank][priv->mb_size * mb_index]); } +static int flexcan_low_power_enter_ack(struct flexcan_priv *priv) +{ + struct flexcan_regs __iomem *regs = priv->regs; + unsigned int timeout = FLEXCAN_TIMEOUT_US / 10; + + while (timeout-- && !(priv->read(®s->mcr) & FLEXCAN_MCR_LPM_ACK)) + udelay(10); + + if (!(priv->read(®s->mcr) & FLEXCAN_MCR_LPM_ACK)) + return -ETIMEDOUT; + + return 0; +} + +static int flexcan_low_power_exit_ack(struct flexcan_priv *priv) +{ + struct flexcan_regs __iomem *regs = priv->regs; + unsigned int timeout = FLEXCAN_TIMEOUT_US / 10; + + while (timeout-- && (priv->read(®s->mcr) & FLEXCAN_MCR_LPM_ACK)) + udelay(10); + + if (priv->read(®s->mcr) & FLEXCAN_MCR_LPM_ACK) + return -ETIMEDOUT; + + return 0; +} + static void flexcan_enable_wakeup_irq(struct flexcan_priv *priv, bool enable) { struct flexcan_regs __iomem *regs = priv->regs; @@ -506,39 +534,25 @@ static inline int flexcan_transceiver_disable(const struct flexcan_priv *priv) static int flexcan_chip_enable(struct flexcan_priv *priv) { struct flexcan_regs __iomem *regs = priv->regs; - unsigned int timeout = FLEXCAN_TIMEOUT_US / 10; u32 reg; reg = priv->read(®s->mcr); reg &= ~FLEXCAN_MCR_MDIS; priv->write(reg, ®s->mcr); - while (timeout-- && (priv->read(®s->mcr) & FLEXCAN_MCR_LPM_ACK)) - udelay(10); - - if (priv->read(®s->mcr) & FLEXCAN_MCR_LPM_ACK) - return -ETIMEDOUT; - - return 0; + return flexcan_low_power_exit_ack(priv); } static int flexcan_chip_disable(struct flexcan_priv *priv) { struct flexcan_regs __iomem *regs = priv->regs; - unsigned int timeout = FLEXCAN_TIMEOUT_US / 10; u32 reg; reg = priv->read(®s->mcr); reg |= FLEXCAN_MCR_MDIS; priv->write(reg, ®s->mcr); - while (timeout-- && !(priv->read(®s->mcr) & FLEXCAN_MCR_LPM_ACK)) - udelay(10); - - if (!(priv->read(®s->mcr) & FLEXCAN_MCR_LPM_ACK)) - return -ETIMEDOUT; - - return 0; + return flexcan_low_power_enter_ack(priv); } static int flexcan_chip_freeze(struct flexcan_priv *priv) -- GitLab From 048e3a34a2e7669bf475eb56c7345ad9d8d2b8e3 Mon Sep 17 00:00:00 2001 From: Joakim Zhang Date: Wed, 4 Dec 2019 11:36:14 +0000 Subject: [PATCH 0201/1437] can: flexcan: poll MCR_LPM_ACK instead of GPR ACK for stop mode acknowledgment Stop Mode is entered when Stop Mode is requested at chip level and MCR[LPM_ACK] is asserted by the FlexCAN. Double check with IP owner, the MCR[LPM_ACK] bit should be polled for stop mode acknowledgment, not the acknowledgment from chip level which is used to gate flexcan clocks. This patch depends on: b7603d080ffc ("can: flexcan: add low power enter/exit acknowledgment helper") Fixes: 5f186c257fa4 (can: flexcan: fix stop mode acknowledgment) Tested-by: Sean Nyekjaer Signed-off-by: Joakim Zhang Cc: linux-stable # >= v5.0 Signed-off-by: Marc Kleine-Budde --- drivers/net/can/flexcan.c | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) diff --git a/drivers/net/can/flexcan.c b/drivers/net/can/flexcan.c index 9f3a5e56fc377..94d10ec954a05 100644 --- a/drivers/net/can/flexcan.c +++ b/drivers/net/can/flexcan.c @@ -435,7 +435,6 @@ static void flexcan_enable_wakeup_irq(struct flexcan_priv *priv, bool enable) static inline int flexcan_enter_stop_mode(struct flexcan_priv *priv) { struct flexcan_regs __iomem *regs = priv->regs; - unsigned int ackval; u32 reg_mcr; reg_mcr = priv->read(®s->mcr); @@ -446,36 +445,24 @@ static inline int flexcan_enter_stop_mode(struct flexcan_priv *priv) regmap_update_bits(priv->stm.gpr, priv->stm.req_gpr, 1 << priv->stm.req_bit, 1 << priv->stm.req_bit); - /* get stop acknowledgment */ - if (regmap_read_poll_timeout(priv->stm.gpr, priv->stm.ack_gpr, - ackval, ackval & (1 << priv->stm.ack_bit), - 0, FLEXCAN_TIMEOUT_US)) - return -ETIMEDOUT; - - return 0; + return flexcan_low_power_enter_ack(priv); } static inline int flexcan_exit_stop_mode(struct flexcan_priv *priv) { struct flexcan_regs __iomem *regs = priv->regs; - unsigned int ackval; u32 reg_mcr; /* remove stop request */ regmap_update_bits(priv->stm.gpr, priv->stm.req_gpr, 1 << priv->stm.req_bit, 0); - /* get stop acknowledgment */ - if (regmap_read_poll_timeout(priv->stm.gpr, priv->stm.ack_gpr, - ackval, !(ackval & (1 << priv->stm.ack_bit)), - 0, FLEXCAN_TIMEOUT_US)) - return -ETIMEDOUT; reg_mcr = priv->read(®s->mcr); reg_mcr &= ~FLEXCAN_MCR_SLF_WAK; priv->write(reg_mcr, ®s->mcr); - return 0; + return flexcan_low_power_exit_ack(priv); } static inline void flexcan_error_irq_enable(const struct flexcan_priv *priv) -- GitLab From 60552253e29c8860ee5bf1e6064591b0917c0394 Mon Sep 17 00:00:00 2001 From: Sean Nyekjaer Date: Fri, 6 Dec 2019 16:29:22 +0100 Subject: [PATCH 0202/1437] can: m_can: tcan4x5x: add required delay after reset According to section "8.3.8 RST Pin" in the datasheet we are required to wait >700us after the device is reset. Signed-off-by: Sean Nyekjaer Acked-by: Dan Murphy Cc: linux-stable # >= v5.4 Signed-off-by: Marc Kleine-Budde --- drivers/net/can/m_can/tcan4x5x.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/can/m_can/tcan4x5x.c b/drivers/net/can/m_can/tcan4x5x.c index 3db619209fe19..d5d4bfa9c8fd7 100644 --- a/drivers/net/can/m_can/tcan4x5x.c +++ b/drivers/net/can/m_can/tcan4x5x.c @@ -354,6 +354,8 @@ static int tcan4x5x_parse_config(struct m_can_classdev *cdev) if (IS_ERR(tcan4x5x->reset_gpio)) tcan4x5x->reset_gpio = NULL; + usleep_range(700, 1000); + tcan4x5x->device_state_gpio = devm_gpiod_get_optional(cdev->dev, "device-state", GPIOD_IN); -- GitLab From 9c9e1b012a218019079d676a95b9570b7f444f7f Mon Sep 17 00:00:00 2001 From: Sean Nyekjaer Date: Fri, 6 Dec 2019 16:29:23 +0100 Subject: [PATCH 0203/1437] dt-bindings: can: tcan4x5x: reset pin is active high Change the reset pin example to active high to be in line with the datasheet Signed-off-by: Sean Nyekjaer Cc: Rob Herring Signed-off-by: Marc Kleine-Budde --- Documentation/devicetree/bindings/net/can/tcan4x5x.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/net/can/tcan4x5x.txt b/Documentation/devicetree/bindings/net/can/tcan4x5x.txt index 27e1b4cebfbd4..e8aa21d9174e2 100644 --- a/Documentation/devicetree/bindings/net/can/tcan4x5x.txt +++ b/Documentation/devicetree/bindings/net/can/tcan4x5x.txt @@ -36,5 +36,5 @@ tcan4x5x: tcan4x5x@0 { interrupts = <14 GPIO_ACTIVE_LOW>; device-state-gpios = <&gpio3 21 GPIO_ACTIVE_HIGH>; device-wake-gpios = <&gpio1 15 GPIO_ACTIVE_HIGH>; - reset-gpios = <&gpio1 27 GPIO_ACTIVE_LOW>; + reset-gpios = <&gpio1 27 GPIO_ACTIVE_HIGH>; }; -- GitLab From 1202d2318e2f370befe6c154db75ffaad3ec7ba0 Mon Sep 17 00:00:00 2001 From: Dan Murphy Date: Wed, 4 Dec 2019 11:51:11 -0600 Subject: [PATCH 0204/1437] dt-bindings: tcan4x5x: Make wake-gpio an optional gpio The wake-up of the device can be configured as an optional feature of the device. Move the wake-up gpio from a requried property to an optional property. Signed-off-by: Dan Murphy Cc: Rob Herring Reviewed-by: Sean Nyekjaer Tested-by: Sean Nyekjaer Signed-off-by: Marc Kleine-Budde --- Documentation/devicetree/bindings/net/can/tcan4x5x.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/net/can/tcan4x5x.txt b/Documentation/devicetree/bindings/net/can/tcan4x5x.txt index e8aa21d9174e2..6bdcc3f84bd3c 100644 --- a/Documentation/devicetree/bindings/net/can/tcan4x5x.txt +++ b/Documentation/devicetree/bindings/net/can/tcan4x5x.txt @@ -10,7 +10,6 @@ Required properties: - #size-cells: 0 - spi-max-frequency: Maximum frequency of the SPI bus the chip can operate at should be less than or equal to 18 MHz. - - device-wake-gpios: Wake up GPIO to wake up the TCAN device. - interrupt-parent: the phandle to the interrupt controller which provides the interrupt. - interrupts: interrupt specification for data-ready. @@ -23,6 +22,7 @@ Optional properties: reset. - device-state-gpios: Input GPIO that indicates if the device is in a sleep state or if the device is active. + - device-wake-gpios: Wake up GPIO to wake up the TCAN device. Example: tcan4x5x: tcan4x5x@0 { -- GitLab From 2de497356955ce58cd066fb03d2da5235f3c7c23 Mon Sep 17 00:00:00 2001 From: Dan Murphy Date: Wed, 4 Dec 2019 11:51:12 -0600 Subject: [PATCH 0205/1437] can: tcan45x: Make wake-up GPIO an optional GPIO The device has the ability to disable the wake-up pin option. The wake-up pin can be either force to GND or Vsup and does not have to be tied to a GPIO. In order for the device to not use the wake-up feature write the register to disable the WAKE_CONFIG option. Signed-off-by: Dan Murphy Cc: Sean Nyekjaer Reviewed-by: Sean Nyekjaer Signed-off-by: Marc Kleine-Budde --- drivers/net/can/m_can/tcan4x5x.c | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/drivers/net/can/m_can/tcan4x5x.c b/drivers/net/can/m_can/tcan4x5x.c index d5d4bfa9c8fd7..4e1789ea2bc3e 100644 --- a/drivers/net/can/m_can/tcan4x5x.c +++ b/drivers/net/can/m_can/tcan4x5x.c @@ -101,6 +101,8 @@ #define TCAN4X5X_MODE_STANDBY BIT(6) #define TCAN4X5X_MODE_NORMAL BIT(7) +#define TCAN4X5X_DISABLE_WAKE_MSK (BIT(31) | BIT(30)) + #define TCAN4X5X_SW_RESET BIT(2) #define TCAN4X5X_MCAN_CONFIGURED BIT(5) @@ -338,6 +340,14 @@ static int tcan4x5x_init(struct m_can_classdev *cdev) return ret; } +static int tcan4x5x_disable_wake(struct m_can_classdev *cdev) +{ + struct tcan4x5x_priv *tcan4x5x = cdev->device_data; + + return regmap_update_bits(tcan4x5x->regmap, TCAN4X5X_CONFIG, + TCAN4X5X_DISABLE_WAKE_MSK, 0x00); +} + static int tcan4x5x_parse_config(struct m_can_classdev *cdev) { struct tcan4x5x_priv *tcan4x5x = cdev->device_data; @@ -345,8 +355,10 @@ static int tcan4x5x_parse_config(struct m_can_classdev *cdev) tcan4x5x->device_wake_gpio = devm_gpiod_get(cdev->dev, "device-wake", GPIOD_OUT_HIGH); if (IS_ERR(tcan4x5x->device_wake_gpio)) { - dev_err(cdev->dev, "device-wake gpio not defined\n"); - return -EINVAL; + if (PTR_ERR(tcan4x5x->power) == -EPROBE_DEFER) + return -EPROBE_DEFER; + + tcan4x5x_disable_wake(cdev); } tcan4x5x->reset_gpio = devm_gpiod_get_optional(cdev->dev, "reset", @@ -430,10 +442,6 @@ static int tcan4x5x_can_probe(struct spi_device *spi) spi_set_drvdata(spi, priv); - ret = tcan4x5x_parse_config(mcan_class); - if (ret) - goto out_clk; - /* Configure the SPI bus */ spi->bits_per_word = 32; ret = spi_setup(spi); @@ -443,6 +451,10 @@ static int tcan4x5x_can_probe(struct spi_device *spi) priv->regmap = devm_regmap_init(&spi->dev, &tcan4x5x_bus, &spi->dev, &tcan4x5x_regmap); + ret = tcan4x5x_parse_config(mcan_class); + if (ret) + goto out_clk; + tcan4x5x_power_enable(priv->power, 1); ret = m_can_class_register(mcan_class); -- GitLab From da2311a6385c3b499da2ed5d9be59ce331fa93e9 Mon Sep 17 00:00:00 2001 From: Xiaolong Huang Date: Sat, 7 Dec 2019 22:40:24 +0800 Subject: [PATCH 0206/1437] can: kvaser_usb: kvaser_usb_leaf: Fix some info-leaks to USB devices Uninitialized Kernel memory can leak to USB devices. Fix this by using kzalloc() instead of kmalloc(). Signed-off-by: Xiaolong Huang Fixes: 7259124eac7d ("can: kvaser_usb: Split driver into kvaser_usb_core.c and kvaser_usb_leaf.c") Cc: linux-stable # >= v4.19 Signed-off-by: Marc Kleine-Budde --- drivers/net/can/usb/kvaser_usb/kvaser_usb_leaf.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/net/can/usb/kvaser_usb/kvaser_usb_leaf.c b/drivers/net/can/usb/kvaser_usb/kvaser_usb_leaf.c index 07d2f3aa2c026..ae4c37e1bb753 100644 --- a/drivers/net/can/usb/kvaser_usb/kvaser_usb_leaf.c +++ b/drivers/net/can/usb/kvaser_usb/kvaser_usb_leaf.c @@ -608,7 +608,7 @@ static int kvaser_usb_leaf_simple_cmd_async(struct kvaser_usb_net_priv *priv, struct kvaser_cmd *cmd; int err; - cmd = kmalloc(sizeof(*cmd), GFP_ATOMIC); + cmd = kzalloc(sizeof(*cmd), GFP_ATOMIC); if (!cmd) return -ENOMEM; @@ -1140,7 +1140,7 @@ static int kvaser_usb_leaf_set_opt_mode(const struct kvaser_usb_net_priv *priv) struct kvaser_cmd *cmd; int rc; - cmd = kmalloc(sizeof(*cmd), GFP_KERNEL); + cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); if (!cmd) return -ENOMEM; @@ -1206,7 +1206,7 @@ static int kvaser_usb_leaf_flush_queue(struct kvaser_usb_net_priv *priv) struct kvaser_cmd *cmd; int rc; - cmd = kmalloc(sizeof(*cmd), GFP_KERNEL); + cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); if (!cmd) return -ENOMEM; -- GitLab From ab0eb16205b43ece4c78e2259e681ff3d645ea66 Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Fri, 6 Dec 2019 16:55:37 +0000 Subject: [PATCH 0207/1437] efi/memreserve: Register reservations as 'reserved' in /proc/iomem Memory regions that are reserved using efi_mem_reserve_persistent() are recorded in a special EFI config table which survives kexec, allowing the incoming kernel to honour them as well. However, such reservations are not visible in /proc/iomem, and so the kexec tools that load the incoming kernel and its initrd into memory may overwrite these reserved regions before the incoming kernel has a chance to reserve them from further use. Address this problem by adding these reservations to /proc/iomem as they are created. Note that reservations that are inherited from a previous kernel are memblock_reserve()'d early on, so they are already visible in /proc/iomem. Tested-by: Masayoshi Mizuma Tested-by: Bhupesh Sharma Signed-off-by: Ard Biesheuvel Reviewed-by: Bhupesh Sharma Cc: # v5.4+ Cc: Andy Shevchenko Cc: Arvind Sankar Cc: linux-efi@vger.kernel.org Link: https://lkml.kernel.org/r/20191206165542.31469-2-ardb@kernel.org Signed-off-by: Ingo Molnar --- drivers/firmware/efi/efi.c | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/drivers/firmware/efi/efi.c b/drivers/firmware/efi/efi.c index d101f072c8f8a..b0961950d9181 100644 --- a/drivers/firmware/efi/efi.c +++ b/drivers/firmware/efi/efi.c @@ -979,6 +979,24 @@ static int __init efi_memreserve_map_root(void) return 0; } +static int efi_mem_reserve_iomem(phys_addr_t addr, u64 size) +{ + struct resource *res, *parent; + + res = kzalloc(sizeof(struct resource), GFP_ATOMIC); + if (!res) + return -ENOMEM; + + res->name = "reserved"; + res->flags = IORESOURCE_MEM; + res->start = addr; + res->end = addr + size - 1; + + /* we expect a conflict with a 'System RAM' region */ + parent = request_resource_conflict(&iomem_resource, res); + return parent ? request_resource(parent, res) : 0; +} + int __ref efi_mem_reserve_persistent(phys_addr_t addr, u64 size) { struct linux_efi_memreserve *rsv; @@ -1003,7 +1021,7 @@ int __ref efi_mem_reserve_persistent(phys_addr_t addr, u64 size) rsv->entry[index].size = size; memunmap(rsv); - return 0; + return efi_mem_reserve_iomem(addr, size); } memunmap(rsv); } @@ -1013,6 +1031,12 @@ int __ref efi_mem_reserve_persistent(phys_addr_t addr, u64 size) if (!rsv) return -ENOMEM; + rc = efi_mem_reserve_iomem(__pa(rsv), SZ_4K); + if (rc) { + free_page((unsigned long)rsv); + return rc; + } + /* * The memremap() call above assumes that a linux_efi_memreserve entry * never crosses a page boundary, so let's ensure that this remains true @@ -1029,7 +1053,7 @@ int __ref efi_mem_reserve_persistent(phys_addr_t addr, u64 size) efi_memreserve_root->next = __pa(rsv); spin_unlock(&efi_mem_reserve_persistent_lock); - return 0; + return efi_mem_reserve_iomem(addr, size); } static int __init efi_memreserve_root_init(void) -- GitLab From 6fc3cec30dfeee7d3c5db8154016aff9d65503c5 Mon Sep 17 00:00:00 2001 From: Arvind Sankar Date: Fri, 6 Dec 2019 16:55:38 +0000 Subject: [PATCH 0208/1437] efi/gop: Return EFI_NOT_FOUND if there are no usable GOPs If we don't find a usable instance of the Graphics Output Protocol (GOP) because none of them have a framebuffer (i.e. they were all PIXEL_BLT_ONLY), but all the EFI calls succeeded, we will return EFI_SUCCESS even though we didn't find a usable GOP. Fix this by explicitly returning EFI_NOT_FOUND if no usable GOPs are found, allowing the caller to probe for UGA instead. Signed-off-by: Arvind Sankar Signed-off-by: Ard Biesheuvel Cc: Andy Shevchenko Cc: Bhupesh Sharma Cc: Masayoshi Mizuma Cc: linux-efi@vger.kernel.org Link: https://lkml.kernel.org/r/20191206165542.31469-3-ardb@kernel.org Signed-off-by: Ingo Molnar --- drivers/firmware/efi/libstub/gop.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/firmware/efi/libstub/gop.c b/drivers/firmware/efi/libstub/gop.c index 0101ca4c13b1a..08f3c1a2fb483 100644 --- a/drivers/firmware/efi/libstub/gop.c +++ b/drivers/firmware/efi/libstub/gop.c @@ -119,7 +119,7 @@ setup_gop32(efi_system_table_t *sys_table_arg, struct screen_info *si, u64 fb_base; struct efi_pixel_bitmask pixel_info; int pixel_format; - efi_status_t status = EFI_NOT_FOUND; + efi_status_t status; u32 *handles = (u32 *)(unsigned long)gop_handle; int i; @@ -175,7 +175,7 @@ setup_gop32(efi_system_table_t *sys_table_arg, struct screen_info *si, /* Did we find any GOPs? */ if (!first_gop) - goto out; + return EFI_NOT_FOUND; /* EFI framebuffer */ si->orig_video_isVGA = VIDEO_TYPE_EFI; @@ -197,7 +197,7 @@ setup_gop32(efi_system_table_t *sys_table_arg, struct screen_info *si, si->lfb_size = si->lfb_linelength * si->lfb_height; si->capabilities |= VIDEO_CAPABILITY_SKIP_QUIRKS; -out: + return status; } @@ -237,7 +237,7 @@ setup_gop64(efi_system_table_t *sys_table_arg, struct screen_info *si, u64 fb_base; struct efi_pixel_bitmask pixel_info; int pixel_format; - efi_status_t status = EFI_NOT_FOUND; + efi_status_t status; u64 *handles = (u64 *)(unsigned long)gop_handle; int i; @@ -293,7 +293,7 @@ setup_gop64(efi_system_table_t *sys_table_arg, struct screen_info *si, /* Did we find any GOPs? */ if (!first_gop) - goto out; + return EFI_NOT_FOUND; /* EFI framebuffer */ si->orig_video_isVGA = VIDEO_TYPE_EFI; @@ -315,7 +315,7 @@ setup_gop64(efi_system_table_t *sys_table_arg, struct screen_info *si, si->lfb_size = si->lfb_linelength * si->lfb_height; si->capabilities |= VIDEO_CAPABILITY_SKIP_QUIRKS; -out: + return status; } -- GitLab From dbd89c303b4420f6cdb689fd398349fc83b059dd Mon Sep 17 00:00:00 2001 From: Arvind Sankar Date: Fri, 6 Dec 2019 16:55:39 +0000 Subject: [PATCH 0209/1437] efi/gop: Return EFI_SUCCESS if a usable GOP was found If we've found a usable instance of the Graphics Output Protocol (GOP) with a framebuffer, it is possible that one of the later EFI calls fails while checking if any support console output. In this case status may be an EFI error code even though we found a usable GOP. Fix this by explicitly return EFI_SUCCESS if a usable GOP has been located. Signed-off-by: Arvind Sankar Signed-off-by: Ard Biesheuvel Cc: Andy Shevchenko Cc: Bhupesh Sharma Cc: Masayoshi Mizuma Cc: linux-efi@vger.kernel.org Link: https://lkml.kernel.org/r/20191206165542.31469-4-ardb@kernel.org Signed-off-by: Ingo Molnar --- drivers/firmware/efi/libstub/gop.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/firmware/efi/libstub/gop.c b/drivers/firmware/efi/libstub/gop.c index 08f3c1a2fb483..69b2b019a1d02 100644 --- a/drivers/firmware/efi/libstub/gop.c +++ b/drivers/firmware/efi/libstub/gop.c @@ -198,7 +198,7 @@ setup_gop32(efi_system_table_t *sys_table_arg, struct screen_info *si, si->capabilities |= VIDEO_CAPABILITY_SKIP_QUIRKS; - return status; + return EFI_SUCCESS; } static efi_status_t @@ -316,7 +316,7 @@ setup_gop64(efi_system_table_t *sys_table_arg, struct screen_info *si, si->capabilities |= VIDEO_CAPABILITY_SKIP_QUIRKS; - return status; + return EFI_SUCCESS; } /* -- GitLab From ff397be685e410a59c34b21ce0c55d4daa466bb7 Mon Sep 17 00:00:00 2001 From: Arvind Sankar Date: Fri, 6 Dec 2019 16:55:40 +0000 Subject: [PATCH 0210/1437] efi/gop: Fix memory leak in __gop_query32/64() efi_graphics_output_protocol::query_mode() returns info in callee-allocated memory which must be freed by the caller, which we aren't doing. We don't actually need to call query_mode() in order to obtain the info for the current graphics mode, which is already there in gop->mode->info, so just access it directly in the setup_gop32/64() functions. Also nothing uses the size of the info structure, so don't update the passed-in size (which is the size of the gop_handle table in bytes) unnecessarily. Signed-off-by: Arvind Sankar Signed-off-by: Ard Biesheuvel Cc: Andy Shevchenko Cc: Bhupesh Sharma Cc: Masayoshi Mizuma Cc: linux-efi@vger.kernel.org Link: https://lkml.kernel.org/r/20191206165542.31469-5-ardb@kernel.org Signed-off-by: Ingo Molnar --- drivers/firmware/efi/libstub/gop.c | 66 ++++++------------------------ 1 file changed, 12 insertions(+), 54 deletions(-) diff --git a/drivers/firmware/efi/libstub/gop.c b/drivers/firmware/efi/libstub/gop.c index 69b2b019a1d02..b7bf1e993b8bb 100644 --- a/drivers/firmware/efi/libstub/gop.c +++ b/drivers/firmware/efi/libstub/gop.c @@ -83,30 +83,6 @@ setup_pixel_info(struct screen_info *si, u32 pixels_per_scan_line, } } -static efi_status_t -__gop_query32(efi_system_table_t *sys_table_arg, - struct efi_graphics_output_protocol_32 *gop32, - struct efi_graphics_output_mode_info **info, - unsigned long *size, u64 *fb_base) -{ - struct efi_graphics_output_protocol_mode_32 *mode; - efi_graphics_output_protocol_query_mode query_mode; - efi_status_t status; - unsigned long m; - - m = gop32->mode; - mode = (struct efi_graphics_output_protocol_mode_32 *)m; - query_mode = (void *)(unsigned long)gop32->query_mode; - - status = __efi_call_early(query_mode, (void *)gop32, mode->mode, size, - info); - if (status != EFI_SUCCESS) - return status; - - *fb_base = mode->frame_buffer_base; - return status; -} - static efi_status_t setup_gop32(efi_system_table_t *sys_table_arg, struct screen_info *si, efi_guid_t *proto, unsigned long size, void **gop_handle) @@ -128,6 +104,7 @@ setup_gop32(efi_system_table_t *sys_table_arg, struct screen_info *si, nr_gops = size / sizeof(u32); for (i = 0; i < nr_gops; i++) { + struct efi_graphics_output_protocol_mode_32 *mode; struct efi_graphics_output_mode_info *info = NULL; efi_guid_t conout_proto = EFI_CONSOLE_OUT_DEVICE_GUID; bool conout_found = false; @@ -145,9 +122,11 @@ setup_gop32(efi_system_table_t *sys_table_arg, struct screen_info *si, if (status == EFI_SUCCESS) conout_found = true; - status = __gop_query32(sys_table_arg, gop32, &info, &size, - ¤t_fb_base); - if (status == EFI_SUCCESS && (!first_gop || conout_found) && + mode = (void *)(unsigned long)gop32->mode; + info = (void *)(unsigned long)mode->info; + current_fb_base = mode->frame_buffer_base; + + if ((!first_gop || conout_found) && info->pixel_format != PIXEL_BLT_ONLY) { /* * Systems that use the UEFI Console Splitter may @@ -201,30 +180,6 @@ setup_gop32(efi_system_table_t *sys_table_arg, struct screen_info *si, return EFI_SUCCESS; } -static efi_status_t -__gop_query64(efi_system_table_t *sys_table_arg, - struct efi_graphics_output_protocol_64 *gop64, - struct efi_graphics_output_mode_info **info, - unsigned long *size, u64 *fb_base) -{ - struct efi_graphics_output_protocol_mode_64 *mode; - efi_graphics_output_protocol_query_mode query_mode; - efi_status_t status; - unsigned long m; - - m = gop64->mode; - mode = (struct efi_graphics_output_protocol_mode_64 *)m; - query_mode = (void *)(unsigned long)gop64->query_mode; - - status = __efi_call_early(query_mode, (void *)gop64, mode->mode, size, - info); - if (status != EFI_SUCCESS) - return status; - - *fb_base = mode->frame_buffer_base; - return status; -} - static efi_status_t setup_gop64(efi_system_table_t *sys_table_arg, struct screen_info *si, efi_guid_t *proto, unsigned long size, void **gop_handle) @@ -246,6 +201,7 @@ setup_gop64(efi_system_table_t *sys_table_arg, struct screen_info *si, nr_gops = size / sizeof(u64); for (i = 0; i < nr_gops; i++) { + struct efi_graphics_output_protocol_mode_64 *mode; struct efi_graphics_output_mode_info *info = NULL; efi_guid_t conout_proto = EFI_CONSOLE_OUT_DEVICE_GUID; bool conout_found = false; @@ -263,9 +219,11 @@ setup_gop64(efi_system_table_t *sys_table_arg, struct screen_info *si, if (status == EFI_SUCCESS) conout_found = true; - status = __gop_query64(sys_table_arg, gop64, &info, &size, - ¤t_fb_base); - if (status == EFI_SUCCESS && (!first_gop || conout_found) && + mode = (void *)(unsigned long)gop64->mode; + info = (void *)(unsigned long)mode->info; + current_fb_base = mode->frame_buffer_base; + + if ((!first_gop || conout_found) && info->pixel_format != PIXEL_BLT_ONLY) { /* * Systems that use the UEFI Console Splitter may -- GitLab From 9fa76ca7b8bdcdf51fc8c7b7b7a7bfc4eccceb58 Mon Sep 17 00:00:00 2001 From: Arvind Sankar Date: Fri, 6 Dec 2019 16:55:41 +0000 Subject: [PATCH 0211/1437] efi: Fix efi_loaded_image_t::unload type The ::unload field is a function pointer, so it should be u32 for 32-bit, u64 for 64-bit. Add a prototype for it in the native efi_loaded_image_t type. Also change type of parent_handle and device_handle from void * to efi_handle_t for documentation purposes. The unload method is not used, so no functional change. Signed-off-by: Arvind Sankar Signed-off-by: Ard Biesheuvel Cc: Andy Shevchenko Cc: Bhupesh Sharma Cc: Linus Torvalds Cc: Masayoshi Mizuma Cc: Peter Zijlstra Cc: Thomas Gleixner Cc: linux-efi@vger.kernel.org Link: https://lkml.kernel.org/r/20191206165542.31469-6-ardb@kernel.org Signed-off-by: Ingo Molnar --- include/linux/efi.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/include/linux/efi.h b/include/linux/efi.h index 99dfea595c8c7..aa54586db7a55 100644 --- a/include/linux/efi.h +++ b/include/linux/efi.h @@ -824,7 +824,7 @@ typedef struct { __aligned_u64 image_size; unsigned int image_code_type; unsigned int image_data_type; - unsigned long unload; + u32 unload; } efi_loaded_image_32_t; typedef struct { @@ -840,14 +840,14 @@ typedef struct { __aligned_u64 image_size; unsigned int image_code_type; unsigned int image_data_type; - unsigned long unload; + u64 unload; } efi_loaded_image_64_t; typedef struct { u32 revision; - void *parent_handle; + efi_handle_t parent_handle; efi_system_table_t *system_table; - void *device_handle; + efi_handle_t device_handle; void *file_path; void *reserved; u32 load_options_size; @@ -856,7 +856,7 @@ typedef struct { __aligned_u64 image_size; unsigned int image_code_type; unsigned int image_data_type; - unsigned long unload; + efi_status_t (*unload)(efi_handle_t image_handle); } efi_loaded_image_t; -- GitLab From b418d660bb9798d2249ac6a46c844389ef50b6a5 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Fri, 6 Dec 2019 16:55:42 +0000 Subject: [PATCH 0212/1437] efi/earlycon: Remap entire framebuffer after page initialization When commit: 69c1f396f25b ("efi/x86: Convert x86 EFI earlyprintk into generic earlycon implementation") moved the x86 specific EFI earlyprintk implementation to a shared location, it also tweaked the behaviour. In particular, it dropped a trick with full framebuffer remapping after page initialization, leading to two regressions: 1) very slow scrolling after page initialization, 2) kernel hang when the 'keep_bootcon' command line argument is passed. Putting the tweak back fixes #2 and mitigates #1, i.e., it limits the slow behavior to the early boot stages, presumably due to eliminating heavy map()/unmap() operations per each pixel line on the screen. [ ardb: ensure efifb is unmapped again unless keep_bootcon is in effect. ] [ mingo: speling fixes. ] Signed-off-by: Andy Shevchenko Signed-off-by: Ard Biesheuvel Cc: Arvind Sankar Cc: Bhupesh Sharma Cc: Masayoshi Mizuma Cc: linux-efi@vger.kernel.org Fixes: 69c1f396f25b ("efi/x86: Convert x86 EFI earlyprintk into generic earlycon implementation") Link: https://lkml.kernel.org/r/20191206165542.31469-7-ardb@kernel.org Signed-off-by: Ingo Molnar --- drivers/firmware/efi/earlycon.c | 40 +++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/drivers/firmware/efi/earlycon.c b/drivers/firmware/efi/earlycon.c index c9a0efca17b01..d4077db6dc97f 100644 --- a/drivers/firmware/efi/earlycon.c +++ b/drivers/firmware/efi/earlycon.c @@ -13,18 +13,57 @@ #include +static const struct console *earlycon_console __initdata; static const struct font_desc *font; static u32 efi_x, efi_y; static u64 fb_base; static pgprot_t fb_prot; +static void *efi_fb; + +/* + * EFI earlycon needs to use early_memremap() to map the framebuffer. + * But early_memremap() is not usable for 'earlycon=efifb keep_bootcon', + * memremap() should be used instead. memremap() will be available after + * paging_init() which is earlier than initcall callbacks. Thus adding this + * early initcall function early_efi_map_fb() to map the whole EFI framebuffer. + */ +static int __init efi_earlycon_remap_fb(void) +{ + /* bail if there is no bootconsole or it has been disabled already */ + if (!earlycon_console || !(earlycon_console->flags & CON_ENABLED)) + return 0; + + if (pgprot_val(fb_prot) == pgprot_val(PAGE_KERNEL)) + efi_fb = memremap(fb_base, screen_info.lfb_size, MEMREMAP_WB); + else + efi_fb = memremap(fb_base, screen_info.lfb_size, MEMREMAP_WC); + + return efi_fb ? 0 : -ENOMEM; +} +early_initcall(efi_earlycon_remap_fb); + +static int __init efi_earlycon_unmap_fb(void) +{ + /* unmap the bootconsole fb unless keep_bootcon has left it enabled */ + if (efi_fb && !(earlycon_console->flags & CON_ENABLED)) + memunmap(efi_fb); + return 0; +} +late_initcall(efi_earlycon_unmap_fb); static __ref void *efi_earlycon_map(unsigned long start, unsigned long len) { + if (efi_fb) + return efi_fb + start; + return early_memremap_prot(fb_base + start, len, pgprot_val(fb_prot)); } static __ref void efi_earlycon_unmap(void *addr, unsigned long len) { + if (efi_fb) + return; + early_memunmap(addr, len); } @@ -201,6 +240,7 @@ static int __init efi_earlycon_setup(struct earlycon_device *device, efi_earlycon_scroll_up(); device->con->write = efi_earlycon_write; + earlycon_console = device->con; return 0; } EARLYCON_DECLARE(efifb, efi_earlycon_setup); -- GitLab From ffafde478309af01b2a495ecaf203125abfb35bd Mon Sep 17 00:00:00 2001 From: Gao Xiang Date: Sat, 7 Dec 2019 10:37:26 +0800 Subject: [PATCH 0213/1437] erofs: update documentation Some on-disk structures, fields have been renamed in v5.4, the corresponding document should be updated as well. Also fix misrespresentation of file time and words about fixed-sized output compression, data inline, etc. Link: https://lore.kernel.org/r/20191207025509.6614-1-hsiangkao@aol.com/ Signed-off-by: Gao Xiang --- Documentation/filesystems/erofs.txt | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/Documentation/filesystems/erofs.txt b/Documentation/filesystems/erofs.txt index b0c085326e2e6..db6d39c3ae715 100644 --- a/Documentation/filesystems/erofs.txt +++ b/Documentation/filesystems/erofs.txt @@ -24,11 +24,11 @@ Here is the main features of EROFS: - Metadata & data could be mixed by design; - 2 inode versions for different requirements: - v1 v2 + compact (v1) extended (v2) Inode metadata size: 32 bytes 64 bytes Max file size: 4 GB 16 EB (also limited by max. vol size) Max uids/gids: 65536 4294967296 - File creation time: no yes (64 + 32-bit timestamp) + File change time: no yes (64 + 32-bit timestamp) Max hardlinks: 65536 4294967296 Metadata reserved: 4 bytes 14 bytes @@ -39,7 +39,7 @@ Here is the main features of EROFS: - Support POSIX.1e ACLs by using xattrs; - Support transparent file compression as an option: - LZ4 algorithm with 4 KB fixed-output compression for high performance; + LZ4 algorithm with 4 KB fixed-sized output compression for high performance. The following git tree provides the file system user-space tools under development (ex, formatting tool mkfs.erofs): @@ -85,7 +85,7 @@ All data areas should be aligned with the block size, but metadata areas may not. All metadatas can be now observed in two different spaces (views): 1. Inode metadata space Each valid inode should be aligned with an inode slot, which is a fixed - value (32 bytes) and designed to be kept in line with v1 inode size. + value (32 bytes) and designed to be kept in line with compact inode size. Each inode can be directly found with the following formula: inode offset = meta_blkaddr * block_size + 32 * nid @@ -117,10 +117,10 @@ may not. All metadatas can be now observed in two different spaces (views): |-> aligned with 4B Inode could be 32 or 64 bytes, which can be distinguished from a common - field which all inode versions have -- i_advise: + field which all inode versions have -- i_format: __________________ __________________ - | i_advise | | i_advise | + | i_format | | i_format | |__________________| |__________________| | ... | | ... | | | | | @@ -129,12 +129,13 @@ may not. All metadatas can be now observed in two different spaces (views): |__________________| 64 bytes Xattrs, extents, data inline are followed by the corresponding inode with - proper alignes, and they could be optional for different data mappings, - _currently_ there are totally 3 valid data mappings supported: + proper alignment, and they could be optional for different data mappings. + _currently_ total 4 valid data mappings are supported: - 1) flat file data without data inline (no extent); - 2) fixed-output size data compression (must have extents); - 3) flat file data with tail-end data inline (no extent); + 0 flat file data without data inline (no extent); + 1 fixed-sized output data compression (with non-compacted indexes); + 2 flat file data with tail packing data inline (no extent); + 3 fixed-sized output data compression (with compacted indexes, v5.3+). The size of the optional xattrs is indicated by i_xattr_count in inode header. Large xattrs or xattrs shared by many different files can be @@ -182,8 +183,8 @@ introduce another on-disk field at all. Compression ----------- -Currently, EROFS supports 4KB fixed-output clustersize transparent file -compression, as illustrated below: +Currently, EROFS supports 4KB fixed-sized output transparent file compression, +as illustrated below: |---- Variant-Length Extent ----|-------- VLE --------|----- VLE ----- clusterofs clusterofs clusterofs -- GitLab From 341826a065660d1b77d89e6335b6095cd654271c Mon Sep 17 00:00:00 2001 From: Beniamin Bia Date: Mon, 4 Nov 2019 18:26:34 +0200 Subject: [PATCH 0214/1437] iio: adc: ad7606: fix reading unnecessary data from device MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When a conversion result is being read from ADC, the driver reads the number of channels + 1 because it thinks that IIO_CHAN_SOFT_TIMESTAMP is also a physical channel. This patch fixes this issue. Fixes: 2985a5d88455 ("staging: iio: adc: ad7606: Move out of staging") Reported-by: Robert Wörle Signed-off-by: Beniamin Bia Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad7606.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/adc/ad7606.c b/drivers/iio/adc/ad7606.c index f5ba94c03a8d9..e4683a68522a4 100644 --- a/drivers/iio/adc/ad7606.c +++ b/drivers/iio/adc/ad7606.c @@ -85,7 +85,7 @@ err_unlock: static int ad7606_read_samples(struct ad7606_state *st) { - unsigned int num = st->chip_info->num_channels; + unsigned int num = st->chip_info->num_channels - 1; u16 *data = st->data; int ret; -- GitLab From 0163c1c521ff8b09cd8ca395003cc00178161d77 Mon Sep 17 00:00:00 2001 From: YueHaibing Date: Mon, 11 Nov 2019 11:21:15 +0800 Subject: [PATCH 0215/1437] iio: st_accel: Fix unused variable warning drivers/iio/accel/st_accel_core.c:1005:44: warning: mount_matrix_ext_info defined but not used [-Wunused-const-variable=] Using stub helper while CONFIG_ACPI is disabled to fix it. Suggested-by: Ladislav Michl Signed-off-by: YueHaibing Signed-off-by: Jonathan Cameron --- drivers/iio/accel/st_accel_core.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/iio/accel/st_accel_core.c b/drivers/iio/accel/st_accel_core.c index 7b837641f166e..7320275c7e56d 100644 --- a/drivers/iio/accel/st_accel_core.c +++ b/drivers/iio/accel/st_accel_core.c @@ -992,6 +992,7 @@ static const struct iio_trigger_ops st_accel_trigger_ops = { #define ST_ACCEL_TRIGGER_OPS NULL #endif +#ifdef CONFIG_ACPI static const struct iio_mount_matrix * get_mount_matrix(const struct iio_dev *indio_dev, const struct iio_chan_spec *chan) @@ -1012,7 +1013,6 @@ static const struct iio_chan_spec_ext_info mount_matrix_ext_info[] = { static int apply_acpi_orientation(struct iio_dev *indio_dev, struct iio_chan_spec *channels) { -#ifdef CONFIG_ACPI struct st_sensor_data *adata = iio_priv(indio_dev); struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL}; struct acpi_device *adev; @@ -1140,10 +1140,14 @@ static int apply_acpi_orientation(struct iio_dev *indio_dev, out: kfree(buffer.pointer); return ret; +} #else /* !CONFIG_ACPI */ +static int apply_acpi_orientation(struct iio_dev *indio_dev, + struct iio_chan_spec *channels) +{ return 0; -#endif } +#endif /* * st_accel_get_settings() - get sensor settings from device name -- GitLab From 2e19b6c3efcf074528533c7b98a42bcb1234ebb9 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Tue, 5 Nov 2019 20:28:18 +0000 Subject: [PATCH 0216/1437] iio: temperature: ltc2983: fix u32 read into a unsigned long long MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently the read of temp using of_property_read_u32_index is reading a u32 value into a unsigned long long. This relies on machine endianness to work correctly, so fix this by reading a u32 value and setting temp to this value. Addresses-Coverity: ("Reliance on integer endianness") Fixes: f110f3188e56 ("iio: temperature: Add support for LTC2983") Signed-off-by: Colin Ian King Acked-by: Nuno Sá Signed-off-by: Jonathan Cameron --- drivers/iio/temperature/ltc2983.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/iio/temperature/ltc2983.c b/drivers/iio/temperature/ltc2983.c index ddf47023364b0..d39c0d6b77f1c 100644 --- a/drivers/iio/temperature/ltc2983.c +++ b/drivers/iio/temperature/ltc2983.c @@ -444,8 +444,10 @@ static struct ltc2983_custom_sensor *__ltc2983_custom_sensor_new( else temp = __convert_to_raw(temp, resolution); } else { - of_property_read_u32_index(np, propname, index, - (u32 *)&temp); + u32 t32; + + of_property_read_u32_index(np, propname, index, &t32); + temp = t32; } for (j = 0; j < n_size; j++) -- GitLab From dd587a7f0af5bdb2567e68da0093bbe654ecc950 Mon Sep 17 00:00:00 2001 From: Marcelo Schmitt Date: Sat, 16 Nov 2019 17:50:29 -0300 Subject: [PATCH 0217/1437] dt-bindings: iio: adc: ad7292: Update SPDX identifier Update SPDX identifier to the preferred dual GPL-2.0 OR BSD-2-Clause licensing. Signed-off-by: Marcelo Schmitt Signed-off-by: Jonathan Cameron --- Documentation/devicetree/bindings/iio/adc/adi,ad7292.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/iio/adc/adi,ad7292.yaml b/Documentation/devicetree/bindings/iio/adc/adi,ad7292.yaml index b68be3aaf587c..5770f63dd5117 100644 --- a/Documentation/devicetree/bindings/iio/adc/adi,ad7292.yaml +++ b/Documentation/devicetree/bindings/iio/adc/adi,ad7292.yaml @@ -1,4 +1,4 @@ -# SPDX-License-Identifier: GPL-2.0-only +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) %YAML 1.2 --- $id: http://devicetree.org/schemas/iio/adc/adi,ad7292.yaml# -- GitLab From ada971cbdc37bd1f0f9b6d7373c24b67e14c5c13 Mon Sep 17 00:00:00 2001 From: Marcelo Schmitt Date: Sat, 16 Nov 2019 17:51:13 -0300 Subject: [PATCH 0218/1437] dt-bindings: iio: adc: ad7292: fix constraint over channel quantity Change items property of AD7292 channels to correctly constrain their quantity. Signed-off-by: Marcelo Schmitt Signed-off-by: Jonathan Cameron --- Documentation/devicetree/bindings/iio/adc/adi,ad7292.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/iio/adc/adi,ad7292.yaml b/Documentation/devicetree/bindings/iio/adc/adi,ad7292.yaml index 5770f63dd5117..e1f6d64bdccd1 100644 --- a/Documentation/devicetree/bindings/iio/adc/adi,ad7292.yaml +++ b/Documentation/devicetree/bindings/iio/adc/adi,ad7292.yaml @@ -53,7 +53,8 @@ patternProperties: description: | The channel number. It can have up to 8 channels numbered from 0 to 7. items: - maximum: 7 + - minimum: 0 + maximum: 7 diff-channels: description: see Documentation/devicetree/bindings/iio/adc/adc.txt -- GitLab From 11d7c8d3b1259c303fb52789febed58f0bc35ad1 Mon Sep 17 00:00:00 2001 From: Mircea Caprioru Date: Mon, 18 Nov 2019 10:38:57 +0200 Subject: [PATCH 0219/1437] iio: adc: ad7124: Enable internal reference When the internal reference was selected by a channel it was not enabled. This patch fixes that and enables it. Fixes: b3af341bbd96 ("iio: adc: Add ad7124 support") Signed-off-by: Mircea Caprioru Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad7124.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/iio/adc/ad7124.c b/drivers/iio/adc/ad7124.c index edc6f1cc90b24..3f03abf100b59 100644 --- a/drivers/iio/adc/ad7124.c +++ b/drivers/iio/adc/ad7124.c @@ -39,6 +39,8 @@ #define AD7124_STATUS_POR_FLAG_MSK BIT(4) /* AD7124_ADC_CONTROL */ +#define AD7124_ADC_CTRL_REF_EN_MSK BIT(8) +#define AD7124_ADC_CTRL_REF_EN(x) FIELD_PREP(AD7124_ADC_CTRL_REF_EN_MSK, x) #define AD7124_ADC_CTRL_PWR_MSK GENMASK(7, 6) #define AD7124_ADC_CTRL_PWR(x) FIELD_PREP(AD7124_ADC_CTRL_PWR_MSK, x) #define AD7124_ADC_CTRL_MODE_MSK GENMASK(5, 2) @@ -424,7 +426,10 @@ static int ad7124_init_channel_vref(struct ad7124_state *st, break; case AD7124_INT_REF: st->channel_config[channel_number].vref_mv = 2500; - break; + st->adc_control &= ~AD7124_ADC_CTRL_REF_EN_MSK; + st->adc_control |= AD7124_ADC_CTRL_REF_EN(1); + return ad_sd_write_reg(&st->sd, AD7124_ADC_CONTROL, + 2, st->adc_control); default: dev_err(&st->sd.spi->dev, "Invalid reference %d\n", refsel); return -EINVAL; -- GitLab From 1f310f77efa25660fcbf58629a96f8ee6aac85ba Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Tue, 19 Nov 2019 09:21:24 +0300 Subject: [PATCH 0220/1437] iio: adc: intel_mrfld_adc: Allocating too much data in probe() This probe function is passing the wrong size to devm_iio_device_alloc(). It is supposed to be the size of the private data. Fortunately, sizeof(*indio_dev) is larger than sizeof(struct mrfld_adc) so it doesn't cause a runtime problem. Fixes: a7118662734a ("iio: adc: intel_mrfld_adc: Add Basin Cove ADC driver") Signed-off-by: Dan Carpenter Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/adc/intel_mrfld_adc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/adc/intel_mrfld_adc.c b/drivers/iio/adc/intel_mrfld_adc.c index 67d096f8180de..c35a1beb817c2 100644 --- a/drivers/iio/adc/intel_mrfld_adc.c +++ b/drivers/iio/adc/intel_mrfld_adc.c @@ -185,7 +185,7 @@ static int mrfld_adc_probe(struct platform_device *pdev) int irq; int ret; - indio_dev = devm_iio_device_alloc(dev, sizeof(*indio_dev)); + indio_dev = devm_iio_device_alloc(dev, sizeof(struct mrfld_adc)); if (!indio_dev) return -ENOMEM; -- GitLab From c41d79b74b3b106310b43d68fd5a304735c3369f Mon Sep 17 00:00:00 2001 From: Chuhong Yuan Date: Mon, 18 Nov 2019 19:40:18 +0800 Subject: [PATCH 0221/1437] iio: adc: max1027: fix not unregistered iio trigger The driver forgets to unregister the iio trigger in probe failure and remove. Use devm API to fix it. Signed-off-by: Chuhong Yuan Signed-off-by: Jonathan Cameron --- drivers/iio/adc/max1027.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/iio/adc/max1027.c b/drivers/iio/adc/max1027.c index e171db20c04a7..02834ca3e1ced 100644 --- a/drivers/iio/adc/max1027.c +++ b/drivers/iio/adc/max1027.c @@ -478,7 +478,13 @@ static int max1027_probe(struct spi_device *spi) st->trig->ops = &max1027_trigger_ops; st->trig->dev.parent = &spi->dev; iio_trigger_set_drvdata(st->trig, indio_dev); - iio_trigger_register(st->trig); + ret = devm_iio_trigger_register(&indio_dev->dev, + st->trig); + if (ret < 0) { + dev_err(&indio_dev->dev, + "Failed to register iio trigger\n"); + return ret; + } ret = devm_request_threaded_irq(&spi->dev, spi->irq, iio_trigger_generic_data_rdy_poll, -- GitLab From 342a6928bd5017edbdae376042d8ad6af3d3b943 Mon Sep 17 00:00:00 2001 From: Chris Lesiak Date: Thu, 21 Nov 2019 20:39:42 +0000 Subject: [PATCH 0222/1437] iio: humidity: hdc100x: fix IIO_HUMIDITYRELATIVE channel reporting The IIO_HUMIDITYRELATIVE channel was being incorrectly reported back as percent when it should have been milli percent. This is via an incorrect scale value being returned to userspace. Signed-off-by: Chris Lesiak Acked-by: Matt Ranostay Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/humidity/hdc100x.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/humidity/hdc100x.c b/drivers/iio/humidity/hdc100x.c index 963ff043eecf9..7ecd2ffa31325 100644 --- a/drivers/iio/humidity/hdc100x.c +++ b/drivers/iio/humidity/hdc100x.c @@ -229,7 +229,7 @@ static int hdc100x_read_raw(struct iio_dev *indio_dev, *val2 = 65536; return IIO_VAL_FRACTIONAL; } else { - *val = 100; + *val = 100000; *val2 = 65536; return IIO_VAL_FRACTIONAL; } -- GitLab From 53eaa9c27fdc01b4f4d885223e29f97393409e7e Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Maneyrol Date: Tue, 26 Nov 2019 17:19:12 +0100 Subject: [PATCH 0223/1437] iio: imu: inv_mpu6050: fix temperature reporting using bad unit Temperature should be reported in milli-degrees, not degrees. Fix scale and offset values to use the correct unit. This is a fix for an issue that has been present for a long time. The fixes tag reflects the point at which the code last changed in a fashion that would make this fix patch no longer apply. Backports will be necessary to fix those elements that predate that patch. Fixes: 1615fe41a195 ("iio: imu: mpu6050: Fix FIFO layout for ICM20602") Cc: stable@vger.kernel.org Signed-off-by: Jean-Baptiste Maneyrol Signed-off-by: Jonathan Cameron --- drivers/iio/imu/inv_mpu6050/inv_mpu_core.c | 23 +++++++++++----------- drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h | 16 +++++++++++---- 2 files changed, 24 insertions(+), 15 deletions(-) diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c index 45e77b308238b..0686e41bb8a1c 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c @@ -117,6 +117,7 @@ static const struct inv_mpu6050_hw hw_info[] = { .reg = ®_set_6050, .config = &chip_config_6050, .fifo_size = 1024, + .temp = {INV_MPU6050_TEMP_OFFSET, INV_MPU6050_TEMP_SCALE}, }, { .whoami = INV_MPU6500_WHOAMI_VALUE, @@ -124,6 +125,7 @@ static const struct inv_mpu6050_hw hw_info[] = { .reg = ®_set_6500, .config = &chip_config_6050, .fifo_size = 512, + .temp = {INV_MPU6500_TEMP_OFFSET, INV_MPU6500_TEMP_SCALE}, }, { .whoami = INV_MPU6515_WHOAMI_VALUE, @@ -131,6 +133,7 @@ static const struct inv_mpu6050_hw hw_info[] = { .reg = ®_set_6500, .config = &chip_config_6050, .fifo_size = 512, + .temp = {INV_MPU6500_TEMP_OFFSET, INV_MPU6500_TEMP_SCALE}, }, { .whoami = INV_MPU6000_WHOAMI_VALUE, @@ -138,6 +141,7 @@ static const struct inv_mpu6050_hw hw_info[] = { .reg = ®_set_6050, .config = &chip_config_6050, .fifo_size = 1024, + .temp = {INV_MPU6050_TEMP_OFFSET, INV_MPU6050_TEMP_SCALE}, }, { .whoami = INV_MPU9150_WHOAMI_VALUE, @@ -145,6 +149,7 @@ static const struct inv_mpu6050_hw hw_info[] = { .reg = ®_set_6050, .config = &chip_config_6050, .fifo_size = 1024, + .temp = {INV_MPU6050_TEMP_OFFSET, INV_MPU6050_TEMP_SCALE}, }, { .whoami = INV_MPU9250_WHOAMI_VALUE, @@ -152,6 +157,7 @@ static const struct inv_mpu6050_hw hw_info[] = { .reg = ®_set_6500, .config = &chip_config_6050, .fifo_size = 512, + .temp = {INV_MPU6500_TEMP_OFFSET, INV_MPU6500_TEMP_SCALE}, }, { .whoami = INV_MPU9255_WHOAMI_VALUE, @@ -159,6 +165,7 @@ static const struct inv_mpu6050_hw hw_info[] = { .reg = ®_set_6500, .config = &chip_config_6050, .fifo_size = 512, + .temp = {INV_MPU6500_TEMP_OFFSET, INV_MPU6500_TEMP_SCALE}, }, { .whoami = INV_ICM20608_WHOAMI_VALUE, @@ -166,6 +173,7 @@ static const struct inv_mpu6050_hw hw_info[] = { .reg = ®_set_6500, .config = &chip_config_6050, .fifo_size = 512, + .temp = {INV_ICM20608_TEMP_OFFSET, INV_ICM20608_TEMP_SCALE}, }, { .whoami = INV_ICM20602_WHOAMI_VALUE, @@ -173,6 +181,7 @@ static const struct inv_mpu6050_hw hw_info[] = { .reg = ®_set_icm20602, .config = &chip_config_6050, .fifo_size = 1008, + .temp = {INV_ICM20608_TEMP_OFFSET, INV_ICM20608_TEMP_SCALE}, }, }; @@ -481,12 +490,8 @@ inv_mpu6050_read_raw(struct iio_dev *indio_dev, return IIO_VAL_INT_PLUS_MICRO; case IIO_TEMP: - *val = 0; - if (st->chip_type == INV_ICM20602) - *val2 = INV_ICM20602_TEMP_SCALE; - else - *val2 = INV_MPU6050_TEMP_SCALE; - + *val = st->hw->temp.scale / 1000000; + *val2 = st->hw->temp.scale % 1000000; return IIO_VAL_INT_PLUS_MICRO; case IIO_MAGN: return inv_mpu_magn_get_scale(st, chan, val, val2); @@ -496,11 +501,7 @@ inv_mpu6050_read_raw(struct iio_dev *indio_dev, case IIO_CHAN_INFO_OFFSET: switch (chan->type) { case IIO_TEMP: - if (st->chip_type == INV_ICM20602) - *val = INV_ICM20602_TEMP_OFFSET; - else - *val = INV_MPU6050_TEMP_OFFSET; - + *val = st->hw->temp.offset; return IIO_VAL_INT; default: return -EINVAL; diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h b/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h index f1fb7b6bdab1c..b096e010d4ee3 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h @@ -107,6 +107,7 @@ struct inv_mpu6050_chip_config { * @reg: register map of the chip. * @config: configuration of the chip. * @fifo_size: size of the FIFO in bytes. + * @temp: offset and scale to apply to raw temperature. */ struct inv_mpu6050_hw { u8 whoami; @@ -114,6 +115,10 @@ struct inv_mpu6050_hw { const struct inv_mpu6050_reg_map *reg; const struct inv_mpu6050_chip_config *config; size_t fifo_size; + struct { + int offset; + int scale; + } temp; }; /* @@ -279,16 +284,19 @@ struct inv_mpu6050_state { #define INV_MPU6050_REG_UP_TIME_MIN 5000 #define INV_MPU6050_REG_UP_TIME_MAX 10000 -#define INV_MPU6050_TEMP_OFFSET 12421 -#define INV_MPU6050_TEMP_SCALE 2941 +#define INV_MPU6050_TEMP_OFFSET 12420 +#define INV_MPU6050_TEMP_SCALE 2941176 #define INV_MPU6050_MAX_GYRO_FS_PARAM 3 #define INV_MPU6050_MAX_ACCL_FS_PARAM 3 #define INV_MPU6050_THREE_AXIS 3 #define INV_MPU6050_GYRO_CONFIG_FSR_SHIFT 3 #define INV_MPU6050_ACCL_CONFIG_FSR_SHIFT 3 -#define INV_ICM20602_TEMP_OFFSET 8170 -#define INV_ICM20602_TEMP_SCALE 3060 +#define INV_MPU6500_TEMP_OFFSET 7011 +#define INV_MPU6500_TEMP_SCALE 2995178 + +#define INV_ICM20608_TEMP_OFFSET 8170 +#define INV_ICM20608_TEMP_SCALE 3059976 /* 6 + 6 + 7 (for MPU9x50) = 19 round up to 24 and plus 8 */ #define INV_MPU6050_OUTPUT_DATA_SIZE 32 -- GitLab From 5685b145c138b2ca696076106d0fb87b7b90b14a Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Thu, 28 Nov 2019 17:55:18 +0200 Subject: [PATCH 0224/1437] iio: imu: st_lsm6dsx: fix decimation factor estimation Fix decimation factor and sip estimation for LSM6DSM series (max value for decimation factor is 32). If gyro and accel sensors are enabled at 12.5Hz and 416Hz respectively, decimation factor lookup will fail, producing unaligned data. Remove unused decimator filed in st_lsm6dsx_sensor structure. Fixes: f8710f0357bc ("iio: imu: st_lsm6dsx: express odr in mHZ") Signed-off-by: Lorenzo Bianconi Signed-off-by: Jonathan Cameron --- drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h | 2 -- .../iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c | 25 +++++++++++++------ 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h index c605b153be410..510c4bd4a7854 100644 --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h @@ -320,7 +320,6 @@ enum st_lsm6dsx_fifo_mode { * @odr: Output data rate of the sensor [Hz]. * @watermark: Sensor watermark level. * @sip: Number of samples in a given pattern. - * @decimator: FIFO decimation factor. * @ts_ref: Sensor timestamp reference for hw one. * @ext_info: Sensor settings if it is connected to i2c controller */ @@ -334,7 +333,6 @@ struct st_lsm6dsx_sensor { u16 watermark; u8 sip; - u8 decimator; s64 ts_ref; struct { diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c index d416990ae309d..a70b04f141872 100644 --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c @@ -78,14 +78,20 @@ struct st_lsm6dsx_decimator_entry st_lsm6dsx_decimator_table[] = { { 32, 0x7 }, }; -static int st_lsm6dsx_get_decimator_val(u8 val) +static int +st_lsm6dsx_get_decimator_val(struct st_lsm6dsx_sensor *sensor, u32 max_odr) { const int max_size = ARRAY_SIZE(st_lsm6dsx_decimator_table); + u32 decimator = max_odr / sensor->odr; int i; - for (i = 0; i < max_size; i++) - if (st_lsm6dsx_decimator_table[i].decimator == val) + if (decimator > 1) + decimator = round_down(decimator, 2); + + for (i = 0; i < max_size; i++) { + if (st_lsm6dsx_decimator_table[i].decimator == decimator) break; + } return i == max_size ? 0 : st_lsm6dsx_decimator_table[i].val; } @@ -111,6 +117,13 @@ static void st_lsm6dsx_get_max_min_odr(struct st_lsm6dsx_hw *hw, } } +static u8 st_lsm6dsx_get_sip(struct st_lsm6dsx_sensor *sensor, u32 min_odr) +{ + u8 sip = sensor->odr / min_odr; + + return sip > 1 ? round_down(sip, 2) : sip; +} + static int st_lsm6dsx_update_decimators(struct st_lsm6dsx_hw *hw) { const struct st_lsm6dsx_reg *ts_dec_reg; @@ -131,12 +144,10 @@ static int st_lsm6dsx_update_decimators(struct st_lsm6dsx_hw *hw) sensor = iio_priv(hw->iio_devs[i]); /* update fifo decimators and sample in pattern */ if (hw->enable_mask & BIT(sensor->id)) { - sensor->sip = sensor->odr / min_odr; - sensor->decimator = max_odr / sensor->odr; - data = st_lsm6dsx_get_decimator_val(sensor->decimator); + sensor->sip = st_lsm6dsx_get_sip(sensor, min_odr); + data = st_lsm6dsx_get_decimator_val(sensor, max_odr); } else { sensor->sip = 0; - sensor->decimator = 0; data = 0; } ts_sip = max_t(u16, ts_sip, sensor->sip); -- GitLab From c2686eb2aed322c642b5a291d5b9d67fe4301d4a Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Thu, 28 Nov 2019 17:42:30 +0200 Subject: [PATCH 0225/1437] iio: imu: st_lsm6dsx: track hw FIFO buffering with fifo_mask Track hw FIFO state introducing fifo_mask since now the accel sensor can be enabled during suspend/resume in order to trigger the wake-up enabling the FIFO in st_lsm6dsx_resume even if it was disabled before the suspend. Hence we must separately track the fifo state. Fixes: 4c997dfa692d ("iio: imu: st_lsm6dsx: add wakeup-source option") Signed-off-by: Lorenzo Bianconi Tested-by: Sean Nyekjaer Signed-off-by: Jonathan Cameron --- drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h | 4 +-- .../iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c | 25 +++++++++++-------- drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c | 4 +-- 3 files changed, 18 insertions(+), 15 deletions(-) diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h index 510c4bd4a7854..dc55d7dff3eb9 100644 --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h @@ -349,9 +349,9 @@ struct st_lsm6dsx_sensor { * @fifo_lock: Mutex to prevent concurrent access to the hw FIFO. * @conf_lock: Mutex to prevent concurrent FIFO configuration update. * @page_lock: Mutex to prevent concurrent memory page configuration. - * @fifo_mode: FIFO operating mode supported by the device. * @suspend_mask: Suspended sensor bitmask. * @enable_mask: Enabled sensor bitmask. + * @fifo_mask: Enabled hw FIFO bitmask. * @ts_gain: Hw timestamp rate after internal calibration. * @ts_sip: Total number of timestamp samples in a given pattern. * @sip: Total number of samples (acc/gyro/ts) in a given pattern. @@ -371,9 +371,9 @@ struct st_lsm6dsx_hw { struct mutex conf_lock; struct mutex page_lock; - enum st_lsm6dsx_fifo_mode fifo_mode; u8 suspend_mask; u8 enable_mask; + u8 fifo_mask; s64 ts_gain; u8 ts_sip; u8 sip; diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c index a70b04f141872..cb536b81a1c29 100644 --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c @@ -187,17 +187,10 @@ int st_lsm6dsx_set_fifo_mode(struct st_lsm6dsx_hw *hw, enum st_lsm6dsx_fifo_mode fifo_mode) { unsigned int data; - int err; data = FIELD_PREP(ST_LSM6DSX_FIFO_MODE_MASK, fifo_mode); - err = st_lsm6dsx_update_bits_locked(hw, ST_LSM6DSX_REG_FIFO_MODE_ADDR, - ST_LSM6DSX_FIFO_MODE_MASK, data); - if (err < 0) - return err; - - hw->fifo_mode = fifo_mode; - - return 0; + return st_lsm6dsx_update_bits_locked(hw, ST_LSM6DSX_REG_FIFO_MODE_ADDR, + ST_LSM6DSX_FIFO_MODE_MASK, data); } static int st_lsm6dsx_set_fifo_odr(struct st_lsm6dsx_sensor *sensor, @@ -619,11 +612,17 @@ int st_lsm6dsx_flush_fifo(struct st_lsm6dsx_hw *hw) int st_lsm6dsx_update_fifo(struct st_lsm6dsx_sensor *sensor, bool enable) { struct st_lsm6dsx_hw *hw = sensor->hw; + u8 fifo_mask; int err; mutex_lock(&hw->conf_lock); - if (hw->fifo_mode != ST_LSM6DSX_FIFO_BYPASS) { + if (enable) + fifo_mask = hw->fifo_mask | BIT(sensor->id); + else + fifo_mask = hw->fifo_mask & ~BIT(sensor->id); + + if (hw->fifo_mask) { err = st_lsm6dsx_flush_fifo(hw); if (err < 0) goto out; @@ -653,15 +652,19 @@ int st_lsm6dsx_update_fifo(struct st_lsm6dsx_sensor *sensor, bool enable) if (err < 0) goto out; - if (hw->enable_mask) { + if (fifo_mask) { /* reset hw ts counter */ err = st_lsm6dsx_reset_hw_ts(hw); if (err < 0) goto out; err = st_lsm6dsx_set_fifo_mode(hw, ST_LSM6DSX_FIFO_CONT); + if (err < 0) + goto out; } + hw->fifo_mask = fifo_mask; + out: mutex_unlock(&hw->conf_lock); diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c index 11b2c7bc8041a..6f628c3cd133a 100644 --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c @@ -2300,7 +2300,7 @@ static int __maybe_unused st_lsm6dsx_suspend(struct device *dev) hw->suspend_mask |= BIT(sensor->id); } - if (hw->fifo_mode != ST_LSM6DSX_FIFO_BYPASS) + if (hw->fifo_mask) err = st_lsm6dsx_flush_fifo(hw); return err; @@ -2336,7 +2336,7 @@ static int __maybe_unused st_lsm6dsx_resume(struct device *dev) hw->suspend_mask &= ~BIT(sensor->id); } - if (hw->enable_mask) + if (hw->fifo_mask) err = st_lsm6dsx_set_fifo_mode(hw, ST_LSM6DSX_FIFO_CONT); return err; -- GitLab From bd41c445b7b96e46efb799ff33bdf870479488cf Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Fri, 29 Nov 2019 11:37:24 +0200 Subject: [PATCH 0226/1437] iio: imu: st_lsm6dsx: do not power-off accel if events are enabled Do not power-off accel unconditionally if wake-up events are enabled powering off the hw FIFO. At the same time do not power-off the accel sensor if it is 'batched' in the hw FIFO disabling sensor events Fixes: b5969abfa8b8 ("iio: imu: st_lsm6dsx: add motion events") Tested-by: Sean Nyekjaer Signed-off-by: Lorenzo Bianconi Signed-off-by: Jonathan Cameron --- drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c | 30 +++++++++++++++++--- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c index 6f628c3cd133a..a7d40c02ce6b5 100644 --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c @@ -1447,8 +1447,9 @@ st_lsm6dsx_set_odr(struct st_lsm6dsx_sensor *sensor, u32 req_odr) return st_lsm6dsx_update_bits_locked(hw, reg->addr, reg->mask, data); } -int st_lsm6dsx_sensor_set_enable(struct st_lsm6dsx_sensor *sensor, - bool enable) +static int +__st_lsm6dsx_sensor_set_enable(struct st_lsm6dsx_sensor *sensor, + bool enable) { struct st_lsm6dsx_hw *hw = sensor->hw; u32 odr = enable ? sensor->odr : 0; @@ -1466,6 +1467,26 @@ int st_lsm6dsx_sensor_set_enable(struct st_lsm6dsx_sensor *sensor, return 0; } +static int +st_lsm6dsx_check_events(struct st_lsm6dsx_sensor *sensor, bool enable) +{ + struct st_lsm6dsx_hw *hw = sensor->hw; + + if (sensor->id == ST_LSM6DSX_ID_GYRO || enable) + return 0; + + return hw->enable_event; +} + +int st_lsm6dsx_sensor_set_enable(struct st_lsm6dsx_sensor *sensor, + bool enable) +{ + if (st_lsm6dsx_check_events(sensor, enable)) + return 0; + + return __st_lsm6dsx_sensor_set_enable(sensor, enable); +} + static int st_lsm6dsx_read_oneshot(struct st_lsm6dsx_sensor *sensor, u8 addr, int *val) { @@ -1661,7 +1682,7 @@ st_lsm6dsx_write_event_config(struct iio_dev *iio_dev, struct st_lsm6dsx_sensor *sensor = iio_priv(iio_dev); struct st_lsm6dsx_hw *hw = sensor->hw; u8 enable_event; - int err = 0; + int err; if (type != IIO_EV_TYPE_THRESH) return -EINVAL; @@ -1689,7 +1710,8 @@ st_lsm6dsx_write_event_config(struct iio_dev *iio_dev, return err; mutex_lock(&hw->conf_lock); - err = st_lsm6dsx_sensor_set_enable(sensor, state); + if (enable_event || !(hw->fifo_mask & BIT(sensor->id))) + err = __st_lsm6dsx_sensor_set_enable(sensor, state); mutex_unlock(&hw->conf_lock); if (err < 0) return err; -- GitLab From 3b71f6b59508b1c9befcb43de434866aafc76520 Mon Sep 17 00:00:00 2001 From: Andrea Merello Date: Mon, 2 Dec 2019 15:13:36 +0100 Subject: [PATCH 0227/1437] iio: ad7949: fix channels mixups Each time we need to read a sample (from the sysfs interface, since the driver supports only it) the driver writes the configuration register with the proper settings needed to perform the said read, then it runs another xfer to actually read the resulting value. Most notably the configuration register is updated to set the ADC internal MUX depending by which channel the read targets. Unfortunately this seems not enough to ensure correct operation because the ADC works in a pipelined-like fashion and the new configuration isn't applied in time. The ADC alternates two phases: acquisition and conversion. During the acquisition phase the ADC samples the analog signal in an internal capacitor; in the conversion phase the ADC performs the actual analog to digital conversion of the stored voltage. Note that of course the MUX needs to be set to the proper channel when the acquisition phase is performed. Once the conversion phase has been completed, the device automatically switches back to a new acquisition; on the other hand the device switches from acquisition to conversion on the rising edge of SPI cs signal (that is when the xfer finishes). Only after both two phases have been completed (with the proper settings already written in the configuration register since the beginning) it is possible to read the outcome from SPI bus. With the current driver implementation, we end up in the following situation: _______ 1st xfer ____________ 2nd xfer ___________________ SPI cs.. \_________/ \_________/ SPI rd.. idle |(val N-2)+ idle | val N-1 + idle ... SPI wr.. idle | cfg N + idle | (X) + idle ... ------------------------ + -------------------- + ------------------ AD .. acq N-1 + cnv N-1 | acq N + cnv N | acq N+1 As shown in the diagram above, the value we read in the Nth read belongs to configuration setting N-1. In case the configuration is not changed (config[N] == config[N-1]), then we still get correct data, but in case the configuration changes (i.e. switching the MUX on another channel), we get wrong data (data from the previously selected channel). This patch fixes this by performing one more "dummy" transfer in order to ending up in reading the data when it's really ready, as per the following timing diagram. _______ 1st xfer ____________ 2nd xfer ___________ 3rd xfer ___ SPI cs.. \_________/ \_________/ \_________/ SPI rd.. idle |(val N-2)+ idle |(val N-1)+ idle | val N + .. SPI wr.. idle | cfg N + idle | (X) + idle | (X) + .. ------------------------ + -------------------- + ------------------- + -- AD .. acq N-1 + cnv N-1 | acq N + cnv N | acq N+1 | .. NOTE: in the latter case (cfg changes), the acquisition phase for the value to be read begins after the 1st xfer, that is after the read request has been issued on sysfs. On the other hand, if the cfg doesn't change, then we can refer to the fist diagram assuming N == (N - 1); the acquisition phase _begins_ before the 1st xfer (potentially a lot of time before the read has been issued via sysfs, but it _ends_ after the 1st xfer, that is _after_ the read has started. This should guarantee a reasonably fresh data, which value represents the voltage that the sampled signal has after the read start or maybe just around it. Signed-off-by: Andrea Merello Reviewed-by: Charles-Antoine Couret Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad7949.c | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/drivers/iio/adc/ad7949.c b/drivers/iio/adc/ad7949.c index 5c2b3446fa4af..2c6f60edb7ced 100644 --- a/drivers/iio/adc/ad7949.c +++ b/drivers/iio/adc/ad7949.c @@ -89,6 +89,7 @@ static int ad7949_spi_read_channel(struct ad7949_adc_chip *ad7949_adc, int *val, unsigned int channel) { int ret; + int i; int bits_per_word = ad7949_adc->resolution; int mask = GENMASK(ad7949_adc->resolution, 0); struct spi_message msg; @@ -100,12 +101,23 @@ static int ad7949_spi_read_channel(struct ad7949_adc_chip *ad7949_adc, int *val, }, }; - ret = ad7949_spi_write_cfg(ad7949_adc, - channel << AD7949_OFFSET_CHANNEL_SEL, - AD7949_MASK_CHANNEL_SEL); - if (ret) - return ret; + /* + * 1: write CFG for sample N and read old data (sample N-2) + * 2: if CFG was not changed since sample N-1 then we'll get good data + * at the next xfer, so we bail out now, otherwise we write something + * and we read garbage (sample N-1 configuration). + */ + for (i = 0; i < 2; i++) { + ret = ad7949_spi_write_cfg(ad7949_adc, + channel << AD7949_OFFSET_CHANNEL_SEL, + AD7949_MASK_CHANNEL_SEL); + if (ret) + return ret; + if (channel == ad7949_adc->current_channel) + break; + } + /* 3: write something and read actual data */ ad7949_adc->buffer = 0; spi_message_init_with_transfers(&msg, tx, 1); ret = spi_sync(ad7949_adc->spi, &msg); -- GitLab From 9fd229c478fbf77c41c8528aa757ef14210365f6 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Mon, 2 Dec 2019 09:55:46 +0100 Subject: [PATCH 0228/1437] iio: adc: max9611: Fix too short conversion time delay MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As of commit b9ddd5091160793e ("iio: adc: max9611: Fix temperature reading in probe"), max9611 initialization sometimes fails on the Salvator-X(S) development board with: max9611 4-007f: Invalid value received from ADC 0x8000: aborting max9611: probe of 4-007f failed with error -5 The max9611 driver tests communications with the chip by reading the die temperature during the probe function, which returns an invalid value. According to the datasheet, the typical ADC conversion time is 2 ms, but no minimum or maximum values are provided. Maxim Technical Support confirmed this was tested with temperature Ta=25 degreeC, and promised to inform me if a maximum/minimum value is available (they didn't get back to me, so I assume it is not). However, the driver assumes a 1 ms conversion time. Usually the usleep_range() call returns after more than 1.8 ms, hence it succeeds. When it returns earlier, the data register may be read too early, and the previous measurement value will be returned. After boot, this is the temperature POR (power-on reset) value, causing the failure above. Fix this by increasing the delay from 1000-2000 µs to 3000-3300 µs. Note that this issue has always been present, but it was exposed by the aformentioned commit. Fixes: 69780a3bbc0b1e7e ("iio: adc: Add Maxim max9611 ADC driver") Signed-off-by: Geert Uytterhoeven Reviewed-by: Jacopo Mondi Reviewed-by: Wolfram Sang Signed-off-by: Jonathan Cameron --- drivers/iio/adc/max9611.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/drivers/iio/adc/max9611.c b/drivers/iio/adc/max9611.c index da073d72f649f..e480529b3f049 100644 --- a/drivers/iio/adc/max9611.c +++ b/drivers/iio/adc/max9611.c @@ -89,6 +89,12 @@ #define MAX9611_TEMP_SCALE_NUM 1000000 #define MAX9611_TEMP_SCALE_DIV 2083 +/* + * Conversion time is 2 ms (typically) at Ta=25 degreeC + * No maximum value is known, so play it safe. + */ +#define MAX9611_CONV_TIME_US_RANGE 3000, 3300 + struct max9611_dev { struct device *dev; struct i2c_client *i2c_client; @@ -236,11 +242,9 @@ static int max9611_read_single(struct max9611_dev *max9611, return ret; } - /* - * need a delay here to make register configuration - * stabilize. 1 msec at least, from empirical testing. - */ - usleep_range(1000, 2000); + /* need a delay here to make register configuration stabilize. */ + + usleep_range(MAX9611_CONV_TIME_US_RANGE); ret = i2c_smbus_read_word_swapped(max9611->i2c_client, reg_addr); if (ret < 0) { @@ -507,7 +511,7 @@ static int max9611_init(struct max9611_dev *max9611) MAX9611_REG_CTRL2, 0); return ret; } - usleep_range(1000, 2000); + usleep_range(MAX9611_CONV_TIME_US_RANGE); return 0; } -- GitLab From 0aeb1f2b74f3402e9cdb7c0b8e2c369c9767301e Mon Sep 17 00:00:00 2001 From: Stefan Roese Date: Tue, 5 Nov 2019 14:04:56 +0100 Subject: [PATCH 0229/1437] ARM: dts: imx6ul: imx6ul-14x14-evk.dtsi: Fix SPI NOR probing Without this "jedec,spi-nor" compatible property, probing of the SPI NOR does not work on the NXP i.MX6ULL EVK. Fix this by adding this compatible property to the DT. Fixes: 7d77b8505aa9 ("ARM: dts: imx6ull: fix the imx6ull-14x14-evk configuration") Signed-off-by: Stefan Roese Reviewed-by: Fabio Estevam Reviewed-by: Frieder Schrempf Signed-off-by: Shawn Guo --- arch/arm/boot/dts/imx6ul-14x14-evk.dtsi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/boot/dts/imx6ul-14x14-evk.dtsi b/arch/arm/boot/dts/imx6ul-14x14-evk.dtsi index 1506eb12b21e0..e5dafb49ef128 100644 --- a/arch/arm/boot/dts/imx6ul-14x14-evk.dtsi +++ b/arch/arm/boot/dts/imx6ul-14x14-evk.dtsi @@ -227,7 +227,7 @@ flash0: n25q256a@0 { #address-cells = <1>; #size-cells = <1>; - compatible = "micron,n25q256a"; + compatible = "micron,n25q256a", "jedec,spi-nor"; spi-max-frequency = <29000000>; spi-rx-bus-width = <4>; spi-tx-bus-width = <4>; -- GitLab From 073a01e8d7c23b3efb59a3d4c20aa546f9ec29a9 Mon Sep 17 00:00:00 2001 From: Peng Fan Date: Fri, 1 Nov 2019 10:16:19 +0000 Subject: [PATCH 0230/1437] clk: imx: clk-composite-8m: add lock to gate/mux There is a lock to divider in the composite driver, but that's not enough. lock to gate/mux are also needed to provide exclusive access to the register. Fixes: d3ff9728134e ("clk: imx: Add imx composite clock") Signed-off-by: Peng Fan Cc: Signed-off-by: Shawn Guo --- drivers/clk/imx/clk-composite-8m.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/clk/imx/clk-composite-8m.c b/drivers/clk/imx/clk-composite-8m.c index 388bdb94f841d..d3486ee79ab54 100644 --- a/drivers/clk/imx/clk-composite-8m.c +++ b/drivers/clk/imx/clk-composite-8m.c @@ -142,6 +142,7 @@ struct clk *imx8m_clk_composite_flags(const char *name, mux->reg = reg; mux->shift = PCG_PCS_SHIFT; mux->mask = PCG_PCS_MASK; + mux->lock = &imx_ccm_lock; div = kzalloc(sizeof(*div), GFP_KERNEL); if (!div) @@ -161,6 +162,7 @@ struct clk *imx8m_clk_composite_flags(const char *name, gate_hw = &gate->hw; gate->reg = reg; gate->bit_idx = PCG_CGC_SHIFT; + gate->lock = &imx_ccm_lock; hw = clk_hw_register_composite(NULL, name, parent_names, num_parents, mux_hw, &clk_mux_ops, div_hw, -- GitLab From b731fadff4899c97be9ff33f3f2bd379b7a6be75 Mon Sep 17 00:00:00 2001 From: Andreas Kemnade Date: Mon, 11 Nov 2019 21:29:59 +0100 Subject: [PATCH 0231/1437] ARM: dts: e60k02: fix power button The power button was only producing irqs, but no key events, Forced power down with long key press works, so probably only a short spike arrives at the SoC. Further investigation shows that LDORTC2 is off after boot of the vendor kernel. LDORTC2 is shared with a GPIO at the pmic which probably transfers the button press to the SoC. That regulator off at boot, so "regulator-boot-on" is definitively wrong. So remove that. Reported-by: H. Nikolaus Schaller Signed-off-by: Andreas Kemnade Fixes: c100ea86e6ab ("ARM: dts: add Netronix E60K02 board common file") Signed-off-by: Shawn Guo --- arch/arm/boot/dts/e60k02.dtsi | 5 ----- 1 file changed, 5 deletions(-) diff --git a/arch/arm/boot/dts/e60k02.dtsi b/arch/arm/boot/dts/e60k02.dtsi index 6472b056a001f..5a2c5320437dd 100644 --- a/arch/arm/boot/dts/e60k02.dtsi +++ b/arch/arm/boot/dts/e60k02.dtsi @@ -265,11 +265,6 @@ regulator-name = "LDORTC1"; regulator-boot-on; }; - - ldortc2_reg: LDORTC2 { - regulator-name = "LDORTC2"; - regulator-boot-on; - }; }; }; }; -- GitLab From cf6b58ab2d55f5a143c88c219c8e66ff0720fa69 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Wed, 4 Dec 2019 11:51:48 +0900 Subject: [PATCH 0232/1437] kbuild: fix 'No such file or directory' warning when cleaning MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since commit fcbb8461fd23 ("kbuild: remove header compile test"), 'make clean' with O= option in the pristine source tree emits 'No such file or directory' warning. $ git clean -d -f -x $ make O=foo clean make[1]: Entering directory '/home/masahiro/linux/foo' find: ‘usr/include’: No such file or directory make[1]: Leaving directory '/home/masahiro/linux/foo' Fixes: fcbb8461fd23 ("kbuild: remove header compile test") Reported-by: kbuild test robot Signed-off-by: Masahiro Yamada --- usr/include/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/usr/include/Makefile b/usr/include/Makefile index 4a753a48767b7..84598469e6ff3 100644 --- a/usr/include/Makefile +++ b/usr/include/Makefile @@ -91,7 +91,7 @@ endif # asm-generic/*.h is used by asm/*.h, and should not be included directly header-test- += asm-generic/% -extra-y := $(patsubst $(obj)/%.h,%.hdrtest, $(shell find $(obj) -name '*.h')) +extra-y := $(patsubst $(obj)/%.h,%.hdrtest, $(shell find $(obj) -name '*.h' 2>/dev/null)) quiet_cmd_hdrtest = HDRTEST $< cmd_hdrtest = \ -- GitLab From ed11e31709d7ddb19d4dc451d5bbfb15129f4cad Mon Sep 17 00:00:00 2001 From: Peng Fan Date: Fri, 22 Nov 2019 06:11:42 +0000 Subject: [PATCH 0233/1437] clk: imx: clk-imx7ulp: Add missing sentinel of ulp_div_table There should be a sentinel of ulp_div_table, otherwise _get_table_div may access data out of the array. Fixes: b1260067ac3d ("clk: imx: add imx7ulp clk driver") Signed-off-by: Peng Fan Cc: Signed-off-by: Shawn Guo --- drivers/clk/imx/clk-imx7ulp.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/clk/imx/clk-imx7ulp.c b/drivers/clk/imx/clk-imx7ulp.c index 3fdf3d494f0af..281191b55b3af 100644 --- a/drivers/clk/imx/clk-imx7ulp.c +++ b/drivers/clk/imx/clk-imx7ulp.c @@ -40,6 +40,7 @@ static const struct clk_div_table ulp_div_table[] = { { .val = 5, .div = 16, }, { .val = 6, .div = 32, }, { .val = 7, .div = 64, }, + { /* sentinel */ }, }; static const int pcc2_uart_clk_ids[] __initconst = { -- GitLab From e876e710ede23f670494331e062d643928e4142a Mon Sep 17 00:00:00 2001 From: Leonard Crestez Date: Thu, 31 Oct 2019 23:34:18 +0200 Subject: [PATCH 0234/1437] PM / devfreq: Fix devfreq_notifier_call returning errno Notifier callbacks shouldn't return negative errno but one of the NOTIFY_OK/DONE/BAD values. The OPP core will ignore return values from notifiers but returning a value that matches NOTIFY_STOP_MASK will stop the notification chain. Fix by always returning NOTIFY_OK. Signed-off-by: Leonard Crestez Reviewed-by: Matthias Kaehlcke Reviewed-by: Chanwoo Choi Signed-off-by: Chanwoo Choi --- drivers/devfreq/devfreq.c | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c index 425149e8bab07..4de70bd0f63d5 100644 --- a/drivers/devfreq/devfreq.c +++ b/drivers/devfreq/devfreq.c @@ -568,26 +568,28 @@ static int devfreq_notifier_call(struct notifier_block *nb, unsigned long type, void *devp) { struct devfreq *devfreq = container_of(nb, struct devfreq, nb); - int ret; + int err = -EINVAL; mutex_lock(&devfreq->lock); devfreq->scaling_min_freq = find_available_min_freq(devfreq); - if (!devfreq->scaling_min_freq) { - mutex_unlock(&devfreq->lock); - return -EINVAL; - } + if (!devfreq->scaling_min_freq) + goto out; devfreq->scaling_max_freq = find_available_max_freq(devfreq); - if (!devfreq->scaling_max_freq) { - mutex_unlock(&devfreq->lock); - return -EINVAL; - } + if (!devfreq->scaling_max_freq) + goto out; + + err = update_devfreq(devfreq); - ret = update_devfreq(devfreq); +out: mutex_unlock(&devfreq->lock); + if (err) + dev_err(devfreq->dev.parent, + "failed to update frequency from OPP notifier (%d)\n", + err); - return ret; + return NOTIFY_OK; } /** -- GitLab From e7cc792d00049c874010b398a27c3cc7bc8fef34 Mon Sep 17 00:00:00 2001 From: Leonard Crestez Date: Thu, 31 Oct 2019 23:34:19 +0200 Subject: [PATCH 0235/1437] PM / devfreq: Set scaling_max_freq to max on OPP notifier error The devfreq_notifier_call functions will update scaling_min_freq and scaling_max_freq when the OPP table is updated. If fetching the maximum frequency fails then scaling_max_freq remains set to zero which is confusing. Set to ULONG_MAX instead so we don't need special handling for this case in other places. Signed-off-by: Leonard Crestez Reviewed-by: Matthias Kaehlcke Reviewed-by: Chanwoo Choi Signed-off-by: Chanwoo Choi --- drivers/devfreq/devfreq.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c index 4de70bd0f63d5..66f0a795442ba 100644 --- a/drivers/devfreq/devfreq.c +++ b/drivers/devfreq/devfreq.c @@ -577,8 +577,10 @@ static int devfreq_notifier_call(struct notifier_block *nb, unsigned long type, goto out; devfreq->scaling_max_freq = find_available_max_freq(devfreq); - if (!devfreq->scaling_max_freq) + if (!devfreq->scaling_max_freq) { + devfreq->scaling_max_freq = ULONG_MAX; goto out; + } err = update_devfreq(devfreq); -- GitLab From 46cecc0bf095bbffdf58618a1b4a5f9c9f073bf8 Mon Sep 17 00:00:00 2001 From: Leonard Crestez Date: Thu, 31 Oct 2019 23:34:26 +0200 Subject: [PATCH 0236/1437] PM / devfreq: Introduce get_freq_range helper Moving handling of min/max freq to a single function and call it from update_devfreq and for printing min/max freq values in sysfs. This changes the behavior of out-of-range min_freq/max_freq: clamping is now done at evaluation time. This means that if an out-of-range constraint is imposed by sysfs and it later becomes valid then it will be enforced. Signed-off-by: Leonard Crestez Reviewed-by: Matthias Kaehlcke Reviewed-by: Chanwoo Choi Signed-off-by: Chanwoo Choi --- drivers/devfreq/devfreq.c | 108 +++++++++++++++++++++----------------- 1 file changed, 60 insertions(+), 48 deletions(-) diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c index 66f0a795442ba..65a4b6cf3fa59 100644 --- a/drivers/devfreq/devfreq.c +++ b/drivers/devfreq/devfreq.c @@ -98,6 +98,47 @@ static unsigned long find_available_max_freq(struct devfreq *devfreq) return max_freq; } +/** + * get_freq_range() - Get the current freq range + * @devfreq: the devfreq instance + * @min_freq: the min frequency + * @max_freq: the max frequency + * + * This takes into consideration all constraints. + */ +static void get_freq_range(struct devfreq *devfreq, + unsigned long *min_freq, + unsigned long *max_freq) +{ + unsigned long *freq_table = devfreq->profile->freq_table; + + lockdep_assert_held(&devfreq->lock); + + /* + * Initialize minimum/maximum frequency from freq table. + * The devfreq drivers can initialize this in either ascending or + * descending order and devfreq core supports both. + */ + if (freq_table[0] < freq_table[devfreq->profile->max_state - 1]) { + *min_freq = freq_table[0]; + *max_freq = freq_table[devfreq->profile->max_state - 1]; + } else { + *min_freq = freq_table[devfreq->profile->max_state - 1]; + *max_freq = freq_table[0]; + } + + /* Apply constraints from sysfs */ + *min_freq = max(*min_freq, devfreq->min_freq); + *max_freq = min(*max_freq, devfreq->max_freq); + + /* Apply constraints from OPP interface */ + *min_freq = max(*min_freq, devfreq->scaling_min_freq); + *max_freq = min(*max_freq, devfreq->scaling_max_freq); + + if (*min_freq > *max_freq) + *min_freq = *max_freq; +} + /** * devfreq_get_freq_level() - Lookup freq_table for the frequency * @devfreq: the devfreq instance @@ -351,16 +392,7 @@ int update_devfreq(struct devfreq *devfreq) err = devfreq->governor->get_target_freq(devfreq, &freq); if (err) return err; - - /* - * Adjust the frequency with user freq, QoS and available freq. - * - * List from the highest priority - * max_freq - * min_freq - */ - max_freq = min(devfreq->scaling_max_freq, devfreq->max_freq); - min_freq = max(devfreq->scaling_min_freq, devfreq->min_freq); + get_freq_range(devfreq, &min_freq, &max_freq); if (freq < min_freq) { freq = min_freq; @@ -1312,36 +1344,24 @@ static ssize_t min_freq_store(struct device *dev, struct device_attribute *attr, return -EINVAL; mutex_lock(&df->lock); - - if (value) { - if (value > df->max_freq) { - ret = -EINVAL; - goto unlock; - } - } else { - unsigned long *freq_table = df->profile->freq_table; - - /* Get minimum frequency according to sorting order */ - if (freq_table[0] < freq_table[df->profile->max_state - 1]) - value = freq_table[0]; - else - value = freq_table[df->profile->max_state - 1]; - } - df->min_freq = value; update_devfreq(df); - ret = count; -unlock: mutex_unlock(&df->lock); - return ret; + + return count; } static ssize_t min_freq_show(struct device *dev, struct device_attribute *attr, char *buf) { struct devfreq *df = to_devfreq(dev); + unsigned long min_freq, max_freq; + + mutex_lock(&df->lock); + get_freq_range(df, &min_freq, &max_freq); + mutex_unlock(&df->lock); - return sprintf(buf, "%lu\n", max(df->scaling_min_freq, df->min_freq)); + return sprintf(buf, "%lu\n", min_freq); } static ssize_t max_freq_store(struct device *dev, struct device_attribute *attr, @@ -1357,27 +1377,14 @@ static ssize_t max_freq_store(struct device *dev, struct device_attribute *attr, mutex_lock(&df->lock); - if (value) { - if (value < df->min_freq) { - ret = -EINVAL; - goto unlock; - } - } else { - unsigned long *freq_table = df->profile->freq_table; - - /* Get maximum frequency according to sorting order */ - if (freq_table[0] < freq_table[df->profile->max_state - 1]) - value = freq_table[df->profile->max_state - 1]; - else - value = freq_table[0]; - } + if (!value) + value = ULONG_MAX; df->max_freq = value; update_devfreq(df); - ret = count; -unlock: mutex_unlock(&df->lock); - return ret; + + return count; } static DEVICE_ATTR_RW(min_freq); @@ -1385,8 +1392,13 @@ static ssize_t max_freq_show(struct device *dev, struct device_attribute *attr, char *buf) { struct devfreq *df = to_devfreq(dev); + unsigned long min_freq, max_freq; + + mutex_lock(&df->lock); + get_freq_range(df, &min_freq, &max_freq); + mutex_unlock(&df->lock); - return sprintf(buf, "%lu\n", min(df->scaling_max_freq, df->max_freq)); + return sprintf(buf, "%lu\n", max_freq); } static DEVICE_ATTR_RW(max_freq); -- GitLab From 42a6b25e67df6ee6675e8d1eaf18065bd73328ba Mon Sep 17 00:00:00 2001 From: Leonard Crestez Date: Thu, 14 Nov 2019 01:21:31 +0200 Subject: [PATCH 0237/1437] PM / devfreq: Don't fail devfreq_dev_release if not in list Right now devfreq_dev_release will print a warning and abort the rest of the cleanup if the devfreq instance is not part of the global devfreq_list. But this is a valid scenario, for example it can happen if the governor can't be found or on any other init error that happens after device_register. Initialize devfreq->node to an empty list head in devfreq_add_device so that list_del becomes a safe noop inside devfreq_dev_release and we can continue the rest of the cleanup. Signed-off-by: Leonard Crestez Reviewed-by: Matthias Kaehlcke Reviewed-by: Chanwoo Choi Signed-off-by: Chanwoo Choi --- drivers/devfreq/devfreq.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c index 65a4b6cf3fa59..59f9fa8b54d10 100644 --- a/drivers/devfreq/devfreq.c +++ b/drivers/devfreq/devfreq.c @@ -637,11 +637,6 @@ static void devfreq_dev_release(struct device *dev) struct devfreq *devfreq = to_devfreq(dev); mutex_lock(&devfreq_list_lock); - if (IS_ERR(find_device_devfreq(devfreq->dev.parent))) { - mutex_unlock(&devfreq_list_lock); - dev_warn(&devfreq->dev, "releasing devfreq which doesn't exist\n"); - return; - } list_del(&devfreq->node); mutex_unlock(&devfreq_list_lock); @@ -696,6 +691,7 @@ struct devfreq *devfreq_add_device(struct device *dev, devfreq->dev.parent = dev; devfreq->dev.class = devfreq_class; devfreq->dev.release = devfreq_dev_release; + INIT_LIST_HEAD(&devfreq->node); devfreq->profile = profile; strncpy(devfreq->governor_name, governor_name, DEVFREQ_NAME_LEN); devfreq->previous_freq = profile->initial_freq; -- GitLab From 05d7ae15cfb18f9ce55eef85bb6bcd62d31acc57 Mon Sep 17 00:00:00 2001 From: Leonard Crestez Date: Thu, 5 Dec 2019 12:05:06 +0200 Subject: [PATCH 0238/1437] PM / devfreq: Add PM QoS support Register notifiers with the PM QoS framework in order to respond to requests for DEV_PM_QOS_MIN_FREQUENCY and DEV_PM_QOS_MAX_FREQUENCY. No notifiers are added by this patch but PM QoS constraints can be imposed externally (for example from other devices). Signed-off-by: Leonard Crestez Acked-by: Chanwoo Choi Reviewed-by: Matthias Kaehlcke Tested-by: Matthias Kaehlcke Signed-off-by: Chanwoo Choi --- drivers/devfreq/devfreq.c | 77 +++++++++++++++++++++++++++++++++++++++ include/linux/devfreq.h | 5 +++ 2 files changed, 82 insertions(+) diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c index 59f9fa8b54d10..f6e2720854482 100644 --- a/drivers/devfreq/devfreq.c +++ b/drivers/devfreq/devfreq.c @@ -24,11 +24,14 @@ #include #include #include +#include #include "governor.h" #define CREATE_TRACE_POINTS #include +#define HZ_PER_KHZ 1000 + static struct class *devfreq_class; /* @@ -111,6 +114,7 @@ static void get_freq_range(struct devfreq *devfreq, unsigned long *max_freq) { unsigned long *freq_table = devfreq->profile->freq_table; + s32 qos_min_freq, qos_max_freq; lockdep_assert_held(&devfreq->lock); @@ -127,6 +131,16 @@ static void get_freq_range(struct devfreq *devfreq, *max_freq = freq_table[0]; } + /* Apply constraints from PM QoS */ + qos_min_freq = dev_pm_qos_read_value(devfreq->dev.parent, + DEV_PM_QOS_MIN_FREQUENCY); + qos_max_freq = dev_pm_qos_read_value(devfreq->dev.parent, + DEV_PM_QOS_MAX_FREQUENCY); + *min_freq = max(*min_freq, (unsigned long)HZ_PER_KHZ * qos_min_freq); + if (qos_max_freq != PM_QOS_MAX_FREQUENCY_DEFAULT_VALUE) + *max_freq = min(*max_freq, + (unsigned long)HZ_PER_KHZ * qos_max_freq); + /* Apply constraints from sysfs */ *min_freq = max(*min_freq, devfreq->min_freq); *max_freq = min(*max_freq, devfreq->max_freq); @@ -626,6 +640,45 @@ out: return NOTIFY_OK; } +/** + * qos_notifier_call() - Common handler for QoS constraints. + * @devfreq: the devfreq instance. + */ +static int qos_notifier_call(struct devfreq *devfreq) +{ + int err; + + mutex_lock(&devfreq->lock); + err = update_devfreq(devfreq); + mutex_unlock(&devfreq->lock); + if (err) + dev_err(devfreq->dev.parent, + "failed to update frequency from PM QoS (%d)\n", + err); + + return NOTIFY_OK; +} + +/** + * qos_min_notifier_call() - Callback for QoS min_freq changes. + * @nb: Should be devfreq->nb_min + */ +static int qos_min_notifier_call(struct notifier_block *nb, + unsigned long val, void *ptr) +{ + return qos_notifier_call(container_of(nb, struct devfreq, nb_min)); +} + +/** + * qos_max_notifier_call() - Callback for QoS max_freq changes. + * @nb: Should be devfreq->nb_max + */ +static int qos_max_notifier_call(struct notifier_block *nb, + unsigned long val, void *ptr) +{ + return qos_notifier_call(container_of(nb, struct devfreq, nb_max)); +} + /** * devfreq_dev_release() - Callback for struct device to release the device. * @dev: the devfreq device @@ -635,11 +688,23 @@ out: static void devfreq_dev_release(struct device *dev) { struct devfreq *devfreq = to_devfreq(dev); + int err; mutex_lock(&devfreq_list_lock); list_del(&devfreq->node); mutex_unlock(&devfreq_list_lock); + err = dev_pm_qos_remove_notifier(devfreq->dev.parent, &devfreq->nb_max, + DEV_PM_QOS_MAX_FREQUENCY); + if (err && err != -ENOENT) + dev_warn(dev->parent, + "Failed to remove max_freq notifier: %d\n", err); + err = dev_pm_qos_remove_notifier(devfreq->dev.parent, &devfreq->nb_min, + DEV_PM_QOS_MIN_FREQUENCY); + if (err && err != -ENOENT) + dev_warn(dev->parent, + "Failed to remove min_freq notifier: %d\n", err); + if (devfreq->profile->exit) devfreq->profile->exit(devfreq->dev.parent); @@ -762,6 +827,18 @@ struct devfreq *devfreq_add_device(struct device *dev, mutex_unlock(&devfreq->lock); + devfreq->nb_min.notifier_call = qos_min_notifier_call; + err = dev_pm_qos_add_notifier(devfreq->dev.parent, &devfreq->nb_min, + DEV_PM_QOS_MIN_FREQUENCY); + if (err) + goto err_devfreq; + + devfreq->nb_max.notifier_call = qos_max_notifier_call; + err = dev_pm_qos_add_notifier(devfreq->dev.parent, &devfreq->nb_max, + DEV_PM_QOS_MAX_FREQUENCY); + if (err) + goto err_devfreq; + mutex_lock(&devfreq_list_lock); governor = try_then_request_governor(devfreq->governor_name); diff --git a/include/linux/devfreq.h b/include/linux/devfreq.h index 2bae9ed3c7831..8b92ccbd1962d 100644 --- a/include/linux/devfreq.h +++ b/include/linux/devfreq.h @@ -136,6 +136,8 @@ struct devfreq_dev_profile { * @time_in_state: Statistics of devfreq states * @last_stat_updated: The last time stat updated * @transition_notifier_list: list head of DEVFREQ_TRANSITION_NOTIFIER notifier + * @nb_min: Notifier block for DEV_PM_QOS_MIN_FREQUENCY + * @nb_max: Notifier block for DEV_PM_QOS_MAX_FREQUENCY * * This structure stores the devfreq information for a give device. * @@ -178,6 +180,9 @@ struct devfreq { unsigned long last_stat_updated; struct srcu_notifier_head transition_notifier_list; + + struct notifier_block nb_min; + struct notifier_block nb_max; }; struct devfreq_freqs { -- GitLab From 27dbc542f651ed09de910f274b32634904103774 Mon Sep 17 00:00:00 2001 From: Leonard Crestez Date: Thu, 5 Dec 2019 12:05:07 +0200 Subject: [PATCH 0239/1437] PM / devfreq: Use PM QoS for sysfs min/max_freq Switch the handling of min_freq and max_freq from sysfs to use the dev_pm_qos_request interface. Since PM QoS handles frequencies as kHz this change reduces the precision of min_freq and max_freq. This shouldn't introduce problems because frequencies which are not an integer number of kHz are likely not an integer number of Hz either. Try to ensure compatibility by rounding min values down and rounding max values up. Signed-off-by: Leonard Crestez Acked-by: Chanwoo Choi Reviewed-by: Matthias Kaehlcke Tested-by: Matthias Kaehlcke [cw00.choi: Return -EAGAIN instead of -EINVAL if dev_pm_qos is inactive] Signed-off-by: Chanwoo Choi --- drivers/devfreq/devfreq.c | 76 ++++++++++++++++++++++++++++++--------- include/linux/devfreq.h | 9 ++--- 2 files changed, 64 insertions(+), 21 deletions(-) diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c index f6e2720854482..57f6944d65a6d 100644 --- a/drivers/devfreq/devfreq.c +++ b/drivers/devfreq/devfreq.c @@ -141,10 +141,6 @@ static void get_freq_range(struct devfreq *devfreq, *max_freq = min(*max_freq, (unsigned long)HZ_PER_KHZ * qos_max_freq); - /* Apply constraints from sysfs */ - *min_freq = max(*min_freq, devfreq->min_freq); - *max_freq = min(*max_freq, devfreq->max_freq); - /* Apply constraints from OPP interface */ *min_freq = max(*min_freq, devfreq->scaling_min_freq); *max_freq = min(*max_freq, devfreq->scaling_max_freq); @@ -705,6 +701,19 @@ static void devfreq_dev_release(struct device *dev) dev_warn(dev->parent, "Failed to remove min_freq notifier: %d\n", err); + if (dev_pm_qos_request_active(&devfreq->user_max_freq_req)) { + err = dev_pm_qos_remove_request(&devfreq->user_max_freq_req); + if (err) + dev_warn(dev->parent, + "Failed to remove max_freq request: %d\n", err); + } + if (dev_pm_qos_request_active(&devfreq->user_min_freq_req)) { + err = dev_pm_qos_remove_request(&devfreq->user_min_freq_req); + if (err) + dev_warn(dev->parent, + "Failed to remove min_freq request: %d\n", err); + } + if (devfreq->profile->exit) devfreq->profile->exit(devfreq->dev.parent); @@ -778,7 +787,6 @@ struct devfreq *devfreq_add_device(struct device *dev, err = -EINVAL; goto err_dev; } - devfreq->min_freq = devfreq->scaling_min_freq; devfreq->scaling_max_freq = find_available_max_freq(devfreq); if (!devfreq->scaling_max_freq) { @@ -786,7 +794,6 @@ struct devfreq *devfreq_add_device(struct device *dev, err = -EINVAL; goto err_dev; } - devfreq->max_freq = devfreq->scaling_max_freq; devfreq->suspend_freq = dev_pm_opp_get_suspend_opp_freq(dev); atomic_set(&devfreq->suspend_count, 0); @@ -827,6 +834,16 @@ struct devfreq *devfreq_add_device(struct device *dev, mutex_unlock(&devfreq->lock); + err = dev_pm_qos_add_request(dev, &devfreq->user_min_freq_req, + DEV_PM_QOS_MIN_FREQUENCY, 0); + if (err < 0) + goto err_devfreq; + err = dev_pm_qos_add_request(dev, &devfreq->user_max_freq_req, + DEV_PM_QOS_MAX_FREQUENCY, + PM_QOS_MAX_FREQUENCY_DEFAULT_VALUE); + if (err < 0) + goto err_devfreq; + devfreq->nb_min.notifier_call = qos_min_notifier_call; err = dev_pm_qos_add_notifier(devfreq->dev.parent, &devfreq->nb_min, DEV_PM_QOS_MIN_FREQUENCY); @@ -1412,14 +1429,22 @@ static ssize_t min_freq_store(struct device *dev, struct device_attribute *attr, unsigned long value; int ret; + /* + * Protect against theoretical sysfs writes between + * device_add and dev_pm_qos_add_request + */ + if (!dev_pm_qos_request_active(&df->user_min_freq_req)) + return -EAGAIN; + ret = sscanf(buf, "%lu", &value); if (ret != 1) return -EINVAL; - mutex_lock(&df->lock); - df->min_freq = value; - update_devfreq(df); - mutex_unlock(&df->lock); + /* Round down to kHz for PM QoS */ + ret = dev_pm_qos_update_request(&df->user_min_freq_req, + value / HZ_PER_KHZ); + if (ret < 0) + return ret; return count; } @@ -1444,18 +1469,35 @@ static ssize_t max_freq_store(struct device *dev, struct device_attribute *attr, unsigned long value; int ret; + /* + * Protect against theoretical sysfs writes between + * device_add and dev_pm_qos_add_request + */ + if (!dev_pm_qos_request_active(&df->user_max_freq_req)) + return -EINVAL; + ret = sscanf(buf, "%lu", &value); if (ret != 1) return -EINVAL; - mutex_lock(&df->lock); - - if (!value) - value = ULONG_MAX; + /* + * PM QoS frequencies are in kHz so we need to convert. Convert by + * rounding upwards so that the acceptable interval never shrinks. + * + * For example if the user writes "666666666" to sysfs this value will + * be converted to 666667 kHz and back to 666667000 Hz before an OPP + * lookup, this ensures that an OPP of 666666666Hz is still accepted. + * + * A value of zero means "no limit". + */ + if (value) + value = DIV_ROUND_UP(value, HZ_PER_KHZ); + else + value = PM_QOS_MAX_FREQUENCY_DEFAULT_VALUE; - df->max_freq = value; - update_devfreq(df); - mutex_unlock(&df->lock); + ret = dev_pm_qos_update_request(&df->user_max_freq_req, value); + if (ret < 0) + return ret; return count; } diff --git a/include/linux/devfreq.h b/include/linux/devfreq.h index 8b92ccbd1962d..fb376b5b72819 100644 --- a/include/linux/devfreq.h +++ b/include/linux/devfreq.h @@ -13,6 +13,7 @@ #include #include #include +#include #define DEVFREQ_NAME_LEN 16 @@ -123,8 +124,8 @@ struct devfreq_dev_profile { * @previous_freq: previously configured frequency value. * @data: Private data of the governor. The devfreq framework does not * touch this. - * @min_freq: Limit minimum frequency requested by user (0: none) - * @max_freq: Limit maximum frequency requested by user (0: none) + * @user_min_freq_req: PM QoS minimum frequency request from user (via sysfs) + * @user_max_freq_req: PM QoS maximum frequency request from user (via sysfs) * @scaling_min_freq: Limit minimum frequency requested by OPP interface * @scaling_max_freq: Limit maximum frequency requested by OPP interface * @stop_polling: devfreq polling status of a device. @@ -163,8 +164,8 @@ struct devfreq { void *data; /* private data for governors */ - unsigned long min_freq; - unsigned long max_freq; + struct dev_pm_qos_request user_min_freq_req; + struct dev_pm_qos_request user_max_freq_req; unsigned long scaling_min_freq; unsigned long scaling_max_freq; bool stop_polling; -- GitLab From 0e72a2f9c1a386aaa8337f9f524f89a7884b82cf Mon Sep 17 00:00:00 2001 From: Olof Johansson Date: Sat, 7 Dec 2019 13:29:16 -0800 Subject: [PATCH 0240/1437] riscv: Fix build dependency for loader The Makefile addition for the flat image loader missed an obj prefix. For most parallel builds this worked out fine, but with -j1 the dependency wasn't fulfilled and thus fails: arch/riscv/boot/loader.S: Assembler messages: arch/riscv/boot/loader.S:7: Error: file not found: arch/riscv/boot/Image Fixes: 405fe7aa0dba ("riscv: provide a flat image loader") Cc: Christoph Hellwig Signed-off-by: Olof Johansson Reviewed-by: Anup Patel Signed-off-by: Paul Walmsley --- arch/riscv/boot/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/riscv/boot/Makefile b/arch/riscv/boot/Makefile index a474f98ce4fae..36db8145f9f46 100644 --- a/arch/riscv/boot/Makefile +++ b/arch/riscv/boot/Makefile @@ -24,7 +24,7 @@ $(obj)/Image: vmlinux FORCE $(obj)/Image.gz: $(obj)/Image FORCE $(call if_changed,gzip) -loader.o: $(src)/loader.S $(obj)/Image +$(obj)/loader.o: $(src)/loader.S $(obj)/Image $(obj)/loader: $(obj)/loader.o $(obj)/Image $(obj)/loader.lds FORCE $(Q)$(LD) -T $(obj)/loader.lds -o $@ $(obj)/loader.o -- GitLab From bc3e8f5d42d5cfac3f7ac9b458c2eeb02e8b1cf7 Mon Sep 17 00:00:00 2001 From: Kefeng Wang Date: Thu, 24 Oct 2019 15:20:03 +0800 Subject: [PATCH 0241/1437] riscv: only select serial sifive if TTY is enabled There is some warning if TTY is not enabled, and lead to build error, only select serial sifive if TTY enabled, and this also makes randconfig happy. Signed-off-by: Kefeng Wang [paul.walmsley@sifive.com: updated to apply] Signed-off-by: Paul Walmsley --- arch/riscv/Kconfig.socs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/riscv/Kconfig.socs b/arch/riscv/Kconfig.socs index 634759ac8c717..d325b67d00dfc 100644 --- a/arch/riscv/Kconfig.socs +++ b/arch/riscv/Kconfig.socs @@ -2,8 +2,8 @@ menu "SoC selection" config SOC_SIFIVE bool "SiFive SoCs" - select SERIAL_SIFIVE - select SERIAL_SIFIVE_CONSOLE + select SERIAL_SIFIVE if TTY + select SERIAL_SIFIVE_CONSOLE if TTY select CLK_SIFIVE select CLK_SIFIVE_FU540_PRCI select SIFIVE_PLIC -- GitLab From 9ff3483e5c552311676369b31bc64f0e8208fcd2 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Mon, 9 Dec 2019 08:22:26 +0900 Subject: [PATCH 0242/1437] ALSA: firewire-motu: fix double unlocked 'motu->mutex' Mutex is doubly unlocked in some error path of pcm.open. This commit fixes ALSA firewire-motu driver in Linux kernel v5.5. Reported-by: kbuild test robot Reported-by: Dan Carpenter Fixes: 3fd80b200388 ("ALSA: firewire-motu: use the same size of period for PCM substream in AMDTP streams") Fixes: 0f5482e7875b ("ALSA: firewire-motu: share PCM buffer size for both direction") Signed-off-by: Takashi Sakamoto Link: https://lore.kernel.org/r/20191208232226.6685-1-o-takashi@sakamocchi.jp Signed-off-by: Takashi Iwai --- sound/firewire/motu/motu-pcm.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/sound/firewire/motu/motu-pcm.c b/sound/firewire/motu/motu-pcm.c index 349b4d09e84f6..0059709310303 100644 --- a/sound/firewire/motu/motu-pcm.c +++ b/sound/firewire/motu/motu-pcm.c @@ -177,18 +177,14 @@ static int pcm_open(struct snd_pcm_substream *substream) err = snd_pcm_hw_constraint_minmax(substream->runtime, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, frames_per_period, frames_per_period); - if (err < 0) { - mutex_unlock(&motu->mutex); + if (err < 0) goto err_locked; - } err = snd_pcm_hw_constraint_minmax(substream->runtime, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, frames_per_buffer, frames_per_buffer); - if (err < 0) { - mutex_unlock(&motu->mutex); + if (err < 0) goto err_locked; - } } } -- GitLab From 43cb86799ff03e9819c07f37f72f80f8246ad7ed Mon Sep 17 00:00:00 2001 From: Martin Blumenstingl Date: Sun, 8 Dec 2019 18:18:31 +0100 Subject: [PATCH 0243/1437] drm: meson: venc: cvbs: fix CVBS mode matching With commit 222ec1618c3ace ("drm: Add aspect ratio parsing in DRM layer") the drm core started honoring the picture_aspect_ratio field when comparing two drm_display_modes. Prior to that it was ignored. When the CVBS encoder driver was initially submitted there was no aspect ratio check. Switch from drm_mode_equal() to drm_mode_match() without DRM_MODE_MATCH_ASPECT_RATIO to fix "kmscube" and X.org output using the CVBS connector. When (for example) kmscube sets the output mode when using the CVBS connector it passes HDMI_PICTURE_ASPECT_NONE, making the drm_mode_equal() fail as it include the aspect ratio. Prior to this patch kmscube reported: failed to set mode: Invalid argument The CVBS mode checking in the sun4i (drivers/gpu/drm/sun4i/sun4i_tv.c sun4i_tv_mode_to_drm_mode) and ZTE (drivers/gpu/drm/zte/zx_tvenc.c tvenc_mode_{pal,ntsc}) drivers don't set the "picture_aspect_ratio" at all. The Meson VPU driver does not rely on the aspect ratio for the CVBS output so we can safely decouple it from the hdmi_picture_aspect setting. Cc: Fixes: 222ec1618c3ace ("drm: Add aspect ratio parsing in DRM layer") Fixes: bbbe775ec5b5da ("drm: Add support for Amlogic Meson Graphic Controller") Signed-off-by: Martin Blumenstingl Acked-by: Neil Armstrong [narmstrong: squashed with drm: meson: venc: cvbs: deduplicate the meson_cvbs_mode lookup code] Signed-off-by: Neil Armstrong Link: https://patchwork.freedesktop.org/patch/msgid/20191208171832.1064772-3-martin.blumenstingl@googlemail.com --- drivers/gpu/drm/meson/meson_venc_cvbs.c | 48 ++++++++++++++----------- 1 file changed, 27 insertions(+), 21 deletions(-) diff --git a/drivers/gpu/drm/meson/meson_venc_cvbs.c b/drivers/gpu/drm/meson/meson_venc_cvbs.c index 9ab27aecfcf31..1bd6b6d15ffb3 100644 --- a/drivers/gpu/drm/meson/meson_venc_cvbs.c +++ b/drivers/gpu/drm/meson/meson_venc_cvbs.c @@ -64,6 +64,25 @@ struct meson_cvbs_mode meson_cvbs_modes[MESON_CVBS_MODES_COUNT] = { }, }; +static const struct meson_cvbs_mode * +meson_cvbs_get_mode(const struct drm_display_mode *req_mode) +{ + int i; + + for (i = 0; i < MESON_CVBS_MODES_COUNT; ++i) { + struct meson_cvbs_mode *meson_mode = &meson_cvbs_modes[i]; + + if (drm_mode_match(req_mode, &meson_mode->mode, + DRM_MODE_MATCH_TIMINGS | + DRM_MODE_MATCH_CLOCK | + DRM_MODE_MATCH_FLAGS | + DRM_MODE_MATCH_3D_FLAGS)) + return meson_mode; + } + + return NULL; +} + /* Connector */ static void meson_cvbs_connector_destroy(struct drm_connector *connector) @@ -136,14 +155,8 @@ static int meson_venc_cvbs_encoder_atomic_check(struct drm_encoder *encoder, struct drm_crtc_state *crtc_state, struct drm_connector_state *conn_state) { - int i; - - for (i = 0; i < MESON_CVBS_MODES_COUNT; ++i) { - struct meson_cvbs_mode *meson_mode = &meson_cvbs_modes[i]; - - if (drm_mode_equal(&crtc_state->mode, &meson_mode->mode)) - return 0; - } + if (meson_cvbs_get_mode(&crtc_state->mode)) + return 0; return -EINVAL; } @@ -191,24 +204,17 @@ static void meson_venc_cvbs_encoder_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode) { + const struct meson_cvbs_mode *meson_mode = meson_cvbs_get_mode(mode); struct meson_venc_cvbs *meson_venc_cvbs = encoder_to_meson_venc_cvbs(encoder); struct meson_drm *priv = meson_venc_cvbs->priv; - int i; - for (i = 0; i < MESON_CVBS_MODES_COUNT; ++i) { - struct meson_cvbs_mode *meson_mode = &meson_cvbs_modes[i]; + if (meson_mode) { + meson_venci_cvbs_mode_set(priv, meson_mode->enci); - if (drm_mode_equal(mode, &meson_mode->mode)) { - meson_venci_cvbs_mode_set(priv, - meson_mode->enci); - - /* Setup 27MHz vclk2 for ENCI and VDAC */ - meson_vclk_setup(priv, MESON_VCLK_TARGET_CVBS, - MESON_VCLK_CVBS, MESON_VCLK_CVBS, - MESON_VCLK_CVBS, true); - break; - } + /* Setup 27MHz vclk2 for ENCI and VDAC */ + meson_vclk_setup(priv, MESON_VCLK_TARGET_CVBS, MESON_VCLK_CVBS, + MESON_VCLK_CVBS, MESON_VCLK_CVBS, true); } } -- GitLab From 36fcb4292473cb9c9ce7706d038bcf0eda5cabeb Mon Sep 17 00:00:00 2001 From: Marcelo Tosatti Date: Fri, 6 Dec 2019 13:07:41 -0200 Subject: [PATCH 0244/1437] cpuidle: use first valid target residency as poll time Commit 259231a04561 ("cpuidle: add poll_limit_ns to cpuidle_device structure") changed, by mistake, the target residency from the first available sleep state to the last available sleep state (which should be longer). This might cause excessive polling. Fixes: 259231a04561 ("cpuidle: add poll_limit_ns to cpuidle_device structure") Signed-off-by: Marcelo Tosatti Cc: 5.4+ # 5.4+ Signed-off-by: Rafael J. Wysocki --- drivers/cpuidle/cpuidle.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/cpuidle/cpuidle.c b/drivers/cpuidle/cpuidle.c index 0005be5ea2b45..15877b4311438 100644 --- a/drivers/cpuidle/cpuidle.c +++ b/drivers/cpuidle/cpuidle.c @@ -382,6 +382,7 @@ u64 cpuidle_poll_time(struct cpuidle_driver *drv, continue; limit_ns = (u64)drv->states[i].target_residency_ns; + break; } dev->poll_limit_ns = limit_ns; -- GitLab From 634f0348fe336fce8f6cab1933139115e983ed2f Mon Sep 17 00:00:00 2001 From: Max Filippov Date: Tue, 26 Nov 2019 11:30:27 -0800 Subject: [PATCH 0245/1437] gpio: xtensa: fix driver build Commit cad6fade6e78 ("xtensa: clean up WSR*/RSR*/get_sr/set_sr") removed {RSR,WSR}_CPENABLE from xtensa code, but did not fix up all users, breaking gpio-xtensa driver build. Update gpio-xtensa to use new xtensa_{get,set}_sr API. Cc: stable@vger.kernel.org # v5.0+ Fixes: cad6fade6e78 ("xtensa: clean up WSR*/RSR*/get_sr/set_sr") Signed-off-by: Max Filippov Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-xtensa.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/gpio/gpio-xtensa.c b/drivers/gpio/gpio-xtensa.c index 08d7c3b320386..c8af34a6368f4 100644 --- a/drivers/gpio/gpio-xtensa.c +++ b/drivers/gpio/gpio-xtensa.c @@ -44,15 +44,14 @@ static inline unsigned long enable_cp(unsigned long *cpenable) unsigned long flags; local_irq_save(flags); - RSR_CPENABLE(*cpenable); - WSR_CPENABLE(*cpenable | BIT(XCHAL_CP_ID_XTIOP)); - + *cpenable = xtensa_get_sr(cpenable); + xtensa_set_sr(*cpenable | BIT(XCHAL_CP_ID_XTIOP), cpenable); return flags; } static inline void disable_cp(unsigned long flags, unsigned long cpenable) { - WSR_CPENABLE(cpenable); + xtensa_set_sr(cpenable, cpenable); local_irq_restore(flags); } -- GitLab From 40ecab551232972a39cdd8b6f17ede54a3fdb296 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 19 Nov 2019 16:46:41 +0100 Subject: [PATCH 0246/1437] pinctrl: baytrail: Really serialize all register accesses Commit 39ce8150a079 ("pinctrl: baytrail: Serialize all register access") added a spinlock around all register accesses because: "There is a hardware issue in Intel Baytrail where concurrent GPIO register access might result reads of 0xffffffff and writes might get dropped completely." Testing has shown that this does not catch all cases, there are still 2 problems remaining 1) The original fix uses a spinlock per byt_gpio device / struct, additional testing has shown that this is not sufficient concurent accesses to 2 different GPIO banks also suffer from the same problem. This commit fixes this by moving to a single global lock. 2) The original fix did not add a lock around the register accesses in the suspend/resume handling. Since pinctrl-baytrail.c is using normal suspend/resume handlers, interrupts are still enabled during suspend/resume handling. Nothing should be using the GPIOs when they are being taken down, _but_ the GPIOs themselves may still cause interrupts, which are likely to use (read) the triggering GPIO. So we need to protect against concurrent GPIO register accesses in the suspend/resume handlers too. This commit fixes this by adding the missing spin_lock / unlock calls. The 2 fixes together fix the Acer Switch 10 SW5-012 getting completely confused after a suspend resume. The DSDT for this device has a bug in its _LID method which reprograms the home and power button trigger- flags requesting both high and low _level_ interrupts so the IRQs for these 2 GPIOs continuously fire. This combined with the saving of registers during suspend, triggers concurrent GPIO register accesses resulting in saving 0xffffffff as pconf0 value during suspend and then when restoring this on resume the pinmux settings get all messed up, resulting in various I2C busses being stuck, the wifi no longer working and often the tablet simply not coming out of suspend at all. Cc: stable@vger.kernel.org Fixes: 39ce8150a079 ("pinctrl: baytrail: Serialize all register access") Signed-off-by: Hans de Goede Acked-by: Mika Westerberg Signed-off-by: Andy Shevchenko --- drivers/pinctrl/intel/pinctrl-baytrail.c | 81 +++++++++++++----------- 1 file changed, 44 insertions(+), 37 deletions(-) diff --git a/drivers/pinctrl/intel/pinctrl-baytrail.c b/drivers/pinctrl/intel/pinctrl-baytrail.c index 9ffb22211d2b0..7d658e6627e7a 100644 --- a/drivers/pinctrl/intel/pinctrl-baytrail.c +++ b/drivers/pinctrl/intel/pinctrl-baytrail.c @@ -110,7 +110,6 @@ struct byt_gpio { struct platform_device *pdev; struct pinctrl_dev *pctl_dev; struct pinctrl_desc pctl_desc; - raw_spinlock_t lock; const struct intel_pinctrl_soc_data *soc_data; struct intel_community *communities_copy; struct byt_gpio_pin_context *saved_context; @@ -549,6 +548,8 @@ static const struct intel_pinctrl_soc_data *byt_soc_data[] = { NULL }; +static DEFINE_RAW_SPINLOCK(byt_lock); + static struct intel_community *byt_get_community(struct byt_gpio *vg, unsigned int pin) { @@ -658,7 +659,7 @@ static void byt_set_group_simple_mux(struct byt_gpio *vg, unsigned long flags; int i; - raw_spin_lock_irqsave(&vg->lock, flags); + raw_spin_lock_irqsave(&byt_lock, flags); for (i = 0; i < group.npins; i++) { void __iomem *padcfg0; @@ -678,7 +679,7 @@ static void byt_set_group_simple_mux(struct byt_gpio *vg, writel(value, padcfg0); } - raw_spin_unlock_irqrestore(&vg->lock, flags); + raw_spin_unlock_irqrestore(&byt_lock, flags); } static void byt_set_group_mixed_mux(struct byt_gpio *vg, @@ -688,7 +689,7 @@ static void byt_set_group_mixed_mux(struct byt_gpio *vg, unsigned long flags; int i; - raw_spin_lock_irqsave(&vg->lock, flags); + raw_spin_lock_irqsave(&byt_lock, flags); for (i = 0; i < group.npins; i++) { void __iomem *padcfg0; @@ -708,7 +709,7 @@ static void byt_set_group_mixed_mux(struct byt_gpio *vg, writel(value, padcfg0); } - raw_spin_unlock_irqrestore(&vg->lock, flags); + raw_spin_unlock_irqrestore(&byt_lock, flags); } static int byt_set_mux(struct pinctrl_dev *pctldev, unsigned int func_selector, @@ -749,11 +750,11 @@ static void byt_gpio_clear_triggering(struct byt_gpio *vg, unsigned int offset) unsigned long flags; u32 value; - raw_spin_lock_irqsave(&vg->lock, flags); + raw_spin_lock_irqsave(&byt_lock, flags); value = readl(reg); value &= ~(BYT_TRIG_POS | BYT_TRIG_NEG | BYT_TRIG_LVL); writel(value, reg); - raw_spin_unlock_irqrestore(&vg->lock, flags); + raw_spin_unlock_irqrestore(&byt_lock, flags); } static int byt_gpio_request_enable(struct pinctrl_dev *pctl_dev, @@ -765,7 +766,7 @@ static int byt_gpio_request_enable(struct pinctrl_dev *pctl_dev, u32 value, gpio_mux; unsigned long flags; - raw_spin_lock_irqsave(&vg->lock, flags); + raw_spin_lock_irqsave(&byt_lock, flags); /* * In most cases, func pin mux 000 means GPIO function. @@ -787,7 +788,7 @@ static int byt_gpio_request_enable(struct pinctrl_dev *pctl_dev, "pin %u forcibly re-configured as GPIO\n", offset); } - raw_spin_unlock_irqrestore(&vg->lock, flags); + raw_spin_unlock_irqrestore(&byt_lock, flags); pm_runtime_get(&vg->pdev->dev); @@ -815,7 +816,7 @@ static int byt_gpio_set_direction(struct pinctrl_dev *pctl_dev, unsigned long flags; u32 value; - raw_spin_lock_irqsave(&vg->lock, flags); + raw_spin_lock_irqsave(&byt_lock, flags); value = readl(val_reg); value &= ~BYT_DIR_MASK; @@ -832,7 +833,7 @@ static int byt_gpio_set_direction(struct pinctrl_dev *pctl_dev, "Potential Error: Setting GPIO with direct_irq_en to output"); writel(value, val_reg); - raw_spin_unlock_irqrestore(&vg->lock, flags); + raw_spin_unlock_irqrestore(&byt_lock, flags); return 0; } @@ -901,11 +902,11 @@ static int byt_pin_config_get(struct pinctrl_dev *pctl_dev, unsigned int offset, u32 conf, pull, val, debounce; u16 arg = 0; - raw_spin_lock_irqsave(&vg->lock, flags); + raw_spin_lock_irqsave(&byt_lock, flags); conf = readl(conf_reg); pull = conf & BYT_PULL_ASSIGN_MASK; val = readl(val_reg); - raw_spin_unlock_irqrestore(&vg->lock, flags); + raw_spin_unlock_irqrestore(&byt_lock, flags); switch (param) { case PIN_CONFIG_BIAS_DISABLE: @@ -932,9 +933,9 @@ static int byt_pin_config_get(struct pinctrl_dev *pctl_dev, unsigned int offset, if (!(conf & BYT_DEBOUNCE_EN)) return -EINVAL; - raw_spin_lock_irqsave(&vg->lock, flags); + raw_spin_lock_irqsave(&byt_lock, flags); debounce = readl(db_reg); - raw_spin_unlock_irqrestore(&vg->lock, flags); + raw_spin_unlock_irqrestore(&byt_lock, flags); switch (debounce & BYT_DEBOUNCE_PULSE_MASK) { case BYT_DEBOUNCE_PULSE_375US: @@ -986,7 +987,7 @@ static int byt_pin_config_set(struct pinctrl_dev *pctl_dev, u32 conf, val, debounce; int i, ret = 0; - raw_spin_lock_irqsave(&vg->lock, flags); + raw_spin_lock_irqsave(&byt_lock, flags); conf = readl(conf_reg); val = readl(val_reg); @@ -1094,7 +1095,7 @@ static int byt_pin_config_set(struct pinctrl_dev *pctl_dev, if (!ret) writel(conf, conf_reg); - raw_spin_unlock_irqrestore(&vg->lock, flags); + raw_spin_unlock_irqrestore(&byt_lock, flags); return ret; } @@ -1119,9 +1120,9 @@ static int byt_gpio_get(struct gpio_chip *chip, unsigned int offset) unsigned long flags; u32 val; - raw_spin_lock_irqsave(&vg->lock, flags); + raw_spin_lock_irqsave(&byt_lock, flags); val = readl(reg); - raw_spin_unlock_irqrestore(&vg->lock, flags); + raw_spin_unlock_irqrestore(&byt_lock, flags); return !!(val & BYT_LEVEL); } @@ -1136,13 +1137,13 @@ static void byt_gpio_set(struct gpio_chip *chip, unsigned int offset, int value) if (!reg) return; - raw_spin_lock_irqsave(&vg->lock, flags); + raw_spin_lock_irqsave(&byt_lock, flags); old_val = readl(reg); if (value) writel(old_val | BYT_LEVEL, reg); else writel(old_val & ~BYT_LEVEL, reg); - raw_spin_unlock_irqrestore(&vg->lock, flags); + raw_spin_unlock_irqrestore(&byt_lock, flags); } static int byt_gpio_get_direction(struct gpio_chip *chip, unsigned int offset) @@ -1155,9 +1156,9 @@ static int byt_gpio_get_direction(struct gpio_chip *chip, unsigned int offset) if (!reg) return -EINVAL; - raw_spin_lock_irqsave(&vg->lock, flags); + raw_spin_lock_irqsave(&byt_lock, flags); value = readl(reg); - raw_spin_unlock_irqrestore(&vg->lock, flags); + raw_spin_unlock_irqrestore(&byt_lock, flags); if (!(value & BYT_OUTPUT_EN)) return 0; @@ -1200,14 +1201,14 @@ static void byt_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip) const char *label; unsigned int pin; - raw_spin_lock_irqsave(&vg->lock, flags); + raw_spin_lock_irqsave(&byt_lock, flags); pin = vg->soc_data->pins[i].number; reg = byt_gpio_reg(vg, pin, BYT_CONF0_REG); if (!reg) { seq_printf(s, "Could not retrieve pin %i conf0 reg\n", pin); - raw_spin_unlock_irqrestore(&vg->lock, flags); + raw_spin_unlock_irqrestore(&byt_lock, flags); continue; } conf0 = readl(reg); @@ -1216,11 +1217,11 @@ static void byt_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip) if (!reg) { seq_printf(s, "Could not retrieve pin %i val reg\n", pin); - raw_spin_unlock_irqrestore(&vg->lock, flags); + raw_spin_unlock_irqrestore(&byt_lock, flags); continue; } val = readl(reg); - raw_spin_unlock_irqrestore(&vg->lock, flags); + raw_spin_unlock_irqrestore(&byt_lock, flags); comm = byt_get_community(vg, pin); if (!comm) { @@ -1304,9 +1305,9 @@ static void byt_irq_ack(struct irq_data *d) if (!reg) return; - raw_spin_lock(&vg->lock); + raw_spin_lock(&byt_lock); writel(BIT(offset % 32), reg); - raw_spin_unlock(&vg->lock); + raw_spin_unlock(&byt_lock); } static void byt_irq_mask(struct irq_data *d) @@ -1330,7 +1331,7 @@ static void byt_irq_unmask(struct irq_data *d) if (!reg) return; - raw_spin_lock_irqsave(&vg->lock, flags); + raw_spin_lock_irqsave(&byt_lock, flags); value = readl(reg); switch (irqd_get_trigger_type(d)) { @@ -1353,7 +1354,7 @@ static void byt_irq_unmask(struct irq_data *d) writel(value, reg); - raw_spin_unlock_irqrestore(&vg->lock, flags); + raw_spin_unlock_irqrestore(&byt_lock, flags); } static int byt_irq_type(struct irq_data *d, unsigned int type) @@ -1367,7 +1368,7 @@ static int byt_irq_type(struct irq_data *d, unsigned int type) if (!reg || offset >= vg->chip.ngpio) return -EINVAL; - raw_spin_lock_irqsave(&vg->lock, flags); + raw_spin_lock_irqsave(&byt_lock, flags); value = readl(reg); WARN(value & BYT_DIRECT_IRQ_EN, @@ -1389,7 +1390,7 @@ static int byt_irq_type(struct irq_data *d, unsigned int type) else if (type & IRQ_TYPE_LEVEL_MASK) irq_set_handler_locked(d, handle_level_irq); - raw_spin_unlock_irqrestore(&vg->lock, flags); + raw_spin_unlock_irqrestore(&byt_lock, flags); return 0; } @@ -1425,9 +1426,9 @@ static void byt_gpio_irq_handler(struct irq_desc *desc) continue; } - raw_spin_lock(&vg->lock); + raw_spin_lock(&byt_lock); pending = readl(reg); - raw_spin_unlock(&vg->lock); + raw_spin_unlock(&byt_lock); for_each_set_bit(pin, &pending, 32) { virq = irq_find_mapping(vg->chip.irq.domain, base + pin); generic_handle_irq(virq); @@ -1638,8 +1639,6 @@ static int byt_pinctrl_probe(struct platform_device *pdev) return PTR_ERR(vg->pctl_dev); } - raw_spin_lock_init(&vg->lock); - ret = byt_gpio_probe(vg); if (ret) return ret; @@ -1654,8 +1653,11 @@ static int byt_pinctrl_probe(struct platform_device *pdev) static int byt_gpio_suspend(struct device *dev) { struct byt_gpio *vg = dev_get_drvdata(dev); + unsigned long flags; int i; + raw_spin_lock_irqsave(&byt_lock, flags); + for (i = 0; i < vg->soc_data->npins; i++) { void __iomem *reg; u32 value; @@ -1676,14 +1678,18 @@ static int byt_gpio_suspend(struct device *dev) vg->saved_context[i].val = value; } + raw_spin_unlock_irqrestore(&byt_lock, flags); return 0; } static int byt_gpio_resume(struct device *dev) { struct byt_gpio *vg = dev_get_drvdata(dev); + unsigned long flags; int i; + raw_spin_lock_irqsave(&byt_lock, flags); + for (i = 0; i < vg->soc_data->npins; i++) { void __iomem *reg; u32 value; @@ -1721,6 +1727,7 @@ static int byt_gpio_resume(struct device *dev) } } + raw_spin_unlock_irqrestore(&byt_lock, flags); return 0; } #endif -- GitLab From b30b736a2b3d5a032fd91ab34c558148cda6590f Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 12 Nov 2019 16:24:41 +0200 Subject: [PATCH 0247/1437] pinctrl: baytrail: Update North Community pin list Update North Community pin list to be more clear about pin functions. Signed-off-by: Andy Shevchenko Acked-by: Mika Westerberg --- drivers/pinctrl/intel/pinctrl-baytrail.c | 56 ++++++++++++------------ 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/drivers/pinctrl/intel/pinctrl-baytrail.c b/drivers/pinctrl/intel/pinctrl-baytrail.c index 7d658e6627e7a..c7fcb3e9d0a85 100644 --- a/drivers/pinctrl/intel/pinctrl-baytrail.c +++ b/drivers/pinctrl/intel/pinctrl-baytrail.c @@ -493,34 +493,34 @@ static const struct intel_pinctrl_soc_data byt_sus_soc_data = { }; static const struct pinctrl_pin_desc byt_ncore_pins[] = { - PINCTRL_PIN(0, "GPIO_NCORE0"), - PINCTRL_PIN(1, "GPIO_NCORE1"), - PINCTRL_PIN(2, "GPIO_NCORE2"), - PINCTRL_PIN(3, "GPIO_NCORE3"), - PINCTRL_PIN(4, "GPIO_NCORE4"), - PINCTRL_PIN(5, "GPIO_NCORE5"), - PINCTRL_PIN(6, "GPIO_NCORE6"), - PINCTRL_PIN(7, "GPIO_NCORE7"), - PINCTRL_PIN(8, "GPIO_NCORE8"), - PINCTRL_PIN(9, "GPIO_NCORE9"), - PINCTRL_PIN(10, "GPIO_NCORE10"), - PINCTRL_PIN(11, "GPIO_NCORE11"), - PINCTRL_PIN(12, "GPIO_NCORE12"), - PINCTRL_PIN(13, "GPIO_NCORE13"), - PINCTRL_PIN(14, "GPIO_NCORE14"), - PINCTRL_PIN(15, "GPIO_NCORE15"), - PINCTRL_PIN(16, "GPIO_NCORE16"), - PINCTRL_PIN(17, "GPIO_NCORE17"), - PINCTRL_PIN(18, "GPIO_NCORE18"), - PINCTRL_PIN(19, "GPIO_NCORE19"), - PINCTRL_PIN(20, "GPIO_NCORE20"), - PINCTRL_PIN(21, "GPIO_NCORE21"), - PINCTRL_PIN(22, "GPIO_NCORE22"), - PINCTRL_PIN(23, "GPIO_NCORE23"), - PINCTRL_PIN(24, "GPIO_NCORE24"), - PINCTRL_PIN(25, "GPIO_NCORE25"), - PINCTRL_PIN(26, "GPIO_NCORE26"), - PINCTRL_PIN(27, "GPIO_NCORE27"), + PINCTRL_PIN(0, "HV_DDI0_HPD"), + PINCTRL_PIN(1, "HV_DDI0_DDC_SDA"), + PINCTRL_PIN(2, "HV_DDI0_DDC_SCL"), + PINCTRL_PIN(3, "PANEL0_VDDEN"), + PINCTRL_PIN(4, "PANEL0_BKLTEN"), + PINCTRL_PIN(5, "PANEL0_BKLTCTL"), + PINCTRL_PIN(6, "HV_DDI1_HPD"), + PINCTRL_PIN(7, "HV_DDI1_DDC_SDA"), + PINCTRL_PIN(8, "HV_DDI1_DDC_SCL"), + PINCTRL_PIN(9, "PANEL1_VDDEN"), + PINCTRL_PIN(10, "PANEL1_BKLTEN"), + PINCTRL_PIN(11, "PANEL1_BKLTCTL"), + PINCTRL_PIN(12, "GP_INTD_DSI_TE1"), + PINCTRL_PIN(13, "HV_DDI2_DDC_SDA"), + PINCTRL_PIN(14, "HV_DDI2_DDC_SCL"), + PINCTRL_PIN(15, "GP_CAMERASB00"), + PINCTRL_PIN(16, "GP_CAMERASB01"), + PINCTRL_PIN(17, "GP_CAMERASB02"), + PINCTRL_PIN(18, "GP_CAMERASB03"), + PINCTRL_PIN(19, "GP_CAMERASB04"), + PINCTRL_PIN(20, "GP_CAMERASB05"), + PINCTRL_PIN(21, "GP_CAMERASB06"), + PINCTRL_PIN(22, "GP_CAMERASB07"), + PINCTRL_PIN(23, "GP_CAMERASB08"), + PINCTRL_PIN(24, "GP_CAMERASB09"), + PINCTRL_PIN(25, "GP_CAMERASB10"), + PINCTRL_PIN(26, "GP_CAMERASB11"), + PINCTRL_PIN(27, "GP_INTD_DSI_TE2"), }; static const unsigned int byt_ncore_pins_map[BYT_NGPIO_NCORE] = { -- GitLab From 9c9b65203492927cc4ae419e9601e837ecbd889e Mon Sep 17 00:00:00 2001 From: Curtis Malainey Date: Wed, 27 Nov 2019 17:13:58 -0800 Subject: [PATCH 0248/1437] ASoC: core: only flush inited work during free There are many paths to soc_free_pcm_runtime which can both have and have not yet inited the workqueue yet. When we flush the queue when we have not yet inited the queue we cause warnings to be printed. An example is soc_cleanup_card_resources which is called by snd_soc_bind_card which has multiple failure points before and after soc_link_init -> soc_new_pcm which is where the queue is inited. Signed-off-by: Curtis Malainey Link: https://lore.kernel.org/r/20191128011358.39234-1-cujomalainey@chromium.org Signed-off-by: Mark Brown --- sound/soc/soc-core.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 0e2e628302f1d..1c84ff1a5bf96 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -419,7 +419,8 @@ static void soc_free_pcm_runtime(struct snd_soc_pcm_runtime *rtd) list_del(&rtd->list); - flush_delayed_work(&rtd->delayed_work); + if (delayed_work_pending(&rtd->delayed_work)) + flush_delayed_work(&rtd->delayed_work); snd_soc_pcm_component_free(rtd); /* -- GitLab From ed3c156462516f3a10c8842cdf6358d20ffa34f5 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 4 Nov 2019 19:07:30 +0200 Subject: [PATCH 0249/1437] pinctrl: baytrail: Add GPIO <-> pin mapping ranges via callback When IRQ chip is instantiated via GPIO library flow, the few functions, in particular the ACPI event registration mechanism, on some of ACPI based platforms expect that the pin ranges are initialized to that point. Add GPIO <-> pin mapping ranges via callback in the GPIO library flow. Signed-off-by: Andy Shevchenko Reviewed-by: Mika Westerberg Reviewed-by: Hans de Goede Tested-by: Hans de Goede --- drivers/pinctrl/intel/pinctrl-baytrail.c | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/drivers/pinctrl/intel/pinctrl-baytrail.c b/drivers/pinctrl/intel/pinctrl-baytrail.c index c7fcb3e9d0a85..ca9976490c410 100644 --- a/drivers/pinctrl/intel/pinctrl-baytrail.c +++ b/drivers/pinctrl/intel/pinctrl-baytrail.c @@ -1507,6 +1507,19 @@ static void byt_gpio_irq_init_hw(struct byt_gpio *vg) } } +static int byt_gpio_add_pin_ranges(struct gpio_chip *chip) +{ + struct byt_gpio *vg = gpiochip_get_data(chip); + struct device *dev = &vg->pdev->dev; + int ret; + + ret = gpiochip_add_pin_range(chip, dev_name(dev), 0, 0, vg->soc_data->npins); + if (ret) + dev_err(dev, "failed to add GPIO pin range\n"); + + return ret; +} + static int byt_gpio_probe(struct byt_gpio *vg) { struct gpio_chip *gc; @@ -1519,6 +1532,7 @@ static int byt_gpio_probe(struct byt_gpio *vg) gc->label = dev_name(&vg->pdev->dev); gc->base = -1; gc->can_sleep = false; + gc->add_pin_ranges = byt_gpio_add_pin_ranges; gc->parent = &vg->pdev->dev; gc->ngpio = vg->soc_data->npins; gc->irq.init_valid_mask = byt_init_irq_valid_mask; @@ -1535,13 +1549,6 @@ static int byt_gpio_probe(struct byt_gpio *vg) return ret; } - ret = gpiochip_add_pin_range(&vg->chip, dev_name(&vg->pdev->dev), - 0, 0, vg->soc_data->npins); - if (ret) { - dev_err(&vg->pdev->dev, "failed to add GPIO pin range\n"); - return ret; - } - /* set up interrupts */ irq_rc = platform_get_resource(vg->pdev, IORESOURCE_IRQ, 0); if (irq_rc && irq_rc->start) { -- GitLab From ca8a958e2acb418846e6506f3ff2cdf161a3c806 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 5 Nov 2019 15:41:11 +0200 Subject: [PATCH 0250/1437] pinctrl: baytrail: Pass irqchip when adding gpiochip We need to convert all old gpio irqchips to pass the irqchip setup along when adding the gpio_chip. For more info see drivers/gpio/TODO. For chained irqchips this is a pretty straight-forward conversion. Cc: Andy Shevchenko Cc: Mika Westerberg Cc: Thierry Reding Signed-off-by: Linus Walleij Signed-off-by: Andy Shevchenko Reviewed-by: Mika Westerberg Reviewed-by: Hans de Goede Tested-by: Hans de Goede --- drivers/pinctrl/intel/pinctrl-baytrail.c | 42 ++++++++++++++---------- 1 file changed, 24 insertions(+), 18 deletions(-) diff --git a/drivers/pinctrl/intel/pinctrl-baytrail.c b/drivers/pinctrl/intel/pinctrl-baytrail.c index ca9976490c410..55141d5de29e6 100644 --- a/drivers/pinctrl/intel/pinctrl-baytrail.c +++ b/drivers/pinctrl/intel/pinctrl-baytrail.c @@ -1451,9 +1451,9 @@ static void byt_init_irq_valid_mask(struct gpio_chip *chip, */ } -static void byt_gpio_irq_init_hw(struct byt_gpio *vg) +static int byt_gpio_irq_init_hw(struct gpio_chip *chip) { - struct gpio_chip *gc = &vg->chip; + struct byt_gpio *vg = gpiochip_get_data(chip); struct device *dev = &vg->pdev->dev; void __iomem *reg; u32 base, value; @@ -1477,7 +1477,7 @@ static void byt_gpio_irq_init_hw(struct byt_gpio *vg) value = readl(reg); if (value & BYT_DIRECT_IRQ_EN) { - clear_bit(i, gc->irq.valid_mask); + clear_bit(i, chip->irq.valid_mask); dev_dbg(dev, "excluding GPIO %d from IRQ domain\n", i); } else if ((value & BYT_PIN_MUX) == byt_get_gpio_mux(vg, i)) { byt_gpio_clear_triggering(vg, i); @@ -1505,6 +1505,8 @@ static void byt_gpio_irq_init_hw(struct byt_gpio *vg) "GPIO interrupt error, pins misconfigured. INT_STAT%u: 0x%08x\n", base / 32, value); } + + return 0; } static int byt_gpio_add_pin_ranges(struct gpio_chip *chip) @@ -1543,26 +1545,30 @@ static int byt_gpio_probe(struct byt_gpio *vg) if (!vg->saved_context) return -ENOMEM; #endif - ret = devm_gpiochip_add_data(&vg->pdev->dev, gc, vg); - if (ret) { - dev_err(&vg->pdev->dev, "failed adding byt-gpio chip\n"); - return ret; - } /* set up interrupts */ irq_rc = platform_get_resource(vg->pdev, IORESOURCE_IRQ, 0); if (irq_rc && irq_rc->start) { - byt_gpio_irq_init_hw(vg); - ret = gpiochip_irqchip_add(gc, &byt_irqchip, 0, - handle_bad_irq, IRQ_TYPE_NONE); - if (ret) { - dev_err(&vg->pdev->dev, "failed to add irqchip\n"); - return ret; - } + struct gpio_irq_chip *girq; + + girq = &gc->irq; + girq->chip = &byt_irqchip; + girq->init_hw = byt_gpio_irq_init_hw; + girq->parent_handler = byt_gpio_irq_handler; + girq->num_parents = 1; + girq->parents = devm_kcalloc(&vg->pdev->dev, girq->num_parents, + sizeof(*girq->parents), GFP_KERNEL); + if (!girq->parents) + return -ENOMEM; + girq->parents[0] = (unsigned int)irq_rc->start; + girq->default_type = IRQ_TYPE_NONE; + girq->handler = handle_bad_irq; + } - gpiochip_set_chained_irqchip(gc, &byt_irqchip, - (unsigned)irq_rc->start, - byt_gpio_irq_handler); + ret = devm_gpiochip_add_data(&vg->pdev->dev, gc, vg); + if (ret) { + dev_err(&vg->pdev->dev, "failed adding byt-gpio chip\n"); + return ret; } return ret; -- GitLab From 82d9beb4b7f701cb2bb4c892e777c5ada14ce99e Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Thu, 14 Nov 2019 11:08:02 +0100 Subject: [PATCH 0251/1437] pinctrl: cherryview: Split out irq hw-init into a separate helper function Split out irq hw-init into a separate chv_gpio_irq_init_hw() function. This is a preparation patch for passing the irqchip when adding the gpiochip. Acked-by: Mika Westerberg Signed-off-by: Hans de Goede Signed-off-by: Andy Shevchenko --- drivers/pinctrl/intel/pinctrl-cherryview.c | 45 +++++++++++++--------- 1 file changed, 27 insertions(+), 18 deletions(-) diff --git a/drivers/pinctrl/intel/pinctrl-cherryview.c b/drivers/pinctrl/intel/pinctrl-cherryview.c index 582fa8a755598..7a4e2af5153c9 100644 --- a/drivers/pinctrl/intel/pinctrl-cherryview.c +++ b/drivers/pinctrl/intel/pinctrl-cherryview.c @@ -1555,6 +1555,32 @@ static void chv_init_irq_valid_mask(struct gpio_chip *chip, } } +static int chv_gpio_irq_init_hw(struct gpio_chip *chip) +{ + struct chv_pinctrl *pctrl = gpiochip_get_data(chip); + + /* + * The same set of machines in chv_no_valid_mask[] have incorrectly + * configured GPIOs that generate spurious interrupts so we use + * this same list to apply another quirk for them. + * + * See also https://bugzilla.kernel.org/show_bug.cgi?id=197953. + */ + if (!pctrl->chip.irq.init_valid_mask) { + /* + * Mask all interrupts the community is able to generate + * but leave the ones that can only generate GPEs unmasked. + */ + chv_writel(GENMASK(31, pctrl->community->nirqs), + pctrl->regs + CHV_INTMASK); + } + + /* Clear all interrupts */ + chv_writel(0xffff, pctrl->regs + CHV_INTSTAT); + + return 0; +} + static int chv_gpio_probe(struct chv_pinctrl *pctrl, int irq) { const struct chv_gpio_pinrange *range; @@ -1589,24 +1615,7 @@ static int chv_gpio_probe(struct chv_pinctrl *pctrl, int irq) } } - /* - * The same set of machines in chv_no_valid_mask[] have incorrectly - * configured GPIOs that generate spurious interrupts so we use - * this same list to apply another quirk for them. - * - * See also https://bugzilla.kernel.org/show_bug.cgi?id=197953. - */ - if (!need_valid_mask) { - /* - * Mask all interrupts the community is able to generate - * but leave the ones that can only generate GPEs unmasked. - */ - chv_writel(GENMASK(31, pctrl->community->nirqs), - pctrl->regs + CHV_INTMASK); - } - - /* Clear all interrupts */ - chv_writel(0xffff, pctrl->regs + CHV_INTSTAT); + chv_gpio_irq_init_hw(chip); if (!need_valid_mask) { irq_base = devm_irq_alloc_descs(pctrl->dev, -1, 0, -- GitLab From bd90633a5c5433bcb53d55843a7118e6c032b1a2 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Thu, 14 Nov 2019 11:08:03 +0100 Subject: [PATCH 0252/1437] pinctrl: cherryview: Add GPIO <-> pin mapping ranges via callback When IRQ chip is instantiated via GPIO library flow, the few functions, in particular the ACPI event registration mechanism, on some of ACPI based platforms expect that the pin ranges are initialized to that point. Add GPIO <-> pin mapping ranges via callback in the GPIO library flow. Acked-by: Mika Westerberg Signed-off-by: Hans de Goede Signed-off-by: Andy Shevchenko --- drivers/pinctrl/intel/pinctrl-cherryview.c | 33 ++++++++++++++-------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/drivers/pinctrl/intel/pinctrl-cherryview.c b/drivers/pinctrl/intel/pinctrl-cherryview.c index 7a4e2af5153c9..b3f6f7726b043 100644 --- a/drivers/pinctrl/intel/pinctrl-cherryview.c +++ b/drivers/pinctrl/intel/pinctrl-cherryview.c @@ -1581,6 +1581,27 @@ static int chv_gpio_irq_init_hw(struct gpio_chip *chip) return 0; } +static int chv_gpio_add_pin_ranges(struct gpio_chip *chip) +{ + struct chv_pinctrl *pctrl = gpiochip_get_data(chip); + const struct chv_community *community = pctrl->community; + const struct chv_gpio_pinrange *range; + int ret, i; + + for (i = 0; i < community->ngpio_ranges; i++) { + range = &community->gpio_ranges[i]; + ret = gpiochip_add_pin_range(chip, dev_name(pctrl->dev), + range->base, range->base, + range->npins); + if (ret) { + dev_err(pctrl->dev, "failed to add GPIO pin range\n"); + return ret; + } + } + + return 0; +} + static int chv_gpio_probe(struct chv_pinctrl *pctrl, int irq) { const struct chv_gpio_pinrange *range; @@ -1593,6 +1614,7 @@ static int chv_gpio_probe(struct chv_pinctrl *pctrl, int irq) chip->ngpio = community->pins[community->npins - 1].number + 1; chip->label = dev_name(pctrl->dev); + chip->add_pin_ranges = chv_gpio_add_pin_ranges; chip->parent = pctrl->dev; chip->base = -1; if (need_valid_mask) @@ -1604,17 +1626,6 @@ static int chv_gpio_probe(struct chv_pinctrl *pctrl, int irq) return ret; } - for (i = 0; i < community->ngpio_ranges; i++) { - range = &community->gpio_ranges[i]; - ret = gpiochip_add_pin_range(chip, dev_name(pctrl->dev), - range->base, range->base, - range->npins); - if (ret) { - dev_err(pctrl->dev, "failed to add GPIO pin range\n"); - return ret; - } - } - chv_gpio_irq_init_hw(chip); if (!need_valid_mask) { -- GitLab From b9a19bdbc843abd659e8ec6b1b3c32ae3a2455eb Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Thu, 14 Nov 2019 11:08:04 +0100 Subject: [PATCH 0253/1437] pinctrl: cherryview: Pass irqchip when adding gpiochip We need to convert all old gpio irqchips to pass the irqchip setup along when adding the gpio_chip. For more info see drivers/gpio/TODO. For chained irqchips this is a pretty straight-forward conversion. Acked-by: Mika Westerberg Signed-off-by: Hans de Goede Signed-off-by: Andy Shevchenko --- drivers/pinctrl/intel/pinctrl-cherryview.c | 45 +++++++++++----------- 1 file changed, 22 insertions(+), 23 deletions(-) diff --git a/drivers/pinctrl/intel/pinctrl-cherryview.c b/drivers/pinctrl/intel/pinctrl-cherryview.c index b3f6f7726b043..60527b93a7115 100644 --- a/drivers/pinctrl/intel/pinctrl-cherryview.c +++ b/drivers/pinctrl/intel/pinctrl-cherryview.c @@ -149,6 +149,7 @@ struct chv_pin_context { * @chip: GPIO chip in this pin controller * @irqchip: IRQ chip in this pin controller * @regs: MMIO registers + * @irq: Our parent irq * @intr_lines: Stores mapping between 16 HW interrupt wires and GPIO * offset (in GPIO number space) * @community: Community this pinctrl instance represents @@ -165,6 +166,7 @@ struct chv_pinctrl { struct gpio_chip chip; struct irq_chip irqchip; void __iomem *regs; + unsigned int irq; unsigned int intr_lines[16]; const struct chv_community *community; u32 saved_intmask; @@ -1617,18 +1619,26 @@ static int chv_gpio_probe(struct chv_pinctrl *pctrl, int irq) chip->add_pin_ranges = chv_gpio_add_pin_ranges; chip->parent = pctrl->dev; chip->base = -1; - if (need_valid_mask) - chip->irq.init_valid_mask = chv_init_irq_valid_mask; - - ret = devm_gpiochip_add_data(pctrl->dev, chip, pctrl); - if (ret) { - dev_err(pctrl->dev, "Failed to register gpiochip\n"); - return ret; - } - chv_gpio_irq_init_hw(chip); + pctrl->irq = irq; + pctrl->irqchip.name = "chv-gpio"; + pctrl->irqchip.irq_startup = chv_gpio_irq_startup; + pctrl->irqchip.irq_ack = chv_gpio_irq_ack; + pctrl->irqchip.irq_mask = chv_gpio_irq_mask; + pctrl->irqchip.irq_unmask = chv_gpio_irq_unmask; + pctrl->irqchip.irq_set_type = chv_gpio_irq_type; + pctrl->irqchip.flags = IRQCHIP_SKIP_SET_WAKE; - if (!need_valid_mask) { + chip->irq.chip = &pctrl->irqchip; + chip->irq.init_hw = chv_gpio_irq_init_hw; + chip->irq.parent_handler = chv_gpio_irq_handler; + chip->irq.num_parents = 1; + chip->irq.parents = &pctrl->irq; + chip->irq.default_type = IRQ_TYPE_NONE; + chip->irq.handler = handle_bad_irq; + if (need_valid_mask) { + chip->irq.init_valid_mask = chv_init_irq_valid_mask; + } else { irq_base = devm_irq_alloc_descs(pctrl->dev, -1, 0, community->npins, NUMA_NO_NODE); if (irq_base < 0) { @@ -1637,18 +1647,9 @@ static int chv_gpio_probe(struct chv_pinctrl *pctrl, int irq) } } - pctrl->irqchip.name = "chv-gpio"; - pctrl->irqchip.irq_startup = chv_gpio_irq_startup; - pctrl->irqchip.irq_ack = chv_gpio_irq_ack; - pctrl->irqchip.irq_mask = chv_gpio_irq_mask; - pctrl->irqchip.irq_unmask = chv_gpio_irq_unmask; - pctrl->irqchip.irq_set_type = chv_gpio_irq_type; - pctrl->irqchip.flags = IRQCHIP_SKIP_SET_WAKE; - - ret = gpiochip_irqchip_add(chip, &pctrl->irqchip, 0, - handle_bad_irq, IRQ_TYPE_NONE); + ret = devm_gpiochip_add_data(pctrl->dev, chip, pctrl); if (ret) { - dev_err(pctrl->dev, "failed to add IRQ chip\n"); + dev_err(pctrl->dev, "Failed to register gpiochip\n"); return ret; } @@ -1662,8 +1663,6 @@ static int chv_gpio_probe(struct chv_pinctrl *pctrl, int irq) } } - gpiochip_set_chained_irqchip(chip, &pctrl->irqchip, irq, - chv_gpio_irq_handler); return 0; } -- GitLab From 2a76352ad2cc6b78e58f737714879cc860903802 Mon Sep 17 00:00:00 2001 From: Sudeep Holla Date: Wed, 27 Nov 2019 15:56:40 +0000 Subject: [PATCH 0254/1437] ARM: vexpress: Set-up shared OPP table instead of individual for each CPU Currently we add individual copy of same OPP table for each CPU within the cluster. This is redundant and doesn't reflect the reality. We can't use core cpumask to set policy->cpus in ve_spc_cpufreq_init() anymore as it gets called via cpuhp_cpufreq_online()->cpufreq_online() ->cpufreq_driver->init() and the cpumask gets updated upon CPU hotplug operations. It also may cause issues when the vexpress_spc_cpufreq driver is built as a module. Since ve_spc_clk_init is built-in device initcall, we should be able to use the same topology_core_cpumask to set the opp sharing cpumask via dev_pm_opp_set_sharing_cpus and use the same later in the driver via dev_pm_opp_get_sharing_cpus. Cc: Liviu Dudau Cc: Lorenzo Pieralisi Acked-by: Viresh Kumar Tested-by: Dietmar Eggemann Signed-off-by: Sudeep Holla --- arch/arm/mach-vexpress/spc.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/arch/arm/mach-vexpress/spc.c b/arch/arm/mach-vexpress/spc.c index 354e0e7025aec..1da11bdb1dfbd 100644 --- a/arch/arm/mach-vexpress/spc.c +++ b/arch/arm/mach-vexpress/spc.c @@ -551,8 +551,9 @@ static struct clk *ve_spc_clk_register(struct device *cpu_dev) static int __init ve_spc_clk_init(void) { - int cpu; + int cpu, cluster; struct clk *clk; + bool init_opp_table[MAX_CLUSTERS] = { false }; if (!info) return 0; /* Continue only if SPC is initialised */ @@ -578,8 +579,17 @@ static int __init ve_spc_clk_init(void) continue; } + cluster = topology_physical_package_id(cpu_dev->id); + if (init_opp_table[cluster]) + continue; + if (ve_init_opp_table(cpu_dev)) pr_warn("failed to initialise cpu%d opp table\n", cpu); + else if (dev_pm_opp_set_sharing_cpus(cpu_dev, + topology_core_cpumask(cpu_dev->id))) + pr_warn("failed to mark OPPs shared for cpu%d\n", cpu); + else + init_opp_table[cluster] = true; } platform_device_register_simple("vexpress-spc-cpufreq", -1, NULL, 0); -- GitLab From c9385887cbbf292ee367f75b400874f00ea34890 Mon Sep 17 00:00:00 2001 From: Sudeep Holla Date: Wed, 27 Nov 2019 16:04:26 +0000 Subject: [PATCH 0255/1437] cpufreq: vexpress-spc: Switch cpumask from topology core to OPP sharing Since commit ca74b316df96 ("arm: Use common cpu_topology structure and functions.") the core cpumask has to be modified during cpu hotplug operations. So using them to set up cpufreq policy cpumask may be incorrect as it may contain only cpus that are online at that instance. Instead, we can use the cpumask setup by OPP library that contains all the cpus sharing OPP table using dev_pm_opp_get_sharing_cpus. Acked-by: Viresh Kumar Tested-by: Dietmar Eggemann Signed-off-by: Sudeep Holla --- drivers/cpufreq/vexpress-spc-cpufreq.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/cpufreq/vexpress-spc-cpufreq.c b/drivers/cpufreq/vexpress-spc-cpufreq.c index 506e3f2bf53ac..83c85d3d67e35 100644 --- a/drivers/cpufreq/vexpress-spc-cpufreq.c +++ b/drivers/cpufreq/vexpress-spc-cpufreq.c @@ -434,7 +434,7 @@ static int ve_spc_cpufreq_init(struct cpufreq_policy *policy) if (cur_cluster < MAX_CLUSTERS) { int cpu; - cpumask_copy(policy->cpus, topology_core_cpumask(policy->cpu)); + dev_pm_opp_get_sharing_cpus(cpu_dev, policy->cpus); for_each_cpu(cpu, policy->cpus) per_cpu(physical_cluster, cpu) = cur_cluster; -- GitLab From 164166558aacea01b99c8c8ffb710d930405ba69 Mon Sep 17 00:00:00 2001 From: Phil Sutter Date: Thu, 5 Dec 2019 13:35:11 +0100 Subject: [PATCH 0256/1437] netfilter: uapi: Avoid undefined left-shift in xt_sctp.h With 'bytes(__u32)' being 32, a left-shift of 31 may happen which is undefined for the signed 32-bit value 1. Avoid this by declaring 1 as unsigned. Signed-off-by: Phil Sutter Signed-off-by: Pablo Neira Ayuso --- include/uapi/linux/netfilter/xt_sctp.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/include/uapi/linux/netfilter/xt_sctp.h b/include/uapi/linux/netfilter/xt_sctp.h index 4bc6d1a087816..b4d804a9fccb2 100644 --- a/include/uapi/linux/netfilter/xt_sctp.h +++ b/include/uapi/linux/netfilter/xt_sctp.h @@ -41,19 +41,19 @@ struct xt_sctp_info { #define SCTP_CHUNKMAP_SET(chunkmap, type) \ do { \ (chunkmap)[type / bytes(__u32)] |= \ - 1 << (type % bytes(__u32)); \ + 1u << (type % bytes(__u32)); \ } while (0) #define SCTP_CHUNKMAP_CLEAR(chunkmap, type) \ do { \ (chunkmap)[type / bytes(__u32)] &= \ - ~(1 << (type % bytes(__u32))); \ + ~(1u << (type % bytes(__u32))); \ } while (0) #define SCTP_CHUNKMAP_IS_SET(chunkmap, type) \ ({ \ ((chunkmap)[type / bytes (__u32)] & \ - (1 << (type % bytes (__u32)))) ? 1: 0; \ + (1u << (type % bytes (__u32)))) ? 1: 0; \ }) #define SCTP_CHUNKMAP_RESET(chunkmap) \ -- GitLab From db3b665dd77b34e34df00e17d7b299c98fcfb2c5 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Fri, 6 Dec 2019 20:23:29 +0100 Subject: [PATCH 0257/1437] netfilter: nft_set_rbtree: bogus lookup/get on consecutive elements in named sets The existing rbtree implementation might store consecutive elements where the closing element and the opening element might overlap, eg. [ a, a+1) [ a+1, a+2) This patch removes the optimization for non-anonymous sets in the exact matching case, where it is assumed to stop searching in case that the closing element is found. Instead, invalidate candidate interval and keep looking further in the tree. The lookup/get operation might return false, while there is an element in the rbtree. Moreover, the get operation returns true as if a+2 would be in the tree. This happens with named sets after several set updates. The existing lookup optimization (that only works for the anonymous sets) might not reach the opening [ a+1,... element if the closing ...,a+1) is found in first place when walking over the rbtree. Hence, walking the full tree in that case is needed. This patch fixes the lookup and get operations. Fixes: e701001e7cbe ("netfilter: nft_rbtree: allow adjacent intervals with dynamic updates") Fixes: ba0e4d9917b4 ("netfilter: nf_tables: get set elements via netlink") Signed-off-by: Pablo Neira Ayuso --- net/netfilter/nft_set_rbtree.c | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/net/netfilter/nft_set_rbtree.c b/net/netfilter/nft_set_rbtree.c index 57123259452ff..a9f804f7a04ac 100644 --- a/net/netfilter/nft_set_rbtree.c +++ b/net/netfilter/nft_set_rbtree.c @@ -74,8 +74,13 @@ static bool __nft_rbtree_lookup(const struct net *net, const struct nft_set *set parent = rcu_dereference_raw(parent->rb_left); continue; } - if (nft_rbtree_interval_end(rbe)) - goto out; + if (nft_rbtree_interval_end(rbe)) { + if (nft_set_is_anonymous(set)) + return false; + parent = rcu_dereference_raw(parent->rb_left); + interval = NULL; + continue; + } *ext = &rbe->ext; return true; @@ -88,7 +93,7 @@ static bool __nft_rbtree_lookup(const struct net *net, const struct nft_set *set *ext = &interval->ext; return true; } -out: + return false; } @@ -139,8 +144,10 @@ static bool __nft_rbtree_get(const struct net *net, const struct nft_set *set, if (flags & NFT_SET_ELEM_INTERVAL_END) interval = rbe; } else { - if (!nft_set_elem_active(&rbe->ext, genmask)) + if (!nft_set_elem_active(&rbe->ext, genmask)) { parent = rcu_dereference_raw(parent->rb_left); + continue; + } if (!nft_set_ext_exists(&rbe->ext, NFT_SET_EXT_FLAGS) || (*nft_set_ext_flags(&rbe->ext) & NFT_SET_ELEM_INTERVAL_END) == @@ -148,7 +155,11 @@ static bool __nft_rbtree_get(const struct net *net, const struct nft_set *set, *elem = rbe; return true; } - return false; + + if (nft_rbtree_interval_end(rbe)) + interval = NULL; + + parent = rcu_dereference_raw(parent->rb_left); } } -- GitLab From bffc124b6fe37d0ae9b428d104efb426403bb5c9 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Fri, 6 Dec 2019 21:55:20 +0100 Subject: [PATCH 0258/1437] netfilter: nf_tables: validate NFT_SET_ELEM_INTERVAL_END Only NFTA_SET_ELEM_KEY and NFTA_SET_ELEM_FLAGS make sense for elements whose NFT_SET_ELEM_INTERVAL_END flag is set on. Fixes: 96518518cc41 ("netfilter: add nftables") Signed-off-by: Pablo Neira Ayuso --- net/netfilter/nf_tables_api.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c index 062b73a83af0e..0db2784fee9aa 100644 --- a/net/netfilter/nf_tables_api.c +++ b/net/netfilter/nf_tables_api.c @@ -4756,14 +4756,20 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set, if (nla[NFTA_SET_ELEM_DATA] == NULL && !(flags & NFT_SET_ELEM_INTERVAL_END)) return -EINVAL; - if (nla[NFTA_SET_ELEM_DATA] != NULL && - flags & NFT_SET_ELEM_INTERVAL_END) - return -EINVAL; } else { if (nla[NFTA_SET_ELEM_DATA] != NULL) return -EINVAL; } + if ((flags & NFT_SET_ELEM_INTERVAL_END) && + (nla[NFTA_SET_ELEM_DATA] || + nla[NFTA_SET_ELEM_OBJREF] || + nla[NFTA_SET_ELEM_TIMEOUT] || + nla[NFTA_SET_ELEM_EXPIRATION] || + nla[NFTA_SET_ELEM_USERDATA] || + nla[NFTA_SET_ELEM_EXPR])) + return -EINVAL; + timeout = 0; if (nla[NFTA_SET_ELEM_TIMEOUT] != NULL) { if (!(set->flags & NFT_SET_TIMEOUT)) -- GitLab From 0d2c96af797ba149e559c5875c0151384ab6dd14 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Fri, 6 Dec 2019 22:09:14 +0100 Subject: [PATCH 0259/1437] netfilter: nf_tables: validate NFT_DATA_VALUE after nft_data_init() Userspace might bogusly sent NFT_DATA_VERDICT in several netlink attributes that assume NFT_DATA_VALUE. Moreover, make sure that error path invokes nft_data_release() to decrement the reference count on the chain object. Fixes: 96518518cc41 ("netfilter: add nftables") Fixes: 0f3cd9b36977 ("netfilter: nf_tables: add range expression") Signed-off-by: Pablo Neira Ayuso --- net/netfilter/nf_tables_api.c | 4 +++- net/netfilter/nft_bitwise.c | 4 ++-- net/netfilter/nft_cmp.c | 6 ++++++ net/netfilter/nft_range.c | 10 ++++++++++ 4 files changed, 21 insertions(+), 3 deletions(-) diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c index 0db2784fee9aa..72a7816ba7615 100644 --- a/net/netfilter/nf_tables_api.c +++ b/net/netfilter/nf_tables_api.c @@ -4519,8 +4519,10 @@ static int nft_get_set_elem(struct nft_ctx *ctx, struct nft_set *set, return err; err = -EINVAL; - if (desc.type != NFT_DATA_VALUE || desc.len != set->klen) + if (desc.type != NFT_DATA_VALUE || desc.len != set->klen) { + nft_data_release(&elem.key.val, desc.type); return err; + } priv = set->ops->get(ctx->net, set, &elem, flags); if (IS_ERR(priv)) diff --git a/net/netfilter/nft_bitwise.c b/net/netfilter/nft_bitwise.c index 02afa752dd2e6..10e9d50e4e193 100644 --- a/net/netfilter/nft_bitwise.c +++ b/net/netfilter/nft_bitwise.c @@ -80,7 +80,7 @@ static int nft_bitwise_init(const struct nft_ctx *ctx, tb[NFTA_BITWISE_MASK]); if (err < 0) return err; - if (d1.len != priv->len) { + if (d1.type != NFT_DATA_VALUE || d1.len != priv->len) { err = -EINVAL; goto err1; } @@ -89,7 +89,7 @@ static int nft_bitwise_init(const struct nft_ctx *ctx, tb[NFTA_BITWISE_XOR]); if (err < 0) goto err1; - if (d2.len != priv->len) { + if (d2.type != NFT_DATA_VALUE || d2.len != priv->len) { err = -EINVAL; goto err2; } diff --git a/net/netfilter/nft_cmp.c b/net/netfilter/nft_cmp.c index b8092069f868f..8a28c127effc2 100644 --- a/net/netfilter/nft_cmp.c +++ b/net/netfilter/nft_cmp.c @@ -81,6 +81,12 @@ static int nft_cmp_init(const struct nft_ctx *ctx, const struct nft_expr *expr, if (err < 0) return err; + if (desc.type != NFT_DATA_VALUE) { + err = -EINVAL; + nft_data_release(&priv->data, desc.type); + return err; + } + priv->sreg = nft_parse_register(tb[NFTA_CMP_SREG]); err = nft_validate_register_load(priv->sreg, desc.len); if (err < 0) diff --git a/net/netfilter/nft_range.c b/net/netfilter/nft_range.c index 4701fa8a45e74..89efcc5a533d2 100644 --- a/net/netfilter/nft_range.c +++ b/net/netfilter/nft_range.c @@ -66,11 +66,21 @@ static int nft_range_init(const struct nft_ctx *ctx, const struct nft_expr *expr if (err < 0) return err; + if (desc_from.type != NFT_DATA_VALUE) { + err = -EINVAL; + goto err1; + } + err = nft_data_init(NULL, &priv->data_to, sizeof(priv->data_to), &desc_to, tb[NFTA_RANGE_TO_DATA]); if (err < 0) goto err1; + if (desc_to.type != NFT_DATA_VALUE) { + err = -EINVAL; + goto err2; + } + if (desc_from.len != desc_to.len) { err = -EINVAL; goto err2; -- GitLab From fd57d0cbe187e93f63777d36e9f49293311d417f Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Fri, 6 Dec 2019 22:25:55 +0100 Subject: [PATCH 0260/1437] netfilter: nf_tables: skip module reference count bump on object updates Use __nft_obj_type_get() instead, otherwise there is a module reference counter leak. Fixes: d62d0ba97b58 ("netfilter: nf_tables: Introduce stateful object update operation") Signed-off-by: Pablo Neira Ayuso --- net/netfilter/nf_tables_api.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c index 72a7816ba7615..a8caf7386fa9c 100644 --- a/net/netfilter/nf_tables_api.c +++ b/net/netfilter/nf_tables_api.c @@ -5484,7 +5484,7 @@ static int nf_tables_newobj(struct net *net, struct sock *nlsk, if (nlh->nlmsg_flags & NLM_F_REPLACE) return -EOPNOTSUPP; - type = nft_obj_type_get(net, objtype); + type = __nft_obj_type_get(objtype); nft_ctx_init(&ctx, net, skb, nlh, family, table, NULL, nla); return nf_tables_updobj(&ctx, type, nla[NFTA_OBJ_DATA], obj); -- GitLab From 81ec61074bcf68acfcb2820cda3ff9d9984419c7 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Fri, 6 Dec 2019 22:49:58 +0100 Subject: [PATCH 0261/1437] netfilter: nf_tables_offload: return EOPNOTSUPP if rule specifies no actions If the rule only specifies the matching side, return EOPNOTSUPP. Otherwise, the front-end relies on the drivers to reject this rule. Fixes: c9626a2cbdb2 ("netfilter: nf_tables: add hardware offload support") Signed-off-by: Pablo Neira Ayuso --- net/netfilter/nf_tables_offload.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/netfilter/nf_tables_offload.c b/net/netfilter/nf_tables_offload.c index d7a35da008ef7..22fb18906ccf5 100644 --- a/net/netfilter/nf_tables_offload.c +++ b/net/netfilter/nf_tables_offload.c @@ -44,6 +44,9 @@ struct nft_flow_rule *nft_flow_rule_create(struct net *net, expr = nft_expr_next(expr); } + if (num_actions == 0) + return ERR_PTR(-EOPNOTSUPP); + flow = nft_flow_rule_alloc(num_actions); if (!flow) return ERR_PTR(-ENOMEM); -- GitLab From 5604285839aaedfb23ebe297799c6e558939334d Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Sat, 7 Dec 2019 14:43:39 -0800 Subject: [PATCH 0262/1437] netfilter: bridge: make sure to pull arp header in br_nf_forward_arp() syzbot is kind enough to remind us we need to call skb_may_pull() BUG: KMSAN: uninit-value in br_nf_forward_arp+0xe61/0x1230 net/bridge/br_netfilter_hooks.c:665 CPU: 1 PID: 11631 Comm: syz-executor.1 Not tainted 5.4.0-rc8-syzkaller #0 Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 01/01/2011 Call Trace: __dump_stack lib/dump_stack.c:77 [inline] dump_stack+0x1c9/0x220 lib/dump_stack.c:118 kmsan_report+0x128/0x220 mm/kmsan/kmsan_report.c:108 __msan_warning+0x64/0xc0 mm/kmsan/kmsan_instr.c:245 br_nf_forward_arp+0xe61/0x1230 net/bridge/br_netfilter_hooks.c:665 nf_hook_entry_hookfn include/linux/netfilter.h:135 [inline] nf_hook_slow+0x18b/0x3f0 net/netfilter/core.c:512 nf_hook include/linux/netfilter.h:260 [inline] NF_HOOK include/linux/netfilter.h:303 [inline] __br_forward+0x78f/0xe30 net/bridge/br_forward.c:109 br_flood+0xef0/0xfe0 net/bridge/br_forward.c:234 br_handle_frame_finish+0x1a77/0x1c20 net/bridge/br_input.c:162 nf_hook_bridge_pre net/bridge/br_input.c:245 [inline] br_handle_frame+0xfb6/0x1eb0 net/bridge/br_input.c:348 __netif_receive_skb_core+0x20b9/0x51a0 net/core/dev.c:4830 __netif_receive_skb_one_core net/core/dev.c:4927 [inline] __netif_receive_skb net/core/dev.c:5043 [inline] process_backlog+0x610/0x13c0 net/core/dev.c:5874 napi_poll net/core/dev.c:6311 [inline] net_rx_action+0x7a6/0x1aa0 net/core/dev.c:6379 __do_softirq+0x4a1/0x83a kernel/softirq.c:293 do_softirq_own_stack+0x49/0x80 arch/x86/entry/entry_64.S:1091 do_softirq kernel/softirq.c:338 [inline] __local_bh_enable_ip+0x184/0x1d0 kernel/softirq.c:190 local_bh_enable+0x36/0x40 include/linux/bottom_half.h:32 rcu_read_unlock_bh include/linux/rcupdate.h:688 [inline] __dev_queue_xmit+0x38e8/0x4200 net/core/dev.c:3819 dev_queue_xmit+0x4b/0x60 net/core/dev.c:3825 packet_snd net/packet/af_packet.c:2959 [inline] packet_sendmsg+0x8234/0x9100 net/packet/af_packet.c:2984 sock_sendmsg_nosec net/socket.c:637 [inline] sock_sendmsg net/socket.c:657 [inline] __sys_sendto+0xc44/0xc70 net/socket.c:1952 __do_sys_sendto net/socket.c:1964 [inline] __se_sys_sendto+0x107/0x130 net/socket.c:1960 __x64_sys_sendto+0x6e/0x90 net/socket.c:1960 do_syscall_64+0xb6/0x160 arch/x86/entry/common.c:291 entry_SYSCALL_64_after_hwframe+0x44/0xa9 RIP: 0033:0x45a679 Code: ad b6 fb ff c3 66 2e 0f 1f 84 00 00 00 00 00 66 90 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d 01 f0 ff ff 0f 83 7b b6 fb ff c3 66 2e 0f 1f 84 00 00 00 00 RSP: 002b:00007f0a3c9e5c78 EFLAGS: 00000246 ORIG_RAX: 000000000000002c RAX: ffffffffffffffda RBX: 0000000000000006 RCX: 000000000045a679 RDX: 000000000000000e RSI: 0000000020000200 RDI: 0000000000000003 RBP: 000000000075bf20 R08: 00000000200000c0 R09: 0000000000000014 R10: 0000000000000000 R11: 0000000000000246 R12: 00007f0a3c9e66d4 R13: 00000000004c8ec1 R14: 00000000004dfe28 R15: 00000000ffffffff Uninit was created at: kmsan_save_stack_with_flags mm/kmsan/kmsan.c:149 [inline] kmsan_internal_poison_shadow+0x5c/0x110 mm/kmsan/kmsan.c:132 kmsan_slab_alloc+0x97/0x100 mm/kmsan/kmsan_hooks.c:86 slab_alloc_node mm/slub.c:2773 [inline] __kmalloc_node_track_caller+0xe27/0x11a0 mm/slub.c:4381 __kmalloc_reserve net/core/skbuff.c:141 [inline] __alloc_skb+0x306/0xa10 net/core/skbuff.c:209 alloc_skb include/linux/skbuff.h:1049 [inline] alloc_skb_with_frags+0x18c/0xa80 net/core/skbuff.c:5662 sock_alloc_send_pskb+0xafd/0x10a0 net/core/sock.c:2244 packet_alloc_skb net/packet/af_packet.c:2807 [inline] packet_snd net/packet/af_packet.c:2902 [inline] packet_sendmsg+0x63a6/0x9100 net/packet/af_packet.c:2984 sock_sendmsg_nosec net/socket.c:637 [inline] sock_sendmsg net/socket.c:657 [inline] __sys_sendto+0xc44/0xc70 net/socket.c:1952 __do_sys_sendto net/socket.c:1964 [inline] __se_sys_sendto+0x107/0x130 net/socket.c:1960 __x64_sys_sendto+0x6e/0x90 net/socket.c:1960 do_syscall_64+0xb6/0x160 arch/x86/entry/common.c:291 entry_SYSCALL_64_after_hwframe+0x44/0xa9 Fixes: c4e70a87d975 ("netfilter: bridge: rename br_netfilter.c to br_netfilter_hooks.c") Signed-off-by: Eric Dumazet Reported-by: syzbot Reviewed-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- net/bridge/br_netfilter_hooks.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/bridge/br_netfilter_hooks.c b/net/bridge/br_netfilter_hooks.c index af7800103e51e..59980ecfc9623 100644 --- a/net/bridge/br_netfilter_hooks.c +++ b/net/bridge/br_netfilter_hooks.c @@ -662,6 +662,9 @@ static unsigned int br_nf_forward_arp(void *priv, nf_bridge_pull_encap_header(skb); } + if (unlikely(!pskb_may_pull(skb, sizeof(struct arphdr)))) + return NF_DROP; + if (arp_hdr(skb)->ar_pln != 4) { if (is_vlan_arp(skb, state->net)) nf_bridge_push_encap_header(skb); -- GitLab From 322f6a3182d42df18059a89c53b09d33919f755e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johnson=20CH=20Chen=20=28=E9=99=B3=E6=98=AD=E5=8B=B3=29?= Date: Tue, 26 Nov 2019 06:51:11 +0000 Subject: [PATCH 0263/1437] gpio: mpc8xxx: Add platform device to gpiochip->parent Dear Linus Walleij, In old kernels, some APIs still try to use parent->of_node from struct gpio_chip, and it could be resulted in kernel panic because parent is NULL. Adding platform device to gpiochip->parent can fix this problem. Signed-off-by: Johnson Chen Link: https://patchwork.kernel.org/patch/11234609 Link: https://lore.kernel.org/r/HK0PR01MB3521489269F76467DFD7843FFA450@HK0PR01MB3521.apcprd01.prod.exchangelabs.com Signed-off-by: Linus Walleij --- drivers/gpio/gpio-mpc8xxx.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpio/gpio-mpc8xxx.c b/drivers/gpio/gpio-mpc8xxx.c index f1e164cecff80..5ae30de3490ac 100644 --- a/drivers/gpio/gpio-mpc8xxx.c +++ b/drivers/gpio/gpio-mpc8xxx.c @@ -346,6 +346,7 @@ static int mpc8xxx_probe(struct platform_device *pdev) return -ENOMEM; gc = &mpc8xxx_gc->gc; + gc->parent = &pdev->dev; if (of_property_read_bool(np, "little-endian")) { ret = bgpio_init(gc, &pdev->dev, 4, -- GitLab From d935bd50dd14a7714cbdba9a76435dbb56edb1ae Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Wed, 27 Nov 2019 10:59:19 +0100 Subject: [PATCH 0264/1437] gpio: Fix error message on out-of-range GPIO in lookup table When a GPIO offset in a lookup table is out-of-range, the printed error message (1) does not include the actual out-of-range value, and (2) contains an off-by-one error in the upper bound. Avoid user confusion by also printing the actual GPIO offset, and correcting the upper bound of the range. While at it, use "%u" for unsigned int. Sample impact: -requested GPIO 0 is out of range [0..32] for chip e6052000.gpio +requested GPIO 0 (45) is out of range [0..31] for chip e6052000.gpio Fixes: 2a3cf6a3599e9015 ("gpiolib: return -ENOENT if no GPIO mapping exists") Signed-off-by: Geert Uytterhoeven Link: https://lore.kernel.org/r/20191127095919.4214-1-geert+renesas@glider.be Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 9913886ede904..dce0b31f4125a 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -4472,8 +4472,9 @@ static struct gpio_desc *gpiod_find(struct device *dev, const char *con_id, if (chip->ngpio <= p->chip_hwnum) { dev_err(dev, - "requested GPIO %d is out of range [0..%d] for chip %s\n", - idx, chip->ngpio, chip->label); + "requested GPIO %u (%u) is out of range [0..%u] for chip %s\n", + idx, p->chip_hwnum, chip->ngpio - 1, + chip->label); return ERR_PTR(-EINVAL); } -- GitLab From 36deeddcd3699c2755ed21c8d2595b1728d844e5 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Thu, 5 Dec 2019 14:59:34 +0000 Subject: [PATCH 0265/1437] drm/i915/gt: Save irqstate around virtual_context_destroy As virtual_context_destroy() may be called from a request signal, it may be called from inside an irq-off section, and so we need to do a full save/restore of the irq state rather than blindly re-enable irqs upon unlocking. <4> [110.024262] WARNING: inconsistent lock state <4> [110.024277] 5.4.0-rc8-CI-CI_DRM_7489+ #1 Tainted: G U <4> [110.024292] -------------------------------- <4> [110.024305] inconsistent {IN-HARDIRQ-W} -> {HARDIRQ-ON-W} usage. <4> [110.024323] kworker/0:0/5 [HC0[0]:SC0[0]:HE1:SE1] takes: <4> [110.024338] ffff88826a0c7a18 (&(&rq->lock)->rlock){?.-.}, at: i915_request_retire+0x221/0x930 [i915] <4> [110.024592] {IN-HARDIRQ-W} state was registered at: <4> [110.024612] lock_acquire+0xa7/0x1c0 <4> [110.024627] _raw_spin_lock_irqsave+0x33/0x50 <4> [110.024788] intel_engine_breadcrumbs_irq+0x38c/0x600 [i915] <4> [110.024808] irq_work_run_list+0x49/0x70 <4> [110.024824] irq_work_run+0x26/0x50 <4> [110.024839] smp_irq_work_interrupt+0x44/0x1e0 <4> [110.024855] irq_work_interrupt+0xf/0x20 <4> [110.024871] __do_softirq+0xb7/0x47f <4> [110.024885] irq_exit+0xba/0xc0 <4> [110.024898] do_IRQ+0x83/0x160 <4> [110.024910] ret_from_intr+0x0/0x1d <4> [110.024922] irq event stamp: 172864 <4> [110.024938] hardirqs last enabled at (172863): [] _raw_spin_unlock_irq+0x24/0x50 <4> [110.024963] hardirqs last disabled at (172864): [] _raw_spin_lock_irq+0xa/0x40 <4> [110.024988] softirqs last enabled at (172812): [] __do_softirq+0x385/0x47f <4> [110.025012] softirqs last disabled at (172797): [] irq_exit+0xba/0xc0 <4> [110.025031] other info that might help us debug this: <4> [110.025049] Possible unsafe locking scenario: <4> [110.025065] CPU0 <4> [110.025075] ---- <4> [110.025084] lock(&(&rq->lock)->rlock); <4> [110.025099] <4> [110.025109] lock(&(&rq->lock)->rlock); <4> [110.025124] *** DEADLOCK *** <4> [110.025144] 4 locks held by kworker/0:0/5: <4> [110.025156] #0: ffff88827588f528 ((wq_completion)events){+.+.}, at: process_one_work+0x1de/0x620 <4> [110.025187] #1: ffffc9000006fe78 ((work_completion)(&engine->retire_work)){+.+.}, at: process_one_work+0x1de/0x620 <4> [110.025219] #2: ffff88825605e270 (&kernel#2){+.+.}, at: engine_retire+0x57/0xe0 [i915] <4> [110.025405] #3: ffff88826a0c7a18 (&(&rq->lock)->rlock){?.-.}, at: i915_request_retire+0x221/0x930 [i915] <4> [110.025634] stack backtrace: <4> [110.025653] CPU: 0 PID: 5 Comm: kworker/0:0 Tainted: G U 5.4.0-rc8-CI-CI_DRM_7489+ #1 <4> [110.025675] Hardware name: /NUC7i5BNB, BIOS BNKBL357.86A.0054.2017.1025.1822 10/25/2017 <4> [110.025856] Workqueue: events engine_retire [i915] <4> [110.025872] Call Trace: <4> [110.025891] dump_stack+0x71/0x9b <4> [110.025907] mark_lock+0x49a/0x500 <4> [110.025926] ? print_shortest_lock_dependencies+0x200/0x200 <4> [110.025946] mark_held_locks+0x49/0x70 <4> [110.025962] ? _raw_spin_unlock_irq+0x24/0x50 <4> [110.025978] lockdep_hardirqs_on+0xa2/0x1c0 <4> [110.025995] _raw_spin_unlock_irq+0x24/0x50 <4> [110.026171] virtual_context_destroy+0xc5/0x2e0 [i915] <4> [110.026376] __active_retire+0xb4/0x290 [i915] <4> [110.026396] dma_fence_signal_locked+0x9e/0x1b0 <4> [110.026613] i915_request_retire+0x451/0x930 [i915] <4> [110.026766] retire_requests+0x4d/0x60 [i915] <4> [110.026919] engine_retire+0x63/0xe0 [i915] Fixes: b1e3177bd1d8 ("drm/i915: Coordinate i915_active with its own mutex") Fixes: 6d06779e8672 ("drm/i915: Load balancing across a virtual engine") Signed-off-by: Chris Wilson Cc: Tvrtko Ursulin Reviewed-by: Tvrtko Ursulin Link: https://patchwork.freedesktop.org/patch/msgid/20191205145934.663183-1-chris@chris-wilson.co.uk (cherry picked from commit 6f7ac8285371fb0df58aba861eaab387f79ed04d) Signed-off-by: Joonas Lahtinen --- drivers/gpu/drm/i915/gt/intel_lrc.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/gt/intel_lrc.c b/drivers/gpu/drm/i915/gt/intel_lrc.c index 9fdefbdc35467..17bb52aeff190 100644 --- a/drivers/gpu/drm/i915/gt/intel_lrc.c +++ b/drivers/gpu/drm/i915/gt/intel_lrc.c @@ -4120,17 +4120,18 @@ static void virtual_context_destroy(struct kref *kref) for (n = 0; n < ve->num_siblings; n++) { struct intel_engine_cs *sibling = ve->siblings[n]; struct rb_node *node = &ve->nodes[sibling->id].rb; + unsigned long flags; if (RB_EMPTY_NODE(node)) continue; - spin_lock_irq(&sibling->active.lock); + spin_lock_irqsave(&sibling->active.lock, flags); /* Detachment is lazily performed in the execlists tasklet */ if (!RB_EMPTY_NODE(node)) rb_erase_cached(node, &sibling->execlists.virtual); - spin_unlock_irq(&sibling->active.lock); + spin_unlock_irqrestore(&sibling->active.lock, flags); } GEM_BUG_ON(__tasklet_is_scheduled(&ve->base.execlists.tasklet)); -- GitLab From 9bd0160d12370a076e44f8d1320cde9c83f2c647 Mon Sep 17 00:00:00 2001 From: Marc Dionne Date: Mon, 9 Dec 2019 15:04:43 +0000 Subject: [PATCH 0266/1437] afs: Fix afs_find_server lookups for ipv4 peers afs_find_server tries to find a server that has an address that matches the transport address of an rxrpc peer. The code assumes that the transport address is always ipv6, with ipv4 represented as ipv4 mapped addresses, but that's not the case. If the transport family is AF_INET, srx->transport.sin6.sin6_addr.s6_addr32[] will be beyond the actual ipv4 address and will always be 0, and all ipv4 addresses will be seen as matching. As a result, the first ipv4 address seen on any server will be considered a match, and the server returned may be the wrong one. One of the consequences is that callbacks received over ipv4 will only be correctly applied for the server that happens to have the first ipv4 address on the fs_addresses4 list. Callbacks over ipv4 from all other servers are dropped, causing the client to serve stale data. This is fixed by looking at the transport family, and comparing ipv4 addresses based on a sockaddr_in structure rather than a sockaddr_in6. Fixes: d2ddc776a458 ("afs: Overhaul volume and server record caching and fileserver rotation") Signed-off-by: Marc Dionne Signed-off-by: David Howells --- fs/afs/server.c | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/fs/afs/server.c b/fs/afs/server.c index 1686bf188ccd0..b7f3cb2130cae 100644 --- a/fs/afs/server.c +++ b/fs/afs/server.c @@ -32,18 +32,11 @@ static void afs_dec_servers_outstanding(struct afs_net *net) struct afs_server *afs_find_server(struct afs_net *net, const struct sockaddr_rxrpc *srx) { - const struct sockaddr_in6 *a = &srx->transport.sin6, *b; const struct afs_addr_list *alist; struct afs_server *server = NULL; unsigned int i; - bool ipv6 = true; int seq = 0, diff; - if (srx->transport.sin6.sin6_addr.s6_addr32[0] == 0 || - srx->transport.sin6.sin6_addr.s6_addr32[1] == 0 || - srx->transport.sin6.sin6_addr.s6_addr32[2] == htonl(0xffff)) - ipv6 = false; - rcu_read_lock(); do { @@ -52,7 +45,8 @@ struct afs_server *afs_find_server(struct afs_net *net, server = NULL; read_seqbegin_or_lock(&net->fs_addr_lock, &seq); - if (ipv6) { + if (srx->transport.family == AF_INET6) { + const struct sockaddr_in6 *a = &srx->transport.sin6, *b; hlist_for_each_entry_rcu(server, &net->fs_addresses6, addr6_link) { alist = rcu_dereference(server->addresses); for (i = alist->nr_ipv4; i < alist->nr_addrs; i++) { @@ -68,15 +62,16 @@ struct afs_server *afs_find_server(struct afs_net *net, } } } else { + const struct sockaddr_in *a = &srx->transport.sin, *b; hlist_for_each_entry_rcu(server, &net->fs_addresses4, addr4_link) { alist = rcu_dereference(server->addresses); for (i = 0; i < alist->nr_ipv4; i++) { - b = &alist->addrs[i].transport.sin6; - diff = ((u16 __force)a->sin6_port - - (u16 __force)b->sin6_port); + b = &alist->addrs[i].transport.sin; + diff = ((u16 __force)a->sin_port - + (u16 __force)b->sin_port); if (diff == 0) - diff = ((u32 __force)a->sin6_addr.s6_addr32[3] - - (u32 __force)b->sin6_addr.s6_addr32[3]); + diff = ((u32 __force)a->sin_addr.s_addr - + (u32 __force)b->sin_addr.s_addr); if (diff == 0) goto found; } -- GitLab From bcbccaf2edcf1b76f73f890e968babef446151a4 Mon Sep 17 00:00:00 2001 From: David Howells Date: Mon, 9 Dec 2019 15:04:45 +0000 Subject: [PATCH 0267/1437] afs: Fix SELinux setting security label on /afs Make the AFS dynamic root superblock R/W so that SELinux can set the security label on it. Without this, upgrades to, say, the Fedora filesystem-afs RPM fail if afs is mounted on it because the SELinux label can't be (re-)applied. It might be better to make it possible to bypass the R/O check for LSM label application through setxattr. Fixes: 4d673da14533 ("afs: Support the AFS dynamic root") Signed-off-by: David Howells Reviewed-by: Marc Dionne cc: selinux@vger.kernel.org cc: linux-security-module@vger.kernel.org --- fs/afs/super.c | 1 - 1 file changed, 1 deletion(-) diff --git a/fs/afs/super.c b/fs/afs/super.c index 488641b1a418d..d9a6036b70b9f 100644 --- a/fs/afs/super.c +++ b/fs/afs/super.c @@ -448,7 +448,6 @@ static int afs_fill_super(struct super_block *sb, struct afs_fs_context *ctx) /* allocate the root inode and dentry */ if (as->dyn_root) { inode = afs_iget_pseudo_dir(sb, true); - sb->s_flags |= SB_RDONLY; } else { sprintf(sb->s_id, "%llu", as->volume->vid); afs_activate_volume(as->volume); -- GitLab From 78f926f72e43e4b974f69688593a9b682089e82a Mon Sep 17 00:00:00 2001 From: David Sterba Date: Thu, 28 Nov 2019 13:02:32 +0100 Subject: [PATCH 0268/1437] btrfs: add Kconfig dependency for BLAKE2B Because the BLAKE2B code went through a different tree, it was not available at the time the btrfs part was merged. Now that the Kconfig symbol exists, add it to the list. Signed-off-by: David Sterba --- fs/btrfs/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/btrfs/Kconfig b/fs/btrfs/Kconfig index 75b6d10c98456..575636f6491ef 100644 --- a/fs/btrfs/Kconfig +++ b/fs/btrfs/Kconfig @@ -7,6 +7,7 @@ config BTRFS_FS select LIBCRC32C select CRYPTO_XXHASH select CRYPTO_SHA256 + select CRYPTO_BLAKE2B select ZLIB_INFLATE select ZLIB_DEFLATE select LZO_COMPRESS -- GitLab From 44a7b6759000ac51b92715579a7bba9e3f9245c2 Mon Sep 17 00:00:00 2001 From: Chuhong Yuan Date: Fri, 6 Dec 2019 09:24:26 +0800 Subject: [PATCH 0269/1437] RDMA/cma: add missed unregister_pernet_subsys in init failure The driver forgets to call unregister_pernet_subsys() in the error path of cma_init(). Add the missed call to fix it. Fixes: 4be74b42a6d0 ("IB/cma: Separate port allocation to network namespaces") Signed-off-by: Chuhong Yuan Reviewed-by: Parav Pandit Link: https://lore.kernel.org/r/20191206012426.12744-1-hslester96@gmail.com Signed-off-by: Doug Ledford --- drivers/infiniband/core/cma.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/infiniband/core/cma.c b/drivers/infiniband/core/cma.c index 25f2b70fd8efb..43a6f07e0afe2 100644 --- a/drivers/infiniband/core/cma.c +++ b/drivers/infiniband/core/cma.c @@ -4763,6 +4763,7 @@ err_ib: err: unregister_netdevice_notifier(&cma_nb); ib_sa_unregister_client(&sa_client); + unregister_pernet_subsys(&cma_pernet_operations); err_wq: destroy_workqueue(cma_wq); return ret; -- GitLab From 093c3f94e922d83a734fc4da08cc5814990f32c6 Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Thu, 5 Dec 2019 16:01:20 -0800 Subject: [PATCH 0270/1437] ARM: dts: BCM5301X: Fix MDIO node address/size cells The MDIO node on BCM5301X had an reversed #address-cells and #size-cells properties, correct those, silencing checker warnings: .../linux/arch/arm/boot/dts/bcm4708-asus-rt-ac56u.dt.yaml: mdio@18003000: #address-cells:0:0: 1 was expected Reported-by: Simon Horman Fixes: 23f1eca6d59b ("ARM: dts: BCM5301X: Specify MDIO bus in the DT") Signed-off-by: Florian Fainelli --- arch/arm/boot/dts/bcm5301x.dtsi | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/arm/boot/dts/bcm5301x.dtsi b/arch/arm/boot/dts/bcm5301x.dtsi index 372dc1eb88a0e..2d9b4dd058307 100644 --- a/arch/arm/boot/dts/bcm5301x.dtsi +++ b/arch/arm/boot/dts/bcm5301x.dtsi @@ -353,8 +353,8 @@ mdio: mdio@18003000 { compatible = "brcm,iproc-mdio"; reg = <0x18003000 0x8>; - #size-cells = <1>; - #address-cells = <0>; + #size-cells = <0>; + #address-cells = <1>; }; mdio-bus-mux@18003000 { -- GitLab From f394722fb0d0f701119368959d7cd0ecbc46363a Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Sat, 7 Dec 2019 12:23:21 -0800 Subject: [PATCH 0271/1437] neighbour: remove neigh_cleanup() method neigh_cleanup() has not been used for seven years, and was a wrong design. Messing with shared pointer in bond_neigh_init() without proper memory barriers would at least trigger syzbot complains eventually. It is time to remove this stuff. Fixes: b63b70d87741 ("IPoIB: Use a private hash table for path lookup in xmit path") Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- drivers/net/bonding/bond_main.c | 8 -------- include/net/neighbour.h | 1 - net/core/neighbour.c | 3 --- 3 files changed, 12 deletions(-) diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index fcb7c2f7f001b..6c72623e48e54 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -3712,18 +3712,10 @@ static int bond_neigh_init(struct neighbour *n) return 0; parms.neigh_setup = NULL; - parms.neigh_cleanup = NULL; ret = slave_ops->ndo_neigh_setup(slave->dev, &parms); if (ret) return ret; - /* Assign slave's neigh_cleanup to neighbour in case cleanup is called - * after the last slave has been detached. Assumes that all slaves - * utilize the same neigh_cleanup (true at this writing as only user - * is ipoib). - */ - n->parms->neigh_cleanup = parms.neigh_cleanup; - if (!parms.neigh_setup) return 0; diff --git a/include/net/neighbour.h b/include/net/neighbour.h index 6ad9ad47a9c54..8ec77bfdc1a41 100644 --- a/include/net/neighbour.h +++ b/include/net/neighbour.h @@ -72,7 +72,6 @@ struct neigh_parms { struct net_device *dev; struct list_head list; int (*neigh_setup)(struct neighbour *); - void (*neigh_cleanup)(struct neighbour *); struct neigh_table *tbl; void *sysctl_table; diff --git a/net/core/neighbour.c b/net/core/neighbour.c index 652da63690376..920784a9b7ffa 100644 --- a/net/core/neighbour.c +++ b/net/core/neighbour.c @@ -98,9 +98,6 @@ static int neigh_blackhole(struct neighbour *neigh, struct sk_buff *skb) static void neigh_cleanup_and_release(struct neighbour *neigh) { - if (neigh->parms->neigh_cleanup) - neigh->parms->neigh_cleanup(neigh); - trace_neigh_cleanup_and_release(neigh, 0); __neigh_notify(neigh, RTM_DELNEIGH, 0, 0); call_netevent_notifiers(NETEVENT_NEIGH_UPDATE, neigh); -- GitLab From 9e99bfefdbce2e23ef37487a3bcb4adf90a791d1 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Sat, 7 Dec 2019 14:10:34 -0800 Subject: [PATCH 0272/1437] bonding: fix bond_neigh_init() 1) syzbot reported an uninit-value in bond_neigh_setup() [1] bond_neigh_setup() uses a temporary on-stack 'struct neigh_parms parms', but only clears parms.neigh_setup field. A stacked bonding device would then enter bond_neigh_setup() and read garbage from parms->dev. If we get really unlucky and garbage is matching @dev, then we could recurse and eventually crash. Let's make sure the whole structure is cleared to avoid surprises. 2) bond_neigh_setup() can be called while another cpu manipulates the master device, removing or adding a slave. We need at least rcu protection to prevent use-after-free. Note: Prior code does not support a stack of bonding devices, this patch does not attempt to fix this, and leave a comment instead. [1] BUG: KMSAN: uninit-value in bond_neigh_setup+0xa4/0x110 drivers/net/bonding/bond_main.c:3655 CPU: 0 PID: 11256 Comm: syz-executor.0 Not tainted 5.4.0-rc8-syzkaller #0 Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 01/01/2011 Call Trace: __dump_stack lib/dump_stack.c:77 [inline] dump_stack+0x1c9/0x220 lib/dump_stack.c:118 kmsan_report+0x128/0x220 mm/kmsan/kmsan_report.c:108 __msan_warning+0x57/0xa0 mm/kmsan/kmsan_instr.c:245 bond_neigh_setup+0xa4/0x110 drivers/net/bonding/bond_main.c:3655 bond_neigh_init+0x216/0x4b0 drivers/net/bonding/bond_main.c:3626 ___neigh_create+0x169e/0x2c40 net/core/neighbour.c:613 __neigh_create+0xbd/0xd0 net/core/neighbour.c:674 ip6_finish_output2+0x149a/0x2670 net/ipv6/ip6_output.c:113 __ip6_finish_output+0x83d/0x8f0 net/ipv6/ip6_output.c:142 ip6_finish_output+0x2db/0x420 net/ipv6/ip6_output.c:152 NF_HOOK_COND include/linux/netfilter.h:294 [inline] ip6_output+0x5d3/0x720 net/ipv6/ip6_output.c:175 dst_output include/net/dst.h:436 [inline] NF_HOOK include/linux/netfilter.h:305 [inline] mld_sendpack+0xebd/0x13d0 net/ipv6/mcast.c:1682 mld_send_cr net/ipv6/mcast.c:1978 [inline] mld_ifc_timer_expire+0x116b/0x1680 net/ipv6/mcast.c:2477 call_timer_fn+0x232/0x530 kernel/time/timer.c:1404 expire_timers kernel/time/timer.c:1449 [inline] __run_timers+0xd60/0x1270 kernel/time/timer.c:1773 run_timer_softirq+0x2d/0x50 kernel/time/timer.c:1786 __do_softirq+0x4a1/0x83a kernel/softirq.c:293 invoke_softirq kernel/softirq.c:375 [inline] irq_exit+0x230/0x280 kernel/softirq.c:416 exiting_irq+0xe/0x10 arch/x86/include/asm/apic.h:536 smp_apic_timer_interrupt+0x48/0x70 arch/x86/kernel/apic/apic.c:1138 apic_timer_interrupt+0x2e/0x40 arch/x86/entry/entry_64.S:835 RIP: 0010:kmsan_free_page+0x18d/0x1c0 mm/kmsan/kmsan_shadow.c:439 Code: 4c 89 ff 44 89 f6 e8 82 0d ee ff 65 ff 0d 9f 26 3b 60 65 8b 05 98 26 3b 60 85 c0 75 24 e8 5b f6 35 ff 4c 89 6d d0 ff 75 d0 9d <48> 83 c4 10 5b 41 5c 41 5d 41 5e 41 5f 5d c3 0f 0b 0f 0b 0f 0b 0f RSP: 0018:ffffb328034af818 EFLAGS: 00000246 ORIG_RAX: ffffffffffffff13 RAX: 0000000000000000 RBX: ffffe2d7471f8360 RCX: 0000000000000000 RDX: ffffffffadea7000 RSI: 0000000000000004 RDI: ffff93496fcda104 RBP: ffffb328034af850 R08: ffff934a47e86d00 R09: ffff93496fc41900 R10: 0000000000000000 R11: 0000000000000000 R12: 0000000000000001 R13: 0000000000000246 R14: 0000000000000000 R15: ffffe2d7472225c0 free_pages_prepare mm/page_alloc.c:1138 [inline] free_pcp_prepare mm/page_alloc.c:1230 [inline] free_unref_page_prepare+0x1d9/0x770 mm/page_alloc.c:3025 free_unref_page mm/page_alloc.c:3074 [inline] free_the_page mm/page_alloc.c:4832 [inline] __free_pages+0x154/0x230 mm/page_alloc.c:4840 __vunmap+0xdac/0xf20 mm/vmalloc.c:2277 __vfree mm/vmalloc.c:2325 [inline] vfree+0x7c/0x170 mm/vmalloc.c:2355 copy_entries_to_user net/ipv6/netfilter/ip6_tables.c:883 [inline] get_entries net/ipv6/netfilter/ip6_tables.c:1041 [inline] do_ip6t_get_ctl+0xfa4/0x1030 net/ipv6/netfilter/ip6_tables.c:1709 nf_sockopt net/netfilter/nf_sockopt.c:104 [inline] nf_getsockopt+0x481/0x4e0 net/netfilter/nf_sockopt.c:122 ipv6_getsockopt+0x264/0x510 net/ipv6/ipv6_sockglue.c:1400 tcp_getsockopt+0x1c6/0x1f0 net/ipv4/tcp.c:3688 sock_common_getsockopt+0x13f/0x180 net/core/sock.c:3110 __sys_getsockopt+0x533/0x7b0 net/socket.c:2129 __do_sys_getsockopt net/socket.c:2144 [inline] __se_sys_getsockopt+0xe1/0x100 net/socket.c:2141 __x64_sys_getsockopt+0x62/0x80 net/socket.c:2141 do_syscall_64+0xb6/0x160 arch/x86/entry/common.c:291 entry_SYSCALL_64_after_hwframe+0x44/0xa9 RIP: 0033:0x45d20a Code: b8 34 01 00 00 0f 05 48 3d 01 f0 ff ff 0f 83 8d 8b fb ff c3 66 2e 0f 1f 84 00 00 00 00 00 66 90 49 89 ca b8 37 00 00 00 0f 05 <48> 3d 01 f0 ff ff 0f 83 6a 8b fb ff c3 66 0f 1f 84 00 00 00 00 00 RSP: 002b:0000000000a6f618 EFLAGS: 00000212 ORIG_RAX: 0000000000000037 RAX: ffffffffffffffda RBX: 0000000000a6f640 RCX: 000000000045d20a RDX: 0000000000000041 RSI: 0000000000000029 RDI: 0000000000000003 RBP: 0000000000717cc0 R08: 0000000000a6f63c R09: 0000000000004000 R10: 0000000000a6f740 R11: 0000000000000212 R12: 0000000000000003 R13: 0000000000000000 R14: 0000000000000029 R15: 0000000000715b00 Local variable description: ----parms@bond_neigh_init Variable was created at: bond_neigh_init+0x8c/0x4b0 drivers/net/bonding/bond_main.c:3617 bond_neigh_init+0x8c/0x4b0 drivers/net/bonding/bond_main.c:3617 Fixes: 9918d5bf329d ("bonding: modify only neigh_parms owned by us") Fixes: 234bcf8a499e ("net/bonding: correctly proxy slave neigh param setup ndo function") Signed-off-by: Eric Dumazet Reported-by: syzbot Cc: Jay Vosburgh Cc: Veaceslav Falico Cc: Andy Gospodarek Signed-off-by: David S. Miller --- drivers/net/bonding/bond_main.c | 31 +++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index 6c72623e48e54..041aa9649dfc7 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -3702,24 +3702,35 @@ static int bond_neigh_init(struct neighbour *n) const struct net_device_ops *slave_ops; struct neigh_parms parms; struct slave *slave; - int ret; + int ret = 0; - slave = bond_first_slave(bond); + rcu_read_lock(); + slave = bond_first_slave_rcu(bond); if (!slave) - return 0; + goto out; slave_ops = slave->dev->netdev_ops; if (!slave_ops->ndo_neigh_setup) - return 0; + goto out; - parms.neigh_setup = NULL; + /* TODO: find another way [1] to implement this. + * Passing a zeroed structure is fragile, + * but at least we do not pass garbage. + * + * [1] One way would be that ndo_neigh_setup() never touch + * struct neigh_parms, but propagate the new neigh_setup() + * back to ___neigh_create() / neigh_parms_alloc() + */ + memset(&parms, 0, sizeof(parms)); ret = slave_ops->ndo_neigh_setup(slave->dev, &parms); - if (ret) - return ret; - if (!parms.neigh_setup) - return 0; + if (ret) + goto out; - return parms.neigh_setup(n); + if (parms.neigh_setup) + ret = parms.neigh_setup(n); +out: + rcu_read_unlock(); + return ret; } /* The bonding ndo_neigh_setup is called at init time beofre any -- GitLab From af3ea3c126ac677dbeae1b93868f69f928bccc13 Mon Sep 17 00:00:00 2001 From: Lukasz Luba Date: Mon, 9 Dec 2019 09:39:07 +0000 Subject: [PATCH 0273/1437] MAINTAINERS: Update Lukasz Luba's email address Update Lukasz Luba's email address to @arm.com in MAINTAINERS and map it correctly in .mailmap file. Signed-off-by: Lukasz Luba Signed-off-by: Krzysztof Kozlowski --- .mailmap | 1 + MAINTAINERS | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.mailmap b/.mailmap index c24773db04a7a..6adff2db70765 100644 --- a/.mailmap +++ b/.mailmap @@ -152,6 +152,7 @@ Linus Lüssing Linus Lüssing Li Yang Li Yang +Lukasz Luba Maciej W. Rozycki Marc Zyngier Marcin Nowakowski diff --git a/MAINTAINERS b/MAINTAINERS index bd5847e802def..e3626bacea408 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4999,7 +4999,7 @@ F: include/linux/dma-mapping.h F: include/linux/dma-noncoherent.h DMC FREQUENCY DRIVER FOR SAMSUNG EXYNOS5422 -M: Lukasz Luba +M: Lukasz Luba L: linux-pm@vger.kernel.org L: linux-samsung-soc@vger.kernel.org S: Maintained -- GitLab From fd1baf6ca2ea3550ea47f2bb0bdcf34ec764a779 Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Tue, 26 Nov 2019 17:34:16 +0900 Subject: [PATCH 0274/1437] selftests/ftrace: Fix to check the existence of set_ftrace_filter If we run ftracetest on the kernel with CONFIG_DYNAMIC_FTRACE=n, there is no set_ftrace_filter and all test cases are failed, because reset_ftrace_filter() returns an error. Let's check whether set_ftrace_filter exists in reset_ftrace_filter() and clean up only set_ftrace_notrace in initialize_ftrace(). Signed-off-by: Masami Hiramatsu Reviewed-by: Steven Rostedt (VMware) Signed-off-by: Shuah Khan --- tools/testing/selftests/ftrace/test.d/functions | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tools/testing/selftests/ftrace/test.d/functions b/tools/testing/selftests/ftrace/test.d/functions index 86986c4bba549..5d4550591ff9f 100644 --- a/tools/testing/selftests/ftrace/test.d/functions +++ b/tools/testing/selftests/ftrace/test.d/functions @@ -46,6 +46,9 @@ reset_events_filter() { # reset all current setting filters } reset_ftrace_filter() { # reset all triggers in set_ftrace_filter + if [ ! -f set_ftrace_filter ]; then + return 0 + fi echo > set_ftrace_filter grep -v '^#' set_ftrace_filter | while read t; do tr=`echo $t | cut -d: -f2` @@ -93,7 +96,7 @@ initialize_ftrace() { # Reset ftrace to initial-state disable_events [ -f set_event_pid ] && echo > set_event_pid [ -f set_ftrace_pid ] && echo > set_ftrace_pid - [ -f set_ftrace_filter ] && echo | tee set_ftrace_* + [ -f set_ftrace_notrace ] && echo > set_ftrace_notrace [ -f set_graph_function ] && echo | tee set_graph_* [ -f stack_trace_filter ] && echo > stack_trace_filter [ -f kprobe_events ] && echo > kprobe_events -- GitLab From 25deae098e748d8d36bc35129a66734b8f6925c9 Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Wed, 27 Nov 2019 08:42:21 +0900 Subject: [PATCH 0275/1437] selftests/ftrace: Fix ftrace test cases to check unsupported Since dynamic function tracer can be disabled, set_ftrace_filter can be disappeared. Test cases which depends on it, must check whether the set_ftrace_filter exists or not before testing and if not, return as unsupported. Also, if the function tracer itself is disabled, we can not set "function" to current_tracer. Test cases must check it before testing, and return as unsupported. Signed-off-by: Masami Hiramatsu Reviewed-by: Steven Rostedt (VMware) Signed-off-by: Shuah Khan --- .../selftests/ftrace/test.d/ftrace/func-filter-stacktrace.tc | 2 ++ tools/testing/selftests/ftrace/test.d/ftrace/func_cpumask.tc | 5 +++++ 2 files changed, 7 insertions(+) diff --git a/tools/testing/selftests/ftrace/test.d/ftrace/func-filter-stacktrace.tc b/tools/testing/selftests/ftrace/test.d/ftrace/func-filter-stacktrace.tc index 36fb59f886ea8..1a52f2883fe02 100644 --- a/tools/testing/selftests/ftrace/test.d/ftrace/func-filter-stacktrace.tc +++ b/tools/testing/selftests/ftrace/test.d/ftrace/func-filter-stacktrace.tc @@ -3,6 +3,8 @@ # description: ftrace - stacktrace filter command # flags: instance +[ ! -f set_ftrace_filter ] && exit_unsupported + echo _do_fork:stacktrace >> set_ftrace_filter grep -q "_do_fork:stacktrace:unlimited" set_ftrace_filter diff --git a/tools/testing/selftests/ftrace/test.d/ftrace/func_cpumask.tc b/tools/testing/selftests/ftrace/test.d/ftrace/func_cpumask.tc index 86a1f07ef2ca7..71fa3f49e35e2 100644 --- a/tools/testing/selftests/ftrace/test.d/ftrace/func_cpumask.tc +++ b/tools/testing/selftests/ftrace/test.d/ftrace/func_cpumask.tc @@ -15,6 +15,11 @@ if [ $NP -eq 1 ] ;then exit_unresolved fi +if ! grep -q "function" available_tracers ; then + echo "Function trace is not enabled" + exit_unsupported +fi + ORIG_CPUMASK=`cat tracing_cpumask` do_reset() { -- GitLab From ba1b9c5048e43716921abe3a1db19cebebf4a5f5 Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Tue, 26 Nov 2019 17:34:33 +0900 Subject: [PATCH 0276/1437] selftests/ftrace: Do not to use absolute debugfs path Use relative path to trigger file instead of absolute debugfs path, because if the user uses tracefs instead of debugfs, it can be mounted at /sys/kernel/tracing. Anyway, since the ftracetest is designed to be run at the tracing directory, user doesn't need to use absolute path. Signed-off-by: Masami Hiramatsu Reviewed-by: Steven Rostedt (VMware) Signed-off-by: Shuah Khan --- .../test.d/trigger/inter-event/trigger-action-hist-xfail.tc | 4 ++-- .../trigger/inter-event/trigger-onchange-action-hist.tc | 2 +- .../trigger/inter-event/trigger-snapshot-action-hist.tc | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/tools/testing/selftests/ftrace/test.d/trigger/inter-event/trigger-action-hist-xfail.tc b/tools/testing/selftests/ftrace/test.d/trigger/inter-event/trigger-action-hist-xfail.tc index 1221240f8cf6e..3f2aee115f6e2 100644 --- a/tools/testing/selftests/ftrace/test.d/trigger/inter-event/trigger-action-hist-xfail.tc +++ b/tools/testing/selftests/ftrace/test.d/trigger/inter-event/trigger-action-hist-xfail.tc @@ -21,10 +21,10 @@ grep -q "snapshot()" README || exit_unsupported # version issue echo "Test expected snapshot action failure" -echo 'hist:keys=comm:onmatch(sched.sched_wakeup).snapshot()' >> /sys/kernel/debug/tracing/events/sched/sched_waking/trigger && exit_fail +echo 'hist:keys=comm:onmatch(sched.sched_wakeup).snapshot()' >> events/sched/sched_waking/trigger && exit_fail echo "Test expected save action failure" -echo 'hist:keys=comm:onmatch(sched.sched_wakeup).save(comm,prio)' >> /sys/kernel/debug/tracing/events/sched/sched_waking/trigger && exit_fail +echo 'hist:keys=comm:onmatch(sched.sched_wakeup).save(comm,prio)' >> events/sched/sched_waking/trigger && exit_fail exit_xfail diff --git a/tools/testing/selftests/ftrace/test.d/trigger/inter-event/trigger-onchange-action-hist.tc b/tools/testing/selftests/ftrace/test.d/trigger/inter-event/trigger-onchange-action-hist.tc index 064a284e4e759..c80007aa9f862 100644 --- a/tools/testing/selftests/ftrace/test.d/trigger/inter-event/trigger-onchange-action-hist.tc +++ b/tools/testing/selftests/ftrace/test.d/trigger/inter-event/trigger-onchange-action-hist.tc @@ -16,7 +16,7 @@ grep -q "onchange(var)" README || exit_unsupported # version issue echo "Test onchange action" -echo 'hist:keys=comm:newprio=prio:onchange($newprio).save(comm,prio) if comm=="ping"' >> /sys/kernel/debug/tracing/events/sched/sched_waking/trigger +echo 'hist:keys=comm:newprio=prio:onchange($newprio).save(comm,prio) if comm=="ping"' >> events/sched/sched_waking/trigger ping $LOCALHOST -c 3 nice -n 1 ping $LOCALHOST -c 3 diff --git a/tools/testing/selftests/ftrace/test.d/trigger/inter-event/trigger-snapshot-action-hist.tc b/tools/testing/selftests/ftrace/test.d/trigger/inter-event/trigger-snapshot-action-hist.tc index 18fff69fc4333..f546c1b66a9b1 100644 --- a/tools/testing/selftests/ftrace/test.d/trigger/inter-event/trigger-snapshot-action-hist.tc +++ b/tools/testing/selftests/ftrace/test.d/trigger/inter-event/trigger-snapshot-action-hist.tc @@ -23,9 +23,9 @@ grep -q "snapshot()" README || exit_unsupported # version issue echo "Test snapshot action" -echo 1 > /sys/kernel/debug/tracing/events/sched/enable +echo 1 > events/sched/enable -echo 'hist:keys=comm:newprio=prio:onchange($newprio).save(comm,prio):onchange($newprio).snapshot() if comm=="ping"' >> /sys/kernel/debug/tracing/events/sched/sched_waking/trigger +echo 'hist:keys=comm:newprio=prio:onchange($newprio).save(comm,prio):onchange($newprio).snapshot() if comm=="ping"' >> events/sched/sched_waking/trigger ping $LOCALHOST -c 3 nice -n 1 ping $LOCALHOST -c 3 -- GitLab From 5cc6c8d4a99d0ee4d5466498e258e593df1d3eb6 Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Tue, 26 Nov 2019 17:34:42 +0900 Subject: [PATCH 0277/1437] selftests/ftrace: Fix multiple kprobe testcase Fix multiple kprobe event testcase to work it correctly. There are 2 bugfixes. - Since `wc -l FILE` returns not only line number but also FILE filename, following "if" statement always failed. Fix this bug by replacing it with 'cat FILE | wc -l' - Since "while do-done loop" block with pipeline becomes a subshell, $N local variable is not update outside of the loop. Fix this bug by using actual target number (256) instead of $N. Signed-off-by: Masami Hiramatsu Reviewed-by: Steven Rostedt (VMware) Signed-off-by: Shuah Khan --- .../selftests/ftrace/test.d/kprobe/multiple_kprobes.tc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/testing/selftests/ftrace/test.d/kprobe/multiple_kprobes.tc b/tools/testing/selftests/ftrace/test.d/kprobe/multiple_kprobes.tc index 5862eee91e1d9..6e3dbe5f96b7b 100644 --- a/tools/testing/selftests/ftrace/test.d/kprobe/multiple_kprobes.tc +++ b/tools/testing/selftests/ftrace/test.d/kprobe/multiple_kprobes.tc @@ -20,9 +20,9 @@ while read i; do test $N -eq 256 && break done -L=`wc -l kprobe_events` -if [ $L -ne $N ]; then - echo "The number of kprobes events ($L) is not $N" +L=`cat kprobe_events | wc -l` +if [ $L -ne 256 ]; then + echo "The number of kprobes events ($L) is not 256" exit_fail fi -- GitLab From be12252212fa3dfed6e75112865095c484c0ce87 Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Thu, 5 Dec 2019 21:20:58 +0900 Subject: [PATCH 0278/1437] selftests: safesetid: Move link library to LDLIBS Move -lcap to LDLIBS from CFLAGS because it is a library to be linked. Without this, safesetid failed to build with link error as below. ---- /usr/bin/ld: /tmp/ccL8rZHT.o: in function `drop_caps': safesetid-test.c:(.text+0xe7): undefined reference to `cap_get_proc' /usr/bin/ld: safesetid-test.c:(.text+0x107): undefined reference to `cap_set_flag' /usr/bin/ld: safesetid-test.c:(.text+0x10f): undefined reference to `cap_set_proc' /usr/bin/ld: safesetid-test.c:(.text+0x117): undefined reference to `cap_free' /usr/bin/ld: safesetid-test.c:(.text+0x136): undefined reference to `cap_clear' collect2: error: ld returned 1 exit status ---- Fixes: c67e8ec03f3f ("LSM: SafeSetID: add selftest") Signed-off-by: Masami Hiramatsu Signed-off-by: Shuah Khan --- tools/testing/selftests/safesetid/Makefile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tools/testing/selftests/safesetid/Makefile b/tools/testing/selftests/safesetid/Makefile index 98da7a5047375..cac42cd36a1be 100644 --- a/tools/testing/selftests/safesetid/Makefile +++ b/tools/testing/selftests/safesetid/Makefile @@ -1,6 +1,7 @@ # SPDX-License-Identifier: GPL-2.0 # Makefile for mount selftests. -CFLAGS = -Wall -lcap -O2 +CFLAGS = -Wall -O2 +LDLIBS = -lcap TEST_PROGS := run_tests.sh TEST_GEN_FILES := safesetid-test -- GitLab From 295c4e21cf27ac9af542140e3e797df9e0cf7b5f Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Thu, 5 Dec 2019 21:21:07 +0900 Subject: [PATCH 0279/1437] selftests: safesetid: Check the return value of setuid/setgid MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Check the return value of setuid() and setgid(). This fixes the following warnings and improves test result. safesetid-test.c: In function ‘main’: safesetid-test.c:294:2: warning: ignoring return value of ‘setuid’, declared with attribute warn_unused_result [-Wunused-result] setuid(NO_POLICY_USER); ^~~~~~~~~~~~~~~~~~~~~~ safesetid-test.c:295:2: warning: ignoring return value of ‘setgid’, declared with attribute warn_unused_result [-Wunused-result] setgid(NO_POLICY_USER); ^~~~~~~~~~~~~~~~~~~~~~ safesetid-test.c:309:2: warning: ignoring return value of ‘setuid’, declared with attribute warn_unused_result [-Wunused-result] setuid(RESTRICTED_PARENT); ^~~~~~~~~~~~~~~~~~~~~~~~~ safesetid-test.c:310:2: warning: ignoring return value of ‘setgid’, declared with attribute warn_unused_result [-Wunused-result] setgid(RESTRICTED_PARENT); ^~~~~~~~~~~~~~~~~~~~~~~~~ safesetid-test.c: In function ‘test_setuid’: safesetid-test.c:216:3: warning: ignoring return value of ‘setuid’, declared with attribute warn_unused_result [-Wunused-result] setuid(child_uid); ^~~~~~~~~~~~~~~~~ Fixes: c67e8ec03f3f ("LSM: SafeSetID: add selftest") Signed-off-by: Masami Hiramatsu Signed-off-by: Shuah Khan --- .../testing/selftests/safesetid/safesetid-test.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/tools/testing/selftests/safesetid/safesetid-test.c b/tools/testing/selftests/safesetid/safesetid-test.c index 8f40c6ecdad18..0c4d50644c131 100644 --- a/tools/testing/selftests/safesetid/safesetid-test.c +++ b/tools/testing/selftests/safesetid/safesetid-test.c @@ -213,7 +213,8 @@ static void test_setuid(uid_t child_uid, bool expect_success) } if (cpid == 0) { /* Code executed by child */ - setuid(child_uid); + if (setuid(child_uid) < 0) + exit(EXIT_FAILURE); if (getuid() == child_uid) exit(EXIT_SUCCESS); else @@ -291,8 +292,10 @@ int main(int argc, char **argv) // First test to make sure we can write userns mappings from a user // that doesn't have any restrictions (as long as it has CAP_SETUID); - setuid(NO_POLICY_USER); - setgid(NO_POLICY_USER); + if (setuid(NO_POLICY_USER) < 0) + die("Error with set uid(%d)\n", NO_POLICY_USER); + if (setgid(NO_POLICY_USER) < 0) + die("Error with set gid(%d)\n", NO_POLICY_USER); // Take away all but setid caps drop_caps(true); @@ -306,8 +309,10 @@ int main(int argc, char **argv) die("test_userns failed when it should work\n"); } - setuid(RESTRICTED_PARENT); - setgid(RESTRICTED_PARENT); + if (setuid(RESTRICTED_PARENT) < 0) + die("Error with set uid(%d)\n", RESTRICTED_PARENT); + if (setgid(RESTRICTED_PARENT) < 0) + die("Error with set gid(%d)\n", RESTRICTED_PARENT); test_setuid(ROOT_USER, false); test_setuid(ALLOWED_CHILD1, true); -- GitLab From 8ef1ec0ca32c6f8a87f5b4c24b1db26da67c5609 Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Thu, 5 Dec 2019 21:21:16 +0900 Subject: [PATCH 0280/1437] selftests: safesetid: Fix Makefile to set correct test program Fix Makefile to set safesetid-test.sh to TEST_PROGS instead of non existing run_tests.sh. Without this fix, I got following error. ---- TAP version 13 1..1 # selftests: safesetid: run_tests.sh # Warning: file run_tests.sh is missing! not ok 1 selftests: safesetid: run_tests.sh ---- Fixes: c67e8ec03f3f ("LSM: SafeSetID: add selftest") Signed-off-by: Masami Hiramatsu Signed-off-by: Shuah Khan --- tools/testing/selftests/safesetid/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/testing/selftests/safesetid/Makefile b/tools/testing/selftests/safesetid/Makefile index cac42cd36a1be..fa02c4d5ec13c 100644 --- a/tools/testing/selftests/safesetid/Makefile +++ b/tools/testing/selftests/safesetid/Makefile @@ -3,7 +3,7 @@ CFLAGS = -Wall -O2 LDLIBS = -lcap -TEST_PROGS := run_tests.sh +TEST_PROGS := safesetid-test.sh TEST_GEN_FILES := safesetid-test include ../lib.mk -- GitLab From 702600eef73033ddd4eafcefcbb6560f3e3a90f7 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 6 Dec 2019 16:26:00 +0100 Subject: [PATCH 0281/1437] lib: raid6: fix awk build warnings Newer versions of awk spit out these fun warnings: awk: ../lib/raid6/unroll.awk:16: warning: regexp escape sequence `\#' is not a known regexp operator As commit 700c1018b86d ("x86/insn: Fix awk regexp warnings") showed, it turns out that there are a number of awk strings that do not need to be escaped and newer versions of awk now warn about this. Fix the string up so that no warning is produced. The exact same kernel module gets created before and after this patch, showing that it wasn't needed. Signed-off-by: Greg Kroah-Hartman Link: https://lore.kernel.org/r/20191206152600.GA75093@kroah.com Signed-off-by: Greg Kroah-Hartman --- lib/raid6/unroll.awk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/raid6/unroll.awk b/lib/raid6/unroll.awk index c6aa03631df86..0809805a7e231 100644 --- a/lib/raid6/unroll.awk +++ b/lib/raid6/unroll.awk @@ -13,7 +13,7 @@ BEGIN { for (i = 0; i < rep; ++i) { tmp = $0 gsub(/\$\$/, i, tmp) - gsub(/\$\#/, n, tmp) + gsub(/\$#/, n, tmp) gsub(/\$\*/, "$", tmp) print tmp } -- GitLab From 94fd07925577caaaec9e0efd60c173959600de13 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Thu, 5 Dec 2019 21:04:22 +0100 Subject: [PATCH 0282/1437] MAINTAINERS: Include Samsung SoC serial driver in Samsung SoC entry Samsung SoC (S3C, S5P and Exynos) serial driver does not have dedicated reviewing person so some patches might be missed be Samsung-related folks (e.g. not even reaching Samsung SoC mailing list). Include them in generic Samsung SoC maintainer entry to provide some level of reviewing and care. This will not change handling of patches (via serial tree). Cc: Greg Kroah-Hartman Cc: Jiri Slaby Signed-off-by: Krzysztof Kozlowski --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index e3626bacea408..950ac3435dcd0 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2272,6 +2272,7 @@ F: drivers/*/*s3c64xx* F: drivers/*/*s5pv210* F: drivers/memory/samsung/ F: drivers/soc/samsung/ +F: drivers/tty/serial/samsung* F: include/linux/soc/samsung/ F: Documentation/arm/samsung/ F: Documentation/devicetree/bindings/arm/samsung/ -- GitLab From a2315d3aea5976acd919d3d3fcf82f752562c25b Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Fri, 6 Dec 2019 13:47:21 +0100 Subject: [PATCH 0283/1437] ARM: exynos_defconfig: Restore debugfs support Commit 9f532d26c75c ("ARM: exynos_defconfig: Trim and reorganize with savedefconfig") removed explicit enable line for CONFIG_DEBUG_FS, because that feature has been selected by other enabled options: CONFIG_TRACING, which in turn had been selected by CONFIG_PERF_EVENTS and CONFIG_PROVE_LOCKING. In meantime, commit 0e4a459f56c3 ("tracing: Remove unnecessary DEBUG_FS dependency") removed the dependency between CONFIG_DEBUG_FS and CONFIG_TRACING, so CONFIG_DEBUG_FS is no longer enabled in default builds. Enable it again explicitly, as debugfs support is essential for various automated testing tools. Signed-off-by: Marek Szyprowski Signed-off-by: Krzysztof Kozlowski --- arch/arm/configs/exynos_defconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm/configs/exynos_defconfig b/arch/arm/configs/exynos_defconfig index e7e4bb5ad8d5c..fde84f123fbb5 100644 --- a/arch/arm/configs/exynos_defconfig +++ b/arch/arm/configs/exynos_defconfig @@ -350,6 +350,7 @@ CONFIG_PRINTK_TIME=y CONFIG_DYNAMIC_DEBUG=y CONFIG_DEBUG_INFO=y CONFIG_MAGIC_SYSRQ=y +CONFIG_DEBUG_FS=y CONFIG_DEBUG_KERNEL=y CONFIG_SOFTLOCKUP_DETECTOR=y # CONFIG_DETECT_HUNG_TASK is not set -- GitLab From 0634a2dc9566de977970c78a06c7ea15cd9c890f Mon Sep 17 00:00:00 2001 From: Nicolas Saenz Julienne Date: Wed, 4 Dec 2019 13:56:33 +0100 Subject: [PATCH 0284/1437] ARM: dts: bcm2711: fix soc's node dma-ranges Raspberry Pi's firmware has a feature to select how much memory to reserve for its GPU called 'gpu_mem'. The possible values go from 16MB to 944MB, with a default of 64MB. This memory resides in the topmost part of the lower 1GB memory area and grows bigger expanding towards the begging of memory. It turns out that with low 'gpu_mem' values (16MB and 32MB) the size of the memory available to the system in the lower 1GB area can outgrow the interconnect's dma-range as its size was selected based on the maximum system memory available given the default gpu_mem configuration. This makes that memory slice unavailable for DMA. And may cause nasty kernel warnings if CMA happens to include it. Change soc's dma-ranges to really reflect it's HW limitation, which is being able to only DMA to the lower 1GB area. Fixes: 7dbe8c62ceeb ("ARM: dts: Add minimal Raspberry Pi 4 support") Signed-off-by: Nicolas Saenz Julienne Reviewed-by: Phil Elwell Signed-off-by: Florian Fainelli --- arch/arm/boot/dts/bcm2711.dtsi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/boot/dts/bcm2711.dtsi b/arch/arm/boot/dts/bcm2711.dtsi index 961bed832755b..e2f6ffb00aa94 100644 --- a/arch/arm/boot/dts/bcm2711.dtsi +++ b/arch/arm/boot/dts/bcm2711.dtsi @@ -43,7 +43,7 @@ <0x7c000000 0x0 0xfc000000 0x02000000>, <0x40000000 0x0 0xff800000 0x00800000>; /* Emulate a contiguous 30-bit address range for DMA */ - dma-ranges = <0xc0000000 0x0 0x00000000 0x3c000000>; + dma-ranges = <0xc0000000 0x0 0x00000000 0x40000000>; /* * This node is the provider for the enable-method for -- GitLab From fac2c2da3596d77c343988bb0d41a8c533b2e73c Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Fri, 6 Dec 2019 10:19:09 -0800 Subject: [PATCH 0285/1437] ARM: dts: Cygnus: Fix MDIO node address/size cells The MDIO node on Cygnus had an reversed #address-cells and #size-cells properties, correct those. Fixes: 40c26d3af60a ("ARM: dts: Cygnus: Add the ethernet switch and ethernet PHY") Reported-by: Simon Horman Reviewed-by: Ray Jui Reviewed-by: Simon Horman Signed-off-by: Florian Fainelli --- arch/arm/boot/dts/bcm-cygnus.dtsi | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/arm/boot/dts/bcm-cygnus.dtsi b/arch/arm/boot/dts/bcm-cygnus.dtsi index 2dac3efc76405..1bc45cfd54538 100644 --- a/arch/arm/boot/dts/bcm-cygnus.dtsi +++ b/arch/arm/boot/dts/bcm-cygnus.dtsi @@ -174,8 +174,8 @@ mdio: mdio@18002000 { compatible = "brcm,iproc-mdio"; reg = <0x18002000 0x8>; - #size-cells = <1>; - #address-cells = <0>; + #size-cells = <0>; + #address-cells = <1>; status = "disabled"; gphy0: ethernet-phy@0 { -- GitLab From 39101b2265440dfe83646a4bfc33015c86f36d71 Mon Sep 17 00:00:00 2001 From: Iurii Zaikin Date: Wed, 27 Nov 2019 10:55:24 -0800 Subject: [PATCH 0286/1437] fs/ext4/inode-test: Fix inode test on 32 bit platforms. Fixes the issue caused by the fact that in C in the expression of the form -1234L only 1234L is the actual literal, the unary minus is an operation applied to the literal. Which means that to express the lower bound for the type one has to negate the upper bound and subtract 1. Original error: Expected test_data[i].expected.tv_sec == timestamp.tv_sec, but test_data[i].expected.tv_sec == -2147483648 timestamp.tv_sec == 2147483648 1901-12-13 Lower bound of 32bit < 0 timestamp, no extra bits: msb:1 lower_bound:1 extra_bits: 0 Expected test_data[i].expected.tv_sec == timestamp.tv_sec, but test_data[i].expected.tv_sec == 2147483648 timestamp.tv_sec == 6442450944 2038-01-19 Lower bound of 32bit <0 timestamp, lo extra sec bit on: msb:1 lower_bound:1 extra_bits: 1 Expected test_data[i].expected.tv_sec == timestamp.tv_sec, but test_data[i].expected.tv_sec == 6442450944 timestamp.tv_sec == 10737418240 2174-02-25 Lower bound of 32bit <0 timestamp, hi extra sec bit on: msb:1 lower_bound:1 extra_bits: 2 not ok 1 - inode_test_xtimestamp_decoding not ok 1 - ext4_inode_test Reported-by: Geert Uytterhoeven Signed-off-by: Iurii Zaikin Tested-by: Geert Uytterhoeven Acked-by: Brendan Higgins Signed-off-by: Shuah Khan --- fs/ext4/inode-test.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/ext4/inode-test.c b/fs/ext4/inode-test.c index 92a9da1774aaa..bbce1c328d855 100644 --- a/fs/ext4/inode-test.c +++ b/fs/ext4/inode-test.c @@ -25,7 +25,7 @@ * For constructing the negative timestamp lower bound value. * binary: 10000000 00000000 00000000 00000000 */ -#define LOWER_MSB_1 (-0x80000000L) +#define LOWER_MSB_1 (-(UPPER_MSB_0) - 1L) /* avoid overflow */ /* * For constructing the negative timestamp upper bound value. * binary: 11111111 11111111 11111111 11111111 -- GitLab From b6f3320b1d5267e7b583a6d0c88dda518101740c Mon Sep 17 00:00:00 2001 From: Xin Long Date: Mon, 9 Dec 2019 13:45:54 +0800 Subject: [PATCH 0287/1437] sctp: fully initialize v4 addr in some functions Syzbot found a crash: BUG: KMSAN: uninit-value in crc32_body lib/crc32.c:112 [inline] BUG: KMSAN: uninit-value in crc32_le_generic lib/crc32.c:179 [inline] BUG: KMSAN: uninit-value in __crc32c_le_base+0x4fa/0xd30 lib/crc32.c:202 Call Trace: crc32_body lib/crc32.c:112 [inline] crc32_le_generic lib/crc32.c:179 [inline] __crc32c_le_base+0x4fa/0xd30 lib/crc32.c:202 chksum_update+0xb2/0x110 crypto/crc32c_generic.c:90 crypto_shash_update+0x4c5/0x530 crypto/shash.c:107 crc32c+0x150/0x220 lib/libcrc32c.c:47 sctp_csum_update+0x89/0xa0 include/net/sctp/checksum.h:36 __skb_checksum+0x1297/0x12a0 net/core/skbuff.c:2640 sctp_compute_cksum include/net/sctp/checksum.h:59 [inline] sctp_packet_pack net/sctp/output.c:528 [inline] sctp_packet_transmit+0x40fb/0x4250 net/sctp/output.c:597 sctp_outq_flush_transports net/sctp/outqueue.c:1146 [inline] sctp_outq_flush+0x1823/0x5d80 net/sctp/outqueue.c:1194 sctp_outq_uncork+0xd0/0xf0 net/sctp/outqueue.c:757 sctp_cmd_interpreter net/sctp/sm_sideeffect.c:1781 [inline] sctp_side_effects net/sctp/sm_sideeffect.c:1184 [inline] sctp_do_sm+0x8fe1/0x9720 net/sctp/sm_sideeffect.c:1155 sctp_primitive_REQUESTHEARTBEAT+0x175/0x1a0 net/sctp/primitive.c:185 sctp_apply_peer_addr_params+0x212/0x1d40 net/sctp/socket.c:2433 sctp_setsockopt_peer_addr_params net/sctp/socket.c:2686 [inline] sctp_setsockopt+0x189bb/0x19090 net/sctp/socket.c:4672 The issue was caused by transport->ipaddr set with uninit addr param, which was passed by: sctp_transport_init net/sctp/transport.c:47 [inline] sctp_transport_new+0x248/0xa00 net/sctp/transport.c:100 sctp_assoc_add_peer+0x5ba/0x2030 net/sctp/associola.c:611 sctp_process_param net/sctp/sm_make_chunk.c:2524 [inline] where 'addr' is set by sctp_v4_from_addr_param(), and it doesn't initialize the padding of addr->v4. Later when calling sctp_make_heartbeat(), hbinfo.daddr(=transport->ipaddr) will become the part of skb, and the issue occurs. This patch is to fix it by initializing the padding of addr->v4 in sctp_v4_from_addr_param(), as well as other functions that do the similar thing, and these functions shouldn't trust that the caller initializes the memory, as Marcelo suggested. Reported-by: syzbot+6dcbfea81cd3d4dd0b02@syzkaller.appspotmail.com Signed-off-by: Xin Long Acked-by: Neil Horman Signed-off-by: David S. Miller --- net/sctp/protocol.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/net/sctp/protocol.c b/net/sctp/protocol.c index fbbf19128c2d7..78af2fcf90cc5 100644 --- a/net/sctp/protocol.c +++ b/net/sctp/protocol.c @@ -227,6 +227,7 @@ static void sctp_v4_from_skb(union sctp_addr *addr, struct sk_buff *skb, sa->sin_port = sh->dest; sa->sin_addr.s_addr = ip_hdr(skb)->daddr; } + memset(sa->sin_zero, 0, sizeof(sa->sin_zero)); } /* Initialize an sctp_addr from a socket. */ @@ -235,6 +236,7 @@ static void sctp_v4_from_sk(union sctp_addr *addr, struct sock *sk) addr->v4.sin_family = AF_INET; addr->v4.sin_port = 0; addr->v4.sin_addr.s_addr = inet_sk(sk)->inet_rcv_saddr; + memset(addr->v4.sin_zero, 0, sizeof(addr->v4.sin_zero)); } /* Initialize sk->sk_rcv_saddr from sctp_addr. */ @@ -257,6 +259,7 @@ static void sctp_v4_from_addr_param(union sctp_addr *addr, addr->v4.sin_family = AF_INET; addr->v4.sin_port = port; addr->v4.sin_addr.s_addr = param->v4.addr.s_addr; + memset(addr->v4.sin_zero, 0, sizeof(addr->v4.sin_zero)); } /* Initialize an address parameter from a sctp_addr and return the length @@ -281,6 +284,7 @@ static void sctp_v4_dst_saddr(union sctp_addr *saddr, struct flowi4 *fl4, saddr->v4.sin_family = AF_INET; saddr->v4.sin_port = port; saddr->v4.sin_addr.s_addr = fl4->saddr; + memset(saddr->v4.sin_zero, 0, sizeof(saddr->v4.sin_zero)); } /* Compare two addresses exactly. */ @@ -303,6 +307,7 @@ static void sctp_v4_inaddr_any(union sctp_addr *addr, __be16 port) addr->v4.sin_family = AF_INET; addr->v4.sin_addr.s_addr = htonl(INADDR_ANY); addr->v4.sin_port = port; + memset(addr->v4.sin_zero, 0, sizeof(addr->v4.sin_zero)); } /* Is this a wildcard address? */ -- GitLab From 62201c00c4679ad8f0730d6d925a5d23651dfad2 Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Mon, 9 Dec 2019 08:55:20 +0200 Subject: [PATCH 0288/1437] mlxsw: spectrum_router: Remove unlikely user-triggerable warning In case the driver vetoes the addition of an IPv6 multipath route, the IPv6 stack will emit delete notifications for the sibling routes that were already added to the FIB trie. Since these siblings are not present in hardware, a warning will be generated. Have the driver ignore notifications for routes it does not have. Fixes: ebee3cad835f ("ipv6: Add IPv6 multipath notifications for add / replace") Signed-off-by: Ido Schimmel Acked-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c index 30bfe3880fafc..08b7e9f964dae 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c @@ -5742,8 +5742,13 @@ static void mlxsw_sp_router_fib6_del(struct mlxsw_sp *mlxsw_sp, if (mlxsw_sp_fib6_rt_should_ignore(rt)) return; + /* Multipath routes are first added to the FIB trie and only then + * notified. If we vetoed the addition, we will get a delete + * notification for a route we do not have. Therefore, do not warn if + * route was not found. + */ fib6_entry = mlxsw_sp_fib6_entry_lookup(mlxsw_sp, rt); - if (WARN_ON(!fib6_entry)) + if (!fib6_entry) return; /* If not all the nexthops are deleted, then only reduce the nexthop -- GitLab From 65cb13986229cec02635a1ecbcd1e2dd18353201 Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Mon, 9 Dec 2019 08:56:34 +0200 Subject: [PATCH 0289/1437] selftests: forwarding: Delete IPv6 address at the end When creating the second host in h2_create(), two addresses are assigned to the interface, but only one is deleted. When running the test twice in a row the following error is observed: $ ./router_bridge_vlan.sh TEST: ping [ OK ] TEST: ping6 [ OK ] TEST: vlan [ OK ] $ ./router_bridge_vlan.sh RTNETLINK answers: File exists TEST: ping [ OK ] TEST: ping6 [ OK ] TEST: vlan [ OK ] Fix this by deleting the address during cleanup. Fixes: 5b1e7f9ebd56 ("selftests: forwarding: Test routed bridge interface") Signed-off-by: Ido Schimmel Signed-off-by: David S. Miller --- tools/testing/selftests/net/forwarding/router_bridge_vlan.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/testing/selftests/net/forwarding/router_bridge_vlan.sh b/tools/testing/selftests/net/forwarding/router_bridge_vlan.sh index fef88eb4b8731..fa6a88c50750d 100755 --- a/tools/testing/selftests/net/forwarding/router_bridge_vlan.sh +++ b/tools/testing/selftests/net/forwarding/router_bridge_vlan.sh @@ -36,7 +36,7 @@ h2_destroy() { ip -6 route del 2001:db8:1::/64 vrf v$h2 ip -4 route del 192.0.2.0/28 vrf v$h2 - simple_if_fini $h2 192.0.2.130/28 + simple_if_fini $h2 192.0.2.130/28 2001:db8:2::2/64 } router_create() -- GitLab From 70efb58bbbc28f0cbd4060c9aa581d79201c8b1a Mon Sep 17 00:00:00 2001 From: Heidi Fahim Date: Mon, 2 Dec 2019 15:53:29 -0800 Subject: [PATCH 0290/1437] kunit: testing kunit: Bug fix in test_run_timeout function Assert in test_run_timeout was not updated with the build_dir argument and caused the following error: AssertionError: Expected call: run_kernel(timeout=3453) Actual call: run_kernel(build_dir=None, timeout=3453) Needed to update kunit_tool_test to reflect this fix https://lkml.org/lkml/2019/9/6/351 Signed-off-by: Heidi Fahim Reviewed-by: SeongJae Park Reviewed-by: Brendan Higgins Tested-by: Brendan Higgins Signed-off-by: Shuah Khan --- tools/testing/kunit/kunit_tool_test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/testing/kunit/kunit_tool_test.py b/tools/testing/kunit/kunit_tool_test.py index 4a12baa0cd4e0..a2a8ea6beae34 100755 --- a/tools/testing/kunit/kunit_tool_test.py +++ b/tools/testing/kunit/kunit_tool_test.py @@ -199,7 +199,7 @@ class KUnitMainTest(unittest.TestCase): timeout = 3453 kunit.main(['run', '--timeout', str(timeout)], self.linux_source_mock) assert self.linux_source_mock.build_reconfig.call_count == 1 - self.linux_source_mock.run_kernel.assert_called_once_with(timeout=timeout) + self.linux_source_mock.run_kernel.assert_called_once_with(build_dir=None, timeout=timeout) self.print_mock.assert_any_call(StrContains('Testing complete.')) if __name__ == '__main__': -- GitLab From e7d7ad0e413f14f191b75d39e2c4a9dbf2683a71 Mon Sep 17 00:00:00 2001 From: Brendan Higgins Date: Tue, 19 Nov 2019 15:38:10 -0800 Subject: [PATCH 0291/1437] Documentation: kunit: fix typos and gramatical errors Fix typos and gramatical errors in the Getting Started and Usage guide for KUnit. Reported-by: Randy Dunlap Link: https://patchwork.kernel.org/patch/11156481/ Reported-by: Rinat Ibragimov Link: https://github.com/google/kunit-docs/issues/1 Signed-off-by: Brendan Higgins Reviewed-by: David Gow Acked-by: Randy Dunlap Signed-off-by: Shuah Khan --- Documentation/dev-tools/kunit/start.rst | 8 ++++---- Documentation/dev-tools/kunit/usage.rst | 24 ++++++++++++------------ 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/Documentation/dev-tools/kunit/start.rst b/Documentation/dev-tools/kunit/start.rst index aeeddfafeea20..75c30c2ea6220 100644 --- a/Documentation/dev-tools/kunit/start.rst +++ b/Documentation/dev-tools/kunit/start.rst @@ -23,7 +23,7 @@ The wrapper can be run with: Creating a kunitconfig ====================== -The Python script is a thin wrapper around Kbuild as such, it needs to be +The Python script is a thin wrapper around Kbuild. As such, it needs to be configured with a ``kunitconfig`` file. This file essentially contains the regular Kernel config, with the specific test targets as well. @@ -59,8 +59,8 @@ If everything worked correctly, you should see the following: followed by a list of tests that are run. All of them should be passing. .. note:: - Because it is building a lot of sources for the first time, the ``Building - kunit kernel`` step may take a while. + Because it is building a lot of sources for the first time, the + ``Building KUnit kernel`` step may take a while. Writing your first test ======================= @@ -159,7 +159,7 @@ Now you can run the test: .. code-block:: bash - ./tools/testing/kunit/kunit.py + ./tools/testing/kunit/kunit.py run You should see the following failure: diff --git a/Documentation/dev-tools/kunit/usage.rst b/Documentation/dev-tools/kunit/usage.rst index c6e69634e274b..b9a065ab681ee 100644 --- a/Documentation/dev-tools/kunit/usage.rst +++ b/Documentation/dev-tools/kunit/usage.rst @@ -16,7 +16,7 @@ Organization of this document ============================= This document is organized into two main sections: Testing and Isolating -Behavior. The first covers what a unit test is and how to use KUnit to write +Behavior. The first covers what unit tests are and how to use KUnit to write them. The second covers how to use KUnit to isolate code and make it possible to unit test code that was otherwise un-unit-testable. @@ -174,13 +174,13 @@ Test Suites ~~~~~~~~~~~ Now obviously one unit test isn't very helpful; the power comes from having -many test cases covering all of your behaviors. Consequently it is common to -have many *similar* tests; in order to reduce duplication in these closely -related tests most unit testing frameworks provide the concept of a *test -suite*, in KUnit we call it a *test suite*; all it is is just a collection of -test cases for a unit of code with a set up function that gets invoked before -every test cases and then a tear down function that gets invoked after every -test case completes. +many test cases covering all of a unit's behaviors. Consequently it is common +to have many *similar* tests; in order to reduce duplication in these closely +related tests most unit testing frameworks - including KUnit - provide the +concept of a *test suite*. A *test suite* is just a collection of test cases +for a unit of code with a set up function that gets invoked before every test +case and then a tear down function that gets invoked after every test case +completes. Example: @@ -211,7 +211,7 @@ KUnit test framework. .. note:: A test case will only be run if it is associated with a test suite. -For a more information on these types of things see the :doc:`api/test`. +For more information on these types of things see the :doc:`api/test`. Isolating Behavior ================== @@ -338,7 +338,7 @@ We can easily test this code by *faking out* the underlying EEPROM: return count; } - ssize_t fake_eeprom_write(struct eeprom *this, size_t offset, const char *buffer, size_t count) + ssize_t fake_eeprom_write(struct eeprom *parent, size_t offset, const char *buffer, size_t count) { struct fake_eeprom *this = container_of(parent, struct fake_eeprom, parent); @@ -454,7 +454,7 @@ KUnit on non-UML architectures By default KUnit uses UML as a way to provide dependencies for code under test. Under most circumstances KUnit's usage of UML should be treated as an implementation detail of how KUnit works under the hood. Nevertheless, there -are instances where being able to run architecture specific code, or test +are instances where being able to run architecture specific code or test against real hardware is desirable. For these reasons KUnit supports running on other architectures. @@ -557,7 +557,7 @@ run your tests on your hardware setup just by compiling for your architecture. .. important:: Always prefer tests that run on UML to tests that only run under a particular architecture, and always prefer tests that run under QEMU or another easy - (and monitarily free) to obtain software environment to a specific piece of + (and monetarily free) to obtain software environment to a specific piece of hardware. Nevertheless, there are still valid reasons to write an architecture or hardware -- GitLab From 99e51aa8f70137310496f98937cfeecac0529a5a Mon Sep 17 00:00:00 2001 From: Brendan Higgins Date: Tue, 19 Nov 2019 17:17:00 -0800 Subject: [PATCH 0292/1437] Documentation: kunit: add documentation for kunit_tool Add documentation for the Python script used to build, run, and collect results from the kernel known as kunit_tool. kunit_tool (tools/testing/kunit/kunit.py) was already added in previous commits. Signed-off-by: Brendan Higgins Reviewed-by: David Gow Cc: Randy Dunlap Acked-by: Randy Dunlap Signed-off-by: Shuah Khan --- Documentation/dev-tools/kunit/index.rst | 1 + Documentation/dev-tools/kunit/kunit-tool.rst | 57 ++++++++++++++++++++ Documentation/dev-tools/kunit/start.rst | 5 +- 3 files changed, 62 insertions(+), 1 deletion(-) create mode 100644 Documentation/dev-tools/kunit/kunit-tool.rst diff --git a/Documentation/dev-tools/kunit/index.rst b/Documentation/dev-tools/kunit/index.rst index 26ffb46bdf99d..c60d760a0eed1 100644 --- a/Documentation/dev-tools/kunit/index.rst +++ b/Documentation/dev-tools/kunit/index.rst @@ -9,6 +9,7 @@ KUnit - Unit Testing for the Linux Kernel start usage + kunit-tool api/index faq diff --git a/Documentation/dev-tools/kunit/kunit-tool.rst b/Documentation/dev-tools/kunit/kunit-tool.rst new file mode 100644 index 0000000000000..50d46394e97e3 --- /dev/null +++ b/Documentation/dev-tools/kunit/kunit-tool.rst @@ -0,0 +1,57 @@ +.. SPDX-License-Identifier: GPL-2.0 + +================= +kunit_tool How-To +================= + +What is kunit_tool? +=================== + +kunit_tool is a script (``tools/testing/kunit/kunit.py``) that aids in building +the Linux kernel as UML (`User Mode Linux +`_), running KUnit tests, parsing +the test results and displaying them in a user friendly manner. + +What is a kunitconfig? +====================== + +It's just a defconfig that kunit_tool looks for in the base directory. +kunit_tool uses it to generate a .config as you might expect. In addition, it +verifies that the generated .config contains the CONFIG options in the +kunitconfig; the reason it does this is so that it is easy to be sure that a +CONFIG that enables a test actually ends up in the .config. + +How do I use kunit_tool? +======================== + +If a kunitconfig is present at the root directory, all you have to do is: + +.. code-block:: bash + + ./tools/testing/kunit/kunit.py run + +However, you most likely want to use it with the following options: + +.. code-block:: bash + + ./tools/testing/kunit/kunit.py run --timeout=30 --jobs=`nproc --all` + +- ``--timeout`` sets a maximum amount of time to allow tests to run. +- ``--jobs`` sets the number of threads to use to build the kernel. + +If you just want to use the defconfig that ships with the kernel, you can +append the ``--defconfig`` flag as well: + +.. code-block:: bash + + ./tools/testing/kunit/kunit.py run --timeout=30 --jobs=`nproc --all` --defconfig + +.. note:: + This command is particularly helpful for getting started because it + just works. No kunitconfig needs to be present. + +For a list of all the flags supported by kunit_tool, you can run: + +.. code-block:: bash + + ./tools/testing/kunit/kunit.py run --help diff --git a/Documentation/dev-tools/kunit/start.rst b/Documentation/dev-tools/kunit/start.rst index 75c30c2ea6220..9d6db892c41c0 100644 --- a/Documentation/dev-tools/kunit/start.rst +++ b/Documentation/dev-tools/kunit/start.rst @@ -19,7 +19,10 @@ The wrapper can be run with: .. code-block:: bash - ./tools/testing/kunit/kunit.py run + ./tools/testing/kunit/kunit.py run --defconfig + +For more information on this wrapper (also called kunit_tool) checkout the +:doc:`kunit-tool` page. Creating a kunitconfig ====================== -- GitLab From f8fc57e8d7c5d95f4180b127d3b167de403557c0 Mon Sep 17 00:00:00 2001 From: Martin Schiller Date: Mon, 9 Dec 2019 08:21:34 +0100 Subject: [PATCH 0293/1437] net/x25: add new state X25_STATE_5 This is needed, because if the flag X25_ACCPT_APPRV_FLAG is not set on a socket (manual call confirmation) and the channel is cleared by remote before the manual call confirmation was sent, this situation needs to be handled. Signed-off-by: Martin Schiller Signed-off-by: David S. Miller --- include/net/x25.h | 3 ++- net/x25/af_x25.c | 8 ++++++++ net/x25/x25_in.c | 32 ++++++++++++++++++++++++++++++++ 3 files changed, 42 insertions(+), 1 deletion(-) diff --git a/include/net/x25.h b/include/net/x25.h index ed1acc3044ace..d7d6c2b4ffa71 100644 --- a/include/net/x25.h +++ b/include/net/x25.h @@ -62,7 +62,8 @@ enum { X25_STATE_1, /* Awaiting Call Accepted */ X25_STATE_2, /* Awaiting Clear Confirmation */ X25_STATE_3, /* Data Transfer */ - X25_STATE_4 /* Awaiting Reset Confirmation */ + X25_STATE_4, /* Awaiting Reset Confirmation */ + X25_STATE_5 /* Call Accepted / Call Connected pending */ }; enum { diff --git a/net/x25/af_x25.c b/net/x25/af_x25.c index c34f7d0776046..2efe44a346443 100644 --- a/net/x25/af_x25.c +++ b/net/x25/af_x25.c @@ -659,6 +659,12 @@ static int x25_release(struct socket *sock) sock_set_flag(sk, SOCK_DEAD); sock_set_flag(sk, SOCK_DESTROY); break; + + case X25_STATE_5: + x25_write_internal(sk, X25_CLEAR_REQUEST); + x25_disconnect(sk, 0, 0, 0); + __x25_destroy_socket(sk); + goto out; } sock_orphan(sk); @@ -1054,6 +1060,8 @@ int x25_rx_call_request(struct sk_buff *skb, struct x25_neigh *nb, if (test_bit(X25_ACCPT_APPRV_FLAG, &makex25->flags)) { x25_write_internal(make, X25_CALL_ACCEPTED); makex25->state = X25_STATE_3; + } else { + makex25->state = X25_STATE_5; } /* diff --git a/net/x25/x25_in.c b/net/x25/x25_in.c index f97c43344e95d..4d3bb46aaae0d 100644 --- a/net/x25/x25_in.c +++ b/net/x25/x25_in.c @@ -382,6 +382,35 @@ out_clear: return 0; } +/* + * State machine for state 5, Call Accepted / Call Connected pending (X25_ACCPT_APPRV_FLAG). + * The handling of the timer(s) is in file x25_timer.c + * Handling of state 0 and connection release is in af_x25.c. + */ +static int x25_state5_machine(struct sock *sk, struct sk_buff *skb, int frametype) +{ + struct x25_sock *x25 = x25_sk(sk); + + switch (frametype) { + case X25_CLEAR_REQUEST: + if (!pskb_may_pull(skb, X25_STD_MIN_LEN + 2)) { + x25_write_internal(sk, X25_CLEAR_REQUEST); + x25->state = X25_STATE_2; + x25_start_t23timer(sk); + return 0; + } + + x25_write_internal(sk, X25_CLEAR_CONFIRMATION); + x25_disconnect(sk, 0, skb->data[3], skb->data[4]); + break; + + default: + break; + } + + return 0; +} + /* Higher level upcall for a LAPB frame */ int x25_process_rx_frame(struct sock *sk, struct sk_buff *skb) { @@ -406,6 +435,9 @@ int x25_process_rx_frame(struct sock *sk, struct sk_buff *skb) case X25_STATE_4: queued = x25_state4_machine(sk, skb, frametype); break; + case X25_STATE_5: + queued = x25_state5_machine(sk, skb, frametype); + break; } x25_kick(sk); -- GitLab From e43723292d6d7f217b572aab07f1f165189a1b56 Mon Sep 17 00:00:00 2001 From: Pankaj Bharadiya Date: Tue, 24 Sep 2019 16:28:37 +0530 Subject: [PATCH 0294/1437] MIPS: OCTEON: Replace SIZEOF_FIELD() macro Switch to the standard sizeof_field() macro to find the size of a member of a struct and remove the custom SIZEOF_FIELD() macro. Signed-off-by: Pankaj Bharadiya Link: https://lore.kernel.org/r/20190924105839.110713-4-pankaj.laxminarayan.bharadiya@intel.com Co-developed-by: Kees Cook Signed-off-by: Kees Cook --- arch/mips/cavium-octeon/executive/cvmx-bootmem.c | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/arch/mips/cavium-octeon/executive/cvmx-bootmem.c b/arch/mips/cavium-octeon/executive/cvmx-bootmem.c index ba8f82a29a816..e794b2d53adfc 100644 --- a/arch/mips/cavium-octeon/executive/cvmx-bootmem.c +++ b/arch/mips/cavium-octeon/executive/cvmx-bootmem.c @@ -44,13 +44,6 @@ static struct cvmx_bootmem_desc *cvmx_bootmem_desc; /* See header file for descriptions of functions */ -/** - * This macro returns the size of a member of a structure. - * Logically it is the same as "sizeof(s::field)" in C++, but - * C lacks the "::" operator. - */ -#define SIZEOF_FIELD(s, field) sizeof(((s *)NULL)->field) - /** * This macro returns a member of the * cvmx_bootmem_named_block_desc_t structure. These members can't @@ -65,7 +58,7 @@ static struct cvmx_bootmem_desc *cvmx_bootmem_desc; #define CVMX_BOOTMEM_NAMED_GET_FIELD(addr, field) \ __cvmx_bootmem_desc_get(addr, \ offsetof(struct cvmx_bootmem_named_block_desc, field), \ - SIZEOF_FIELD(struct cvmx_bootmem_named_block_desc, field)) + sizeof_field(struct cvmx_bootmem_named_block_desc, field)) /** * This function is the implementation of the get macros defined -- GitLab From c593642c8be046915ca3a4a300243a68077cd207 Mon Sep 17 00:00:00 2001 From: Pankaj Bharadiya Date: Mon, 9 Dec 2019 10:31:43 -0800 Subject: [PATCH 0295/1437] treewide: Use sizeof_field() macro Replace all the occurrences of FIELD_SIZEOF() with sizeof_field() except at places where these are defined. Later patches will remove the unused definition of FIELD_SIZEOF(). This patch is generated using following script: EXCLUDE_FILES="include/linux/stddef.h|include/linux/kernel.h" git grep -l -e "\bFIELD_SIZEOF\b" | while read file; do if [[ "$file" =~ $EXCLUDE_FILES ]]; then continue fi sed -i -e 's/\bFIELD_SIZEOF\b/sizeof_field/g' $file; done Signed-off-by: Pankaj Bharadiya Link: https://lore.kernel.org/r/20190924105839.110713-3-pankaj.laxminarayan.bharadiya@intel.com Co-developed-by: Kees Cook Signed-off-by: Kees Cook Acked-by: David Miller # for net --- Documentation/process/coding-style.rst | 2 +- .../it_IT/process/coding-style.rst | 2 +- .../zh_CN/process/coding-style.rst | 2 +- arch/arc/kernel/unwind.c | 6 +- arch/powerpc/net/bpf_jit32.h | 4 +- arch/powerpc/net/bpf_jit_comp.c | 16 +- arch/sparc/net/bpf_jit_comp_32.c | 8 +- arch/x86/kernel/fpu/xstate.c | 2 +- block/blk-core.c | 4 +- crypto/adiantum.c | 4 +- crypto/essiv.c | 2 +- drivers/firmware/efi/efi.c | 2 +- drivers/infiniband/hw/efa/efa_verbs.c | 2 +- drivers/infiniband/hw/hfi1/sdma.c | 2 +- drivers/infiniband/hw/hfi1/verbs.h | 4 +- .../ulp/opa_vnic/opa_vnic_ethtool.c | 2 +- drivers/md/raid5-ppl.c | 2 +- drivers/media/platform/omap3isp/isppreview.c | 24 +-- drivers/media/v4l2-core/v4l2-ioctl.c | 2 +- drivers/net/ethernet/amd/xgbe/xgbe-ethtool.c | 4 +- .../ethernet/cavium/liquidio/octeon_console.c | 16 +- .../net/ethernet/emulex/benet/be_ethtool.c | 2 +- .../hisilicon/hns3/hns3pf/hclge_main.c | 2 +- .../ethernet/hisilicon/hns3/hns3pf/hclge_tm.c | 2 +- .../net/ethernet/huawei/hinic/hinic_ethtool.c | 8 +- .../net/ethernet/intel/fm10k/fm10k_ethtool.c | 2 +- .../net/ethernet/intel/i40e/i40e_ethtool.c | 2 +- .../net/ethernet/intel/i40e/i40e_lan_hmc.c | 2 +- .../net/ethernet/intel/iavf/iavf_ethtool.c | 2 +- drivers/net/ethernet/intel/ice/ice_ethtool.c | 10 +- .../net/ethernet/intel/ice/ice_lan_tx_rx.h | 2 +- drivers/net/ethernet/intel/igb/igb_ethtool.c | 4 +- drivers/net/ethernet/intel/igc/igc_ethtool.c | 4 +- .../net/ethernet/intel/ixgb/ixgb_ethtool.c | 4 +- drivers/net/ethernet/intel/ixgbevf/ethtool.c | 4 +- drivers/net/ethernet/marvell/mv643xx_eth.c | 4 +- .../net/ethernet/mellanox/mlx4/en_ethtool.c | 2 +- .../ethernet/mellanox/mlx5/core/fpga/ipsec.c | 6 +- .../net/ethernet/mellanox/mlx5/core/fs_core.c | 4 +- drivers/net/ethernet/netronome/nfp/bpf/jit.c | 10 +- drivers/net/ethernet/netronome/nfp/bpf/main.c | 2 +- .../net/ethernet/netronome/nfp/bpf/offload.c | 2 +- .../net/ethernet/netronome/nfp/flower/main.h | 2 +- .../oki-semi/pch_gbe/pch_gbe_ethtool.c | 2 +- drivers/net/ethernet/qlogic/qede/qede.h | 2 +- .../ethernet/qlogic/qlcnic/qlcnic_ethtool.c | 2 +- drivers/net/ethernet/realtek/r8169_firmware.c | 2 +- .../ethernet/samsung/sxgbe/sxgbe_ethtool.c | 2 +- .../ethernet/stmicro/stmmac/stmmac_ethtool.c | 4 +- drivers/net/ethernet/ti/cpsw_ethtool.c | 6 +- drivers/net/ethernet/ti/netcp_ethss.c | 32 ++-- drivers/net/fjes/fjes_ethtool.c | 2 +- drivers/net/geneve.c | 2 +- drivers/net/hyperv/netvsc_drv.c | 2 +- drivers/net/usb/sierra_net.c | 2 +- drivers/net/usb/usbnet.c | 2 +- drivers/net/vxlan.c | 4 +- .../net/wireless/marvell/libertas/debugfs.c | 2 +- drivers/net/wireless/marvell/mwifiex/util.h | 4 +- drivers/s390/net/qeth_core_main.c | 2 +- drivers/s390/net/qeth_core_mpc.h | 10 +- drivers/scsi/aacraid/aachba.c | 4 +- drivers/scsi/be2iscsi/be_cmds.h | 2 +- drivers/scsi/cxgbi/libcxgbi.c | 2 +- drivers/scsi/smartpqi/smartpqi_init.c | 6 +- drivers/staging/qlge/qlge_ethtool.c | 2 +- drivers/staging/wfx/data_tx.c | 2 +- drivers/target/iscsi/cxgbit/cxgbit_main.c | 2 +- drivers/usb/atm/usbatm.c | 2 +- drivers/usb/gadget/function/f_fs.c | 2 +- fs/crypto/keyring.c | 2 +- fs/verity/enable.c | 2 +- include/linux/filter.h | 12 +- include/linux/kvm_host.h | 2 +- include/linux/phy_led_triggers.h | 2 +- include/net/garp.h | 2 +- include/net/ip_tunnels.h | 6 +- include/net/mrp.h | 2 +- include/net/netfilter/nf_conntrack_helper.h | 2 +- include/net/netfilter/nf_tables_core.h | 2 +- include/net/sock.h | 2 +- ipc/util.c | 2 +- kernel/bpf/cgroup.c | 2 +- kernel/bpf/local_storage.c | 4 +- net/802/mrp.c | 6 +- net/batman-adv/main.c | 2 +- net/bpf/test_run.c | 8 +- net/bridge/br.c | 2 +- net/core/dev.c | 2 +- net/core/filter.c | 140 +++++++++--------- net/core/flow_dissector.c | 10 +- net/core/xdp.c | 4 +- net/dccp/proto.c | 2 +- net/ipv4/ip_gre.c | 4 +- net/ipv4/ip_vti.c | 4 +- net/ipv4/tcp.c | 2 +- net/ipv6/ip6_gre.c | 4 +- net/iucv/af_iucv.c | 2 +- net/netfilter/nf_tables_api.c | 4 +- net/netfilter/nfnetlink_cthelper.c | 2 +- net/netfilter/nft_ct.c | 12 +- net/netfilter/nft_masq.c | 2 +- net/netfilter/nft_nat.c | 6 +- net/netfilter/nft_redir.c | 2 +- net/netfilter/nft_tproxy.c | 4 +- net/netfilter/xt_RATEEST.c | 2 +- net/netlink/af_netlink.c | 2 +- net/openvswitch/datapath.c | 2 +- net/openvswitch/flow.h | 4 +- net/rxrpc/af_rxrpc.c | 2 +- net/sched/act_ct.c | 4 +- net/sched/cls_flower.c | 2 +- net/unix/af_unix.c | 2 +- security/integrity/ima/ima_policy.c | 4 +- sound/soc/codecs/hdmi-codec.c | 2 +- 115 files changed, 298 insertions(+), 298 deletions(-) diff --git a/Documentation/process/coding-style.rst b/Documentation/process/coding-style.rst index ada573b7d703b..edb296c52f61e 100644 --- a/Documentation/process/coding-style.rst +++ b/Documentation/process/coding-style.rst @@ -988,7 +988,7 @@ Similarly, if you need to calculate the size of some structure member, use .. code-block:: c - #define FIELD_SIZEOF(t, f) (sizeof(((t*)0)->f)) + #define sizeof_field(t, f) (sizeof(((t*)0)->f)) There are also min() and max() macros that do strict type checking if you need them. Feel free to peruse that header file to see what else is already diff --git a/Documentation/translations/it_IT/process/coding-style.rst b/Documentation/translations/it_IT/process/coding-style.rst index 8995d2d19f202..8725f2b9e9603 100644 --- a/Documentation/translations/it_IT/process/coding-style.rst +++ b/Documentation/translations/it_IT/process/coding-style.rst @@ -1005,7 +1005,7 @@ struttura, usate .. code-block:: c - #define FIELD_SIZEOF(t, f) (sizeof(((t*)0)->f)) + #define sizeof_field(t, f) (sizeof(((t*)0)->f)) Ci sono anche le macro min() e max() che, se vi serve, effettuano un controllo rigido sui tipi. Sentitevi liberi di leggere attentamente questo file diff --git a/Documentation/translations/zh_CN/process/coding-style.rst b/Documentation/translations/zh_CN/process/coding-style.rst index 4f6237392e65a..eae10bc7f86f2 100644 --- a/Documentation/translations/zh_CN/process/coding-style.rst +++ b/Documentation/translations/zh_CN/process/coding-style.rst @@ -826,7 +826,7 @@ inline gcc 也可以自动使其内联。而且其他用户可能会要求移除 .. code-block:: c - #define FIELD_SIZEOF(t, f) (sizeof(((t*)0)->f)) + #define sizeof_field(t, f) (sizeof(((t*)0)->f)) 还有可以做严格的类型检查的 min() 和 max() 宏,如果你需要可以使用它们。你可以 自己看看那个头文件里还定义了什么你可以拿来用的东西,如果有定义的话,你就不应 diff --git a/arch/arc/kernel/unwind.c b/arch/arc/kernel/unwind.c index dc05a63516f5b..27ea64b1fa332 100644 --- a/arch/arc/kernel/unwind.c +++ b/arch/arc/kernel/unwind.c @@ -42,10 +42,10 @@ do { \ #define EXTRA_INFO(f) { \ BUILD_BUG_ON_ZERO(offsetof(struct unwind_frame_info, f) \ - % FIELD_SIZEOF(struct unwind_frame_info, f)) \ + % sizeof_field(struct unwind_frame_info, f)) \ + offsetof(struct unwind_frame_info, f) \ - / FIELD_SIZEOF(struct unwind_frame_info, f), \ - FIELD_SIZEOF(struct unwind_frame_info, f) \ + / sizeof_field(struct unwind_frame_info, f), \ + sizeof_field(struct unwind_frame_info, f) \ } #define PTREGS_INFO(f) EXTRA_INFO(regs.f) diff --git a/arch/powerpc/net/bpf_jit32.h b/arch/powerpc/net/bpf_jit32.h index 6e5a2a4faeab0..4ec2a9f14f845 100644 --- a/arch/powerpc/net/bpf_jit32.h +++ b/arch/powerpc/net/bpf_jit32.h @@ -97,12 +97,12 @@ DECLARE_LOAD_FUNC(sk_load_byte_msh); #ifdef CONFIG_SMP #ifdef CONFIG_PPC64 #define PPC_BPF_LOAD_CPU(r) \ - do { BUILD_BUG_ON(FIELD_SIZEOF(struct paca_struct, paca_index) != 2); \ + do { BUILD_BUG_ON(sizeof_field(struct paca_struct, paca_index) != 2); \ PPC_LHZ_OFFS(r, 13, offsetof(struct paca_struct, paca_index)); \ } while (0) #else #define PPC_BPF_LOAD_CPU(r) \ - do { BUILD_BUG_ON(FIELD_SIZEOF(struct task_struct, cpu) != 4); \ + do { BUILD_BUG_ON(sizeof_field(struct task_struct, cpu) != 4); \ PPC_LHZ_OFFS(r, 2, offsetof(struct task_struct, cpu)); \ } while(0) #endif diff --git a/arch/powerpc/net/bpf_jit_comp.c b/arch/powerpc/net/bpf_jit_comp.c index d57b46e0dd604..0acc9d5fb19e9 100644 --- a/arch/powerpc/net/bpf_jit_comp.c +++ b/arch/powerpc/net/bpf_jit_comp.c @@ -321,7 +321,7 @@ static int bpf_jit_build_body(struct bpf_prog *fp, u32 *image, ctx->seen |= SEEN_XREG | SEEN_MEM | (1<<(K & 0xf)); break; case BPF_LD | BPF_W | BPF_LEN: /* A = skb->len; */ - BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, len) != 4); + BUILD_BUG_ON(sizeof_field(struct sk_buff, len) != 4); PPC_LWZ_OFFS(r_A, r_skb, offsetof(struct sk_buff, len)); break; case BPF_LDX | BPF_W | BPF_ABS: /* A = *((u32 *)(seccomp_data + K)); */ @@ -333,16 +333,16 @@ static int bpf_jit_build_body(struct bpf_prog *fp, u32 *image, /*** Ancillary info loads ***/ case BPF_ANC | SKF_AD_PROTOCOL: /* A = ntohs(skb->protocol); */ - BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, + BUILD_BUG_ON(sizeof_field(struct sk_buff, protocol) != 2); PPC_NTOHS_OFFS(r_A, r_skb, offsetof(struct sk_buff, protocol)); break; case BPF_ANC | SKF_AD_IFINDEX: case BPF_ANC | SKF_AD_HATYPE: - BUILD_BUG_ON(FIELD_SIZEOF(struct net_device, + BUILD_BUG_ON(sizeof_field(struct net_device, ifindex) != 4); - BUILD_BUG_ON(FIELD_SIZEOF(struct net_device, + BUILD_BUG_ON(sizeof_field(struct net_device, type) != 2); PPC_LL_OFFS(r_scratch1, r_skb, offsetof(struct sk_buff, dev)); @@ -365,17 +365,17 @@ static int bpf_jit_build_body(struct bpf_prog *fp, u32 *image, break; case BPF_ANC | SKF_AD_MARK: - BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, mark) != 4); + BUILD_BUG_ON(sizeof_field(struct sk_buff, mark) != 4); PPC_LWZ_OFFS(r_A, r_skb, offsetof(struct sk_buff, mark)); break; case BPF_ANC | SKF_AD_RXHASH: - BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, hash) != 4); + BUILD_BUG_ON(sizeof_field(struct sk_buff, hash) != 4); PPC_LWZ_OFFS(r_A, r_skb, offsetof(struct sk_buff, hash)); break; case BPF_ANC | SKF_AD_VLAN_TAG: - BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, vlan_tci) != 2); + BUILD_BUG_ON(sizeof_field(struct sk_buff, vlan_tci) != 2); PPC_LHZ_OFFS(r_A, r_skb, offsetof(struct sk_buff, vlan_tci)); @@ -388,7 +388,7 @@ static int bpf_jit_build_body(struct bpf_prog *fp, u32 *image, PPC_ANDI(r_A, r_A, 1); break; case BPF_ANC | SKF_AD_QUEUE: - BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, + BUILD_BUG_ON(sizeof_field(struct sk_buff, queue_mapping) != 2); PPC_LHZ_OFFS(r_A, r_skb, offsetof(struct sk_buff, queue_mapping)); diff --git a/arch/sparc/net/bpf_jit_comp_32.c b/arch/sparc/net/bpf_jit_comp_32.c index 84cc8f7f83e99..c8eabb973b868 100644 --- a/arch/sparc/net/bpf_jit_comp_32.c +++ b/arch/sparc/net/bpf_jit_comp_32.c @@ -180,19 +180,19 @@ do { \ #define emit_loadptr(BASE, STRUCT, FIELD, DEST) \ do { unsigned int _off = offsetof(STRUCT, FIELD); \ - BUILD_BUG_ON(FIELD_SIZEOF(STRUCT, FIELD) != sizeof(void *)); \ + BUILD_BUG_ON(sizeof_field(STRUCT, FIELD) != sizeof(void *)); \ *prog++ = LDPTRI | RS1(BASE) | S13(_off) | RD(DEST); \ } while (0) #define emit_load32(BASE, STRUCT, FIELD, DEST) \ do { unsigned int _off = offsetof(STRUCT, FIELD); \ - BUILD_BUG_ON(FIELD_SIZEOF(STRUCT, FIELD) != sizeof(u32)); \ + BUILD_BUG_ON(sizeof_field(STRUCT, FIELD) != sizeof(u32)); \ *prog++ = LD32I | RS1(BASE) | S13(_off) | RD(DEST); \ } while (0) #define emit_load16(BASE, STRUCT, FIELD, DEST) \ do { unsigned int _off = offsetof(STRUCT, FIELD); \ - BUILD_BUG_ON(FIELD_SIZEOF(STRUCT, FIELD) != sizeof(u16)); \ + BUILD_BUG_ON(sizeof_field(STRUCT, FIELD) != sizeof(u16)); \ *prog++ = LD16I | RS1(BASE) | S13(_off) | RD(DEST); \ } while (0) @@ -202,7 +202,7 @@ do { unsigned int _off = offsetof(STRUCT, FIELD); \ } while (0) #define emit_load8(BASE, STRUCT, FIELD, DEST) \ -do { BUILD_BUG_ON(FIELD_SIZEOF(STRUCT, FIELD) != sizeof(u8)); \ +do { BUILD_BUG_ON(sizeof_field(STRUCT, FIELD) != sizeof(u8)); \ __emit_load8(BASE, STRUCT, FIELD, DEST); \ } while (0) diff --git a/arch/x86/kernel/fpu/xstate.c b/arch/x86/kernel/fpu/xstate.c index 319be936c348e..fa31470bbf24a 100644 --- a/arch/x86/kernel/fpu/xstate.c +++ b/arch/x86/kernel/fpu/xstate.c @@ -259,7 +259,7 @@ static void __init setup_xstate_features(void) xmm_space); xstate_offsets[XFEATURE_SSE] = xstate_sizes[XFEATURE_FP]; - xstate_sizes[XFEATURE_SSE] = FIELD_SIZEOF(struct fxregs_state, + xstate_sizes[XFEATURE_SSE] = sizeof_field(struct fxregs_state, xmm_space); for (i = FIRST_EXTENDED_XFEATURE; i < XFEATURE_MAX; i++) { diff --git a/block/blk-core.c b/block/blk-core.c index a1e228752083f..e4b27f7e9f51d 100644 --- a/block/blk-core.c +++ b/block/blk-core.c @@ -1792,9 +1792,9 @@ int __init blk_dev_init(void) { BUILD_BUG_ON(REQ_OP_LAST >= (1 << REQ_OP_BITS)); BUILD_BUG_ON(REQ_OP_BITS + REQ_FLAG_BITS > 8 * - FIELD_SIZEOF(struct request, cmd_flags)); + sizeof_field(struct request, cmd_flags)); BUILD_BUG_ON(REQ_OP_BITS + REQ_FLAG_BITS > 8 * - FIELD_SIZEOF(struct bio, bi_opf)); + sizeof_field(struct bio, bi_opf)); /* used for unplugging and affects IO latency/throughput - HIGHPRI */ kblockd_workqueue = alloc_workqueue("kblockd", diff --git a/crypto/adiantum.c b/crypto/adiantum.c index aded260922684..9dc53cf9b1f17 100644 --- a/crypto/adiantum.c +++ b/crypto/adiantum.c @@ -436,10 +436,10 @@ static int adiantum_init_tfm(struct crypto_skcipher *tfm) BUILD_BUG_ON(offsetofend(struct adiantum_request_ctx, u) != sizeof(struct adiantum_request_ctx)); - subreq_size = max(FIELD_SIZEOF(struct adiantum_request_ctx, + subreq_size = max(sizeof_field(struct adiantum_request_ctx, u.hash_desc) + crypto_shash_descsize(hash), - FIELD_SIZEOF(struct adiantum_request_ctx, + sizeof_field(struct adiantum_request_ctx, u.streamcipher_req) + crypto_skcipher_reqsize(streamcipher)); diff --git a/crypto/essiv.c b/crypto/essiv.c index 808f2b3621068..495a2d1e1460e 100644 --- a/crypto/essiv.c +++ b/crypto/essiv.c @@ -347,7 +347,7 @@ static int essiv_aead_init_tfm(struct crypto_aead *tfm) if (IS_ERR(aead)) return PTR_ERR(aead); - subreq_size = FIELD_SIZEOF(struct essiv_aead_request_ctx, aead_req) + + subreq_size = sizeof_field(struct essiv_aead_request_ctx, aead_req) + crypto_aead_reqsize(aead); tctx->ivoffset = offsetof(struct essiv_aead_request_ctx, aead_req) + diff --git a/drivers/firmware/efi/efi.c b/drivers/firmware/efi/efi.c index d101f072c8f8a..407816da9fcb4 100644 --- a/drivers/firmware/efi/efi.c +++ b/drivers/firmware/efi/efi.c @@ -681,7 +681,7 @@ device_initcall(efi_load_efivars); { name }, \ { prop }, \ offsetof(struct efi_fdt_params, field), \ - FIELD_SIZEOF(struct efi_fdt_params, field) \ + sizeof_field(struct efi_fdt_params, field) \ } struct params { diff --git a/drivers/infiniband/hw/efa/efa_verbs.c b/drivers/infiniband/hw/efa/efa_verbs.c index c9d294caa27a9..50c22575aed65 100644 --- a/drivers/infiniband/hw/efa/efa_verbs.c +++ b/drivers/infiniband/hw/efa/efa_verbs.c @@ -145,7 +145,7 @@ static inline bool is_rdma_read_cap(struct efa_dev *dev) } #define field_avail(x, fld, sz) (offsetof(typeof(x), fld) + \ - FIELD_SIZEOF(typeof(x), fld) <= (sz)) + sizeof_field(typeof(x), fld) <= (sz)) #define is_reserved_cleared(reserved) \ !memchr_inv(reserved, 0, sizeof(reserved)) diff --git a/drivers/infiniband/hw/hfi1/sdma.c b/drivers/infiniband/hw/hfi1/sdma.c index 5774dfc22e18c..a51525647ac86 100644 --- a/drivers/infiniband/hw/hfi1/sdma.c +++ b/drivers/infiniband/hw/hfi1/sdma.c @@ -848,7 +848,7 @@ static const struct rhashtable_params sdma_rht_params = { .nelem_hint = NR_CPUS_HINT, .head_offset = offsetof(struct sdma_rht_node, node), .key_offset = offsetof(struct sdma_rht_node, cpu_id), - .key_len = FIELD_SIZEOF(struct sdma_rht_node, cpu_id), + .key_len = sizeof_field(struct sdma_rht_node, cpu_id), .max_size = NR_CPUS, .min_size = 8, .automatic_shrinking = true, diff --git a/drivers/infiniband/hw/hfi1/verbs.h b/drivers/infiniband/hw/hfi1/verbs.h index b0e9bf7cd1508..d36e3e14896dd 100644 --- a/drivers/infiniband/hw/hfi1/verbs.h +++ b/drivers/infiniband/hw/hfi1/verbs.h @@ -107,9 +107,9 @@ enum { HFI1_HAS_GRH = (1 << 0), }; -#define LRH_16B_BYTES (FIELD_SIZEOF(struct hfi1_16b_header, lrh)) +#define LRH_16B_BYTES (sizeof_field(struct hfi1_16b_header, lrh)) #define LRH_16B_DWORDS (LRH_16B_BYTES / sizeof(u32)) -#define LRH_9B_BYTES (FIELD_SIZEOF(struct ib_header, lrh)) +#define LRH_9B_BYTES (sizeof_field(struct ib_header, lrh)) #define LRH_9B_DWORDS (LRH_9B_BYTES / sizeof(u32)) /* 24Bits for qpn, upper 8Bits reserved */ diff --git a/drivers/infiniband/ulp/opa_vnic/opa_vnic_ethtool.c b/drivers/infiniband/ulp/opa_vnic/opa_vnic_ethtool.c index 62390e9e00236..8ad7da989a0ec 100644 --- a/drivers/infiniband/ulp/opa_vnic/opa_vnic_ethtool.c +++ b/drivers/infiniband/ulp/opa_vnic/opa_vnic_ethtool.c @@ -63,7 +63,7 @@ struct vnic_stats { }; }; -#define VNIC_STAT(m) { FIELD_SIZEOF(struct opa_vnic_stats, m), \ +#define VNIC_STAT(m) { sizeof_field(struct opa_vnic_stats, m), \ offsetof(struct opa_vnic_stats, m) } static struct vnic_stats vnic_gstrings_stats[] = { diff --git a/drivers/md/raid5-ppl.c b/drivers/md/raid5-ppl.c index cab5b1352892f..d50238d0a85db 100644 --- a/drivers/md/raid5-ppl.c +++ b/drivers/md/raid5-ppl.c @@ -1360,7 +1360,7 @@ int ppl_init_log(struct r5conf *conf) return -EINVAL; } - max_disks = FIELD_SIZEOF(struct ppl_log, disk_flush_bitmap) * + max_disks = sizeof_field(struct ppl_log, disk_flush_bitmap) * BITS_PER_BYTE; if (conf->raid_disks > max_disks) { pr_warn("md/raid:%s PPL doesn't support over %d disks in the array\n", diff --git a/drivers/media/platform/omap3isp/isppreview.c b/drivers/media/platform/omap3isp/isppreview.c index 97d660606d984..4dbdf3180d108 100644 --- a/drivers/media/platform/omap3isp/isppreview.c +++ b/drivers/media/platform/omap3isp/isppreview.c @@ -753,7 +753,7 @@ static const struct preview_update update_attrs[] = { preview_config_luma_enhancement, preview_enable_luma_enhancement, offsetof(struct prev_params, luma), - FIELD_SIZEOF(struct prev_params, luma), + sizeof_field(struct prev_params, luma), offsetof(struct omap3isp_prev_update_config, luma), }, /* OMAP3ISP_PREV_INVALAW */ { NULL, @@ -762,55 +762,55 @@ static const struct preview_update update_attrs[] = { preview_config_hmed, preview_enable_hmed, offsetof(struct prev_params, hmed), - FIELD_SIZEOF(struct prev_params, hmed), + sizeof_field(struct prev_params, hmed), offsetof(struct omap3isp_prev_update_config, hmed), }, /* OMAP3ISP_PREV_CFA */ { preview_config_cfa, NULL, offsetof(struct prev_params, cfa), - FIELD_SIZEOF(struct prev_params, cfa), + sizeof_field(struct prev_params, cfa), offsetof(struct omap3isp_prev_update_config, cfa), }, /* OMAP3ISP_PREV_CHROMA_SUPP */ { preview_config_chroma_suppression, preview_enable_chroma_suppression, offsetof(struct prev_params, csup), - FIELD_SIZEOF(struct prev_params, csup), + sizeof_field(struct prev_params, csup), offsetof(struct omap3isp_prev_update_config, csup), }, /* OMAP3ISP_PREV_WB */ { preview_config_whitebalance, NULL, offsetof(struct prev_params, wbal), - FIELD_SIZEOF(struct prev_params, wbal), + sizeof_field(struct prev_params, wbal), offsetof(struct omap3isp_prev_update_config, wbal), }, /* OMAP3ISP_PREV_BLKADJ */ { preview_config_blkadj, NULL, offsetof(struct prev_params, blkadj), - FIELD_SIZEOF(struct prev_params, blkadj), + sizeof_field(struct prev_params, blkadj), offsetof(struct omap3isp_prev_update_config, blkadj), }, /* OMAP3ISP_PREV_RGB2RGB */ { preview_config_rgb_blending, NULL, offsetof(struct prev_params, rgb2rgb), - FIELD_SIZEOF(struct prev_params, rgb2rgb), + sizeof_field(struct prev_params, rgb2rgb), offsetof(struct omap3isp_prev_update_config, rgb2rgb), }, /* OMAP3ISP_PREV_COLOR_CONV */ { preview_config_csc, NULL, offsetof(struct prev_params, csc), - FIELD_SIZEOF(struct prev_params, csc), + sizeof_field(struct prev_params, csc), offsetof(struct omap3isp_prev_update_config, csc), }, /* OMAP3ISP_PREV_YC_LIMIT */ { preview_config_yc_range, NULL, offsetof(struct prev_params, yclimit), - FIELD_SIZEOF(struct prev_params, yclimit), + sizeof_field(struct prev_params, yclimit), offsetof(struct omap3isp_prev_update_config, yclimit), }, /* OMAP3ISP_PREV_DEFECT_COR */ { preview_config_dcor, preview_enable_dcor, offsetof(struct prev_params, dcor), - FIELD_SIZEOF(struct prev_params, dcor), + sizeof_field(struct prev_params, dcor), offsetof(struct omap3isp_prev_update_config, dcor), }, /* Previously OMAP3ISP_PREV_GAMMABYPASS, not used anymore */ { NULL, @@ -828,13 +828,13 @@ static const struct preview_update update_attrs[] = { preview_config_noisefilter, preview_enable_noisefilter, offsetof(struct prev_params, nf), - FIELD_SIZEOF(struct prev_params, nf), + sizeof_field(struct prev_params, nf), offsetof(struct omap3isp_prev_update_config, nf), }, /* OMAP3ISP_PREV_GAMMA */ { preview_config_gammacorrn, preview_enable_gammacorrn, offsetof(struct prev_params, gamma), - FIELD_SIZEOF(struct prev_params, gamma), + sizeof_field(struct prev_params, gamma), offsetof(struct omap3isp_prev_update_config, gamma), }, /* OMAP3ISP_PREV_CONTRAST */ { preview_config_contrast, diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c index 4e700583659ba..003b7422aeef6 100644 --- a/drivers/media/v4l2-core/v4l2-ioctl.c +++ b/drivers/media/v4l2-core/v4l2-ioctl.c @@ -2652,7 +2652,7 @@ struct v4l2_ioctl_info { /* Zero struct from after the field to the end */ #define INFO_FL_CLEAR(v4l2_struct, field) \ ((offsetof(struct v4l2_struct, field) + \ - FIELD_SIZEOF(struct v4l2_struct, field)) << 16) + sizeof_field(struct v4l2_struct, field)) << 16) #define INFO_FL_CLEAR_MASK (_IOC_SIZEMASK << 16) #define DEFINE_V4L_STUB_FUNC(_vidioc) \ diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-ethtool.c b/drivers/net/ethernet/amd/xgbe/xgbe-ethtool.c index a880f10e3e703..8083173f1a8f3 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-ethtool.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-ethtool.c @@ -129,13 +129,13 @@ struct xgbe_stats { #define XGMAC_MMC_STAT(_string, _var) \ { _string, \ - FIELD_SIZEOF(struct xgbe_mmc_stats, _var), \ + sizeof_field(struct xgbe_mmc_stats, _var), \ offsetof(struct xgbe_prv_data, mmc_stats._var), \ } #define XGMAC_EXT_STAT(_string, _var) \ { _string, \ - FIELD_SIZEOF(struct xgbe_ext_stats, _var), \ + sizeof_field(struct xgbe_ext_stats, _var), \ offsetof(struct xgbe_prv_data, ext_stats._var), \ } diff --git a/drivers/net/ethernet/cavium/liquidio/octeon_console.c b/drivers/net/ethernet/cavium/liquidio/octeon_console.c index 0cc2338d8d2a8..dfc77507b159a 100644 --- a/drivers/net/ethernet/cavium/liquidio/octeon_console.c +++ b/drivers/net/ethernet/cavium/liquidio/octeon_console.c @@ -205,11 +205,11 @@ static int __cvmx_bootmem_check_version(struct octeon_device *oct, major_version = (u32)__cvmx_bootmem_desc_get( oct, oct->bootmem_desc_addr, offsetof(struct cvmx_bootmem_desc, major_version), - FIELD_SIZEOF(struct cvmx_bootmem_desc, major_version)); + sizeof_field(struct cvmx_bootmem_desc, major_version)); minor_version = (u32)__cvmx_bootmem_desc_get( oct, oct->bootmem_desc_addr, offsetof(struct cvmx_bootmem_desc, minor_version), - FIELD_SIZEOF(struct cvmx_bootmem_desc, minor_version)); + sizeof_field(struct cvmx_bootmem_desc, minor_version)); dev_dbg(&oct->pci_dev->dev, "%s: major_version=%d\n", __func__, major_version); @@ -237,13 +237,13 @@ static const struct cvmx_bootmem_named_block_desc oct, named_addr, offsetof(struct cvmx_bootmem_named_block_desc, base_addr), - FIELD_SIZEOF( + sizeof_field( struct cvmx_bootmem_named_block_desc, base_addr)); desc->size = __cvmx_bootmem_desc_get(oct, named_addr, offsetof(struct cvmx_bootmem_named_block_desc, size), - FIELD_SIZEOF( + sizeof_field( struct cvmx_bootmem_named_block_desc, size)); @@ -268,20 +268,20 @@ static u64 cvmx_bootmem_phy_named_block_find(struct octeon_device *oct, oct, oct->bootmem_desc_addr, offsetof(struct cvmx_bootmem_desc, named_block_array_addr), - FIELD_SIZEOF(struct cvmx_bootmem_desc, + sizeof_field(struct cvmx_bootmem_desc, named_block_array_addr)); u32 num_blocks = (u32)__cvmx_bootmem_desc_get( oct, oct->bootmem_desc_addr, offsetof(struct cvmx_bootmem_desc, nb_num_blocks), - FIELD_SIZEOF(struct cvmx_bootmem_desc, + sizeof_field(struct cvmx_bootmem_desc, nb_num_blocks)); u32 name_length = (u32)__cvmx_bootmem_desc_get( oct, oct->bootmem_desc_addr, offsetof(struct cvmx_bootmem_desc, named_block_name_len), - FIELD_SIZEOF(struct cvmx_bootmem_desc, + sizeof_field(struct cvmx_bootmem_desc, named_block_name_len)); u64 named_addr = named_block_array_addr; @@ -292,7 +292,7 @@ static u64 cvmx_bootmem_phy_named_block_find(struct octeon_device *oct, offsetof( struct cvmx_bootmem_named_block_desc, size), - FIELD_SIZEOF( + sizeof_field( struct cvmx_bootmem_named_block_desc, size)); diff --git a/drivers/net/ethernet/emulex/benet/be_ethtool.c b/drivers/net/ethernet/emulex/benet/be_ethtool.c index 5bb5abf995887..022a54a1805b4 100644 --- a/drivers/net/ethernet/emulex/benet/be_ethtool.c +++ b/drivers/net/ethernet/emulex/benet/be_ethtool.c @@ -23,7 +23,7 @@ struct be_ethtool_stat { }; enum {DRVSTAT_TX, DRVSTAT_RX, DRVSTAT}; -#define FIELDINFO(_struct, field) FIELD_SIZEOF(_struct, field), \ +#define FIELDINFO(_struct, field) sizeof_field(_struct, field), \ offsetof(_struct, field) #define DRVSTAT_TX_INFO(field) #field, DRVSTAT_TX,\ FIELDINFO(struct be_tx_stats, field) diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c index d862e9ba27e15..13dbd249f35fa 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c @@ -10240,7 +10240,7 @@ static int hclge_get_dfx_reg_len(struct hclge_dev *hdev, int *len) return ret; } - data_len_per_desc = FIELD_SIZEOF(struct hclge_desc, data); + data_len_per_desc = sizeof_field(struct hclge_desc, data); *len = 0; for (i = 0; i < dfx_reg_type_num; i++) { bd_num = bd_num_list[i]; diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_tm.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_tm.c index fbc39a2480d05..180224eab1ca4 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_tm.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_tm.c @@ -614,7 +614,7 @@ static void hclge_tm_vport_tc_info_update(struct hclge_vport *vport) } memcpy(kinfo->prio_tc, hdev->tm_info.prio_tc, - FIELD_SIZEOF(struct hnae3_knic_private_info, prio_tc)); + sizeof_field(struct hnae3_knic_private_info, prio_tc)); } static void hclge_tm_vport_info_update(struct hclge_dev *hdev) diff --git a/drivers/net/ethernet/huawei/hinic/hinic_ethtool.c b/drivers/net/ethernet/huawei/hinic/hinic_ethtool.c index 60ec48fe41446..966aea949c0bd 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_ethtool.c +++ b/drivers/net/ethernet/huawei/hinic/hinic_ethtool.c @@ -450,7 +450,7 @@ static u32 hinic_get_rxfh_indir_size(struct net_device *netdev) #define HINIC_FUNC_STAT(_stat_item) { \ .name = #_stat_item, \ - .size = FIELD_SIZEOF(struct hinic_vport_stats, _stat_item), \ + .size = sizeof_field(struct hinic_vport_stats, _stat_item), \ .offset = offsetof(struct hinic_vport_stats, _stat_item) \ } @@ -477,7 +477,7 @@ static struct hinic_stats hinic_function_stats[] = { #define HINIC_PORT_STAT(_stat_item) { \ .name = #_stat_item, \ - .size = FIELD_SIZEOF(struct hinic_phy_port_stats, _stat_item), \ + .size = sizeof_field(struct hinic_phy_port_stats, _stat_item), \ .offset = offsetof(struct hinic_phy_port_stats, _stat_item) \ } @@ -571,7 +571,7 @@ static struct hinic_stats hinic_port_stats[] = { #define HINIC_TXQ_STAT(_stat_item) { \ .name = "txq%d_"#_stat_item, \ - .size = FIELD_SIZEOF(struct hinic_txq_stats, _stat_item), \ + .size = sizeof_field(struct hinic_txq_stats, _stat_item), \ .offset = offsetof(struct hinic_txq_stats, _stat_item) \ } @@ -586,7 +586,7 @@ static struct hinic_stats hinic_tx_queue_stats[] = { #define HINIC_RXQ_STAT(_stat_item) { \ .name = "rxq%d_"#_stat_item, \ - .size = FIELD_SIZEOF(struct hinic_rxq_stats, _stat_item), \ + .size = sizeof_field(struct hinic_rxq_stats, _stat_item), \ .offset = offsetof(struct hinic_rxq_stats, _stat_item) \ } diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_ethtool.c b/drivers/net/ethernet/intel/fm10k/fm10k_ethtool.c index c681d2d28107d..68edf55ac9062 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k_ethtool.c +++ b/drivers/net/ethernet/intel/fm10k/fm10k_ethtool.c @@ -18,7 +18,7 @@ struct fm10k_stats { #define FM10K_STAT_FIELDS(_type, _name, _stat) { \ .stat_string = _name, \ - .sizeof_stat = FIELD_SIZEOF(_type, _stat), \ + .sizeof_stat = sizeof_field(_type, _stat), \ .stat_offset = offsetof(_type, _stat) \ } diff --git a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c index d24d8731bef02..317f3f1458db4 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c +++ b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c @@ -43,7 +43,7 @@ struct i40e_stats { */ #define I40E_STAT(_type, _name, _stat) { \ .stat_string = _name, \ - .sizeof_stat = FIELD_SIZEOF(_type, _stat), \ + .sizeof_stat = sizeof_field(_type, _stat), \ .stat_offset = offsetof(_type, _stat) \ } diff --git a/drivers/net/ethernet/intel/i40e/i40e_lan_hmc.c b/drivers/net/ethernet/intel/i40e/i40e_lan_hmc.c index be24d42280d82..a3da422ab05b6 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_lan_hmc.c +++ b/drivers/net/ethernet/intel/i40e/i40e_lan_hmc.c @@ -659,7 +659,7 @@ i40e_status i40e_shutdown_lan_hmc(struct i40e_hw *hw) #define I40E_HMC_STORE(_struct, _ele) \ offsetof(struct _struct, _ele), \ - FIELD_SIZEOF(struct _struct, _ele) + sizeof_field(struct _struct, _ele) struct i40e_context_ele { u16 offset; diff --git a/drivers/net/ethernet/intel/iavf/iavf_ethtool.c b/drivers/net/ethernet/intel/iavf/iavf_ethtool.c index dad3eec8ccd86..84c3d8d97ef6f 100644 --- a/drivers/net/ethernet/intel/iavf/iavf_ethtool.c +++ b/drivers/net/ethernet/intel/iavf/iavf_ethtool.c @@ -42,7 +42,7 @@ struct iavf_stats { */ #define IAVF_STAT(_type, _name, _stat) { \ .stat_string = _name, \ - .sizeof_stat = FIELD_SIZEOF(_type, _stat), \ + .sizeof_stat = sizeof_field(_type, _stat), \ .stat_offset = offsetof(_type, _stat) \ } diff --git a/drivers/net/ethernet/intel/ice/ice_ethtool.c b/drivers/net/ethernet/intel/ice/ice_ethtool.c index aec3c6c379df8..9ebd93e79aeb6 100644 --- a/drivers/net/ethernet/intel/ice/ice_ethtool.c +++ b/drivers/net/ethernet/intel/ice/ice_ethtool.c @@ -15,7 +15,7 @@ struct ice_stats { #define ICE_STAT(_type, _name, _stat) { \ .stat_string = _name, \ - .sizeof_stat = FIELD_SIZEOF(_type, _stat), \ + .sizeof_stat = sizeof_field(_type, _stat), \ .stat_offset = offsetof(_type, _stat) \ } @@ -36,10 +36,10 @@ static int ice_q_stats_len(struct net_device *netdev) #define ICE_VSI_STATS_LEN ARRAY_SIZE(ice_gstrings_vsi_stats) #define ICE_PFC_STATS_LEN ( \ - (FIELD_SIZEOF(struct ice_pf, stats.priority_xoff_rx) + \ - FIELD_SIZEOF(struct ice_pf, stats.priority_xon_rx) + \ - FIELD_SIZEOF(struct ice_pf, stats.priority_xoff_tx) + \ - FIELD_SIZEOF(struct ice_pf, stats.priority_xon_tx)) \ + (sizeof_field(struct ice_pf, stats.priority_xoff_rx) + \ + sizeof_field(struct ice_pf, stats.priority_xon_rx) + \ + sizeof_field(struct ice_pf, stats.priority_xoff_tx) + \ + sizeof_field(struct ice_pf, stats.priority_xon_tx)) \ / sizeof(u64)) #define ICE_ALL_STATS_LEN(n) (ICE_PF_STATS_LEN + ICE_PFC_STATS_LEN + \ ICE_VSI_STATS_LEN + ice_q_stats_len(n)) diff --git a/drivers/net/ethernet/intel/ice/ice_lan_tx_rx.h b/drivers/net/ethernet/intel/ice/ice_lan_tx_rx.h index ad34f22d44ef1..0997d352709b7 100644 --- a/drivers/net/ethernet/intel/ice/ice_lan_tx_rx.h +++ b/drivers/net/ethernet/intel/ice/ice_lan_tx_rx.h @@ -302,7 +302,7 @@ struct ice_ctx_ele { #define ICE_CTX_STORE(_struct, _ele, _width, _lsb) { \ .offset = offsetof(struct _struct, _ele), \ - .size_of = FIELD_SIZEOF(struct _struct, _ele), \ + .size_of = sizeof_field(struct _struct, _ele), \ .width = _width, \ .lsb = _lsb, \ } diff --git a/drivers/net/ethernet/intel/igb/igb_ethtool.c b/drivers/net/ethernet/intel/igb/igb_ethtool.c index 3182b059bf55c..4690d6c87f39a 100644 --- a/drivers/net/ethernet/intel/igb/igb_ethtool.c +++ b/drivers/net/ethernet/intel/igb/igb_ethtool.c @@ -26,7 +26,7 @@ struct igb_stats { #define IGB_STAT(_name, _stat) { \ .stat_string = _name, \ - .sizeof_stat = FIELD_SIZEOF(struct igb_adapter, _stat), \ + .sizeof_stat = sizeof_field(struct igb_adapter, _stat), \ .stat_offset = offsetof(struct igb_adapter, _stat) \ } static const struct igb_stats igb_gstrings_stats[] = { @@ -76,7 +76,7 @@ static const struct igb_stats igb_gstrings_stats[] = { #define IGB_NETDEV_STAT(_net_stat) { \ .stat_string = __stringify(_net_stat), \ - .sizeof_stat = FIELD_SIZEOF(struct rtnl_link_stats64, _net_stat), \ + .sizeof_stat = sizeof_field(struct rtnl_link_stats64, _net_stat), \ .stat_offset = offsetof(struct rtnl_link_stats64, _net_stat) \ } static const struct igb_stats igb_gstrings_net_stats[] = { diff --git a/drivers/net/ethernet/intel/igc/igc_ethtool.c b/drivers/net/ethernet/intel/igc/igc_ethtool.c index ac98f1d968921..455c1cdceb6e2 100644 --- a/drivers/net/ethernet/intel/igc/igc_ethtool.c +++ b/drivers/net/ethernet/intel/igc/igc_ethtool.c @@ -16,7 +16,7 @@ struct igc_stats { #define IGC_STAT(_name, _stat) { \ .stat_string = _name, \ - .sizeof_stat = FIELD_SIZEOF(struct igc_adapter, _stat), \ + .sizeof_stat = sizeof_field(struct igc_adapter, _stat), \ .stat_offset = offsetof(struct igc_adapter, _stat) \ } @@ -67,7 +67,7 @@ static const struct igc_stats igc_gstrings_stats[] = { #define IGC_NETDEV_STAT(_net_stat) { \ .stat_string = __stringify(_net_stat), \ - .sizeof_stat = FIELD_SIZEOF(struct rtnl_link_stats64, _net_stat), \ + .sizeof_stat = sizeof_field(struct rtnl_link_stats64, _net_stat), \ .stat_offset = offsetof(struct rtnl_link_stats64, _net_stat) \ } diff --git a/drivers/net/ethernet/intel/ixgb/ixgb_ethtool.c b/drivers/net/ethernet/intel/ixgb/ixgb_ethtool.c index c8c93ac436d4c..c65eb1afc8fb9 100644 --- a/drivers/net/ethernet/intel/ixgb/ixgb_ethtool.c +++ b/drivers/net/ethernet/intel/ixgb/ixgb_ethtool.c @@ -19,10 +19,10 @@ struct ixgb_stats { }; #define IXGB_STAT(m) IXGB_STATS, \ - FIELD_SIZEOF(struct ixgb_adapter, m), \ + sizeof_field(struct ixgb_adapter, m), \ offsetof(struct ixgb_adapter, m) #define IXGB_NETDEV_STAT(m) NETDEV_STATS, \ - FIELD_SIZEOF(struct net_device, m), \ + sizeof_field(struct net_device, m), \ offsetof(struct net_device, m) static struct ixgb_stats ixgb_gstrings_stats[] = { diff --git a/drivers/net/ethernet/intel/ixgbevf/ethtool.c b/drivers/net/ethernet/intel/ixgbevf/ethtool.c index 54459b69c9484..f7f309c96fa84 100644 --- a/drivers/net/ethernet/intel/ixgbevf/ethtool.c +++ b/drivers/net/ethernet/intel/ixgbevf/ethtool.c @@ -31,14 +31,14 @@ struct ixgbe_stats { #define IXGBEVF_STAT(_name, _stat) { \ .stat_string = _name, \ .type = IXGBEVF_STATS, \ - .sizeof_stat = FIELD_SIZEOF(struct ixgbevf_adapter, _stat), \ + .sizeof_stat = sizeof_field(struct ixgbevf_adapter, _stat), \ .stat_offset = offsetof(struct ixgbevf_adapter, _stat) \ } #define IXGBEVF_NETDEV_STAT(_net_stat) { \ .stat_string = #_net_stat, \ .type = NETDEV_STATS, \ - .sizeof_stat = FIELD_SIZEOF(struct net_device_stats, _net_stat), \ + .sizeof_stat = sizeof_field(struct net_device_stats, _net_stat), \ .stat_offset = offsetof(struct net_device_stats, _net_stat) \ } diff --git a/drivers/net/ethernet/marvell/mv643xx_eth.c b/drivers/net/ethernet/marvell/mv643xx_eth.c index d5b644131cff5..65a093216dacf 100644 --- a/drivers/net/ethernet/marvell/mv643xx_eth.c +++ b/drivers/net/ethernet/marvell/mv643xx_eth.c @@ -1432,11 +1432,11 @@ struct mv643xx_eth_stats { }; #define SSTAT(m) \ - { #m, FIELD_SIZEOF(struct net_device_stats, m), \ + { #m, sizeof_field(struct net_device_stats, m), \ offsetof(struct net_device, stats.m), -1 } #define MIBSTAT(m) \ - { #m, FIELD_SIZEOF(struct mib_counters, m), \ + { #m, sizeof_field(struct mib_counters, m), \ -1, offsetof(struct mv643xx_eth_private, mib_counters.m) } static const struct mv643xx_eth_stats mv643xx_eth_stats[] = { diff --git a/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c b/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c index a1202e53710cd..8bf1f08fdee26 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c @@ -611,7 +611,7 @@ static u32 ptys_get_active_port(struct mlx4_ptys_reg *ptys_reg) } #define MLX4_LINK_MODES_SZ \ - (FIELD_SIZEOF(struct mlx4_ptys_reg, eth_proto_cap) * 8) + (sizeof_field(struct mlx4_ptys_reg, eth_proto_cap) * 8) enum ethtool_report { SUPPORTED = 0, diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fpga/ipsec.c b/drivers/net/ethernet/mellanox/mlx5/core/fpga/ipsec.c index c76da309506b2..e4ec0e03c2894 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/fpga/ipsec.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/fpga/ipsec.c @@ -87,10 +87,10 @@ static const struct rhashtable_params rhash_sa = { * value is not constant during the lifetime * of the key object. */ - .key_len = FIELD_SIZEOF(struct mlx5_fpga_ipsec_sa_ctx, hw_sa) - - FIELD_SIZEOF(struct mlx5_ifc_fpga_ipsec_sa_v1, cmd), + .key_len = sizeof_field(struct mlx5_fpga_ipsec_sa_ctx, hw_sa) - + sizeof_field(struct mlx5_ifc_fpga_ipsec_sa_v1, cmd), .key_offset = offsetof(struct mlx5_fpga_ipsec_sa_ctx, hw_sa) + - FIELD_SIZEOF(struct mlx5_ifc_fpga_ipsec_sa_v1, cmd), + sizeof_field(struct mlx5_ifc_fpga_ipsec_sa_v1, cmd), .head_offset = offsetof(struct mlx5_fpga_ipsec_sa_ctx, hash), .automatic_shrinking = true, .min_size = 1, diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c index d605774845676..9a48c4310887d 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c @@ -209,7 +209,7 @@ enum fs_i_lock_class { }; static const struct rhashtable_params rhash_fte = { - .key_len = FIELD_SIZEOF(struct fs_fte, val), + .key_len = sizeof_field(struct fs_fte, val), .key_offset = offsetof(struct fs_fte, val), .head_offset = offsetof(struct fs_fte, hash), .automatic_shrinking = true, @@ -217,7 +217,7 @@ static const struct rhashtable_params rhash_fte = { }; static const struct rhashtable_params rhash_fg = { - .key_len = FIELD_SIZEOF(struct mlx5_flow_group, mask), + .key_len = sizeof_field(struct mlx5_flow_group, mask), .key_offset = offsetof(struct mlx5_flow_group, mask), .head_offset = offsetof(struct mlx5_flow_group, hash), .automatic_shrinking = true, diff --git a/drivers/net/ethernet/netronome/nfp/bpf/jit.c b/drivers/net/ethernet/netronome/nfp/bpf/jit.c index c80bb83c8ac92..0a721f6e8676e 100644 --- a/drivers/net/ethernet/netronome/nfp/bpf/jit.c +++ b/drivers/net/ethernet/netronome/nfp/bpf/jit.c @@ -2652,17 +2652,17 @@ static int mem_ldx_skb(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta, switch (meta->insn.off) { case offsetof(struct __sk_buff, len): - if (size != FIELD_SIZEOF(struct __sk_buff, len)) + if (size != sizeof_field(struct __sk_buff, len)) return -EOPNOTSUPP; wrp_mov(nfp_prog, dst, plen_reg(nfp_prog)); break; case offsetof(struct __sk_buff, data): - if (size != FIELD_SIZEOF(struct __sk_buff, data)) + if (size != sizeof_field(struct __sk_buff, data)) return -EOPNOTSUPP; wrp_mov(nfp_prog, dst, pptr_reg(nfp_prog)); break; case offsetof(struct __sk_buff, data_end): - if (size != FIELD_SIZEOF(struct __sk_buff, data_end)) + if (size != sizeof_field(struct __sk_buff, data_end)) return -EOPNOTSUPP; emit_alu(nfp_prog, dst, plen_reg(nfp_prog), ALU_OP_ADD, pptr_reg(nfp_prog)); @@ -2683,12 +2683,12 @@ static int mem_ldx_xdp(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta, switch (meta->insn.off) { case offsetof(struct xdp_md, data): - if (size != FIELD_SIZEOF(struct xdp_md, data)) + if (size != sizeof_field(struct xdp_md, data)) return -EOPNOTSUPP; wrp_mov(nfp_prog, dst, pptr_reg(nfp_prog)); break; case offsetof(struct xdp_md, data_end): - if (size != FIELD_SIZEOF(struct xdp_md, data_end)) + if (size != sizeof_field(struct xdp_md, data_end)) return -EOPNOTSUPP; emit_alu(nfp_prog, dst, plen_reg(nfp_prog), ALU_OP_ADD, pptr_reg(nfp_prog)); diff --git a/drivers/net/ethernet/netronome/nfp/bpf/main.c b/drivers/net/ethernet/netronome/nfp/bpf/main.c index 8f732771d3fad..11c83a99b0140 100644 --- a/drivers/net/ethernet/netronome/nfp/bpf/main.c +++ b/drivers/net/ethernet/netronome/nfp/bpf/main.c @@ -15,7 +15,7 @@ const struct rhashtable_params nfp_bpf_maps_neutral_params = { .nelem_hint = 4, - .key_len = FIELD_SIZEOF(struct bpf_map, id), + .key_len = sizeof_field(struct bpf_map, id), .key_offset = offsetof(struct nfp_bpf_neutral_map, map_id), .head_offset = offsetof(struct nfp_bpf_neutral_map, l), .automatic_shrinking = true, diff --git a/drivers/net/ethernet/netronome/nfp/bpf/offload.c b/drivers/net/ethernet/netronome/nfp/bpf/offload.c index 95a0d3910e316..ac02369174a99 100644 --- a/drivers/net/ethernet/netronome/nfp/bpf/offload.c +++ b/drivers/net/ethernet/netronome/nfp/bpf/offload.c @@ -374,7 +374,7 @@ nfp_bpf_map_alloc(struct nfp_app_bpf *bpf, struct bpf_offloaded_map *offmap) } use_map_size = DIV_ROUND_UP(offmap->map.value_size, 4) * - FIELD_SIZEOF(struct nfp_bpf_map, use_map[0]); + sizeof_field(struct nfp_bpf_map, use_map[0]); nfp_map = kzalloc(sizeof(*nfp_map) + use_map_size, GFP_USER); if (!nfp_map) diff --git a/drivers/net/ethernet/netronome/nfp/flower/main.h b/drivers/net/ethernet/netronome/nfp/flower/main.h index 31d94592a7c02..e0c985fcaec12 100644 --- a/drivers/net/ethernet/netronome/nfp/flower/main.h +++ b/drivers/net/ethernet/netronome/nfp/flower/main.h @@ -24,7 +24,7 @@ struct nfp_app; #define NFP_FL_STAT_ID_MU_NUM GENMASK(31, 22) #define NFP_FL_STAT_ID_STAT GENMASK(21, 0) -#define NFP_FL_STATS_ELEM_RS FIELD_SIZEOF(struct nfp_fl_stats_id, \ +#define NFP_FL_STATS_ELEM_RS sizeof_field(struct nfp_fl_stats_id, \ init_unalloc) #define NFP_FLOWER_MASK_ENTRY_RS 256 #define NFP_FLOWER_MASK_ELEMENT_RS 1 diff --git a/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_ethtool.c b/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_ethtool.c index 1a3008e331824..b36aa5bf3c5fa 100644 --- a/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_ethtool.c +++ b/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_ethtool.c @@ -20,7 +20,7 @@ struct pch_gbe_stats { #define PCH_GBE_STAT(m) \ { \ .string = #m, \ - .size = FIELD_SIZEOF(struct pch_gbe_hw_stats, m), \ + .size = sizeof_field(struct pch_gbe_hw_stats, m), \ .offset = offsetof(struct pch_gbe_hw_stats, m), \ } diff --git a/drivers/net/ethernet/qlogic/qede/qede.h b/drivers/net/ethernet/qlogic/qede/qede.h index c303a92d5b06d..e8a1b27db84de 100644 --- a/drivers/net/ethernet/qlogic/qede/qede.h +++ b/drivers/net/ethernet/qlogic/qede/qede.h @@ -464,7 +464,7 @@ struct qede_fastpath { struct qede_tx_queue *txq; struct qede_tx_queue *xdp_tx; -#define VEC_NAME_SIZE (FIELD_SIZEOF(struct net_device, name) + 8) +#define VEC_NAME_SIZE (sizeof_field(struct net_device, name) + 8) char name[VEC_NAME_SIZE]; }; diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ethtool.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ethtool.c index a4cd6f2cfb862..75d83c3cbf27f 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ethtool.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ethtool.c @@ -20,7 +20,7 @@ struct qlcnic_stats { int stat_offset; }; -#define QLC_SIZEOF(m) FIELD_SIZEOF(struct qlcnic_adapter, m) +#define QLC_SIZEOF(m) sizeof_field(struct qlcnic_adapter, m) #define QLC_OFF(m) offsetof(struct qlcnic_adapter, m) static const u32 qlcnic_fw_dump_level[] = { 0x3, 0x7, 0xf, 0x1f, 0x3f, 0x7f, 0xff diff --git a/drivers/net/ethernet/realtek/r8169_firmware.c b/drivers/net/ethernet/realtek/r8169_firmware.c index 355cc810e3224..cbc6b846ded54 100644 --- a/drivers/net/ethernet/realtek/r8169_firmware.c +++ b/drivers/net/ethernet/realtek/r8169_firmware.c @@ -37,7 +37,7 @@ struct fw_info { u8 chksum; } __packed; -#define FW_OPCODE_SIZE FIELD_SIZEOF(struct rtl_fw_phy_action, code[0]) +#define FW_OPCODE_SIZE sizeof_field(struct rtl_fw_phy_action, code[0]) static bool rtl_fw_format_ok(struct rtl_fw *rtl_fw) { diff --git a/drivers/net/ethernet/samsung/sxgbe/sxgbe_ethtool.c b/drivers/net/ethernet/samsung/sxgbe/sxgbe_ethtool.c index 0775b9464b4ea..466483c4ac672 100644 --- a/drivers/net/ethernet/samsung/sxgbe/sxgbe_ethtool.c +++ b/drivers/net/ethernet/samsung/sxgbe/sxgbe_ethtool.c @@ -30,7 +30,7 @@ struct sxgbe_stats { #define SXGBE_STAT(m) \ { \ #m, \ - FIELD_SIZEOF(struct sxgbe_extra_stats, m), \ + sizeof_field(struct sxgbe_extra_stats, m), \ offsetof(struct sxgbe_priv_data, xstats.m) \ } diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c index 1a768837ca728..b29603ec744c4 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c @@ -34,7 +34,7 @@ struct stmmac_stats { }; #define STMMAC_STAT(m) \ - { #m, FIELD_SIZEOF(struct stmmac_extra_stats, m), \ + { #m, sizeof_field(struct stmmac_extra_stats, m), \ offsetof(struct stmmac_priv, xstats.m)} static const struct stmmac_stats stmmac_gstrings_stats[] = { @@ -163,7 +163,7 @@ static const struct stmmac_stats stmmac_gstrings_stats[] = { /* HW MAC Management counters (if supported) */ #define STMMAC_MMC_STAT(m) \ - { #m, FIELD_SIZEOF(struct stmmac_counters, m), \ + { #m, sizeof_field(struct stmmac_counters, m), \ offsetof(struct stmmac_priv, mmc.m)} static const struct stmmac_stats stmmac_mmc[] = { diff --git a/drivers/net/ethernet/ti/cpsw_ethtool.c b/drivers/net/ethernet/ti/cpsw_ethtool.c index 31248a6cc642e..fa54efe3be635 100644 --- a/drivers/net/ethernet/ti/cpsw_ethtool.c +++ b/drivers/net/ethernet/ti/cpsw_ethtool.c @@ -73,13 +73,13 @@ enum { }; #define CPSW_STAT(m) CPSW_STATS, \ - FIELD_SIZEOF(struct cpsw_hw_stats, m), \ + sizeof_field(struct cpsw_hw_stats, m), \ offsetof(struct cpsw_hw_stats, m) #define CPDMA_RX_STAT(m) CPDMA_RX_STATS, \ - FIELD_SIZEOF(struct cpdma_chan_stats, m), \ + sizeof_field(struct cpdma_chan_stats, m), \ offsetof(struct cpdma_chan_stats, m) #define CPDMA_TX_STAT(m) CPDMA_TX_STATS, \ - FIELD_SIZEOF(struct cpdma_chan_stats, m), \ + sizeof_field(struct cpdma_chan_stats, m), \ offsetof(struct cpdma_chan_stats, m) static const struct cpsw_stats cpsw_gstrings_stats[] = { diff --git a/drivers/net/ethernet/ti/netcp_ethss.c b/drivers/net/ethernet/ti/netcp_ethss.c index 86a3f42a3dcc0..d6a192c1f3378 100644 --- a/drivers/net/ethernet/ti/netcp_ethss.c +++ b/drivers/net/ethernet/ti/netcp_ethss.c @@ -783,28 +783,28 @@ struct netcp_ethtool_stat { #define GBE_STATSA_INFO(field) \ { \ "GBE_A:"#field, GBE_STATSA_MODULE, \ - FIELD_SIZEOF(struct gbe_hw_stats, field), \ + sizeof_field(struct gbe_hw_stats, field), \ offsetof(struct gbe_hw_stats, field) \ } #define GBE_STATSB_INFO(field) \ { \ "GBE_B:"#field, GBE_STATSB_MODULE, \ - FIELD_SIZEOF(struct gbe_hw_stats, field), \ + sizeof_field(struct gbe_hw_stats, field), \ offsetof(struct gbe_hw_stats, field) \ } #define GBE_STATSC_INFO(field) \ { \ "GBE_C:"#field, GBE_STATSC_MODULE, \ - FIELD_SIZEOF(struct gbe_hw_stats, field), \ + sizeof_field(struct gbe_hw_stats, field), \ offsetof(struct gbe_hw_stats, field) \ } #define GBE_STATSD_INFO(field) \ { \ "GBE_D:"#field, GBE_STATSD_MODULE, \ - FIELD_SIZEOF(struct gbe_hw_stats, field), \ + sizeof_field(struct gbe_hw_stats, field), \ offsetof(struct gbe_hw_stats, field) \ } @@ -957,7 +957,7 @@ static const struct netcp_ethtool_stat gbe13_et_stats[] = { #define GBENU_STATS_HOST(field) \ { \ "GBE_HOST:"#field, GBENU_STATS0_MODULE, \ - FIELD_SIZEOF(struct gbenu_hw_stats, field), \ + sizeof_field(struct gbenu_hw_stats, field), \ offsetof(struct gbenu_hw_stats, field) \ } @@ -967,56 +967,56 @@ static const struct netcp_ethtool_stat gbe13_et_stats[] = { #define GBENU_STATS_P1(field) \ { \ "GBE_P1:"#field, GBENU_STATS1_MODULE, \ - FIELD_SIZEOF(struct gbenu_hw_stats, field), \ + sizeof_field(struct gbenu_hw_stats, field), \ offsetof(struct gbenu_hw_stats, field) \ } #define GBENU_STATS_P2(field) \ { \ "GBE_P2:"#field, GBENU_STATS2_MODULE, \ - FIELD_SIZEOF(struct gbenu_hw_stats, field), \ + sizeof_field(struct gbenu_hw_stats, field), \ offsetof(struct gbenu_hw_stats, field) \ } #define GBENU_STATS_P3(field) \ { \ "GBE_P3:"#field, GBENU_STATS3_MODULE, \ - FIELD_SIZEOF(struct gbenu_hw_stats, field), \ + sizeof_field(struct gbenu_hw_stats, field), \ offsetof(struct gbenu_hw_stats, field) \ } #define GBENU_STATS_P4(field) \ { \ "GBE_P4:"#field, GBENU_STATS4_MODULE, \ - FIELD_SIZEOF(struct gbenu_hw_stats, field), \ + sizeof_field(struct gbenu_hw_stats, field), \ offsetof(struct gbenu_hw_stats, field) \ } #define GBENU_STATS_P5(field) \ { \ "GBE_P5:"#field, GBENU_STATS5_MODULE, \ - FIELD_SIZEOF(struct gbenu_hw_stats, field), \ + sizeof_field(struct gbenu_hw_stats, field), \ offsetof(struct gbenu_hw_stats, field) \ } #define GBENU_STATS_P6(field) \ { \ "GBE_P6:"#field, GBENU_STATS6_MODULE, \ - FIELD_SIZEOF(struct gbenu_hw_stats, field), \ + sizeof_field(struct gbenu_hw_stats, field), \ offsetof(struct gbenu_hw_stats, field) \ } #define GBENU_STATS_P7(field) \ { \ "GBE_P7:"#field, GBENU_STATS7_MODULE, \ - FIELD_SIZEOF(struct gbenu_hw_stats, field), \ + sizeof_field(struct gbenu_hw_stats, field), \ offsetof(struct gbenu_hw_stats, field) \ } #define GBENU_STATS_P8(field) \ { \ "GBE_P8:"#field, GBENU_STATS8_MODULE, \ - FIELD_SIZEOF(struct gbenu_hw_stats, field), \ + sizeof_field(struct gbenu_hw_stats, field), \ offsetof(struct gbenu_hw_stats, field) \ } @@ -1607,21 +1607,21 @@ static const struct netcp_ethtool_stat gbenu_et_stats[] = { #define XGBE_STATS0_INFO(field) \ { \ "GBE_0:"#field, XGBE_STATS0_MODULE, \ - FIELD_SIZEOF(struct xgbe_hw_stats, field), \ + sizeof_field(struct xgbe_hw_stats, field), \ offsetof(struct xgbe_hw_stats, field) \ } #define XGBE_STATS1_INFO(field) \ { \ "GBE_1:"#field, XGBE_STATS1_MODULE, \ - FIELD_SIZEOF(struct xgbe_hw_stats, field), \ + sizeof_field(struct xgbe_hw_stats, field), \ offsetof(struct xgbe_hw_stats, field) \ } #define XGBE_STATS2_INFO(field) \ { \ "GBE_2:"#field, XGBE_STATS2_MODULE, \ - FIELD_SIZEOF(struct xgbe_hw_stats, field), \ + sizeof_field(struct xgbe_hw_stats, field), \ offsetof(struct xgbe_hw_stats, field) \ } diff --git a/drivers/net/fjes/fjes_ethtool.c b/drivers/net/fjes/fjes_ethtool.c index 09f3604cfbf8f..746736c838737 100644 --- a/drivers/net/fjes/fjes_ethtool.c +++ b/drivers/net/fjes/fjes_ethtool.c @@ -21,7 +21,7 @@ struct fjes_stats { #define FJES_STAT(name, stat) { \ .stat_string = name, \ - .sizeof_stat = FIELD_SIZEOF(struct fjes_adapter, stat), \ + .sizeof_stat = sizeof_field(struct fjes_adapter, stat), \ .stat_offset = offsetof(struct fjes_adapter, stat) \ } diff --git a/drivers/net/geneve.c b/drivers/net/geneve.c index 5c6b7fc04ea62..75757e9954ba2 100644 --- a/drivers/net/geneve.c +++ b/drivers/net/geneve.c @@ -1156,7 +1156,7 @@ static void geneve_setup(struct net_device *dev) static const struct nla_policy geneve_policy[IFLA_GENEVE_MAX + 1] = { [IFLA_GENEVE_ID] = { .type = NLA_U32 }, - [IFLA_GENEVE_REMOTE] = { .len = FIELD_SIZEOF(struct iphdr, daddr) }, + [IFLA_GENEVE_REMOTE] = { .len = sizeof_field(struct iphdr, daddr) }, [IFLA_GENEVE_REMOTE6] = { .len = sizeof(struct in6_addr) }, [IFLA_GENEVE_TTL] = { .type = NLA_U8 }, [IFLA_GENEVE_TOS] = { .type = NLA_U8 }, diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c index eff8fef4f775f..02e66473f2ed1 100644 --- a/drivers/net/hyperv/netvsc_drv.c +++ b/drivers/net/hyperv/netvsc_drv.c @@ -571,7 +571,7 @@ static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net) /* Use the skb control buffer for building up the packet */ BUILD_BUG_ON(sizeof(struct hv_netvsc_packet) > - FIELD_SIZEOF(struct sk_buff, cb)); + sizeof_field(struct sk_buff, cb)); packet = (struct hv_netvsc_packet *)skb->cb; packet->q_idx = skb_get_queue_mapping(skb); diff --git a/drivers/net/usb/sierra_net.c b/drivers/net/usb/sierra_net.c index 34c1eaba536c0..389d19dd7909c 100644 --- a/drivers/net/usb/sierra_net.c +++ b/drivers/net/usb/sierra_net.c @@ -865,7 +865,7 @@ static struct sk_buff *sierra_net_tx_fixup(struct usbnet *dev, u16 len; bool need_tail; - BUILD_BUG_ON(FIELD_SIZEOF(struct usbnet, data) + BUILD_BUG_ON(sizeof_field(struct usbnet, data) < sizeof(struct cdc_state)); dev_dbg(&dev->udev->dev, "%s", __func__); diff --git a/drivers/net/usb/usbnet.c b/drivers/net/usb/usbnet.c index 30e511c2c8d01..9ce6d30576dde 100644 --- a/drivers/net/usb/usbnet.c +++ b/drivers/net/usb/usbnet.c @@ -2184,7 +2184,7 @@ static int __init usbnet_init(void) { /* Compiler should optimize this out. */ BUILD_BUG_ON( - FIELD_SIZEOF(struct sk_buff, cb) < sizeof(struct skb_data)); + sizeof_field(struct sk_buff, cb) < sizeof(struct skb_data)); eth_random_addr(node_id); return 0; diff --git a/drivers/net/vxlan.c b/drivers/net/vxlan.c index 4c34375c2e220..3ec6b506033d8 100644 --- a/drivers/net/vxlan.c +++ b/drivers/net/vxlan.c @@ -3069,10 +3069,10 @@ static void vxlan_raw_setup(struct net_device *dev) static const struct nla_policy vxlan_policy[IFLA_VXLAN_MAX + 1] = { [IFLA_VXLAN_ID] = { .type = NLA_U32 }, - [IFLA_VXLAN_GROUP] = { .len = FIELD_SIZEOF(struct iphdr, daddr) }, + [IFLA_VXLAN_GROUP] = { .len = sizeof_field(struct iphdr, daddr) }, [IFLA_VXLAN_GROUP6] = { .len = sizeof(struct in6_addr) }, [IFLA_VXLAN_LINK] = { .type = NLA_U32 }, - [IFLA_VXLAN_LOCAL] = { .len = FIELD_SIZEOF(struct iphdr, saddr) }, + [IFLA_VXLAN_LOCAL] = { .len = sizeof_field(struct iphdr, saddr) }, [IFLA_VXLAN_LOCAL6] = { .len = sizeof(struct in6_addr) }, [IFLA_VXLAN_TOS] = { .type = NLA_U8 }, [IFLA_VXLAN_TTL] = { .type = NLA_U8 }, diff --git a/drivers/net/wireless/marvell/libertas/debugfs.c b/drivers/net/wireless/marvell/libertas/debugfs.c index fe14814af3007..c604613ab506d 100644 --- a/drivers/net/wireless/marvell/libertas/debugfs.c +++ b/drivers/net/wireless/marvell/libertas/debugfs.c @@ -774,7 +774,7 @@ void lbs_debugfs_remove_one(struct lbs_private *priv) #ifdef PROC_DEBUG -#define item_size(n) (FIELD_SIZEOF(struct lbs_private, n)) +#define item_size(n) (sizeof_field(struct lbs_private, n)) #define item_addr(n) (offsetof(struct lbs_private, n)) diff --git a/drivers/net/wireless/marvell/mwifiex/util.h b/drivers/net/wireless/marvell/mwifiex/util.h index c386992abcdb8..7cafcecd7b856 100644 --- a/drivers/net/wireless/marvell/mwifiex/util.h +++ b/drivers/net/wireless/marvell/mwifiex/util.h @@ -36,11 +36,11 @@ struct mwifiex_cb { }; /* size/addr for mwifiex_debug_info */ -#define item_size(n) (FIELD_SIZEOF(struct mwifiex_debug_info, n)) +#define item_size(n) (sizeof_field(struct mwifiex_debug_info, n)) #define item_addr(n) (offsetof(struct mwifiex_debug_info, n)) /* size/addr for struct mwifiex_adapter */ -#define adapter_item_size(n) (FIELD_SIZEOF(struct mwifiex_adapter, n)) +#define adapter_item_size(n) (sizeof_field(struct mwifiex_adapter, n)) #define adapter_item_addr(n) (offsetof(struct mwifiex_adapter, n)) struct mwifiex_debug_data { diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c index b9a2349e4b909..33a62a6692c05 100644 --- a/drivers/s390/net/qeth_core_main.c +++ b/drivers/s390/net/qeth_core_main.c @@ -4779,7 +4779,7 @@ static int qeth_qdio_establish(struct qeth_card *card) QETH_CARD_TEXT(card, 2, "qdioest"); - qib_param_field = kzalloc(FIELD_SIZEOF(struct qib, parm), GFP_KERNEL); + qib_param_field = kzalloc(sizeof_field(struct qib, parm), GFP_KERNEL); if (!qib_param_field) { rc = -ENOMEM; goto out_free_nothing; diff --git a/drivers/s390/net/qeth_core_mpc.h b/drivers/s390/net/qeth_core_mpc.h index 88f4dc140751c..c1ecce95094d8 100644 --- a/drivers/s390/net/qeth_core_mpc.h +++ b/drivers/s390/net/qeth_core_mpc.h @@ -421,7 +421,7 @@ struct qeth_ipacmd_setassparms { } data; } __attribute__ ((packed)); -#define SETASS_DATA_SIZEOF(field) FIELD_SIZEOF(struct qeth_ipacmd_setassparms,\ +#define SETASS_DATA_SIZEOF(field) sizeof_field(struct qeth_ipacmd_setassparms,\ data.field) /* SETRTG IPA Command: ****************************************************/ @@ -535,7 +535,7 @@ struct qeth_ipacmd_setadpparms { } data; } __attribute__ ((packed)); -#define SETADP_DATA_SIZEOF(field) FIELD_SIZEOF(struct qeth_ipacmd_setadpparms,\ +#define SETADP_DATA_SIZEOF(field) sizeof_field(struct qeth_ipacmd_setadpparms,\ data.field) /* CREATE_ADDR IPA Command: ***********************************************/ @@ -648,7 +648,7 @@ struct qeth_ipacmd_vnicc { } data; }; -#define VNICC_DATA_SIZEOF(field) FIELD_SIZEOF(struct qeth_ipacmd_vnicc,\ +#define VNICC_DATA_SIZEOF(field) sizeof_field(struct qeth_ipacmd_vnicc,\ data.field) /* SETBRIDGEPORT IPA Command: *********************************************/ @@ -729,7 +729,7 @@ struct qeth_ipacmd_setbridgeport { } data; } __packed; -#define SBP_DATA_SIZEOF(field) FIELD_SIZEOF(struct qeth_ipacmd_setbridgeport,\ +#define SBP_DATA_SIZEOF(field) sizeof_field(struct qeth_ipacmd_setbridgeport,\ data.field) /* ADDRESS_CHANGE_NOTIFICATION adapter-initiated "command" *******************/ @@ -790,7 +790,7 @@ struct qeth_ipa_cmd { } data; } __attribute__ ((packed)); -#define IPA_DATA_SIZEOF(field) FIELD_SIZEOF(struct qeth_ipa_cmd, data.field) +#define IPA_DATA_SIZEOF(field) sizeof_field(struct qeth_ipa_cmd, data.field) /* * special command for ARP processing. diff --git a/drivers/scsi/aacraid/aachba.c b/drivers/scsi/aacraid/aachba.c index e36608ce937ab..33dbc051bff97 100644 --- a/drivers/scsi/aacraid/aachba.c +++ b/drivers/scsi/aacraid/aachba.c @@ -535,7 +535,7 @@ static void get_container_name_callback(void *context, struct fib * fibptr) if ((le32_to_cpu(get_name_reply->status) == CT_OK) && (get_name_reply->data[0] != '\0')) { char *sp = get_name_reply->data; - int data_size = FIELD_SIZEOF(struct aac_get_name_resp, data); + int data_size = sizeof_field(struct aac_get_name_resp, data); sp[data_size - 1] = '\0'; while (*sp == ' ') @@ -574,7 +574,7 @@ static int aac_get_container_name(struct scsi_cmnd * scsicmd) dev = (struct aac_dev *)scsicmd->device->host->hostdata; - data_size = FIELD_SIZEOF(struct aac_get_name_resp, data); + data_size = sizeof_field(struct aac_get_name_resp, data); cmd_fibcontext = aac_fib_alloc_tag(dev, scsicmd); diff --git a/drivers/scsi/be2iscsi/be_cmds.h b/drivers/scsi/be2iscsi/be_cmds.h index 063dccc18f70a..5f9f0b18ddf37 100644 --- a/drivers/scsi/be2iscsi/be_cmds.h +++ b/drivers/scsi/be2iscsi/be_cmds.h @@ -1300,7 +1300,7 @@ struct be_cmd_get_port_name { /* Returns the number of items in the field array. */ #define BE_NUMBER_OF_FIELD(_type_, _field_) \ - (FIELD_SIZEOF(_type_, _field_)/sizeof((((_type_ *)0)->_field_[0])))\ + (sizeof_field(_type_, _field_)/sizeof((((_type_ *)0)->_field_[0])))\ /** * Different types of iSCSI completions to host driver for both initiator diff --git a/drivers/scsi/cxgbi/libcxgbi.c b/drivers/scsi/cxgbi/libcxgbi.c index 0d044c1659609..c4e4b0136f86e 100644 --- a/drivers/scsi/cxgbi/libcxgbi.c +++ b/drivers/scsi/cxgbi/libcxgbi.c @@ -2746,7 +2746,7 @@ static int __init libcxgbi_init_module(void) { pr_info("%s", version); - BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, cb) < + BUILD_BUG_ON(sizeof_field(struct sk_buff, cb) < sizeof(struct cxgbi_skb_cb)); return 0; } diff --git a/drivers/scsi/smartpqi/smartpqi_init.c b/drivers/scsi/smartpqi/smartpqi_init.c index 7b7ef3acb504c..412ac56ecd602 100644 --- a/drivers/scsi/smartpqi/smartpqi_init.c +++ b/drivers/scsi/smartpqi/smartpqi_init.c @@ -8689,11 +8689,11 @@ static void __attribute__((unused)) verify_structures(void) BUILD_BUG_ON(offsetof(struct pqi_general_admin_request, data.delete_operational_queue.queue_id) != 12); BUILD_BUG_ON(sizeof(struct pqi_general_admin_request) != 64); - BUILD_BUG_ON(FIELD_SIZEOF(struct pqi_general_admin_request, + BUILD_BUG_ON(sizeof_field(struct pqi_general_admin_request, data.create_operational_iq) != 64 - 11); - BUILD_BUG_ON(FIELD_SIZEOF(struct pqi_general_admin_request, + BUILD_BUG_ON(sizeof_field(struct pqi_general_admin_request, data.create_operational_oq) != 64 - 11); - BUILD_BUG_ON(FIELD_SIZEOF(struct pqi_general_admin_request, + BUILD_BUG_ON(sizeof_field(struct pqi_general_admin_request, data.delete_operational_queue) != 64 - 11); BUILD_BUG_ON(offsetof(struct pqi_general_admin_response, diff --git a/drivers/staging/qlge/qlge_ethtool.c b/drivers/staging/qlge/qlge_ethtool.c index a6886cc5654ce..56d116d79e56a 100644 --- a/drivers/staging/qlge/qlge_ethtool.c +++ b/drivers/staging/qlge/qlge_ethtool.c @@ -41,7 +41,7 @@ struct ql_stats { int stat_offset; }; -#define QL_SIZEOF(m) FIELD_SIZEOF(struct ql_adapter, m) +#define QL_SIZEOF(m) sizeof_field(struct ql_adapter, m) #define QL_OFF(m) offsetof(struct ql_adapter, m) static const struct ql_stats ql_gstrings_stats[] = { diff --git a/drivers/staging/wfx/data_tx.c b/drivers/staging/wfx/data_tx.c index b722e9773232b..df2640a79f02f 100644 --- a/drivers/staging/wfx/data_tx.c +++ b/drivers/staging/wfx/data_tx.c @@ -679,7 +679,7 @@ void wfx_tx(struct ieee80211_hw *hw, struct ieee80211_tx_control *control, struct ieee80211_sta *sta = control ? control->sta : NULL; struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; - size_t driver_data_room = FIELD_SIZEOF(struct ieee80211_tx_info, + size_t driver_data_room = sizeof_field(struct ieee80211_tx_info, rate_driver_data); compiletime_assert(sizeof(struct wfx_tx_priv) <= driver_data_room, diff --git a/drivers/target/iscsi/cxgbit/cxgbit_main.c b/drivers/target/iscsi/cxgbit/cxgbit_main.c index e877b917c15f5..30ea37e1a3f5e 100644 --- a/drivers/target/iscsi/cxgbit/cxgbit_main.c +++ b/drivers/target/iscsi/cxgbit/cxgbit_main.c @@ -708,7 +708,7 @@ static int __init cxgbit_init(void) pr_info("%s dcb enabled.\n", DRV_NAME); register_dcbevent_notifier(&cxgbit_dcbevent_nb); #endif - BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, cb) < + BUILD_BUG_ON(sizeof_field(struct sk_buff, cb) < sizeof(union cxgbit_skb_cb)); return 0; } diff --git a/drivers/usb/atm/usbatm.c b/drivers/usb/atm/usbatm.c index dbea28495e1dd..4e12a32ca392d 100644 --- a/drivers/usb/atm/usbatm.c +++ b/drivers/usb/atm/usbatm.c @@ -1275,7 +1275,7 @@ EXPORT_SYMBOL_GPL(usbatm_usb_disconnect); static int __init usbatm_usb_init(void) { - if (sizeof(struct usbatm_control) > FIELD_SIZEOF(struct sk_buff, cb)) { + if (sizeof(struct usbatm_control) > sizeof_field(struct sk_buff, cb)) { printk(KERN_ERR "%s unusable with this kernel!\n", usbatm_driver_name); return -EIO; } diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c index ce1d0235969c3..0bbccac94d6c5 100644 --- a/drivers/usb/gadget/function/f_fs.c +++ b/drivers/usb/gadget/function/f_fs.c @@ -3509,7 +3509,7 @@ static void ffs_free_inst(struct usb_function_instance *f) static int ffs_set_inst_name(struct usb_function_instance *fi, const char *name) { - if (strlen(name) >= FIELD_SIZEOF(struct ffs_dev, name)) + if (strlen(name) >= sizeof_field(struct ffs_dev, name)) return -ENAMETOOLONG; return ffs_name_dev(to_f_fs_opts(fi)->dev, name); } diff --git a/fs/crypto/keyring.c b/fs/crypto/keyring.c index 040df1f5e1c8b..40cca351273f4 100644 --- a/fs/crypto/keyring.c +++ b/fs/crypto/keyring.c @@ -151,7 +151,7 @@ static struct key *search_fscrypt_keyring(struct key *keyring, } #define FSCRYPT_FS_KEYRING_DESCRIPTION_SIZE \ - (CONST_STRLEN("fscrypt-") + FIELD_SIZEOF(struct super_block, s_id)) + (CONST_STRLEN("fscrypt-") + sizeof_field(struct super_block, s_id)) #define FSCRYPT_MK_DESCRIPTION_SIZE (2 * FSCRYPT_KEY_IDENTIFIER_SIZE + 1) diff --git a/fs/verity/enable.c b/fs/verity/enable.c index eabc6ac199064..b79e3fd19d115 100644 --- a/fs/verity/enable.c +++ b/fs/verity/enable.c @@ -315,7 +315,7 @@ int fsverity_ioctl_enable(struct file *filp, const void __user *uarg) if (arg.block_size != PAGE_SIZE) return -EINVAL; - if (arg.salt_size > FIELD_SIZEOF(struct fsverity_descriptor, salt)) + if (arg.salt_size > sizeof_field(struct fsverity_descriptor, salt)) return -EMSGSIZE; if (arg.sig_size > FS_VERITY_MAX_SIGNATURE_SIZE) diff --git a/include/linux/filter.h b/include/linux/filter.h index a141cb07e76a7..345f3748e0fb7 100644 --- a/include/linux/filter.h +++ b/include/linux/filter.h @@ -420,7 +420,7 @@ static inline bool insn_is_zext(const struct bpf_insn *insn) #define BPF_FIELD_SIZEOF(type, field) \ ({ \ - const int __size = bytes_to_bpf_size(FIELD_SIZEOF(type, field)); \ + const int __size = bytes_to_bpf_size(sizeof_field(type, field)); \ BUILD_BUG_ON(__size < 0); \ __size; \ }) @@ -497,7 +497,7 @@ static inline bool insn_is_zext(const struct bpf_insn *insn) #define bpf_target_off(TYPE, MEMBER, SIZE, PTR_SIZE) \ ({ \ - BUILD_BUG_ON(FIELD_SIZEOF(TYPE, MEMBER) != (SIZE)); \ + BUILD_BUG_ON(sizeof_field(TYPE, MEMBER) != (SIZE)); \ *(PTR_SIZE) = (SIZE); \ offsetof(TYPE, MEMBER); \ }) @@ -608,7 +608,7 @@ static inline void bpf_compute_data_pointers(struct sk_buff *skb) { struct bpf_skb_data_end *cb = (struct bpf_skb_data_end *)skb->cb; - BUILD_BUG_ON(sizeof(*cb) > FIELD_SIZEOF(struct sk_buff, cb)); + BUILD_BUG_ON(sizeof(*cb) > sizeof_field(struct sk_buff, cb)); cb->data_meta = skb->data - skb_metadata_len(skb); cb->data_end = skb->data + skb_headlen(skb); } @@ -646,9 +646,9 @@ static inline u8 *bpf_skb_cb(struct sk_buff *skb) * attached to sockets, we need to clear the bpf_skb_cb() area * to not leak previous contents to user space. */ - BUILD_BUG_ON(FIELD_SIZEOF(struct __sk_buff, cb) != BPF_SKB_CB_LEN); - BUILD_BUG_ON(FIELD_SIZEOF(struct __sk_buff, cb) != - FIELD_SIZEOF(struct qdisc_skb_cb, data)); + BUILD_BUG_ON(sizeof_field(struct __sk_buff, cb) != BPF_SKB_CB_LEN); + BUILD_BUG_ON(sizeof_field(struct __sk_buff, cb) != + sizeof_field(struct qdisc_skb_cb, data)); return qdisc_skb_cb(skb)->data; } diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h index 7ed1e2f8641e2..538c25e778c07 100644 --- a/include/linux/kvm_host.h +++ b/include/linux/kvm_host.h @@ -149,7 +149,7 @@ static inline bool is_error_page(struct page *page) #define KVM_REQUEST_ARCH_BASE 8 #define KVM_ARCH_REQ_FLAGS(nr, flags) ({ \ - BUILD_BUG_ON((unsigned)(nr) >= (FIELD_SIZEOF(struct kvm_vcpu, requests) * 8) - KVM_REQUEST_ARCH_BASE); \ + BUILD_BUG_ON((unsigned)(nr) >= (sizeof_field(struct kvm_vcpu, requests) * 8) - KVM_REQUEST_ARCH_BASE); \ (unsigned)(((nr) + KVM_REQUEST_ARCH_BASE) | (flags)); \ }) #define KVM_ARCH_REQ(nr) KVM_ARCH_REQ_FLAGS(nr, 0) diff --git a/include/linux/phy_led_triggers.h b/include/linux/phy_led_triggers.h index 3d507a8a69892..5c4d7a7551011 100644 --- a/include/linux/phy_led_triggers.h +++ b/include/linux/phy_led_triggers.h @@ -14,7 +14,7 @@ struct phy_device; #define PHY_LED_TRIGGER_SPEED_SUFFIX_SIZE 11 #define PHY_LINK_LED_TRIGGER_NAME_SIZE (MII_BUS_ID_SIZE + \ - FIELD_SIZEOF(struct mdio_device, addr)+\ + sizeof_field(struct mdio_device, addr)+\ PHY_LED_TRIGGER_SPEED_SUFFIX_SIZE) struct phy_led_trigger { diff --git a/include/net/garp.h b/include/net/garp.h index c41833bd4590c..4d9a0c6a2e5f3 100644 --- a/include/net/garp.h +++ b/include/net/garp.h @@ -37,7 +37,7 @@ struct garp_skb_cb { static inline struct garp_skb_cb *garp_cb(struct sk_buff *skb) { BUILD_BUG_ON(sizeof(struct garp_skb_cb) > - FIELD_SIZEOF(struct sk_buff, cb)); + sizeof_field(struct sk_buff, cb)); return (struct garp_skb_cb *)skb->cb; } diff --git a/include/net/ip_tunnels.h b/include/net/ip_tunnels.h index af645604f3289..236503a50759a 100644 --- a/include/net/ip_tunnels.h +++ b/include/net/ip_tunnels.h @@ -33,8 +33,8 @@ /* Used to memset ipv4 address padding. */ #define IP_TUNNEL_KEY_IPV4_PAD offsetofend(struct ip_tunnel_key, u.ipv4.dst) #define IP_TUNNEL_KEY_IPV4_PAD_LEN \ - (FIELD_SIZEOF(struct ip_tunnel_key, u) - \ - FIELD_SIZEOF(struct ip_tunnel_key, u.ipv4)) + (sizeof_field(struct ip_tunnel_key, u) - \ + sizeof_field(struct ip_tunnel_key, u.ipv4)) struct ip_tunnel_key { __be64 tun_id; @@ -63,7 +63,7 @@ struct ip_tunnel_key { /* Maximum tunnel options length. */ #define IP_TUNNEL_OPTS_MAX \ - GENMASK((FIELD_SIZEOF(struct ip_tunnel_info, \ + GENMASK((sizeof_field(struct ip_tunnel_info, \ options_len) * BITS_PER_BYTE) - 1, 0) struct ip_tunnel_info { diff --git a/include/net/mrp.h b/include/net/mrp.h index ef58b4a071900..1c308c034e1a6 100644 --- a/include/net/mrp.h +++ b/include/net/mrp.h @@ -39,7 +39,7 @@ struct mrp_skb_cb { static inline struct mrp_skb_cb *mrp_cb(struct sk_buff *skb) { BUILD_BUG_ON(sizeof(struct mrp_skb_cb) > - FIELD_SIZEOF(struct sk_buff, cb)); + sizeof_field(struct sk_buff, cb)); return (struct mrp_skb_cb *)skb->cb; } diff --git a/include/net/netfilter/nf_conntrack_helper.h b/include/net/netfilter/nf_conntrack_helper.h index 44b5a00a9c64c..37f0fbefb060f 100644 --- a/include/net/netfilter/nf_conntrack_helper.h +++ b/include/net/netfilter/nf_conntrack_helper.h @@ -81,7 +81,7 @@ struct nf_conn_help { }; #define NF_CT_HELPER_BUILD_BUG_ON(structsize) \ - BUILD_BUG_ON((structsize) > FIELD_SIZEOF(struct nf_conn_help, data)) + BUILD_BUG_ON((structsize) > sizeof_field(struct nf_conn_help, data)) struct nf_conntrack_helper *__nf_conntrack_helper_find(const char *name, u16 l3num, u8 protonum); diff --git a/include/net/netfilter/nf_tables_core.h b/include/net/netfilter/nf_tables_core.h index 7281895fa6d99..2656155b40697 100644 --- a/include/net/netfilter/nf_tables_core.h +++ b/include/net/netfilter/nf_tables_core.h @@ -41,7 +41,7 @@ struct nft_immediate_expr { */ static inline u32 nft_cmp_fast_mask(unsigned int len) { - return cpu_to_le32(~0U >> (FIELD_SIZEOF(struct nft_cmp_fast_expr, + return cpu_to_le32(~0U >> (sizeof_field(struct nft_cmp_fast_expr, data) * BITS_PER_BYTE - len)); } diff --git a/include/net/sock.h b/include/net/sock.h index 87d54ef57f004..80f996406bba8 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -2305,7 +2305,7 @@ struct sock_skb_cb { * using skb->cb[] would keep using it directly and utilize its * alignement guarantee. */ -#define SOCK_SKB_CB_OFFSET ((FIELD_SIZEOF(struct sk_buff, cb) - \ +#define SOCK_SKB_CB_OFFSET ((sizeof_field(struct sk_buff, cb) - \ sizeof(struct sock_skb_cb))) #define SOCK_SKB_CB(__skb) ((struct sock_skb_cb *)((__skb)->cb + \ diff --git a/ipc/util.c b/ipc/util.c index d126d156efc64..915eacb9c059d 100644 --- a/ipc/util.c +++ b/ipc/util.c @@ -100,7 +100,7 @@ device_initcall(ipc_init); static const struct rhashtable_params ipc_kht_params = { .head_offset = offsetof(struct kern_ipc_perm, khtnode), .key_offset = offsetof(struct kern_ipc_perm, key), - .key_len = FIELD_SIZEOF(struct kern_ipc_perm, key), + .key_len = sizeof_field(struct kern_ipc_perm, key), .automatic_shrinking = true, }; diff --git a/kernel/bpf/cgroup.c b/kernel/bpf/cgroup.c index 9f90d3c92bdac..4fb20ab179fee 100644 --- a/kernel/bpf/cgroup.c +++ b/kernel/bpf/cgroup.c @@ -1341,7 +1341,7 @@ static u32 sysctl_convert_ctx_access(enum bpf_access_type type, *insn++ = BPF_LDX_MEM( BPF_SIZE(si->code), si->dst_reg, si->src_reg, bpf_target_off(struct bpf_sysctl_kern, write, - FIELD_SIZEOF(struct bpf_sysctl_kern, + sizeof_field(struct bpf_sysctl_kern, write), target_size)); break; diff --git a/kernel/bpf/local_storage.c b/kernel/bpf/local_storage.c index 2ba750725cb26..6bd22f6d9f416 100644 --- a/kernel/bpf/local_storage.c +++ b/kernel/bpf/local_storage.c @@ -357,7 +357,7 @@ static int cgroup_storage_check_btf(const struct bpf_map *map, * The first field must be a 64 bit integer at 0 offset. */ m = (struct btf_member *)(key_type + 1); - size = FIELD_SIZEOF(struct bpf_cgroup_storage_key, cgroup_inode_id); + size = sizeof_field(struct bpf_cgroup_storage_key, cgroup_inode_id); if (!btf_member_is_reg_int(btf, key_type, m, 0, size)) return -EINVAL; @@ -366,7 +366,7 @@ static int cgroup_storage_check_btf(const struct bpf_map *map, */ m++; offset = offsetof(struct bpf_cgroup_storage_key, attach_type); - size = FIELD_SIZEOF(struct bpf_cgroup_storage_key, attach_type); + size = sizeof_field(struct bpf_cgroup_storage_key, attach_type); if (!btf_member_is_reg_int(btf, key_type, m, offset, size)) return -EINVAL; diff --git a/net/802/mrp.c b/net/802/mrp.c index 2cfdfbfbb2edb..bea6e43d45a0d 100644 --- a/net/802/mrp.c +++ b/net/802/mrp.c @@ -523,7 +523,7 @@ int mrp_request_join(const struct net_device *dev, struct mrp_attr *attr; if (sizeof(struct mrp_skb_cb) + len > - FIELD_SIZEOF(struct sk_buff, cb)) + sizeof_field(struct sk_buff, cb)) return -ENOMEM; spin_lock_bh(&app->lock); @@ -548,7 +548,7 @@ void mrp_request_leave(const struct net_device *dev, struct mrp_attr *attr; if (sizeof(struct mrp_skb_cb) + len > - FIELD_SIZEOF(struct sk_buff, cb)) + sizeof_field(struct sk_buff, cb)) return; spin_lock_bh(&app->lock); @@ -692,7 +692,7 @@ static int mrp_pdu_parse_vecattr(struct mrp_applicant *app, * advance to the next event in its Vector. */ if (sizeof(struct mrp_skb_cb) + mrp_cb(skb)->mh->attrlen > - FIELD_SIZEOF(struct sk_buff, cb)) + sizeof_field(struct sk_buff, cb)) return -1; if (skb_copy_bits(skb, *offset, mrp_cb(skb)->attrvalue, mrp_cb(skb)->mh->attrlen) < 0) diff --git a/net/batman-adv/main.c b/net/batman-adv/main.c index 4a89177def647..4811ec65bc434 100644 --- a/net/batman-adv/main.c +++ b/net/batman-adv/main.c @@ -548,7 +548,7 @@ static void batadv_recv_handler_init(void) BUILD_BUG_ON(sizeof(struct batadv_tvlv_tt_change) != 12); BUILD_BUG_ON(sizeof(struct batadv_tvlv_roam_adv) != 8); - i = FIELD_SIZEOF(struct sk_buff, cb); + i = sizeof_field(struct sk_buff, cb); BUILD_BUG_ON(sizeof(struct batadv_skb_cb) > i); /* broadcast packet */ diff --git a/net/bpf/test_run.c b/net/bpf/test_run.c index 915c2d6f7fb9d..f79205d4444f5 100644 --- a/net/bpf/test_run.c +++ b/net/bpf/test_run.c @@ -253,21 +253,21 @@ static int convert___skb_to_skb(struct sk_buff *skb, struct __sk_buff *__skb) /* priority is allowed */ if (!range_is_zero(__skb, offsetof(struct __sk_buff, priority) + - FIELD_SIZEOF(struct __sk_buff, priority), + sizeof_field(struct __sk_buff, priority), offsetof(struct __sk_buff, cb))) return -EINVAL; /* cb is allowed */ if (!range_is_zero(__skb, offsetof(struct __sk_buff, cb) + - FIELD_SIZEOF(struct __sk_buff, cb), + sizeof_field(struct __sk_buff, cb), offsetof(struct __sk_buff, tstamp))) return -EINVAL; /* tstamp is allowed */ if (!range_is_zero(__skb, offsetof(struct __sk_buff, tstamp) + - FIELD_SIZEOF(struct __sk_buff, tstamp), + sizeof_field(struct __sk_buff, tstamp), sizeof(struct __sk_buff))) return -EINVAL; @@ -438,7 +438,7 @@ static int verify_user_bpf_flow_keys(struct bpf_flow_keys *ctx) /* flags is allowed */ if (!range_is_zero(ctx, offsetof(struct bpf_flow_keys, flags) + - FIELD_SIZEOF(struct bpf_flow_keys, flags), + sizeof_field(struct bpf_flow_keys, flags), sizeof(struct bpf_flow_keys))) return -EINVAL; diff --git a/net/bridge/br.c b/net/bridge/br.c index 8a8f9e5f264f2..b6fe30e3768f8 100644 --- a/net/bridge/br.c +++ b/net/bridge/br.c @@ -312,7 +312,7 @@ static int __init br_init(void) { int err; - BUILD_BUG_ON(sizeof(struct br_input_skb_cb) > FIELD_SIZEOF(struct sk_buff, cb)); + BUILD_BUG_ON(sizeof(struct br_input_skb_cb) > sizeof_field(struct sk_buff, cb)); err = stp_proto_register(&br_stp_proto); if (err < 0) { diff --git a/net/core/dev.c b/net/core/dev.c index 2c277b8aba38b..0ad39c87b7fd2 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -10165,7 +10165,7 @@ static struct hlist_head * __net_init netdev_create_hash(void) static int __net_init netdev_init(struct net *net) { BUILD_BUG_ON(GRO_HASH_BUCKETS > - 8 * FIELD_SIZEOF(struct napi_struct, gro_bitmask)); + 8 * sizeof_field(struct napi_struct, gro_bitmask)); if (net != &init_net) INIT_LIST_HEAD(&net->dev_base_head); diff --git a/net/core/filter.c b/net/core/filter.c index f1e703eed3d22..c19dd0973e0cc 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -274,7 +274,7 @@ static u32 convert_skb_access(int skb_field, int dst_reg, int src_reg, switch (skb_field) { case SKF_AD_MARK: - BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, mark) != 4); + BUILD_BUG_ON(sizeof_field(struct sk_buff, mark) != 4); *insn++ = BPF_LDX_MEM(BPF_W, dst_reg, src_reg, offsetof(struct sk_buff, mark)); @@ -289,14 +289,14 @@ static u32 convert_skb_access(int skb_field, int dst_reg, int src_reg, break; case SKF_AD_QUEUE: - BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, queue_mapping) != 2); + BUILD_BUG_ON(sizeof_field(struct sk_buff, queue_mapping) != 2); *insn++ = BPF_LDX_MEM(BPF_H, dst_reg, src_reg, offsetof(struct sk_buff, queue_mapping)); break; case SKF_AD_VLAN_TAG: - BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, vlan_tci) != 2); + BUILD_BUG_ON(sizeof_field(struct sk_buff, vlan_tci) != 2); /* dst_reg = *(u16 *) (src_reg + offsetof(vlan_tci)) */ *insn++ = BPF_LDX_MEM(BPF_H, dst_reg, src_reg, @@ -322,7 +322,7 @@ static bool convert_bpf_extensions(struct sock_filter *fp, switch (fp->k) { case SKF_AD_OFF + SKF_AD_PROTOCOL: - BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, protocol) != 2); + BUILD_BUG_ON(sizeof_field(struct sk_buff, protocol) != 2); /* A = *(u16 *) (CTX + offsetof(protocol)) */ *insn++ = BPF_LDX_MEM(BPF_H, BPF_REG_A, BPF_REG_CTX, @@ -338,8 +338,8 @@ static bool convert_bpf_extensions(struct sock_filter *fp, case SKF_AD_OFF + SKF_AD_IFINDEX: case SKF_AD_OFF + SKF_AD_HATYPE: - BUILD_BUG_ON(FIELD_SIZEOF(struct net_device, ifindex) != 4); - BUILD_BUG_ON(FIELD_SIZEOF(struct net_device, type) != 2); + BUILD_BUG_ON(sizeof_field(struct net_device, ifindex) != 4); + BUILD_BUG_ON(sizeof_field(struct net_device, type) != 2); *insn++ = BPF_LDX_MEM(BPF_FIELD_SIZEOF(struct sk_buff, dev), BPF_REG_TMP, BPF_REG_CTX, @@ -361,7 +361,7 @@ static bool convert_bpf_extensions(struct sock_filter *fp, break; case SKF_AD_OFF + SKF_AD_RXHASH: - BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, hash) != 4); + BUILD_BUG_ON(sizeof_field(struct sk_buff, hash) != 4); *insn = BPF_LDX_MEM(BPF_W, BPF_REG_A, BPF_REG_CTX, offsetof(struct sk_buff, hash)); @@ -385,7 +385,7 @@ static bool convert_bpf_extensions(struct sock_filter *fp, break; case SKF_AD_OFF + SKF_AD_VLAN_TPID: - BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, vlan_proto) != 2); + BUILD_BUG_ON(sizeof_field(struct sk_buff, vlan_proto) != 2); /* A = *(u16 *) (CTX + offsetof(vlan_proto)) */ *insn++ = BPF_LDX_MEM(BPF_H, BPF_REG_A, BPF_REG_CTX, @@ -5589,8 +5589,8 @@ u32 bpf_tcp_sock_convert_ctx_access(enum bpf_access_type type, #define BPF_TCP_SOCK_GET_COMMON(FIELD) \ do { \ - BUILD_BUG_ON(FIELD_SIZEOF(struct tcp_sock, FIELD) > \ - FIELD_SIZEOF(struct bpf_tcp_sock, FIELD)); \ + BUILD_BUG_ON(sizeof_field(struct tcp_sock, FIELD) > \ + sizeof_field(struct bpf_tcp_sock, FIELD)); \ *insn++ = BPF_LDX_MEM(BPF_FIELD_SIZEOF(struct tcp_sock, FIELD),\ si->dst_reg, si->src_reg, \ offsetof(struct tcp_sock, FIELD)); \ @@ -5598,9 +5598,9 @@ u32 bpf_tcp_sock_convert_ctx_access(enum bpf_access_type type, #define BPF_INET_SOCK_GET_COMMON(FIELD) \ do { \ - BUILD_BUG_ON(FIELD_SIZEOF(struct inet_connection_sock, \ + BUILD_BUG_ON(sizeof_field(struct inet_connection_sock, \ FIELD) > \ - FIELD_SIZEOF(struct bpf_tcp_sock, FIELD)); \ + sizeof_field(struct bpf_tcp_sock, FIELD)); \ *insn++ = BPF_LDX_MEM(BPF_FIELD_SIZEOF( \ struct inet_connection_sock, \ FIELD), \ @@ -5615,7 +5615,7 @@ u32 bpf_tcp_sock_convert_ctx_access(enum bpf_access_type type, switch (si->off) { case offsetof(struct bpf_tcp_sock, rtt_min): - BUILD_BUG_ON(FIELD_SIZEOF(struct tcp_sock, rtt_min) != + BUILD_BUG_ON(sizeof_field(struct tcp_sock, rtt_min) != sizeof(struct minmax)); BUILD_BUG_ON(sizeof(struct minmax) < sizeof(struct minmax_sample)); @@ -5780,8 +5780,8 @@ u32 bpf_xdp_sock_convert_ctx_access(enum bpf_access_type type, #define BPF_XDP_SOCK_GET(FIELD) \ do { \ - BUILD_BUG_ON(FIELD_SIZEOF(struct xdp_sock, FIELD) > \ - FIELD_SIZEOF(struct bpf_xdp_sock, FIELD)); \ + BUILD_BUG_ON(sizeof_field(struct xdp_sock, FIELD) > \ + sizeof_field(struct bpf_xdp_sock, FIELD)); \ *insn++ = BPF_LDX_MEM(BPF_FIELD_SIZEOF(struct xdp_sock, FIELD),\ si->dst_reg, si->src_reg, \ offsetof(struct xdp_sock, FIELD)); \ @@ -7344,7 +7344,7 @@ static u32 bpf_convert_ctx_access(enum bpf_access_type type, case offsetof(struct __sk_buff, cb[0]) ... offsetofend(struct __sk_buff, cb[4]) - 1: - BUILD_BUG_ON(FIELD_SIZEOF(struct qdisc_skb_cb, data) < 20); + BUILD_BUG_ON(sizeof_field(struct qdisc_skb_cb, data) < 20); BUILD_BUG_ON((offsetof(struct sk_buff, cb) + offsetof(struct qdisc_skb_cb, data)) % sizeof(__u64)); @@ -7363,7 +7363,7 @@ static u32 bpf_convert_ctx_access(enum bpf_access_type type, break; case offsetof(struct __sk_buff, tc_classid): - BUILD_BUG_ON(FIELD_SIZEOF(struct qdisc_skb_cb, tc_classid) != 2); + BUILD_BUG_ON(sizeof_field(struct qdisc_skb_cb, tc_classid) != 2); off = si->off; off -= offsetof(struct __sk_buff, tc_classid); @@ -7434,7 +7434,7 @@ static u32 bpf_convert_ctx_access(enum bpf_access_type type, #endif break; case offsetof(struct __sk_buff, family): - BUILD_BUG_ON(FIELD_SIZEOF(struct sock_common, skc_family) != 2); + BUILD_BUG_ON(sizeof_field(struct sock_common, skc_family) != 2); *insn++ = BPF_LDX_MEM(BPF_FIELD_SIZEOF(struct sk_buff, sk), si->dst_reg, si->src_reg, @@ -7445,7 +7445,7 @@ static u32 bpf_convert_ctx_access(enum bpf_access_type type, 2, target_size)); break; case offsetof(struct __sk_buff, remote_ip4): - BUILD_BUG_ON(FIELD_SIZEOF(struct sock_common, skc_daddr) != 4); + BUILD_BUG_ON(sizeof_field(struct sock_common, skc_daddr) != 4); *insn++ = BPF_LDX_MEM(BPF_FIELD_SIZEOF(struct sk_buff, sk), si->dst_reg, si->src_reg, @@ -7456,7 +7456,7 @@ static u32 bpf_convert_ctx_access(enum bpf_access_type type, 4, target_size)); break; case offsetof(struct __sk_buff, local_ip4): - BUILD_BUG_ON(FIELD_SIZEOF(struct sock_common, + BUILD_BUG_ON(sizeof_field(struct sock_common, skc_rcv_saddr) != 4); *insn++ = BPF_LDX_MEM(BPF_FIELD_SIZEOF(struct sk_buff, sk), @@ -7470,7 +7470,7 @@ static u32 bpf_convert_ctx_access(enum bpf_access_type type, case offsetof(struct __sk_buff, remote_ip6[0]) ... offsetof(struct __sk_buff, remote_ip6[3]): #if IS_ENABLED(CONFIG_IPV6) - BUILD_BUG_ON(FIELD_SIZEOF(struct sock_common, + BUILD_BUG_ON(sizeof_field(struct sock_common, skc_v6_daddr.s6_addr32[0]) != 4); off = si->off; @@ -7490,7 +7490,7 @@ static u32 bpf_convert_ctx_access(enum bpf_access_type type, case offsetof(struct __sk_buff, local_ip6[0]) ... offsetof(struct __sk_buff, local_ip6[3]): #if IS_ENABLED(CONFIG_IPV6) - BUILD_BUG_ON(FIELD_SIZEOF(struct sock_common, + BUILD_BUG_ON(sizeof_field(struct sock_common, skc_v6_rcv_saddr.s6_addr32[0]) != 4); off = si->off; @@ -7509,7 +7509,7 @@ static u32 bpf_convert_ctx_access(enum bpf_access_type type, break; case offsetof(struct __sk_buff, remote_port): - BUILD_BUG_ON(FIELD_SIZEOF(struct sock_common, skc_dport) != 2); + BUILD_BUG_ON(sizeof_field(struct sock_common, skc_dport) != 2); *insn++ = BPF_LDX_MEM(BPF_FIELD_SIZEOF(struct sk_buff, sk), si->dst_reg, si->src_reg, @@ -7524,7 +7524,7 @@ static u32 bpf_convert_ctx_access(enum bpf_access_type type, break; case offsetof(struct __sk_buff, local_port): - BUILD_BUG_ON(FIELD_SIZEOF(struct sock_common, skc_num) != 2); + BUILD_BUG_ON(sizeof_field(struct sock_common, skc_num) != 2); *insn++ = BPF_LDX_MEM(BPF_FIELD_SIZEOF(struct sk_buff, sk), si->dst_reg, si->src_reg, @@ -7535,7 +7535,7 @@ static u32 bpf_convert_ctx_access(enum bpf_access_type type, break; case offsetof(struct __sk_buff, tstamp): - BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, tstamp) != 8); + BUILD_BUG_ON(sizeof_field(struct sk_buff, tstamp) != 8); if (type == BPF_WRITE) *insn++ = BPF_STX_MEM(BPF_DW, @@ -7573,7 +7573,7 @@ static u32 bpf_convert_ctx_access(enum bpf_access_type type, target_size)); break; case offsetof(struct __sk_buff, wire_len): - BUILD_BUG_ON(FIELD_SIZEOF(struct qdisc_skb_cb, pkt_len) != 4); + BUILD_BUG_ON(sizeof_field(struct qdisc_skb_cb, pkt_len) != 4); off = si->off; off -= offsetof(struct __sk_buff, wire_len); @@ -7603,7 +7603,7 @@ u32 bpf_sock_convert_ctx_access(enum bpf_access_type type, switch (si->off) { case offsetof(struct bpf_sock, bound_dev_if): - BUILD_BUG_ON(FIELD_SIZEOF(struct sock, sk_bound_dev_if) != 4); + BUILD_BUG_ON(sizeof_field(struct sock, sk_bound_dev_if) != 4); if (type == BPF_WRITE) *insn++ = BPF_STX_MEM(BPF_W, si->dst_reg, si->src_reg, @@ -7614,7 +7614,7 @@ u32 bpf_sock_convert_ctx_access(enum bpf_access_type type, break; case offsetof(struct bpf_sock, mark): - BUILD_BUG_ON(FIELD_SIZEOF(struct sock, sk_mark) != 4); + BUILD_BUG_ON(sizeof_field(struct sock, sk_mark) != 4); if (type == BPF_WRITE) *insn++ = BPF_STX_MEM(BPF_W, si->dst_reg, si->src_reg, @@ -7625,7 +7625,7 @@ u32 bpf_sock_convert_ctx_access(enum bpf_access_type type, break; case offsetof(struct bpf_sock, priority): - BUILD_BUG_ON(FIELD_SIZEOF(struct sock, sk_priority) != 4); + BUILD_BUG_ON(sizeof_field(struct sock, sk_priority) != 4); if (type == BPF_WRITE) *insn++ = BPF_STX_MEM(BPF_W, si->dst_reg, si->src_reg, @@ -7641,7 +7641,7 @@ u32 bpf_sock_convert_ctx_access(enum bpf_access_type type, si->dst_reg, si->src_reg, bpf_target_off(struct sock_common, skc_family, - FIELD_SIZEOF(struct sock_common, + sizeof_field(struct sock_common, skc_family), target_size)); break; @@ -7668,7 +7668,7 @@ u32 bpf_sock_convert_ctx_access(enum bpf_access_type type, *insn++ = BPF_LDX_MEM( BPF_SIZE(si->code), si->dst_reg, si->src_reg, bpf_target_off(struct sock_common, skc_rcv_saddr, - FIELD_SIZEOF(struct sock_common, + sizeof_field(struct sock_common, skc_rcv_saddr), target_size)); break; @@ -7677,7 +7677,7 @@ u32 bpf_sock_convert_ctx_access(enum bpf_access_type type, *insn++ = BPF_LDX_MEM( BPF_SIZE(si->code), si->dst_reg, si->src_reg, bpf_target_off(struct sock_common, skc_daddr, - FIELD_SIZEOF(struct sock_common, + sizeof_field(struct sock_common, skc_daddr), target_size)); break; @@ -7691,7 +7691,7 @@ u32 bpf_sock_convert_ctx_access(enum bpf_access_type type, bpf_target_off( struct sock_common, skc_v6_rcv_saddr.s6_addr32[0], - FIELD_SIZEOF(struct sock_common, + sizeof_field(struct sock_common, skc_v6_rcv_saddr.s6_addr32[0]), target_size) + off); #else @@ -7708,7 +7708,7 @@ u32 bpf_sock_convert_ctx_access(enum bpf_access_type type, BPF_SIZE(si->code), si->dst_reg, si->src_reg, bpf_target_off(struct sock_common, skc_v6_daddr.s6_addr32[0], - FIELD_SIZEOF(struct sock_common, + sizeof_field(struct sock_common, skc_v6_daddr.s6_addr32[0]), target_size) + off); #else @@ -7722,7 +7722,7 @@ u32 bpf_sock_convert_ctx_access(enum bpf_access_type type, BPF_FIELD_SIZEOF(struct sock_common, skc_num), si->dst_reg, si->src_reg, bpf_target_off(struct sock_common, skc_num, - FIELD_SIZEOF(struct sock_common, + sizeof_field(struct sock_common, skc_num), target_size)); break; @@ -7732,7 +7732,7 @@ u32 bpf_sock_convert_ctx_access(enum bpf_access_type type, BPF_FIELD_SIZEOF(struct sock_common, skc_dport), si->dst_reg, si->src_reg, bpf_target_off(struct sock_common, skc_dport, - FIELD_SIZEOF(struct sock_common, + sizeof_field(struct sock_common, skc_dport), target_size)); break; @@ -7742,7 +7742,7 @@ u32 bpf_sock_convert_ctx_access(enum bpf_access_type type, BPF_FIELD_SIZEOF(struct sock_common, skc_state), si->dst_reg, si->src_reg, bpf_target_off(struct sock_common, skc_state, - FIELD_SIZEOF(struct sock_common, + sizeof_field(struct sock_common, skc_state), target_size)); break; @@ -7837,7 +7837,7 @@ static u32 xdp_convert_ctx_access(enum bpf_access_type type, si->src_reg, offsetof(S, F)); \ *insn++ = BPF_LDX_MEM( \ SIZE, si->dst_reg, si->dst_reg, \ - bpf_target_off(NS, NF, FIELD_SIZEOF(NS, NF), \ + bpf_target_off(NS, NF, sizeof_field(NS, NF), \ target_size) \ + OFF); \ } while (0) @@ -7868,7 +7868,7 @@ static u32 xdp_convert_ctx_access(enum bpf_access_type type, *insn++ = BPF_LDX_MEM(BPF_FIELD_SIZEOF(S, F), tmp_reg, \ si->dst_reg, offsetof(S, F)); \ *insn++ = BPF_STX_MEM(SIZE, tmp_reg, si->src_reg, \ - bpf_target_off(NS, NF, FIELD_SIZEOF(NS, NF), \ + bpf_target_off(NS, NF, sizeof_field(NS, NF), \ target_size) \ + OFF); \ *insn++ = BPF_LDX_MEM(BPF_DW, tmp_reg, si->dst_reg, \ @@ -7930,8 +7930,8 @@ static u32 sock_addr_convert_ctx_access(enum bpf_access_type type, */ BUILD_BUG_ON(offsetof(struct sockaddr_in, sin_port) != offsetof(struct sockaddr_in6, sin6_port)); - BUILD_BUG_ON(FIELD_SIZEOF(struct sockaddr_in, sin_port) != - FIELD_SIZEOF(struct sockaddr_in6, sin6_port)); + BUILD_BUG_ON(sizeof_field(struct sockaddr_in, sin_port) != + sizeof_field(struct sockaddr_in6, sin6_port)); SOCK_ADDR_LOAD_OR_STORE_NESTED_FIELD(struct bpf_sock_addr_kern, struct sockaddr_in6, uaddr, sin6_port, tmp_reg); @@ -7997,8 +7997,8 @@ static u32 sock_ops_convert_ctx_access(enum bpf_access_type type, /* Helper macro for adding read access to tcp_sock or sock fields. */ #define SOCK_OPS_GET_FIELD(BPF_FIELD, OBJ_FIELD, OBJ) \ do { \ - BUILD_BUG_ON(FIELD_SIZEOF(OBJ, OBJ_FIELD) > \ - FIELD_SIZEOF(struct bpf_sock_ops, BPF_FIELD)); \ + BUILD_BUG_ON(sizeof_field(OBJ, OBJ_FIELD) > \ + sizeof_field(struct bpf_sock_ops, BPF_FIELD)); \ *insn++ = BPF_LDX_MEM(BPF_FIELD_SIZEOF( \ struct bpf_sock_ops_kern, \ is_fullsock), \ @@ -8031,8 +8031,8 @@ static u32 sock_ops_convert_ctx_access(enum bpf_access_type type, #define SOCK_OPS_SET_FIELD(BPF_FIELD, OBJ_FIELD, OBJ) \ do { \ int reg = BPF_REG_9; \ - BUILD_BUG_ON(FIELD_SIZEOF(OBJ, OBJ_FIELD) > \ - FIELD_SIZEOF(struct bpf_sock_ops, BPF_FIELD)); \ + BUILD_BUG_ON(sizeof_field(OBJ, OBJ_FIELD) > \ + sizeof_field(struct bpf_sock_ops, BPF_FIELD)); \ if (si->dst_reg == reg || si->src_reg == reg) \ reg--; \ if (si->dst_reg == reg || si->src_reg == reg) \ @@ -8073,12 +8073,12 @@ static u32 sock_ops_convert_ctx_access(enum bpf_access_type type, switch (si->off) { case offsetof(struct bpf_sock_ops, op) ... offsetof(struct bpf_sock_ops, replylong[3]): - BUILD_BUG_ON(FIELD_SIZEOF(struct bpf_sock_ops, op) != - FIELD_SIZEOF(struct bpf_sock_ops_kern, op)); - BUILD_BUG_ON(FIELD_SIZEOF(struct bpf_sock_ops, reply) != - FIELD_SIZEOF(struct bpf_sock_ops_kern, reply)); - BUILD_BUG_ON(FIELD_SIZEOF(struct bpf_sock_ops, replylong) != - FIELD_SIZEOF(struct bpf_sock_ops_kern, replylong)); + BUILD_BUG_ON(sizeof_field(struct bpf_sock_ops, op) != + sizeof_field(struct bpf_sock_ops_kern, op)); + BUILD_BUG_ON(sizeof_field(struct bpf_sock_ops, reply) != + sizeof_field(struct bpf_sock_ops_kern, reply)); + BUILD_BUG_ON(sizeof_field(struct bpf_sock_ops, replylong) != + sizeof_field(struct bpf_sock_ops_kern, replylong)); off = si->off; off -= offsetof(struct bpf_sock_ops, op); off += offsetof(struct bpf_sock_ops_kern, op); @@ -8091,7 +8091,7 @@ static u32 sock_ops_convert_ctx_access(enum bpf_access_type type, break; case offsetof(struct bpf_sock_ops, family): - BUILD_BUG_ON(FIELD_SIZEOF(struct sock_common, skc_family) != 2); + BUILD_BUG_ON(sizeof_field(struct sock_common, skc_family) != 2); *insn++ = BPF_LDX_MEM(BPF_FIELD_SIZEOF( struct bpf_sock_ops_kern, sk), @@ -8102,7 +8102,7 @@ static u32 sock_ops_convert_ctx_access(enum bpf_access_type type, break; case offsetof(struct bpf_sock_ops, remote_ip4): - BUILD_BUG_ON(FIELD_SIZEOF(struct sock_common, skc_daddr) != 4); + BUILD_BUG_ON(sizeof_field(struct sock_common, skc_daddr) != 4); *insn++ = BPF_LDX_MEM(BPF_FIELD_SIZEOF( struct bpf_sock_ops_kern, sk), @@ -8113,7 +8113,7 @@ static u32 sock_ops_convert_ctx_access(enum bpf_access_type type, break; case offsetof(struct bpf_sock_ops, local_ip4): - BUILD_BUG_ON(FIELD_SIZEOF(struct sock_common, + BUILD_BUG_ON(sizeof_field(struct sock_common, skc_rcv_saddr) != 4); *insn++ = BPF_LDX_MEM(BPF_FIELD_SIZEOF( @@ -8128,7 +8128,7 @@ static u32 sock_ops_convert_ctx_access(enum bpf_access_type type, case offsetof(struct bpf_sock_ops, remote_ip6[0]) ... offsetof(struct bpf_sock_ops, remote_ip6[3]): #if IS_ENABLED(CONFIG_IPV6) - BUILD_BUG_ON(FIELD_SIZEOF(struct sock_common, + BUILD_BUG_ON(sizeof_field(struct sock_common, skc_v6_daddr.s6_addr32[0]) != 4); off = si->off; @@ -8149,7 +8149,7 @@ static u32 sock_ops_convert_ctx_access(enum bpf_access_type type, case offsetof(struct bpf_sock_ops, local_ip6[0]) ... offsetof(struct bpf_sock_ops, local_ip6[3]): #if IS_ENABLED(CONFIG_IPV6) - BUILD_BUG_ON(FIELD_SIZEOF(struct sock_common, + BUILD_BUG_ON(sizeof_field(struct sock_common, skc_v6_rcv_saddr.s6_addr32[0]) != 4); off = si->off; @@ -8168,7 +8168,7 @@ static u32 sock_ops_convert_ctx_access(enum bpf_access_type type, break; case offsetof(struct bpf_sock_ops, remote_port): - BUILD_BUG_ON(FIELD_SIZEOF(struct sock_common, skc_dport) != 2); + BUILD_BUG_ON(sizeof_field(struct sock_common, skc_dport) != 2); *insn++ = BPF_LDX_MEM(BPF_FIELD_SIZEOF( struct bpf_sock_ops_kern, sk), @@ -8182,7 +8182,7 @@ static u32 sock_ops_convert_ctx_access(enum bpf_access_type type, break; case offsetof(struct bpf_sock_ops, local_port): - BUILD_BUG_ON(FIELD_SIZEOF(struct sock_common, skc_num) != 2); + BUILD_BUG_ON(sizeof_field(struct sock_common, skc_num) != 2); *insn++ = BPF_LDX_MEM(BPF_FIELD_SIZEOF( struct bpf_sock_ops_kern, sk), @@ -8202,7 +8202,7 @@ static u32 sock_ops_convert_ctx_access(enum bpf_access_type type, break; case offsetof(struct bpf_sock_ops, state): - BUILD_BUG_ON(FIELD_SIZEOF(struct sock_common, skc_state) != 1); + BUILD_BUG_ON(sizeof_field(struct sock_common, skc_state) != 1); *insn++ = BPF_LDX_MEM(BPF_FIELD_SIZEOF( struct bpf_sock_ops_kern, sk), @@ -8213,7 +8213,7 @@ static u32 sock_ops_convert_ctx_access(enum bpf_access_type type, break; case offsetof(struct bpf_sock_ops, rtt_min): - BUILD_BUG_ON(FIELD_SIZEOF(struct tcp_sock, rtt_min) != + BUILD_BUG_ON(sizeof_field(struct tcp_sock, rtt_min) != sizeof(struct minmax)); BUILD_BUG_ON(sizeof(struct minmax) < sizeof(struct minmax_sample)); @@ -8224,7 +8224,7 @@ static u32 sock_ops_convert_ctx_access(enum bpf_access_type type, offsetof(struct bpf_sock_ops_kern, sk)); *insn++ = BPF_LDX_MEM(BPF_W, si->dst_reg, si->dst_reg, offsetof(struct tcp_sock, rtt_min) + - FIELD_SIZEOF(struct minmax_sample, t)); + sizeof_field(struct minmax_sample, t)); break; case offsetof(struct bpf_sock_ops, bpf_sock_ops_cb_flags): @@ -8366,7 +8366,7 @@ static u32 sk_msg_convert_ctx_access(enum bpf_access_type type, offsetof(struct sk_msg, data_end)); break; case offsetof(struct sk_msg_md, family): - BUILD_BUG_ON(FIELD_SIZEOF(struct sock_common, skc_family) != 2); + BUILD_BUG_ON(sizeof_field(struct sock_common, skc_family) != 2); *insn++ = BPF_LDX_MEM(BPF_FIELD_SIZEOF( struct sk_msg, sk), @@ -8377,7 +8377,7 @@ static u32 sk_msg_convert_ctx_access(enum bpf_access_type type, break; case offsetof(struct sk_msg_md, remote_ip4): - BUILD_BUG_ON(FIELD_SIZEOF(struct sock_common, skc_daddr) != 4); + BUILD_BUG_ON(sizeof_field(struct sock_common, skc_daddr) != 4); *insn++ = BPF_LDX_MEM(BPF_FIELD_SIZEOF( struct sk_msg, sk), @@ -8388,7 +8388,7 @@ static u32 sk_msg_convert_ctx_access(enum bpf_access_type type, break; case offsetof(struct sk_msg_md, local_ip4): - BUILD_BUG_ON(FIELD_SIZEOF(struct sock_common, + BUILD_BUG_ON(sizeof_field(struct sock_common, skc_rcv_saddr) != 4); *insn++ = BPF_LDX_MEM(BPF_FIELD_SIZEOF( @@ -8403,7 +8403,7 @@ static u32 sk_msg_convert_ctx_access(enum bpf_access_type type, case offsetof(struct sk_msg_md, remote_ip6[0]) ... offsetof(struct sk_msg_md, remote_ip6[3]): #if IS_ENABLED(CONFIG_IPV6) - BUILD_BUG_ON(FIELD_SIZEOF(struct sock_common, + BUILD_BUG_ON(sizeof_field(struct sock_common, skc_v6_daddr.s6_addr32[0]) != 4); off = si->off; @@ -8424,7 +8424,7 @@ static u32 sk_msg_convert_ctx_access(enum bpf_access_type type, case offsetof(struct sk_msg_md, local_ip6[0]) ... offsetof(struct sk_msg_md, local_ip6[3]): #if IS_ENABLED(CONFIG_IPV6) - BUILD_BUG_ON(FIELD_SIZEOF(struct sock_common, + BUILD_BUG_ON(sizeof_field(struct sock_common, skc_v6_rcv_saddr.s6_addr32[0]) != 4); off = si->off; @@ -8443,7 +8443,7 @@ static u32 sk_msg_convert_ctx_access(enum bpf_access_type type, break; case offsetof(struct sk_msg_md, remote_port): - BUILD_BUG_ON(FIELD_SIZEOF(struct sock_common, skc_dport) != 2); + BUILD_BUG_ON(sizeof_field(struct sock_common, skc_dport) != 2); *insn++ = BPF_LDX_MEM(BPF_FIELD_SIZEOF( struct sk_msg, sk), @@ -8457,7 +8457,7 @@ static u32 sk_msg_convert_ctx_access(enum bpf_access_type type, break; case offsetof(struct sk_msg_md, local_port): - BUILD_BUG_ON(FIELD_SIZEOF(struct sock_common, skc_num) != 2); + BUILD_BUG_ON(sizeof_field(struct sock_common, skc_num) != 2); *insn++ = BPF_LDX_MEM(BPF_FIELD_SIZEOF( struct sk_msg, sk), @@ -8847,7 +8847,7 @@ sk_reuseport_is_valid_access(int off, int size, /* Fields that allow narrowing */ case bpf_ctx_range(struct sk_reuseport_md, eth_protocol): - if (size < FIELD_SIZEOF(struct sk_buff, protocol)) + if (size < sizeof_field(struct sk_buff, protocol)) return false; /* fall through */ case bpf_ctx_range(struct sk_reuseport_md, ip_protocol): @@ -8865,7 +8865,7 @@ sk_reuseport_is_valid_access(int off, int size, *insn++ = BPF_LDX_MEM(BPF_FIELD_SIZEOF(struct sk_reuseport_kern, F), \ si->dst_reg, si->src_reg, \ bpf_target_off(struct sk_reuseport_kern, F, \ - FIELD_SIZEOF(struct sk_reuseport_kern, F), \ + sizeof_field(struct sk_reuseport_kern, F), \ target_size)); \ }) diff --git a/net/core/flow_dissector.c b/net/core/flow_dissector.c index d524a693e00fa..2dbbb030fbedf 100644 --- a/net/core/flow_dissector.c +++ b/net/core/flow_dissector.c @@ -599,8 +599,8 @@ __skb_flow_dissect_gre(const struct sk_buff *skb, offset += sizeof(struct gre_base_hdr); if (hdr->flags & GRE_CSUM) - offset += FIELD_SIZEOF(struct gre_full_hdr, csum) + - FIELD_SIZEOF(struct gre_full_hdr, reserved1); + offset += sizeof_field(struct gre_full_hdr, csum) + + sizeof_field(struct gre_full_hdr, reserved1); if (hdr->flags & GRE_KEY) { const __be32 *keyid; @@ -622,11 +622,11 @@ __skb_flow_dissect_gre(const struct sk_buff *skb, else key_keyid->keyid = *keyid & GRE_PPTP_KEY_MASK; } - offset += FIELD_SIZEOF(struct gre_full_hdr, key); + offset += sizeof_field(struct gre_full_hdr, key); } if (hdr->flags & GRE_SEQ) - offset += FIELD_SIZEOF(struct pptp_gre_header, seq); + offset += sizeof_field(struct pptp_gre_header, seq); if (gre_ver == 0) { if (*p_proto == htons(ETH_P_TEB)) { @@ -653,7 +653,7 @@ __skb_flow_dissect_gre(const struct sk_buff *skb, u8 *ppp_hdr; if (hdr->flags & GRE_ACK) - offset += FIELD_SIZEOF(struct pptp_gre_header, ack); + offset += sizeof_field(struct pptp_gre_header, ack); ppp_hdr = __skb_header_pointer(skb, *p_nhoff + offset, sizeof(_ppp_hdr), diff --git a/net/core/xdp.c b/net/core/xdp.c index 7c8390ad4dc61..8310714c47fd7 100644 --- a/net/core/xdp.c +++ b/net/core/xdp.c @@ -36,7 +36,7 @@ static u32 xdp_mem_id_hashfn(const void *data, u32 len, u32 seed) const u32 *k = data; const u32 key = *k; - BUILD_BUG_ON(FIELD_SIZEOF(struct xdp_mem_allocator, mem.id) + BUILD_BUG_ON(sizeof_field(struct xdp_mem_allocator, mem.id) != sizeof(u32)); /* Use cyclic increasing ID as direct hash key */ @@ -56,7 +56,7 @@ static const struct rhashtable_params mem_id_rht_params = { .nelem_hint = 64, .head_offset = offsetof(struct xdp_mem_allocator, node), .key_offset = offsetof(struct xdp_mem_allocator, mem.id), - .key_len = FIELD_SIZEOF(struct xdp_mem_allocator, mem.id), + .key_len = sizeof_field(struct xdp_mem_allocator, mem.id), .max_size = MEM_ID_MAX, .min_size = 8, .automatic_shrinking = true, diff --git a/net/dccp/proto.c b/net/dccp/proto.c index a52e8ba1ced04..4af8a98fe7846 100644 --- a/net/dccp/proto.c +++ b/net/dccp/proto.c @@ -1132,7 +1132,7 @@ static int __init dccp_init(void) int rc; BUILD_BUG_ON(sizeof(struct dccp_skb_cb) > - FIELD_SIZEOF(struct sk_buff, cb)); + sizeof_field(struct sk_buff, cb)); rc = percpu_counter_init(&dccp_orphan_count, 0, GFP_KERNEL); if (rc) goto out_fail; diff --git a/net/ipv4/ip_gre.c b/net/ipv4/ip_gre.c index 572b6307a2dff..8274f98c511cc 100644 --- a/net/ipv4/ip_gre.c +++ b/net/ipv4/ip_gre.c @@ -1464,8 +1464,8 @@ static const struct nla_policy ipgre_policy[IFLA_GRE_MAX + 1] = { [IFLA_GRE_OFLAGS] = { .type = NLA_U16 }, [IFLA_GRE_IKEY] = { .type = NLA_U32 }, [IFLA_GRE_OKEY] = { .type = NLA_U32 }, - [IFLA_GRE_LOCAL] = { .len = FIELD_SIZEOF(struct iphdr, saddr) }, - [IFLA_GRE_REMOTE] = { .len = FIELD_SIZEOF(struct iphdr, daddr) }, + [IFLA_GRE_LOCAL] = { .len = sizeof_field(struct iphdr, saddr) }, + [IFLA_GRE_REMOTE] = { .len = sizeof_field(struct iphdr, daddr) }, [IFLA_GRE_TTL] = { .type = NLA_U8 }, [IFLA_GRE_TOS] = { .type = NLA_U8 }, [IFLA_GRE_PMTUDISC] = { .type = NLA_U8 }, diff --git a/net/ipv4/ip_vti.c b/net/ipv4/ip_vti.c index cfb0256067936..9b153c7fcbb4d 100644 --- a/net/ipv4/ip_vti.c +++ b/net/ipv4/ip_vti.c @@ -580,8 +580,8 @@ static const struct nla_policy vti_policy[IFLA_VTI_MAX + 1] = { [IFLA_VTI_LINK] = { .type = NLA_U32 }, [IFLA_VTI_IKEY] = { .type = NLA_U32 }, [IFLA_VTI_OKEY] = { .type = NLA_U32 }, - [IFLA_VTI_LOCAL] = { .len = FIELD_SIZEOF(struct iphdr, saddr) }, - [IFLA_VTI_REMOTE] = { .len = FIELD_SIZEOF(struct iphdr, daddr) }, + [IFLA_VTI_LOCAL] = { .len = sizeof_field(struct iphdr, saddr) }, + [IFLA_VTI_REMOTE] = { .len = sizeof_field(struct iphdr, daddr) }, [IFLA_VTI_FWMARK] = { .type = NLA_U32 }, }; diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index 8a39ee7948919..3e50ac24fe410 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -3949,7 +3949,7 @@ void __init tcp_init(void) BUILD_BUG_ON(TCP_MIN_SND_MSS <= MAX_TCP_OPTION_SPACE); BUILD_BUG_ON(sizeof(struct tcp_skb_cb) > - FIELD_SIZEOF(struct sk_buff, cb)); + sizeof_field(struct sk_buff, cb)); percpu_counter_init(&tcp_sockets_allocated, 0, GFP_KERNEL); percpu_counter_init(&tcp_orphan_count, 0, GFP_KERNEL); diff --git a/net/ipv6/ip6_gre.c b/net/ipv6/ip6_gre.c index 923034c52ce40..9d0965252ddf7 100644 --- a/net/ipv6/ip6_gre.c +++ b/net/ipv6/ip6_gre.c @@ -2170,8 +2170,8 @@ static const struct nla_policy ip6gre_policy[IFLA_GRE_MAX + 1] = { [IFLA_GRE_OFLAGS] = { .type = NLA_U16 }, [IFLA_GRE_IKEY] = { .type = NLA_U32 }, [IFLA_GRE_OKEY] = { .type = NLA_U32 }, - [IFLA_GRE_LOCAL] = { .len = FIELD_SIZEOF(struct ipv6hdr, saddr) }, - [IFLA_GRE_REMOTE] = { .len = FIELD_SIZEOF(struct ipv6hdr, daddr) }, + [IFLA_GRE_LOCAL] = { .len = sizeof_field(struct ipv6hdr, saddr) }, + [IFLA_GRE_REMOTE] = { .len = sizeof_field(struct ipv6hdr, daddr) }, [IFLA_GRE_TTL] = { .type = NLA_U8 }, [IFLA_GRE_ENCAP_LIMIT] = { .type = NLA_U8 }, [IFLA_GRE_FLOWINFO] = { .type = NLA_U32 }, diff --git a/net/iucv/af_iucv.c b/net/iucv/af_iucv.c index ebb62a4ebe30d..c4bdcbc84b07f 100644 --- a/net/iucv/af_iucv.c +++ b/net/iucv/af_iucv.c @@ -50,7 +50,7 @@ static struct iucv_interface *pr_iucv; static const u8 iprm_shutdown[8] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01}; -#define TRGCLS_SIZE FIELD_SIZEOF(struct iucv_message, class) +#define TRGCLS_SIZE sizeof_field(struct iucv_message, class) #define __iucv_sock_wait(sk, condition, timeo, ret) \ do { \ diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c index 062b73a83af0e..c26a5663795ef 100644 --- a/net/netfilter/nf_tables_api.c +++ b/net/netfilter/nf_tables_api.c @@ -7595,7 +7595,7 @@ int nft_validate_register_load(enum nft_registers reg, unsigned int len) return -EINVAL; if (len == 0) return -EINVAL; - if (reg * NFT_REG32_SIZE + len > FIELD_SIZEOF(struct nft_regs, data)) + if (reg * NFT_REG32_SIZE + len > sizeof_field(struct nft_regs, data)) return -ERANGE; return 0; @@ -7643,7 +7643,7 @@ int nft_validate_register_store(const struct nft_ctx *ctx, if (len == 0) return -EINVAL; if (reg * NFT_REG32_SIZE + len > - FIELD_SIZEOF(struct nft_regs, data)) + sizeof_field(struct nft_regs, data)) return -ERANGE; if (data != NULL && type != NFT_DATA_VALUE) diff --git a/net/netfilter/nfnetlink_cthelper.c b/net/netfilter/nfnetlink_cthelper.c index 7525063c25f5f..de3a9596b7f1b 100644 --- a/net/netfilter/nfnetlink_cthelper.c +++ b/net/netfilter/nfnetlink_cthelper.c @@ -236,7 +236,7 @@ nfnl_cthelper_create(const struct nlattr * const tb[], nla_strlcpy(helper->name, tb[NFCTH_NAME], NF_CT_HELPER_NAME_LEN); size = ntohl(nla_get_be32(tb[NFCTH_PRIV_DATA_LEN])); - if (size > FIELD_SIZEOF(struct nf_conn_help, data)) { + if (size > sizeof_field(struct nf_conn_help, data)) { ret = -ENOMEM; goto err2; } diff --git a/net/netfilter/nft_ct.c b/net/netfilter/nft_ct.c index 46ca8bcca1bd5..faea72c2df328 100644 --- a/net/netfilter/nft_ct.c +++ b/net/netfilter/nft_ct.c @@ -440,12 +440,12 @@ static int nft_ct_get_init(const struct nft_ctx *ctx, switch (ctx->family) { case NFPROTO_IPV4: - len = FIELD_SIZEOF(struct nf_conntrack_tuple, + len = sizeof_field(struct nf_conntrack_tuple, src.u3.ip); break; case NFPROTO_IPV6: case NFPROTO_INET: - len = FIELD_SIZEOF(struct nf_conntrack_tuple, + len = sizeof_field(struct nf_conntrack_tuple, src.u3.ip6); break; default: @@ -457,20 +457,20 @@ static int nft_ct_get_init(const struct nft_ctx *ctx, if (tb[NFTA_CT_DIRECTION] == NULL) return -EINVAL; - len = FIELD_SIZEOF(struct nf_conntrack_tuple, src.u3.ip); + len = sizeof_field(struct nf_conntrack_tuple, src.u3.ip); break; case NFT_CT_SRC_IP6: case NFT_CT_DST_IP6: if (tb[NFTA_CT_DIRECTION] == NULL) return -EINVAL; - len = FIELD_SIZEOF(struct nf_conntrack_tuple, src.u3.ip6); + len = sizeof_field(struct nf_conntrack_tuple, src.u3.ip6); break; case NFT_CT_PROTO_SRC: case NFT_CT_PROTO_DST: if (tb[NFTA_CT_DIRECTION] == NULL) return -EINVAL; - len = FIELD_SIZEOF(struct nf_conntrack_tuple, src.u.all); + len = sizeof_field(struct nf_conntrack_tuple, src.u.all); break; case NFT_CT_BYTES: case NFT_CT_PKTS: @@ -551,7 +551,7 @@ static int nft_ct_set_init(const struct nft_ctx *ctx, case NFT_CT_MARK: if (tb[NFTA_CT_DIRECTION]) return -EINVAL; - len = FIELD_SIZEOF(struct nf_conn, mark); + len = sizeof_field(struct nf_conn, mark); break; #endif #ifdef CONFIG_NF_CONNTRACK_LABELS diff --git a/net/netfilter/nft_masq.c b/net/netfilter/nft_masq.c index 39dc94f2491e3..bc9fd98c5d6d9 100644 --- a/net/netfilter/nft_masq.c +++ b/net/netfilter/nft_masq.c @@ -43,7 +43,7 @@ static int nft_masq_init(const struct nft_ctx *ctx, const struct nft_expr *expr, const struct nlattr * const tb[]) { - u32 plen = FIELD_SIZEOF(struct nf_nat_range, min_addr.all); + u32 plen = sizeof_field(struct nf_nat_range, min_addr.all); struct nft_masq *priv = nft_expr_priv(expr); int err; diff --git a/net/netfilter/nft_nat.c b/net/netfilter/nft_nat.c index c3c93e95b46e7..8b44a4de53294 100644 --- a/net/netfilter/nft_nat.c +++ b/net/netfilter/nft_nat.c @@ -141,10 +141,10 @@ static int nft_nat_init(const struct nft_ctx *ctx, const struct nft_expr *expr, switch (family) { case NFPROTO_IPV4: - alen = FIELD_SIZEOF(struct nf_nat_range, min_addr.ip); + alen = sizeof_field(struct nf_nat_range, min_addr.ip); break; case NFPROTO_IPV6: - alen = FIELD_SIZEOF(struct nf_nat_range, min_addr.ip6); + alen = sizeof_field(struct nf_nat_range, min_addr.ip6); break; default: return -EAFNOSUPPORT; @@ -171,7 +171,7 @@ static int nft_nat_init(const struct nft_ctx *ctx, const struct nft_expr *expr, } } - plen = FIELD_SIZEOF(struct nf_nat_range, min_addr.all); + plen = sizeof_field(struct nf_nat_range, min_addr.all); if (tb[NFTA_NAT_REG_PROTO_MIN]) { priv->sreg_proto_min = nft_parse_register(tb[NFTA_NAT_REG_PROTO_MIN]); diff --git a/net/netfilter/nft_redir.c b/net/netfilter/nft_redir.c index 43eeb1f609f13..5b779171565c1 100644 --- a/net/netfilter/nft_redir.c +++ b/net/netfilter/nft_redir.c @@ -48,7 +48,7 @@ static int nft_redir_init(const struct nft_ctx *ctx, unsigned int plen; int err; - plen = FIELD_SIZEOF(struct nf_nat_range, min_addr.all); + plen = sizeof_field(struct nf_nat_range, min_addr.all); if (tb[NFTA_REDIR_REG_PROTO_MIN]) { priv->sreg_proto_min = nft_parse_register(tb[NFTA_REDIR_REG_PROTO_MIN]); diff --git a/net/netfilter/nft_tproxy.c b/net/netfilter/nft_tproxy.c index f92a82c738808..4c33dfc9dab50 100644 --- a/net/netfilter/nft_tproxy.c +++ b/net/netfilter/nft_tproxy.c @@ -218,14 +218,14 @@ static int nft_tproxy_init(const struct nft_ctx *ctx, switch (priv->family) { case NFPROTO_IPV4: - alen = FIELD_SIZEOF(union nf_inet_addr, in); + alen = sizeof_field(union nf_inet_addr, in); err = nf_defrag_ipv4_enable(ctx->net); if (err) return err; break; #if IS_ENABLED(CONFIG_NF_TABLES_IPV6) case NFPROTO_IPV6: - alen = FIELD_SIZEOF(union nf_inet_addr, in6); + alen = sizeof_field(union nf_inet_addr, in6); err = nf_defrag_ipv6_enable(ctx->net); if (err) return err; diff --git a/net/netfilter/xt_RATEEST.c b/net/netfilter/xt_RATEEST.c index 2236455b10a36..37253d399c6b8 100644 --- a/net/netfilter/xt_RATEEST.c +++ b/net/netfilter/xt_RATEEST.c @@ -30,7 +30,7 @@ static unsigned int jhash_rnd __read_mostly; static unsigned int xt_rateest_hash(const char *name) { - return jhash(name, FIELD_SIZEOF(struct xt_rateest, name), jhash_rnd) & + return jhash(name, sizeof_field(struct xt_rateest, name), jhash_rnd) & (RATEEST_HSIZE - 1); } diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c index 90b2ab9dd4495..4e31721e72936 100644 --- a/net/netlink/af_netlink.c +++ b/net/netlink/af_netlink.c @@ -2755,7 +2755,7 @@ static int __init netlink_proto_init(void) if (err != 0) goto out; - BUILD_BUG_ON(sizeof(struct netlink_skb_parms) > FIELD_SIZEOF(struct sk_buff, cb)); + BUILD_BUG_ON(sizeof(struct netlink_skb_parms) > sizeof_field(struct sk_buff, cb)); nl_table = kcalloc(MAX_LINKS, sizeof(*nl_table), GFP_KERNEL); if (!nl_table) diff --git a/net/openvswitch/datapath.c b/net/openvswitch/datapath.c index 1047e8043084f..e3a37d22539c0 100644 --- a/net/openvswitch/datapath.c +++ b/net/openvswitch/datapath.c @@ -2497,7 +2497,7 @@ static int __init dp_init(void) { int err; - BUILD_BUG_ON(sizeof(struct ovs_skb_cb) > FIELD_SIZEOF(struct sk_buff, cb)); + BUILD_BUG_ON(sizeof(struct ovs_skb_cb) > sizeof_field(struct sk_buff, cb)); pr_info("Open vSwitch switching datapath\n"); diff --git a/net/openvswitch/flow.h b/net/openvswitch/flow.h index fd8ed766bdd17..758a8c77f7361 100644 --- a/net/openvswitch/flow.h +++ b/net/openvswitch/flow.h @@ -37,7 +37,7 @@ enum sw_flow_mac_proto { * matching for small options. */ #define TUN_METADATA_OFFSET(opt_len) \ - (FIELD_SIZEOF(struct sw_flow_key, tun_opts) - opt_len) + (sizeof_field(struct sw_flow_key, tun_opts) - opt_len) #define TUN_METADATA_OPTS(flow_key, opt_len) \ ((void *)((flow_key)->tun_opts + TUN_METADATA_OFFSET(opt_len))) @@ -52,7 +52,7 @@ struct vlan_head { #define OVS_SW_FLOW_KEY_METADATA_SIZE \ (offsetof(struct sw_flow_key, recirc_id) + \ - FIELD_SIZEOF(struct sw_flow_key, recirc_id)) + sizeof_field(struct sw_flow_key, recirc_id)) struct ovs_key_nsh { struct ovs_nsh_key_base base; diff --git a/net/rxrpc/af_rxrpc.c b/net/rxrpc/af_rxrpc.c index d72ddb67bb742..9d3c4d2d893ab 100644 --- a/net/rxrpc/af_rxrpc.c +++ b/net/rxrpc/af_rxrpc.c @@ -972,7 +972,7 @@ static int __init af_rxrpc_init(void) int ret = -1; unsigned int tmp; - BUILD_BUG_ON(sizeof(struct rxrpc_skb_priv) > FIELD_SIZEOF(struct sk_buff, cb)); + BUILD_BUG_ON(sizeof(struct rxrpc_skb_priv) > sizeof_field(struct sk_buff, cb)); get_random_bytes(&tmp, sizeof(tmp)); tmp &= 0x3fffffff; diff --git a/net/sched/act_ct.c b/net/sched/act_ct.c index bf2d69335d4b9..f685c0d737086 100644 --- a/net/sched/act_ct.c +++ b/net/sched/act_ct.c @@ -312,7 +312,7 @@ static void tcf_ct_act_set_labels(struct nf_conn *ct, u32 *labels_m) { #if IS_ENABLED(CONFIG_NF_CONNTRACK_LABELS) - size_t labels_sz = FIELD_SIZEOF(struct tcf_ct_params, labels); + size_t labels_sz = sizeof_field(struct tcf_ct_params, labels); if (!memchr_inv(labels_m, 0, labels_sz)) return; @@ -936,7 +936,7 @@ static struct tc_action_ops act_ct_ops = { static __net_init int ct_init_net(struct net *net) { - unsigned int n_bits = FIELD_SIZEOF(struct tcf_ct_params, labels) * 8; + unsigned int n_bits = sizeof_field(struct tcf_ct_params, labels) * 8; struct tc_ct_action_net *tn = net_generic(net, ct_net_id); if (nf_connlabels_get(net, n_bits - 1)) { diff --git a/net/sched/cls_flower.c b/net/sched/cls_flower.c index 6c68971d99df7..0d125de542850 100644 --- a/net/sched/cls_flower.c +++ b/net/sched/cls_flower.c @@ -1481,7 +1481,7 @@ static int fl_init_mask_hashtable(struct fl_flow_mask *mask) } #define FL_KEY_MEMBER_OFFSET(member) offsetof(struct fl_flow_key, member) -#define FL_KEY_MEMBER_SIZE(member) FIELD_SIZEOF(struct fl_flow_key, member) +#define FL_KEY_MEMBER_SIZE(member) sizeof_field(struct fl_flow_key, member) #define FL_KEY_IS_MASKED(mask, member) \ memchr_inv(((char *)mask) + FL_KEY_MEMBER_OFFSET(member), \ diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c index 7cfdce10de36b..774babbee045f 100644 --- a/net/unix/af_unix.c +++ b/net/unix/af_unix.c @@ -2865,7 +2865,7 @@ static int __init af_unix_init(void) { int rc = -1; - BUILD_BUG_ON(sizeof(struct unix_skb_parms) > FIELD_SIZEOF(struct sk_buff, cb)); + BUILD_BUG_ON(sizeof(struct unix_skb_parms) > sizeof_field(struct sk_buff, cb)); rc = proto_register(&unix_proto, 1); if (rc != 0) { diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c index f19a895ad7cdf..ef8dfd47c7e39 100644 --- a/security/integrity/ima/ima_policy.c +++ b/security/integrity/ima/ima_policy.c @@ -45,7 +45,7 @@ #define DONT_HASH 0x0200 #define INVALID_PCR(a) (((a) < 0) || \ - (a) >= (FIELD_SIZEOF(struct integrity_iint_cache, measured_pcrs) * 8)) + (a) >= (sizeof_field(struct integrity_iint_cache, measured_pcrs) * 8)) int ima_policy_flag; static int temp_ima_appraise; @@ -274,7 +274,7 @@ static struct ima_rule_entry *ima_lsm_copy_rule(struct ima_rule_entry *entry) * lsm rules can change */ memcpy(nentry, entry, sizeof(*nentry)); - memset(nentry->lsm, 0, FIELD_SIZEOF(struct ima_rule_entry, lsm)); + memset(nentry->lsm, 0, sizeof_field(struct ima_rule_entry, lsm)); for (i = 0; i < MAX_LSM_RULES; i++) { if (!entry->lsm[i].rule) diff --git a/sound/soc/codecs/hdmi-codec.c b/sound/soc/codecs/hdmi-codec.c index f8b5b960e5970..4eaa2b5b20a58 100644 --- a/sound/soc/codecs/hdmi-codec.c +++ b/sound/soc/codecs/hdmi-codec.c @@ -292,7 +292,7 @@ static int hdmi_eld_ctl_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES; - uinfo->count = FIELD_SIZEOF(struct hdmi_codec_priv, eld); + uinfo->count = sizeof_field(struct hdmi_codec_priv, eld); return 0; } -- GitLab From 6d30fc511bec82dd8801b9bb8718cbeea1366ad8 Mon Sep 17 00:00:00 2001 From: Cristian Marussi Date: Mon, 9 Dec 2019 12:52:39 +0000 Subject: [PATCH 0296/1437] regulator: core: avoid unneeded .list_voltage calls Inside machine_constraints_voltage() a loop is in charge of verifying that each of the defined voltages are within the configured constraints and that those constraints are in fact compatible with the available voltages' list. When the registered regulator happens to be defined with a wide range of possible voltages the above O(n) loop can be costly. Moreover since this behaviour is triggered during the registration process, it means also that it can be easily triggered at probe time, slowing down considerably some module loading. On the other side if such wide range of voltage values happens to be also continuous and without discontinuity of any kind, the above potentially cumbersome operation is also useless. For these reasons, avoid such .list_voltage poll loop when regulator is described as 'continuous_voltage_range' as is, indeed, similarly already done inside regulator_is_supported_voltage(). Signed-off-by: Cristian Marussi Link: https://lore.kernel.org/r/20191209125239.46054-1-cristian.marussi@arm.com Signed-off-by: Mark Brown --- drivers/regulator/core.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 2c3a03cfd381d..2961ac08d1ae6 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -1198,6 +1198,10 @@ static int machine_constraints_voltage(struct regulator_dev *rdev, return -EINVAL; } + /* no need to loop voltages if range is continuous */ + if (rdev->desc->continuous_voltage_range) + return 0; + /* initial: [cmin..cmax] valid, [min_uV..max_uV] not */ for (i = 0; i < count; i++) { int value; -- GitLab From a841e2853e1afecc2ee692b8cc5bff606bc84e4c Mon Sep 17 00:00:00 2001 From: Chuhong Yuan Date: Fri, 6 Dec 2019 15:55:00 +0800 Subject: [PATCH 0297/1437] spi: spi-cavium-thunderx: Add missing pci_release_regions() The driver forgets to call pci_release_regions() in probe failure and remove. Add the missed calls to fix it. Signed-off-by: Chuhong Yuan Link: https://lore.kernel.org/r/20191206075500.18525-1-hslester96@gmail.com Signed-off-by: Mark Brown --- drivers/spi/spi-cavium-thunderx.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/spi/spi-cavium-thunderx.c b/drivers/spi/spi-cavium-thunderx.c index d12e149f1a419..fd6b9caffaf0c 100644 --- a/drivers/spi/spi-cavium-thunderx.c +++ b/drivers/spi/spi-cavium-thunderx.c @@ -82,6 +82,7 @@ static int thunderx_spi_probe(struct pci_dev *pdev, error: clk_disable_unprepare(p->clk); + pci_release_regions(pdev); spi_master_put(master); return ret; } @@ -96,6 +97,7 @@ static void thunderx_spi_remove(struct pci_dev *pdev) return; clk_disable_unprepare(p->clk); + pci_release_regions(pdev); /* Put everything in a known state. */ writeq(0, p->register_base + OCTEON_SPI_CFG(p)); } -- GitLab From 3194d2533efffae8b815d84729ecc58b6a9000ab Mon Sep 17 00:00:00 2001 From: Christophe Leroy Date: Mon, 9 Dec 2019 15:27:27 +0000 Subject: [PATCH 0298/1437] spi: fsl: don't map irq during probe With lastest kernel, the following warning is observed at startup: [ 1.500609] ------------[ cut here ]------------ [ 1.505225] remove_proc_entry: removing non-empty directory 'irq/22', leaking at least 'fsl_spi' [ 1.514234] WARNING: CPU: 0 PID: 1 at fs/proc/generic.c:682 remove_proc_entry+0x198/0x1c0 [ 1.522403] CPU: 0 PID: 1 Comm: swapper Not tainted 5.4.0-s3k-dev-02248-g93532430a4ff #2564 [ 1.530724] NIP: c0197694 LR: c0197694 CTR: c0050d80 [ 1.535762] REGS: df4a5af0 TRAP: 0700 Not tainted (5.4.0-02248-g93532430a4ff) [ 1.543818] MSR: 00029032 CR: 22028222 XER: 00000000 [ 1.550524] [ 1.550524] GPR00: c0197694 df4a5ba8 df4a0000 00000054 00000000 00000000 00004a38 00000010 [ 1.550524] GPR08: c07c5a30 00000800 00000000 00001032 22000208 00000000 c0004b14 00000000 [ 1.550524] GPR16: 00000000 00000000 00000000 00000000 00000000 00000000 c0830000 c07fc078 [ 1.550524] GPR24: c08e8ca0 df665d10 df60ea98 c07c9db8 00000001 df5d5ae3 df5d5a80 df43f8e3 [ 1.585327] NIP [c0197694] remove_proc_entry+0x198/0x1c0 [ 1.590628] LR [c0197694] remove_proc_entry+0x198/0x1c0 [ 1.595829] Call Trace: [ 1.598280] [df4a5ba8] [c0197694] remove_proc_entry+0x198/0x1c0 (unreliable) [ 1.605321] [df4a5bd8] [c0067acc] unregister_irq_proc+0x5c/0x70 [ 1.611238] [df4a5bf8] [c005fbc4] free_desc+0x3c/0x80 [ 1.616286] [df4a5c18] [c005fe2c] irq_free_descs+0x70/0xa8 [ 1.621778] [df4a5c38] [c033d3fc] of_fsl_spi_probe+0xdc/0x3cc [ 1.627525] [df4a5c88] [c02f0f64] platform_drv_probe+0x44/0xa4 [ 1.633350] [df4a5c98] [c02eee44] really_probe+0x1ac/0x418 [ 1.638829] [df4a5cc8] [c02ed3e8] bus_for_each_drv+0x64/0xb0 [ 1.644481] [df4a5cf8] [c02ef950] __device_attach+0xd4/0x128 [ 1.650132] [df4a5d28] [c02ed61c] bus_probe_device+0xa0/0xbc [ 1.655783] [df4a5d48] [c02ebbe8] device_add+0x544/0x74c [ 1.661096] [df4a5d88] [c0382b78] of_platform_device_create_pdata+0xa4/0x100 [ 1.668131] [df4a5da8] [c0382cf4] of_platform_bus_create+0x120/0x20c [ 1.674474] [df4a5df8] [c0382d50] of_platform_bus_create+0x17c/0x20c [ 1.680818] [df4a5e48] [c0382e88] of_platform_bus_probe+0x9c/0xf0 [ 1.686907] [df4a5e68] [c0751404] __machine_initcall_cmpcpro_cmpcpro_declare_of_platform_devices+0x74/0x1a4 [ 1.696629] [df4a5e98] [c072a4cc] do_one_initcall+0x8c/0x1d4 [ 1.702282] [df4a5ef8] [c072a768] kernel_init_freeable+0x154/0x204 [ 1.708455] [df4a5f28] [c0004b2c] kernel_init+0x18/0x110 [ 1.713769] [df4a5f38] [c00122ac] ret_from_kernel_thread+0x14/0x1c [ 1.719926] Instruction dump: [ 1.722889] 2c030000 4182004c 3863ffb0 3c80c05f 80e3005c 388436a0 3c60c06d 7fa6eb78 [ 1.730630] 7fe5fb78 38840280 38634178 4be8c611 <0fe00000> 4bffff6c 3c60c071 7fe4fb78 [ 1.738556] ---[ end trace 05d0720bf2e352e2 ]--- The problem comes from the error path which calls irq_dispose_mapping() while the IRQ has been requested with devm_request_irq(). IRQ doesn't need to be mapped with irq_of_parse_and_map(). The only need is to get the IRQ virtual number. For that, use of_irq_to_resource() instead of the irq_of_parse_and_map()/irq_dispose_mapping() pair. Fixes: 500a32abaf81 ("spi: fsl: Call irq_dispose_mapping in err path") Cc: stable@vger.kernel.org Signed-off-by: Christophe Leroy Link: https://lore.kernel.org/r/518cfb83347d5372748e7fe72f94e2e9443d0d4a.1575905123.git.christophe.leroy@c-s.fr Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-spi.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/spi/spi-fsl-spi.c b/drivers/spi/spi-fsl-spi.c index 4b70887cf4438..d0ad9709f4a6c 100644 --- a/drivers/spi/spi-fsl-spi.c +++ b/drivers/spi/spi-fsl-spi.c @@ -746,8 +746,8 @@ static int of_fsl_spi_probe(struct platform_device *ofdev) if (ret) goto err; - irq = irq_of_parse_and_map(np, 0); - if (!irq) { + irq = of_irq_to_resource(np, 0, NULL); + if (irq <= 0) { ret = -EINVAL; goto err; } @@ -761,7 +761,6 @@ static int of_fsl_spi_probe(struct platform_device *ofdev) return 0; err: - irq_dispose_mapping(irq); return ret; } -- GitLab From 71bbac6e2f23e946875475ce2fb9f6752900d463 Mon Sep 17 00:00:00 2001 From: Steve Wise Date: Mon, 2 Dec 2019 20:03:18 -0600 Subject: [PATCH 0299/1437] Update mailmap info for Steve Wise Signed-off-by: Steve Wise Link: https://lore.kernel.org/r/20191203020319.15036-1-larrystevenwise@gmail.com Signed-off-by: Doug Ledford --- .mailmap | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.mailmap b/.mailmap index c24773db04a7a..00581c1f09838 100644 --- a/.mailmap +++ b/.mailmap @@ -276,3 +276,5 @@ Gustavo Padovan Gustavo Padovan Changbin Du Changbin Du +Steve Wise +Steve Wise -- GitLab From 2030abddec6884aaf5892f5724c48fc340e6826f Mon Sep 17 00:00:00 2001 From: Steve Wise Date: Mon, 2 Dec 2019 20:03:20 -0600 Subject: [PATCH 0300/1437] rxe: correctly calculate iCRC for unaligned payloads If RoCE PDUs being sent or received contain pad bytes, then the iCRC is miscalculated, resulting in PDUs being emitted by RXE with an incorrect iCRC, as well as ingress PDUs being dropped due to erroneously detecting a bad iCRC in the PDU. The fix is to include the pad bytes, if any, in iCRC computations. Note: This bug has caused broken on-the-wire compatibility with actual hardware RoCE devices since the soft-RoCE driver was first put into the mainstream kernel. Fixing it will create an incompatibility with the original soft-RoCE devices, but is necessary to be compatible with real hardware devices. Fixes: 8700e3e7c485 ("Soft RoCE driver") Signed-off-by: Steve Wise Link: https://lore.kernel.org/r/20191203020319.15036-2-larrystevenwise@gmail.com Signed-off-by: Doug Ledford --- drivers/infiniband/sw/rxe/rxe_recv.c | 2 +- drivers/infiniband/sw/rxe/rxe_req.c | 6 ++++++ drivers/infiniband/sw/rxe/rxe_resp.c | 7 +++++++ 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/drivers/infiniband/sw/rxe/rxe_recv.c b/drivers/infiniband/sw/rxe/rxe_recv.c index f9a492ed900b9..831ad578a7b29 100644 --- a/drivers/infiniband/sw/rxe/rxe_recv.c +++ b/drivers/infiniband/sw/rxe/rxe_recv.c @@ -389,7 +389,7 @@ void rxe_rcv(struct sk_buff *skb) calc_icrc = rxe_icrc_hdr(pkt, skb); calc_icrc = rxe_crc32(rxe, calc_icrc, (u8 *)payload_addr(pkt), - payload_size(pkt)); + payload_size(pkt) + bth_pad(pkt)); calc_icrc = (__force u32)cpu_to_be32(~calc_icrc); if (unlikely(calc_icrc != pack_icrc)) { if (skb->protocol == htons(ETH_P_IPV6)) diff --git a/drivers/infiniband/sw/rxe/rxe_req.c b/drivers/infiniband/sw/rxe/rxe_req.c index c5d9b558fa90a..e5031172c0193 100644 --- a/drivers/infiniband/sw/rxe/rxe_req.c +++ b/drivers/infiniband/sw/rxe/rxe_req.c @@ -500,6 +500,12 @@ static int fill_packet(struct rxe_qp *qp, struct rxe_send_wqe *wqe, if (err) return err; } + if (bth_pad(pkt)) { + u8 *pad = payload_addr(pkt) + paylen; + + memset(pad, 0, bth_pad(pkt)); + crc = rxe_crc32(rxe, crc, pad, bth_pad(pkt)); + } } p = payload_addr(pkt) + paylen + bth_pad(pkt); diff --git a/drivers/infiniband/sw/rxe/rxe_resp.c b/drivers/infiniband/sw/rxe/rxe_resp.c index 1cbfbd98eb221..c4a8195bf6709 100644 --- a/drivers/infiniband/sw/rxe/rxe_resp.c +++ b/drivers/infiniband/sw/rxe/rxe_resp.c @@ -732,6 +732,13 @@ static enum resp_states read_reply(struct rxe_qp *qp, if (err) pr_err("Failed copying memory\n"); + if (bth_pad(&ack_pkt)) { + struct rxe_dev *rxe = to_rdev(qp->ibqp.device); + u8 *pad = payload_addr(&ack_pkt) + payload; + + memset(pad, 0, bth_pad(&ack_pkt)); + icrc = rxe_crc32(rxe, icrc, pad, bth_pad(&ack_pkt)); + } p = payload_addr(&ack_pkt) + payload + bth_pad(&ack_pkt); *p = ~icrc; -- GitLab From 59a126aa3113fc23f03fedcafe3705f1de5aff50 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Tue, 10 Dec 2019 00:03:04 +0900 Subject: [PATCH 0301/1437] ALSA: oxfw: fix return value in error path of isochronous resources reservation Even if isochronous resources reservation fails, error code doesn't return in pcm.hw_params callback. Cc: #5.3+ Fixes: 4f380d007052 ("ALSA: oxfw: configure packet format in pcm.hw_params callback") Signed-off-by: Takashi Sakamoto Link: https://lore.kernel.org/r/20191209151655.GA8090@workstation Signed-off-by: Takashi Iwai --- sound/firewire/oxfw/oxfw-pcm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/firewire/oxfw/oxfw-pcm.c b/sound/firewire/oxfw/oxfw-pcm.c index 9124603edabe5..67fd3e844dd6a 100644 --- a/sound/firewire/oxfw/oxfw-pcm.c +++ b/sound/firewire/oxfw/oxfw-pcm.c @@ -285,7 +285,7 @@ static int pcm_playback_hw_params(struct snd_pcm_substream *substream, mutex_unlock(&oxfw->mutex); } - return 0; + return err; } static int pcm_capture_hw_free(struct snd_pcm_substream *substream) -- GitLab From 480136343cbe89426d6c2ab74ffb4e3ee572c7ee Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Tue, 10 Dec 2019 00:05:41 +0900 Subject: [PATCH 0302/1437] ALSA: fireface: fix return value in error path of isochronous resources reservation Even if isochronous resources reservation fails, error code doesn't return in pcm.hw_params callback. Cc: #5.3+ Fixes: 55162d2bb0e8 ("ALSA: fireface: reserve/release isochronous resources in pcm.hw_params/hw_free callbacks") Signed-off-by: Takashi Sakamoto Link: https://lore.kernel.org/r/20191209151655.GA8090@workstation Signed-off-by: Takashi Iwai --- sound/firewire/fireface/ff-pcm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/firewire/fireface/ff-pcm.c b/sound/firewire/fireface/ff-pcm.c index 4e3bd9a2bec06..bd91c6ecb1123 100644 --- a/sound/firewire/fireface/ff-pcm.c +++ b/sound/firewire/fireface/ff-pcm.c @@ -247,7 +247,7 @@ static int pcm_hw_params(struct snd_pcm_substream *substream, mutex_unlock(&ff->mutex); } - return 0; + return err; } static int pcm_hw_free(struct snd_pcm_substream *substream) -- GitLab From 7acd9378dc65296b2531758aa62ee9bcf55b371c Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Sat, 7 Dec 2019 18:38:12 +0100 Subject: [PATCH 0303/1437] netfilter: nf_flow_table_offload: Correct memcpy size for flow_overload_mangle() In function 'memcpy', inlined from 'flow_offload_mangle' at net/netfilter/nf_flow_table_offload.c:112:2, inlined from 'flow_offload_port_dnat' at net/netfilter/nf_flow_table_offload.c:373:2, inlined from 'nf_flow_rule_route_ipv4' at net/netfilter/nf_flow_table_offload.c:424:3: ./include/linux/string.h:376:4: error: call to '__read_overflow2' declared with attribute error: detected read beyond size of object passed as 2nd parameter 376 | __read_overflow2(); | ^~~~~~~~~~~~~~~~~~ The original u8* was done in the hope to make this more adaptable but consensus is to keep this like it is in tc pedit. Fixes: c29f74e0df7a ("netfilter: nf_flow_table: hardware offload support") Reported-by: Laura Abbott Signed-off-by: Pablo Neira Ayuso --- net/netfilter/nf_flow_table_offload.c | 59 +++++++++++++-------------- 1 file changed, 28 insertions(+), 31 deletions(-) diff --git a/net/netfilter/nf_flow_table_offload.c b/net/netfilter/nf_flow_table_offload.c index c94ebad78c5c4..de7a0d1e15c88 100644 --- a/net/netfilter/nf_flow_table_offload.c +++ b/net/netfilter/nf_flow_table_offload.c @@ -112,8 +112,8 @@ static int nf_flow_rule_match(struct nf_flow_match *match, } static void flow_offload_mangle(struct flow_action_entry *entry, - enum flow_action_mangle_base htype, - u32 offset, u8 *value, u8 *mask) + enum flow_action_mangle_base htype, u32 offset, + const __be32 *value, const __be32 *mask) { entry->id = FLOW_ACTION_MANGLE; entry->mangle.htype = htype; @@ -150,12 +150,12 @@ static int flow_offload_eth_src(struct net *net, memcpy(&val16, dev->dev_addr, 2); val = val16 << 16; flow_offload_mangle(entry0, FLOW_ACT_MANGLE_HDR_TYPE_ETH, 4, - (u8 *)&val, (u8 *)&mask); + &val, &mask); mask = ~0xffffffff; memcpy(&val, dev->dev_addr + 2, 4); flow_offload_mangle(entry1, FLOW_ACT_MANGLE_HDR_TYPE_ETH, 8, - (u8 *)&val, (u8 *)&mask); + &val, &mask); dev_put(dev); return 0; @@ -180,13 +180,13 @@ static int flow_offload_eth_dst(struct net *net, mask = ~0xffffffff; memcpy(&val, n->ha, 4); flow_offload_mangle(entry0, FLOW_ACT_MANGLE_HDR_TYPE_ETH, 0, - (u8 *)&val, (u8 *)&mask); + &val, &mask); mask = ~0x0000ffff; memcpy(&val16, n->ha + 4, 2); val = val16; flow_offload_mangle(entry1, FLOW_ACT_MANGLE_HDR_TYPE_ETH, 4, - (u8 *)&val, (u8 *)&mask); + &val, &mask); neigh_release(n); return 0; @@ -216,7 +216,7 @@ static void flow_offload_ipv4_snat(struct net *net, } flow_offload_mangle(entry, FLOW_ACT_MANGLE_HDR_TYPE_IP4, offset, - (u8 *)&addr, (u8 *)&mask); + &addr, &mask); } static void flow_offload_ipv4_dnat(struct net *net, @@ -243,12 +243,12 @@ static void flow_offload_ipv4_dnat(struct net *net, } flow_offload_mangle(entry, FLOW_ACT_MANGLE_HDR_TYPE_IP4, offset, - (u8 *)&addr, (u8 *)&mask); + &addr, &mask); } static void flow_offload_ipv6_mangle(struct nf_flow_rule *flow_rule, unsigned int offset, - u8 *addr, u8 *mask) + const __be32 *addr, const __be32 *mask) { struct flow_action_entry *entry; int i; @@ -256,8 +256,7 @@ static void flow_offload_ipv6_mangle(struct nf_flow_rule *flow_rule, for (i = 0; i < sizeof(struct in6_addr) / sizeof(u32); i += sizeof(u32)) { entry = flow_action_entry_next(flow_rule); flow_offload_mangle(entry, FLOW_ACT_MANGLE_HDR_TYPE_IP6, - offset + i, - &addr[i], mask); + offset + i, &addr[i], mask); } } @@ -267,23 +266,23 @@ static void flow_offload_ipv6_snat(struct net *net, struct nf_flow_rule *flow_rule) { u32 mask = ~htonl(0xffffffff); - const u8 *addr; + const __be32 *addr; u32 offset; switch (dir) { case FLOW_OFFLOAD_DIR_ORIGINAL: - addr = flow->tuplehash[FLOW_OFFLOAD_DIR_REPLY].tuple.dst_v6.s6_addr; + addr = flow->tuplehash[FLOW_OFFLOAD_DIR_REPLY].tuple.dst_v6.s6_addr32; offset = offsetof(struct ipv6hdr, saddr); break; case FLOW_OFFLOAD_DIR_REPLY: - addr = flow->tuplehash[FLOW_OFFLOAD_DIR_ORIGINAL].tuple.src_v6.s6_addr; + addr = flow->tuplehash[FLOW_OFFLOAD_DIR_ORIGINAL].tuple.src_v6.s6_addr32; offset = offsetof(struct ipv6hdr, daddr); break; default: return; } - flow_offload_ipv6_mangle(flow_rule, offset, (u8 *)addr, (u8 *)&mask); + flow_offload_ipv6_mangle(flow_rule, offset, addr, &mask); } static void flow_offload_ipv6_dnat(struct net *net, @@ -292,23 +291,23 @@ static void flow_offload_ipv6_dnat(struct net *net, struct nf_flow_rule *flow_rule) { u32 mask = ~htonl(0xffffffff); - const u8 *addr; + const __be32 *addr; u32 offset; switch (dir) { case FLOW_OFFLOAD_DIR_ORIGINAL: - addr = flow->tuplehash[FLOW_OFFLOAD_DIR_REPLY].tuple.src_v6.s6_addr; + addr = flow->tuplehash[FLOW_OFFLOAD_DIR_REPLY].tuple.src_v6.s6_addr32; offset = offsetof(struct ipv6hdr, daddr); break; case FLOW_OFFLOAD_DIR_REPLY: - addr = flow->tuplehash[FLOW_OFFLOAD_DIR_ORIGINAL].tuple.dst_v6.s6_addr; + addr = flow->tuplehash[FLOW_OFFLOAD_DIR_ORIGINAL].tuple.dst_v6.s6_addr32; offset = offsetof(struct ipv6hdr, saddr); break; default: return; } - flow_offload_ipv6_mangle(flow_rule, offset, (u8 *)addr, (u8 *)&mask); + flow_offload_ipv6_mangle(flow_rule, offset, addr, &mask); } static int flow_offload_l4proto(const struct flow_offload *flow) @@ -336,25 +335,24 @@ static void flow_offload_port_snat(struct net *net, struct nf_flow_rule *flow_rule) { struct flow_action_entry *entry = flow_action_entry_next(flow_rule); - u32 mask = ~htonl(0xffff0000); - __be16 port; + u32 mask = ~htonl(0xffff0000), port; u32 offset; switch (dir) { case FLOW_OFFLOAD_DIR_ORIGINAL: - port = flow->tuplehash[FLOW_OFFLOAD_DIR_REPLY].tuple.dst_port; + port = ntohs(flow->tuplehash[FLOW_OFFLOAD_DIR_REPLY].tuple.dst_port); offset = 0; /* offsetof(struct tcphdr, source); */ break; case FLOW_OFFLOAD_DIR_REPLY: - port = flow->tuplehash[FLOW_OFFLOAD_DIR_ORIGINAL].tuple.src_port; + port = ntohs(flow->tuplehash[FLOW_OFFLOAD_DIR_ORIGINAL].tuple.src_port); offset = 0; /* offsetof(struct tcphdr, dest); */ break; default: return; } - + port = htonl(port << 16); flow_offload_mangle(entry, flow_offload_l4proto(flow), offset, - (u8 *)&port, (u8 *)&mask); + &port, &mask); } static void flow_offload_port_dnat(struct net *net, @@ -363,25 +361,24 @@ static void flow_offload_port_dnat(struct net *net, struct nf_flow_rule *flow_rule) { struct flow_action_entry *entry = flow_action_entry_next(flow_rule); - u32 mask = ~htonl(0xffff); - __be16 port; + u32 mask = ~htonl(0xffff), port; u32 offset; switch (dir) { case FLOW_OFFLOAD_DIR_ORIGINAL: - port = flow->tuplehash[FLOW_OFFLOAD_DIR_REPLY].tuple.dst_port; + port = ntohs(flow->tuplehash[FLOW_OFFLOAD_DIR_REPLY].tuple.dst_port); offset = 0; /* offsetof(struct tcphdr, source); */ break; case FLOW_OFFLOAD_DIR_REPLY: - port = flow->tuplehash[FLOW_OFFLOAD_DIR_ORIGINAL].tuple.src_port; + port = ntohs(flow->tuplehash[FLOW_OFFLOAD_DIR_ORIGINAL].tuple.src_port); offset = 0; /* offsetof(struct tcphdr, dest); */ break; default: return; } - + port = htonl(port); flow_offload_mangle(entry, flow_offload_l4proto(flow), offset, - (u8 *)&port, (u8 *)&mask); + &port, &mask); } static void flow_offload_ipv4_checksum(struct net *net, -- GitLab From ad8c28a9eb81ca90fda29f48cfb4d19305943737 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Mon, 9 Sep 2019 15:58:55 -0400 Subject: [PATCH 0304/1437] ceph: convert int fields in ceph_mount_options to unsigned int Most of these values should never be negative, so convert them to unsigned values. Add some sanity checking to the parsed values, and clean up some unneeded casts. Note that while caps_max should never be negative, this patch leaves it signed, since this value ends up later being compared to a signed counter. Just ensure that userland never passes in a negative value for caps_max. Signed-off-by: Jeff Layton Signed-off-by: Ilya Dryomov --- fs/ceph/mds_client.c | 5 +++-- fs/ceph/super.c | 28 +++++++++++++++------------- fs/ceph/super.h | 16 ++++++++-------- 3 files changed, 26 insertions(+), 23 deletions(-) diff --git a/fs/ceph/mds_client.c b/fs/ceph/mds_client.c index 068b029cf0739..a3110d216aae2 100644 --- a/fs/ceph/mds_client.c +++ b/fs/ceph/mds_client.c @@ -2032,12 +2032,13 @@ int ceph_alloc_readdir_reply_buffer(struct ceph_mds_request *req, struct ceph_mds_reply_info_parsed *rinfo = &req->r_reply_info; struct ceph_mount_options *opt = req->r_mdsc->fsc->mount_options; size_t size = sizeof(struct ceph_mds_reply_dir_entry); - int order, num_entries; + unsigned int num_entries; + int order; spin_lock(&ci->i_ceph_lock); num_entries = ci->i_files + ci->i_subdirs; spin_unlock(&ci->i_ceph_lock); - num_entries = max(num_entries, 1); + num_entries = max(num_entries, 1U); num_entries = min(num_entries, opt->max_readdir); order = get_order(size * num_entries); diff --git a/fs/ceph/super.c b/fs/ceph/super.c index 9c9a7c68eea3b..29a795f975dfa 100644 --- a/fs/ceph/super.c +++ b/fs/ceph/super.c @@ -172,10 +172,10 @@ static const struct fs_parameter_enum ceph_mount_param_enums[] = { static const struct fs_parameter_spec ceph_mount_param_specs[] = { fsparam_flag_no ("acl", Opt_acl), fsparam_flag_no ("asyncreaddir", Opt_asyncreaddir), - fsparam_u32 ("caps_max", Opt_caps_max), + fsparam_s32 ("caps_max", Opt_caps_max), fsparam_u32 ("caps_wanted_delay_max", Opt_caps_wanted_delay_max), fsparam_u32 ("caps_wanted_delay_min", Opt_caps_wanted_delay_min), - fsparam_s32 ("write_congestion_kb", Opt_congestion_kb), + fsparam_u32 ("write_congestion_kb", Opt_congestion_kb), fsparam_flag_no ("copyfrom", Opt_copyfrom), fsparam_flag_no ("dcache", Opt_dcache), fsparam_flag_no ("dirstat", Opt_dirstat), @@ -187,8 +187,8 @@ static const struct fs_parameter_spec ceph_mount_param_specs[] = { fsparam_flag_no ("quotadf", Opt_quotadf), fsparam_u32 ("rasize", Opt_rasize), fsparam_flag_no ("rbytes", Opt_rbytes), - fsparam_s32 ("readdir_max_bytes", Opt_readdir_max_bytes), - fsparam_s32 ("readdir_max_entries", Opt_readdir_max_entries), + fsparam_u32 ("readdir_max_bytes", Opt_readdir_max_bytes), + fsparam_u32 ("readdir_max_entries", Opt_readdir_max_entries), fsparam_enum ("recover_session", Opt_recover_session), fsparam_flag_no ("require_active_mds", Opt_require_active_mds), fsparam_u32 ("rsize", Opt_rsize), @@ -328,7 +328,9 @@ static int ceph_parse_mount_param(struct fs_context *fc, fsopt->caps_wanted_delay_max = result.uint_32; break; case Opt_caps_max: - fsopt->caps_max = result.uint_32; + if (result.int_32 < 0) + goto out_of_range; + fsopt->caps_max = result.int_32; break; case Opt_readdir_max_entries: if (result.uint_32 < 1) @@ -547,25 +549,25 @@ static int ceph_show_options(struct seq_file *m, struct dentry *root) seq_show_option(m, "recover_session", "clean"); if (fsopt->wsize != CEPH_MAX_WRITE_SIZE) - seq_printf(m, ",wsize=%d", fsopt->wsize); + seq_printf(m, ",wsize=%u", fsopt->wsize); if (fsopt->rsize != CEPH_MAX_READ_SIZE) - seq_printf(m, ",rsize=%d", fsopt->rsize); + seq_printf(m, ",rsize=%u", fsopt->rsize); if (fsopt->rasize != CEPH_RASIZE_DEFAULT) - seq_printf(m, ",rasize=%d", fsopt->rasize); + seq_printf(m, ",rasize=%u", fsopt->rasize); if (fsopt->congestion_kb != default_congestion_kb()) - seq_printf(m, ",write_congestion_kb=%d", fsopt->congestion_kb); + seq_printf(m, ",write_congestion_kb=%u", fsopt->congestion_kb); if (fsopt->caps_max) seq_printf(m, ",caps_max=%d", fsopt->caps_max); if (fsopt->caps_wanted_delay_min != CEPH_CAPS_WANTED_DELAY_MIN_DEFAULT) - seq_printf(m, ",caps_wanted_delay_min=%d", + seq_printf(m, ",caps_wanted_delay_min=%u", fsopt->caps_wanted_delay_min); if (fsopt->caps_wanted_delay_max != CEPH_CAPS_WANTED_DELAY_MAX_DEFAULT) - seq_printf(m, ",caps_wanted_delay_max=%d", + seq_printf(m, ",caps_wanted_delay_max=%u", fsopt->caps_wanted_delay_max); if (fsopt->max_readdir != CEPH_MAX_READDIR_DEFAULT) - seq_printf(m, ",readdir_max_entries=%d", fsopt->max_readdir); + seq_printf(m, ",readdir_max_entries=%u", fsopt->max_readdir); if (fsopt->max_readdir_bytes != CEPH_MAX_READDIR_BYTES_DEFAULT) - seq_printf(m, ",readdir_max_bytes=%d", fsopt->max_readdir_bytes); + seq_printf(m, ",readdir_max_bytes=%u", fsopt->max_readdir_bytes); if (strcmp(fsopt->snapdir_name, CEPH_SNAPDIRNAME_DEFAULT)) seq_show_option(m, "snapdirname", fsopt->snapdir_name); diff --git a/fs/ceph/super.h b/fs/ceph/super.h index f0f9cb7447ac9..3bf1a01cd536d 100644 --- a/fs/ceph/super.h +++ b/fs/ceph/super.h @@ -73,16 +73,16 @@ #define CEPH_CAPS_WANTED_DELAY_MAX_DEFAULT 60 /* cap release delay */ struct ceph_mount_options { - int flags; + unsigned int flags; - int wsize; /* max write size */ - int rsize; /* max read size */ - int rasize; /* max readahead */ - int congestion_kb; /* max writeback in flight */ - int caps_wanted_delay_min, caps_wanted_delay_max; + unsigned int wsize; /* max write size */ + unsigned int rsize; /* max read size */ + unsigned int rasize; /* max readahead */ + unsigned int congestion_kb; /* max writeback in flight */ + unsigned int caps_wanted_delay_min, caps_wanted_delay_max; int caps_max; - int max_readdir; /* max readdir result (entires) */ - int max_readdir_bytes; /* max readdir result (bytes) */ + unsigned int max_readdir; /* max readdir result (entries) */ + unsigned int max_readdir_bytes; /* max readdir result (bytes) */ /* * everything above this point can be memcmp'd; everything below -- GitLab From 3a3430affce5de301fc8e6e50fa3543d7597820e Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Wed, 20 Nov 2019 12:00:59 -0500 Subject: [PATCH 0305/1437] ceph: show tasks waiting on caps in debugfs caps file Add some visibility of tasks that are waiting for caps to the "caps" debugfs file. Display the tgid of the waiting task, inode number, and the caps the task needs and wants. Signed-off-by: Jeff Layton Reviewed-by: "Yan, Zheng" Signed-off-by: Ilya Dryomov --- fs/ceph/caps.c | 17 +++++++++++++++++ fs/ceph/debugfs.c | 13 +++++++++++++ fs/ceph/mds_client.c | 1 + fs/ceph/mds_client.h | 9 +++++++++ 4 files changed, 40 insertions(+) diff --git a/fs/ceph/caps.c b/fs/ceph/caps.c index f5a38910a82bf..271d8283d263d 100644 --- a/fs/ceph/caps.c +++ b/fs/ceph/caps.c @@ -2764,7 +2764,19 @@ int ceph_get_caps(struct file *filp, int need, int want, if (ret == -EAGAIN) continue; if (!ret) { + struct ceph_mds_client *mdsc = fsc->mdsc; + struct cap_wait cw; DEFINE_WAIT_FUNC(wait, woken_wake_function); + + cw.ino = inode->i_ino; + cw.tgid = current->tgid; + cw.need = need; + cw.want = want; + + spin_lock(&mdsc->caps_list_lock); + list_add(&cw.list, &mdsc->cap_wait_list); + spin_unlock(&mdsc->caps_list_lock); + add_wait_queue(&ci->i_cap_wq, &wait); flags |= NON_BLOCKING; @@ -2778,6 +2790,11 @@ int ceph_get_caps(struct file *filp, int need, int want, } remove_wait_queue(&ci->i_cap_wq, &wait); + + spin_lock(&mdsc->caps_list_lock); + list_del(&cw.list); + spin_unlock(&mdsc->caps_list_lock); + if (ret == -EAGAIN) continue; } diff --git a/fs/ceph/debugfs.c b/fs/ceph/debugfs.c index facb387c27356..c281f32b54f7b 100644 --- a/fs/ceph/debugfs.c +++ b/fs/ceph/debugfs.c @@ -139,6 +139,7 @@ static int caps_show(struct seq_file *s, void *p) struct ceph_fs_client *fsc = s->private; struct ceph_mds_client *mdsc = fsc->mdsc; int total, avail, used, reserved, min, i; + struct cap_wait *cw; ceph_reservation_status(fsc, &total, &avail, &used, &reserved, &min); seq_printf(s, "total\t\t%d\n" @@ -166,6 +167,18 @@ static int caps_show(struct seq_file *s, void *p) } mutex_unlock(&mdsc->mutex); + seq_printf(s, "\n\nWaiters:\n--------\n"); + seq_printf(s, "tgid ino need want\n"); + seq_printf(s, "-----------------------------------------------------\n"); + + spin_lock(&mdsc->caps_list_lock); + list_for_each_entry(cw, &mdsc->cap_wait_list, list) { + seq_printf(s, "%-13d0x%-17lx%-17s%-17s\n", cw->tgid, cw->ino, + ceph_cap_string(cw->need), + ceph_cap_string(cw->want)); + } + spin_unlock(&mdsc->caps_list_lock); + return 0; } diff --git a/fs/ceph/mds_client.c b/fs/ceph/mds_client.c index a3110d216aae2..f8735fc9f9d6d 100644 --- a/fs/ceph/mds_client.c +++ b/fs/ceph/mds_client.c @@ -4169,6 +4169,7 @@ int ceph_mdsc_init(struct ceph_fs_client *fsc) INIT_DELAYED_WORK(&mdsc->delayed_work, delayed_work); mdsc->last_renew_caps = jiffies; INIT_LIST_HEAD(&mdsc->cap_delay_list); + INIT_LIST_HEAD(&mdsc->cap_wait_list); spin_lock_init(&mdsc->cap_delay_lock); INIT_LIST_HEAD(&mdsc->snap_flush_list); spin_lock_init(&mdsc->snap_flush_lock); diff --git a/fs/ceph/mds_client.h b/fs/ceph/mds_client.h index 5cd131b41d84f..14c7e8c49970a 100644 --- a/fs/ceph/mds_client.h +++ b/fs/ceph/mds_client.h @@ -340,6 +340,14 @@ struct ceph_quotarealm_inode { struct inode *inode; }; +struct cap_wait { + struct list_head list; + unsigned long ino; + pid_t tgid; + int need; + int want; +}; + /* * mds client state */ @@ -416,6 +424,7 @@ struct ceph_mds_client { spinlock_t caps_list_lock; struct list_head caps_list; /* unused (reserved or unreserved) */ + struct list_head cap_wait_list; int caps_total_count; /* total caps allocated */ int caps_use_count; /* in use */ int caps_use_max; /* max used caps */ -- GitLab From bba1560bd4a46aa0d16bb7d81abd9d0eb47dea36 Mon Sep 17 00:00:00 2001 From: Xiubo Li Date: Tue, 26 Nov 2019 07:32:22 -0500 Subject: [PATCH 0306/1437] ceph: trigger the reclaim work once there has enough pending caps The nr in ceph_reclaim_caps_nr() is very possibly larger than 1, so we may miss it and the reclaim work couldn't triggered as expected. Signed-off-by: Xiubo Li Reviewed-by: "Yan, Zheng" Signed-off-by: Ilya Dryomov --- fs/ceph/mds_client.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/ceph/mds_client.c b/fs/ceph/mds_client.c index f8735fc9f9d6d..374db1bd57d10 100644 --- a/fs/ceph/mds_client.c +++ b/fs/ceph/mds_client.c @@ -2015,7 +2015,7 @@ void ceph_reclaim_caps_nr(struct ceph_mds_client *mdsc, int nr) if (!nr) return; val = atomic_add_return(nr, &mdsc->cap_reclaim_pending); - if (!(val % CEPH_CAPS_PER_RELEASE)) { + if ((val % CEPH_CAPS_PER_RELEASE) < nr) { atomic_set(&mdsc->cap_reclaim_pending, 0); ceph_queue_cap_reclaim_work(mdsc); } -- GitLab From bd84fbcb319852f7aeaabc0db5bdeb87e3337bf3 Mon Sep 17 00:00:00 2001 From: Xiubo Li Date: Tue, 3 Dec 2019 03:00:51 -0500 Subject: [PATCH 0307/1437] ceph: switch to global cap helper __ceph_is_any_caps is a duplicate helper. Signed-off-by: Xiubo Li Reviewed-by: Jeff Layton Signed-off-by: Ilya Dryomov --- fs/ceph/caps.c | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/fs/ceph/caps.c b/fs/ceph/caps.c index 271d8283d263d..9d09bb53c1ab4 100644 --- a/fs/ceph/caps.c +++ b/fs/ceph/caps.c @@ -1011,18 +1011,13 @@ static int __ceph_is_single_caps(struct ceph_inode_info *ci) return rb_first(&ci->i_caps) == rb_last(&ci->i_caps); } -static int __ceph_is_any_caps(struct ceph_inode_info *ci) -{ - return !RB_EMPTY_ROOT(&ci->i_caps); -} - int ceph_is_any_caps(struct inode *inode) { struct ceph_inode_info *ci = ceph_inode(inode); int ret; spin_lock(&ci->i_ceph_lock); - ret = __ceph_is_any_caps(ci); + ret = __ceph_is_any_real_caps(ci); spin_unlock(&ci->i_ceph_lock); return ret; @@ -1099,15 +1094,16 @@ void __ceph_remove_cap(struct ceph_cap *cap, bool queue_release) if (removed) ceph_put_cap(mdsc, cap); - /* when reconnect denied, we remove session caps forcibly, - * i_wr_ref can be non-zero. If there are ongoing write, - * keep i_snap_realm. - */ - if (!__ceph_is_any_caps(ci) && ci->i_wr_ref == 0 && ci->i_snap_realm) - drop_inode_snap_realm(ci); + if (!__ceph_is_any_real_caps(ci)) { + /* when reconnect denied, we remove session caps forcibly, + * i_wr_ref can be non-zero. If there are ongoing write, + * keep i_snap_realm. + */ + if (ci->i_wr_ref == 0 && ci->i_snap_realm) + drop_inode_snap_realm(ci); - if (!__ceph_is_any_real_caps(ci)) __cap_delay_cancel(mdsc, ci); + } } struct cap_msg_args { @@ -2945,7 +2941,7 @@ void ceph_put_cap_refs(struct ceph_inode_info *ci, int had) ci->i_head_snapc = NULL; } /* see comment in __ceph_remove_cap() */ - if (!__ceph_is_any_caps(ci) && ci->i_snap_realm) + if (!__ceph_is_any_real_caps(ci) && ci->i_snap_realm) drop_inode_snap_realm(ci); } spin_unlock(&ci->i_ceph_lock); -- GitLab From da08e1e1d7c3f805f8771ad6a6fd3a7a30ba4fe2 Mon Sep 17 00:00:00 2001 From: Xiubo Li Date: Tue, 26 Nov 2019 07:24:20 -0500 Subject: [PATCH 0308/1437] ceph: add more debug info when decoding mdsmap Show the laggy state. Signed-off-by: Xiubo Li Reviewed-by: Jeff Layton Signed-off-by: Ilya Dryomov --- fs/ceph/mdsmap.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/fs/ceph/mdsmap.c b/fs/ceph/mdsmap.c index aeec1d6e3769e..471bac335fae6 100644 --- a/fs/ceph/mdsmap.c +++ b/fs/ceph/mdsmap.c @@ -158,6 +158,7 @@ struct ceph_mdsmap *ceph_mdsmap_decode(void **p, void *end) void *pexport_targets = NULL; struct ceph_timespec laggy_since; struct ceph_mds_info *info; + bool laggy; ceph_decode_need(p, end, sizeof(u64) + 1, bad); global_id = ceph_decode_64(p); @@ -190,6 +191,7 @@ struct ceph_mdsmap *ceph_mdsmap_decode(void **p, void *end) if (err) goto corrupt; ceph_decode_copy(p, &laggy_since, sizeof(laggy_since)); + laggy = laggy_since.tv_sec != 0 || laggy_since.tv_nsec != 0; *p += sizeof(u32); ceph_decode_32_safe(p, end, namelen, bad); *p += namelen; @@ -207,10 +209,11 @@ struct ceph_mdsmap *ceph_mdsmap_decode(void **p, void *end) *p = info_end; } - dout("mdsmap_decode %d/%d %lld mds%d.%d %s %s\n", + dout("mdsmap_decode %d/%d %lld mds%d.%d %s %s%s\n", i+1, n, global_id, mds, inc, ceph_pr_addr(&addr), - ceph_mds_state_name(state)); + ceph_mds_state_name(state), + laggy ? "(laggy)" : ""); if (mds < 0 || state <= 0) continue; @@ -230,8 +233,7 @@ struct ceph_mdsmap *ceph_mdsmap_decode(void **p, void *end) info->global_id = global_id; info->state = state; info->addr = addr; - info->laggy = (laggy_since.tv_sec != 0 || - laggy_since.tv_nsec != 0); + info->laggy = laggy; info->num_export_targets = num_export_targets; if (num_export_targets) { info->export_targets = kcalloc(num_export_targets, @@ -355,6 +357,8 @@ struct ceph_mdsmap *ceph_mdsmap_decode(void **p, void *end) m->m_damaged = false; } bad_ext: + dout("mdsmap_decode m_enabled: %d, m_damaged: %d, m_num_laggy: %d\n", + !!m->m_enabled, !!m->m_damaged, m->m_num_laggy); *p = end; dout("mdsmap_decode success epoch %u\n", m->m_epoch); return m; -- GitLab From 8a2b22203f8596729c54eba221b4044351bfe167 Mon Sep 17 00:00:00 2001 From: Grygorii Strashko Date: Mon, 9 Dec 2019 13:19:24 +0200 Subject: [PATCH 0309/1437] net: ethernet: ti: davinci_cpdma: fix warning "device driver frees DMA memory with different size" The TI CPSW(s) driver produces warning with DMA API debug options enabled: WARNING: CPU: 0 PID: 1033 at kernel/dma/debug.c:1025 check_unmap+0x4a8/0x968 DMA-API: cpsw 48484000.ethernet: device driver frees DMA memory with different size [device address=0x00000000abc6aa02] [map size=64 bytes] [unmap size=42 bytes] CPU: 0 PID: 1033 Comm: ping Not tainted 5.3.0-dirty #41 Hardware name: Generic DRA72X (Flattened Device Tree) [] (unwind_backtrace) from [] (show_stack+0x10/0x14) [] (show_stack) from [] (dump_stack+0xd8/0x110) [] (dump_stack) from [] (__warn+0xe0/0x10c) [] (__warn) from [] (warn_slowpath_fmt+0x44/0x6c) [] (warn_slowpath_fmt) from [] (check_unmap+0x4a8/0x968) [] (check_unmap) from [] (debug_dma_unmap_page+0x80/0x90) [] (debug_dma_unmap_page) from [] (__cpdma_chan_free+0x114/0x16c) [] (__cpdma_chan_free) from [] (__cpdma_chan_process+0x158/0x17c) [] (__cpdma_chan_process) from [] (cpdma_chan_process+0x3c/0x5c) [] (cpdma_chan_process) from [] (cpsw_tx_mq_poll+0x48/0x94) [] (cpsw_tx_mq_poll) from [] (net_rx_action+0x108/0x4e4) [] (net_rx_action) from [] (__do_softirq+0xec/0x598) [] (__do_softirq) from [] (do_softirq.part.4+0x68/0x74) [] (do_softirq.part.4) from [] (__local_bh_enable_ip+0x124/0x17c) [] (__local_bh_enable_ip) from [] (ip_finish_output2+0x294/0xb7c) [] (ip_finish_output2) from [] (ip_output+0x210/0x364) [] (ip_output) from [] (ip_send_skb+0x1c/0xf8) [] (ip_send_skb) from [] (raw_sendmsg+0x9a8/0xc74) [] (raw_sendmsg) from [] (sock_sendmsg+0x14/0x24) [] (sock_sendmsg) from [] (__sys_sendto+0xbc/0x100) [] (__sys_sendto) from [] (__sys_trace_return+0x0/0x14) Exception stack(0xea9a7fa8 to 0xea9a7ff0) ... The reason is that cpdma_chan_submit_si() now stores original buffer length (sw_len) in CPDMA descriptor instead of adjusted buffer length (hw_len) used to map the buffer. Hence, fix an issue by passing correct buffer length in CPDMA descriptor. Cc: Ivan Khoronzhuk Fixes: 6670acacd59e ("net: ethernet: ti: davinci_cpdma: add dma mapped submit") Signed-off-by: Grygorii Strashko Reviewed-by: Ivan Khoronzhuk Signed-off-by: David S. Miller --- drivers/net/ethernet/ti/davinci_cpdma.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/ti/davinci_cpdma.c b/drivers/net/ethernet/ti/davinci_cpdma.c index 37ba708ac7813..6614fa3089b2c 100644 --- a/drivers/net/ethernet/ti/davinci_cpdma.c +++ b/drivers/net/ethernet/ti/davinci_cpdma.c @@ -1018,7 +1018,6 @@ static int cpdma_chan_submit_si(struct submit_info *si) struct cpdma_chan *chan = si->chan; struct cpdma_ctlr *ctlr = chan->ctlr; int len = si->len; - int swlen = len; struct cpdma_desc __iomem *desc; dma_addr_t buffer; u32 mode; @@ -1046,7 +1045,6 @@ static int cpdma_chan_submit_si(struct submit_info *si) if (si->data_dma) { buffer = si->data_dma; dma_sync_single_for_device(ctlr->dev, buffer, len, chan->dir); - swlen |= CPDMA_DMA_EXT_MAP; } else { buffer = dma_map_single(ctlr->dev, si->data_virt, len, chan->dir); ret = dma_mapping_error(ctlr->dev, buffer); @@ -1065,7 +1063,8 @@ static int cpdma_chan_submit_si(struct submit_info *si) writel_relaxed(mode | len, &desc->hw_mode); writel_relaxed((uintptr_t)si->token, &desc->sw_token); writel_relaxed(buffer, &desc->sw_buffer); - writel_relaxed(swlen, &desc->sw_len); + writel_relaxed(si->data_dma ? len | CPDMA_DMA_EXT_MAP : len, + &desc->sw_len); desc_read(desc, sw_len); __cpdma_chan_submit(chan, desc); -- GitLab From b43d1f9f7067c6759b1051e8ecb84e82cef569fe Mon Sep 17 00:00:00 2001 From: Mao Wenan Date: Mon, 9 Dec 2019 21:31:25 +0800 Subject: [PATCH 0310/1437] af_packet: set defaule value for tmo There is softlockup when using TPACKET_V3: ... NMI watchdog: BUG: soft lockup - CPU#2 stuck for 60010ms! (__irq_svc) from [] (_raw_spin_unlock_irqrestore+0x44/0x54) (_raw_spin_unlock_irqrestore) from [] (mod_timer+0x210/0x25c) (mod_timer) from [] (prb_retire_rx_blk_timer_expired+0x68/0x11c) (prb_retire_rx_blk_timer_expired) from [] (call_timer_fn+0x90/0x17c) (call_timer_fn) from [] (run_timer_softirq+0x2d4/0x2fc) (run_timer_softirq) from [] (__do_softirq+0x218/0x318) (__do_softirq) from [] (irq_exit+0x88/0xac) (irq_exit) from [] (msa_irq_exit+0x11c/0x1d4) (msa_irq_exit) from [] (handle_IPI+0x650/0x7f4) (handle_IPI) from [] (gic_handle_irq+0x108/0x118) (gic_handle_irq) from [] (__irq_usr+0x44/0x5c) ... If __ethtool_get_link_ksettings() is failed in prb_calc_retire_blk_tmo(), msec and tmo will be zero, so tov_in_jiffies is zero and the timer expire for retire_blk_timer is turn to mod_timer(&pkc->retire_blk_timer, jiffies + 0), which will trigger cpu usage of softirq is 100%. Fixes: f6fb8f100b80 ("af-packet: TPACKET_V3 flexible buffer implementation.") Tested-by: Xiao Jiangfeng Signed-off-by: Mao Wenan Signed-off-by: David S. Miller --- net/packet/af_packet.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c index 53c1d41fb1c98..118cd66b75163 100644 --- a/net/packet/af_packet.c +++ b/net/packet/af_packet.c @@ -544,7 +544,8 @@ static int prb_calc_retire_blk_tmo(struct packet_sock *po, msec = 1; div = ecmd.base.speed / 1000; } - } + } else + return DEFAULT_PRB_RETIRE_TOV; mbits = (blk_size_in_bytes * 8) / (1024 * 1024); -- GitLab From a288f105a03a7e0e629a8da2b31f34ebf0343ee2 Mon Sep 17 00:00:00 2001 From: Chuhong Yuan Date: Tue, 10 Dec 2019 00:22:07 +0800 Subject: [PATCH 0311/1437] fjes: fix missed check in fjes_acpi_add fjes_acpi_add() misses a check for platform_device_register_simple(). Add a check to fix it. Fixes: 658d439b2292 ("fjes: Introduce FUJITSU Extended Socket Network Device driver") Signed-off-by: Chuhong Yuan Signed-off-by: David S. Miller --- drivers/net/fjes/fjes_main.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/fjes/fjes_main.c b/drivers/net/fjes/fjes_main.c index b517c1af9de05..91a1059517f55 100644 --- a/drivers/net/fjes/fjes_main.c +++ b/drivers/net/fjes/fjes_main.c @@ -166,6 +166,9 @@ static int fjes_acpi_add(struct acpi_device *device) /* create platform_device */ plat_dev = platform_device_register_simple(DRV_NAME, 0, fjes_resource, ARRAY_SIZE(fjes_resource)); + if (IS_ERR(plat_dev)) + return PTR_ERR(plat_dev); + device->driver_data = plat_dev; return 0; -- GitLab From 991a34593bad5047420d281737b074cc6f821af4 Mon Sep 17 00:00:00 2001 From: Davide Caratti Date: Mon, 9 Dec 2019 17:58:52 +0100 Subject: [PATCH 0312/1437] tc-testing: unbreak full listing of tdc testcases the following command currently fails: [root@fedora tc-testing]# ./tdc.py -l The following test case IDs are not unique: {'6f5e'} Please correct them before continuing. this happens because there are two tests having the same id: [root@fedora tc-testing]# grep -r 6f5e tc-tests/* tc-tests/actions/pedit.json: "id": "6f5e", tc-tests/filters/basic.json: "id": "6f5e", fix it replacing the latest duplicate id with a brand new one: [root@fedora tc-testing]# sed -i 's/6f5e//1' tc-tests/filters/basic.json [root@fedora tc-testing]# ./tdc.py -i Fixes: 4717b05328ba ("tc-testing: Introduced tdc tests for basic filter") Signed-off-by: Davide Caratti Signed-off-by: David S. Miller --- tools/testing/selftests/tc-testing/tc-tests/filters/basic.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/testing/selftests/tc-testing/tc-tests/filters/basic.json b/tools/testing/selftests/tc-testing/tc-tests/filters/basic.json index 76ae03a64506b..2e361cea63bcd 100644 --- a/tools/testing/selftests/tc-testing/tc-tests/filters/basic.json +++ b/tools/testing/selftests/tc-testing/tc-tests/filters/basic.json @@ -152,7 +152,7 @@ ] }, { - "id": "6f5e", + "id": "b99c", "name": "Add basic filter with cmp ematch u8/transport layer and default action", "category": [ "filter", -- GitLab From 4e71dcae0c4cd1e9d19b8b3d80214a4bcdca5a42 Mon Sep 17 00:00:00 2001 From: Himanshu Madhani Date: Tue, 3 Dec 2019 14:36:55 -0800 Subject: [PATCH 0313/1437] scsi: qla2xxx: Correctly retrieve and interpret active flash region ISP27XX/28XX supports multiple flash regions. This patch fixes issue where active flash region was not interpreted correctly during secure flash update process. [mkp: typo] Fixes: 5fa8774c7f38c ("scsi: qla2xxx: Add 28xx flash primary/secondary status/image mechanism") Cc: stable@vger.kernel.org Link: https://lore.kernel.org/r/20191203223657.22109-2-hmadhani@marvell.com Signed-off-by: Michael Hernandez Signed-off-by: Himanshu Madhani Signed-off-by: Martin K. Petersen --- drivers/scsi/qla2xxx/qla_attr.c | 1 + drivers/scsi/qla2xxx/qla_bsg.c | 2 +- drivers/scsi/qla2xxx/qla_sup.c | 6 +++--- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/scsi/qla2xxx/qla_attr.c b/drivers/scsi/qla2xxx/qla_attr.c index ae97e2f310a36..d7e7043f9eab2 100644 --- a/drivers/scsi/qla2xxx/qla_attr.c +++ b/drivers/scsi/qla2xxx/qla_attr.c @@ -178,6 +178,7 @@ qla2x00_sysfs_read_nvram(struct file *filp, struct kobject *kobj, faddr = ha->flt_region_nvram; if (IS_QLA28XX(ha)) { + qla28xx_get_aux_images(vha, &active_regions); if (active_regions.aux.vpd_nvram == QLA27XX_SECONDARY_IMAGE) faddr = ha->flt_region_nvram_sec; } diff --git a/drivers/scsi/qla2xxx/qla_bsg.c b/drivers/scsi/qla2xxx/qla_bsg.c index 99f0a1a08143e..cbaf178fc9796 100644 --- a/drivers/scsi/qla2xxx/qla_bsg.c +++ b/drivers/scsi/qla2xxx/qla_bsg.c @@ -2399,7 +2399,7 @@ qla2x00_get_flash_image_status(struct bsg_job *bsg_job) struct qla_active_regions regions = { }; struct active_regions active_regions = { }; - qla28xx_get_aux_images(vha, &active_regions); + qla27xx_get_active_image(vha, &active_regions); regions.global_image = active_regions.global; if (IS_QLA28XX(ha)) { diff --git a/drivers/scsi/qla2xxx/qla_sup.c b/drivers/scsi/qla2xxx/qla_sup.c index f2d5115b2d8d1..b93a0d99e5734 100644 --- a/drivers/scsi/qla2xxx/qla_sup.c +++ b/drivers/scsi/qla2xxx/qla_sup.c @@ -847,15 +847,15 @@ qla2xxx_get_flt_info(scsi_qla_host_t *vha, uint32_t flt_addr) ha->flt_region_img_status_pri = start; break; case FLT_REG_IMG_SEC_27XX: - if (IS_QLA27XX(ha) && !IS_QLA28XX(ha)) + if (IS_QLA27XX(ha) || IS_QLA28XX(ha)) ha->flt_region_img_status_sec = start; break; case FLT_REG_FW_SEC_27XX: - if (IS_QLA27XX(ha) && !IS_QLA28XX(ha)) + if (IS_QLA27XX(ha) || IS_QLA28XX(ha)) ha->flt_region_fw_sec = start; break; case FLT_REG_BOOTLOAD_SEC_27XX: - if (IS_QLA27XX(ha) && !IS_QLA28XX(ha)) + if (IS_QLA27XX(ha) || IS_QLA28XX(ha)) ha->flt_region_boot_sec = start; break; case FLT_REG_AUX_IMG_PRI_28XX: -- GitLab From a530bf691f0e4691214562c165e6c8889dc51e57 Mon Sep 17 00:00:00 2001 From: Michael Hernandez Date: Tue, 3 Dec 2019 14:36:56 -0800 Subject: [PATCH 0314/1437] scsi: qla2xxx: Added support for MPI and PEP regions for ISP28XX This patch adds support for MPI/PEP region updates which is required with secure flash updates for ISP28XX. Fixes: 3f006ac342c0 ("scsi: qla2xxx: Secure flash update support for ISP28XX") Cc: stable@vger.kernel.org Link: https://lore.kernel.org/r/20191203223657.22109-3-hmadhani@marvell.com Signed-off-by: Michael Hernandez Signed-off-by: Himanshu Madhani Signed-off-by: Martin K. Petersen --- drivers/scsi/qla2xxx/qla_fw.h | 4 ++++ drivers/scsi/qla2xxx/qla_sup.c | 27 ++++++++++++++++++++++----- 2 files changed, 26 insertions(+), 5 deletions(-) diff --git a/drivers/scsi/qla2xxx/qla_fw.h b/drivers/scsi/qla2xxx/qla_fw.h index 59f6903e5abe3..9dc09c1174169 100644 --- a/drivers/scsi/qla2xxx/qla_fw.h +++ b/drivers/scsi/qla2xxx/qla_fw.h @@ -1523,6 +1523,10 @@ struct qla_flt_header { #define FLT_REG_NVRAM_SEC_28XX_1 0x10F #define FLT_REG_NVRAM_SEC_28XX_2 0x111 #define FLT_REG_NVRAM_SEC_28XX_3 0x113 +#define FLT_REG_MPI_PRI_28XX 0xD3 +#define FLT_REG_MPI_SEC_28XX 0xF0 +#define FLT_REG_PEP_PRI_28XX 0xD1 +#define FLT_REG_PEP_SEC_28XX 0xF1 struct qla_flt_region { uint16_t code; diff --git a/drivers/scsi/qla2xxx/qla_sup.c b/drivers/scsi/qla2xxx/qla_sup.c index b93a0d99e5734..ae9d7422e78b4 100644 --- a/drivers/scsi/qla2xxx/qla_sup.c +++ b/drivers/scsi/qla2xxx/qla_sup.c @@ -2725,8 +2725,11 @@ qla28xx_write_flash_data(scsi_qla_host_t *vha, uint32_t *dwptr, uint32_t faddr, ql_log(ql_log_warn + ql_dbg_verbose, vha, 0xffff, "Region %x is secure\n", region.code); - if (region.code == FLT_REG_FW || - region.code == FLT_REG_FW_SEC_27XX) { + switch (region.code) { + case FLT_REG_FW: + case FLT_REG_FW_SEC_27XX: + case FLT_REG_MPI_PRI_28XX: + case FLT_REG_MPI_SEC_28XX: fw_array = dwptr; /* 1st fw array */ @@ -2757,9 +2760,23 @@ qla28xx_write_flash_data(scsi_qla_host_t *vha, uint32_t *dwptr, uint32_t faddr, buf_size_without_sfub += risc_size; fw_array += risc_size; } - } else { - ql_log(ql_log_warn + ql_dbg_verbose, vha, 0xffff, - "Secure region %x not supported\n", + break; + + case FLT_REG_PEP_PRI_28XX: + case FLT_REG_PEP_SEC_28XX: + fw_array = dwptr; + + /* 1st fw array */ + risc_size = be32_to_cpu(fw_array[3]); + risc_attr = be32_to_cpu(fw_array[9]); + + buf_size_without_sfub = risc_size; + fw_array += risc_size; + break; + + default: + ql_log(ql_log_warn + ql_dbg_verbose, vha, + 0xffff, "Secure region %x not supported\n", region.code); rval = QLA_COMMAND_ERROR; goto done; -- GitLab From c868907e1ac6a08a17f8fa9ce482c0a496896e9e Mon Sep 17 00:00:00 2001 From: Michael Hernandez Date: Tue, 3 Dec 2019 14:36:57 -0800 Subject: [PATCH 0315/1437] scsi: qla2xxx: Fix incorrect SFUB length used for Secure Flash Update MB Cmd SFUB length should be in DWORDs when passed to FW. Fixes: 3f006ac342c03 ("scsi: qla2xxx: Secure flash update support for ISP28XX") Cc: stable@vger.kernel.org Link: https://lore.kernel.org/r/20191203223657.22109-4-hmadhani@marvell.com Signed-off-by: Michael Hernandez Signed-off-by: Himanshu Madhani Signed-off-by: Martin K. Petersen --- drivers/scsi/qla2xxx/qla_sup.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/scsi/qla2xxx/qla_sup.c b/drivers/scsi/qla2xxx/qla_sup.c index ae9d7422e78b4..bbe90354f49b0 100644 --- a/drivers/scsi/qla2xxx/qla_sup.c +++ b/drivers/scsi/qla2xxx/qla_sup.c @@ -2897,7 +2897,7 @@ qla28xx_write_flash_data(scsi_qla_host_t *vha, uint32_t *dwptr, uint32_t faddr, "Sending Secure Flash MB Cmd\n"); rval = qla28xx_secure_flash_update(vha, 0, region.code, buf_size_without_sfub, sfub_dma, - sizeof(struct secure_flash_update_block)); + sizeof(struct secure_flash_update_block) >> 2); if (rval != QLA_SUCCESS) { ql_log(ql_log_warn, vha, 0xffff, "Secure Flash MB Cmd failed %x.", rval); -- GitLab From 9a1b0b9a6dab452fb0e39fe96880c4faf3878369 Mon Sep 17 00:00:00 2001 From: Bo Wu Date: Sat, 7 Dec 2019 03:22:46 +0000 Subject: [PATCH 0316/1437] scsi: lpfc: Fix memory leak on lpfc_bsg_write_ebuf_set func When phba->mbox_ext_buf_ctx.seqNum != phba->mbox_ext_buf_ctx.numBuf, dd_data should be freed before return SLI_CONFIG_HANDLED. When lpfc_sli_issue_mbox func return fails, pmboxq should be also freed in job_error tag. Link: https://lore.kernel.org/r/EDBAAA0BBBA2AC4E9C8B6B81DEEE1D6915E7A966@DGGEML525-MBS.china.huawei.com Signed-off-by: Bo Wu Reviewed-by: Zhiqiang Liu Reviewed-by: James Smart Signed-off-by: Martin K. Petersen --- drivers/scsi/lpfc/lpfc_bsg.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/drivers/scsi/lpfc/lpfc_bsg.c b/drivers/scsi/lpfc/lpfc_bsg.c index d4e1b120cc9ec..0ea03ae93d91d 100644 --- a/drivers/scsi/lpfc/lpfc_bsg.c +++ b/drivers/scsi/lpfc/lpfc_bsg.c @@ -4489,12 +4489,6 @@ lpfc_bsg_write_ebuf_set(struct lpfc_hba *phba, struct bsg_job *job, phba->mbox_ext_buf_ctx.seqNum++; nemb_tp = phba->mbox_ext_buf_ctx.nembType; - dd_data = kmalloc(sizeof(struct bsg_job_data), GFP_KERNEL); - if (!dd_data) { - rc = -ENOMEM; - goto job_error; - } - pbuf = (uint8_t *)dmabuf->virt; size = job->request_payload.payload_len; sg_copy_to_buffer(job->request_payload.sg_list, @@ -4531,6 +4525,13 @@ lpfc_bsg_write_ebuf_set(struct lpfc_hba *phba, struct bsg_job *job, "2968 SLI_CONFIG ext-buffer wr all %d " "ebuffers received\n", phba->mbox_ext_buf_ctx.numBuf); + + dd_data = kmalloc(sizeof(struct bsg_job_data), GFP_KERNEL); + if (!dd_data) { + rc = -ENOMEM; + goto job_error; + } + /* mailbox command structure for base driver */ pmboxq = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL); if (!pmboxq) { @@ -4579,6 +4580,8 @@ lpfc_bsg_write_ebuf_set(struct lpfc_hba *phba, struct bsg_job *job, return SLI_CONFIG_HANDLED; job_error: + if (pmboxq) + mempool_free(pmboxq, phba->mbox_mem_pool); lpfc_bsg_dma_page_free(phba, dmabuf); kfree(dd_data); -- GitLab From b9ea0bae260f6aae546db224daa6ac1bd9d94b91 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 4 Dec 2019 02:54:27 +0100 Subject: [PATCH 0317/1437] ACPI: PM: Avoid attaching ACPI PM domain to certain devices Certain ACPI-enumerated devices represented as platform devices in Linux, like fans, require special low-level power management handling implemented by their drivers that is not in agreement with the ACPI PM domain behavior. That leads to problems with managing ACPI fans during system-wide suspend and resume. For this reason, make acpi_dev_pm_attach() skip the affected devices by adding a list of device IDs to avoid to it and putting the IDs of the affected devices into that list. Fixes: e5cc8ef31267 (ACPI / PM: Provide ACPI PM callback routines for subsystems) Reported-by: Zhang Rui Tested-by: Todd Brandt Cc: 3.10+ # 3.10+ Signed-off-by: Rafael J. Wysocki --- drivers/acpi/device_pm.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/drivers/acpi/device_pm.c b/drivers/acpi/device_pm.c index 08bb9f2f2d231..5e4a8860a9c0c 100644 --- a/drivers/acpi/device_pm.c +++ b/drivers/acpi/device_pm.c @@ -1314,9 +1314,19 @@ static void acpi_dev_pm_detach(struct device *dev, bool power_off) */ int acpi_dev_pm_attach(struct device *dev, bool power_on) { + /* + * Skip devices whose ACPI companions match the device IDs below, + * because they require special power management handling incompatible + * with the generic ACPI PM domain. + */ + static const struct acpi_device_id special_pm_ids[] = { + {"PNP0C0B", }, /* Generic ACPI fan */ + {"INT3404", }, /* Fan */ + {} + }; struct acpi_device *adev = ACPI_COMPANION(dev); - if (!adev) + if (!adev || !acpi_match_device_ids(adev, special_pm_ids)) return 0; /* -- GitLab From f2c9ee54a56995a293efef290657d8a1d80e14ab Mon Sep 17 00:00:00 2001 From: Roman Bolshakov Date: Mon, 25 Nov 2019 19:56:50 +0300 Subject: [PATCH 0318/1437] scsi: qla2xxx: Ignore NULL pointer in tcm_qla2xxx_free_mcmd If ABTS cannot be completed in target mode, the driver attempts to free related management command and crashes: NIP [d000000019181ee8] tcm_qla2xxx_free_mcmd+0x40/0x80 [tcm_qla2xxx] LR [d00000001dc1e6f8] qlt_response_pkt+0x190/0xa10 [qla2xxx] Call Trace: [c000003fff27bb50] [c000003fff27bc10] 0xc000003fff27bc10 (unreliable) [c000003fff27bb70] [d00000001dc1e6f8] qlt_response_pkt+0x190/0xa10 [qla2xxx] [c000003fff27bc10] [d00000001dbc2be0] qla24xx_process_response_queue+0x5d8/0xbd0 [qla2xxx] [c000003fff27bd50] [d00000001dbc632c] qla24xx_msix_rsp_q+0x64/0x150 [qla2xxx] [c000003fff27bde0] [c000000000187200] __handle_irq_event_percpu+0x90/0x310 [c000003fff27bea0] [c0000000001874b8] handle_irq_event_percpu+0x38/0x90 [c000003fff27bee0] [c000000000187574] handle_irq_event+0x64/0xb0 [c000003fff27bf10] [c00000000018cd38] handle_fasteoi_irq+0xe8/0x280 [c000003fff27bf40] [c000000000185ccc] generic_handle_irq+0x4c/0x70 [c000003fff27bf60] [c000000000016cec] __do_irq+0x7c/0x1d0 [c000003fff27bf90] [c00000000002a530] call_do_irq+0x14/0x24 [c00000207d2cba90] [c000000000016edc] do_IRQ+0x9c/0x130 [c00000207d2cbae0] [c000000000008bf4] hardware_interrupt_common+0x114/0x120 --- interrupt: 501 at arch_local_irq_restore+0x74/0x90 LR = arch_local_irq_restore+0x74/0x90 [c00000207d2cbdd0] [c0000000001c64fc] tick_broadcast_oneshot_control+0x4c/0x60 (unreliable) [c00000207d2cbdf0] [c0000000007ac840] cpuidle_enter_state+0xf0/0x450 [c00000207d2cbe50] [c00000000016b81c] call_cpuidle+0x4c/0x90 [c00000207d2cbe70] [c00000000016bc30] do_idle+0x2b0/0x330 [c00000207d2cbec0] [c00000000016beec] cpu_startup_entry+0x3c/0x50 [c00000207d2cbef0] [c00000000004a06c] start_secondary+0x63c/0x670 [c00000207d2cbf90] [c00000000000aa6c] start_secondary_prolog+0x10/0x14 The crash can be triggered by ACL deletion when there's active I/O. During ACL deletion, qla2xxx performs implicit LOGO that's invisible for the initiator. Only the driver and firmware are aware of the logout. Therefore the initiator continues to send SCSI commands and the target always responds with SAM STATUS BUSY as it can't find the session. The command times out after a while and initiator invokes ABORT TASK TMF for the command. The TMF is mapped to ABTS-LS in FCP. The target can't find session for S_ID originating ABTS-LS so it never allocates mcmd. And since N_Port handle was deleted after LOGO, it is no longer valid and ABTS Response IOCB is returned from firmware with status 31. Then free_mcmd is invoked on NULL pointer and the kernel crashes. [ 7734.578642] qla2xxx [0000:00:0c.0]-e837:6: ABTS_RECV_24XX: instance 0 [ 7734.578644] qla2xxx [0000:00:0c.0]-f811:6: qla_target(0): task abort (s_id=1:2:0, tag=1209504, param=0) [ 7734.578645] find_sess_by_s_id: 0x010200 [ 7734.578645] Unable to locate s_id: 0x010200 [ 7734.578646] qla2xxx [0000:00:0c.0]-f812:6: qla_target(0): task abort for non-existent session [ 7734.578648] qla2xxx [0000:00:0c.0]-e806:6: Sending task mgmt ABTS response (ha=c0000000d5819000, atio=c0000000d3fd4700, status=4 [ 7734.578730] qla2xxx [0000:00:0c.0]-e838:6: ABTS_RESP_24XX: compl_status 31 [ 7734.578732] qla2xxx [0000:00:0c.0]-e863:6: qla_target(0): ABTS_RESP_24XX failed 31 (subcode 19:a) [ 7734.578740] Unable to handle kernel paging request for data at address 0x00000200 Fixes: 6b0431d6fa20b ("scsi: qla2xxx: Fix out of order Termination and ABTS response") Cc: Quinn Tran Cc: Bart Van Assche Cc: Thomas Abraham Cc: stable@vger.kernel.org Link: https://lore.kernel.org/r/20191125165702.1013-2-r.bolshakov@yadro.com Acked-by: Himanshu Madhani Reviewed-by: Hannes Reinecke Tested-by: Hannes Reinecke Reviewed-by: Bart Van Assche Signed-off-by: Roman Bolshakov Signed-off-by: Martin K. Petersen --- drivers/scsi/qla2xxx/tcm_qla2xxx.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/scsi/qla2xxx/tcm_qla2xxx.c b/drivers/scsi/qla2xxx/tcm_qla2xxx.c index 042a24314edcf..bab2073c1f724 100644 --- a/drivers/scsi/qla2xxx/tcm_qla2xxx.c +++ b/drivers/scsi/qla2xxx/tcm_qla2xxx.c @@ -246,6 +246,8 @@ static void tcm_qla2xxx_complete_mcmd(struct work_struct *work) */ static void tcm_qla2xxx_free_mcmd(struct qla_tgt_mgmt_cmd *mcmd) { + if (!mcmd) + return; INIT_WORK(&mcmd->free_work, tcm_qla2xxx_complete_mcmd); queue_work(tcm_qla2xxx_free_wq, &mcmd->free_work); } -- GitLab From 86196a8fa8a84af1395a28ea0548f2ce6ae9bc22 Mon Sep 17 00:00:00 2001 From: Quinn Tran Date: Mon, 25 Nov 2019 19:56:51 +0300 Subject: [PATCH 0319/1437] scsi: qla2xxx: Use explicit LOGO in target mode Target makes implicit LOGO on session teardown. LOGO ELS is not send on the wire and initiator is not aware that target no longer wants talking to it. Initiator keeps sending I/O requests, target responds with BA_RJT, they time out and then initiator sends ABORT TASK (ABTS-LS). Current behaviour incurs unneeded I/O timeout and can be fixed for some initiators by making explicit LOGO on session deletion. Link: https://lore.kernel.org/r/20191125165702.1013-3-r.bolshakov@yadro.com Reviewed-by: Hannes Reinecke Tested-by: Hannes Reinecke Signed-off-by: Quinn Tran Signed-off-by: Himanshu Madhani Signed-off-by: Roman Bolshakov Signed-off-by: Martin K. Petersen --- drivers/scsi/qla2xxx/qla_def.h | 1 + drivers/scsi/qla2xxx/qla_iocb.c | 16 ++++++++++++---- drivers/scsi/qla2xxx/qla_target.c | 1 + drivers/scsi/qla2xxx/tcm_qla2xxx.c | 1 + 4 files changed, 15 insertions(+), 4 deletions(-) diff --git a/drivers/scsi/qla2xxx/qla_def.h b/drivers/scsi/qla2xxx/qla_def.h index 460f443f64716..2edd9f7b30742 100644 --- a/drivers/scsi/qla2xxx/qla_def.h +++ b/drivers/scsi/qla2xxx/qla_def.h @@ -2401,6 +2401,7 @@ typedef struct fc_port { unsigned int id_changed:1; unsigned int scan_needed:1; unsigned int n2n_flag:1; + unsigned int explicit_logout:1; struct completion nvme_del_done; uint32_t nvme_prli_service_param; diff --git a/drivers/scsi/qla2xxx/qla_iocb.c b/drivers/scsi/qla2xxx/qla_iocb.c index b25f87ff8cdee..c5a7736c45be1 100644 --- a/drivers/scsi/qla2xxx/qla_iocb.c +++ b/drivers/scsi/qla2xxx/qla_iocb.c @@ -2405,11 +2405,19 @@ qla2x00_login_iocb(srb_t *sp, struct mbx_entry *mbx) static void qla24xx_logout_iocb(srb_t *sp, struct logio_entry_24xx *logio) { + u16 control_flags = LCF_COMMAND_LOGO; logio->entry_type = LOGINOUT_PORT_IOCB_TYPE; - logio->control_flags = - cpu_to_le16(LCF_COMMAND_LOGO|LCF_IMPL_LOGO); - if (!sp->fcport->keep_nport_handle) - logio->control_flags |= cpu_to_le16(LCF_FREE_NPORT); + + if (sp->fcport->explicit_logout) { + control_flags |= LCF_EXPL_LOGO|LCF_FREE_NPORT; + } else { + control_flags |= LCF_IMPL_LOGO; + + if (!sp->fcport->keep_nport_handle) + control_flags |= LCF_FREE_NPORT; + } + + logio->control_flags = cpu_to_le16(control_flags); logio->nport_handle = cpu_to_le16(sp->fcport->loop_id); logio->port_id[0] = sp->fcport->d_id.b.al_pa; logio->port_id[1] = sp->fcport->d_id.b.area; diff --git a/drivers/scsi/qla2xxx/qla_target.c b/drivers/scsi/qla2xxx/qla_target.c index 51b275a575a52..f94afb3fe4483 100644 --- a/drivers/scsi/qla2xxx/qla_target.c +++ b/drivers/scsi/qla2xxx/qla_target.c @@ -1104,6 +1104,7 @@ void qlt_free_session_done(struct work_struct *work) } } + sess->explicit_logout = 0; spin_unlock_irqrestore(&ha->tgt.sess_lock, flags); sess->free_pending = 0; diff --git a/drivers/scsi/qla2xxx/tcm_qla2xxx.c b/drivers/scsi/qla2xxx/tcm_qla2xxx.c index bab2073c1f724..abe7f79bb7895 100644 --- a/drivers/scsi/qla2xxx/tcm_qla2xxx.c +++ b/drivers/scsi/qla2xxx/tcm_qla2xxx.c @@ -350,6 +350,7 @@ static void tcm_qla2xxx_close_session(struct se_session *se_sess) target_sess_cmd_list_set_waiting(se_sess); spin_unlock_irqrestore(&vha->hw->tgt.sess_lock, flags); + sess->explicit_logout = 1; tcm_qla2xxx_put_sess(sess); } -- GitLab From 4c86b037a6db3ad2922ef3ba8a8989eb7794e040 Mon Sep 17 00:00:00 2001 From: Roman Bolshakov Date: Mon, 25 Nov 2019 19:56:52 +0300 Subject: [PATCH 0320/1437] scsi: qla2xxx: Initialize free_work before flushing it Target creation triggers a new BUG_ON introduced in in commit 4d43d395fed1 ("workqueue: Try to catch flush_work() without INIT_WORK()."). The BUG_ON reveals an attempt to flush free_work in qla24xx_do_nack_work before it's initialized in qlt_unreg_sess: WARNING: CPU: 7 PID: 211 at kernel/workqueue.c:3031 __flush_work.isra.38+0x40/0x2e0 CPU: 7 PID: 211 Comm: kworker/7:1 Kdump: loaded Tainted: G E 5.3.0-rc7-vanilla+ #2 Workqueue: qla2xxx_wq qla2x00_iocb_work_fn [qla2xxx] NIP: c000000000159620 LR: c0080000009d91b0 CTR: c0000000001598c0 REGS: c000000005f3f730 TRAP: 0700 Tainted: G E (5.3.0-rc7-vanilla+) MSR: 800000000282b033 CR: 24002222 XER: 00000000 CFAR: c0000000001598d0 IRQMASK: 0 GPR00: c0080000009d91b0 c000000005f3f9c0 c000000001670a00 c0000003f8655ca8 GPR04: c0000003f8655c00 000000000000ffff 0000000000000011 ffffffffffffffff GPR08: c008000000949228 0000000000000000 0000000000000001 c0080000009e7780 GPR12: 0000000000002200 c00000003fff6200 c000000000161bc8 0000000000000004 GPR16: c0000003f9d68280 0000000002000000 0000000000000005 0000000000000003 GPR20: 0000000000000002 000000000000ffff 0000000000000000 fffffffffffffef7 GPR24: c000000004f73848 c000000004f73838 c000000004f73f28 c000000005f3fb60 GPR28: c000000004f73e48 c000000004f73c80 c000000004f73818 c0000003f9d68280 NIP [c000000000159620] __flush_work.isra.38+0x40/0x2e0 LR [c0080000009d91b0] qla24xx_do_nack_work+0x88/0x180 [qla2xxx] Call Trace: [c000000005f3f9c0] [c000000000159644] __flush_work.isra.38+0x64/0x2e0 (unreliable) [c000000005f3fa50] [c0080000009d91a0] qla24xx_do_nack_work+0x78/0x180 [qla2xxx] [c000000005f3fae0] [c0080000009496ec] qla2x00_do_work+0x604/0xb90 [qla2xxx] [c000000005f3fc40] [c008000000949cd8] qla2x00_iocb_work_fn+0x60/0xe0 [qla2xxx] [c000000005f3fc80] [c000000000157bb8] process_one_work+0x2c8/0x5b0 [c000000005f3fd10] [c000000000157f28] worker_thread+0x88/0x660 [c000000005f3fdb0] [c000000000161d64] kthread+0x1a4/0x1b0 [c000000005f3fe20] [c00000000000b960] ret_from_kernel_thread+0x5c/0x7c Instruction dump: 3d22001d 892966b1 7d908026 91810008 f821ff71 69290001 0b090000 2e290000 40920200 e9230018 7d2a0074 794ad182 <0b0a0000> 2fa90000 419e01e8 7c0802a6 ---[ end trace 5ccf335d4f90fcb8 ]--- Fixes: 1021f0bc2f3d6 ("scsi: qla2xxx: allow session delete to finish before create.") Cc: Quinn Tran Cc: stable@vger.kernel.org Link: https://lore.kernel.org/r/20191125165702.1013-4-r.bolshakov@yadro.com Acked-by: Himanshu Madhani Reviewed-by: Hannes Reinecke Tested-by: Hannes Reinecke Reviewed-by: Bart Van Assche Signed-off-by: Roman Bolshakov Signed-off-by: Martin K. Petersen --- drivers/scsi/qla2xxx/qla_init.c | 1 + drivers/scsi/qla2xxx/qla_target.c | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/scsi/qla2xxx/qla_init.c b/drivers/scsi/qla2xxx/qla_init.c index 6c28f38f8021a..b42be42ff1a69 100644 --- a/drivers/scsi/qla2xxx/qla_init.c +++ b/drivers/scsi/qla2xxx/qla_init.c @@ -4852,6 +4852,7 @@ qla2x00_alloc_fcport(scsi_qla_host_t *vha, gfp_t flags) } INIT_WORK(&fcport->del_work, qla24xx_delete_sess_fn); + INIT_WORK(&fcport->free_work, qlt_free_session_done); INIT_WORK(&fcport->reg_work, qla_register_fcport_fn); INIT_LIST_HEAD(&fcport->gnl_entry); INIT_LIST_HEAD(&fcport->list); diff --git a/drivers/scsi/qla2xxx/qla_target.c b/drivers/scsi/qla2xxx/qla_target.c index f94afb3fe4483..74cfcb13fe8a9 100644 --- a/drivers/scsi/qla2xxx/qla_target.c +++ b/drivers/scsi/qla2xxx/qla_target.c @@ -1161,7 +1161,6 @@ void qlt_unreg_sess(struct fc_port *sess) sess->last_rscn_gen = sess->rscn_gen; sess->last_login_gen = sess->login_gen; - INIT_WORK(&sess->free_work, qlt_free_session_done); queue_work(sess->vha->hw->wq, &sess->free_work); } EXPORT_SYMBOL(qlt_unreg_sess); -- GitLab From 600954e6f2df695434887dfc6a99a098859990cf Mon Sep 17 00:00:00 2001 From: Roman Bolshakov Date: Mon, 25 Nov 2019 19:56:53 +0300 Subject: [PATCH 0321/1437] scsi: qla2xxx: Drop superfluous INIT_WORK of del_work del_work is already initialized inside qla2x00_alloc_fcport, there's no need to overwrite it. Indeed, it might prevent complete traversal of workqueue list. Fixes: a01c77d2cbc45 ("scsi: qla2xxx: Move session delete to driver work queue") Cc: Quinn Tran Link: https://lore.kernel.org/r/20191125165702.1013-5-r.bolshakov@yadro.com Acked-by: Himanshu Madhani Reviewed-by: Hannes Reinecke Tested-by: Hannes Reinecke Reviewed-by: Bart Van Assche Signed-off-by: Roman Bolshakov Signed-off-by: Martin K. Petersen --- drivers/scsi/qla2xxx/qla_target.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/scsi/qla2xxx/qla_target.c b/drivers/scsi/qla2xxx/qla_target.c index 74cfcb13fe8a9..eeb069d52f3d3 100644 --- a/drivers/scsi/qla2xxx/qla_target.c +++ b/drivers/scsi/qla2xxx/qla_target.c @@ -1265,7 +1265,6 @@ void qlt_schedule_sess_for_deletion(struct fc_port *sess) "Scheduling sess %p for deletion %8phC\n", sess, sess->port_name); - INIT_WORK(&sess->del_work, qla24xx_delete_sess_fn); WARN_ON(!queue_work(sess->vha->hw->wq, &sess->del_work)); } -- GitLab From 58e39a2ce4be08162c0368030cdc405f7fd849aa Mon Sep 17 00:00:00 2001 From: Roman Bolshakov Date: Mon, 25 Nov 2019 19:56:54 +0300 Subject: [PATCH 0322/1437] scsi: qla2xxx: Change discovery state before PLOGI When a port sends PLOGI, discovery state should be changed to login pending, otherwise RELOGIN_NEEDED bit is set in qla24xx_handle_plogi_done_event(). RELOGIN_NEEDED triggers another PLOGI, and it never goes out of the loop until login timer expires. Fixes: 8777e4314d397 ("scsi: qla2xxx: Migrate NVME N2N handling into state machine") Fixes: 8b5292bcfcacf ("scsi: qla2xxx: Fix Relogin to prevent modifying scan_state flag") Cc: Quinn Tran Cc: stable@vger.kernel.org Link: https://lore.kernel.org/r/20191125165702.1013-6-r.bolshakov@yadro.com Acked-by: Himanshu Madhani Reviewed-by: Hannes Reinecke Tested-by: Hannes Reinecke Signed-off-by: Roman Bolshakov Signed-off-by: Martin K. Petersen --- drivers/scsi/qla2xxx/qla_init.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/scsi/qla2xxx/qla_init.c b/drivers/scsi/qla2xxx/qla_init.c index b42be42ff1a69..fc2ff169dc5ab 100644 --- a/drivers/scsi/qla2xxx/qla_init.c +++ b/drivers/scsi/qla2xxx/qla_init.c @@ -533,6 +533,7 @@ static int qla_post_els_plogi_work(struct scsi_qla_host *vha, fc_port_t *fcport) e->u.fcport.fcport = fcport; fcport->flags |= FCF_ASYNC_ACTIVE; + fcport->disc_state = DSC_LOGIN_PEND; return qla2x00_post_work(vha, e); } -- GitLab From a9ed06d4e640a8dc978a4649ab78dac8b16d2db6 Mon Sep 17 00:00:00 2001 From: Roman Bolshakov Date: Mon, 25 Nov 2019 19:56:55 +0300 Subject: [PATCH 0323/1437] scsi: qla2xxx: Allow PLOGI in target mode According to FC-LS-3 (Fibre Channel Link Services) 6.3.2.4 "N_Port Login - No Fabric present", if both parties in the point-to-point connection know N_Port_Names of each other, Nx_Port with the highest N_Port_name shall transmit PLOGI. The specification sets no restrictions on the port role that should send PLOGI. However, FCP-4 (Fibre Channel Protocol for SCSI, Fourth Version) 6.2 "Overview of Process Login and Process Logout", instructs that in point-to-point topology, initiator shall send explicit PRLI ELS. The change fixes stuck P2P login, when target WWPN is higher than initiator WWPN. Cc: Quinn Tran Cc: Himanshu Madhani Link: https://lore.kernel.org/r/20191125165702.1013-7-r.bolshakov@yadro.com Signed-off-by: Roman Bolshakov Signed-off-by: Martin K. Petersen --- drivers/scsi/qla2xxx/qla_init.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/scsi/qla2xxx/qla_init.c b/drivers/scsi/qla2xxx/qla_init.c index fc2ff169dc5ab..d2c1f8080b9e1 100644 --- a/drivers/scsi/qla2xxx/qla_init.c +++ b/drivers/scsi/qla2xxx/qla_init.c @@ -1527,8 +1527,8 @@ int qla24xx_fcport_handle_login(struct scsi_qla_host *vha, fc_port_t *fcport) } } - /* for pure Target Mode. Login will not be initiated */ - if (vha->host->active_mode == MODE_TARGET) + /* Target won't initiate port login if fabric is present */ + if (vha->host->active_mode == MODE_TARGET && !N2N_TOPO(vha->hw)) return 0; if (fcport->flags & FCF_ASYNC_SENT) { @@ -1720,6 +1720,10 @@ void qla24xx_handle_relogin_event(scsi_qla_host_t *vha, void qla_handle_els_plogi_done(scsi_qla_host_t *vha, struct event_arg *ea) { + /* for pure Target Mode, PRLI will not be initiated */ + if (vha->host->active_mode == MODE_TARGET) + return; + ql_dbg(ql_dbg_disc, vha, 0x2118, "%s %d %8phC post PRLI\n", __func__, __LINE__, ea->fcport->port_name); -- GitLab From 2c2f4bed9b6299e6430a65a29b5d27b8763fdf25 Mon Sep 17 00:00:00 2001 From: Roman Bolshakov Date: Mon, 25 Nov 2019 19:56:56 +0300 Subject: [PATCH 0324/1437] scsi: qla2xxx: Don't call qlt_async_event twice MBA_PORT_UPDATE generates duplicate log lines in target mode because qlt_async_event is called twice. Drop the calls within the case as the function will be called right after the switch statement. Cc: Quinn Tran Link: https://lore.kernel.org/r/20191125165702.1013-8-r.bolshakov@yadro.com Acked-by: Himanshu Madhani Reviewed-by: Hannes Reinecke Tested-by: Hannes Reinecke Acked-by: Himanshu Madhani Signed-off-by: Roman Bolshakov Signed-off-by: Martin K. Petersen --- drivers/scsi/qla2xxx/qla_isr.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/scsi/qla2xxx/qla_isr.c b/drivers/scsi/qla2xxx/qla_isr.c index 2601d7673c37d..7b8a6bfcf08d9 100644 --- a/drivers/scsi/qla2xxx/qla_isr.c +++ b/drivers/scsi/qla2xxx/qla_isr.c @@ -1061,8 +1061,6 @@ global_port_update: ql_dbg(ql_dbg_async, vha, 0x5011, "Asynchronous PORT UPDATE ignored %04x/%04x/%04x.\n", mb[1], mb[2], mb[3]); - - qlt_async_event(mb[0], vha, mb); break; } @@ -1079,8 +1077,6 @@ global_port_update: set_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags); set_bit(LOCAL_LOOP_UPDATE, &vha->dpc_flags); set_bit(VP_CONFIG_OK, &vha->vp_flags); - - qlt_async_event(mb[0], vha, mb); break; case MBA_RSCN_UPDATE: /* State Change Registration */ -- GitLab From 0334cdea1fba36fad8bdf9516f267ce01de625f7 Mon Sep 17 00:00:00 2001 From: Roman Bolshakov Date: Mon, 25 Nov 2019 19:56:57 +0300 Subject: [PATCH 0325/1437] scsi: qla2xxx: Fix PLOGI payload and ELS IOCB dump length The size of the buffer is hardcoded as 0x70 or 112 bytes, while the size of ELS IOCB is 0x40 and the size of PLOGI payload returned by Get Parameters command is 0x74. Cc: Quinn Tran Link: https://lore.kernel.org/r/20191125165702.1013-9-r.bolshakov@yadro.com Acked-by: Himanshu Madhani Reviewed-by: Hannes Reinecke Tested-by: Hannes Reinecke Signed-off-by: Roman Bolshakov Signed-off-by: Martin K. Petersen --- drivers/scsi/qla2xxx/qla_iocb.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/scsi/qla2xxx/qla_iocb.c b/drivers/scsi/qla2xxx/qla_iocb.c index c5a7736c45be1..53ccbd6b71ede 100644 --- a/drivers/scsi/qla2xxx/qla_iocb.c +++ b/drivers/scsi/qla2xxx/qla_iocb.c @@ -2684,7 +2684,8 @@ qla24xx_els_logo_iocb(srb_t *sp, struct els_entry_24xx *els_iocb) ql_dbg(ql_dbg_io + ql_dbg_buffer, vha, 0x3073, "PLOGI ELS IOCB:\n"); ql_dump_buffer(ql_log_info, vha, 0x0109, - (uint8_t *)els_iocb, 0x70); + (uint8_t *)els_iocb, + sizeof(*els_iocb)); } else { els_iocb->control_flags = 1 << 13; els_iocb->tx_byte_count = @@ -2942,7 +2943,8 @@ qla24xx_els_dcmd2_iocb(scsi_qla_host_t *vha, int els_opcode, ql_dbg(ql_dbg_disc + ql_dbg_buffer, vha, 0x3073, "PLOGI buffer:\n"); ql_dump_buffer(ql_dbg_disc + ql_dbg_buffer, vha, 0x0109, - (uint8_t *)elsio->u.els_plogi.els_plogi_pyld, 0x70); + (uint8_t *)elsio->u.els_plogi.els_plogi_pyld, + sizeof(*elsio->u.els_plogi.els_plogi_pyld)); rval = qla2x00_start_sp(sp); if (rval != QLA_SUCCESS) { -- GitLab From fd1de5830a5abaf444cc4312871e02c41e24fdc1 Mon Sep 17 00:00:00 2001 From: Roman Bolshakov Date: Mon, 25 Nov 2019 19:56:58 +0300 Subject: [PATCH 0326/1437] scsi: qla2xxx: Configure local loop for N2N target qla2x00_configure_local_loop initializes PLOGI payload for PLOGI ELS using Get Parameters mailbox command. In the case when the driver is running in target mode, the topology is N2N and the target port has higher WWPN, LOCAL_LOOP_UPDATE bit is cleared too early and PLOGI payload is not initialized by the Get Parameters command. That causes a failure of ELS IOCB carrying the PLOGI with 0x15 aka Data Underrun error. LOCAL_LOOP_UPDATE has to be set to initialize PLOGI payload. Fixes: 48acad099074 ("scsi: qla2xxx: Fix N2N link re-connect") Link: https://lore.kernel.org/r/20191125165702.1013-10-r.bolshakov@yadro.com Acked-by: Quinn Tran Acked-by: Himanshu Madhani Reviewed-by: Hannes Reinecke Tested-by: Hannes Reinecke Signed-off-by: Roman Bolshakov Signed-off-by: Martin K. Petersen --- drivers/scsi/qla2xxx/qla_init.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/drivers/scsi/qla2xxx/qla_init.c b/drivers/scsi/qla2xxx/qla_init.c index d2c1f8080b9e1..c893d89ed9360 100644 --- a/drivers/scsi/qla2xxx/qla_init.c +++ b/drivers/scsi/qla2xxx/qla_init.c @@ -4936,14 +4936,8 @@ qla2x00_configure_loop(scsi_qla_host_t *vha) set_bit(RSCN_UPDATE, &flags); clear_bit(LOCAL_LOOP_UPDATE, &flags); - } else if (ha->current_topology == ISP_CFG_N) { - clear_bit(RSCN_UPDATE, &flags); - if (qla_tgt_mode_enabled(vha)) { - /* allow the other side to start the login */ - clear_bit(LOCAL_LOOP_UPDATE, &flags); - set_bit(RELOGIN_NEEDED, &vha->dpc_flags); - } - } else if (ha->current_topology == ISP_CFG_NL) { + } else if (ha->current_topology == ISP_CFG_NL || + ha->current_topology == ISP_CFG_N) { clear_bit(RSCN_UPDATE, &flags); set_bit(LOCAL_LOOP_UPDATE, &flags); } else if (!vha->flags.online || -- GitLab From 5e6b01d84b9d20bcd77fc7c4733a2a4149bf220a Mon Sep 17 00:00:00 2001 From: Roman Bolshakov Date: Mon, 25 Nov 2019 19:56:59 +0300 Subject: [PATCH 0327/1437] scsi: qla2xxx: Send Notify ACK after N2N PLOGI qlt_handle_login schedules session for deletion even if a login is in progress. That causes login bouncing, i.e. a few logins are made before it settles down. Complete the first login by sending Notify Acknowledge IOCB via qlt_plogi_ack_unref if the session is pending login completion. Fixes: 9cd883f07a54 ("scsi: qla2xxx: Fix session cleanup for N2N") Cc: Krishna Kant Cc: Alexei Potashnik Link: https://lore.kernel.org/r/20191125165702.1013-11-r.bolshakov@yadro.com Acked-by: Quinn Tran Acked-by: Himanshu Madhani Reviewed-by: Hannes Reinecke Tested-by: Hannes Reinecke Signed-off-by: Roman Bolshakov Signed-off-by: Martin K. Petersen --- drivers/scsi/qla2xxx/qla_target.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/scsi/qla2xxx/qla_target.c b/drivers/scsi/qla2xxx/qla_target.c index eeb069d52f3d3..68c14143e50e4 100644 --- a/drivers/scsi/qla2xxx/qla_target.c +++ b/drivers/scsi/qla2xxx/qla_target.c @@ -4803,6 +4803,7 @@ static int qlt_handle_login(struct scsi_qla_host *vha, switch (sess->disc_state) { case DSC_DELETED: + case DSC_LOGIN_PEND: qlt_plogi_ack_unref(vha, pla); break; -- GitLab From dabc5ec915f3a2c657ecfb529cd3d4ec303a4412 Mon Sep 17 00:00:00 2001 From: Roman Bolshakov Date: Mon, 25 Nov 2019 19:57:00 +0300 Subject: [PATCH 0328/1437] scsi: qla2xxx: Don't defer relogin unconditonally qla2x00_configure_local_loop sets RELOGIN_NEEDED bit and calls qla24xx_fcport_handle_login to perform the login. This bit triggers a wake up of DPC later after a successful login. The deferred call is not needed if login succeeds, and it's set in qla24xx_fcport_handle_login in case of errors, hence it should be safe to drop. Link: https://lore.kernel.org/r/20191125165702.1013-12-r.bolshakov@yadro.com Acked-by: Himanshu Madhani Acked-by: Quinn Tran Reviewed-by: Hannes Reinecke Tested-by: Hannes Reinecke Signed-off-by: Roman Bolshakov Signed-off-by: Martin K. Petersen --- drivers/scsi/qla2xxx/qla_init.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/scsi/qla2xxx/qla_init.c b/drivers/scsi/qla2xxx/qla_init.c index c893d89ed9360..aa5204163beca 100644 --- a/drivers/scsi/qla2xxx/qla_init.c +++ b/drivers/scsi/qla2xxx/qla_init.c @@ -5054,7 +5054,6 @@ qla2x00_configure_local_loop(scsi_qla_host_t *vha) memcpy(&ha->plogi_els_payld.data, (void *)ha->init_cb, sizeof(ha->plogi_els_payld.data)); - set_bit(RELOGIN_NEEDED, &vha->dpc_flags); } else { ql_dbg(ql_dbg_init, vha, 0x00d1, "PLOGI ELS param read fail.\n"); -- GitLab From af22f0c7b052c5c203207f1e5ebd6aa65f87c538 Mon Sep 17 00:00:00 2001 From: Roman Bolshakov Date: Mon, 25 Nov 2019 19:57:01 +0300 Subject: [PATCH 0329/1437] scsi: qla2xxx: Ignore PORT UPDATE after N2N PLOGI PORT UPDATE asynchronous event is generated on the host that issues PLOGI ELS (in the case of higher WWPN). In that case, the event shouldn't be handled as it sets unwanted DPC flags (i.e. LOOP_RESYNC_NEEDED) that trigger link flap. Ignore the event if the host has higher WWPN, but handle otherwise. Cc: Quinn Tran Link: https://lore.kernel.org/r/20191125165702.1013-13-r.bolshakov@yadro.com Acked-by: Himanshu Madhani Reviewed-by: Hannes Reinecke Tested-by: Hannes Reinecke Signed-off-by: Roman Bolshakov Signed-off-by: Martin K. Petersen --- drivers/scsi/qla2xxx/qla_mbx.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/scsi/qla2xxx/qla_mbx.c b/drivers/scsi/qla2xxx/qla_mbx.c index 0cf94f05f0080..b7c1108c48e20 100644 --- a/drivers/scsi/qla2xxx/qla_mbx.c +++ b/drivers/scsi/qla2xxx/qla_mbx.c @@ -3921,6 +3921,7 @@ qla24xx_report_id_acquisition(scsi_qla_host_t *vha, vha->d_id.b24 = 0; vha->d_id.b.al_pa = 1; ha->flags.n2n_bigger = 1; + ha->flags.n2n_ae = 0; id.b.al_pa = 2; ql_dbg(ql_dbg_async, vha, 0x5075, @@ -3931,6 +3932,7 @@ qla24xx_report_id_acquisition(scsi_qla_host_t *vha, "Format 1: Remote login - Waiting for WWPN %8phC.\n", rptid_entry->u.f1.port_name); ha->flags.n2n_bigger = 0; + ha->flags.n2n_ae = 1; } qla24xx_post_newsess_work(vha, &id, rptid_entry->u.f1.port_name, @@ -3942,7 +3944,6 @@ qla24xx_report_id_acquisition(scsi_qla_host_t *vha, /* if our portname is higher then initiate N2N login */ set_bit(N2N_LOGIN_NEEDED, &vha->dpc_flags); - ha->flags.n2n_ae = 1; return; break; case TOPO_FL: -- GitLab From 9545767db3cde2d4f9ca581d096d97ed00754282 Mon Sep 17 00:00:00 2001 From: Roman Bolshakov Date: Mon, 25 Nov 2019 19:57:02 +0300 Subject: [PATCH 0330/1437] scsi: qla2xxx: Add debug dump of LOGO payload and ELS IOCB The change adds a way to debug LOGO ELS, likewise PLOGI. Link: https://lore.kernel.org/r/20191125165702.1013-14-r.bolshakov@yadro.com Acked-by: Himanshu Madhani Acked-by: Quinn Tran Reviewed-by: Hannes Reinecke Tested-by: Hannes Reinecke Reviewed-by: Bart Van Assche Signed-off-by: Roman Bolshakov Signed-off-by: Martin K. Petersen --- drivers/scsi/qla2xxx/qla_iocb.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/scsi/qla2xxx/qla_iocb.c b/drivers/scsi/qla2xxx/qla_iocb.c index 53ccbd6b71ede..8b050f0b43330 100644 --- a/drivers/scsi/qla2xxx/qla_iocb.c +++ b/drivers/scsi/qla2xxx/qla_iocb.c @@ -2625,6 +2625,10 @@ qla24xx_els_dcmd_iocb(scsi_qla_host_t *vha, int els_opcode, memcpy(elsio->u.els_logo.els_logo_pyld, &logo_pyld, sizeof(struct els_logo_payload)); + ql_dbg(ql_dbg_disc + ql_dbg_buffer, vha, 0x3075, "LOGO buffer:"); + ql_dump_buffer(ql_dbg_disc + ql_dbg_buffer, vha, 0x010a, + elsio->u.els_logo.els_logo_pyld, + sizeof(*elsio->u.els_logo.els_logo_pyld)); rval = qla2x00_start_sp(sp); if (rval != QLA_SUCCESS) { @@ -2697,6 +2701,11 @@ qla24xx_els_logo_iocb(srb_t *sp, struct els_entry_24xx *els_iocb) els_iocb->rx_byte_count = 0; els_iocb->rx_address = 0; els_iocb->rx_len = 0; + ql_dbg(ql_dbg_io + ql_dbg_buffer, vha, 0x3076, + "LOGO ELS IOCB:"); + ql_dump_buffer(ql_log_info, vha, 0x010b, + els_iocb, + sizeof(*els_iocb)); } sp->vha->qla_stats.control_requests++; -- GitLab From 8c850a0296004409e7bcb9464712fb2807da656a Mon Sep 17 00:00:00 2001 From: Can Guo Date: Tue, 3 Dec 2019 06:58:40 +0000 Subject: [PATCH 0331/1437] scsi: ufs: Give an unique ID to each ufs-bsg Considering there can be multiple UFS hosts in SoC, give each ufs-bsg an unique ID by appending the scsi host number to its device name. Link: https://lore.kernel.org/r/0101016eca8dc9d7-d24468d3-04d2-4ef3-a906-abe8b8cbcd3d-000000@us-west-2.amazonses.com Fixes: df032bf27a41 ("scsi: ufs: Add a bsg endpoint that supports UPIUs") Signed-off-by: Can Guo Reviewed-by: Avri Altman Reviewed-by: Christoph Hellwig Signed-off-by: Martin K. Petersen --- drivers/scsi/ufs/ufs_bsg.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/scsi/ufs/ufs_bsg.c b/drivers/scsi/ufs/ufs_bsg.c index baeecee35d1e1..53dd87628cbe4 100644 --- a/drivers/scsi/ufs/ufs_bsg.c +++ b/drivers/scsi/ufs/ufs_bsg.c @@ -203,7 +203,7 @@ int ufs_bsg_probe(struct ufs_hba *hba) bsg_dev->parent = get_device(parent); bsg_dev->release = ufs_bsg_node_release; - dev_set_name(bsg_dev, "ufs-bsg"); + dev_set_name(bsg_dev, "ufs-bsg%u", shost->host_no); ret = device_add(bsg_dev); if (ret) -- GitLab From fee92f25777789d73e1936b91472e9c4644457c8 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Tue, 3 Dec 2019 12:45:09 +0300 Subject: [PATCH 0332/1437] scsi: iscsi: qla4xxx: fix double free in probe On this error path we call qla4xxx_mem_free() and then the caller also calls qla4xxx_free_adapter() which calls qla4xxx_mem_free(). It leads to a couple double frees: drivers/scsi/qla4xxx/ql4_os.c:8856 qla4xxx_probe_adapter() warn: 'ha->chap_dma_pool' double freed drivers/scsi/qla4xxx/ql4_os.c:8856 qla4xxx_probe_adapter() warn: 'ha->fw_ddb_dma_pool' double freed Fixes: afaf5a2d341d ("[SCSI] Initial Commit of qla4xxx") Link: https://lore.kernel.org/r/20191203094421.hw7ex7qr3j2rbsmx@kili.mountain Signed-off-by: Dan Carpenter Signed-off-by: Martin K. Petersen --- drivers/scsi/qla4xxx/ql4_os.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/scsi/qla4xxx/ql4_os.c b/drivers/scsi/qla4xxx/ql4_os.c index 8c674eca09f13..2323432a0edbc 100644 --- a/drivers/scsi/qla4xxx/ql4_os.c +++ b/drivers/scsi/qla4xxx/ql4_os.c @@ -4275,7 +4275,6 @@ static int qla4xxx_mem_alloc(struct scsi_qla_host *ha) return QLA_SUCCESS; mem_alloc_error_exit: - qla4xxx_mem_free(ha); return QLA_ERROR; } -- GitLab From d168001d14eccfda229b4a41a2c31a21e3c379da Mon Sep 17 00:00:00 2001 From: sheebab Date: Tue, 3 Dec 2019 11:07:15 +0100 Subject: [PATCH 0333/1437] scsi: ufs: Disable autohibern8 feature in Cadence UFS This patch disables autohibern8 feature in Cadence UFS. The autohibern8 feature has issues due to which unexpected interrupt trigger is happening. After the interrupt issue is sorted out, autohibern8 feature will be re-enabled Link: https://lore.kernel.org/r/1575367635-22662-1-git-send-email-sheebab@cadence.com Cc: Signed-off-by: sheebab Reviewed-by: Alim Akhtar Tested-by: Vignesh Raghavendra Signed-off-by: Martin K. Petersen --- drivers/scsi/ufs/cdns-pltfrm.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/scsi/ufs/cdns-pltfrm.c b/drivers/scsi/ufs/cdns-pltfrm.c index b2af04c57a39b..6feeb0faf123a 100644 --- a/drivers/scsi/ufs/cdns-pltfrm.c +++ b/drivers/scsi/ufs/cdns-pltfrm.c @@ -99,6 +99,12 @@ static int cdns_ufs_link_startup_notify(struct ufs_hba *hba, */ ufshcd_dme_set(hba, UIC_ARG_MIB(PA_LOCAL_TX_LCC_ENABLE), 0); + /* + * Disabling Autohibern8 feature in cadence UFS + * to mask unexpected interrupt trigger. + */ + hba->ahit = 0; + return 0; } -- GitLab From f70267f379b5e5e11bdc5d72a56bf17e5feed01f Mon Sep 17 00:00:00 2001 From: Jason Yan Date: Fri, 6 Dec 2019 09:11:18 +0800 Subject: [PATCH 0334/1437] scsi: libsas: stop discovering if oob mode is disconnected The discovering of sas port is driven by workqueue in libsas. When libsas is processing port events or phy events in workqueue, new events may rise up and change the state of some structures such as asd_sas_phy. This may cause some problems such as follows: ==>thread 1 ==>thread 2 ==>phy up ==>phy_up_v3_hw() ==>oob_mode = SATA_OOB_MODE; ==>phy down quickly ==>hisi_sas_phy_down() ==>sas_ha->notify_phy_event() ==>sas_phy_disconnected() ==>oob_mode = OOB_NOT_CONNECTED ==>workqueue wakeup ==>sas_form_port() ==>sas_discover_domain() ==>sas_get_port_device() ==>oob_mode is OOB_NOT_CONNECTED and device is wrongly taken as expander This at last lead to the panic when libsas trying to issue a command to discover the device. [183047.614035] Unable to handle kernel NULL pointer dereference at virtual address 0000000000000058 [183047.622896] Mem abort info: [183047.625762] ESR = 0x96000004 [183047.628893] Exception class = DABT (current EL), IL = 32 bits [183047.634888] SET = 0, FnV = 0 [183047.638015] EA = 0, S1PTW = 0 [183047.641232] Data abort info: [183047.644189] ISV = 0, ISS = 0x00000004 [183047.648100] CM = 0, WnR = 0 [183047.651145] user pgtable: 4k pages, 48-bit VAs, pgdp = 00000000b7df67be [183047.657834] [0000000000000058] pgd=0000000000000000 [183047.662789] Internal error: Oops: 96000004 [#1] SMP [183047.667740] Process kworker/u16:2 (pid: 31291, stack limit = 0x00000000417c4974) [183047.675208] CPU: 0 PID: 3291 Comm: kworker/u16:2 Tainted: G W OE 4.19.36-vhulk1907.1.0.h410.eulerosv2r8.aarch64 #1 [183047.687015] Hardware name: N/A N/A/Kunpeng Desktop Board D920S10, BIOS 0.15 10/22/2019 [183047.695007] Workqueue: 0000:74:02.0_disco_q sas_discover_domain [183047.700999] pstate: 20c00009 (nzCv daif +PAN +UAO) [183047.705864] pc : prep_ata_v3_hw+0xf8/0x230 [hisi_sas_v3_hw] [183047.711510] lr : prep_ata_v3_hw+0xb0/0x230 [hisi_sas_v3_hw] [183047.717153] sp : ffff00000f28ba60 [183047.720541] x29: ffff00000f28ba60 x28: ffff8026852d7228 [183047.725925] x27: ffff8027dba3e0a8 x26: ffff8027c05fc200 [183047.731310] x25: 0000000000000000 x24: ffff8026bafa8dc0 [183047.736695] x23: ffff8027c05fc218 x22: ffff8026852d7228 [183047.742079] x21: ffff80007c2f2940 x20: ffff8027c05fc200 [183047.747464] x19: 0000000000f80800 x18: 0000000000000010 [183047.752848] x17: 0000000000000000 x16: 0000000000000000 [183047.758232] x15: ffff000089a5a4ff x14: 0000000000000005 [183047.763617] x13: ffff000009a5a50e x12: ffff8026bafa1e20 [183047.769001] x11: ffff0000087453b8 x10: ffff00000f28b870 [183047.774385] x9 : 0000000000000000 x8 : ffff80007e58f9b0 [183047.779770] x7 : 0000000000000000 x6 : 000000000000003f [183047.785154] x5 : 0000000000000040 x4 : ffffffffffffffe0 [183047.790538] x3 : 00000000000000f8 x2 : 0000000002000007 [183047.795922] x1 : 0000000000000008 x0 : 0000000000000000 [183047.801307] Call trace: [183047.803827] prep_ata_v3_hw+0xf8/0x230 [hisi_sas_v3_hw] [183047.809127] hisi_sas_task_prep+0x750/0x888 [hisi_sas_main] [183047.814773] hisi_sas_task_exec.isra.7+0x88/0x1f0 [hisi_sas_main] [183047.820939] hisi_sas_queue_command+0x28/0x38 [hisi_sas_main] [183047.826757] smp_execute_task_sg+0xec/0x218 [183047.831013] smp_execute_task+0x74/0xa0 [183047.834921] sas_discover_expander.part.7+0x9c/0x5f8 [183047.839959] sas_discover_root_expander+0x90/0x160 [183047.844822] sas_discover_domain+0x1b8/0x1e8 [183047.849164] process_one_work+0x1b4/0x3f8 [183047.853246] worker_thread+0x54/0x470 [183047.856981] kthread+0x134/0x138 [183047.860283] ret_from_fork+0x10/0x18 [183047.863931] Code: f9407a80 528000e2 39409281 72a04002 (b9405800) [183047.870097] kernel fault(0x1) notification starting on CPU 0 [183047.875828] kernel fault(0x1) notification finished on CPU 0 [183047.881559] Modules linked in: unibsp(OE) hns3(OE) hclge(OE) hnae3(OE) mem_drv(OE) hisi_sas_v3_hw(OE) hisi_sas_main(OE) [183047.892418] ---[ end trace 4cc26083fc11b783 ]--- [183047.897107] Kernel panic - not syncing: Fatal exception [183047.902403] kernel fault(0x5) notification starting on CPU 0 [183047.908134] kernel fault(0x5) notification finished on CPU 0 [183047.913865] SMP: stopping secondary CPUs [183047.917861] Kernel Offset: disabled [183047.921422] CPU features: 0x2,a2a00a38 [183047.925243] Memory Limit: none [183047.928372] kernel reboot(0x2) notification starting on CPU 0 [183047.934190] kernel reboot(0x2) notification finished on CPU 0 [183047.940008] ---[ end Kernel panic - not syncing: Fatal exception ]--- Fixes: 2908d778ab3e ("[SCSI] aic94xx: new driver") Link: https://lore.kernel.org/r/20191206011118.46909-1-yanaijie@huawei.com Reported-by: Gao Chuan Reviewed-by: John Garry Signed-off-by: Jason Yan Signed-off-by: Martin K. Petersen --- drivers/scsi/libsas/sas_discover.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/drivers/scsi/libsas/sas_discover.c b/drivers/scsi/libsas/sas_discover.c index f47b4b281b14a..d7302c2052f91 100644 --- a/drivers/scsi/libsas/sas_discover.c +++ b/drivers/scsi/libsas/sas_discover.c @@ -81,12 +81,21 @@ static int sas_get_port_device(struct asd_sas_port *port) else dev->dev_type = SAS_SATA_DEV; dev->tproto = SAS_PROTOCOL_SATA; - } else { + } else if (port->oob_mode == SAS_OOB_MODE) { struct sas_identify_frame *id = (struct sas_identify_frame *) dev->frame_rcvd; dev->dev_type = id->dev_type; dev->iproto = id->initiator_bits; dev->tproto = id->target_bits; + } else { + /* If the oob mode is OOB_NOT_CONNECTED, the port is + * disconnected due to race with PHY down. We cannot + * continue to discover this port + */ + sas_put_device(dev); + pr_warn("Port %016llx is disconnected when discovering\n", + SAS_ADDR(port->attached_sas_addr)); + return -ENODEV; } sas_init_dev(dev); -- GitLab From a2bdd0c904da12b223c8d7218e98138d4e6d9f4f Mon Sep 17 00:00:00 2001 From: Paul Menzel Date: Mon, 9 Dec 2019 16:06:53 +0100 Subject: [PATCH 0335/1437] scsi: smartpqi: Update attribute name to `driver_version` The file name in the documentation is currently incorrect, so fix it. Link: https://lore.kernel.org/r/fe264d62-0371-ea59-b66a-6d855290ce65@molgen.mpg.de Fixes: 6d90615f1346 ("scsi: smartpqi: add sysfs entries") Signed-off-by: Paul Menzel Signed-off-by: Martin K. Petersen --- Documentation/scsi/smartpqi.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/scsi/smartpqi.txt b/Documentation/scsi/smartpqi.txt index 201f80c7c0506..df129f55ace5e 100644 --- a/Documentation/scsi/smartpqi.txt +++ b/Documentation/scsi/smartpqi.txt @@ -29,7 +29,7 @@ smartpqi specific entries in /sys smartpqi host attributes: ------------------------- /sys/class/scsi_host/host*/rescan - /sys/class/scsi_host/host*/version + /sys/class/scsi_host/host*/driver_version The host rescan attribute is a write only attribute. Writing to this attribute will trigger the driver to scan for new, changed, or removed -- GitLab From 5480e299b5ae57956af01d4839c9fc88a465eeab Mon Sep 17 00:00:00 2001 From: Bart Van Assche Date: Mon, 9 Dec 2019 09:34:57 -0800 Subject: [PATCH 0336/1437] scsi: iscsi: Fix a potential deadlock in the timeout handler Some time ago the block layer was modified such that timeout handlers are called from thread context instead of interrupt context. Make it safe to run the iSCSI timeout handler in thread context. This patch fixes the following lockdep complaint: ================================ WARNING: inconsistent lock state 5.5.1-dbg+ #11 Not tainted -------------------------------- inconsistent {IN-SOFTIRQ-W} -> {SOFTIRQ-ON-W} usage. kworker/7:1H/206 [HC0[0]:SC0[0]:HE1:SE1] takes: ffff88802d9827e8 (&(&session->frwd_lock)->rlock){+.?.}, at: iscsi_eh_cmd_timed_out+0xa6/0x6d0 [libiscsi] {IN-SOFTIRQ-W} state was registered at: lock_acquire+0x106/0x240 _raw_spin_lock+0x38/0x50 iscsi_check_transport_timeouts+0x3e/0x210 [libiscsi] call_timer_fn+0x132/0x470 __run_timers.part.0+0x39f/0x5b0 run_timer_softirq+0x63/0xc0 __do_softirq+0x12d/0x5fd irq_exit+0xb3/0x110 smp_apic_timer_interrupt+0x131/0x3d0 apic_timer_interrupt+0xf/0x20 default_idle+0x31/0x230 arch_cpu_idle+0x13/0x20 default_idle_call+0x53/0x60 do_idle+0x38a/0x3f0 cpu_startup_entry+0x24/0x30 start_secondary+0x222/0x290 secondary_startup_64+0xa4/0xb0 irq event stamp: 1383705 hardirqs last enabled at (1383705): [] _raw_spin_unlock_irq+0x2c/0x50 hardirqs last disabled at (1383704): [] _raw_spin_lock_irq+0x18/0x50 softirqs last enabled at (1383690): [] iscsi_queuecommand+0x76a/0xa20 [libiscsi] softirqs last disabled at (1383682): [] iscsi_queuecommand+0x118/0xa20 [libiscsi] other info that might help us debug this: Possible unsafe locking scenario: CPU0 ---- lock(&(&session->frwd_lock)->rlock); lock(&(&session->frwd_lock)->rlock); *** DEADLOCK *** 2 locks held by kworker/7:1H/206: #0: ffff8880d57bf928 ((wq_completion)kblockd){+.+.}, at: process_one_work+0x472/0xab0 #1: ffff88802b9c7de8 ((work_completion)(&q->timeout_work)){+.+.}, at: process_one_work+0x476/0xab0 stack backtrace: CPU: 7 PID: 206 Comm: kworker/7:1H Not tainted 5.5.1-dbg+ #11 Hardware name: Bochs Bochs, BIOS Bochs 01/01/2011 Workqueue: kblockd blk_mq_timeout_work Call Trace: dump_stack+0xa5/0xe6 print_usage_bug.cold+0x232/0x23b mark_lock+0x8dc/0xa70 __lock_acquire+0xcea/0x2af0 lock_acquire+0x106/0x240 _raw_spin_lock+0x38/0x50 iscsi_eh_cmd_timed_out+0xa6/0x6d0 [libiscsi] scsi_times_out+0xf4/0x440 [scsi_mod] scsi_timeout+0x1d/0x20 [scsi_mod] blk_mq_check_expired+0x365/0x3a0 bt_iter+0xd6/0xf0 blk_mq_queue_tag_busy_iter+0x3de/0x650 blk_mq_timeout_work+0x1af/0x380 process_one_work+0x56d/0xab0 worker_thread+0x7a/0x5d0 kthread+0x1bc/0x210 ret_from_fork+0x24/0x30 Fixes: 287922eb0b18 ("block: defer timeouts to a workqueue") Cc: Christoph Hellwig Cc: Keith Busch Cc: Lee Duncan Cc: Chris Leech Cc: Link: https://lore.kernel.org/r/20191209173457.187370-1-bvanassche@acm.org Signed-off-by: Bart Van Assche Reviewed-by: Lee Duncan Signed-off-by: Martin K. Petersen --- drivers/scsi/libiscsi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/scsi/libiscsi.c b/drivers/scsi/libiscsi.c index ebd47c0cf9e93..70b99c0e2e678 100644 --- a/drivers/scsi/libiscsi.c +++ b/drivers/scsi/libiscsi.c @@ -1945,7 +1945,7 @@ enum blk_eh_timer_return iscsi_eh_cmd_timed_out(struct scsi_cmnd *sc) ISCSI_DBG_EH(session, "scsi cmd %p timedout\n", sc); - spin_lock(&session->frwd_lock); + spin_lock_bh(&session->frwd_lock); task = (struct iscsi_task *)sc->SCp.ptr; if (!task) { /* @@ -2072,7 +2072,7 @@ enum blk_eh_timer_return iscsi_eh_cmd_timed_out(struct scsi_cmnd *sc) done: if (task) task->last_timeout = jiffies; - spin_unlock(&session->frwd_lock); + spin_unlock_bh(&session->frwd_lock); ISCSI_DBG_EH(session, "return %s\n", rc == BLK_EH_RESET_TIMER ? "timer reset" : "shutdown or nh"); return rc; -- GitLab From bba340c79bfe3644829db5c852fdfa9e33837d6d Mon Sep 17 00:00:00 2001 From: Bo Wu Date: Wed, 20 Nov 2019 13:26:17 +0000 Subject: [PATCH 0337/1437] scsi: iscsi: Avoid potential deadlock in iscsi_if_rx func In iscsi_if_rx func, after receiving one request through iscsi_if_recv_msg func, iscsi_if_send_reply will be called to try to reply to the request in a do-while loop. If the iscsi_if_send_reply function keeps returning -EAGAIN, a deadlock will occur. For example, a client only send msg without calling recvmsg func, then it will result in the watchdog soft lockup. The details are given as follows: sock_fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ISCSI); retval = bind(sock_fd, (struct sock addr*) & src_addr, sizeof(src_addr); while (1) { state_msg = sendmsg(sock_fd, &msg, 0); //Note: recvmsg(sock_fd, &msg, 0) is not processed here. } close(sock_fd); watchdog: BUG: soft lockup - CPU#7 stuck for 22s! [netlink_test:253305] Sample time: 4000897528 ns(HZ: 250) Sample stat: curr: user: 675503481560, nice: 321724050, sys: 448689506750, idle: 4654054240530, iowait: 40885550700, irq: 14161174020, softirq: 8104324140, st: 0 deta: user: 0, nice: 0, sys: 3998210100, idle: 0, iowait: 0, irq: 1547170, softirq: 242870, st: 0 Sample softirq: TIMER: 992 SCHED: 8 Sample irqstat: irq 2: delta 1003, curr: 3103802, arch_timer CPU: 7 PID: 253305 Comm: netlink_test Kdump: loaded Tainted: G OE Hardware name: QEMU KVM Virtual Machine, BIOS 0.0.0 02/06/2015 pstate: 40400005 (nZcv daif +PAN -UAO) pc : __alloc_skb+0x104/0x1b0 lr : __alloc_skb+0x9c/0x1b0 sp : ffff000033603a30 x29: ffff000033603a30 x28: 00000000000002dd x27: ffff800b34ced810 x26: ffff800ba7569f00 x25: 00000000ffffffff x24: 0000000000000000 x23: ffff800f7c43f600 x22: 0000000000480020 x21: ffff0000091d9000 x20: ffff800b34eff200 x19: ffff800ba7569f00 x18: 0000000000000000 x17: 0000000000000000 x16: 0000000000000000 x15: 0000000000000000 x14: 0001000101000100 x13: 0000000101010000 x12: 0101000001010100 x11: 0001010101010001 x10: 00000000000002dd x9 : ffff000033603d58 x8 : ffff800b34eff400 x7 : ffff800ba7569200 x6 : ffff800b34eff400 x5 : 0000000000000000 x4 : 00000000ffffffff x3 : 0000000000000000 x2 : 0000000000000001 x1 : ffff800b34eff2c0 x0 : 0000000000000300 Call trace: __alloc_skb+0x104/0x1b0 iscsi_if_rx+0x144/0x12bc [scsi_transport_iscsi] netlink_unicast+0x1e0/0x258 netlink_sendmsg+0x310/0x378 sock_sendmsg+0x4c/0x70 sock_write_iter+0x90/0xf0 __vfs_write+0x11c/0x190 vfs_write+0xac/0x1c0 ksys_write+0x6c/0xd8 __arm64_sys_write+0x24/0x30 el0_svc_common+0x78/0x130 el0_svc_handler+0x38/0x78 el0_svc+0x8/0xc Link: https://lore.kernel.org/r/EDBAAA0BBBA2AC4E9C8B6B81DEEE1D6915E3D4D2@dggeml505-mbx.china.huawei.com Signed-off-by: Bo Wu Reviewed-by: Zhiqiang Liu Reviewed-by: Lee Duncan Signed-off-by: Martin K. Petersen --- drivers/scsi/scsi_transport_iscsi.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/scsi/scsi_transport_iscsi.c b/drivers/scsi/scsi_transport_iscsi.c index 417b868d8735e..ed8d9709b9b96 100644 --- a/drivers/scsi/scsi_transport_iscsi.c +++ b/drivers/scsi/scsi_transport_iscsi.c @@ -24,6 +24,8 @@ #define ISCSI_TRANSPORT_VERSION "2.0-870" +#define ISCSI_SEND_MAX_ALLOWED 10 + #define CREATE_TRACE_POINTS #include @@ -3682,6 +3684,7 @@ iscsi_if_rx(struct sk_buff *skb) struct nlmsghdr *nlh; struct iscsi_uevent *ev; uint32_t group; + int retries = ISCSI_SEND_MAX_ALLOWED; nlh = nlmsg_hdr(skb); if (nlh->nlmsg_len < sizeof(*nlh) + sizeof(*ev) || @@ -3712,6 +3715,10 @@ iscsi_if_rx(struct sk_buff *skb) break; err = iscsi_if_send_reply(portid, nlh->nlmsg_type, ev, sizeof(*ev)); + if (err == -EAGAIN && --retries < 0) { + printk(KERN_WARNING "Send reply failed, error %d\n", err); + break; + } } while (err < 0 && err != -ECONNREFUSED && err != -ESRCH); skb_pull(skb, rlen); } -- GitLab From 281393894af9cc3f9483204475014e89d728987c Mon Sep 17 00:00:00 2001 From: Steve French Date: Mon, 9 Dec 2019 19:47:10 -0600 Subject: [PATCH 0338/1437] smb3: fix refcount underflow warning on unmount when no directory leases Fix refcount underflow warning when unmounting to servers which didn't grant directory leases. [ 301.680095] refcount_t: underflow; use-after-free. [ 301.680192] WARNING: CPU: 1 PID: 3569 at lib/refcount.c:28 refcount_warn_saturate+0xb4/0xf3 ... [ 301.682139] Call Trace: [ 301.682240] close_shroot+0x97/0xda [cifs] [ 301.682351] SMB2_tdis+0x7c/0x176 [cifs] [ 301.682456] ? _get_xid+0x58/0x91 [cifs] [ 301.682563] cifs_put_tcon.part.0+0x99/0x202 [cifs] [ 301.682637] ? ida_free+0x99/0x10a [ 301.682727] ? cifs_umount+0x3d/0x9d [cifs] [ 301.682829] cifs_put_tlink+0x3a/0x50 [cifs] [ 301.682929] cifs_umount+0x44/0x9d [cifs] Fixes: 72e73c78c446 ("cifs: close the shared root handle on tree disconnect") Signed-off-by: Steve French Acked-by: Ronnie Sahlberg Reviewed-by: Aurelien Aptel Reviewed-by: Pavel Shilovsky Reported-and-tested-by: Arthur Marsh --- fs/cifs/smb2pdu.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c index 0ab6b1200288e..d2658f51ff608 100644 --- a/fs/cifs/smb2pdu.c +++ b/fs/cifs/smb2pdu.c @@ -1847,7 +1847,8 @@ SMB2_tdis(const unsigned int xid, struct cifs_tcon *tcon) if ((tcon->need_reconnect) || (tcon->ses->need_reconnect)) return 0; - close_shroot(&tcon->crfid); + if (tcon->crfid.is_valid) + close_shroot(&tcon->crfid); rc = smb2_plain_req_init(SMB2_TREE_DISCONNECT, tcon, (void **) &req, &total_len); -- GitLab From 868afbaca1e2a7923e48b5e8c07be34660525db5 Mon Sep 17 00:00:00 2001 From: Stephan Gerhold Date: Mon, 9 Dec 2019 19:53:43 +0100 Subject: [PATCH 0339/1437] NFC: nxp-nci: Fix probing without ACPI devm_acpi_dev_add_driver_gpios() returns -ENXIO if CONFIG_ACPI is disabled (e.g. on device tree platforms). In this case, nxp-nci will silently fail to probe. The other NFC drivers only log a debug message if devm_acpi_dev_add_driver_gpios() fails. Do the same in nxp-nci to fix this problem. Fixes: ad0acfd69add ("NFC: nxp-nci: Get rid of code duplication in ->probe()") Cc: Andy Shevchenko Signed-off-by: Stephan Gerhold Acked-by: Andy Shevchenko Signed-off-by: David S. Miller --- drivers/nfc/nxp-nci/i2c.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/nfc/nxp-nci/i2c.c b/drivers/nfc/nxp-nci/i2c.c index 4d1909aecd6c4..9f60e4dc5a908 100644 --- a/drivers/nfc/nxp-nci/i2c.c +++ b/drivers/nfc/nxp-nci/i2c.c @@ -278,7 +278,7 @@ static int nxp_nci_i2c_probe(struct i2c_client *client, r = devm_acpi_dev_add_driver_gpios(dev, acpi_nxp_nci_gpios); if (r) - return r; + dev_dbg(dev, "Unable to add GPIO mapping table\n"); phy->gpiod_en = devm_gpiod_get(dev, "enable", GPIOD_OUT_LOW); if (IS_ERR(phy->gpiod_en)) { -- GitLab From cc90bc68422318eb8e75b15cd74bc8d538a7df29 Mon Sep 17 00:00:00 2001 From: Andreas Gruenbacher Date: Mon, 9 Dec 2019 20:11:14 +0100 Subject: [PATCH 0340/1437] block: fix "check bi_size overflow before merge" This partially reverts commit e3a5d8e386c3fb973fa75f2403622a8f3640ec06. Commit e3a5d8e386c3 ("check bi_size overflow before merge") adds a bio_full check to __bio_try_merge_page. This will cause __bio_try_merge_page to fail when the last bi_io_vec has been reached. Instead, what we want here is only the bi_size overflow check. Fixes: e3a5d8e386c3 ("block: check bi_size overflow before merge") Cc: stable@vger.kernel.org # v5.4+ Reviewed-by: Ming Lei Signed-off-by: Andreas Gruenbacher Signed-off-by: Jens Axboe --- block/bio.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/block/bio.c b/block/bio.c index 9d54aa37ce6c7..a5d75f6bf4c7e 100644 --- a/block/bio.c +++ b/block/bio.c @@ -754,10 +754,12 @@ bool __bio_try_merge_page(struct bio *bio, struct page *page, if (WARN_ON_ONCE(bio_flagged(bio, BIO_CLONED))) return false; - if (bio->bi_vcnt > 0 && !bio_full(bio, len)) { + if (bio->bi_vcnt > 0) { struct bio_vec *bv = &bio->bi_io_vec[bio->bi_vcnt - 1]; if (page_is_mergeable(bv, page, len, off, same_page)) { + if (bio->bi_iter.bi_size > UINT_MAX - len) + return false; bv->bv_len += len; bio->bi_iter.bi_size += len; return true; -- GitLab From db5cce1afc8d2475d2c1c37c2a8267dd0e151526 Mon Sep 17 00:00:00 2001 From: Anders Kaseorg Date: Mon, 2 Dec 2019 17:09:20 -0500 Subject: [PATCH 0341/1437] Revert "iwlwifi: assign directly to iwl_trans->cfg in QuZ detection" This reverts commit 968dcfb4905245dc64d65312c0d17692fa087b99. Both that commit and commit 809805a820c6445f7a701ded24fdc6bbc841d1e4 attempted to fix the same bug (dead assignments to the local variable cfg), but they did so in incompatible ways. When they were both merged, independently of each other, the combination actually caused the bug to reappear, leading to a firmware crash on boot for some cards. https://bugzilla.kernel.org/show_bug.cgi?id=205719 Signed-off-by: Anders Kaseorg Acked-by: Luca Coelho Signed-off-by: Kalle Valo --- drivers/net/wireless/intel/iwlwifi/pcie/drv.c | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/drv.c b/drivers/net/wireless/intel/iwlwifi/pcie/drv.c index 040cec17d3ad6..b0b7eca1754ed 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/drv.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/drv.c @@ -1111,18 +1111,18 @@ static int iwl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) /* same thing for QuZ... */ if (iwl_trans->hw_rev == CSR_HW_REV_TYPE_QUZ) { - if (iwl_trans->cfg == &iwl_ax101_cfg_qu_hr) - iwl_trans->cfg = &iwl_ax101_cfg_quz_hr; - else if (iwl_trans->cfg == &iwl_ax201_cfg_qu_hr) - iwl_trans->cfg = &iwl_ax201_cfg_quz_hr; - else if (iwl_trans->cfg == &iwl9461_2ac_cfg_qu_b0_jf_b0) - iwl_trans->cfg = &iwl9461_2ac_cfg_quz_a0_jf_b0_soc; - else if (iwl_trans->cfg == &iwl9462_2ac_cfg_qu_b0_jf_b0) - iwl_trans->cfg = &iwl9462_2ac_cfg_quz_a0_jf_b0_soc; - else if (iwl_trans->cfg == &iwl9560_2ac_cfg_qu_b0_jf_b0) - iwl_trans->cfg = &iwl9560_2ac_cfg_quz_a0_jf_b0_soc; - else if (iwl_trans->cfg == &iwl9560_2ac_160_cfg_qu_b0_jf_b0) - iwl_trans->cfg = &iwl9560_2ac_160_cfg_quz_a0_jf_b0_soc; + if (cfg == &iwl_ax101_cfg_qu_hr) + cfg = &iwl_ax101_cfg_quz_hr; + else if (cfg == &iwl_ax201_cfg_qu_hr) + cfg = &iwl_ax201_cfg_quz_hr; + else if (cfg == &iwl9461_2ac_cfg_qu_b0_jf_b0) + cfg = &iwl9461_2ac_cfg_quz_a0_jf_b0_soc; + else if (cfg == &iwl9462_2ac_cfg_qu_b0_jf_b0) + cfg = &iwl9462_2ac_cfg_quz_a0_jf_b0_soc; + else if (cfg == &iwl9560_2ac_cfg_qu_b0_jf_b0) + cfg = &iwl9560_2ac_cfg_quz_a0_jf_b0_soc; + else if (cfg == &iwl9560_2ac_160_cfg_qu_b0_jf_b0) + cfg = &iwl9560_2ac_160_cfg_quz_a0_jf_b0_soc; } #endif -- GitLab From 0df36b90c47d93295b7e393da2d961b2f3b6cde4 Mon Sep 17 00:00:00 2001 From: Luca Coelho Date: Thu, 5 Dec 2019 09:03:54 +0200 Subject: [PATCH 0342/1437] iwlwifi: pcie: move power gating workaround earlier in the flow We need to reset the NIC after setting the bits to enable power gating and that cannot be done too late in the flow otherwise it cleans other registers and things that were already configured, causing initialization to fail. In order to fix this, move the function to the common code in trans.c so it can be called directly from there at an earlier point, just after the reset we already do during initialization. Fixes: 9a47cb988338 ("iwlwifi: pcie: add workaround for power gating in integrated 22000") Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=205719 Cc: stable@ver.kernel.org # 5.4+ Reported-by: Anders Kaseorg Signed-off-by: Luca Coelho Signed-off-by: Kalle Valo --- .../wireless/intel/iwlwifi/pcie/trans-gen2.c | 25 ---------------- .../net/wireless/intel/iwlwifi/pcie/trans.c | 30 +++++++++++++++++++ 2 files changed, 30 insertions(+), 25 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/trans-gen2.c b/drivers/net/wireless/intel/iwlwifi/pcie/trans-gen2.c index 0252716c0b247..0d8b2a8ffa5d8 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/trans-gen2.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/trans-gen2.c @@ -57,24 +57,6 @@ #include "internal.h" #include "fw/dbg.h" -static int iwl_pcie_gen2_force_power_gating(struct iwl_trans *trans) -{ - iwl_set_bits_prph(trans, HPM_HIPM_GEN_CFG, - HPM_HIPM_GEN_CFG_CR_FORCE_ACTIVE); - udelay(20); - iwl_set_bits_prph(trans, HPM_HIPM_GEN_CFG, - HPM_HIPM_GEN_CFG_CR_PG_EN | - HPM_HIPM_GEN_CFG_CR_SLP_EN); - udelay(20); - iwl_clear_bits_prph(trans, HPM_HIPM_GEN_CFG, - HPM_HIPM_GEN_CFG_CR_FORCE_ACTIVE); - - iwl_trans_sw_reset(trans); - iwl_clear_bit(trans, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_INIT_DONE); - - return 0; -} - /* * Start up NIC's basic functionality after it has been reset * (e.g. after platform boot, or shutdown via iwl_pcie_apm_stop()) @@ -110,13 +92,6 @@ int iwl_pcie_gen2_apm_init(struct iwl_trans *trans) iwl_pcie_apm_config(trans); - if (trans->trans_cfg->device_family == IWL_DEVICE_FAMILY_22000 && - trans->cfg->integrated) { - ret = iwl_pcie_gen2_force_power_gating(trans); - if (ret) - return ret; - } - ret = iwl_finish_nic_init(trans, trans->trans_cfg); if (ret) return ret; diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c index af9bc6b645427..a0677131634d5 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c @@ -1783,6 +1783,29 @@ static int iwl_trans_pcie_clear_persistence_bit(struct iwl_trans *trans) return 0; } +static int iwl_pcie_gen2_force_power_gating(struct iwl_trans *trans) +{ + int ret; + + ret = iwl_finish_nic_init(trans, trans->trans_cfg); + if (ret < 0) + return ret; + + iwl_set_bits_prph(trans, HPM_HIPM_GEN_CFG, + HPM_HIPM_GEN_CFG_CR_FORCE_ACTIVE); + udelay(20); + iwl_set_bits_prph(trans, HPM_HIPM_GEN_CFG, + HPM_HIPM_GEN_CFG_CR_PG_EN | + HPM_HIPM_GEN_CFG_CR_SLP_EN); + udelay(20); + iwl_clear_bits_prph(trans, HPM_HIPM_GEN_CFG, + HPM_HIPM_GEN_CFG_CR_FORCE_ACTIVE); + + iwl_trans_pcie_sw_reset(trans); + + return 0; +} + static int _iwl_trans_pcie_start_hw(struct iwl_trans *trans) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); @@ -1802,6 +1825,13 @@ static int _iwl_trans_pcie_start_hw(struct iwl_trans *trans) iwl_trans_pcie_sw_reset(trans); + if (trans->trans_cfg->device_family == IWL_DEVICE_FAMILY_22000 && + trans->cfg->integrated) { + err = iwl_pcie_gen2_force_power_gating(trans); + if (err) + return err; + } + err = iwl_pcie_apm_init(trans); if (err) return err; -- GitLab From 7d42b7961b9198efc75ee5192eab7d03e691d692 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Mon, 2 Dec 2019 06:18:36 -0800 Subject: [PATCH 0343/1437] staging/octeon: Mark Ethernet driver as BROKEN The code doesn't compile due to incompatible pointer errors such as drivers/staging/octeon/ethernet-tx.c:649:50: error: passing argument 1 of 'cvmx_wqe_get_grp' from incompatible pointer type This is due to mixing, for example, cvmx_wqe_t with 'struct cvmx_wqe'. Unfortunately, one can not just revert the primary offending commit, as doing so results in secondary errors. This is made worse by the fact that the "removed" typedefs still exist and are used widely outside the staging directory, making the entire set of "remove typedef" changes pointless and wrong. Reflect reality and mark the driver as BROKEN. Fixes: ef1fe6b7369a ("staging: octeon: remove typedef declaration for cvmx_wqe") Fixes: 73aef0c9d2c6 ("staging: octeon: remove typedef declaration for cvmx_helper_link_info") Cc: Wambui Karuga Cc: Julia Lawall Signed-off-by: Guenter Roeck Link: https://lore.kernel.org/r/20191202141836.9363-1-linux@roeck-us.net Signed-off-by: Greg Kroah-Hartman --- drivers/staging/octeon/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/staging/octeon/Kconfig b/drivers/staging/octeon/Kconfig index 5319909eb2f6e..e7f4ddcc13619 100644 --- a/drivers/staging/octeon/Kconfig +++ b/drivers/staging/octeon/Kconfig @@ -3,6 +3,7 @@ config OCTEON_ETHERNET tristate "Cavium Networks Octeon Ethernet support" depends on CAVIUM_OCTEON_SOC || COMPILE_TEST depends on NETDEVICES + depends on BROKEN select PHYLIB select MDIO_OCTEON help -- GitLab From a58d37bce0d21cf7fbd589384c619e465ef2f927 Mon Sep 17 00:00:00 2001 From: Frederic Barrat Date: Mon, 24 Jun 2019 16:41:48 +0200 Subject: [PATCH 0344/1437] ocxl: Fix concurrent AFU open and device removal If an ocxl device is unbound through sysfs at the same time its AFU is being opened by a user process, the open code may dereference freed stuctures, which can lead to kernel oops messages. You'd have to hit a tiny time window, but it's possible. It's fairly easy to test by making the time window bigger artificially. Fix it with a combination of 2 changes: - when an AFU device is found in the IDR by looking for the device minor number, we should hold a reference on the device until after the context is allocated. A reference on the AFU structure is kept when the context is allocated, so we can release the reference on the device after the context allocation. - with the fix above, there's still another even tinier window, between the time the AFU device is found in the IDR and the reference on the device is taken. We can fix this one by removing the IDR entry earlier, when the device setup is removed, instead of waiting for the 'release' device callback. With proper locking around the IDR. Fixes: 75ca758adbaf ("ocxl: Create a clear delineation between ocxl backend & frontend") Cc: stable@vger.kernel.org # v5.2+ Signed-off-by: Frederic Barrat Reviewed-by: Greg Kurz Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20190624144148.32022-1-fbarrat@linux.ibm.com --- drivers/misc/ocxl/file.c | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/drivers/misc/ocxl/file.c b/drivers/misc/ocxl/file.c index 2870c25da166f..4d1b44de14921 100644 --- a/drivers/misc/ocxl/file.c +++ b/drivers/misc/ocxl/file.c @@ -18,18 +18,15 @@ static struct class *ocxl_class; static struct mutex minors_idr_lock; static struct idr minors_idr; -static struct ocxl_file_info *find_file_info(dev_t devno) +static struct ocxl_file_info *find_and_get_file_info(dev_t devno) { struct ocxl_file_info *info; - /* - * We don't declare an RCU critical section here, as our AFU - * is protected by a reference counter on the device. By the time the - * info reference is removed from the idr, the ref count of - * the device is already at 0, so no user API will access that AFU and - * this function can't return it. - */ + mutex_lock(&minors_idr_lock); info = idr_find(&minors_idr, MINOR(devno)); + if (info) + get_device(&info->dev); + mutex_unlock(&minors_idr_lock); return info; } @@ -58,14 +55,16 @@ static int afu_open(struct inode *inode, struct file *file) pr_debug("%s for device %x\n", __func__, inode->i_rdev); - info = find_file_info(inode->i_rdev); + info = find_and_get_file_info(inode->i_rdev); if (!info) return -ENODEV; rc = ocxl_context_alloc(&ctx, info->afu, inode->i_mapping); - if (rc) + if (rc) { + put_device(&info->dev); return rc; - + } + put_device(&info->dev); file->private_data = ctx; return 0; } @@ -487,7 +486,6 @@ static void info_release(struct device *dev) { struct ocxl_file_info *info = container_of(dev, struct ocxl_file_info, dev); - free_minor(info); ocxl_afu_put(info->afu); kfree(info); } @@ -577,6 +575,7 @@ void ocxl_file_unregister_afu(struct ocxl_afu *afu) ocxl_file_make_invisible(info); ocxl_sysfs_unregister_afu(info); + free_minor(info); device_unregister(&info->dev); } -- GitLab From 1184fd9966702a5a1cea73bdb6fe646b961c387c Mon Sep 17 00:00:00 2001 From: Andrea Righi Date: Thu, 5 Dec 2019 16:29:13 +0100 Subject: [PATCH 0345/1437] staging: exfat: properly support discard in clr_alloc_bitmap() Currently the discard code in clr_alloc_bitmap() is just dead code. Move code around so that the discard operation is properly attempted when enabled. Signed-off-by: Andrea Righi Link: https://lore.kernel.org/r/20191205152913.GJ3276@xps-13 Signed-off-by: Greg Kroah-Hartman --- drivers/staging/exfat/exfat_core.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/staging/exfat/exfat_core.c b/drivers/staging/exfat/exfat_core.c index d2d3447083c7b..463eb89c676a0 100644 --- a/drivers/staging/exfat/exfat_core.c +++ b/drivers/staging/exfat/exfat_core.c @@ -192,8 +192,6 @@ static s32 clr_alloc_bitmap(struct super_block *sb, u32 clu) exfat_bitmap_clear((u8 *)p_fs->vol_amap[i]->b_data, b); - return sector_write(sb, sector, p_fs->vol_amap[i], 0); - #ifdef CONFIG_EXFAT_DISCARD if (opts->discard) { ret = sb_issue_discard(sb, START_SECTOR(clu), @@ -202,9 +200,13 @@ static s32 clr_alloc_bitmap(struct super_block *sb, u32 clu) if (ret == -EOPNOTSUPP) { pr_warn("discard not supported by device, disabling"); opts->discard = 0; + } else { + return ret; } } #endif /* CONFIG_EXFAT_DISCARD */ + + return sector_write(sb, sector, p_fs->vol_amap[i], 0); } static u32 test_alloc_bitmap(struct super_block *sb, u32 clu) -- GitLab From 453a4b6d8e1bdff2b8e1f56f4b8a8ef6d36b0e77 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Thu, 5 Dec 2019 00:32:30 +0100 Subject: [PATCH 0346/1437] staging: fbtft: Do not hardcode SPI CS polarity inversion The current use of the mode flag SPI_CS_HIGH is fragile: it overwrites anything already assigned by the SPI core. Assign ^= SPI_CS_HIGH since we might be active high already, and that is usually the case with GPIOs used for chip select, even if they are in practice active low. Add a comment clarifying why ^= SPI_CS_HIGH is the right choice here. Reported-by: Mark Brown Signed-off-by: Linus Walleij Link: https://lore.kernel.org/r/20191204233230.22309-1-linus.walleij@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/staging/fbtft/fb_uc1611.c | 12 +++++++++--- drivers/staging/fbtft/fb_watterott.c | 13 ++++++++++--- 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/drivers/staging/fbtft/fb_uc1611.c b/drivers/staging/fbtft/fb_uc1611.c index e763205e9e4ff..f61e373c75e96 100644 --- a/drivers/staging/fbtft/fb_uc1611.c +++ b/drivers/staging/fbtft/fb_uc1611.c @@ -63,11 +63,17 @@ static int init_display(struct fbtft_par *par) { int ret; - /* Set CS active high */ - par->spi->mode |= SPI_CS_HIGH; + /* + * Set CS active inverse polarity: just setting SPI_CS_HIGH does not + * work with GPIO based chip selects that are logically active high + * but inverted inside the GPIO library, so enforce inverted + * semantics. + */ + par->spi->mode ^= SPI_CS_HIGH; ret = spi_setup(par->spi); if (ret) { - dev_err(par->info->device, "Could not set SPI_CS_HIGH\n"); + dev_err(par->info->device, + "Could not set inverse CS polarity\n"); return ret; } diff --git a/drivers/staging/fbtft/fb_watterott.c b/drivers/staging/fbtft/fb_watterott.c index 27cc8eabcbe9d..76b25df376b8f 100644 --- a/drivers/staging/fbtft/fb_watterott.c +++ b/drivers/staging/fbtft/fb_watterott.c @@ -150,10 +150,17 @@ static int init_display(struct fbtft_par *par) /* enable SPI interface by having CS and MOSI low during reset */ save_mode = par->spi->mode; - par->spi->mode |= SPI_CS_HIGH; - ret = spi_setup(par->spi); /* set CS inactive low */ + /* + * Set CS active inverse polarity: just setting SPI_CS_HIGH does not + * work with GPIO based chip selects that are logically active high + * but inverted inside the GPIO library, so enforce inverted + * semantics. + */ + par->spi->mode ^= SPI_CS_HIGH; + ret = spi_setup(par->spi); if (ret) { - dev_err(par->info->device, "Could not set SPI_CS_HIGH\n"); + dev_err(par->info->device, + "Could not set inverse CS polarity\n"); return ret; } write_reg(par, 0x00); /* make sure mode is set */ -- GitLab From 2f4d984b9544e03ef9435a750f4cc7de1df61051 Mon Sep 17 00:00:00 2001 From: YueHaibing Date: Wed, 13 Nov 2019 10:13:06 +0800 Subject: [PATCH 0347/1437] staging: hp100: Fix build error without ETHERNET It should depends on ETHERNET, otherwise building fails: drivers/staging/hp/hp100.o: In function `hp100_pci_remove': hp100.c:(.text+0x165): undefined reference to `unregister_netdev' hp100.c:(.text+0x214): undefined reference to `free_netdev' Fixes: 52340b82cf1a ("hp100: Move 100BaseVG AnyLAN driver to staging") Signed-off-by: YueHaibing Acked-by: Randy Dunlap # build-tested Link: https://lore.kernel.org/r/20191113021306.35464-1-yuehaibing@huawei.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/hp/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/staging/hp/Kconfig b/drivers/staging/hp/Kconfig index fb395cfe6b927..f20ab21a6b2ad 100644 --- a/drivers/staging/hp/Kconfig +++ b/drivers/staging/hp/Kconfig @@ -6,6 +6,7 @@ config NET_VENDOR_HP bool "HP devices" default y + depends on ETHERNET depends on ISA || EISA || PCI ---help--- If you have a network (Ethernet) card belonging to this class, say Y. -- GitLab From 2740bd3351cd5a4351f458aabaa1c9b77de3867b Mon Sep 17 00:00:00 2001 From: Kay Friedrich Date: Wed, 27 Nov 2019 12:24:57 +0100 Subject: [PATCH 0348/1437] staging/wlan-ng: add CRC32 dependency in Kconfig wlan-ng uses the function crc32_le, but CRC32 wasn't a dependency of wlan-ng Co-developed-by: Michael Kupfer Signed-off-by: Michael Kupfer Signed-off-by: Kay Friedrich Link: https://lore.kernel.org/r/20191127112457.2301-1-kay.friedrich@fau.de Signed-off-by: Greg Kroah-Hartman --- drivers/staging/wlan-ng/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/staging/wlan-ng/Kconfig b/drivers/staging/wlan-ng/Kconfig index ac136663fa8e5..082c16a31616e 100644 --- a/drivers/staging/wlan-ng/Kconfig +++ b/drivers/staging/wlan-ng/Kconfig @@ -4,6 +4,7 @@ config PRISM2_USB depends on WLAN && USB && CFG80211 select WIRELESS_EXT select WEXT_PRIV + select CRC32 help This is the wlan-ng prism 2.5/3 USB driver for a wide range of old USB wireless devices. -- GitLab From 1af73a25e6e7d9f2f1e2a14259cc9ffce6d8f6d4 Mon Sep 17 00:00:00 2001 From: Brendan Higgins Date: Wed, 4 Dec 2019 15:45:22 -0800 Subject: [PATCH 0349/1437] staging: exfat: fix multiple definition error of `rename_file' `rename_file' was exported but not properly namespaced causing a multiple definition error because `rename_file' is already defined in fs/hostfs/hostfs_user.c: ld: drivers/staging/exfat/exfat_core.o: in function `rename_file': drivers/staging/exfat/exfat_core.c:2327: multiple definition of `rename_file'; fs/hostfs/hostfs_user.o:fs/hostfs/hostfs_user.c:350: first defined here make: *** [Makefile:1077: vmlinux] Error 1 This error can be reproduced on ARCH=um by selecting: CONFIG_EXFAT_FS=y CONFIG_HOSTFS=y Add a namespace prefix exfat_* to fix this error. Reported-by: Brendan Higgins Signed-off-by: Brendan Higgins Cc: stable Cc: Valdis Kletnieks Tested-by: David Gow Reviewed-by: David Gow Link: https://lore.kernel.org/r/20191204234522.42855-1-brendanhiggins@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/exfat/exfat.h | 4 ++-- drivers/staging/exfat/exfat_core.c | 4 ++-- drivers/staging/exfat/exfat_super.c | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/staging/exfat/exfat.h b/drivers/staging/exfat/exfat.h index 2aac1e000977e..51c665a924b76 100644 --- a/drivers/staging/exfat/exfat.h +++ b/drivers/staging/exfat/exfat.h @@ -805,8 +805,8 @@ s32 create_dir(struct inode *inode, struct chain_t *p_dir, s32 create_file(struct inode *inode, struct chain_t *p_dir, struct uni_name_t *p_uniname, u8 mode, struct file_id_t *fid); void remove_file(struct inode *inode, struct chain_t *p_dir, s32 entry); -s32 rename_file(struct inode *inode, struct chain_t *p_dir, s32 old_entry, - struct uni_name_t *p_uniname, struct file_id_t *fid); +s32 exfat_rename_file(struct inode *inode, struct chain_t *p_dir, s32 old_entry, + struct uni_name_t *p_uniname, struct file_id_t *fid); s32 move_file(struct inode *inode, struct chain_t *p_olddir, s32 oldentry, struct chain_t *p_newdir, struct uni_name_t *p_uniname, struct file_id_t *fid); diff --git a/drivers/staging/exfat/exfat_core.c b/drivers/staging/exfat/exfat_core.c index 463eb89c676a0..794000e7bc6fa 100644 --- a/drivers/staging/exfat/exfat_core.c +++ b/drivers/staging/exfat/exfat_core.c @@ -2324,8 +2324,8 @@ void remove_file(struct inode *inode, struct chain_t *p_dir, s32 entry) fs_func->delete_dir_entry(sb, p_dir, entry, 0, num_entries); } -s32 rename_file(struct inode *inode, struct chain_t *p_dir, s32 oldentry, - struct uni_name_t *p_uniname, struct file_id_t *fid) +s32 exfat_rename_file(struct inode *inode, struct chain_t *p_dir, s32 oldentry, + struct uni_name_t *p_uniname, struct file_id_t *fid) { s32 ret, newentry = -1, num_old_entries, num_new_entries; sector_t sector_old, sector_new; diff --git a/drivers/staging/exfat/exfat_super.c b/drivers/staging/exfat/exfat_super.c index 6e481908c59f6..9f91853b189b0 100644 --- a/drivers/staging/exfat/exfat_super.c +++ b/drivers/staging/exfat/exfat_super.c @@ -1262,8 +1262,8 @@ static int ffsMoveFile(struct inode *old_parent_inode, struct file_id_t *fid, fs_set_vol_flags(sb, VOL_DIRTY); if (olddir.dir == newdir.dir) - ret = rename_file(new_parent_inode, &olddir, dentry, &uni_name, - fid); + ret = exfat_rename_file(new_parent_inode, &olddir, dentry, + &uni_name, fid); else ret = move_file(new_parent_inode, &olddir, dentry, &newdir, &uni_name, fid); -- GitLab From d2cdb20507fe2079a146459f9718b45d78cbbe61 Mon Sep 17 00:00:00 2001 From: Marcelo Diop-Gonzalez Date: Tue, 3 Dec 2019 10:39:21 -0500 Subject: [PATCH 0350/1437] staging: vchiq: call unregister_chrdev_region() when driver registration fails This undoes the previous call to alloc_chrdev_region() on failure, and is probably what was meant originally given the label name. Signed-off-by: Marcelo Diop-Gonzalez Cc: stable Fixes: 187ac53e590c ("staging: vchiq_arm: rework probe and init functions") Reviewed-by: Dan Carpenter Reviewed-by: Nicolas Saenz Julienne Link: https://lore.kernel.org/r/20191203153921.70540-1-marcgonzalez@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c index 02148a24818a6..4458c1e60fa31 100644 --- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c +++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c @@ -3309,7 +3309,7 @@ static int __init vchiq_driver_init(void) return 0; region_unregister: - platform_driver_unregister(&vchiq_driver); + unregister_chrdev_region(vchiq_devid, 1); class_destroy: class_destroy(vchiq_class); -- GitLab From 53f35a39c3860baac1e5ca80bf052751cfb24a99 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 2 Dec 2019 09:56:08 +0100 Subject: [PATCH 0351/1437] staging: gigaset: fix general protection fault on probe Fix a general protection fault when accessing the endpoint descriptors which could be triggered by a malicious device due to missing sanity checks on the number of endpoints. Reported-by: syzbot+35b1c403a14f5c89eba7@syzkaller.appspotmail.com Fixes: 07dc1f9f2f80 ("[PATCH] isdn4linux: Siemens Gigaset drivers - M105 USB DECT adapter") Cc: stable # 2.6.17 Cc: Hansjoerg Lipp Cc: Tilman Schmidt Signed-off-by: Johan Hovold Link: https://lore.kernel.org/r/20191202085610.12719-2-johan@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/staging/isdn/gigaset/usb-gigaset.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/staging/isdn/gigaset/usb-gigaset.c b/drivers/staging/isdn/gigaset/usb-gigaset.c index 1b9b43659bdf4..5e393e7dde456 100644 --- a/drivers/staging/isdn/gigaset/usb-gigaset.c +++ b/drivers/staging/isdn/gigaset/usb-gigaset.c @@ -685,6 +685,11 @@ static int gigaset_probe(struct usb_interface *interface, return -ENODEV; } + if (hostif->desc.bNumEndpoints < 2) { + dev_err(&interface->dev, "missing endpoints\n"); + return -ENODEV; + } + dev_info(&udev->dev, "%s: Device matched ... !\n", __func__); /* allocate memory for our device state and initialize it */ -- GitLab From 84f60ca7b326ed8c08582417493982fe2573a9ad Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 2 Dec 2019 09:56:09 +0100 Subject: [PATCH 0352/1437] staging: gigaset: fix illegal free on probe errors The driver failed to initialise its receive-buffer pointer, something which could lead to an illegal free on late probe errors. Fix this by making sure to clear all driver data at allocation. Fixes: 2032e2c2309d ("usb_gigaset: code cleanup") Cc: stable # 2.6.33 Cc: Tilman Schmidt Signed-off-by: Johan Hovold Link: https://lore.kernel.org/r/20191202085610.12719-3-johan@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/staging/isdn/gigaset/usb-gigaset.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/drivers/staging/isdn/gigaset/usb-gigaset.c b/drivers/staging/isdn/gigaset/usb-gigaset.c index 5e393e7dde456..a84722d83bc6d 100644 --- a/drivers/staging/isdn/gigaset/usb-gigaset.c +++ b/drivers/staging/isdn/gigaset/usb-gigaset.c @@ -571,8 +571,7 @@ static int gigaset_initcshw(struct cardstate *cs) { struct usb_cardstate *ucs; - cs->hw.usb = ucs = - kmalloc(sizeof(struct usb_cardstate), GFP_KERNEL); + cs->hw.usb = ucs = kzalloc(sizeof(struct usb_cardstate), GFP_KERNEL); if (!ucs) { pr_err("out of memory\n"); return -ENOMEM; @@ -584,9 +583,6 @@ static int gigaset_initcshw(struct cardstate *cs) ucs->bchars[3] = 0; ucs->bchars[4] = 0x11; ucs->bchars[5] = 0x13; - ucs->bulk_out_buffer = NULL; - ucs->bulk_out_urb = NULL; - ucs->read_urb = NULL; tasklet_init(&cs->write_tasklet, gigaset_modem_fill, (unsigned long) cs); -- GitLab From ed9ed5a89acba51b82bdff61144d4e4a4245ec8a Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 2 Dec 2019 09:56:10 +0100 Subject: [PATCH 0353/1437] staging: gigaset: add endpoint-type sanity check Add missing endpoint-type sanity checks to probe. This specifically prevents a warning in USB core on URB submission when fuzzing USB descriptors. Signed-off-by: Johan Hovold Cc: stable Link: https://lore.kernel.org/r/20191202085610.12719-4-johan@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/staging/isdn/gigaset/usb-gigaset.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/drivers/staging/isdn/gigaset/usb-gigaset.c b/drivers/staging/isdn/gigaset/usb-gigaset.c index a84722d83bc6d..a20c0bfa68f38 100644 --- a/drivers/staging/isdn/gigaset/usb-gigaset.c +++ b/drivers/staging/isdn/gigaset/usb-gigaset.c @@ -705,6 +705,12 @@ static int gigaset_probe(struct usb_interface *interface, endpoint = &hostif->endpoint[0].desc; + if (!usb_endpoint_is_bulk_out(endpoint)) { + dev_err(&interface->dev, "missing bulk-out endpoint\n"); + retval = -ENODEV; + goto error; + } + buffer_size = le16_to_cpu(endpoint->wMaxPacketSize); ucs->bulk_out_size = buffer_size; ucs->bulk_out_epnum = usb_endpoint_num(endpoint); @@ -724,6 +730,12 @@ static int gigaset_probe(struct usb_interface *interface, endpoint = &hostif->endpoint[1].desc; + if (!usb_endpoint_is_int_in(endpoint)) { + dev_err(&interface->dev, "missing int-in endpoint\n"); + retval = -ENODEV; + goto error; + } + ucs->busy = 0; ucs->read_urb = usb_alloc_urb(0, GFP_KERNEL); -- GitLab From 78baee8d3b976a6a6a2c208e3a36d3f1e6297e6c Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Wed, 4 Dec 2019 22:51:05 +0100 Subject: [PATCH 0354/1437] MAINTAINERS: Match on dma_buf|fence|resv anywhere I've spent a bit too much time reviewing all kinds of users all over the kernel for this buffer sharing infrastructure. And some of it is at least questionable. Make sure we at least see when this stuff flies by. Acked-by: Alex Deucher Acked-by: Thierry Reding Acked-by: Sumit Semwal Acked-by: Dave Airlie Signed-off-by: Daniel Vetter Cc: Sumit Semwal Cc: Mauro Carvalho Chehab Cc: Greg Kroah-Hartman Cc: Rob Herring Cc: linux-media@vger.kernel.org Cc: linaro-mm-sig@lists.linaro.org Link: https://patchwork.freedesktop.org/patch/msgid/20191204215105.874074-1-daniel.vetter@ffwll.ch --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index 296de2b51c832..f02fe0cf0a7b5 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4941,6 +4941,7 @@ F: include/linux/dma-buf* F: include/linux/reservation.h F: include/linux/*fence.h F: Documentation/driver-api/dma-buf.rst +K: dma_(buf|fence|resv) T: git git://anongit.freedesktop.org/drm/drm-misc DMA GENERIC OFFLOAD ENGINE SUBSYSTEM -- GitLab From 2a264a0fd4dc9aa7900fd181c3a6464027cb4df1 Mon Sep 17 00:00:00 2001 From: Umesh Nerlige Ramappa Date: Fri, 6 Dec 2019 11:43:38 -0800 Subject: [PATCH 0355/1437] drm/i915/perf: Allow non-privileged access when OA buffer is not sampled SAMPLE_OA_REPORT enables sampling of OA reports from the OA buffer. Since reports from OA buffer had system wide visibility, collecting samples from the OA buffer was a privileged operation on previous platforms. Prior to TGL, it was also necessary to sample the OA buffer to normalize reports from MI REPORT PERF COUNT. TGL has a dedicated OAR unit to sample perf reports for a specific render context. This removes the necessity to sample OA buffer. - If not sampling the OA buffer, allow non-privileged access. An earlier patch allows the non-privilege access: https://patchwork.freedesktop.org/patch/337716/?series=68582&rev=1 - Clear up the path for non-privileged access in this patch Signed-off-by: Umesh Nerlige Ramappa Fixes: 00a7f0d7155c ("drm/i915/tgl: Add perf support on TGL") Reviewed-by: Lionel Landwerlin Signed-off-by: Lionel Landwerlin Link: https://patchwork.freedesktop.org/patch/msgid/20191206194339.31356-1-umesh.nerlige.ramappa@intel.com (cherry picked from commit 322d56aa3145a28445907ecc638a2c3aa3295c6b) Signed-off-by: Joonas Lahtinen --- drivers/gpu/drm/i915/i915_perf.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_perf.c b/drivers/gpu/drm/i915/i915_perf.c index 65d7c2e599de0..0f6e1d741d11d 100644 --- a/drivers/gpu/drm/i915/i915_perf.c +++ b/drivers/gpu/drm/i915/i915_perf.c @@ -2713,7 +2713,8 @@ static int i915_oa_stream_init(struct i915_perf_stream *stream, return -EINVAL; } - if (!(props->sample_flags & SAMPLE_OA_REPORT)) { + if (!(props->sample_flags & SAMPLE_OA_REPORT) && + (INTEL_GEN(perf->i915) < 12 || !stream->ctx)) { DRM_DEBUG("Only OA report sampling supported\n"); return -EINVAL; } @@ -2745,7 +2746,7 @@ static int i915_oa_stream_init(struct i915_perf_stream *stream, format_size = perf->oa_formats[props->oa_format].size; - stream->sample_flags |= SAMPLE_OA_REPORT; + stream->sample_flags = props->sample_flags; stream->sample_size += format_size; stream->oa_buffer.format_size = format_size; -- GitLab From 177e876af8d291d0351db3bae84b483e4722c4a7 Mon Sep 17 00:00:00 2001 From: Umesh Nerlige Ramappa Date: Fri, 6 Dec 2019 11:43:39 -0800 Subject: [PATCH 0356/1437] drm/i915/perf: Configure OAR for specific context Gen12 supports saving/restoring render counters per context. Apply OAR configuration only for the context that is passed in to perf. v2: - Fix OACTXCONTROL value to only stop/resume counters. - Remove gen12_update_reg_state_unlocked as power state is already applied by the caller. v3: (Lionel) - Move register initialization into the array - Assume a valid oa_config in enable_metric_set Signed-off-by: Umesh Nerlige Ramappa Fixes: 00a7f0d7155c ("drm/i915/tgl: Add perf support on TGL") Reviewed-by: Lionel Landwerlin Signed-off-by: Lionel Landwerlin Link: https://patchwork.freedesktop.org/patch/msgid/20191206194339.31356-2-umesh.nerlige.ramappa@intel.com (cherry picked from commit ccdeed497042676e13fc1625e2a341880eff5da5) Signed-off-by: Joonas Lahtinen --- drivers/gpu/drm/i915/i915_perf.c | 199 +++++++++++++++++-------------- 1 file changed, 112 insertions(+), 87 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_perf.c b/drivers/gpu/drm/i915/i915_perf.c index 0f6e1d741d11d..2ae14bc149317 100644 --- a/drivers/gpu/drm/i915/i915_perf.c +++ b/drivers/gpu/drm/i915/i915_perf.c @@ -2078,20 +2078,12 @@ gen8_update_reg_state_unlocked(const struct intel_context *ce, u32 *reg_state = ce->lrc_reg_state; int i; - if (IS_GEN(stream->perf->i915, 12)) { - u32 format = stream->oa_buffer.format; + reg_state[ctx_oactxctrl + 1] = + (stream->period_exponent << GEN8_OA_TIMER_PERIOD_SHIFT) | + (stream->periodic ? GEN8_OA_TIMER_ENABLE : 0) | + GEN8_OA_COUNTER_RESUME; - reg_state[ctx_oactxctrl + 1] = - (format << GEN12_OAR_OACONTROL_COUNTER_FORMAT_SHIFT) | - (stream->oa_config ? GEN12_OAR_OACONTROL_COUNTER_ENABLE : 0); - } else { - reg_state[ctx_oactxctrl + 1] = - (stream->period_exponent << GEN8_OA_TIMER_PERIOD_SHIFT) | - (stream->periodic ? GEN8_OA_TIMER_ENABLE : 0) | - GEN8_OA_COUNTER_RESUME; - } - - for (i = 0; !!ctx_flexeu0 && i < ARRAY_SIZE(flex_regs); i++) + for (i = 0; i < ARRAY_SIZE(flex_regs); i++) reg_state[ctx_flexeu0 + i * 2 + 1] = oa_config_flex_reg(stream->oa_config, flex_regs[i]); @@ -2224,34 +2216,51 @@ static int gen8_configure_context(struct i915_gem_context *ctx, return err; } -static int gen12_emit_oar_config(struct intel_context *ce, bool enable) +static int gen12_configure_oar_context(struct i915_perf_stream *stream, bool enable) { - struct i915_request *rq; - u32 *cs; - int err = 0; - - rq = i915_request_create(ce); - if (IS_ERR(rq)) - return PTR_ERR(rq); - - cs = intel_ring_begin(rq, 4); - if (IS_ERR(cs)) { - err = PTR_ERR(cs); - goto out; - } - - *cs++ = MI_LOAD_REGISTER_IMM(1); - *cs++ = i915_mmio_reg_offset(RING_CONTEXT_CONTROL(ce->engine->mmio_base)); - *cs++ = _MASKED_FIELD(GEN12_CTX_CTRL_OAR_CONTEXT_ENABLE, - enable ? GEN12_CTX_CTRL_OAR_CONTEXT_ENABLE : 0); - *cs++ = MI_NOOP; + int err; + struct intel_context *ce = stream->pinned_ctx; + u32 format = stream->oa_buffer.format; + struct flex regs_context[] = { + { + GEN8_OACTXCONTROL, + stream->perf->ctx_oactxctrl_offset + 1, + enable ? GEN8_OA_COUNTER_RESUME : 0, + }, + }; + /* Offsets in regs_lri are not used since this configuration is only + * applied using LRI. Initialize the correct offsets for posterity. + */ +#define GEN12_OAR_OACONTROL_OFFSET 0x5B0 + struct flex regs_lri[] = { + { + GEN12_OAR_OACONTROL, + GEN12_OAR_OACONTROL_OFFSET + 1, + (format << GEN12_OAR_OACONTROL_COUNTER_FORMAT_SHIFT) | + (enable ? GEN12_OAR_OACONTROL_COUNTER_ENABLE : 0) + }, + { + RING_CONTEXT_CONTROL(ce->engine->mmio_base), + CTX_CONTEXT_CONTROL, + _MASKED_FIELD(GEN12_CTX_CTRL_OAR_CONTEXT_ENABLE, + enable ? + GEN12_CTX_CTRL_OAR_CONTEXT_ENABLE : + 0) + }, + }; - intel_ring_advance(rq, cs); + /* Modify the context image of pinned context with regs_context*/ + err = intel_context_lock_pinned(ce); + if (err) + return err; -out: - i915_request_add(rq); + err = gen8_modify_context(ce, regs_context, ARRAY_SIZE(regs_context)); + intel_context_unlock_pinned(ce); + if (err) + return err; - return err; + /* Apply regs_lri using LRI with pinned context */ + return gen8_modify_self(ce, regs_lri, ARRAY_SIZE(regs_lri)); } /* @@ -2277,53 +2286,16 @@ out: * per-context OA state. * * Note: it's only the RCS/Render context that has any OA state. + * Note: the first flex register passed must always be R_PWR_CLK_STATE */ -static int lrc_configure_all_contexts(struct i915_perf_stream *stream, - const struct i915_oa_config *oa_config) +static int oa_configure_all_contexts(struct i915_perf_stream *stream, + struct flex *regs, + size_t num_regs) { struct drm_i915_private *i915 = stream->perf->i915; - /* The MMIO offsets for Flex EU registers aren't contiguous */ - const u32 ctx_flexeu0 = stream->perf->ctx_flexeu0_offset; -#define ctx_flexeuN(N) (ctx_flexeu0 + 2 * (N) + 1) - struct flex regs[] = { - { - GEN8_R_PWR_CLK_STATE, - CTX_R_PWR_CLK_STATE, - }, - { - IS_GEN(i915, 12) ? - GEN12_OAR_OACONTROL : GEN8_OACTXCONTROL, - stream->perf->ctx_oactxctrl_offset + 1, - }, - { EU_PERF_CNTL0, ctx_flexeuN(0) }, - { EU_PERF_CNTL1, ctx_flexeuN(1) }, - { EU_PERF_CNTL2, ctx_flexeuN(2) }, - { EU_PERF_CNTL3, ctx_flexeuN(3) }, - { EU_PERF_CNTL4, ctx_flexeuN(4) }, - { EU_PERF_CNTL5, ctx_flexeuN(5) }, - { EU_PERF_CNTL6, ctx_flexeuN(6) }, - }; -#undef ctx_flexeuN struct intel_engine_cs *engine; struct i915_gem_context *ctx, *cn; - size_t array_size = IS_GEN(i915, 12) ? 2 : ARRAY_SIZE(regs); - int i, err; - - if (IS_GEN(i915, 12)) { - u32 format = stream->oa_buffer.format; - - regs[1].value = - (format << GEN12_OAR_OACONTROL_COUNTER_FORMAT_SHIFT) | - (oa_config ? GEN12_OAR_OACONTROL_COUNTER_ENABLE : 0); - } else { - regs[1].value = - (stream->period_exponent << GEN8_OA_TIMER_PERIOD_SHIFT) | - (stream->periodic ? GEN8_OA_TIMER_ENABLE : 0) | - GEN8_OA_COUNTER_RESUME; - } - - for (i = 2; !!ctx_flexeu0 && i < array_size; i++) - regs[i].value = oa_config_flex_reg(oa_config, regs[i].reg); + int err; lockdep_assert_held(&stream->perf->lock); @@ -2353,7 +2325,7 @@ static int lrc_configure_all_contexts(struct i915_perf_stream *stream, spin_unlock(&i915->gem.contexts.lock); - err = gen8_configure_context(ctx, regs, array_size); + err = gen8_configure_context(ctx, regs, num_regs); if (err) { i915_gem_context_put(ctx); return err; @@ -2378,7 +2350,7 @@ static int lrc_configure_all_contexts(struct i915_perf_stream *stream, regs[0].value = intel_sseu_make_rpcs(i915, &ce->sseu); - err = gen8_modify_self(ce, regs, array_size); + err = gen8_modify_self(ce, regs, num_regs); if (err) return err; } @@ -2386,6 +2358,56 @@ static int lrc_configure_all_contexts(struct i915_perf_stream *stream, return 0; } +static int gen12_configure_all_contexts(struct i915_perf_stream *stream, + const struct i915_oa_config *oa_config) +{ + struct flex regs[] = { + { + GEN8_R_PWR_CLK_STATE, + CTX_R_PWR_CLK_STATE, + }, + }; + + return oa_configure_all_contexts(stream, regs, ARRAY_SIZE(regs)); +} + +static int lrc_configure_all_contexts(struct i915_perf_stream *stream, + const struct i915_oa_config *oa_config) +{ + /* The MMIO offsets for Flex EU registers aren't contiguous */ + const u32 ctx_flexeu0 = stream->perf->ctx_flexeu0_offset; +#define ctx_flexeuN(N) (ctx_flexeu0 + 2 * (N) + 1) + struct flex regs[] = { + { + GEN8_R_PWR_CLK_STATE, + CTX_R_PWR_CLK_STATE, + }, + { + GEN8_OACTXCONTROL, + stream->perf->ctx_oactxctrl_offset + 1, + }, + { EU_PERF_CNTL0, ctx_flexeuN(0) }, + { EU_PERF_CNTL1, ctx_flexeuN(1) }, + { EU_PERF_CNTL2, ctx_flexeuN(2) }, + { EU_PERF_CNTL3, ctx_flexeuN(3) }, + { EU_PERF_CNTL4, ctx_flexeuN(4) }, + { EU_PERF_CNTL5, ctx_flexeuN(5) }, + { EU_PERF_CNTL6, ctx_flexeuN(6) }, + }; +#undef ctx_flexeuN + int i; + + regs[1].value = + (stream->period_exponent << GEN8_OA_TIMER_PERIOD_SHIFT) | + (stream->periodic ? GEN8_OA_TIMER_ENABLE : 0) | + GEN8_OA_COUNTER_RESUME; + + for (i = 2; i < ARRAY_SIZE(regs); i++) + regs[i].value = oa_config_flex_reg(oa_config, regs[i].reg); + + return oa_configure_all_contexts(stream, regs, ARRAY_SIZE(regs)); +} + static int gen8_enable_metric_set(struct i915_perf_stream *stream) { struct intel_uncore *uncore = stream->uncore; @@ -2464,7 +2486,7 @@ static int gen12_enable_metric_set(struct i915_perf_stream *stream) * to make sure all slices/subslices are ON before writing to NOA * registers. */ - ret = lrc_configure_all_contexts(stream, oa_config); + ret = gen12_configure_all_contexts(stream, oa_config); if (ret) return ret; @@ -2474,8 +2496,7 @@ static int gen12_enable_metric_set(struct i915_perf_stream *stream) * requested this. */ if (stream->ctx) { - ret = gen12_emit_oar_config(stream->pinned_ctx, - oa_config != NULL); + ret = gen12_configure_oar_context(stream, true); if (ret) return ret; } @@ -2509,11 +2530,11 @@ static void gen12_disable_metric_set(struct i915_perf_stream *stream) struct intel_uncore *uncore = stream->uncore; /* Reset all contexts' slices/subslices configurations. */ - lrc_configure_all_contexts(stream, NULL); + gen12_configure_all_contexts(stream, NULL); /* disable the context save/restore or OAR counters */ if (stream->ctx) - gen12_emit_oar_config(stream->pinned_ctx, false); + gen12_configure_oar_context(stream, false); /* Make sure we disable noa to save power. */ intel_uncore_rmw(uncore, RPM_CONFIG1, GEN10_GT_NOA_ENABLE, 0); @@ -2855,7 +2876,11 @@ void i915_oa_init_reg_state(const struct intel_context *ce, return; stream = engine->i915->perf.exclusive_stream; - if (stream) + /* + * For gen12, only CTX_R_PWR_CLK_STATE needs update, but the caller + * is already doing that, so nothing to be done for gen12 here. + */ + if (stream && INTEL_GEN(stream->perf->i915) < 12) gen8_update_reg_state_unlocked(ce, stream); } -- GitLab From 0eb8e74f7202a4a98bbc0c1adeed3986cf50b66a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Wed, 27 Nov 2019 22:12:09 +0200 Subject: [PATCH 0357/1437] drm/i915/fbc: Disable fbc by default on all glk+ MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We're missing a workaround in the fbc code for all glk+ platforms which can cause corruption around the top of the screen. So enabling fbc by default is a bad idea. I'm not keen to backport the w/a so let's start by disabling fbc by default on all glk+. We'll lift the restriction once the w/a is in place. Cc: stable@vger.kernel.org Cc: Daniel Drake Cc: Paulo Zanoni Cc: Jian-Hong Pan Cc: Maarten Lankhorst Signed-off-by: Ville Syrjälä Link: https://patchwork.freedesktop.org/patch/msgid/20191127201222.16669-2-ville.syrjala@linux.intel.com Reviewed-by: Maarten Lankhorst (cherry picked from commit cd8c021b36a66833cefe2c90a79a9e312a2a5690) Signed-off-by: Joonas Lahtinen --- drivers/gpu/drm/i915/display/intel_fbc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/display/intel_fbc.c b/drivers/gpu/drm/i915/display/intel_fbc.c index 3111ecaeabd0e..20616639b8ab1 100644 --- a/drivers/gpu/drm/i915/display/intel_fbc.c +++ b/drivers/gpu/drm/i915/display/intel_fbc.c @@ -1284,7 +1284,7 @@ static int intel_sanitize_fbc_option(struct drm_i915_private *dev_priv) return 0; /* https://bugs.freedesktop.org/show_bug.cgi?id=108085 */ - if (IS_GEMINILAKE(dev_priv)) + if (INTEL_GEN(dev_priv) >= 10 || IS_GEMINILAKE(dev_priv)) return 0; if (IS_BROADWELL(dev_priv) || INTEL_GEN(dev_priv) >= 9) -- GitLab From 3ce8209735e120db6f0ef739480e3dc7e2cb6763 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Wed, 4 Dec 2019 20:05:41 +0200 Subject: [PATCH 0358/1437] drm/i915/hdcp: Nuke intel_hdcp_transcoder_config() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit intel_hdcp_transcoder_config() is clobbering some globally visible state in .compute_config(). That is a big no no as .compute_config() is supposed to have no visible side effects when either the commit fails or it's just a TEST_ONLY commit. Inline this stuff into intel_hdcp_enable() so that the state only gets modified when we actually commit the state to the hardware. Cc: Ramalingam C Cc: Jani Nikula Cc: Uma Shankar Fixes: 39e2df090c3c ("drm/i915/hdcp: update current transcoder into intel_hdcp") Signed-off-by: Ville Syrjälä Link: https://patchwork.freedesktop.org/patch/msgid/20191204180549.1267-2-ville.syrjala@linux.intel.com Reviewed-by: Ramalingam C (cherry picked from commit 67e1d5ed85a83e232a9e0b995f5778a86722b96e) Signed-off-by: Joonas Lahtinen --- drivers/gpu/drm/i915/display/intel_ddi.c | 5 ++++- drivers/gpu/drm/i915/display/intel_dp.c | 3 --- drivers/gpu/drm/i915/display/intel_hdcp.c | 26 +++++++---------------- drivers/gpu/drm/i915/display/intel_hdcp.h | 5 ++--- drivers/gpu/drm/i915/display/intel_hdmi.c | 3 --- 5 files changed, 14 insertions(+), 28 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_ddi.c b/drivers/gpu/drm/i915/display/intel_ddi.c index c7c2b349858d2..2a27fb5d7dc6f 100644 --- a/drivers/gpu/drm/i915/display/intel_ddi.c +++ b/drivers/gpu/drm/i915/display/intel_ddi.c @@ -3986,6 +3986,7 @@ static void intel_enable_ddi(struct intel_encoder *encoder, if (conn_state->content_protection == DRM_MODE_CONTENT_PROTECTION_DESIRED) intel_hdcp_enable(to_intel_connector(conn_state->connector), + crtc_state->cpu_transcoder, (u8)conn_state->hdcp_content_type); } @@ -4089,7 +4090,9 @@ static void intel_ddi_update_pipe(struct intel_encoder *encoder, if (conn_state->content_protection == DRM_MODE_CONTENT_PROTECTION_DESIRED || content_protection_type_changed) - intel_hdcp_enable(connector, (u8)conn_state->hdcp_content_type); + intel_hdcp_enable(connector, + crtc_state->cpu_transcoder, + (u8)conn_state->hdcp_content_type); } static void diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c index 050655a1a3d8d..b05b2191b919b 100644 --- a/drivers/gpu/drm/i915/display/intel_dp.c +++ b/drivers/gpu/drm/i915/display/intel_dp.c @@ -2414,9 +2414,6 @@ intel_dp_compute_config(struct intel_encoder *encoder, intel_psr_compute_config(intel_dp, pipe_config); - intel_hdcp_transcoder_config(intel_connector, - pipe_config->cpu_transcoder); - return 0; } diff --git a/drivers/gpu/drm/i915/display/intel_hdcp.c b/drivers/gpu/drm/i915/display/intel_hdcp.c index f1f41ca8402bf..a448815d8fc2e 100644 --- a/drivers/gpu/drm/i915/display/intel_hdcp.c +++ b/drivers/gpu/drm/i915/display/intel_hdcp.c @@ -1821,23 +1821,6 @@ enum mei_fw_tc intel_get_mei_fw_tc(enum transcoder cpu_transcoder) } } -void intel_hdcp_transcoder_config(struct intel_connector *connector, - enum transcoder cpu_transcoder) -{ - struct drm_i915_private *dev_priv = to_i915(connector->base.dev); - struct intel_hdcp *hdcp = &connector->hdcp; - - if (!hdcp->shim) - return; - - if (INTEL_GEN(dev_priv) >= 12) { - mutex_lock(&hdcp->mutex); - hdcp->cpu_transcoder = cpu_transcoder; - hdcp->port_data.fw_tc = intel_get_mei_fw_tc(cpu_transcoder); - mutex_unlock(&hdcp->mutex); - } -} - static inline int initialize_hdcp_port_data(struct intel_connector *connector, const struct intel_hdcp_shim *shim) { @@ -1959,8 +1942,10 @@ int intel_hdcp_init(struct intel_connector *connector, return 0; } -int intel_hdcp_enable(struct intel_connector *connector, u8 content_type) +int intel_hdcp_enable(struct intel_connector *connector, + enum transcoder cpu_transcoder, u8 content_type) { + struct drm_i915_private *dev_priv = to_i915(connector->base.dev); struct intel_hdcp *hdcp = &connector->hdcp; unsigned long check_link_interval = DRM_HDCP_CHECK_PERIOD_MS; int ret = -EINVAL; @@ -1972,6 +1957,11 @@ int intel_hdcp_enable(struct intel_connector *connector, u8 content_type) WARN_ON(hdcp->value == DRM_MODE_CONTENT_PROTECTION_ENABLED); hdcp->content_type = content_type; + if (INTEL_GEN(dev_priv) >= 12) { + hdcp->cpu_transcoder = cpu_transcoder; + hdcp->port_data.fw_tc = intel_get_mei_fw_tc(cpu_transcoder); + } + /* * Considering that HDCP2.2 is more secure than HDCP1.4, If the setup * is capable of HDCP2.2, it is preferred to use HDCP2.2. diff --git a/drivers/gpu/drm/i915/display/intel_hdcp.h b/drivers/gpu/drm/i915/display/intel_hdcp.h index 41c1053d9e38b..f3c3272e712a1 100644 --- a/drivers/gpu/drm/i915/display/intel_hdcp.h +++ b/drivers/gpu/drm/i915/display/intel_hdcp.h @@ -21,11 +21,10 @@ enum transcoder; void intel_hdcp_atomic_check(struct drm_connector *connector, struct drm_connector_state *old_state, struct drm_connector_state *new_state); -void intel_hdcp_transcoder_config(struct intel_connector *connector, - enum transcoder cpu_transcoder); int intel_hdcp_init(struct intel_connector *connector, const struct intel_hdcp_shim *hdcp_shim); -int intel_hdcp_enable(struct intel_connector *connector, u8 content_type); +int intel_hdcp_enable(struct intel_connector *connector, + enum transcoder cpu_transcoder, u8 content_type); int intel_hdcp_disable(struct intel_connector *connector); bool is_hdcp_supported(struct drm_i915_private *dev_priv, enum port port); bool intel_hdcp_capable(struct intel_connector *connector); diff --git a/drivers/gpu/drm/i915/display/intel_hdmi.c b/drivers/gpu/drm/i915/display/intel_hdmi.c index f6f5312205c49..f56fffc474faf 100644 --- a/drivers/gpu/drm/i915/display/intel_hdmi.c +++ b/drivers/gpu/drm/i915/display/intel_hdmi.c @@ -2489,9 +2489,6 @@ int intel_hdmi_compute_config(struct intel_encoder *encoder, return -EINVAL; } - intel_hdcp_transcoder_config(intel_hdmi->attached_connector, - pipe_config->cpu_transcoder); - return 0; } -- GitLab From f26a9e959a7b1588c59f7a919b41b67175b211d8 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Mon, 9 Dec 2019 02:32:15 +0000 Subject: [PATCH 0359/1437] drm/i915/gt: Detect if we miss WaIdleLiteRestore In order to avoid confusing the HW, we must never submit an empty ring during lite-restore, that is we should always advance the RING_TAIL before submitting to stay ahead of the RING_HEAD. Normally this is prevented by keeping a couple of spare NOPs in the request->wa_tail so that on resubmission we can advance the tail. This relies on the request only being resubmitted once, which is the normal condition as it is seen once for ELSP[1] and then later in ELSP[0]. On preemption, the requests are unwound and the tail reset back to the normal end point (as we know the request is incomplete and therefore its RING_HEAD is even earlier). However, if this w/a should fail we would try and resubmit the request with the RING_TAIL already set to the location of this request's wa_tail potentially causing a GPU hang. We can spot when we do try and incorrectly resubmit without advancing the RING_TAIL and spare any embarrassment by forcing the context restore. In the case of preempt-to-busy, we leave the requests running on the HW while we unwind. As the ring is still live, we cannot rewind our rq->tail without forcing a reload so leave it set to rq->wa_tail and only force a reload if we resubmit after a lite-restore. (Normally, the forced reload will be a part of the preemption event.) Fixes: 22b7a426bbe1 ("drm/i915/execlists: Preempt-to-busy") Closes: https://gitlab.freedesktop.org/drm/intel/issues/673 Signed-off-by: Chris Wilson Cc: Mika Kuoppala Cc: Tvrtko Ursulin Reviewed-by: Tvrtko Ursulin Cc: stable@kernel.vger.org Link: https://patchwork.freedesktop.org/patch/msgid/20191209023215.3519970-1-chris@chris-wilson.co.uk (cherry picked from commit 82c69bf58650e644c61aa2bf5100b63a1070fd2f) Signed-off-by: Joonas Lahtinen --- drivers/gpu/drm/i915/gt/intel_lrc.c | 46 +++++++++++++---------------- 1 file changed, 21 insertions(+), 25 deletions(-) diff --git a/drivers/gpu/drm/i915/gt/intel_lrc.c b/drivers/gpu/drm/i915/gt/intel_lrc.c index 17bb52aeff190..75dd0e0367b7a 100644 --- a/drivers/gpu/drm/i915/gt/intel_lrc.c +++ b/drivers/gpu/drm/i915/gt/intel_lrc.c @@ -845,12 +845,6 @@ static const u8 *reg_offsets(const struct intel_engine_cs *engine) } } -static void unwind_wa_tail(struct i915_request *rq) -{ - rq->tail = intel_ring_wrap(rq->ring, rq->wa_tail - WA_TAIL_BYTES); - assert_ring_tail_valid(rq->ring, rq->tail); -} - static struct i915_request * __unwind_incomplete_requests(struct intel_engine_cs *engine) { @@ -863,12 +857,10 @@ __unwind_incomplete_requests(struct intel_engine_cs *engine) list_for_each_entry_safe_reverse(rq, rn, &engine->active.requests, sched.link) { - if (i915_request_completed(rq)) continue; /* XXX */ __i915_request_unsubmit(rq); - unwind_wa_tail(rq); /* * Push the request back into the queue for later resubmission. @@ -1161,13 +1153,29 @@ execlists_schedule_out(struct i915_request *rq) i915_request_put(rq); } -static u64 execlists_update_context(const struct i915_request *rq) +static u64 execlists_update_context(struct i915_request *rq) { struct intel_context *ce = rq->hw_context; - u64 desc; + u64 desc = ce->lrc_desc; + u32 tail; - ce->lrc_reg_state[CTX_RING_TAIL] = - intel_ring_set_tail(rq->ring, rq->tail); + /* + * WaIdleLiteRestore:bdw,skl + * + * We should never submit the context with the same RING_TAIL twice + * just in case we submit an empty ring, which confuses the HW. + * + * We append a couple of NOOPs (gen8_emit_wa_tail) after the end of + * the normal request to be able to always advance the RING_TAIL on + * subsequent resubmissions (for lite restore). Should that fail us, + * and we try and submit the same tail again, force the context + * reload. + */ + tail = intel_ring_set_tail(rq->ring, rq->tail); + if (unlikely(ce->lrc_reg_state[CTX_RING_TAIL] == tail)) + desc |= CTX_DESC_FORCE_RESTORE; + ce->lrc_reg_state[CTX_RING_TAIL] = tail; + rq->tail = rq->wa_tail; /* * Make sure the context image is complete before we submit it to HW. @@ -1186,13 +1194,11 @@ static u64 execlists_update_context(const struct i915_request *rq) */ mb(); - desc = ce->lrc_desc; - ce->lrc_desc &= ~CTX_DESC_FORCE_RESTORE; - /* Wa_1607138340:tgl */ if (IS_TGL_REVID(rq->i915, TGL_REVID_A0, TGL_REVID_A0)) desc |= CTX_DESC_FORCE_RESTORE; + ce->lrc_desc &= ~CTX_DESC_FORCE_RESTORE; return desc; } @@ -1703,16 +1709,6 @@ static void execlists_dequeue(struct intel_engine_cs *engine) return; } - - /* - * WaIdleLiteRestore:bdw,skl - * Apply the wa NOOPs to prevent - * ring:HEAD == rq:TAIL as we resubmit the - * request. See gen8_emit_fini_breadcrumb() for - * where we prepare the padding after the - * end of the request. - */ - last->tail = last->wa_tail; } } -- GitLab From 1848a543191ae32e558bb0a5974ae7c38ebd86fc Mon Sep 17 00:00:00 2001 From: Wen Yang Date: Sun, 24 Nov 2019 22:22:36 +0800 Subject: [PATCH 0360/1437] usb: roles: fix a potential use after free Free the sw structure only after we are done using it. This patch just moves the put_device() down a bit to avoid the use after free. Fixes: 5c54fcac9a9d ("usb: roles: Take care of driver module reference counting") Signed-off-by: Wen Yang Reviewed-by: Heikki Krogerus Reviewed-by: Peter Chen Cc: stable Cc: Hans de Goede Cc: Chunfeng Yun Cc: Suzuki K Poulose Cc: linux-usb@vger.kernel.org Cc: linux-kernel@vger.kernel.org Link: https://lore.kernel.org/r/20191124142236.25671-1-wenyang@linux.alibaba.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/roles/class.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/roles/class.c b/drivers/usb/roles/class.c index 8273126ffdf4b..63a00ff26655e 100644 --- a/drivers/usb/roles/class.c +++ b/drivers/usb/roles/class.c @@ -169,8 +169,8 @@ EXPORT_SYMBOL_GPL(fwnode_usb_role_switch_get); void usb_role_switch_put(struct usb_role_switch *sw) { if (!IS_ERR_OR_NULL(sw)) { - put_device(&sw->dev); module_put(sw->dev.parent->driver->owner); + put_device(&sw->dev); } } EXPORT_SYMBOL_GPL(usb_role_switch_put); -- GitLab From 5c388abefda0d92355714010c0199055c57ab6c7 Mon Sep 17 00:00:00 2001 From: Wen Yang Date: Tue, 26 Nov 2019 22:04:52 +0800 Subject: [PATCH 0361/1437] usb: typec: fix use after free in typec_register_port() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We can't use "port->sw" and/or "port->mux" after it has been freed. Fixes: 23481121c81d ("usb: typec: class: Don't use port parent for getting mux handles") Signed-off-by: Wen Yang Cc: stable Cc: linux-usb@vger.kernel.org Cc: linux-kernel@vger.kernel.org Acked-by: Heikki Krogerus  Link: https://lore.kernel.org/r/20191126140452.14048-1-wenyang@linux.alibaba.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/class.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/usb/typec/class.c b/drivers/usb/typec/class.c index 7ece6ca6e690b..91d62276b56fe 100644 --- a/drivers/usb/typec/class.c +++ b/drivers/usb/typec/class.c @@ -1612,14 +1612,16 @@ struct typec_port *typec_register_port(struct device *parent, port->sw = typec_switch_get(&port->dev); if (IS_ERR(port->sw)) { + ret = PTR_ERR(port->sw); put_device(&port->dev); - return ERR_CAST(port->sw); + return ERR_PTR(ret); } port->mux = typec_mux_get(&port->dev, NULL); if (IS_ERR(port->mux)) { + ret = PTR_ERR(port->mux); put_device(&port->dev); - return ERR_CAST(port->mux); + return ERR_PTR(ret); } ret = device_add(&port->dev); -- GitLab From 1cd17f7f0def31e3695501c4f86cd3faf8489840 Mon Sep 17 00:00:00 2001 From: Emiliano Ingrassia Date: Wed, 27 Nov 2019 17:03:55 +0100 Subject: [PATCH 0362/1437] usb: core: urb: fix URB structure initialization function Explicitly initialize URB structure urb_list field in usb_init_urb(). This field can be potentially accessed uninitialized and its initialization is coherent with the usage of list_del_init() in usb_hcd_unlink_urb_from_ep() and usb_giveback_urb_bh() and its explicit initialization in usb_hcd_submit_urb() error path. Signed-off-by: Emiliano Ingrassia Cc: stable Link: https://lore.kernel.org/r/20191127160355.GA27196@ingrassia.epigenesys.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/urb.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/usb/core/urb.c b/drivers/usb/core/urb.c index 0eab79f82ce41..da923ec176122 100644 --- a/drivers/usb/core/urb.c +++ b/drivers/usb/core/urb.c @@ -45,6 +45,7 @@ void usb_init_urb(struct urb *urb) if (urb) { memset(urb, 0, sizeof(*urb)); kref_init(&urb->kref); + INIT_LIST_HEAD(&urb->urb_list); INIT_LIST_HEAD(&urb->anchor_list); } } -- GitLab From 59120962e4be4f72be537adb17da6881c4b3797c Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue Date: Thu, 28 Nov 2019 13:43:57 +0000 Subject: [PATCH 0363/1437] usb: common: usb-conn-gpio: Don't log an error on probe deferral This patch makes the printout of the error message for failing to get a VBUS regulator handle conditional on the error code being something other than -EPROBE_DEFER. Deferral is a normal thing, we don't need an error message for this. Cc: Chunfeng Yun Cc: Nagarjuna Kristam Cc: Linus Walleij Cc: Greg Kroah-Hartman Cc: linux-usb@vger.kernel.org Signed-off-by: Bryan O'Donoghue Cc: stable Link: https://lore.kernel.org/r/20191128134358.3880498-2-bryan.odonoghue@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/common/usb-conn-gpio.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/usb/common/usb-conn-gpio.c b/drivers/usb/common/usb-conn-gpio.c index 87338f9eb5bec..ed204cbb63ea1 100644 --- a/drivers/usb/common/usb-conn-gpio.c +++ b/drivers/usb/common/usb-conn-gpio.c @@ -156,7 +156,8 @@ static int usb_conn_probe(struct platform_device *pdev) info->vbus = devm_regulator_get(dev, "vbus"); if (IS_ERR(info->vbus)) { - dev_err(dev, "failed to get vbus\n"); + if (PTR_ERR(info->vbus) != -EPROBE_DEFER) + dev_err(dev, "failed to get vbus\n"); return PTR_ERR(info->vbus); } -- GitLab From 19e6317d24c25ee737c65d1ffb7483bdda4bb54a Mon Sep 17 00:00:00 2001 From: Pete Zaitcev Date: Wed, 4 Dec 2019 20:39:41 -0600 Subject: [PATCH 0364/1437] usb: mon: Fix a deadlock in usbmon between mmap and read The problem arises because our read() function grabs a lock of the circular buffer, finds something of interest, then invokes copy_to_user() straight from the buffer, which in turn takes mm->mmap_sem. In the same time, the callback mon_bin_vma_fault() is invoked under mm->mmap_sem. It attempts to take the fetch lock and deadlocks. This patch does away with protecting of our page list with any semaphores, and instead relies on the kernel not close the device while mmap is active in a process. In addition, we prohibit re-sizing of a buffer while mmap is active. This way, when (now unlocked) fault is processed, it works with the page that is intended to be mapped-in, and not some other random page. Note that this may have an ABI impact, but hopefully no legitimate program is this wrong. Signed-off-by: Pete Zaitcev Reported-by: syzbot+56f9673bb4cdcbeb0e92@syzkaller.appspotmail.com Reviewed-by: Alan Stern Fixes: 46eb14a6e158 ("USB: fix usbmon BUG trigger") Cc: Link: https://lore.kernel.org/r/20191204203941.3503452b@suzdal.zaitcev.lan Signed-off-by: Greg Kroah-Hartman --- drivers/usb/mon/mon_bin.c | 32 +++++++++++++++++++++----------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/drivers/usb/mon/mon_bin.c b/drivers/usb/mon/mon_bin.c index ac2b4fcc265f6..f48a23adbc35d 100644 --- a/drivers/usb/mon/mon_bin.c +++ b/drivers/usb/mon/mon_bin.c @@ -1039,12 +1039,18 @@ static long mon_bin_ioctl(struct file *file, unsigned int cmd, unsigned long arg mutex_lock(&rp->fetch_lock); spin_lock_irqsave(&rp->b_lock, flags); - mon_free_buff(rp->b_vec, rp->b_size/CHUNK_SIZE); - kfree(rp->b_vec); - rp->b_vec = vec; - rp->b_size = size; - rp->b_read = rp->b_in = rp->b_out = rp->b_cnt = 0; - rp->cnt_lost = 0; + if (rp->mmap_active) { + mon_free_buff(vec, size/CHUNK_SIZE); + kfree(vec); + ret = -EBUSY; + } else { + mon_free_buff(rp->b_vec, rp->b_size/CHUNK_SIZE); + kfree(rp->b_vec); + rp->b_vec = vec; + rp->b_size = size; + rp->b_read = rp->b_in = rp->b_out = rp->b_cnt = 0; + rp->cnt_lost = 0; + } spin_unlock_irqrestore(&rp->b_lock, flags); mutex_unlock(&rp->fetch_lock); } @@ -1216,13 +1222,21 @@ mon_bin_poll(struct file *file, struct poll_table_struct *wait) static void mon_bin_vma_open(struct vm_area_struct *vma) { struct mon_reader_bin *rp = vma->vm_private_data; + unsigned long flags; + + spin_lock_irqsave(&rp->b_lock, flags); rp->mmap_active++; + spin_unlock_irqrestore(&rp->b_lock, flags); } static void mon_bin_vma_close(struct vm_area_struct *vma) { + unsigned long flags; + struct mon_reader_bin *rp = vma->vm_private_data; + spin_lock_irqsave(&rp->b_lock, flags); rp->mmap_active--; + spin_unlock_irqrestore(&rp->b_lock, flags); } /* @@ -1234,16 +1248,12 @@ static vm_fault_t mon_bin_vma_fault(struct vm_fault *vmf) unsigned long offset, chunk_idx; struct page *pageptr; - mutex_lock(&rp->fetch_lock); offset = vmf->pgoff << PAGE_SHIFT; - if (offset >= rp->b_size) { - mutex_unlock(&rp->fetch_lock); + if (offset >= rp->b_size) return VM_FAULT_SIGBUS; - } chunk_idx = offset / CHUNK_SIZE; pageptr = rp->b_vec[chunk_idx].pg; get_page(pageptr); - mutex_unlock(&rp->fetch_lock); vmf->page = pageptr; return 0; } -- GitLab From 392a9f63058f2cdcec8363b849a25532ee40da9f Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Mon, 4 Nov 2019 10:15:01 -0800 Subject: [PATCH 0365/1437] dt-bindings: reset: Fix brcmstb-reset example The reset controller has a #reset-cells value of 1, so we should see a phandle plus a register identifier, fix the example. Fixes: 0807caf647dd ("dt-bindings: reset: Add document for Broadcom STB reset controller") Signed-off-by: Florian Fainelli Signed-off-by: Philipp Zabel --- Documentation/devicetree/bindings/reset/brcm,brcmstb-reset.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/reset/brcm,brcmstb-reset.txt b/Documentation/devicetree/bindings/reset/brcm,brcmstb-reset.txt index 6e5341b4f8919..ee59409640f24 100644 --- a/Documentation/devicetree/bindings/reset/brcm,brcmstb-reset.txt +++ b/Documentation/devicetree/bindings/reset/brcm,brcmstb-reset.txt @@ -22,6 +22,6 @@ Example: }; ðernet_switch { - resets = <&reset>; + resets = <&reset 26>; reset-names = "switch"; }; -- GitLab From ce89d8d3a70fa530e16f0b0f8994385a214cd0c0 Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Mon, 4 Nov 2019 10:15:02 -0800 Subject: [PATCH 0366/1437] reset: brcmstb: Remove resource checks The use of IS_ALIGNED() is incorrect, the typical resource we pass looks like this: start: 0x8404318, size: 0x30. When using IS_ALIGNED() we will get the following 0x8404318 & (0x18 - 1) = 0x10 which is definitively not equal to 0, same goes with the size. These two checks would make the driver fail probing. Remove the resource checks, since there should be no constraint on the base addresse or size. Fixes: 77750bc089e4 ("reset: Add Broadcom STB SW_INIT reset controller driver") Signed-off-by: Florian Fainelli Signed-off-by: Philipp Zabel --- drivers/reset/reset-brcmstb.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/drivers/reset/reset-brcmstb.c b/drivers/reset/reset-brcmstb.c index a608f445dad6f..f213264c8567b 100644 --- a/drivers/reset/reset-brcmstb.c +++ b/drivers/reset/reset-brcmstb.c @@ -91,12 +91,6 @@ static int brcmstb_reset_probe(struct platform_device *pdev) return -ENOMEM; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!IS_ALIGNED(res->start, SW_INIT_BANK_SIZE) || - !IS_ALIGNED(resource_size(res), SW_INIT_BANK_SIZE)) { - dev_err(kdev, "incorrect register range\n"); - return -EINVAL; - } - priv->base = devm_ioremap_resource(kdev, res); if (IS_ERR(priv->base)) return PTR_ERR(priv->base); -- GitLab From 723c0011c7f6992f57e2c629fa9c89141acc115f Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Wed, 20 Nov 2019 15:26:13 +0100 Subject: [PATCH 0367/1437] reset: Fix {of,devm}_reset_control_array_get kerneldoc return types of_reset_control_array_get() and devm_reset_control_array_get() return struct reset_control pointers, not internal struct reset_control_array pointers, just like all other reset control API calls. Correct the kerneldoc to match the code. Fixes: 17c82e206d2a3cd8 ("reset: Add APIs to manage array of resets") Signed-off-by: Geert Uytterhoeven Signed-off-by: Philipp Zabel --- drivers/reset/core.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/reset/core.c b/drivers/reset/core.c index ca1d49146f611..c8cc8cacdade0 100644 --- a/drivers/reset/core.c +++ b/drivers/reset/core.c @@ -861,8 +861,7 @@ static int of_reset_control_get_count(struct device_node *node) * @acquired: only one reset control may be acquired for a given controller * and ID * - * Returns pointer to allocated reset_control_array on success or - * error on failure + * Returns pointer to allocated reset_control on success or error on failure */ struct reset_control * of_reset_control_array_get(struct device_node *np, bool shared, bool optional, @@ -915,8 +914,7 @@ EXPORT_SYMBOL_GPL(of_reset_control_array_get); * that just have to be asserted or deasserted, without any * requirements on the order. * - * Returns pointer to allocated reset_control_array on success or - * error on failure + * Returns pointer to allocated reset_control on success or error on failure */ struct reset_control * devm_reset_control_array_get(struct device *dev, bool shared, bool optional) -- GitLab From db23808615e29d9a04f96806cac56f78b0fee0ef Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Wed, 20 Nov 2019 15:59:26 +0100 Subject: [PATCH 0368/1437] reset: Do not register resource data for missing resets When an optional reset is not present, __devm_reset_control_get() and devm_reset_control_array_get() still register resource data to release the non-existing reset on cleanup, which is futile. Fix this by skipping NULL reset control pointers. Signed-off-by: Geert Uytterhoeven Signed-off-by: Philipp Zabel --- drivers/reset/core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/reset/core.c b/drivers/reset/core.c index c8cc8cacdade0..7597c70e04d5b 100644 --- a/drivers/reset/core.c +++ b/drivers/reset/core.c @@ -787,7 +787,7 @@ struct reset_control *__devm_reset_control_get(struct device *dev, return ERR_PTR(-ENOMEM); rstc = __reset_control_get(dev, id, index, shared, optional, acquired); - if (!IS_ERR(rstc)) { + if (!IS_ERR_OR_NULL(rstc)) { *ptr = rstc; devres_add(dev, ptr); } else { @@ -928,7 +928,7 @@ devm_reset_control_array_get(struct device *dev, bool shared, bool optional) return ERR_PTR(-ENOMEM); rstc = of_reset_control_array_get(dev->of_node, shared, optional, true); - if (IS_ERR(rstc)) { + if (IS_ERR_OR_NULL(rstc)) { devres_free(devres); return rstc; } -- GitLab From a470552ee8965da0fe6fd4df0aa39c4cda652c7c Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Tue, 10 Dec 2019 10:09:45 +0100 Subject: [PATCH 0369/1437] efi: Don't attempt to map RCI2 config table if it doesn't exist Commit: 1c5fecb61255aa12 ("efi: Export Runtime Configuration Interface table to sysfs") ... added support for a Dell specific UEFI configuration table, but failed to take into account that mapping the table should not be attempted unless the table actually exists. If it doesn't exist, the code usually fails silently unless pr_debug() prints are enabled. However, on 32-bit PAE x86, the splat below is produced due to the attempt to map the placeholder value EFI_INVALID_TABLE_ADDR which we use for non-existing UEFI configuration tables, and which equals ULONG_MAX. memremap attempted on mixed range 0x00000000ffffffff size: 0x1e WARNING: CPU: 1 PID: 1 at kernel/iomem.c:81 memremap+0x1a3/0x1c0 Modules linked in: CPU: 1 PID: 1 Comm: swapper/0 Not tainted 5.4.2-smp-mine #1 Hardware name: Hewlett-Packard HP Z400 Workstation/0B4Ch, BIOS 786G3 v03.61 03/05/2018 EIP: memremap+0x1a3/0x1c0 ... Call Trace: ? map_properties+0x473/0x473 ? efi_rci2_sysfs_init+0x2c/0x154 ? map_properties+0x473/0x473 ? do_one_initcall+0x49/0x1d4 ? parse_args+0x1e8/0x2a0 ? do_early_param+0x7a/0x7a ? kernel_init_freeable+0x139/0x1c2 ? rest_init+0x8e/0x8e ? kernel_init+0xd/0xf2 ? ret_from_fork+0x2e/0x38 Fix this by checking whether the table exists before attempting to map it. Reported-by: Richard Narron Tested-by: Richard Narron Signed-off-by: Ard Biesheuvel Cc: linux-efi@vger.kernel.org Fixes: 1c5fecb61255aa12 ("efi: Export Runtime Configuration Interface table to sysfs") Link: https://lkml.kernel.org/r/20191210090945.11501-2-ardb@kernel.org Signed-off-by: Ingo Molnar --- drivers/firmware/efi/rci2-table.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/firmware/efi/rci2-table.c b/drivers/firmware/efi/rci2-table.c index 76b0c354a0277..de1a9a1f9f146 100644 --- a/drivers/firmware/efi/rci2-table.c +++ b/drivers/firmware/efi/rci2-table.c @@ -81,6 +81,9 @@ static int __init efi_rci2_sysfs_init(void) struct kobject *tables_kobj; int ret = -ENOMEM; + if (rci2_table_phys == EFI_INVALID_TABLE_ADDR) + return 0; + rci2_base = memremap(rci2_table_phys, sizeof(struct rci2_table_global_hdr), MEMREMAP_WB); -- GitLab From 37a68eab4cd92b507c9e8afd760fdc18e4fecac6 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Thu, 24 Oct 2019 10:52:52 +0200 Subject: [PATCH 0370/1437] drm/nouveau: Move the declaration of struct nouveau_conn_atom up a bit Place the declaration of struct nouveau_conn_atom above that of struct nouveau_connector. This commit makes no changes to the moved block what so ever, it just moves it up a bit. This is a preparation patch to fix some issues with connector handling on pre nv50 displays (which do not use atomic modesetting). Signed-off-by: Hans de Goede Reviewed-by: Lyude Paul Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_connector.h | 110 ++++++++++---------- 1 file changed, 55 insertions(+), 55 deletions(-) diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.h b/drivers/gpu/drm/nouveau/nouveau_connector.h index f43a8d63aef86..de95884208848 100644 --- a/drivers/gpu/drm/nouveau/nouveau_connector.h +++ b/drivers/gpu/drm/nouveau/nouveau_connector.h @@ -29,6 +29,7 @@ #include +#include #include #include #include @@ -44,6 +45,60 @@ struct dcb_output; struct nouveau_backlight; #endif +#define nouveau_conn_atom(p) \ + container_of((p), struct nouveau_conn_atom, state) + +struct nouveau_conn_atom { + struct drm_connector_state state; + + struct { + /* The enum values specifically defined here match nv50/gf119 + * hw values, and the code relies on this. + */ + enum { + DITHERING_MODE_OFF = 0x00, + DITHERING_MODE_ON = 0x01, + DITHERING_MODE_DYNAMIC2X2 = 0x10 | DITHERING_MODE_ON, + DITHERING_MODE_STATIC2X2 = 0x18 | DITHERING_MODE_ON, + DITHERING_MODE_TEMPORAL = 0x20 | DITHERING_MODE_ON, + DITHERING_MODE_AUTO + } mode; + enum { + DITHERING_DEPTH_6BPC = 0x00, + DITHERING_DEPTH_8BPC = 0x02, + DITHERING_DEPTH_AUTO + } depth; + } dither; + + struct { + int mode; /* DRM_MODE_SCALE_* */ + struct { + enum { + UNDERSCAN_OFF, + UNDERSCAN_ON, + UNDERSCAN_AUTO, + } mode; + u32 hborder; + u32 vborder; + } underscan; + bool full; + } scaler; + + struct { + int color_vibrance; + int vibrant_hue; + } procamp; + + union { + struct { + bool dither:1; + bool scaler:1; + bool procamp:1; + }; + u8 mask; + } set; +}; + struct nouveau_connector { struct drm_connector base; enum dcb_connector_type type; @@ -121,61 +176,6 @@ extern int nouveau_ignorelid; extern int nouveau_duallink; extern int nouveau_hdmimhz; -#include -#define nouveau_conn_atom(p) \ - container_of((p), struct nouveau_conn_atom, state) - -struct nouveau_conn_atom { - struct drm_connector_state state; - - struct { - /* The enum values specifically defined here match nv50/gf119 - * hw values, and the code relies on this. - */ - enum { - DITHERING_MODE_OFF = 0x00, - DITHERING_MODE_ON = 0x01, - DITHERING_MODE_DYNAMIC2X2 = 0x10 | DITHERING_MODE_ON, - DITHERING_MODE_STATIC2X2 = 0x18 | DITHERING_MODE_ON, - DITHERING_MODE_TEMPORAL = 0x20 | DITHERING_MODE_ON, - DITHERING_MODE_AUTO - } mode; - enum { - DITHERING_DEPTH_6BPC = 0x00, - DITHERING_DEPTH_8BPC = 0x02, - DITHERING_DEPTH_AUTO - } depth; - } dither; - - struct { - int mode; /* DRM_MODE_SCALE_* */ - struct { - enum { - UNDERSCAN_OFF, - UNDERSCAN_ON, - UNDERSCAN_AUTO, - } mode; - u32 hborder; - u32 vborder; - } underscan; - bool full; - } scaler; - - struct { - int color_vibrance; - int vibrant_hue; - } procamp; - - union { - struct { - bool dither:1; - bool scaler:1; - bool procamp:1; - }; - u8 mask; - } set; -}; - void nouveau_conn_attach_properties(struct drm_connector *); void nouveau_conn_reset(struct drm_connector *); struct drm_connector_state * -- GitLab From 64d17f25dcad518461ccf0c260544e1e379c5b35 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Thu, 24 Oct 2019 10:52:53 +0200 Subject: [PATCH 0371/1437] drm/nouveau: Fix drm-core using atomic code-paths on pre-nv50 hardware We do not support atomic modesetting on pre-nv50 hardware, but until now our connector code was setting drm_connector->state on pre-nv50 hardware. This causes the core to enter atomic modesetting paths in at least: 1. drm_connector_get_encoder(), returning connector->state->best_encoder which is always 0, causing us to always report 0 as encoder_id in the drmModeConnector struct returned by drmModeGetConnector(). 2. drm_encoder_get_crtc(), returning NULL because uses_atomic get set, causing us to always report 0 as crtc_id in the drmModeEncoder struct returned by drmModeGetEncoder() Which in turn confuses userspace, at least plymouth thinks that the pipe has changed because of this and tries to reconfigure it unnecessarily. More in general we should not set drm_connector->state in the non-atomic code as this violates the drm-core's expectations. This commit fixes this by using a nouveau_conn_atom struct embedded in the nouveau_connector struct for property handling in the non-atomic case. Fixes: https://bugzilla.redhat.com/show_bug.cgi?id=1706557 Signed-off-by: Hans de Goede Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_connector.c | 28 +++++++++++++++------ drivers/gpu/drm/nouveau/nouveau_connector.h | 6 +++++ 2 files changed, 27 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.c b/drivers/gpu/drm/nouveau/nouveau_connector.c index 5b413588b8230..9a9a7f5003d3f 100644 --- a/drivers/gpu/drm/nouveau/nouveau_connector.c +++ b/drivers/gpu/drm/nouveau/nouveau_connector.c @@ -245,14 +245,22 @@ nouveau_conn_atomic_duplicate_state(struct drm_connector *connector) void nouveau_conn_reset(struct drm_connector *connector) { + struct nouveau_connector *nv_connector = nouveau_connector(connector); struct nouveau_conn_atom *asyc; - if (WARN_ON(!(asyc = kzalloc(sizeof(*asyc), GFP_KERNEL)))) - return; + if (drm_drv_uses_atomic_modeset(connector->dev)) { + if (WARN_ON(!(asyc = kzalloc(sizeof(*asyc), GFP_KERNEL)))) + return; + + if (connector->state) + nouveau_conn_atomic_destroy_state(connector, + connector->state); + + __drm_atomic_helper_connector_reset(connector, &asyc->state); + } else { + asyc = &nv_connector->properties_state; + } - if (connector->state) - nouveau_conn_atomic_destroy_state(connector, connector->state); - __drm_atomic_helper_connector_reset(connector, &asyc->state); asyc->dither.mode = DITHERING_MODE_AUTO; asyc->dither.depth = DITHERING_DEPTH_AUTO; asyc->scaler.mode = DRM_MODE_SCALE_NONE; @@ -276,8 +284,14 @@ void nouveau_conn_attach_properties(struct drm_connector *connector) { struct drm_device *dev = connector->dev; - struct nouveau_conn_atom *armc = nouveau_conn_atom(connector->state); struct nouveau_display *disp = nouveau_display(dev); + struct nouveau_connector *nv_connector = nouveau_connector(connector); + struct nouveau_conn_atom *armc; + + if (drm_drv_uses_atomic_modeset(connector->dev)) + armc = nouveau_conn_atom(connector->state); + else + armc = &nv_connector->properties_state; /* Init DVI-I specific properties. */ if (connector->connector_type == DRM_MODE_CONNECTOR_DVII) @@ -748,9 +762,9 @@ static int nouveau_connector_set_property(struct drm_connector *connector, struct drm_property *property, uint64_t value) { - struct nouveau_conn_atom *asyc = nouveau_conn_atom(connector->state); struct nouveau_connector *nv_connector = nouveau_connector(connector); struct nouveau_encoder *nv_encoder = nv_connector->detected_encoder; + struct nouveau_conn_atom *asyc = &nv_connector->properties_state; struct drm_encoder *encoder = to_drm_encoder(nv_encoder); int ret; diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.h b/drivers/gpu/drm/nouveau/nouveau_connector.h index de95884208848..de84fb4708c7a 100644 --- a/drivers/gpu/drm/nouveau/nouveau_connector.h +++ b/drivers/gpu/drm/nouveau/nouveau_connector.h @@ -118,6 +118,12 @@ struct nouveau_connector { #ifdef CONFIG_DRM_NOUVEAU_BACKLIGHT struct nouveau_backlight *backlight; #endif + /* + * Our connector property code expects a nouveau_conn_atom struct + * even on pre-nv50 where we do not support atomic. This embedded + * version gets used in the non atomic modeset case. + */ + struct nouveau_conn_atom properties_state; }; static inline struct nouveau_connector *nouveau_connector( -- GitLab From 310d35771ee9040f5744109fc277206ad96ba253 Mon Sep 17 00:00:00 2001 From: Lyude Paul Date: Fri, 15 Nov 2019 16:07:18 -0500 Subject: [PATCH 0372/1437] drm/nouveau/kms/nv50-: Call outp_atomic_check_view() before handling PBN Since nv50_outp_atomic_check_view() can set crtc_state->mode_changed, we probably should be calling it before handling any PBN changes. Just a precaution. Signed-off-by: Lyude Paul Fixes: 232c9eec417a ("drm/nouveau: Use atomic VCPI helpers for MST") Cc: Ben Skeggs Cc: Daniel Vetter Cc: David Airlie Cc: Jerry Zuo Cc: Harry Wentland Cc: Juston Li Cc: Sean Paul Cc: Laurent Pinchart Cc: # v5.1+ Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/dispnv50/disp.c | 44 ++++++++++++++----------- 1 file changed, 24 insertions(+), 20 deletions(-) diff --git a/drivers/gpu/drm/nouveau/dispnv50/disp.c b/drivers/gpu/drm/nouveau/dispnv50/disp.c index 549486f1d9376..6327aaf37c082 100644 --- a/drivers/gpu/drm/nouveau/dispnv50/disp.c +++ b/drivers/gpu/drm/nouveau/dispnv50/disp.c @@ -770,32 +770,36 @@ nv50_msto_atomic_check(struct drm_encoder *encoder, struct nv50_mstm *mstm = mstc->mstm; struct nv50_head_atom *asyh = nv50_head_atom(crtc_state); int slots; + int ret; - if (crtc_state->mode_changed || crtc_state->connectors_changed) { - /* - * When restoring duplicated states, we need to make sure that - * the bw remains the same and avoid recalculating it, as the - * connector's bpc may have changed after the state was - * duplicated - */ - if (!state->duplicated) { - const int bpp = connector->display_info.bpc * 3; - const int clock = crtc_state->adjusted_mode.clock; + ret = nv50_outp_atomic_check_view(encoder, crtc_state, conn_state, + mstc->native); + if (ret) + return ret; - asyh->dp.pbn = drm_dp_calc_pbn_mode(clock, bpp); - } + if (!crtc_state->mode_changed && !crtc_state->connectors_changed) + return 0; - slots = drm_dp_atomic_find_vcpi_slots(state, &mstm->mgr, - mstc->port, - asyh->dp.pbn); - if (slots < 0) - return slots; + /* + * When restoring duplicated states, we need to make sure that the bw + * remains the same and avoid recalculating it, as the connector's bpc + * may have changed after the state was duplicated + */ + if (!state->duplicated) { + const int bpp = connector->display_info.bpc * 3; + const int clock = crtc_state->adjusted_mode.clock; - asyh->dp.tu = slots; + asyh->dp.pbn = drm_dp_calc_pbn_mode(clock, bpp); } - return nv50_outp_atomic_check_view(encoder, crtc_state, conn_state, - mstc->native); + slots = drm_dp_atomic_find_vcpi_slots(state, &mstm->mgr, mstc->port, + asyh->dp.pbn); + if (slots < 0) + return slots; + + asyh->dp.tu = slots; + + return 0; } static void -- GitLab From ac2d9275f371346922b31a388bbaa6a54f1154a4 Mon Sep 17 00:00:00 2001 From: Lyude Paul Date: Fri, 15 Nov 2019 16:07:19 -0500 Subject: [PATCH 0373/1437] drm/nouveau/kms/nv50-: Store the bpc we're using in nv50_head_atom In order to be able to use bpc values that are different from what the connector reports, we want to be able to store the bpc value we decide on using for an atomic state in nv50_head_atom and refer to that instead of simply using the value that the connector reports throughout the whole atomic check phase and commit phase. This will let us (eventually) implement the max bpc connector property, and will also be needed for limiting the bpc we use on MST displays to 8 in the next commit. Signed-off-by: Lyude Paul Fixes: 232c9eec417a ("drm/nouveau: Use atomic VCPI helpers for MST") Cc: Ben Skeggs Cc: Daniel Vetter Cc: David Airlie Cc: Jerry Zuo Cc: Harry Wentland Cc: Juston Li Cc: Sean Paul Cc: Laurent Pinchart Cc: # v5.1+ Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/dispnv50/atom.h | 1 + drivers/gpu/drm/nouveau/dispnv50/disp.c | 57 ++++++++++++++----------- drivers/gpu/drm/nouveau/dispnv50/head.c | 5 +-- 3 files changed, 36 insertions(+), 27 deletions(-) diff --git a/drivers/gpu/drm/nouveau/dispnv50/atom.h b/drivers/gpu/drm/nouveau/dispnv50/atom.h index 43df86c38f58b..24f7700768dab 100644 --- a/drivers/gpu/drm/nouveau/dispnv50/atom.h +++ b/drivers/gpu/drm/nouveau/dispnv50/atom.h @@ -114,6 +114,7 @@ struct nv50_head_atom { u8 nhsync:1; u8 nvsync:1; u8 depth:4; + u8 bpc; } or; /* Currently only used for MST */ diff --git a/drivers/gpu/drm/nouveau/dispnv50/disp.c b/drivers/gpu/drm/nouveau/dispnv50/disp.c index 6327aaf37c082..93665aecce571 100644 --- a/drivers/gpu/drm/nouveau/dispnv50/disp.c +++ b/drivers/gpu/drm/nouveau/dispnv50/disp.c @@ -353,10 +353,20 @@ nv50_outp_atomic_check(struct drm_encoder *encoder, struct drm_crtc_state *crtc_state, struct drm_connector_state *conn_state) { - struct nouveau_connector *nv_connector = - nouveau_connector(conn_state->connector); - return nv50_outp_atomic_check_view(encoder, crtc_state, conn_state, - nv_connector->native_mode); + struct drm_connector *connector = conn_state->connector; + struct nouveau_connector *nv_connector = nouveau_connector(connector); + struct nv50_head_atom *asyh = nv50_head_atom(crtc_state); + int ret; + + ret = nv50_outp_atomic_check_view(encoder, crtc_state, conn_state, + nv_connector->native_mode); + if (ret) + return ret; + + if (crtc_state->mode_changed || crtc_state->connectors_changed) + asyh->or.bpc = connector->display_info.bpc; + + return 0; } /****************************************************************************** @@ -786,10 +796,10 @@ nv50_msto_atomic_check(struct drm_encoder *encoder, * may have changed after the state was duplicated */ if (!state->duplicated) { - const int bpp = connector->display_info.bpc * 3; const int clock = crtc_state->adjusted_mode.clock; - asyh->dp.pbn = drm_dp_calc_pbn_mode(clock, bpp); + asyh->or.bpc = connector->display_info.bpc; + asyh->dp.pbn = drm_dp_calc_pbn_mode(clock, asyh->or.bpc * 3); } slots = drm_dp_atomic_find_vcpi_slots(state, &mstm->mgr, mstc->port, @@ -802,6 +812,17 @@ nv50_msto_atomic_check(struct drm_encoder *encoder, return 0; } +static u8 +nv50_dp_bpc_to_depth(unsigned int bpc) +{ + switch (bpc) { + case 6: return 0x2; + case 8: return 0x5; + case 10: /* fall-through */ + default: return 0x6; + } +} + static void nv50_msto_enable(struct drm_encoder *encoder) { @@ -812,7 +833,7 @@ nv50_msto_enable(struct drm_encoder *encoder) struct nv50_mstm *mstm = NULL; struct drm_connector *connector; struct drm_connector_list_iter conn_iter; - u8 proto, depth; + u8 proto; bool r; drm_connector_list_iter_begin(encoder->dev, &conn_iter); @@ -841,14 +862,8 @@ nv50_msto_enable(struct drm_encoder *encoder) else proto = 0x9; - switch (mstc->connector.display_info.bpc) { - case 6: depth = 0x2; break; - case 8: depth = 0x5; break; - case 10: - default: depth = 0x6; break; - } - - mstm->outp->update(mstm->outp, head->base.index, armh, proto, depth); + mstm->outp->update(mstm->outp, head->base.index, armh, proto, + nv50_dp_bpc_to_depth(armh->or.bpc)); msto->head = head; msto->mstc = mstc; @@ -1502,20 +1517,14 @@ nv50_sor_enable(struct drm_encoder *encoder) lvds.lvds.script |= 0x0200; } - if (nv_connector->base.display_info.bpc == 8) + if (asyh->or.bpc == 8) lvds.lvds.script |= 0x0200; } nvif_mthd(&disp->disp->object, 0, &lvds, sizeof(lvds)); break; case DCB_OUTPUT_DP: - if (nv_connector->base.display_info.bpc == 6) - depth = 0x2; - else - if (nv_connector->base.display_info.bpc == 8) - depth = 0x5; - else - depth = 0x6; + depth = nv50_dp_bpc_to_depth(asyh->or.bpc); if (nv_encoder->link & 1) proto = 0x8; @@ -1666,7 +1675,7 @@ nv50_pior_enable(struct drm_encoder *encoder) nv50_outp_acquire(nv_encoder); nv_connector = nouveau_encoder_connector_get(nv_encoder); - switch (nv_connector->base.display_info.bpc) { + switch (asyh->or.bpc) { case 10: asyh->or.depth = 0x6; break; case 8: asyh->or.depth = 0x5; break; case 6: asyh->or.depth = 0x2; break; diff --git a/drivers/gpu/drm/nouveau/dispnv50/head.c b/drivers/gpu/drm/nouveau/dispnv50/head.c index 71c23bf1fe259..c9692df2b76cc 100644 --- a/drivers/gpu/drm/nouveau/dispnv50/head.c +++ b/drivers/gpu/drm/nouveau/dispnv50/head.c @@ -81,18 +81,17 @@ nv50_head_atomic_check_dither(struct nv50_head_atom *armh, struct nv50_head_atom *asyh, struct nouveau_conn_atom *asyc) { - struct drm_connector *connector = asyc->state.connector; u32 mode = 0x00; if (asyc->dither.mode == DITHERING_MODE_AUTO) { - if (asyh->base.depth > connector->display_info.bpc * 3) + if (asyh->base.depth > asyh->or.bpc * 3) mode = DITHERING_MODE_DYNAMIC2X2; } else { mode = asyc->dither.mode; } if (asyc->dither.depth == DITHERING_DEPTH_AUTO) { - if (connector->display_info.bpc >= 8) + if (asyh->or.bpc >= 8) mode |= DITHERING_DEPTH_8BPC; } else { mode |= asyc->dither.depth; -- GitLab From ae5769d4670982bc483885b120b557a9ffd57527 Mon Sep 17 00:00:00 2001 From: Lyude Paul Date: Fri, 15 Nov 2019 16:07:20 -0500 Subject: [PATCH 0374/1437] drm/nouveau/kms/nv50-: Limit MST BPC to 8 Noticed this while working on some unrelated CRC stuff. Currently, userspace has very little support for BPCs higher than 8. While this doesn't matter for most things, on MST topologies we need to be careful about ensuring that we do our best to make any given display configuration fit within the bandwidth restraints of the topology, since otherwise less people's monitor configurations will work. Allowing for BPC settings higher than 8 dramatically increases the required bandwidth for displays in most configurations, and consequently makes it a lot less likely that said display configurations will pass the atomic check. In the future we want to fix this correctly by making it so that we adjust the bpp for each display in a topology to be as high as possible, while making sure to lower the bpp of each display in the event that we run out of bandwidth and need to rerun our atomic check. But for now, follow the behavior that both i915 and amdgpu are sticking to. Signed-off-by: Lyude Paul Fixes: 232c9eec417a ("drm/nouveau: Use atomic VCPI helpers for MST") Cc: Ben Skeggs Cc: Daniel Vetter Cc: David Airlie Cc: Jerry Zuo Cc: Harry Wentland Cc: Juston Li Cc: Sam Ravnborg Cc: Sean Paul Cc: # v5.1+ Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/dispnv50/disp.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/nouveau/dispnv50/disp.c b/drivers/gpu/drm/nouveau/dispnv50/disp.c index 93665aecce571..9ac47fe519f88 100644 --- a/drivers/gpu/drm/nouveau/dispnv50/disp.c +++ b/drivers/gpu/drm/nouveau/dispnv50/disp.c @@ -798,7 +798,14 @@ nv50_msto_atomic_check(struct drm_encoder *encoder, if (!state->duplicated) { const int clock = crtc_state->adjusted_mode.clock; - asyh->or.bpc = connector->display_info.bpc; + /* + * XXX: Since we don't use HDR in userspace quite yet, limit + * the bpc to 8 to save bandwidth on the topology. In the + * future, we'll want to properly fix this by dynamically + * selecting the highest possible bpc that would fit in the + * topology + */ + asyh->or.bpc = min(connector->display_info.bpc, 8U); asyh->dp.pbn = drm_dp_calc_pbn_mode(clock, asyh->or.bpc * 3); } -- GitLab From 3d1890ef8023e61934e070021b06cc9f417260c0 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Tue, 10 Dec 2019 12:15:44 +1000 Subject: [PATCH 0375/1437] drm/nouveau/kms/nv50-: fix panel scaling Under certain circumstances, encoder atomic_check() can be entered without adjusted_mode having been reset to the same as mode, which confuses the scaling logic and can lead to a misprogrammed display. Fix this by checking against the user-provided mode directly. Link: https://bugs.freedesktop.org/show_bug.cgi?id=108615 Link: https://gitlab.freedesktop.org/xorg/driver/xf86-video-nouveau/issues/464 Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/dispnv50/disp.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/nouveau/dispnv50/disp.c b/drivers/gpu/drm/nouveau/dispnv50/disp.c index 9ac47fe519f88..63425e2460189 100644 --- a/drivers/gpu/drm/nouveau/dispnv50/disp.c +++ b/drivers/gpu/drm/nouveau/dispnv50/disp.c @@ -326,9 +326,9 @@ nv50_outp_atomic_check_view(struct drm_encoder *encoder, * same size as the native one (e.g. different * refresh rate) */ - if (adjusted_mode->hdisplay == native_mode->hdisplay && - adjusted_mode->vdisplay == native_mode->vdisplay && - adjusted_mode->type & DRM_MODE_TYPE_DRIVER) + if (mode->hdisplay == native_mode->hdisplay && + mode->vdisplay == native_mode->vdisplay && + mode->type & DRM_MODE_TYPE_DRIVER) break; mode = native_mode; asyc->scaler.full = true; -- GitLab From 7c5a2df3367a2c4984f1300261345817d95b71f8 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 10 Dec 2019 12:26:01 +0100 Subject: [PATCH 0376/1437] USB: serial: io_edgeport: fix epic endpoint lookup Make sure to use the current alternate setting when looking up the endpoints on epic devices to avoid binding to an invalid interface. Failing to do so could cause the driver to misbehave or trigger a WARN() in usb_submit_urb() that kernels with panic_on_warn set would choke on. Fixes: 6e8cf7751f9f ("USB: add EPIC support to the io_edgeport driver") Cc: stable # 2.6.21 Signed-off-by: Johan Hovold Link: https://lore.kernel.org/r/20191210112601.3561-5-johan@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/io_edgeport.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/usb/serial/io_edgeport.c b/drivers/usb/serial/io_edgeport.c index 48a439298a68f..9690a5f4b9d61 100644 --- a/drivers/usb/serial/io_edgeport.c +++ b/drivers/usb/serial/io_edgeport.c @@ -2901,16 +2901,18 @@ static int edge_startup(struct usb_serial *serial) response = 0; if (edge_serial->is_epic) { + struct usb_host_interface *alt; + + alt = serial->interface->cur_altsetting; + /* EPIC thing, set up our interrupt polling now and our read * urb, so that the device knows it really is connected. */ interrupt_in_found = bulk_in_found = bulk_out_found = false; - for (i = 0; i < serial->interface->altsetting[0] - .desc.bNumEndpoints; ++i) { + for (i = 0; i < alt->desc.bNumEndpoints; ++i) { struct usb_endpoint_descriptor *endpoint; int buffer_size; - endpoint = &serial->interface->altsetting[0]. - endpoint[i].desc; + endpoint = &alt->endpoint[i].desc; buffer_size = usb_endpoint_maxp(endpoint); if (!interrupt_in_found && (usb_endpoint_is_int_in(endpoint))) { -- GitLab From 59920635b89d74b9207ea803d5e91498d39e8b69 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 10 Dec 2019 12:26:00 +0100 Subject: [PATCH 0377/1437] USB: idmouse: fix interface sanity checks Make sure to use the current alternate setting when verifying the interface descriptors to avoid binding to an invalid interface. Failing to do so could cause the driver to misbehave or trigger a WARN() in usb_submit_urb() that kernels with panic_on_warn set would choke on. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Cc: stable Signed-off-by: Johan Hovold Link: https://lore.kernel.org/r/20191210112601.3561-4-johan@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/idmouse.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/misc/idmouse.c b/drivers/usb/misc/idmouse.c index 4afb5ddfd361f..e9437a176518a 100644 --- a/drivers/usb/misc/idmouse.c +++ b/drivers/usb/misc/idmouse.c @@ -322,7 +322,7 @@ static int idmouse_probe(struct usb_interface *interface, int result; /* check if we have gotten the data or the hid interface */ - iface_desc = &interface->altsetting[0]; + iface_desc = interface->cur_altsetting; if (iface_desc->desc.bInterfaceClass != 0x0A) return -ENODEV; -- GitLab From 3c11c4bed02b202e278c0f5c319ae435d7fb9815 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 10 Dec 2019 12:25:59 +0100 Subject: [PATCH 0378/1437] USB: adutux: fix interface sanity check Make sure to use the current alternate setting when verifying the interface descriptors to avoid binding to an invalid interface. Failing to do so could cause the driver to misbehave or trigger a WARN() in usb_submit_urb() that kernels with panic_on_warn set would choke on. Fixes: 03270634e242 ("USB: Add ADU support for Ontrak ADU devices") Cc: stable # 2.6.19 Signed-off-by: Johan Hovold Link: https://lore.kernel.org/r/20191210112601.3561-3-johan@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/adutux.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/misc/adutux.c b/drivers/usb/misc/adutux.c index 6f5edb9fc61e0..d8d157c4c271d 100644 --- a/drivers/usb/misc/adutux.c +++ b/drivers/usb/misc/adutux.c @@ -669,7 +669,7 @@ static int adu_probe(struct usb_interface *interface, init_waitqueue_head(&dev->read_wait); init_waitqueue_head(&dev->write_wait); - res = usb_find_common_endpoints_reverse(&interface->altsetting[0], + res = usb_find_common_endpoints_reverse(interface->cur_altsetting, NULL, NULL, &dev->interrupt_in_endpoint, &dev->interrupt_out_endpoint); -- GitLab From 09068c1ad53fb077bdac288869dec2435420bdc4 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 10 Dec 2019 12:25:58 +0100 Subject: [PATCH 0379/1437] USB: atm: ueagle-atm: add missing endpoint check Make sure that the interrupt interface has an endpoint before trying to access its endpoint descriptors to avoid dereferencing a NULL pointer. The driver binds to the interrupt interface with interface number 0, but must not assume that this interface or its current alternate setting are the first entries in the corresponding configuration arrays. Fixes: b72458a80c75 ("[PATCH] USB: Eagle and ADI 930 usb adsl modem driver") Cc: stable # 2.6.16 Signed-off-by: Johan Hovold Link: https://lore.kernel.org/r/20191210112601.3561-2-johan@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/atm/ueagle-atm.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/drivers/usb/atm/ueagle-atm.c b/drivers/usb/atm/ueagle-atm.c index 8b0ea8c70d730..635cf0466b592 100644 --- a/drivers/usb/atm/ueagle-atm.c +++ b/drivers/usb/atm/ueagle-atm.c @@ -2124,10 +2124,11 @@ resubmit: /* * Start the modem : init the data and start kernel thread */ -static int uea_boot(struct uea_softc *sc) +static int uea_boot(struct uea_softc *sc, struct usb_interface *intf) { - int ret, size; struct intr_pkt *intr; + int ret = -ENOMEM; + int size; uea_enters(INS_TO_USBDEV(sc)); @@ -2152,6 +2153,11 @@ static int uea_boot(struct uea_softc *sc) if (UEA_CHIP_VERSION(sc) == ADI930) load_XILINX_firmware(sc); + if (intf->cur_altsetting->desc.bNumEndpoints < 1) { + ret = -ENODEV; + goto err0; + } + intr = kmalloc(size, GFP_KERNEL); if (!intr) goto err0; @@ -2163,8 +2169,7 @@ static int uea_boot(struct uea_softc *sc) usb_fill_int_urb(sc->urb_int, sc->usb_dev, usb_rcvintpipe(sc->usb_dev, UEA_INTR_PIPE), intr, size, uea_intr, sc, - sc->usb_dev->actconfig->interface[0]->altsetting[0]. - endpoint[0].desc.bInterval); + intf->cur_altsetting->endpoint[0].desc.bInterval); ret = usb_submit_urb(sc->urb_int, GFP_KERNEL); if (ret < 0) { @@ -2179,6 +2184,7 @@ static int uea_boot(struct uea_softc *sc) sc->kthread = kthread_create(uea_kthread, sc, "ueagle-atm"); if (IS_ERR(sc->kthread)) { uea_err(INS_TO_USBDEV(sc), "failed to create thread\n"); + ret = PTR_ERR(sc->kthread); goto err2; } @@ -2193,7 +2199,7 @@ err1: kfree(intr); err0: uea_leaves(INS_TO_USBDEV(sc)); - return -ENOMEM; + return ret; } /* @@ -2548,7 +2554,7 @@ static int uea_bind(struct usbatm_data *usbatm, struct usb_interface *intf, } } - ret = uea_boot(sc); + ret = uea_boot(sc, intf); if (ret < 0) goto error; -- GitLab From 74ca34118a0e05793935d804ccffcedd6eb56596 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 10 Dec 2019 12:47:50 +0100 Subject: [PATCH 0380/1437] staging: rtl8188eu: fix interface sanity check Make sure to use the current alternate setting when verifying the interface descriptors to avoid binding to an invalid interface. Failing to do so could cause the driver to misbehave or trigger a WARN() in usb_submit_urb() that kernels with panic_on_warn set would choke on. Fixes: c2478d39076b ("staging: r8188eu: Add files for new driver - part 20") Cc: stable # 3.12 Signed-off-by: Johan Hovold Link: https://lore.kernel.org/r/20191210114751.5119-2-johan@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8188eu/os_dep/usb_intf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/rtl8188eu/os_dep/usb_intf.c b/drivers/staging/rtl8188eu/os_dep/usb_intf.c index 4fac9dca798e8..a7cac0719b8bc 100644 --- a/drivers/staging/rtl8188eu/os_dep/usb_intf.c +++ b/drivers/staging/rtl8188eu/os_dep/usb_intf.c @@ -70,7 +70,7 @@ static struct dvobj_priv *usb_dvobj_init(struct usb_interface *usb_intf) phost_conf = pusbd->actconfig; pconf_desc = &phost_conf->desc; - phost_iface = &usb_intf->altsetting[0]; + phost_iface = usb_intf->cur_altsetting; piface_desc = &phost_iface->desc; pdvobjpriv->NumInterfaces = pconf_desc->bNumInterfaces; -- GitLab From c724f776f048538ecfdf53a52b7a522309f5c504 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 10 Dec 2019 12:47:51 +0100 Subject: [PATCH 0381/1437] staging: rtl8712: fix interface sanity check Make sure to use the current alternate setting when verifying the interface descriptors to avoid binding to an invalid interface. Failing to do so could cause the driver to misbehave or trigger a WARN() in usb_submit_urb() that kernels with panic_on_warn set would choke on. Fixes: 2865d42c78a9 ("staging: r8712u: Add the new driver to the mainline kernel") Cc: stable # 2.6.37 Signed-off-by: Johan Hovold Link: https://lore.kernel.org/r/20191210114751.5119-3-johan@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8712/usb_intf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/rtl8712/usb_intf.c b/drivers/staging/rtl8712/usb_intf.c index ba1288297ee4b..a87562f632a7f 100644 --- a/drivers/staging/rtl8712/usb_intf.c +++ b/drivers/staging/rtl8712/usb_intf.c @@ -247,7 +247,7 @@ static uint r8712_usb_dvobj_init(struct _adapter *padapter) pdvobjpriv->padapter = padapter; padapter->eeprom_address_size = 6; - phost_iface = &pintf->altsetting[0]; + phost_iface = pintf->cur_altsetting; piface_desc = &phost_iface->desc; pdvobjpriv->nr_endpoint = piface_desc->bNumEndpoints; if (pusbd->speed == USB_SPEED_HIGH) { -- GitLab From 8c7d4b7b3d43c54c0b8c1e4adb917a151c754196 Mon Sep 17 00:00:00 2001 From: Tejas Joglekar Date: Wed, 13 Nov 2019 11:45:16 +0530 Subject: [PATCH 0382/1437] usb: dwc3: gadget: Fix logical condition This patch corrects the condition to kick the transfer without giving back the requests when either request has remaining data or when there are pending SGs. The && check was introduced during spliting up the dwc3_gadget_ep_cleanup_completed_requests() function. Fixes: f38e35dd84e2 ("usb: dwc3: gadget: split dwc3_gadget_ep_cleanup_completed_requests()") Cc: stable@vger.kernel.org Signed-off-by: Tejas Joglekar Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/gadget.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index a9aba716bf80b..6ff6ee4fe5aae 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -2491,7 +2491,7 @@ static int dwc3_gadget_ep_cleanup_completed_request(struct dwc3_ep *dep, req->request.actual = req->request.length - req->remaining; - if (!dwc3_gadget_ep_request_completed(req) && + if (!dwc3_gadget_ep_request_completed(req) || req->num_pending_sgs) { __dwc3_gadget_kick_transfer(dep); goto out; -- GitLab From d3abda5a98a18e524e17fd4085c9f4bd53e9ef53 Mon Sep 17 00:00:00 2001 From: Thinh Nguyen Date: Wed, 27 Nov 2019 13:10:47 -0800 Subject: [PATCH 0383/1437] usb: dwc3: gadget: Clear started flag for non-IOC Normally the END_TRANSFER command completion handler will clear the DWC3_EP_TRANSFER_STARTED flag. However, if the command was sent without interrupt on completion, then the flag will not be cleared. Make sure to clear the flag in this case. Cc: stable@vger.kernel.org Signed-off-by: Thinh Nguyen Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/gadget.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 6ff6ee4fe5aae..0c960a97ea021 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -2719,6 +2719,9 @@ static void dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force, WARN_ON_ONCE(ret); dep->resource_index = 0; + if (!interrupt) + dep->flags &= ~DWC3_EP_TRANSFER_STARTED; + if (dwc3_is_usb31(dwc) || dwc->revision < DWC3_REVISION_310A) udelay(100); } -- GitLab From 2d7b78f59e020b07fc6338eefe286f54ee2d6773 Mon Sep 17 00:00:00 2001 From: Thinh Nguyen Date: Wed, 27 Nov 2019 13:10:54 -0800 Subject: [PATCH 0384/1437] usb: dwc3: ep0: Clear started flag on completion Clear ep0's DWC3_EP_TRANSFER_STARTED flag if the END_TRANSFER command is completed. Otherwise, we can't start control transfer again after END_TRANSFER. Cc: stable@vger.kernel.org Signed-off-by: Thinh Nguyen Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/ep0.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c index 3996b9c4ff8d4..fd1b100d2927e 100644 --- a/drivers/usb/dwc3/ep0.c +++ b/drivers/usb/dwc3/ep0.c @@ -1117,6 +1117,9 @@ static void dwc3_ep0_xfernotready(struct dwc3 *dwc, void dwc3_ep0_interrupt(struct dwc3 *dwc, const struct dwc3_event_depevt *event) { + struct dwc3_ep *dep = dwc->eps[event->endpoint_number]; + u8 cmd; + switch (event->endpoint_event) { case DWC3_DEPEVT_XFERCOMPLETE: dwc3_ep0_xfer_complete(dwc, event); @@ -1129,7 +1132,12 @@ void dwc3_ep0_interrupt(struct dwc3 *dwc, case DWC3_DEPEVT_XFERINPROGRESS: case DWC3_DEPEVT_RXTXFIFOEVT: case DWC3_DEPEVT_STREAMEVT: + break; case DWC3_DEPEVT_EPCMDCMPLT: + cmd = DEPEVT_PARAMETER_CMD(event->parameters); + + if (cmd == DWC3_DEPCMD_ENDTRANSFER) + dep->flags &= ~DWC3_EP_TRANSFER_STARTED; break; } } -- GitLab From 51ba8b3a0e1ab5240e6ed5b73995d0cf16b05b52 Mon Sep 17 00:00:00 2001 From: Saravana Kannan Date: Mon, 9 Dec 2019 11:31:19 -0800 Subject: [PATCH 0385/1437] of/platform: Unconditionally pause/resume sync state during kernel init Commit 5e6669387e22 ("of/platform: Pause/resume sync state during init and of_platform_populate()") paused/resumed sync state during init only if Linux had parsed and populated a devicetree. However, the check for that (of_have_populated_dt()) can change after of_platform_default_populate_init() executes. One example of this is when devicetree unittests are enabled. This causes an unmatched pause/resume of sync state. To avoid this, just unconditionally pause/resume sync state during init. Fixes: 5e6669387e22 ("of/platform: Pause/resume sync state during init and of_platform_populate()") Reported-by: kernel test robot Signed-off-by: Saravana Kannan Reviewed-by: Frank Rowand Link: https://lore.kernel.org/r/20191209193119.147056-1-saravanak@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/of/platform.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/of/platform.c b/drivers/of/platform.c index d93891a05f603..3371e4a062483 100644 --- a/drivers/of/platform.c +++ b/drivers/of/platform.c @@ -518,10 +518,11 @@ static int __init of_platform_default_populate_init(void) { struct device_node *node; + device_links_supplier_sync_state_pause(); + if (!of_have_populated_dt()) return -ENODEV; - device_links_supplier_sync_state_pause(); /* * Handle certain compatibles explicitly, since we don't want to create * platform_devices for every node in /reserved-memory with a @@ -545,8 +546,7 @@ arch_initcall_sync(of_platform_default_populate_init); static int __init of_platform_sync_state_init(void) { - if (of_have_populated_dt()) - device_links_supplier_sync_state_resume(); + device_links_supplier_sync_state_resume(); return 0; } late_initcall_sync(of_platform_sync_state_init); -- GitLab From e5b5da96da50ef30abb39cb9f694e99366404d24 Mon Sep 17 00:00:00 2001 From: EJ Hsu Date: Tue, 3 Dec 2019 23:34:56 -0800 Subject: [PATCH 0386/1437] usb: gadget: fix wrong endpoint desc Gadget driver should always use config_ep_by_speed() to initialize usb_ep struct according to usb device's operating speed. Otherwise, usb_ep struct may be wrong if usb devcie's operating speed is changed. The key point in this patch is that we want to make sure the desc pointer in usb_ep struct will be set to NULL when gadget is disconnected. This will force it to call config_ep_by_speed() to correctly initialize usb_ep struct based on the new operating speed when gadget is re-connected later. Reviewed-by: Peter Chen Signed-off-by: EJ Hsu Signed-off-by: Felipe Balbi --- drivers/usb/gadget/function/f_ecm.c | 6 +++++- drivers/usb/gadget/function/f_rndis.c | 1 + 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/usb/gadget/function/f_ecm.c b/drivers/usb/gadget/function/f_ecm.c index 6ce044008cf6c..460d5d7c984f5 100644 --- a/drivers/usb/gadget/function/f_ecm.c +++ b/drivers/usb/gadget/function/f_ecm.c @@ -621,8 +621,12 @@ static void ecm_disable(struct usb_function *f) DBG(cdev, "ecm deactivated\n"); - if (ecm->port.in_ep->enabled) + if (ecm->port.in_ep->enabled) { gether_disconnect(&ecm->port); + } else { + ecm->port.in_ep->desc = NULL; + ecm->port.out_ep->desc = NULL; + } usb_ep_disable(ecm->notify); ecm->notify->desc = NULL; diff --git a/drivers/usb/gadget/function/f_rndis.c b/drivers/usb/gadget/function/f_rndis.c index d48df36622b74..0d8e4a364ca6e 100644 --- a/drivers/usb/gadget/function/f_rndis.c +++ b/drivers/usb/gadget/function/f_rndis.c @@ -618,6 +618,7 @@ static void rndis_disable(struct usb_function *f) gether_disconnect(&rndis->port); usb_ep_disable(rndis->notify); + rndis->notify->desc = NULL; } /*-------------------------------------------------------------------------*/ -- GitLab From 76d2703649321c296df7ec0dafd50add96215de4 Mon Sep 17 00:00:00 2001 From: Dragos Tarcatu Date: Mon, 9 Dec 2019 18:39:38 -0600 Subject: [PATCH 0387/1437] ASoC: topology: Check return value for snd_soc_add_dai_link() snd_soc_add_dai_link() might fail. This situation occurs for instance in a very specific use case where a PCM device and a Back End DAI link are given identical names in the topology. When this happens, soc_new_pcm_runtime() fails and then snd_soc_add_dai_link() returns -ENOMEM when called from soc_tplg_fe_link_create(). Because of that, the link will not get added into the card list, so any attempt to remove it later ends up in a panic. Fix that by checking the return status and free the memory in case of an error. Reviewed-by: Ranjani Sridharan Signed-off-by: Dragos Tarcatu Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20191210003939.15752-2-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/soc-topology.c | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/sound/soc/soc-topology.c b/sound/soc/soc-topology.c index 81d2af000a5c8..65c2796b6e02c 100644 --- a/sound/soc/soc-topology.c +++ b/sound/soc/soc-topology.c @@ -1933,11 +1933,13 @@ static int soc_tplg_fe_link_create(struct soc_tplg *tplg, ret = soc_tplg_dai_link_load(tplg, link, NULL); if (ret < 0) { dev_err(tplg->comp->dev, "ASoC: FE link loading failed\n"); - kfree(link->name); - kfree(link->stream_name); - kfree(link->cpus->dai_name); - kfree(link); - return ret; + goto err; + } + + ret = snd_soc_add_dai_link(tplg->comp->card, link); + if (ret < 0) { + dev_err(tplg->comp->dev, "ASoC: adding FE link failed\n"); + goto err; } link->dobj.index = tplg->index; @@ -1945,8 +1947,13 @@ static int soc_tplg_fe_link_create(struct soc_tplg *tplg, link->dobj.type = SND_SOC_DOBJ_DAI_LINK; list_add(&link->dobj.list, &tplg->comp->dobj_list); - snd_soc_add_dai_link(tplg->comp->card, link); return 0; +err: + kfree(link->name); + kfree(link->stream_name); + kfree(link->cpus->dai_name); + kfree(link); + return ret; } /* create a FE DAI and DAI link from the PCM object */ -- GitLab From a3039aef52d9ffeb67e9211899cd3e8a2953a01f Mon Sep 17 00:00:00 2001 From: Dragos Tarcatu Date: Mon, 9 Dec 2019 18:39:39 -0600 Subject: [PATCH 0388/1437] ASoC: topology: Check return value for soc_tplg_pcm_create() The return value of soc_tplg_pcm_create() is currently not checked in soc_tplg_pcm_elems_load(). If an error is to occur there, the topology ignores it and continues loading. Fix that by checking the status and rejecting the topology on error. Reviewed-by: Ranjani Sridharan Signed-off-by: Dragos Tarcatu Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20191210003939.15752-3-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/soc-topology.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/sound/soc/soc-topology.c b/sound/soc/soc-topology.c index 65c2796b6e02c..b28613149b0cc 100644 --- a/sound/soc/soc-topology.c +++ b/sound/soc/soc-topology.c @@ -2046,6 +2046,7 @@ static int soc_tplg_pcm_elems_load(struct soc_tplg *tplg, int size; int i; bool abi_match; + int ret; count = le32_to_cpu(hdr->count); @@ -2087,7 +2088,12 @@ static int soc_tplg_pcm_elems_load(struct soc_tplg *tplg, } /* create the FE DAIs and DAI links */ - soc_tplg_pcm_create(tplg, _pcm); + ret = soc_tplg_pcm_create(tplg, _pcm); + if (ret < 0) { + if (!abi_match) + kfree(_pcm); + return ret; + } /* offset by version-specific struct size and * real priv data size -- GitLab From 5525cf07d15f7c7eab619707627c31aa8e39dff1 Mon Sep 17 00:00:00 2001 From: Daniel Baluta Date: Mon, 9 Dec 2019 15:53:53 +0200 Subject: [PATCH 0389/1437] ASoC: simple-card: Don't create separate link when platform is present In normal sound case all DAIs are detected as CPU-Codec. simple_dai_link_of supports the presence of a platform but it counts it as a CPU DAI resulting in the creation of an extra link. Adding a platform property to a link description like: simple-audio-card,dai-link { cpu { sound-dai = <&sai1>; }; plat { sound-dai = <&dsp>; }; codec { sound-dai = <&wm8960>; } will result in the creation of two links: * sai1 <-> wm8960 * dsp <-> wm8960 which is obviously not what we want. We just want one single link with: * sai1 <-> wm8960 (and platform set to dsp). Signed-off-by: Daniel Baluta Acked-by: Kuninori Morimoto Link: https://lore.kernel.org/r/20191209135353.17427-1-daniel.baluta@nxp.com Signed-off-by: Mark Brown --- sound/soc/generic/simple-card.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/sound/soc/generic/simple-card.c b/sound/soc/generic/simple-card.c index 10b82bf043d1a..55e9f8800b3e1 100644 --- a/sound/soc/generic/simple-card.c +++ b/sound/soc/generic/simple-card.c @@ -371,6 +371,7 @@ static int simple_for_each_link(struct asoc_simple_priv *priv, do { struct asoc_simple_data adata; struct device_node *codec; + struct device_node *plat; struct device_node *np; int num = of_get_child_count(node); @@ -381,6 +382,9 @@ static int simple_for_each_link(struct asoc_simple_priv *priv, ret = -ENODEV; goto error; } + /* get platform */ + plat = of_get_child_by_name(node, is_top ? + PREFIX "plat" : "plat"); /* get convert-xxx property */ memset(&adata, 0, sizeof(adata)); @@ -389,6 +393,8 @@ static int simple_for_each_link(struct asoc_simple_priv *priv, /* loop for all CPU/Codec node */ for_each_child_of_node(node, np) { + if (plat == np) + continue; /* * It is DPCM * if it has many CPUs, -- GitLab From 8edc95667646a75f0fc97e08ecb180581fdff300 Mon Sep 17 00:00:00 2001 From: Karol Trzcinski Date: Mon, 9 Dec 2019 18:48:48 -0600 Subject: [PATCH 0390/1437] ASoC: SOF: loader: snd_sof_fw_parse_ext_data log warning on unknown header Added warning log when found some unknown FW boot ext header, to improve debuggability. Signed-off-by: Karol Trzcinski Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20191210004854.16845-3-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/loader.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/soc/sof/loader.c b/sound/soc/sof/loader.c index 9a9a381a908de..a041adf0669de 100644 --- a/sound/soc/sof/loader.c +++ b/sound/soc/sof/loader.c @@ -66,6 +66,8 @@ int snd_sof_fw_parse_ext_data(struct snd_sof_dev *sdev, u32 bar, u32 offset) ret = get_ext_windows(sdev, ext_hdr); break; default: + dev_warn(sdev->dev, "warning: unknown ext header type %d size 0x%x\n", + ext_hdr->type, ext_hdr->hdr.size); break; } -- GitLab From 6bb03c21e4bfee29e48e480ee4ca7cb1e12f587c Mon Sep 17 00:00:00 2001 From: Karol Trzcinski Date: Mon, 9 Dec 2019 18:48:49 -0600 Subject: [PATCH 0391/1437] ASoC: SOF: loader: fix snd_sof_fw_parse_ext_data An error occurs during parsing more than one ext_data from the mailbox, because of invalid data offset handling. Fix by removing the incorrect duplicate increment of the offset. The return value is also reset in the switch case. This does not change the behavior but improves readability - there is no longer a need to check what the return value of get_ext_windows is. Signed-off-by: Karol Trzcinski Signed-off-by: Bartosz Kokoszko Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20191210004854.16845-4-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/loader.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/sound/soc/sof/loader.c b/sound/soc/sof/loader.c index a041adf0669de..432d12bd49379 100644 --- a/sound/soc/sof/loader.c +++ b/sound/soc/sof/loader.c @@ -50,8 +50,7 @@ int snd_sof_fw_parse_ext_data(struct snd_sof_dev *sdev, u32 bar, u32 offset) while (ext_hdr->hdr.cmd == SOF_IPC_FW_READY) { /* read in ext structure */ - offset += sizeof(*ext_hdr); - snd_sof_dsp_block_read(sdev, bar, offset, + snd_sof_dsp_block_read(sdev, bar, offset + sizeof(*ext_hdr), (void *)((u8 *)ext_data + sizeof(*ext_hdr)), ext_hdr->hdr.size - sizeof(*ext_hdr)); @@ -61,6 +60,7 @@ int snd_sof_fw_parse_ext_data(struct snd_sof_dev *sdev, u32 bar, u32 offset) /* process structure data */ switch (ext_hdr->type) { case SOF_IPC_EXT_DMA_BUFFER: + ret = 0; break; case SOF_IPC_EXT_WINDOW: ret = get_ext_windows(sdev, ext_hdr); @@ -68,6 +68,7 @@ int snd_sof_fw_parse_ext_data(struct snd_sof_dev *sdev, u32 bar, u32 offset) default: dev_warn(sdev->dev, "warning: unknown ext header type %d size 0x%x\n", ext_hdr->type, ext_hdr->hdr.size); + ret = 0; break; } -- GitLab From f84337c3fb8ff4d533ccbed0d2db4e8587d0ff58 Mon Sep 17 00:00:00 2001 From: Curtis Malainey Date: Mon, 9 Dec 2019 18:48:52 -0600 Subject: [PATCH 0392/1437] ASoC: SOF: Intel: split cht and byt debug window sizes Turns out SSP 3-5 are only available on cht, to avoid dumping on undefined registers let's split the definition. Signed-off-by: Curtis Malainey Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20191210004854.16845-7-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/intel/byt.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/sound/soc/sof/intel/byt.c b/sound/soc/sof/intel/byt.c index b9061b79a57b1..92ef6a796fd5e 100644 --- a/sound/soc/sof/intel/byt.c +++ b/sound/soc/sof/intel/byt.c @@ -24,7 +24,8 @@ #define DRAM_OFFSET 0x100000 #define DRAM_SIZE (160 * 1024) #define SHIM_OFFSET 0x140000 -#define SHIM_SIZE 0x100 +#define SHIM_SIZE_BYT 0x100 +#define SHIM_SIZE_CHT 0x118 #define MBOX_OFFSET 0x144000 #define MBOX_SIZE 0x1000 #define EXCEPT_OFFSET 0x800 @@ -75,7 +76,7 @@ static const struct snd_sof_debugfs_map byt_debugfs[] = { SOF_DEBUGFS_ACCESS_D0_ONLY}, {"dram", BYT_DSP_BAR, DRAM_OFFSET, DRAM_SIZE, SOF_DEBUGFS_ACCESS_D0_ONLY}, - {"shim", BYT_DSP_BAR, SHIM_OFFSET, SHIM_SIZE, + {"shim", BYT_DSP_BAR, SHIM_OFFSET, SHIM_SIZE_BYT, SOF_DEBUGFS_ACCESS_ALWAYS}, }; @@ -102,7 +103,7 @@ static const struct snd_sof_debugfs_map cht_debugfs[] = { SOF_DEBUGFS_ACCESS_D0_ONLY}, {"dram", BYT_DSP_BAR, DRAM_OFFSET, DRAM_SIZE, SOF_DEBUGFS_ACCESS_D0_ONLY}, - {"shim", BYT_DSP_BAR, SHIM_OFFSET, SHIM_SIZE, + {"shim", BYT_DSP_BAR, SHIM_OFFSET, SHIM_SIZE_CHT, SOF_DEBUGFS_ACCESS_ALWAYS}, }; -- GitLab From 4e64ba3fd181b207c00d392b27ed484b89108dce Mon Sep 17 00:00:00 2001 From: Amery Song Date: Mon, 9 Dec 2019 18:48:54 -0600 Subject: [PATCH 0393/1437] ASoC: Intel: common: work-around incorrect ACPI HID for CML boards On CML boards with the RT5682 headset codec and RT1011 speaker amplifier, the platform firmware exposes three ACPI HIDs (10EC5682, 10EC1011 and MX98357A). The last HID is a mistake in DSDT tables, which causes the wrong machine driver to be loaded. This patch changes the key used to identify boards and changes the order of entries in the table to load the correct machine driver. The order does matter and should not be modified to work-around this firmware issue. Signed-off-by: Amery Song Signed-off-by: Keyon Jie Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20191210004854.16845-9-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- .../intel/common/soc-acpi-intel-cml-match.c | 41 +++++++++++-------- 1 file changed, 24 insertions(+), 17 deletions(-) diff --git a/sound/soc/intel/common/soc-acpi-intel-cml-match.c b/sound/soc/intel/common/soc-acpi-intel-cml-match.c index 5d08ae0667380..fb9ba88197060 100644 --- a/sound/soc/intel/common/soc-acpi-intel-cml-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-cml-match.c @@ -9,45 +9,52 @@ #include #include -static struct snd_soc_acpi_codecs cml_codecs = { +static struct snd_soc_acpi_codecs rt1011_spk_codecs = { .num_codecs = 1, - .codecs = {"10EC5682"} + .codecs = {"10EC1011"} }; -static struct snd_soc_acpi_codecs cml_spk_codecs = { +static struct snd_soc_acpi_codecs max98357a_spk_codecs = { .num_codecs = 1, .codecs = {"MX98357A"} }; +/* + * The order of the three entries with .id = "10EC5682" matters + * here, because DSDT tables expose an ACPI HID for the MAX98357A + * speaker amplifier which is not populated on the board. + */ struct snd_soc_acpi_mach snd_soc_acpi_intel_cml_machines[] = { { - .id = "DLGS7219", - .drv_name = "cml_da7219_max98357a", - .quirk_data = &cml_spk_codecs, + .id = "10EC5682", + .drv_name = "cml_rt1011_rt5682", + .machine_quirk = snd_soc_acpi_codec_list, + .quirk_data = &rt1011_spk_codecs, .sof_fw_filename = "sof-cml.ri", - .sof_tplg_filename = "sof-cml-da7219-max98357a.tplg", + .sof_tplg_filename = "sof-cml-rt1011-rt5682.tplg", }, { - .id = "MX98357A", + .id = "10EC5682", .drv_name = "sof_rt5682", - .quirk_data = &cml_codecs, + .machine_quirk = snd_soc_acpi_codec_list, + .quirk_data = &max98357a_spk_codecs, .sof_fw_filename = "sof-cml.ri", .sof_tplg_filename = "sof-cml-rt5682-max98357a.tplg", }, - { - .id = "10EC1011", - .drv_name = "cml_rt1011_rt5682", - .quirk_data = &cml_codecs, - .sof_fw_filename = "sof-cml.ri", - .sof_tplg_filename = "sof-cml-rt1011-rt5682.tplg", - }, { .id = "10EC5682", .drv_name = "sof_rt5682", .sof_fw_filename = "sof-cml.ri", .sof_tplg_filename = "sof-cml-rt5682.tplg", }, - + { + .id = "DLGS7219", + .drv_name = "cml_da7219_max98357a", + .machine_quirk = snd_soc_acpi_codec_list, + .quirk_data = &max98357a_spk_codecs, + .sof_fw_filename = "sof-cml.ri", + .sof_tplg_filename = "sof-cml-da7219-max98357a.tplg", + }, {}, }; EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_cml_machines); -- GitLab From eecd37e105f0137af0d1b726bf61ff35d1d7d2eb Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Tue, 3 Dec 2019 12:58:52 -0800 Subject: [PATCH 0394/1437] drivers: Fix boot problem on SuperH SuperH images crash too eearly to display any console output. Bisect points to commit 507fd01d5333 ("drivers: move the early platform device support to arch/sh"). An analysis of that patch suggests that early_platform_cleanup() is now called at the wrong time. Restoring its call point fixes the problem. Cc: Bartosz Golaszewski Fixes: 507fd01d5333 ("drivers: move the early platform device support to arch/sh") Tested-by: Geert Uytterhoeven Signed-off-by: Guenter Roeck Acked-by: Rob Landley Link: https://lore.kernel.org/r/20191203205852.15659-1-linux@roeck-us.net Signed-off-by: Greg Kroah-Hartman --- arch/sh/drivers/platform_early.c | 11 ++--------- drivers/base/platform.c | 4 ++++ 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/arch/sh/drivers/platform_early.c b/arch/sh/drivers/platform_early.c index f6d148451dfc7..f3dc3f25b3ff0 100644 --- a/arch/sh/drivers/platform_early.c +++ b/arch/sh/drivers/platform_early.c @@ -325,9 +325,9 @@ int __init sh_early_platform_driver_probe(char *class_str, } /** - * sh_early_platform_cleanup - clean up early platform code + * early_platform_cleanup - clean up early platform code */ -static int __init sh_early_platform_cleanup(void) +void __init early_platform_cleanup(void) { struct platform_device *pd, *pd2; @@ -337,11 +337,4 @@ static int __init sh_early_platform_cleanup(void) list_del(&pd->dev.devres_head); memset(&pd->dev.devres_head, 0, sizeof(pd->dev.devres_head)); } - - return 0; } -/* - * This must happen once after all early devices are probed but before probing - * real platform devices. - */ -subsys_initcall(sh_early_platform_cleanup); diff --git a/drivers/base/platform.c b/drivers/base/platform.c index 7c532548b0a62..cf6b6b722e5c9 100644 --- a/drivers/base/platform.c +++ b/drivers/base/platform.c @@ -1325,10 +1325,14 @@ struct device *platform_find_device_by_driver(struct device *start, } EXPORT_SYMBOL_GPL(platform_find_device_by_driver); +void __weak __init early_platform_cleanup(void) { } + int __init platform_bus_init(void) { int error; + early_platform_cleanup(); + error = device_register(&platform_bus); if (error) { put_device(&platform_bus); -- GitLab From 4bcd9eae731083bb724faf68cce6021213308333 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 21 Nov 2019 16:02:07 +0200 Subject: [PATCH 0395/1437] fbtft: Fix the initialization from property algorithm When converting to device property API the commit 8b2d3aeeb7ec ("fbtft: Make use of device property API") mistakenly placed the reading of the first value inside the loop, that jumps over value after initialization sequence or sleep commands. Move the above mentioned reading outside of the loop to restore correct behaviour. Besides that, we are using pre-increment operation which may lead to out of the boundary access at the end of sequence. Thus, allocate buffer with an additional element at the end to prevent out of the boundary access. Fixes: 8b2d3aeeb7ec ("fbtft: Make use of device property API") Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20191121140207.65089-1-andriy.shevchenko@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/fbtft/fbtft-core.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/staging/fbtft/fbtft-core.c b/drivers/staging/fbtft/fbtft-core.c index ffb84987dd867..d3e098b41b1a4 100644 --- a/drivers/staging/fbtft/fbtft-core.c +++ b/drivers/staging/fbtft/fbtft-core.c @@ -913,7 +913,7 @@ static int fbtft_init_display_from_property(struct fbtft_par *par) if (count == 0) return -EINVAL; - values = kmalloc_array(count, sizeof(*values), GFP_KERNEL); + values = kmalloc_array(count + 1, sizeof(*values), GFP_KERNEL); if (!values) return -ENOMEM; @@ -926,9 +926,9 @@ static int fbtft_init_display_from_property(struct fbtft_par *par) gpiod_set_value(par->gpio.cs, 0); /* Activate chip */ index = -1; - while (index < count) { - val = values[++index]; + val = values[++index]; + while (index < count) { if (val & FBTFT_OF_INIT_CMD) { val &= 0xFFFF; i = 0; -- GitLab From 7e63c87fc2dcf3be9d3aab82d4a0ea085880bdca Mon Sep 17 00:00:00 2001 From: Amir Goldstein Date: Thu, 14 Nov 2019 22:28:41 +0200 Subject: [PATCH 0396/1437] ovl: fix lookup failure on multi lower squashfs In the past, overlayfs required that lower fs have non null uuid in order to support nfs export and decode copy up origin file handles. Commit 9df085f3c9a2 ("ovl: relax requirement for non null uuid of lower fs") relaxed this requirement for nfs export support, as long as uuid (even if null) is unique among all lower fs. However, said commit unintentionally also relaxed the non null uuid requirement for decoding copy up origin file handles, regardless of the unique uuid requirement. Amend this mistake by disabling decoding of copy up origin file handle from lower fs with a conflicting uuid. We still encode copy up origin file handles from those fs, because file handles like those already exist in the wild and because they might provide useful information in the future. There is an unhandled corner case described by Miklos this way: - two filesystems, A and B, both have null uuid - upper layer is on A - lower layer 1 is also on A - lower layer 2 is on B In this case bad_uuid won't be set for B, because the check only involves the list of lower fs. Hence we'll try to decode a layer 2 origin on layer 1 and fail. We will deal with this corner case later. Reported-by: Colin Ian King Tested-by: Colin Ian King Link: https://lore.kernel.org/lkml/20191106234301.283006-1-colin.king@canonical.com/ Fixes: 9df085f3c9a2 ("ovl: relax requirement for non null uuid ...") Cc: stable@vger.kernel.org # v4.20+ Signed-off-by: Amir Goldstein Signed-off-by: Miklos Szeredi --- fs/overlayfs/namei.c | 8 ++++++++ fs/overlayfs/ovl_entry.h | 2 ++ fs/overlayfs/super.c | 24 +++++++++++++++++------- 3 files changed, 27 insertions(+), 7 deletions(-) diff --git a/fs/overlayfs/namei.c b/fs/overlayfs/namei.c index e9717c2f7d455..f47c591402d76 100644 --- a/fs/overlayfs/namei.c +++ b/fs/overlayfs/namei.c @@ -325,6 +325,14 @@ int ovl_check_origin_fh(struct ovl_fs *ofs, struct ovl_fh *fh, bool connected, int i; for (i = 0; i < ofs->numlower; i++) { + /* + * If lower fs uuid is not unique among lower fs we cannot match + * fh->uuid to layer. + */ + if (ofs->lower_layers[i].fsid && + ofs->lower_layers[i].fs->bad_uuid) + continue; + origin = ovl_decode_real_fh(fh, ofs->lower_layers[i].mnt, connected); if (origin) diff --git a/fs/overlayfs/ovl_entry.h b/fs/overlayfs/ovl_entry.h index a8279280e88dd..28348c44ea5b2 100644 --- a/fs/overlayfs/ovl_entry.h +++ b/fs/overlayfs/ovl_entry.h @@ -22,6 +22,8 @@ struct ovl_config { struct ovl_sb { struct super_block *sb; dev_t pseudo_dev; + /* Unusable (conflicting) uuid */ + bool bad_uuid; }; struct ovl_layer { diff --git a/fs/overlayfs/super.c b/fs/overlayfs/super.c index afbcb116a7f1b..7621ff176d15c 100644 --- a/fs/overlayfs/super.c +++ b/fs/overlayfs/super.c @@ -1255,7 +1255,7 @@ static bool ovl_lower_uuid_ok(struct ovl_fs *ofs, const uuid_t *uuid) { unsigned int i; - if (!ofs->config.nfs_export && !(ofs->config.index && ofs->upper_mnt)) + if (!ofs->config.nfs_export && !ofs->upper_mnt) return true; for (i = 0; i < ofs->numlowerfs; i++) { @@ -1263,9 +1263,13 @@ static bool ovl_lower_uuid_ok(struct ovl_fs *ofs, const uuid_t *uuid) * We use uuid to associate an overlay lower file handle with a * lower layer, so we can accept lower fs with null uuid as long * as all lower layers with null uuid are on the same fs. + * if we detect multiple lower fs with the same uuid, we + * disable lower file handle decoding on all of them. */ - if (uuid_equal(&ofs->lower_fs[i].sb->s_uuid, uuid)) + if (uuid_equal(&ofs->lower_fs[i].sb->s_uuid, uuid)) { + ofs->lower_fs[i].bad_uuid = true; return false; + } } return true; } @@ -1277,6 +1281,7 @@ static int ovl_get_fsid(struct ovl_fs *ofs, const struct path *path) unsigned int i; dev_t dev; int err; + bool bad_uuid = false; /* fsid 0 is reserved for upper fs even with non upper overlay */ if (ofs->upper_mnt && ofs->upper_mnt->mnt_sb == sb) @@ -1288,11 +1293,15 @@ static int ovl_get_fsid(struct ovl_fs *ofs, const struct path *path) } if (!ovl_lower_uuid_ok(ofs, &sb->s_uuid)) { - ofs->config.index = false; - ofs->config.nfs_export = false; - pr_warn("overlayfs: %s uuid detected in lower fs '%pd2', falling back to index=off,nfs_export=off.\n", - uuid_is_null(&sb->s_uuid) ? "null" : "conflicting", - path->dentry); + bad_uuid = true; + if (ofs->config.index || ofs->config.nfs_export) { + ofs->config.index = false; + ofs->config.nfs_export = false; + pr_warn("overlayfs: %s uuid detected in lower fs '%pd2', falling back to index=off,nfs_export=off.\n", + uuid_is_null(&sb->s_uuid) ? "null" : + "conflicting", + path->dentry); + } } err = get_anon_bdev(&dev); @@ -1303,6 +1312,7 @@ static int ovl_get_fsid(struct ovl_fs *ofs, const struct path *path) ofs->lower_fs[ofs->numlowerfs].sb = sb; ofs->lower_fs[ofs->numlowerfs].pseudo_dev = dev; + ofs->lower_fs[ofs->numlowerfs].bad_uuid = bad_uuid; ofs->numlowerfs++; return ofs->numlowerfs; -- GitLab From cbe7fba8edfc8cb8e621599e376f8ac5c224fa72 Mon Sep 17 00:00:00 2001 From: Amir Goldstein Date: Fri, 15 Nov 2019 13:33:03 +0200 Subject: [PATCH 0397/1437] ovl: make sure that real fid is 32bit aligned in memory Seprate on-disk encoding from in-memory and on-wire resresentation of overlay file handle. In-memory and on-wire we only ever pass around pointers to struct ovl_fh, which encapsulates at offset 3 the on-disk format struct ovl_fb. struct ovl_fb encapsulates at offset 21 the real file handle. That makes sure that the real file handle is always 32bit aligned in-memory when passed down to the underlying filesystem. On-disk format remains the same and store/load are done into correctly aligned buffer. New nfs exported file handles are exported with aligned real fid. Old nfs file handles are copied to an aligned buffer before being decoded. Reported-by: Al Viro Signed-off-by: Amir Goldstein Signed-off-by: Miklos Szeredi --- fs/overlayfs/copy_up.c | 30 ++++++++------- fs/overlayfs/export.c | 80 ++++++++++++++++++++++++---------------- fs/overlayfs/namei.c | 44 +++++++++++----------- fs/overlayfs/overlayfs.h | 34 ++++++++++++++--- 4 files changed, 115 insertions(+), 73 deletions(-) diff --git a/fs/overlayfs/copy_up.c b/fs/overlayfs/copy_up.c index b801c63531009..7f744a9541e5b 100644 --- a/fs/overlayfs/copy_up.c +++ b/fs/overlayfs/copy_up.c @@ -251,18 +251,20 @@ struct ovl_fh *ovl_encode_real_fh(struct dentry *real, bool is_upper) WARN_ON(fh_type == FILEID_INVALID)) goto out; - BUILD_BUG_ON(MAX_HANDLE_SZ + offsetof(struct ovl_fh, fid) > 255); - fh_len = offsetof(struct ovl_fh, fid) + buflen; - fh = kmalloc(fh_len, GFP_KERNEL); + /* Make sure the real fid stays 32bit aligned */ + BUILD_BUG_ON(OVL_FH_FID_OFFSET % 4); + BUILD_BUG_ON(MAX_HANDLE_SZ + OVL_FH_FID_OFFSET > 255); + fh_len = OVL_FH_FID_OFFSET + buflen; + fh = kzalloc(fh_len, GFP_KERNEL); if (!fh) { fh = ERR_PTR(-ENOMEM); goto out; } - fh->version = OVL_FH_VERSION; - fh->magic = OVL_FH_MAGIC; - fh->type = fh_type; - fh->flags = OVL_FH_FLAG_CPU_ENDIAN; + fh->fb.version = OVL_FH_VERSION; + fh->fb.magic = OVL_FH_MAGIC; + fh->fb.type = fh_type; + fh->fb.flags = OVL_FH_FLAG_CPU_ENDIAN; /* * When we will want to decode an overlay dentry from this handle * and all layers are on the same fs, if we get a disconncted real @@ -270,10 +272,10 @@ struct ovl_fh *ovl_encode_real_fh(struct dentry *real, bool is_upper) * it to upperdentry or to lowerstack is by checking this flag. */ if (is_upper) - fh->flags |= OVL_FH_FLAG_PATH_UPPER; - fh->len = fh_len; - fh->uuid = *uuid; - memcpy(fh->fid, buf, buflen); + fh->fb.flags |= OVL_FH_FLAG_PATH_UPPER; + fh->fb.len = fh_len - OVL_FH_WIRE_OFFSET; + fh->fb.uuid = *uuid; + memcpy(fh->fb.fid, buf, buflen); out: kfree(buf); @@ -300,8 +302,8 @@ int ovl_set_origin(struct dentry *dentry, struct dentry *lower, /* * Do not fail when upper doesn't support xattrs. */ - err = ovl_check_setxattr(dentry, upper, OVL_XATTR_ORIGIN, fh, - fh ? fh->len : 0, 0); + err = ovl_check_setxattr(dentry, upper, OVL_XATTR_ORIGIN, fh->buf, + fh ? fh->fb.len : 0, 0); kfree(fh); return err; @@ -317,7 +319,7 @@ static int ovl_set_upper_fh(struct dentry *upper, struct dentry *index) if (IS_ERR(fh)) return PTR_ERR(fh); - err = ovl_do_setxattr(index, OVL_XATTR_UPPER, fh, fh->len, 0); + err = ovl_do_setxattr(index, OVL_XATTR_UPPER, fh->buf, fh->fb.len, 0); kfree(fh); return err; diff --git a/fs/overlayfs/export.c b/fs/overlayfs/export.c index 73c9775215b33..70e55588aedc7 100644 --- a/fs/overlayfs/export.c +++ b/fs/overlayfs/export.c @@ -211,10 +211,11 @@ static int ovl_check_encode_origin(struct dentry *dentry) return 1; } -static int ovl_d_to_fh(struct dentry *dentry, char *buf, int buflen) +static int ovl_dentry_to_fid(struct dentry *dentry, u32 *fid, int buflen) { struct ovl_fh *fh = NULL; int err, enc_lower; + int len; /* * Check if we should encode a lower or upper file handle and maybe @@ -231,11 +232,12 @@ static int ovl_d_to_fh(struct dentry *dentry, char *buf, int buflen) return PTR_ERR(fh); err = -EOVERFLOW; - if (fh->len > buflen) + len = OVL_FH_LEN(fh); + if (len > buflen) goto fail; - memcpy(buf, (char *)fh, fh->len); - err = fh->len; + memcpy(fid, fh, len); + err = len; out: kfree(fh); @@ -243,31 +245,16 @@ out: fail: pr_warn_ratelimited("overlayfs: failed to encode file handle (%pd2, err=%i, buflen=%d, len=%d, type=%d)\n", - dentry, err, buflen, fh ? (int)fh->len : 0, - fh ? fh->type : 0); + dentry, err, buflen, fh ? (int)fh->fb.len : 0, + fh ? fh->fb.type : 0); goto out; } -static int ovl_dentry_to_fh(struct dentry *dentry, u32 *fid, int *max_len) -{ - int res, len = *max_len << 2; - - res = ovl_d_to_fh(dentry, (char *)fid, len); - if (res <= 0) - return FILEID_INVALID; - - len = res; - - /* Round up to dwords */ - *max_len = (len + 3) >> 2; - return OVL_FILEID; -} - static int ovl_encode_fh(struct inode *inode, u32 *fid, int *max_len, struct inode *parent) { struct dentry *dentry; - int type; + int bytes = *max_len << 2; /* TODO: encode connectable file handles */ if (parent) @@ -277,10 +264,14 @@ static int ovl_encode_fh(struct inode *inode, u32 *fid, int *max_len, if (WARN_ON(!dentry)) return FILEID_INVALID; - type = ovl_dentry_to_fh(dentry, fid, max_len); - + bytes = ovl_dentry_to_fid(dentry, fid, bytes); dput(dentry); - return type; + if (bytes <= 0) + return FILEID_INVALID; + + *max_len = bytes >> 2; + + return OVL_FILEID_V1; } /* @@ -777,24 +768,45 @@ out_err: goto out; } +static struct ovl_fh *ovl_fid_to_fh(struct fid *fid, int buflen, int fh_type) +{ + struct ovl_fh *fh; + + /* If on-wire inner fid is aligned - nothing to do */ + if (fh_type == OVL_FILEID_V1) + return (struct ovl_fh *)fid; + + if (fh_type != OVL_FILEID_V0) + return ERR_PTR(-EINVAL); + + fh = kzalloc(buflen, GFP_KERNEL); + if (!fh) + return ERR_PTR(-ENOMEM); + + /* Copy unaligned inner fh into aligned buffer */ + memcpy(&fh->fb, fid, buflen - OVL_FH_WIRE_OFFSET); + return fh; +} + static struct dentry *ovl_fh_to_dentry(struct super_block *sb, struct fid *fid, int fh_len, int fh_type) { struct dentry *dentry = NULL; - struct ovl_fh *fh = (struct ovl_fh *) fid; + struct ovl_fh *fh = NULL; int len = fh_len << 2; unsigned int flags = 0; int err; - err = -EINVAL; - if (fh_type != OVL_FILEID) + fh = ovl_fid_to_fh(fid, len, fh_type); + err = PTR_ERR(fh); + if (IS_ERR(fh)) goto out_err; err = ovl_check_fh_len(fh, len); if (err) goto out_err; - flags = fh->flags; + flags = fh->fb.flags; dentry = (flags & OVL_FH_FLAG_PATH_UPPER) ? ovl_upper_fh_to_d(sb, fh) : ovl_lower_fh_to_d(sb, fh); @@ -802,12 +814,18 @@ static struct dentry *ovl_fh_to_dentry(struct super_block *sb, struct fid *fid, if (IS_ERR(dentry) && err != -ESTALE) goto out_err; +out: + /* We may have needed to re-align OVL_FILEID_V0 */ + if (!IS_ERR_OR_NULL(fh) && fh != (void *)fid) + kfree(fh); + return dentry; out_err: pr_warn_ratelimited("overlayfs: failed to decode file handle (len=%d, type=%d, flags=%x, err=%i)\n", - len, fh_type, flags, err); - return ERR_PTR(err); + fh_len, fh_type, flags, err); + dentry = ERR_PTR(err); + goto out; } static struct dentry *ovl_fh_to_parent(struct super_block *sb, struct fid *fid, diff --git a/fs/overlayfs/namei.c b/fs/overlayfs/namei.c index f47c591402d76..80fbca5219d4a 100644 --- a/fs/overlayfs/namei.c +++ b/fs/overlayfs/namei.c @@ -84,21 +84,21 @@ static int ovl_acceptable(void *ctx, struct dentry *dentry) * Return -ENODATA for "origin unknown". * Return <0 for an invalid file handle. */ -int ovl_check_fh_len(struct ovl_fh *fh, int fh_len) +int ovl_check_fb_len(struct ovl_fb *fb, int fb_len) { - if (fh_len < sizeof(struct ovl_fh) || fh_len < fh->len) + if (fb_len < sizeof(struct ovl_fb) || fb_len < fb->len) return -EINVAL; - if (fh->magic != OVL_FH_MAGIC) + if (fb->magic != OVL_FH_MAGIC) return -EINVAL; /* Treat larger version and unknown flags as "origin unknown" */ - if (fh->version > OVL_FH_VERSION || fh->flags & ~OVL_FH_FLAG_ALL) + if (fb->version > OVL_FH_VERSION || fb->flags & ~OVL_FH_FLAG_ALL) return -ENODATA; /* Treat endianness mismatch as "origin unknown" */ - if (!(fh->flags & OVL_FH_FLAG_ANY_ENDIAN) && - (fh->flags & OVL_FH_FLAG_BIG_ENDIAN) != OVL_FH_FLAG_CPU_ENDIAN) + if (!(fb->flags & OVL_FH_FLAG_ANY_ENDIAN) && + (fb->flags & OVL_FH_FLAG_BIG_ENDIAN) != OVL_FH_FLAG_CPU_ENDIAN) return -ENODATA; return 0; @@ -119,15 +119,15 @@ static struct ovl_fh *ovl_get_fh(struct dentry *dentry, const char *name) if (res == 0) return NULL; - fh = kzalloc(res, GFP_KERNEL); + fh = kzalloc(res + OVL_FH_WIRE_OFFSET, GFP_KERNEL); if (!fh) return ERR_PTR(-ENOMEM); - res = vfs_getxattr(dentry, name, fh, res); + res = vfs_getxattr(dentry, name, fh->buf, res); if (res < 0) goto fail; - err = ovl_check_fh_len(fh, res); + err = ovl_check_fb_len(&fh->fb, res); if (err < 0) { if (err == -ENODATA) goto out; @@ -158,12 +158,12 @@ struct dentry *ovl_decode_real_fh(struct ovl_fh *fh, struct vfsmount *mnt, * Make sure that the stored uuid matches the uuid of the lower * layer where file handle will be decoded. */ - if (!uuid_equal(&fh->uuid, &mnt->mnt_sb->s_uuid)) + if (!uuid_equal(&fh->fb.uuid, &mnt->mnt_sb->s_uuid)) return NULL; - bytes = (fh->len - offsetof(struct ovl_fh, fid)); - real = exportfs_decode_fh(mnt, (struct fid *)fh->fid, - bytes >> 2, (int)fh->type, + bytes = (fh->fb.len - offsetof(struct ovl_fb, fid)); + real = exportfs_decode_fh(mnt, (struct fid *)fh->fb.fid, + bytes >> 2, (int)fh->fb.type, connected ? ovl_acceptable : NULL, mnt); if (IS_ERR(real)) { /* @@ -173,7 +173,7 @@ struct dentry *ovl_decode_real_fh(struct ovl_fh *fh, struct vfsmount *mnt, * index entries correctly. */ if (real == ERR_PTR(-ESTALE) && - !(fh->flags & OVL_FH_FLAG_PATH_UPPER)) + !(fh->fb.flags & OVL_FH_FLAG_PATH_UPPER)) real = NULL; return real; } @@ -410,7 +410,7 @@ static int ovl_verify_fh(struct dentry *dentry, const char *name, if (IS_ERR(ofh)) return PTR_ERR(ofh); - if (fh->len != ofh->len || memcmp(fh, ofh, fh->len)) + if (fh->fb.len != ofh->fb.len || memcmp(&fh->fb, &ofh->fb, fh->fb.len)) err = -ESTALE; kfree(ofh); @@ -441,7 +441,7 @@ int ovl_verify_set_fh(struct dentry *dentry, const char *name, err = ovl_verify_fh(dentry, name, fh); if (set && err == -ENODATA) - err = ovl_do_setxattr(dentry, name, fh, fh->len, 0); + err = ovl_do_setxattr(dentry, name, fh->buf, fh->fb.len, 0); if (err) goto fail; @@ -515,20 +515,20 @@ int ovl_verify_index(struct ovl_fs *ofs, struct dentry *index) goto fail; err = -EINVAL; - if (index->d_name.len < sizeof(struct ovl_fh)*2) + if (index->d_name.len < sizeof(struct ovl_fb)*2) goto fail; err = -ENOMEM; len = index->d_name.len / 2; - fh = kzalloc(len, GFP_KERNEL); + fh = kzalloc(len + OVL_FH_WIRE_OFFSET, GFP_KERNEL); if (!fh) goto fail; err = -EINVAL; - if (hex2bin((u8 *)fh, index->d_name.name, len)) + if (hex2bin(fh->buf, index->d_name.name, len)) goto fail; - err = ovl_check_fh_len(fh, len); + err = ovl_check_fb_len(&fh->fb, len); if (err) goto fail; @@ -607,11 +607,11 @@ static int ovl_get_index_name_fh(struct ovl_fh *fh, struct qstr *name) { char *n, *s; - n = kcalloc(fh->len, 2, GFP_KERNEL); + n = kcalloc(fh->fb.len, 2, GFP_KERNEL); if (!n) return -ENOMEM; - s = bin2hex(n, fh, fh->len); + s = bin2hex(n, fh->buf, fh->fb.len); *name = (struct qstr) QSTR_INIT(n, s - n); return 0; diff --git a/fs/overlayfs/overlayfs.h b/fs/overlayfs/overlayfs.h index 6934bcf030f0b..f283b1d69a9ed 100644 --- a/fs/overlayfs/overlayfs.h +++ b/fs/overlayfs/overlayfs.h @@ -71,20 +71,36 @@ enum ovl_entry_flag { #error Endianness not defined #endif -/* The type returned by overlay exportfs ops when encoding an ovl_fh handle */ -#define OVL_FILEID 0xfb +/* The type used to be returned by overlay exportfs for misaligned fid */ +#define OVL_FILEID_V0 0xfb +/* The type returned by overlay exportfs for 32bit aligned fid */ +#define OVL_FILEID_V1 0xf8 -/* On-disk and in-memeory format for redirect by file handle */ -struct ovl_fh { +/* On-disk format for "origin" file handle */ +struct ovl_fb { u8 version; /* 0 */ u8 magic; /* 0xfb */ u8 len; /* size of this header + size of fid */ u8 flags; /* OVL_FH_FLAG_* */ u8 type; /* fid_type of fid */ uuid_t uuid; /* uuid of filesystem */ - u8 fid[0]; /* file identifier */ + u32 fid[0]; /* file identifier should be 32bit aligned in-memory */ } __packed; +/* In-memory and on-wire format for overlay file handle */ +struct ovl_fh { + u8 padding[3]; /* make sure fb.fid is 32bit aligned */ + union { + struct ovl_fb fb; + u8 buf[0]; + }; +} __packed; + +#define OVL_FH_WIRE_OFFSET offsetof(struct ovl_fh, fb) +#define OVL_FH_LEN(fh) (OVL_FH_WIRE_OFFSET + (fh)->fb.len) +#define OVL_FH_FID_OFFSET (OVL_FH_WIRE_OFFSET + \ + offsetof(struct ovl_fb, fid)) + static inline int ovl_do_rmdir(struct inode *dir, struct dentry *dentry) { int err = vfs_rmdir(dir, dentry); @@ -302,7 +318,13 @@ static inline void ovl_inode_unlock(struct inode *inode) /* namei.c */ -int ovl_check_fh_len(struct ovl_fh *fh, int fh_len); +int ovl_check_fb_len(struct ovl_fb *fb, int fb_len); + +static inline int ovl_check_fh_len(struct ovl_fh *fh, int fh_len) +{ + return ovl_check_fb_len(&fh->fb, fh_len - OVL_FH_WIRE_OFFSET); +} + struct dentry *ovl_decode_real_fh(struct ovl_fh *fh, struct vfsmount *mnt, bool connected); int ovl_check_origin_fh(struct ovl_fs *ofs, struct ovl_fh *fh, bool connected, -- GitLab From ec7bbb53d339250df7d4b042ff93c468535c0a90 Mon Sep 17 00:00:00 2001 From: Amir Goldstein Date: Fri, 15 Nov 2019 13:33:04 +0200 Subject: [PATCH 0398/1437] ovl: don't use a temp buf for encoding real fh We can allocate maximum fh size and encode into it directly. Suggested-by: Al Viro Signed-off-by: Amir Goldstein Signed-off-by: Miklos Szeredi --- fs/overlayfs/copy_up.c | 37 ++++++++++++++++--------------------- 1 file changed, 16 insertions(+), 21 deletions(-) diff --git a/fs/overlayfs/copy_up.c b/fs/overlayfs/copy_up.c index 7f744a9541e5b..6220642fe113b 100644 --- a/fs/overlayfs/copy_up.c +++ b/fs/overlayfs/copy_up.c @@ -227,13 +227,17 @@ int ovl_set_attr(struct dentry *upperdentry, struct kstat *stat) struct ovl_fh *ovl_encode_real_fh(struct dentry *real, bool is_upper) { struct ovl_fh *fh; - int fh_type, fh_len, dwords; - void *buf; + int fh_type, dwords; int buflen = MAX_HANDLE_SZ; uuid_t *uuid = &real->d_sb->s_uuid; + int err; - buf = kmalloc(buflen, GFP_KERNEL); - if (!buf) + /* Make sure the real fid stays 32bit aligned */ + BUILD_BUG_ON(OVL_FH_FID_OFFSET % 4); + BUILD_BUG_ON(MAX_HANDLE_SZ + OVL_FH_FID_OFFSET > 255); + + fh = kzalloc(buflen + OVL_FH_FID_OFFSET, GFP_KERNEL); + if (!fh) return ERR_PTR(-ENOMEM); /* @@ -242,24 +246,14 @@ struct ovl_fh *ovl_encode_real_fh(struct dentry *real, bool is_upper) * the price or reconnecting the dentry. */ dwords = buflen >> 2; - fh_type = exportfs_encode_fh(real, buf, &dwords, 0); + fh_type = exportfs_encode_fh(real, (void *)fh->fb.fid, &dwords, 0); buflen = (dwords << 2); - fh = ERR_PTR(-EIO); + err = -EIO; if (WARN_ON(fh_type < 0) || WARN_ON(buflen > MAX_HANDLE_SZ) || WARN_ON(fh_type == FILEID_INVALID)) - goto out; - - /* Make sure the real fid stays 32bit aligned */ - BUILD_BUG_ON(OVL_FH_FID_OFFSET % 4); - BUILD_BUG_ON(MAX_HANDLE_SZ + OVL_FH_FID_OFFSET > 255); - fh_len = OVL_FH_FID_OFFSET + buflen; - fh = kzalloc(fh_len, GFP_KERNEL); - if (!fh) { - fh = ERR_PTR(-ENOMEM); - goto out; - } + goto out_err; fh->fb.version = OVL_FH_VERSION; fh->fb.magic = OVL_FH_MAGIC; @@ -273,13 +267,14 @@ struct ovl_fh *ovl_encode_real_fh(struct dentry *real, bool is_upper) */ if (is_upper) fh->fb.flags |= OVL_FH_FLAG_PATH_UPPER; - fh->fb.len = fh_len - OVL_FH_WIRE_OFFSET; + fh->fb.len = sizeof(fh->fb) + buflen; fh->fb.uuid = *uuid; - memcpy(fh->fb.fid, buf, buflen); -out: - kfree(buf); return fh; + +out_err: + kfree(fh); + return ERR_PTR(err); } int ovl_set_origin(struct dentry *dentry, struct dentry *lower, -- GitLab From 9c6d8f13e9da10a26ad7f0a020ef86e8ef142835 Mon Sep 17 00:00:00 2001 From: Amir Goldstein Date: Sun, 17 Nov 2019 17:43:44 +0200 Subject: [PATCH 0399/1437] ovl: fix corner case of non-unique st_dev;st_ino On non-samefs overlay without xino, non pure upper inodes should use a pseudo_dev assigned to each unique lower fs and pure upper inodes use the real upper st_dev. It is fine for an overlay pure upper inode to use the same st_dev;st_ino values as the real upper inode, because the content of those two different filesystem objects is always the same. In this case, however: - two filesystems, A and B - upper layer is on A - lower layer 1 is also on A - lower layer 2 is on B Non pure upper overlay inode, whose origin is in layer 1 will have the same st_dev;st_ino values as the real lower inode. This may result with a false positive results of 'diff' between the real lower and copied up overlay inode. Fix this by using the upper st_dev;st_ino values in this case. This breaks the property of constant st_dev;st_ino across copy up of this case. This breakage will be fixed by a later patch. Fixes: 5148626b806a ("ovl: allocate anon bdev per unique lower fs") Cc: stable@vger.kernel.org # v4.17+ Signed-off-by: Amir Goldstein Signed-off-by: Miklos Szeredi --- fs/overlayfs/inode.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/fs/overlayfs/inode.c b/fs/overlayfs/inode.c index bc14781886bf0..b045cf1826fc4 100644 --- a/fs/overlayfs/inode.c +++ b/fs/overlayfs/inode.c @@ -200,8 +200,14 @@ int ovl_getattr(const struct path *path, struct kstat *stat, if (ovl_test_flag(OVL_INDEX, d_inode(dentry)) || (!ovl_verify_lower(dentry->d_sb) && (is_dir || lowerstat.nlink == 1))) { - stat->ino = lowerstat.ino; lower_layer = ovl_layer_lower(dentry); + /* + * Cannot use origin st_dev;st_ino because + * origin inode content may differ from overlay + * inode content. + */ + if (samefs || lower_layer->fsid) + stat->ino = lowerstat.ino; } /* -- GitLab From 6889ee5a53b8d969aa542047f5ac8acdc0e79a91 Mon Sep 17 00:00:00 2001 From: Amir Goldstein Date: Fri, 6 Dec 2019 08:33:36 +0200 Subject: [PATCH 0400/1437] ovl: relax WARN_ON() on rename to self In ovl_rename(), if new upper is hardlinked to old upper underneath overlayfs before upper dirs are locked, user will get an ESTALE error and a WARN_ON will be printed. Changes to underlying layers while overlayfs is mounted may result in unexpected behavior, but it shouldn't crash the kernel and it shouldn't trigger WARN_ON() either, so relax this WARN_ON(). Reported-by: syzbot+bb1836a212e69f8e201a@syzkaller.appspotmail.com Fixes: 804032fabb3b ("ovl: don't check rename to self") Cc: # v4.9+ Signed-off-by: Amir Goldstein Signed-off-by: Miklos Szeredi --- fs/overlayfs/dir.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/overlayfs/dir.c b/fs/overlayfs/dir.c index 702aa63f6774d..29abdb1d3b5c6 100644 --- a/fs/overlayfs/dir.c +++ b/fs/overlayfs/dir.c @@ -1170,7 +1170,7 @@ static int ovl_rename(struct inode *olddir, struct dentry *old, if (newdentry == trap) goto out_dput; - if (WARN_ON(olddentry->d_inode == newdentry->d_inode)) + if (olddentry->d_inode == newdentry->d_inode) goto out_dput; err = 0; -- GitLab From 5356ab06448556a456c7991cbd0fc8234ef84ad0 Mon Sep 17 00:00:00 2001 From: Amir Goldstein Date: Mon, 25 Nov 2019 11:51:25 +0200 Subject: [PATCH 0401/1437] docs: filesystems: overlayfs: Rename overlayfs.txt to .rst It is already formatted as RST. Signed-off-by: Amir Goldstein Signed-off-by: Miklos Szeredi --- Documentation/filesystems/{overlayfs.txt => overlayfs.rst} | 0 MAINTAINERS | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename Documentation/filesystems/{overlayfs.txt => overlayfs.rst} (100%) diff --git a/Documentation/filesystems/overlayfs.txt b/Documentation/filesystems/overlayfs.rst similarity index 100% rename from Documentation/filesystems/overlayfs.txt rename to Documentation/filesystems/overlayfs.rst diff --git a/MAINTAINERS b/MAINTAINERS index 9d3a5c54a41d2..d757420f0da1e 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -12220,7 +12220,7 @@ L: linux-unionfs@vger.kernel.org T: git git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/vfs.git S: Supported F: fs/overlayfs/ -F: Documentation/filesystems/overlayfs.txt +F: Documentation/filesystems/overlayfs.rst P54 WIRELESS DRIVER M: Christian Lamparter -- GitLab From 35c6cb41686c4de03d738fd95617a0cf7441bbe1 Mon Sep 17 00:00:00 2001 From: Amir Goldstein Date: Mon, 25 Nov 2019 11:51:25 +0200 Subject: [PATCH 0402/1437] docs: filesystems: overlayfs: Fix restview warnings Fix only the obvious problems [SzM: add SPDX license line] Signed-off-by: Amir Goldstein Signed-off-by: Miklos Szeredi --- Documentation/filesystems/overlayfs.rst | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/Documentation/filesystems/overlayfs.rst b/Documentation/filesystems/overlayfs.rst index 845d689e0fd71..e443be7928db2 100644 --- a/Documentation/filesystems/overlayfs.rst +++ b/Documentation/filesystems/overlayfs.rst @@ -1,3 +1,5 @@ +.. SPDX-License-Identifier: GPL-2.0 + Written by: Neil Brown Please see MAINTAINERS file for where to send questions. @@ -181,7 +183,7 @@ Kernel config options: worried about backward compatibility with kernels that have the redirect_dir feature and follow redirects even if turned off. -Module options (can also be changed through /sys/module/overlay/parameters/*): +Module options (can also be changed through /sys/module/overlay/parameters/): - "redirect_dir=BOOL": See OVERLAY_FS_REDIRECT_DIR kernel config option above. @@ -263,7 +265,7 @@ top, lower2 the middle and lower3 the bottom layer. Metadata only copy up --------------------- +--------------------- When metadata only copy up feature is enabled, overlayfs will only copy up metadata (as opposed to whole file), when a metadata specific operation @@ -286,10 +288,10 @@ pointed by REDIRECT. This should not be possible on local system as setting "trusted." xattrs will require CAP_SYS_ADMIN. But it should be possible for untrusted layers like from a pen drive. -Note: redirect_dir={off|nofollow|follow(*)} conflicts with metacopy=on, and +Note: redirect_dir={off|nofollow|follow[*]} conflicts with metacopy=on, and results in an error. -(*) redirect_dir=follow only conflicts with metacopy=on if upperdir=... is +[*] redirect_dir=follow only conflicts with metacopy=on if upperdir=... is given. Sharing and copying layers -- GitLab From 7fc979f8204fb763e203d3e716c17d352eb96b35 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Fri, 6 Dec 2019 19:59:21 -0800 Subject: [PATCH 0403/1437] docs: dm-integrity: remove reference to ARC4 ARC4 is no longer considered secure, so it shouldn't be used, even as just an example. Signed-off-by: Eric Biggers Signed-off-by: Mike Snitzer --- Documentation/admin-guide/device-mapper/dm-integrity.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/admin-guide/device-mapper/dm-integrity.rst b/Documentation/admin-guide/device-mapper/dm-integrity.rst index 594095b54b296..c00f9f11e3f3f 100644 --- a/Documentation/admin-guide/device-mapper/dm-integrity.rst +++ b/Documentation/admin-guide/device-mapper/dm-integrity.rst @@ -144,7 +144,7 @@ journal_crypt:algorithm(:key) (the key is optional) Encrypt the journal using given algorithm to make sure that the attacker can't read the journal. You can use a block cipher here (such as "cbc(aes)") or a stream cipher (for example "chacha20", - "salsa20", "ctr(aes)" or "ecb(arc4)"). + "salsa20" or "ctr(aes)"). The journal contains history of last writes to the block device, an attacker reading the journal could see the last sector nubmers -- GitLab From 93c60483b5feefced92b869d5f97769495bc6313 Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Mon, 9 Dec 2019 13:55:15 -0800 Subject: [PATCH 0404/1437] bus: ti-sysc: Fix missing force mstandby quirk handling Commit 03856e928b0e ("bus: ti-sysc: Handle mstandby quirk and use it for musb") added quirk handling for mstandby quirk but did not consider that we also need a quirk variant for SYSC_QUIRK_FORCE_MSTANDBY. We need to use forced idle mode for both SYSC_QUIRK_SWSUP_MSTANDBY and SYSC_QUIRK_FORCE_MSTANDBY, but SYSC_QUIRK_SWSUP_MSTANDBY also need to additionally also configure no-idle mode when enabled. Fixes: 03856e928b0e ("bus: ti-sysc: Handle mstandby quirk and use it for musb") Signed-off-by: Tony Lindgren --- drivers/bus/ti-sysc.c | 3 ++- include/linux/platform_data/ti-sysc.h | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/bus/ti-sysc.c b/drivers/bus/ti-sysc.c index 56887c6877a7b..563707779e188 100644 --- a/drivers/bus/ti-sysc.c +++ b/drivers/bus/ti-sysc.c @@ -981,7 +981,8 @@ static int sysc_disable_module(struct device *dev) return ret; } - if (ddata->cfg.quirks & SYSC_QUIRK_SWSUP_MSTANDBY) + if (ddata->cfg.quirks & (SYSC_QUIRK_SWSUP_MSTANDBY) || + ddata->cfg.quirks & (SYSC_QUIRK_FORCE_MSTANDBY)) best_mode = SYSC_IDLE_FORCE; reg &= ~(SYSC_IDLE_MASK << regbits->midle_shift); diff --git a/include/linux/platform_data/ti-sysc.h b/include/linux/platform_data/ti-sysc.h index 0b93804751444..8cfe570fdece6 100644 --- a/include/linux/platform_data/ti-sysc.h +++ b/include/linux/platform_data/ti-sysc.h @@ -49,6 +49,7 @@ struct sysc_regbits { s8 emufree_shift; }; +#define SYSC_QUIRK_FORCE_MSTANDBY BIT(20) #define SYSC_MODULE_QUIRK_AESS BIT(19) #define SYSC_MODULE_QUIRK_SGX BIT(18) #define SYSC_MODULE_QUIRK_HDQ1W BIT(17) -- GitLab From c842b8c4ff9859f750447f3ca08f64b2ed23cebc Mon Sep 17 00:00:00 2001 From: Mans Rullgard Date: Sun, 1 Dec 2019 17:07:06 +0000 Subject: [PATCH 0405/1437] ARM: dts: am335x-sancloud-bbe: fix phy mode The phy mode should be rgmii-id. For some reason, it used to work with rgmii-txid but doesn't any more. Signed-off-by: Mans Rullgard Signed-off-by: Tony Lindgren --- arch/arm/boot/dts/am335x-sancloud-bbe.dts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/boot/dts/am335x-sancloud-bbe.dts b/arch/arm/boot/dts/am335x-sancloud-bbe.dts index 8678e6e35493f..e5fdb7abb0d54 100644 --- a/arch/arm/boot/dts/am335x-sancloud-bbe.dts +++ b/arch/arm/boot/dts/am335x-sancloud-bbe.dts @@ -108,7 +108,7 @@ &cpsw_emac0 { phy-handle = <ðphy0>; - phy-mode = "rgmii-txid"; + phy-mode = "rgmii-id"; }; &i2c0 { -- GitLab From 90d79edc57a5625b9adaf397719e3a760b051167 Mon Sep 17 00:00:00 2001 From: Grygorii Strashko Date: Wed, 4 Dec 2019 19:45:33 +0200 Subject: [PATCH 0406/1437] ARM: omap2plus_defconfig: enable NET_SWITCHDEV The TI_CPSW_SWITCHDEV definition in Kconfig was changed from "select NET_SWITCHDEV" to "depends on NET_SWITCHDEV", and therefore it is required to explicitelly enable NET_SWITCHDEV config option in omap2plus_defconfig. Fixes: 3727d259ddaf ("arm: omap2plus_defconfig: enable new cpsw switchdev driver") Signed-off-by: Grygorii Strashko Signed-off-by: Tony Lindgren --- arch/arm/configs/omap2plus_defconfig | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/arm/configs/omap2plus_defconfig b/arch/arm/configs/omap2plus_defconfig index 8c37cc8ab6f2b..15e1b2b2410c7 100644 --- a/arch/arm/configs/omap2plus_defconfig +++ b/arch/arm/configs/omap2plus_defconfig @@ -92,6 +92,7 @@ CONFIG_IP_PNP_BOOTP=y CONFIG_IP_PNP_RARP=y CONFIG_NETFILTER=y CONFIG_PHONET=m +CONFIG_NET_SWITCHDEV=y CONFIG_CAN=m CONFIG_CAN_C_CAN=m CONFIG_CAN_C_CAN_PLATFORM=m @@ -181,6 +182,7 @@ CONFIG_SMSC911X=y # CONFIG_NET_VENDOR_STMICRO is not set CONFIG_TI_DAVINCI_EMAC=y CONFIG_TI_CPSW=y +CONFIG_TI_CPSW_SWITCHDEV=y CONFIG_TI_CPTS=y # CONFIG_NET_VENDOR_VIA is not set # CONFIG_NET_VENDOR_WIZNET is not set @@ -556,4 +558,3 @@ CONFIG_DEBUG_INFO_DWARF4=y CONFIG_MAGIC_SYSRQ=y CONFIG_SCHEDSTATS=y # CONFIG_DEBUG_BUGVERBOSE is not set -CONFIG_TI_CPSW_SWITCHDEV=y -- GitLab From e00b59d30506dc9ef91caf2f3c584209cc9f61e4 Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Mon, 9 Dec 2019 13:41:17 -0800 Subject: [PATCH 0407/1437] ARM: omap2plus_defconfig: Add back DEBUG_FS Commit 0e4a459f56c3 ("tracing: Remove unnecessary DEBUG_FS dependency") removed select for DEBUG_FS but we still need it at least for enabling deeper idle states for the SoCs. Signed-off-by: Tony Lindgren --- arch/arm/configs/omap2plus_defconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm/configs/omap2plus_defconfig b/arch/arm/configs/omap2plus_defconfig index 15e1b2b2410c7..c32c338f77042 100644 --- a/arch/arm/configs/omap2plus_defconfig +++ b/arch/arm/configs/omap2plus_defconfig @@ -556,5 +556,6 @@ CONFIG_DEBUG_INFO=y CONFIG_DEBUG_INFO_SPLIT=y CONFIG_DEBUG_INFO_DWARF4=y CONFIG_MAGIC_SYSRQ=y +CONFIG_DEBUG_FS=y CONFIG_SCHEDSTATS=y # CONFIG_DEBUG_BUGVERBOSE is not set -- GitLab From 6e8aeda224c83c7c7841e143d410b6d0e7bda05e Mon Sep 17 00:00:00 2001 From: Lukas Wunner Date: Tue, 10 Dec 2019 14:39:50 +0100 Subject: [PATCH 0408/1437] ALSA: hda/hdmi - Fix duplicate unref of pci_dev Nicholas Johnson reports a null pointer deref as well as a refcount underflow upon hot-removal of a Thunderbolt-attached AMD eGPU. He's bisected the issue down to commit 586bc4aab878 ("ALSA: hda/hdmi - fix vgaswitcheroo detection for AMD"). The commit iterates over PCI devices using pci_get_class() and unreferences each device found, even though pci_get_class() subsequently unreferences the device as well. Fix it. Fixes: 586bc4aab878 ("ALSA: hda/hdmi - fix vgaswitcheroo detection for AMD") Link: https://lore.kernel.org/r/PSXP216MB0438BFEAA0617283A834E11580580@PSXP216MB0438.KORP216.PROD.OUTLOOK.COM/ Reported-and-tested-by: Nicholas Johnson Signed-off-by: Lukas Wunner Reviewed-by: Alex Deucher Cc: Mika Westerberg Cc: Alexander Deucher Cc: Bjorn Helgaas Link: https://lore.kernel.org/r/77aa6c01aefe1ebc4004e87b0bc714f2759f15c4.1575985006.git.lukas@wunner.de Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_intel.c | 1 - 1 file changed, 1 deletion(-) diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 35b4526f0d287..b856b89378ac5 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -1419,7 +1419,6 @@ static bool atpx_present(void) return true; } } - pci_dev_put(pdev); } return false; } -- GitLab From af74262337faa65d5ac2944553437d3f5fb29123 Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (VMware)" Date: Mon, 9 Dec 2019 10:33:30 -0500 Subject: [PATCH 0409/1437] module: Remove accidental change of module_enable_x() When pulling in Divya Indi's patch, I made a minor fix to remove unneeded braces. I commited my fix up via "git commit -a --amend". Unfortunately, I didn't realize I had some changes I was testing in the module code, and those changes were applied to Divya's patch as well. This reverts the accidental updates to the module code. Cc: Jessica Yu Cc: Divya Indi Reported-by: Peter Zijlstra Fixes: e585e6469d6f ("tracing: Verify if trace array exists before destroying it.") Signed-off-by: Steven Rostedt (VMware) --- kernel/module.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/kernel/module.c b/kernel/module.c index 6e2fd40a6ed99..ff2d7359a4189 100644 --- a/kernel/module.c +++ b/kernel/module.c @@ -3728,6 +3728,7 @@ static int complete_formation(struct module *mod, struct load_info *info) module_enable_ro(mod, false); module_enable_nx(mod); + module_enable_x(mod); /* Mark state as coming so strong_try_module_get() ignores us, * but kallsyms etc. can see us. */ @@ -3750,11 +3751,6 @@ static int prepare_coming_module(struct module *mod) if (err) return err; - /* Make module executable after ftrace is enabled */ - mutex_lock(&module_mutex); - module_enable_x(mod); - mutex_unlock(&module_mutex); - blocking_notifier_call_chain(&module_notify_list, MODULE_STATE_COMING, mod); return 0; -- GitLab From a61f810567be0ef101d8bbb65b89ffeac7d62103 Mon Sep 17 00:00:00 2001 From: YueHaibing Date: Sat, 7 Dec 2019 11:44:09 +0800 Subject: [PATCH 0410/1437] tracing: remove set but not used variable 'buffer' kernel/trace/trace_events_inject.c: In function trace_inject_entry: kernel/trace/trace_events_inject.c:20:22: warning: variable buffer set but not used [-Wunused-but-set-variable] It is never used, so remove it. Link: http://lkml.kernel.org/r/20191207034409.25668-1-yuehaibing@huawei.com Reported-by: Hulk Robot Acked-by: Cong Wang Signed-off-by: YueHaibing Signed-off-by: Steven Rostedt (VMware) --- kernel/trace/trace_events_inject.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/kernel/trace/trace_events_inject.c b/kernel/trace/trace_events_inject.c index d43710718ee59..d45079ee62f8d 100644 --- a/kernel/trace/trace_events_inject.c +++ b/kernel/trace/trace_events_inject.c @@ -17,12 +17,10 @@ static int trace_inject_entry(struct trace_event_file *file, void *rec, int len) { struct trace_event_buffer fbuffer; - struct ring_buffer *buffer; int written = 0; void *entry; rcu_read_lock_sched(); - buffer = file->tr->trace_buffer.buffer; entry = trace_event_buffer_reserve(&fbuffer, file, len); if (entry) { memcpy(entry, rec, len); -- GitLab From ff205766dbbee024a4a716638868d98ffb17748a Mon Sep 17 00:00:00 2001 From: Alexei Starovoitov Date: Sun, 8 Dec 2019 16:01:12 -0800 Subject: [PATCH 0411/1437] ftrace: Fix function_graph tracer interaction with BPF trampoline Depending on type of BPF programs served by BPF trampoline it can call original function. In such case the trampoline will skip one stack frame while returning. That will confuse function_graph tracer and will cause crashes with bad RIP. Teach graph tracer to skip functions that have BPF trampoline attached. Signed-off-by: Alexei Starovoitov Signed-off-by: Steven Rostedt (VMware) --- arch/x86/kernel/ftrace.c | 14 -------------- include/linux/ftrace.h | 5 +++++ kernel/trace/fgraph.c | 9 +++++++++ kernel/trace/ftrace.c | 19 +++++++------------ 4 files changed, 21 insertions(+), 26 deletions(-) diff --git a/arch/x86/kernel/ftrace.c b/arch/x86/kernel/ftrace.c index 060a361d9d11b..024c3053dbbab 100644 --- a/arch/x86/kernel/ftrace.c +++ b/arch/x86/kernel/ftrace.c @@ -1042,20 +1042,6 @@ void prepare_ftrace_return(unsigned long self_addr, unsigned long *parent, if (unlikely(atomic_read(¤t->tracing_graph_pause))) return; - /* - * If the return location is actually pointing directly to - * the start of a direct trampoline (if we trace the trampoline - * it will still be offset by MCOUNT_INSN_SIZE), then the - * return address is actually off by one word, and we - * need to adjust for that. - */ - if (ftrace_direct_func_count) { - if (ftrace_find_direct_func(self_addr + MCOUNT_INSN_SIZE)) { - self_addr = *parent; - parent++; - } - } - /* * Protect against fault, even if it shouldn't * happen. This tool is too much intrusive to diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h index 232806d5689de..987c2dc55bde5 100644 --- a/include/linux/ftrace.h +++ b/include/linux/ftrace.h @@ -264,6 +264,7 @@ int ftrace_modify_direct_caller(struct ftrace_func_entry *entry, struct dyn_ftrace *rec, unsigned long old_addr, unsigned long new_addr); +unsigned long ftrace_find_rec_direct(unsigned long ip); #else # define ftrace_direct_func_count 0 static inline int register_ftrace_direct(unsigned long ip, unsigned long addr) @@ -290,6 +291,10 @@ static inline int ftrace_modify_direct_caller(struct ftrace_func_entry *entry, { return -ENODEV; } +static inline unsigned long ftrace_find_rec_direct(unsigned long ip) +{ + return 0; +} #endif /* CONFIG_DYNAMIC_FTRACE_WITH_DIRECT_CALLS */ #ifndef CONFIG_HAVE_DYNAMIC_FTRACE_WITH_DIRECT_CALLS diff --git a/kernel/trace/fgraph.c b/kernel/trace/fgraph.c index 67e0c462b059c..a2659735db73b 100644 --- a/kernel/trace/fgraph.c +++ b/kernel/trace/fgraph.c @@ -101,6 +101,15 @@ int function_graph_enter(unsigned long ret, unsigned long func, { struct ftrace_graph_ent trace; + /* + * Skip graph tracing if the return location is served by direct trampoline, + * since call sequence and return addresses is unpredicatable anymore. + * Ex: BPF trampoline may call original function and may skip frame + * depending on type of BPF programs attached. + */ + if (ftrace_direct_func_count && + ftrace_find_rec_direct(ret - MCOUNT_INSN_SIZE)) + return -EBUSY; trace.func = func; trace.depth = ++current->curr_ret_depth; diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c index caae523f4ef3a..57477dc683db5 100644 --- a/kernel/trace/ftrace.c +++ b/kernel/trace/ftrace.c @@ -2364,7 +2364,7 @@ int ftrace_direct_func_count; * Search the direct_functions hash to see if the given instruction pointer * has a direct caller attached to it. */ -static unsigned long find_rec_direct(unsigned long ip) +unsigned long ftrace_find_rec_direct(unsigned long ip) { struct ftrace_func_entry *entry; @@ -2380,7 +2380,7 @@ static void call_direct_funcs(unsigned long ip, unsigned long pip, { unsigned long addr; - addr = find_rec_direct(ip); + addr = ftrace_find_rec_direct(ip); if (!addr) return; @@ -2393,11 +2393,6 @@ struct ftrace_ops direct_ops = { | FTRACE_OPS_FL_DIRECT | FTRACE_OPS_FL_SAVE_REGS | FTRACE_OPS_FL_PERMANENT, }; -#else -static inline unsigned long find_rec_direct(unsigned long ip) -{ - return 0; -} #endif /* CONFIG_DYNAMIC_FTRACE_WITH_DIRECT_CALLS */ /** @@ -2417,7 +2412,7 @@ unsigned long ftrace_get_addr_new(struct dyn_ftrace *rec) if ((rec->flags & FTRACE_FL_DIRECT) && (ftrace_rec_count(rec) == 1)) { - addr = find_rec_direct(rec->ip); + addr = ftrace_find_rec_direct(rec->ip); if (addr) return addr; WARN_ON_ONCE(1); @@ -2458,7 +2453,7 @@ unsigned long ftrace_get_addr_curr(struct dyn_ftrace *rec) /* Direct calls take precedence over trampolines */ if (rec->flags & FTRACE_FL_DIRECT_EN) { - addr = find_rec_direct(rec->ip); + addr = ftrace_find_rec_direct(rec->ip); if (addr) return addr; WARN_ON_ONCE(1); @@ -3604,7 +3599,7 @@ static int t_show(struct seq_file *m, void *v) if (rec->flags & FTRACE_FL_DIRECT) { unsigned long direct; - direct = find_rec_direct(rec->ip); + direct = ftrace_find_rec_direct(rec->ip); if (direct) seq_printf(m, "\n\tdirect-->%pS", (void *)direct); } @@ -5008,7 +5003,7 @@ int register_ftrace_direct(unsigned long ip, unsigned long addr) mutex_lock(&direct_mutex); /* See if there's a direct function at @ip already */ - if (find_rec_direct(ip)) + if (ftrace_find_rec_direct(ip)) goto out_unlock; ret = -ENODEV; @@ -5027,7 +5022,7 @@ int register_ftrace_direct(unsigned long ip, unsigned long addr) if (ip != rec->ip) { ip = rec->ip; /* Need to check this ip for a direct. */ - if (find_rec_direct(ip)) + if (ftrace_find_rec_direct(ip)) goto out_unlock; } -- GitLab From 30e647a764d446723a7e0fb08d209e0104f16173 Mon Sep 17 00:00:00 2001 From: Stefan Wahren Date: Sat, 30 Nov 2019 13:31:13 +0100 Subject: [PATCH 0412/1437] ARM: dts: bcm283x: Fix critical trip point During definition of the CPU thermal zone of BCM283x SoC family there was a misunderstanding of the meaning "criticial trip point" and the thermal throttling range of the VideoCore firmware. The latter one takes effect when the core temperature is at least 85 degree celsius or higher So the current critical trip point doesn't make sense, because the thermal shutdown appears before the firmware has a chance to throttle the ARM core(s). Fix these unwanted shutdowns by increasing the critical trip point to a value which shouldn't be reached with working thermal throttling. Fixes: 0fe4d2181cc4 ("ARM: dts: bcm283x: Add CPU thermal zone with 1 trip point") Signed-off-by: Stefan Wahren Signed-off-by: Florian Fainelli --- arch/arm/boot/dts/bcm283x.dtsi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/boot/dts/bcm283x.dtsi b/arch/arm/boot/dts/bcm283x.dtsi index 3caaa57eb6c81..839491628e87b 100644 --- a/arch/arm/boot/dts/bcm283x.dtsi +++ b/arch/arm/boot/dts/bcm283x.dtsi @@ -37,7 +37,7 @@ trips { cpu-crit { - temperature = <80000>; + temperature = <90000>; hysteresis = <0>; type = "critical"; }; -- GitLab From 3d519d6d388ba4f9a75d0e0b6f60d890987bc096 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Mon, 25 Nov 2019 16:16:21 +0900 Subject: [PATCH 0413/1437] sh: kgdb: Mark expected switch fall-throughs Mark switch cases where we are expecting to fall through. This patch fixes the following error: LINUX/arch/sh/kernel/kgdb.c: In function 'kgdb_arch_handle_exception': LINUX/arch/sh/kernel/kgdb.c:267:6: error: this statement may fall through [-Werror=implicit-fallthrough=] if (kgdb_hex2long(&ptr, &addr)) ^ LINUX/arch/sh/kernel/kgdb.c:269:2: note: here case 'D': ^~~~ Signed-off-by: Kuninori Morimoto Acked-by: Daniel Thompson Signed-off-by: Gustavo A. R. Silva --- arch/sh/kernel/kgdb.c | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/sh/kernel/kgdb.c b/arch/sh/kernel/kgdb.c index 6d61f8cf4c131..0d5f3c9d52f30 100644 --- a/arch/sh/kernel/kgdb.c +++ b/arch/sh/kernel/kgdb.c @@ -266,6 +266,7 @@ int kgdb_arch_handle_exception(int e_vector, int signo, int err_code, ptr = &remcomInBuffer[1]; if (kgdb_hex2long(&ptr, &addr)) linux_regs->pc = addr; + /* fallthrough */ case 'D': case 'k': atomic_set(&kgdb_cpu_doing_single_step, -1); -- GitLab From 1f00534a764aed160652770e0c0a63657c231fde Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 10 Dec 2019 21:30:46 +0100 Subject: [PATCH 0414/1437] drm/amd/display: fix undefined struct member reference An initialization was added for two optional struct members. One of these is always present in the dcn20_resource file, but the other one depends on CONFIG_DRM_AMD_DC_DSC_SUPPORT and causes a build failure if that is missing: drivers/gpu/drm/amd/amdgpu/../display/dc/dcn20/dcn20_resource.c:926:14: error: excess elements in struct initializer [-Werror] .num_dsc = 5, Add another #ifdef around the assignment. Fixes: c3d03c5a196f ("drm/amd/display: Include num_vmid and num_dsc within NV14's resource caps") Reviewed-by: Zhan Liu Signed-off-by: Arnd Bergmann Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/display/dc/dcn20/dcn20_resource.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/gpu/drm/amd/display/dc/dcn20/dcn20_resource.c b/drivers/gpu/drm/amd/display/dc/dcn20/dcn20_resource.c index 06f9b71591f1c..23ff2f1c75b5c 100644 --- a/drivers/gpu/drm/amd/display/dc/dcn20/dcn20_resource.c +++ b/drivers/gpu/drm/amd/display/dc/dcn20/dcn20_resource.c @@ -923,7 +923,9 @@ static const struct resource_caps res_cap_nv14 = { .num_dwb = 1, .num_ddc = 5, .num_vmid = 16, +#ifdef CONFIG_DRM_AMD_DC_DSC_SUPPORT .num_dsc = 5, +#endif }; static const struct dc_debug_options debug_defaults_drv = { -- GitLab From 2c2f00ab1641895183488ff2bce53c415344fb87 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Fri, 6 Dec 2019 01:23:22 +0100 Subject: [PATCH 0415/1437] i2c: remove i2c_new_dummy() API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit All in-kernel users have been converted to {devm_}i2c_new_dummy_device(). Remove the old API. Signed-off-by: Wolfram Sang Reviewed-by: Niklas Söderlund Tested-by: Luca Ceresoli Reviewed-by: Luca Ceresoli Signed-off-by: Wolfram Sang --- drivers/i2c/i2c-core-base.c | 23 ----------------------- include/linux/i2c.h | 6 ------ 2 files changed, 29 deletions(-) diff --git a/drivers/i2c/i2c-core-base.c b/drivers/i2c/i2c-core-base.c index 9333c865d4a9a..9f8dcd3f83850 100644 --- a/drivers/i2c/i2c-core-base.c +++ b/drivers/i2c/i2c-core-base.c @@ -896,29 +896,6 @@ struct i2c_client *i2c_new_dummy_device(struct i2c_adapter *adapter, u16 address } EXPORT_SYMBOL_GPL(i2c_new_dummy_device); -/** - * i2c_new_dummy - return a new i2c device bound to a dummy driver - * @adapter: the adapter managing the device - * @address: seven bit address to be used - * Context: can sleep - * - * This deprecated function has the same functionality as @i2c_new_dummy_device, - * it just returns NULL instead of an ERR_PTR in case of an error for - * compatibility with current I2C API. It will be removed once all users are - * converted. - * - * This returns the new i2c client, which should be saved for later use with - * i2c_unregister_device(); or NULL to indicate an error. - */ -struct i2c_client *i2c_new_dummy(struct i2c_adapter *adapter, u16 address) -{ - struct i2c_client *ret; - - ret = i2c_new_dummy_device(adapter, address); - return IS_ERR(ret) ? NULL : ret; -} -EXPORT_SYMBOL_GPL(i2c_new_dummy); - struct i2c_dummy_devres { struct i2c_client *client; }; diff --git a/include/linux/i2c.h b/include/linux/i2c.h index d2f786706657b..d1baf8d57536d 100644 --- a/include/linux/i2c.h +++ b/include/linux/i2c.h @@ -466,12 +466,6 @@ i2c_new_probed_device(struct i2c_adapter *adap, /* Common custom probe functions */ extern int i2c_probe_func_quick_read(struct i2c_adapter *adap, unsigned short addr); -/* For devices that use several addresses, use i2c_new_dummy() to make - * client handles for the extra addresses. - */ -extern struct i2c_client * -i2c_new_dummy(struct i2c_adapter *adap, u16 address); - extern struct i2c_client * i2c_new_dummy_device(struct i2c_adapter *adapter, u16 address); -- GitLab From 3ac61258599b5abe45d97b926d44a79262385bae Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Sun, 8 Dec 2019 20:35:38 -0800 Subject: [PATCH 0416/1437] i2c: fix header file kernel-doc warning Fix kernel-doc warning in . ../include/linux/i2c.h:337: warning: Function parameter or member 'init_irq' not described in 'i2c_client' Signed-off-by: Randy Dunlap Signed-off-by: Wolfram Sang --- include/linux/i2c.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/linux/i2c.h b/include/linux/i2c.h index d1baf8d57536d..91954324f985e 100644 --- a/include/linux/i2c.h +++ b/include/linux/i2c.h @@ -300,6 +300,7 @@ struct i2c_driver { * generic enough to hide second-sourcing and compatible revisions. * @adapter: manages the bus segment hosting this I2C device * @dev: Driver model device node for the slave. + * @init_irq: IRQ that was set at initialization * @irq: indicates the IRQ generated by this device (if any) * @detected: member of an i2c_driver.clients list or i2c-core's * userspace_devices list -- GitLab From 07b67280037303845f480590986549b266cb3ad6 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 10 Dec 2019 20:59:24 +0100 Subject: [PATCH 0417/1437] drm/amd/display: include linux/slab.h where needed Calling kzalloc() and related functions requires the linux/slab.h header to be included: drivers/gpu/drm/amd/amdgpu/../display/dc/dcn21/dcn21_resource.c: In function 'dcn21_ipp_create': drivers/gpu/drm/amd/amdgpu/../display/dc/dcn21/dcn21_resource.c:679:3: error: implicit declaration of function 'kzalloc'; did you mean 'd_alloc'? [-Werror=implicit-function-declaration] kzalloc(sizeof(struct dcn10_ipp), GFP_KERNEL); A lot of other headers also miss a direct include in this file, but this is the only one that causes a problem for now. Signed-off-by: Arnd Bergmann Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/display/dc/dcn21/dcn21_resource.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/gpu/drm/amd/display/dc/dcn21/dcn21_resource.c b/drivers/gpu/drm/amd/display/dc/dcn21/dcn21_resource.c index c28cb5d50cf46..b29b2c99a564e 100644 --- a/drivers/gpu/drm/amd/display/dc/dcn21/dcn21_resource.c +++ b/drivers/gpu/drm/amd/display/dc/dcn21/dcn21_resource.c @@ -23,6 +23,8 @@ * */ +#include + #include "dm_services.h" #include "dc.h" -- GitLab From b0142d66f4edb8578b7772d6d7ad731836b82ddb Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 10 Dec 2019 12:48:35 +0100 Subject: [PATCH 0418/1437] cpuidle: Fix cpuidle_driver_state_disabled() It turns out that cpuidle_driver_state_disabled() can be called before registering the cpufreq driver on some platforms, which was not expected when it was introduced and which leads to a NULL pointer dereference when trying to walk the CPUs associated with the given cpuidle driver. Fix the problem by making cpuidle_driver_state_disabled() check if the driver's mask of CPUs associated with it is present and to set CPUIDLE_FLAG_UNUSABLE for the given idle state in the driver's states list if that is not the case to cause __cpuidle_register_device() to set CPUIDLE_STATE_DISABLED_BY_DRIVER for that state for all cpuidle devices registered by it later. Fixes: cbda56d5fefc ("cpuidle: Introduce cpuidle_driver_state_disabled() for driver quirks") Reported-by: Daniel Lezcano Tested-by: Daniel Lezcano Signed-off-by: Rafael J. Wysocki --- drivers/cpuidle/driver.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/cpuidle/driver.c b/drivers/cpuidle/driver.c index c76423aaef4d7..ce6a5f80fb838 100644 --- a/drivers/cpuidle/driver.c +++ b/drivers/cpuidle/driver.c @@ -403,6 +403,13 @@ void cpuidle_driver_state_disabled(struct cpuidle_driver *drv, int idx, mutex_lock(&cpuidle_lock); + spin_lock(&cpuidle_driver_lock); + + if (!drv->cpumask) { + drv->states[idx].flags |= CPUIDLE_FLAG_UNUSABLE; + goto unlock; + } + for_each_cpu(cpu, drv->cpumask) { struct cpuidle_device *dev = per_cpu(cpuidle_devices, cpu); @@ -415,5 +422,8 @@ void cpuidle_driver_state_disabled(struct cpuidle_driver *drv, int idx, dev->states_usage[idx].disable &= ~CPUIDLE_STATE_DISABLED_BY_DRIVER; } +unlock: + spin_unlock(&cpuidle_driver_lock); + mutex_unlock(&cpuidle_lock); } -- GitLab From c571b72e2b845ca0519670cb7c4b5fe5f56498a5 Mon Sep 17 00:00:00 2001 From: Davidlohr Bueso Date: Tue, 10 Dec 2019 14:05:23 -0800 Subject: [PATCH 0419/1437] Revert "locking/mutex: Complain upon mutex API misuse in IRQ contexts" This ended up causing some noise in places such as rxrpc running in softirq. The warning is misleading in this case as the mutex trylock and unlock operations are done within the same context; and therefore we need not worry about the PI-boosting issues that comes along with no single-owner lock guarantees. While we don't want to support this in mutexes, there is no way out of this yet; so lets get rid of the WARNs for now, as it is only fair to code that has historically relied on non-preemptible softirq guarantees. In addition, changing the lock type is also unviable: exclusive rwsems have the same issue (just not the WARN_ON) and counting semaphores would introduce a performance hit as mutexes are a lot more optimized. This reverts: a0855d24fc22: ("locking/mutex: Complain upon mutex API misuse in IRQ contexts") Fixes: a0855d24fc22: ("locking/mutex: Complain upon mutex API misuse in IRQ contexts") Signed-off-by: Davidlohr Bueso Tested-by: David Howells Cc: Linus Torvalds Cc: Peter Zijlstra Cc: Thomas Gleixner Cc: linux-afs@lists.infradead.org Cc: linux-fsdevel@vger.kernel.org Cc: will@kernel.org Link: https://lkml.kernel.org/r/20191210220523.28540-1-dave@stgolabs.net Signed-off-by: Ingo Molnar --- kernel/locking/mutex.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/kernel/locking/mutex.c b/kernel/locking/mutex.c index 54cc5f9286e93..5352ce50a97e3 100644 --- a/kernel/locking/mutex.c +++ b/kernel/locking/mutex.c @@ -733,9 +733,6 @@ static noinline void __sched __mutex_unlock_slowpath(struct mutex *lock, unsigne */ void __sched mutex_unlock(struct mutex *lock) { -#ifdef CONFIG_DEBUG_MUTEXES - WARN_ON(in_interrupt()); -#endif #ifndef CONFIG_DEBUG_LOCK_ALLOC if (__mutex_unlock_fast(lock)) return; @@ -1416,7 +1413,6 @@ int __sched mutex_trylock(struct mutex *lock) #ifdef CONFIG_DEBUG_MUTEXES DEBUG_LOCKS_WARN_ON(lock->magic != lock); - WARN_ON(in_interrupt()); #endif locked = __mutex_trylock(lock); -- GitLab From 4e88d6e7793f2f445f43bd608828541d7f43b608 Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Sat, 7 Dec 2019 20:59:47 -0700 Subject: [PATCH 0420/1437] io_uring: allow unbreakable links MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some commands will invariably end in a failure in the sense that the completion result will be less than zero. One such example is timeouts that don't have a completion count set, they will always complete with -ETIME unless cancelled. For linked commands, we sever links and fail the rest of the chain if the result is less than zero. Since we have commands where we know that will happen, add IOSQE_IO_HARDLINK as a stronger link that doesn't sever regardless of the completion result. Note that the link will still sever if we fail submitting the parent request, hard links are only resilient in the presence of completion results for requests that did submit correctly. Cc: stable@vger.kernel.org # v5.4 Reviewed-by: Pavel Begunkov Reported-by: 李通洲 Signed-off-by: Jens Axboe --- fs/io_uring.c | 84 +++++++++++++++++++---------------- include/uapi/linux/io_uring.h | 1 + 2 files changed, 47 insertions(+), 38 deletions(-) diff --git a/fs/io_uring.c b/fs/io_uring.c index 405be10da73d4..040e6c11ff375 100644 --- a/fs/io_uring.c +++ b/fs/io_uring.c @@ -377,6 +377,7 @@ struct io_kiocb { #define REQ_F_TIMEOUT_NOSEQ 8192 /* no timeout sequence */ #define REQ_F_INFLIGHT 16384 /* on inflight list */ #define REQ_F_COMP_LOCKED 32768 /* completion under lock */ +#define REQ_F_HARDLINK 65536 /* doesn't sever on completion < 0 */ u64 user_data; u32 result; u32 sequence; @@ -1292,6 +1293,12 @@ static void kiocb_end_write(struct io_kiocb *req) file_end_write(req->file); } +static inline void req_set_fail_links(struct io_kiocb *req) +{ + if ((req->flags & (REQ_F_LINK | REQ_F_HARDLINK)) == REQ_F_LINK) + req->flags |= REQ_F_FAIL_LINK; +} + static void io_complete_rw_common(struct kiocb *kiocb, long res) { struct io_kiocb *req = container_of(kiocb, struct io_kiocb, rw); @@ -1299,8 +1306,8 @@ static void io_complete_rw_common(struct kiocb *kiocb, long res) if (kiocb->ki_flags & IOCB_WRITE) kiocb_end_write(req); - if ((req->flags & REQ_F_LINK) && res != req->result) - req->flags |= REQ_F_FAIL_LINK; + if (res != req->result) + req_set_fail_links(req); io_cqring_add_event(req, res); } @@ -1330,8 +1337,8 @@ static void io_complete_rw_iopoll(struct kiocb *kiocb, long res, long res2) if (kiocb->ki_flags & IOCB_WRITE) kiocb_end_write(req); - if ((req->flags & REQ_F_LINK) && res != req->result) - req->flags |= REQ_F_FAIL_LINK; + if (res != req->result) + req_set_fail_links(req); req->result = res; if (res != -EAGAIN) req->flags |= REQ_F_IOPOLL_COMPLETED; @@ -1956,8 +1963,8 @@ static int io_fsync(struct io_kiocb *req, const struct io_uring_sqe *sqe, end > 0 ? end : LLONG_MAX, fsync_flags & IORING_FSYNC_DATASYNC); - if (ret < 0 && (req->flags & REQ_F_LINK)) - req->flags |= REQ_F_FAIL_LINK; + if (ret < 0) + req_set_fail_links(req); io_cqring_add_event(req, ret); io_put_req_find_next(req, nxt); return 0; @@ -2003,8 +2010,8 @@ static int io_sync_file_range(struct io_kiocb *req, ret = sync_file_range(req->rw.ki_filp, sqe_off, sqe_len, flags); - if (ret < 0 && (req->flags & REQ_F_LINK)) - req->flags |= REQ_F_FAIL_LINK; + if (ret < 0) + req_set_fail_links(req); io_cqring_add_event(req, ret); io_put_req_find_next(req, nxt); return 0; @@ -2079,8 +2086,8 @@ static int io_sendmsg(struct io_kiocb *req, const struct io_uring_sqe *sqe, out: io_cqring_add_event(req, ret); - if (ret < 0 && (req->flags & REQ_F_LINK)) - req->flags |= REQ_F_FAIL_LINK; + if (ret < 0) + req_set_fail_links(req); io_put_req_find_next(req, nxt); return 0; #else @@ -2161,8 +2168,8 @@ static int io_recvmsg(struct io_kiocb *req, const struct io_uring_sqe *sqe, out: io_cqring_add_event(req, ret); - if (ret < 0 && (req->flags & REQ_F_LINK)) - req->flags |= REQ_F_FAIL_LINK; + if (ret < 0) + req_set_fail_links(req); io_put_req_find_next(req, nxt); return 0; #else @@ -2196,8 +2203,8 @@ static int io_accept(struct io_kiocb *req, const struct io_uring_sqe *sqe, } if (ret == -ERESTARTSYS) ret = -EINTR; - if (ret < 0 && (req->flags & REQ_F_LINK)) - req->flags |= REQ_F_FAIL_LINK; + if (ret < 0) + req_set_fail_links(req); io_cqring_add_event(req, ret); io_put_req_find_next(req, nxt); return 0; @@ -2263,8 +2270,8 @@ static int io_connect(struct io_kiocb *req, const struct io_uring_sqe *sqe, if (ret == -ERESTARTSYS) ret = -EINTR; out: - if (ret < 0 && (req->flags & REQ_F_LINK)) - req->flags |= REQ_F_FAIL_LINK; + if (ret < 0) + req_set_fail_links(req); io_cqring_add_event(req, ret); io_put_req_find_next(req, nxt); return 0; @@ -2340,8 +2347,8 @@ static int io_poll_remove(struct io_kiocb *req, const struct io_uring_sqe *sqe) spin_unlock_irq(&ctx->completion_lock); io_cqring_add_event(req, ret); - if (ret < 0 && (req->flags & REQ_F_LINK)) - req->flags |= REQ_F_FAIL_LINK; + if (ret < 0) + req_set_fail_links(req); io_put_req(req); return 0; } @@ -2399,8 +2406,8 @@ static void io_poll_complete_work(struct io_wq_work **workptr) io_cqring_ev_posted(ctx); - if (ret < 0 && req->flags & REQ_F_LINK) - req->flags |= REQ_F_FAIL_LINK; + if (ret < 0) + req_set_fail_links(req); io_put_req_find_next(req, &nxt); if (nxt) *workptr = &nxt->work; @@ -2582,8 +2589,7 @@ static enum hrtimer_restart io_timeout_fn(struct hrtimer *timer) spin_unlock_irqrestore(&ctx->completion_lock, flags); io_cqring_ev_posted(ctx); - if (req->flags & REQ_F_LINK) - req->flags |= REQ_F_FAIL_LINK; + req_set_fail_links(req); io_put_req(req); return HRTIMER_NORESTART; } @@ -2608,8 +2614,7 @@ static int io_timeout_cancel(struct io_ring_ctx *ctx, __u64 user_data) if (ret == -1) return -EALREADY; - if (req->flags & REQ_F_LINK) - req->flags |= REQ_F_FAIL_LINK; + req_set_fail_links(req); io_cqring_fill_event(req, -ECANCELED); io_put_req(req); return 0; @@ -2640,8 +2645,8 @@ static int io_timeout_remove(struct io_kiocb *req, io_commit_cqring(ctx); spin_unlock_irq(&ctx->completion_lock); io_cqring_ev_posted(ctx); - if (ret < 0 && req->flags & REQ_F_LINK) - req->flags |= REQ_F_FAIL_LINK; + if (ret < 0) + req_set_fail_links(req); io_put_req(req); return 0; } @@ -2822,8 +2827,8 @@ done: spin_unlock_irqrestore(&ctx->completion_lock, flags); io_cqring_ev_posted(ctx); - if (ret < 0 && (req->flags & REQ_F_LINK)) - req->flags |= REQ_F_FAIL_LINK; + if (ret < 0) + req_set_fail_links(req); io_put_req_find_next(req, nxt); } @@ -3044,8 +3049,7 @@ static void io_wq_submit_work(struct io_wq_work **workptr) io_put_req(req); if (ret) { - if (req->flags & REQ_F_LINK) - req->flags |= REQ_F_FAIL_LINK; + req_set_fail_links(req); io_cqring_add_event(req, ret); io_put_req(req); } @@ -3179,8 +3183,7 @@ static enum hrtimer_restart io_link_timeout_fn(struct hrtimer *timer) spin_unlock_irqrestore(&ctx->completion_lock, flags); if (prev) { - if (prev->flags & REQ_F_LINK) - prev->flags |= REQ_F_FAIL_LINK; + req_set_fail_links(prev); io_async_find_and_cancel(ctx, req, prev->user_data, NULL, -ETIME); io_put_req(prev); @@ -3273,8 +3276,7 @@ err: /* and drop final reference, if we failed */ if (ret) { io_cqring_add_event(req, ret); - if (req->flags & REQ_F_LINK) - req->flags |= REQ_F_FAIL_LINK; + req_set_fail_links(req); io_put_req(req); } } @@ -3293,8 +3295,7 @@ static void io_queue_sqe(struct io_kiocb *req) if (ret) { if (ret != -EIOCBQUEUED) { io_cqring_add_event(req, ret); - if (req->flags & REQ_F_LINK) - req->flags |= REQ_F_FAIL_LINK; + req_set_fail_links(req); io_double_put_req(req); } } else @@ -3311,7 +3312,8 @@ static inline void io_queue_link_head(struct io_kiocb *req) } -#define SQE_VALID_FLAGS (IOSQE_FIXED_FILE|IOSQE_IO_DRAIN|IOSQE_IO_LINK) +#define SQE_VALID_FLAGS (IOSQE_FIXED_FILE|IOSQE_IO_DRAIN|IOSQE_IO_LINK| \ + IOSQE_IO_HARDLINK) static bool io_submit_sqe(struct io_kiocb *req, struct io_submit_state *state, struct io_kiocb **link) @@ -3349,6 +3351,9 @@ err_req: if (req->sqe->flags & IOSQE_IO_DRAIN) (*link)->flags |= REQ_F_DRAIN_LINK | REQ_F_IO_DRAIN; + if (req->sqe->flags & IOSQE_IO_HARDLINK) + req->flags |= REQ_F_HARDLINK; + io = kmalloc(sizeof(*io), GFP_KERNEL); if (!io) { ret = -EAGAIN; @@ -3358,13 +3363,16 @@ err_req: ret = io_req_defer_prep(req, io); if (ret) { kfree(io); + /* fail even hard links since we don't submit */ prev->flags |= REQ_F_FAIL_LINK; goto err_req; } trace_io_uring_link(ctx, req, prev); list_add_tail(&req->link_list, &prev->link_list); - } else if (req->sqe->flags & IOSQE_IO_LINK) { + } else if (req->sqe->flags & (IOSQE_IO_LINK|IOSQE_IO_HARDLINK)) { req->flags |= REQ_F_LINK; + if (req->sqe->flags & IOSQE_IO_HARDLINK) + req->flags |= REQ_F_HARDLINK; INIT_LIST_HEAD(&req->link_list); *link = req; diff --git a/include/uapi/linux/io_uring.h b/include/uapi/linux/io_uring.h index eabccb46edd12..ea231366f5fd7 100644 --- a/include/uapi/linux/io_uring.h +++ b/include/uapi/linux/io_uring.h @@ -48,6 +48,7 @@ struct io_uring_sqe { #define IOSQE_FIXED_FILE (1U << 0) /* use fixed fileset */ #define IOSQE_IO_DRAIN (1U << 1) /* issue after inflight IO */ #define IOSQE_IO_LINK (1U << 2) /* links next sqe */ +#define IOSQE_IO_HARDLINK (1U << 3) /* like LINK, but stronger */ /* * io_uring_setup() flags -- GitLab From 506d95ff5d6aa0a099a116c49d3884e29801d843 Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Sat, 7 Dec 2019 21:03:59 -0700 Subject: [PATCH 0421/1437] io-wq: remove worker->wait waitqueue We only have one cases of using the waitqueue to wake the worker, the rest are using wake_up_process(). Since we can save some cycles not fiddling with the waitqueue io_wqe_worker(), switch the work activation to task wakeup and get rid of the now unused wait_queue_head_t in struct io_worker. Signed-off-by: Jens Axboe --- fs/io-wq.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/fs/io-wq.c b/fs/io-wq.c index 74b40506c5d91..6b663ddceb427 100644 --- a/fs/io-wq.c +++ b/fs/io-wq.c @@ -49,7 +49,6 @@ struct io_worker { struct hlist_nulls_node nulls_node; struct list_head all_list; struct task_struct *task; - wait_queue_head_t wait; struct io_wqe *wqe; struct io_wq_work *cur_work; @@ -258,7 +257,7 @@ static bool io_wqe_activate_free_worker(struct io_wqe *wqe) worker = hlist_nulls_entry(n, struct io_worker, nulls_node); if (io_worker_get(worker)) { - wake_up(&worker->wait); + wake_up_process(worker->task); io_worker_release(worker); return true; } @@ -497,13 +496,11 @@ static int io_wqe_worker(void *data) struct io_worker *worker = data; struct io_wqe *wqe = worker->wqe; struct io_wq *wq = wqe->wq; - DEFINE_WAIT(wait); io_worker_start(wqe, worker); while (!test_bit(IO_WQ_BIT_EXIT, &wq->state)) { - prepare_to_wait(&worker->wait, &wait, TASK_INTERRUPTIBLE); - + set_current_state(TASK_INTERRUPTIBLE); spin_lock_irq(&wqe->lock); if (io_wqe_run_queue(wqe)) { __set_current_state(TASK_RUNNING); @@ -526,8 +523,6 @@ static int io_wqe_worker(void *data) break; } - finish_wait(&worker->wait, &wait); - if (test_bit(IO_WQ_BIT_EXIT, &wq->state)) { spin_lock_irq(&wqe->lock); if (!wq_list_empty(&wqe->work_list)) @@ -589,7 +584,6 @@ static bool create_io_worker(struct io_wq *wq, struct io_wqe *wqe, int index) refcount_set(&worker->ref, 1); worker->nulls_node.pprev = NULL; - init_waitqueue_head(&worker->wait); worker->wqe = wqe; spin_lock_init(&worker->lock); -- GitLab From e995d5123ed433e37a8d63ac528737c912592e3d Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Sat, 7 Dec 2019 21:06:46 -0700 Subject: [PATCH 0422/1437] io-wq: briefly spin for new work after finishing work To avoid going to sleep only to get woken shortly thereafter, spin briefly for new work upon completion of work. Signed-off-by: Jens Axboe --- fs/io-wq.c | 24 ++++++++++++++++++++++-- fs/io-wq.h | 7 ++++--- 2 files changed, 26 insertions(+), 5 deletions(-) diff --git a/fs/io-wq.c b/fs/io-wq.c index 6b663ddceb427..90c4978781fb5 100644 --- a/fs/io-wq.c +++ b/fs/io-wq.c @@ -491,26 +491,46 @@ next: } while (1); } +static inline void io_worker_spin_for_work(struct io_wqe *wqe) +{ + int i = 0; + + while (++i < 1000) { + if (io_wqe_run_queue(wqe)) + break; + if (need_resched()) + break; + cpu_relax(); + } +} + static int io_wqe_worker(void *data) { struct io_worker *worker = data; struct io_wqe *wqe = worker->wqe; struct io_wq *wq = wqe->wq; + bool did_work; io_worker_start(wqe, worker); + did_work = false; while (!test_bit(IO_WQ_BIT_EXIT, &wq->state)) { set_current_state(TASK_INTERRUPTIBLE); +loop: + if (did_work) + io_worker_spin_for_work(wqe); spin_lock_irq(&wqe->lock); if (io_wqe_run_queue(wqe)) { __set_current_state(TASK_RUNNING); io_worker_handle_work(worker); - continue; + did_work = true; + goto loop; } + did_work = false; /* drops the lock on success, retry */ if (__io_worker_idle(wqe, worker)) { __release(&wqe->lock); - continue; + goto loop; } spin_unlock_irq(&wqe->lock); if (signal_pending(current)) diff --git a/fs/io-wq.h b/fs/io-wq.h index 7c333a28e2a7e..fb993b2bd0ef0 100644 --- a/fs/io-wq.h +++ b/fs/io-wq.h @@ -35,7 +35,8 @@ static inline void wq_list_add_tail(struct io_wq_work_node *node, struct io_wq_work_list *list) { if (!list->first) { - list->first = list->last = node; + list->last = node; + WRITE_ONCE(list->first, node); } else { list->last->next = node; list->last = node; @@ -47,7 +48,7 @@ static inline void wq_node_del(struct io_wq_work_list *list, struct io_wq_work_node *prev) { if (node == list->first) - list->first = node->next; + WRITE_ONCE(list->first, node->next); if (node == list->last) list->last = prev; if (prev) @@ -58,7 +59,7 @@ static inline void wq_node_del(struct io_wq_work_list *list, #define wq_list_for_each(pos, prv, head) \ for (pos = (head)->first, prv = NULL; pos; prv = pos, pos = (pos)->next) -#define wq_list_empty(list) ((list)->first == NULL) +#define wq_list_empty(list) (READ_ONCE((list)->first) == NULL) #define INIT_WQ_LIST(list) do { \ (list)->first = NULL; \ (list)->last = NULL; \ -- GitLab From 8a4955ff1cca7d4da480774034a16e7c28bafec8 Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Mon, 9 Dec 2019 14:52:35 -0700 Subject: [PATCH 0423/1437] io_uring: sqthread should grab ctx->uring_lock for submissions We use the mutex to guard against registered file updates, for instance. Ensure we're safe in accessing that state against concurrent updates. Signed-off-by: Jens Axboe --- fs/io_uring.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/fs/io_uring.c b/fs/io_uring.c index 040e6c11ff375..366fc351869df 100644 --- a/fs/io_uring.c +++ b/fs/io_uring.c @@ -2996,12 +2996,7 @@ static int io_issue_sqe(struct io_kiocb *req, struct io_kiocb **nxt, if (req->result == -EAGAIN) return -EAGAIN; - /* workqueue context doesn't hold uring_lock, grab it now */ - if (req->in_async) - mutex_lock(&ctx->uring_lock); io_iopoll_req_issued(req); - if (req->in_async) - mutex_unlock(&ctx->uring_lock); } return 0; @@ -3655,7 +3650,9 @@ static int io_sq_thread(void *data) } to_submit = min(to_submit, ctx->sq_entries); + mutex_lock(&ctx->uring_lock); ret = io_submit_sqes(ctx, to_submit, NULL, -1, &cur_mm, true); + mutex_unlock(&ctx->uring_lock); if (ret > 0) inflight += ret; } -- GitLab From d96885658d9971fc2c752b8699f17a42ef745db6 Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Mon, 9 Dec 2019 19:35:20 -0700 Subject: [PATCH 0424/1437] io_uring: deferred send/recvmsg should assign iov Don't just assign it from the main call path, that can miss the case when we're called from issue deferral. Signed-off-by: Jens Axboe --- fs/io_uring.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/io_uring.c b/fs/io_uring.c index 366fc351869df..b84def504201e 100644 --- a/fs/io_uring.c +++ b/fs/io_uring.c @@ -2026,6 +2026,7 @@ static int io_sendmsg_prep(struct io_kiocb *req, struct io_async_ctx *io) flags = READ_ONCE(sqe->msg_flags); msg = (struct user_msghdr __user *)(unsigned long) READ_ONCE(sqe->addr); + io->msg.iov = io->msg.fast_iov; return sendmsg_copy_msghdr(&io->msg.msg, msg, flags, &io->msg.iov); #else return 0; @@ -2061,7 +2062,6 @@ static int io_sendmsg(struct io_kiocb *req, const struct io_uring_sqe *sqe, } else { kmsg = &io.msg.msg; kmsg->msg_name = &addr; - io.msg.iov = io.msg.fast_iov; ret = io_sendmsg_prep(req, &io); if (ret) goto out; @@ -2104,6 +2104,7 @@ static int io_recvmsg_prep(struct io_kiocb *req, struct io_async_ctx *io) flags = READ_ONCE(sqe->msg_flags); msg = (struct user_msghdr __user *)(unsigned long) READ_ONCE(sqe->addr); + io->msg.iov = io->msg.fast_iov; return recvmsg_copy_msghdr(&io->msg.msg, msg, flags, &io->msg.uaddr, &io->msg.iov); #else @@ -2143,7 +2144,6 @@ static int io_recvmsg(struct io_kiocb *req, const struct io_uring_sqe *sqe, } else { kmsg = &io.msg.msg; kmsg->msg_name = &addr; - io.msg.iov = io.msg.fast_iov; ret = io_recvmsg_prep(req, &io); if (ret) goto out; -- GitLab From 392edb45b24337eaa0bc1ecd4e3cf897e662ec61 Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Mon, 9 Dec 2019 17:52:20 -0700 Subject: [PATCH 0425/1437] io_uring: don't dynamically allocate poll data This essentially reverts commit e944475e6984. For high poll ops workloads, like TAO, the dynamic allocation of the wait_queue entry for IORING_OP_POLL_ADD adds considerable extra overhead. Go back to embedding the wait_queue_entry, but keep the usage of wait->private for the pointer stashing. Signed-off-by: Jens Axboe --- fs/io_uring.c | 27 +++++++++++---------------- 1 file changed, 11 insertions(+), 16 deletions(-) diff --git a/fs/io_uring.c b/fs/io_uring.c index b84def504201e..89a2c8b4f8fbe 100644 --- a/fs/io_uring.c +++ b/fs/io_uring.c @@ -293,7 +293,7 @@ struct io_poll_iocb { __poll_t events; bool done; bool canceled; - struct wait_queue_entry *wait; + struct wait_queue_entry wait; }; struct io_timeout_data { @@ -2286,8 +2286,8 @@ static void io_poll_remove_one(struct io_kiocb *req) spin_lock(&poll->head->lock); WRITE_ONCE(poll->canceled, true); - if (!list_empty(&poll->wait->entry)) { - list_del_init(&poll->wait->entry); + if (!list_empty(&poll->wait.entry)) { + list_del_init(&poll->wait.entry); io_queue_async_work(req); } spin_unlock(&poll->head->lock); @@ -2358,7 +2358,6 @@ static void io_poll_complete(struct io_kiocb *req, __poll_t mask, int error) struct io_ring_ctx *ctx = req->ctx; req->poll.done = true; - kfree(req->poll.wait); if (error) io_cqring_fill_event(req, error); else @@ -2396,7 +2395,7 @@ static void io_poll_complete_work(struct io_wq_work **workptr) */ spin_lock_irq(&ctx->completion_lock); if (!mask && ret != -ECANCELED) { - add_wait_queue(poll->head, poll->wait); + add_wait_queue(poll->head, &poll->wait); spin_unlock_irq(&ctx->completion_lock); return; } @@ -2426,7 +2425,7 @@ static int io_poll_wake(struct wait_queue_entry *wait, unsigned mode, int sync, if (mask && !(mask & poll->events)) return 0; - list_del_init(&poll->wait->entry); + list_del_init(&poll->wait.entry); /* * Run completion inline if we can. We're using trylock here because @@ -2467,7 +2466,7 @@ static void io_poll_queue_proc(struct file *file, struct wait_queue_head *head, pt->error = 0; pt->req->poll.head = head; - add_wait_queue(head, pt->req->poll.wait); + add_wait_queue(head, &pt->req->poll.wait); } static void io_poll_req_insert(struct io_kiocb *req) @@ -2496,10 +2495,6 @@ static int io_poll_add(struct io_kiocb *req, const struct io_uring_sqe *sqe, if (!poll->file) return -EBADF; - poll->wait = kmalloc(sizeof(*poll->wait), GFP_KERNEL); - if (!poll->wait) - return -ENOMEM; - req->io = NULL; INIT_IO_WORK(&req->work, io_poll_complete_work); events = READ_ONCE(sqe->poll_events); @@ -2516,9 +2511,9 @@ static int io_poll_add(struct io_kiocb *req, const struct io_uring_sqe *sqe, ipt.error = -EINVAL; /* same as no support for IOCB_CMD_POLL */ /* initialized the list so that we can do list_empty checks */ - INIT_LIST_HEAD(&poll->wait->entry); - init_waitqueue_func_entry(poll->wait, io_poll_wake); - poll->wait->private = poll; + INIT_LIST_HEAD(&poll->wait.entry); + init_waitqueue_func_entry(&poll->wait, io_poll_wake); + poll->wait.private = poll; INIT_LIST_HEAD(&req->list); @@ -2527,14 +2522,14 @@ static int io_poll_add(struct io_kiocb *req, const struct io_uring_sqe *sqe, spin_lock_irq(&ctx->completion_lock); if (likely(poll->head)) { spin_lock(&poll->head->lock); - if (unlikely(list_empty(&poll->wait->entry))) { + if (unlikely(list_empty(&poll->wait.entry))) { if (ipt.error) cancel = true; ipt.error = 0; mask = 0; } if (mask || ipt.error) - list_del_init(&poll->wait->entry); + list_del_init(&poll->wait.entry); else if (cancel) WRITE_ONCE(poll->canceled, true); else if (!poll->done) /* actually waiting for an event */ -- GitLab From 4a0a7a187453e65bdd24b9ede045b4c36b958868 Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Mon, 9 Dec 2019 20:01:01 -0700 Subject: [PATCH 0426/1437] io_uring: run next sqe inline if possible One major use case of linked commands is the ability to run the next link inline, if at all possible. This is done correctly for async offload, but somewhere along the line we lost the ability to do so when we were able to complete a request without having to punt it. Ensure that we do so correctly. Signed-off-by: Jens Axboe --- fs/io_uring.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/fs/io_uring.c b/fs/io_uring.c index 89a2c8b4f8fbe..3dae005bfb567 100644 --- a/fs/io_uring.c +++ b/fs/io_uring.c @@ -3224,13 +3224,14 @@ static struct io_kiocb *io_prep_linked_timeout(struct io_kiocb *req) static void __io_queue_sqe(struct io_kiocb *req) { - struct io_kiocb *linked_timeout = io_prep_linked_timeout(req); + struct io_kiocb *linked_timeout; struct io_kiocb *nxt = NULL; int ret; +again: + linked_timeout = io_prep_linked_timeout(req); + ret = io_issue_sqe(req, &nxt, true); - if (nxt) - io_queue_async_work(nxt); /* * We async punt it if the file wasn't marked NOWAIT, or if the file @@ -3249,7 +3250,7 @@ static void __io_queue_sqe(struct io_kiocb *req) * submit reference when the iocb is actually submitted. */ io_queue_async_work(req); - return; + goto done_req; } err: @@ -3269,6 +3270,12 @@ err: req_set_fail_links(req); io_put_req(req); } +done_req: + if (nxt) { + req = nxt; + nxt = NULL; + goto again; + } } static void io_queue_sqe(struct io_kiocb *req) -- GitLab From 53108d476a105ab2597d7a4e6040b127829391b5 Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Mon, 9 Dec 2019 20:12:38 -0700 Subject: [PATCH 0427/1437] io_uring: only hash regular files for async work execution We hash regular files to avoid having multiple threads hammer on the inode mutex, but it should not be needed on other types of files (like sockets). Signed-off-by: Jens Axboe --- fs/io_uring.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/fs/io_uring.c b/fs/io_uring.c index 3dae005bfb567..e54bd469d53ad 100644 --- a/fs/io_uring.c +++ b/fs/io_uring.c @@ -581,7 +581,9 @@ static inline bool io_prep_async_work(struct io_kiocb *req, switch (req->sqe->opcode) { case IORING_OP_WRITEV: case IORING_OP_WRITE_FIXED: - do_hashed = true; + /* only regular files should be hashed for writes */ + if (req->flags & REQ_F_ISREG) + do_hashed = true; /* fall-through */ case IORING_OP_READV: case IORING_OP_READ_FIXED: -- GitLab From ebfcd8955c0b52eb793bcbc9e71140e3d0cdb228 Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Mon, 9 Dec 2019 20:58:56 -0700 Subject: [PATCH 0428/1437] net: make socket read/write_iter() honor IOCB_NOWAIT The socket read/write helpers only look at the file O_NONBLOCK. not the iocb IOCB_NOWAIT flag. This breaks users like preadv2/pwritev2 and io_uring that rely on not having the file itself marked nonblocking, but rather the iocb itself. Cc: netdev@vger.kernel.org Acked-by: David Miller Signed-off-by: Jens Axboe --- net/socket.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/socket.c b/net/socket.c index b343db1489bdb..b116e58d6438f 100644 --- a/net/socket.c +++ b/net/socket.c @@ -957,7 +957,7 @@ static ssize_t sock_read_iter(struct kiocb *iocb, struct iov_iter *to) .msg_iocb = iocb}; ssize_t res; - if (file->f_flags & O_NONBLOCK) + if (file->f_flags & O_NONBLOCK || (iocb->ki_flags & IOCB_NOWAIT)) msg.msg_flags = MSG_DONTWAIT; if (iocb->ki_pos != 0) @@ -982,7 +982,7 @@ static ssize_t sock_write_iter(struct kiocb *iocb, struct iov_iter *from) if (iocb->ki_pos != 0) return -ESPIPE; - if (file->f_flags & O_NONBLOCK) + if (file->f_flags & O_NONBLOCK || (iocb->ki_flags & IOCB_NOWAIT)) msg.msg_flags = MSG_DONTWAIT; if (sock->type == SOCK_SEQPACKET) -- GitLab From 10d59345578a116042c1a5d737a18234aaf3e0e6 Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Mon, 9 Dec 2019 20:16:22 -0700 Subject: [PATCH 0429/1437] io_uring: add sockets to list of files that support non-blocking issue In chasing a performance issue between using IORING_OP_RECVMSG and IORING_OP_READV on sockets, tracing showed that we always punt the socket reads to async offload. This is due to io_file_supports_async() not checking for S_ISSOCK on the inode. Since sockets supports the O_NONBLOCK (or MSG_DONTWAIT) flag just fine, add sockets to the list of file types that we can do a non-blocking issue to. Signed-off-by: Jens Axboe --- fs/io_uring.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/fs/io_uring.c b/fs/io_uring.c index e54bd469d53ad..42de210be6310 100644 --- a/fs/io_uring.c +++ b/fs/io_uring.c @@ -1431,7 +1431,7 @@ static bool io_file_supports_async(struct file *file) { umode_t mode = file_inode(file)->i_mode; - if (S_ISBLK(mode) || S_ISCHR(mode)) + if (S_ISBLK(mode) || S_ISCHR(mode) || S_ISSOCK(mode)) return true; if (S_ISREG(mode) && file->f_op != &io_uring_fops) return true; @@ -1867,7 +1867,9 @@ static int io_write(struct io_kiocb *req, struct io_kiocb **nxt, goto copy_iov; } - if (force_nonblock && !(kiocb->ki_flags & IOCB_DIRECT)) + /* file path doesn't support NOWAIT for non-direct_IO */ + if (force_nonblock && !(kiocb->ki_flags & IOCB_DIRECT) && + (req->flags & REQ_F_ISREG)) goto copy_iov; iov_count = iov_iter_count(&iter); -- GitLab From 0c722ec0a289c7f6b53f89bad1cfb7c4db3f7a62 Mon Sep 17 00:00:00 2001 From: Michael Chan Date: Tue, 10 Dec 2019 02:49:07 -0500 Subject: [PATCH 0430/1437] bnxt_en: Fix MSIX request logic for RDMA driver. The logic needs to check both bp->total_irqs and the reserved IRQs in hw_resc->resv_irqs if applicable and see if both are enough to cover the L2 and RDMA requested vectors. The current code is only checking bp->total_irqs and can fail in some code paths, such as the TX timeout code path with the RDMA driver requesting vectors after recovery. In this code path, we have not reserved enough MSIX resources for the RDMA driver yet. Fixes: 75720e6323a1 ("bnxt_en: Keep track of reserved IRQs.") Signed-off-by: Michael Chan Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bnxt/bnxt_ulp.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_ulp.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_ulp.c index c601ff7b8f61c..4a316c4b3fa80 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_ulp.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_ulp.c @@ -113,8 +113,10 @@ static int bnxt_req_msix_vecs(struct bnxt_en_dev *edev, int ulp_id, { struct net_device *dev = edev->net; struct bnxt *bp = netdev_priv(dev); + struct bnxt_hw_resc *hw_resc; int max_idx, max_cp_rings; int avail_msix, idx; + int total_vecs; int rc = 0; ASSERT_RTNL(); @@ -142,7 +144,10 @@ static int bnxt_req_msix_vecs(struct bnxt_en_dev *edev, int ulp_id, } edev->ulp_tbl[ulp_id].msix_base = idx; edev->ulp_tbl[ulp_id].msix_requested = avail_msix; - if (bp->total_irqs < (idx + avail_msix)) { + hw_resc = &bp->hw_resc; + total_vecs = idx + avail_msix; + if (bp->total_irqs < total_vecs || + (BNXT_NEW_RM(bp) && hw_resc->resv_irqs < total_vecs)) { if (netif_running(dev)) { bnxt_close_nic(bp, true, false); rc = bnxt_open_nic(bp, true, false); @@ -156,7 +161,6 @@ static int bnxt_req_msix_vecs(struct bnxt_en_dev *edev, int ulp_id, } if (BNXT_NEW_RM(bp)) { - struct bnxt_hw_resc *hw_resc = &bp->hw_resc; int resv_msix; resv_msix = hw_resc->resv_irqs - bp->cp_nr_rings; -- GitLab From 325f85f37e5b35807d86185bdf2c64d2980c44ba Mon Sep 17 00:00:00 2001 From: Michael Chan Date: Tue, 10 Dec 2019 02:49:08 -0500 Subject: [PATCH 0431/1437] bnxt_en: Free context memory in the open path if firmware has been reset. This will trigger new context memory to be rediscovered and allocated during the re-probe process after a firmware reset. Without this, the newly reset firmware does not have valid context memory and the driver will eventually fail to allocate some resources. Fixes: ec5d31e3c15d ("bnxt_en: Handle firmware reset status during IF_UP.") Signed-off-by: Michael Chan Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bnxt/bnxt.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index 85983f0e31347..65c1c4e26f4f0 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -8796,6 +8796,9 @@ static int bnxt_hwrm_if_change(struct bnxt *bp, bool up) if (fw_reset) { if (!test_bit(BNXT_STATE_IN_FW_RESET, &bp->state)) bnxt_ulp_stop(bp); + bnxt_free_ctx_mem(bp); + kfree(bp->ctx); + bp->ctx = NULL; rc = bnxt_fw_init_one(bp); if (rc) { set_bit(BNXT_STATE_ABORT_ERR, &bp->state); -- GitLab From c74751f4c39232c31214ec6a3bc1c7e62f5c728b Mon Sep 17 00:00:00 2001 From: Vasundhara Volam Date: Tue, 10 Dec 2019 02:49:09 -0500 Subject: [PATCH 0432/1437] bnxt_en: Return error if FW returns more data than dump length If any change happened in the configuration of VF in VM while collecting live dump, there could be a race and firmware can return more data than allocated dump length. Fix it by keeping track of the accumulated core dump length copied so far and abort the copy with error code if the next chunk of core dump will exceed the original dump length. Fixes: 6c5657d085ae ("bnxt_en: Add support for ethtool get dump.") Signed-off-by: Vasundhara Volam Signed-off-by: Michael Chan Signed-off-by: David S. Miller --- .../net/ethernet/broadcom/bnxt/bnxt_ethtool.c | 38 +++++++++++++++---- .../net/ethernet/broadcom/bnxt/bnxt_ethtool.h | 4 ++ 2 files changed, 34 insertions(+), 8 deletions(-) diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c index 2ccf79cdcb1ef..08d56ec7b68a5 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c @@ -3071,8 +3071,15 @@ static int bnxt_hwrm_dbg_dma_data(struct bnxt *bp, void *msg, int msg_len, } } - if (info->dest_buf) - memcpy(info->dest_buf + off, dma_buf, len); + if (info->dest_buf) { + if ((info->seg_start + off + len) <= + BNXT_COREDUMP_BUF_LEN(info->buf_len)) { + memcpy(info->dest_buf + off, dma_buf, len); + } else { + rc = -ENOBUFS; + break; + } + } if (cmn_req->req_type == cpu_to_le16(HWRM_DBG_COREDUMP_RETRIEVE)) @@ -3126,7 +3133,7 @@ static int bnxt_hwrm_dbg_coredump_initiate(struct bnxt *bp, u16 component_id, static int bnxt_hwrm_dbg_coredump_retrieve(struct bnxt *bp, u16 component_id, u16 segment_id, u32 *seg_len, - void *buf, u32 offset) + void *buf, u32 buf_len, u32 offset) { struct hwrm_dbg_coredump_retrieve_input req = {0}; struct bnxt_hwrm_dbg_dma_info info = {NULL}; @@ -3141,8 +3148,11 @@ static int bnxt_hwrm_dbg_coredump_retrieve(struct bnxt *bp, u16 component_id, seq_no); info.data_len_off = offsetof(struct hwrm_dbg_coredump_retrieve_output, data_len); - if (buf) + if (buf) { info.dest_buf = buf + offset; + info.buf_len = buf_len; + info.seg_start = offset; + } rc = bnxt_hwrm_dbg_dma_data(bp, &req, sizeof(req), &info); if (!rc) @@ -3232,14 +3242,17 @@ bnxt_fill_coredump_record(struct bnxt *bp, struct bnxt_coredump_record *record, static int bnxt_get_coredump(struct bnxt *bp, void *buf, u32 *dump_len) { u32 ver_get_resp_len = sizeof(struct hwrm_ver_get_output); + u32 offset = 0, seg_hdr_len, seg_record_len, buf_len = 0; struct coredump_segment_record *seg_record = NULL; - u32 offset = 0, seg_hdr_len, seg_record_len; struct bnxt_coredump_segment_hdr seg_hdr; struct bnxt_coredump coredump = {NULL}; time64_t start_time; u16 start_utc; int rc = 0, i; + if (buf) + buf_len = *dump_len; + start_time = ktime_get_real_seconds(); start_utc = sys_tz.tz_minuteswest * 60; seg_hdr_len = sizeof(seg_hdr); @@ -3272,6 +3285,12 @@ static int bnxt_get_coredump(struct bnxt *bp, void *buf, u32 *dump_len) u32 duration = 0, seg_len = 0; unsigned long start, end; + if (buf && ((offset + seg_hdr_len) > + BNXT_COREDUMP_BUF_LEN(buf_len))) { + rc = -ENOBUFS; + goto err; + } + start = jiffies; rc = bnxt_hwrm_dbg_coredump_initiate(bp, comp_id, seg_id); @@ -3284,9 +3303,11 @@ static int bnxt_get_coredump(struct bnxt *bp, void *buf, u32 *dump_len) /* Write segment data into the buffer */ rc = bnxt_hwrm_dbg_coredump_retrieve(bp, comp_id, seg_id, - &seg_len, buf, + &seg_len, buf, buf_len, offset + seg_hdr_len); - if (rc) + if (rc && rc == -ENOBUFS) + goto err; + else if (rc) netdev_err(bp->dev, "Failed to retrieve coredump for seg = %d\n", seg_record->segment_id); @@ -3316,7 +3337,8 @@ err: rc); kfree(coredump.data); *dump_len += sizeof(struct bnxt_coredump_record); - + if (rc == -ENOBUFS) + netdev_err(bp->dev, "Firmware returned large coredump buffer"); return rc; } diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.h b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.h index 4428d0abcbc1a..3576d951727b6 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.h +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.h @@ -31,6 +31,8 @@ struct bnxt_coredump { u16 total_segs; }; +#define BNXT_COREDUMP_BUF_LEN(len) ((len) - sizeof(struct bnxt_coredump_record)) + struct bnxt_hwrm_dbg_dma_info { void *dest_buf; int dest_buf_size; @@ -38,6 +40,8 @@ struct bnxt_hwrm_dbg_dma_info { u16 seq_off; u16 data_len_off; u16 segs; + u32 seg_start; + u32 buf_len; }; struct hwrm_dbg_cmn_input { -- GitLab From 8280b38e01f71e0f89389ccad3fa43b79e57c604 Mon Sep 17 00:00:00 2001 From: Vasundhara Volam Date: Tue, 10 Dec 2019 02:49:10 -0500 Subject: [PATCH 0433/1437] bnxt_en: Fix bp->fw_health allocation and free logic. bp->fw_health needs to be allocated for either the firmware initiated reset feature or the driver initiated error recovery feature. The current code is not allocating bp->fw_health for all the necessary cases. This patch corrects the logic to allocate bp->fw_health correctly when needed. If allocation fails, we clear the feature flags. We also add the the missing kfree(bp->fw_health) when the driver is unloaded. If we get an async reset message from the firmware, we also need to make sure that we have a valid bp->fw_health before proceeding. Fixes: 07f83d72d238 ("bnxt_en: Discover firmware error recovery capabilities.") Signed-off-by: Vasundhara Volam Signed-off-by: Michael Chan Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bnxt/bnxt.c | 36 ++++++++++++++++------- 1 file changed, 26 insertions(+), 10 deletions(-) diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index 65c1c4e26f4f0..d6a5fce1b06e9 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -2001,6 +2001,9 @@ static int bnxt_async_event_process(struct bnxt *bp, case ASYNC_EVENT_CMPL_EVENT_ID_RESET_NOTIFY: { u32 data1 = le32_to_cpu(cmpl->event_data1); + if (!bp->fw_health) + goto async_event_process_exit; + bp->fw_reset_timestamp = jiffies; bp->fw_reset_min_dsecs = cmpl->timestamp_lo; if (!bp->fw_reset_min_dsecs) @@ -4421,8 +4424,9 @@ int bnxt_hwrm_func_drv_rgtr(struct bnxt *bp, unsigned long *bmap, int bmap_size, FUNC_DRV_RGTR_REQ_ENABLES_ASYNC_EVENT_FWD); req.os_type = cpu_to_le16(FUNC_DRV_RGTR_REQ_OS_TYPE_LINUX); - flags = FUNC_DRV_RGTR_REQ_FLAGS_16BIT_VER_MODE | - FUNC_DRV_RGTR_REQ_FLAGS_HOT_RESET_SUPPORT; + flags = FUNC_DRV_RGTR_REQ_FLAGS_16BIT_VER_MODE; + if (bp->fw_cap & BNXT_FW_CAP_HOT_RESET) + flags |= FUNC_DRV_RGTR_REQ_FLAGS_HOT_RESET_SUPPORT; if (bp->fw_cap & BNXT_FW_CAP_ERROR_RECOVERY) flags |= FUNC_DRV_RGTR_REQ_FLAGS_ERROR_RECOVERY_SUPPORT | FUNC_DRV_RGTR_REQ_FLAGS_MASTER_SUPPORT; @@ -7115,14 +7119,6 @@ static int bnxt_hwrm_error_recovery_qcfg(struct bnxt *bp) rc = _hwrm_send_message(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT); if (rc) goto err_recovery_out; - if (!fw_health) { - fw_health = kzalloc(sizeof(*fw_health), GFP_KERNEL); - bp->fw_health = fw_health; - if (!fw_health) { - rc = -ENOMEM; - goto err_recovery_out; - } - } fw_health->flags = le32_to_cpu(resp->flags); if ((fw_health->flags & ERROR_RECOVERY_QCFG_RESP_FLAGS_CO_CPU) && !(bp->fw_cap & BNXT_FW_CAP_KONG_MB_CHNL)) { @@ -10485,6 +10481,23 @@ static void bnxt_init_dflt_coal(struct bnxt *bp) bp->stats_coal_ticks = BNXT_DEF_STATS_COAL_TICKS; } +static void bnxt_alloc_fw_health(struct bnxt *bp) +{ + if (bp->fw_health) + return; + + if (!(bp->fw_cap & BNXT_FW_CAP_HOT_RESET) && + !(bp->fw_cap & BNXT_FW_CAP_ERROR_RECOVERY)) + return; + + bp->fw_health = kzalloc(sizeof(*bp->fw_health), GFP_KERNEL); + if (!bp->fw_health) { + netdev_warn(bp->dev, "Failed to allocate fw_health\n"); + bp->fw_cap &= ~BNXT_FW_CAP_HOT_RESET; + bp->fw_cap &= ~BNXT_FW_CAP_ERROR_RECOVERY; + } +} + static int bnxt_fw_init_one_p1(struct bnxt *bp) { int rc; @@ -10531,6 +10544,7 @@ static int bnxt_fw_init_one_p2(struct bnxt *bp) netdev_warn(bp->dev, "hwrm query adv flow mgnt failure rc: %d\n", rc); + bnxt_alloc_fw_health(bp); rc = bnxt_hwrm_error_recovery_qcfg(bp); if (rc) netdev_warn(bp->dev, "hwrm query error recovery failure rc: %d\n", @@ -11418,6 +11432,8 @@ static void bnxt_remove_one(struct pci_dev *pdev) bnxt_dcb_free(bp); kfree(bp->edev); bp->edev = NULL; + kfree(bp->fw_health); + bp->fw_health = NULL; bnxt_cleanup_pci(bp); bnxt_free_ctx_mem(bp); kfree(bp->ctx); -- GitLab From 0797c10d2d1fa0d6f14612404781b348fc757c3e Mon Sep 17 00:00:00 2001 From: Vasundhara Volam Date: Tue, 10 Dec 2019 02:49:11 -0500 Subject: [PATCH 0434/1437] bnxt_en: Remove unnecessary NULL checks for fw_health After fixing the allocation of bp->fw_health in the previous patch, the driver will not go through the fw reset and recovery code paths if bp->fw_health allocation fails. So we can now remove the unnecessary NULL checks. Signed-off-by: Vasundhara Volam Signed-off-by: Michael Chan Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bnxt/bnxt.c | 6 ++---- drivers/net/ethernet/broadcom/bnxt/bnxt_devlink.c | 6 +----- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index d6a5fce1b06e9..2a100ff3da8b6 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -9989,8 +9989,7 @@ static void bnxt_fw_health_check(struct bnxt *bp) struct bnxt_fw_health *fw_health = bp->fw_health; u32 val; - if (!fw_health || !fw_health->enabled || - test_bit(BNXT_STATE_IN_FW_RESET, &bp->state)) + if (!fw_health->enabled || test_bit(BNXT_STATE_IN_FW_RESET, &bp->state)) return; if (fw_health->tmr_counter) { @@ -10768,8 +10767,7 @@ static void bnxt_fw_reset_task(struct work_struct *work) bnxt_queue_fw_reset_work(bp, bp->fw_reset_min_dsecs * HZ / 10); return; case BNXT_FW_RESET_STATE_ENABLE_DEV: - if (test_bit(BNXT_STATE_FW_FATAL_COND, &bp->state) && - bp->fw_health) { + if (test_bit(BNXT_STATE_FW_FATAL_COND, &bp->state)) { u32 val; val = bnxt_fw_health_readl(bp, diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_devlink.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_devlink.c index acb2dd64c023d..1e7c7c39718c8 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_devlink.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_devlink.c @@ -39,11 +39,10 @@ static int bnxt_fw_reporter_diagnose(struct devlink_health_reporter *reporter, struct netlink_ext_ack *extack) { struct bnxt *bp = devlink_health_reporter_priv(reporter); - struct bnxt_fw_health *health = bp->fw_health; u32 val, health_status; int rc; - if (!health || test_bit(BNXT_STATE_IN_FW_RESET, &bp->state)) + if (test_bit(BNXT_STATE_IN_FW_RESET, &bp->state)) return 0; val = bnxt_fw_health_readl(bp, BNXT_FW_HEALTH_REG); @@ -185,9 +184,6 @@ void bnxt_devlink_health_report(struct bnxt *bp, unsigned long event) struct bnxt_fw_health *fw_health = bp->fw_health; struct bnxt_fw_reporter_ctx fw_reporter_ctx; - if (!fw_health) - return; - fw_reporter_ctx.sp_event = event; switch (event) { case BNXT_FW_RESET_NOTIFY_SP_EVENT: -- GitLab From 937f188c1f4f89b3fa93ba31fc8587dc1fb14a22 Mon Sep 17 00:00:00 2001 From: Vasundhara Volam Date: Tue, 10 Dec 2019 02:49:12 -0500 Subject: [PATCH 0435/1437] bnxt_en: Fix the logic that creates the health reporters. Fix the logic to properly check the fw capabilities and create the devlink health reporters only when needed. The current code creates the reporters unconditionally as long as bp->fw_health is valid, and that's not correct. Call bnxt_dl_fw_reporters_create() directly from the init and reset code path instead of from bnxt_dl_register(). This allows the reporters to be adjusted when capabilities change. The same applies to bnxt_dl_fw_reporters_destroy(). Fixes: 6763c779c2d8 ("bnxt_en: Add new FW devlink_health_reporter") Signed-off-by: Vasundhara Volam Signed-off-by: Michael Chan Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bnxt/bnxt.c | 11 +++- .../net/ethernet/broadcom/bnxt/bnxt_devlink.c | 64 +++++++++++++------ .../net/ethernet/broadcom/bnxt/bnxt_devlink.h | 2 + 3 files changed, 56 insertions(+), 21 deletions(-) diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index 2a100ff3da8b6..819b7d72f9de5 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -10625,6 +10625,12 @@ static int bnxt_fw_init_one(struct bnxt *bp) rc = bnxt_approve_mac(bp, bp->dev->dev_addr, false); if (rc) return rc; + + /* In case fw capabilities have changed, destroy the unneeded + * reporters and create newly capable ones. + */ + bnxt_dl_fw_reporters_destroy(bp, false); + bnxt_dl_fw_reporters_create(bp); bnxt_fw_init_one_p3(bp); return 0; } @@ -11413,6 +11419,7 @@ static void bnxt_remove_one(struct pci_dev *pdev) if (BNXT_PF(bp)) { bnxt_sriov_disable(bp); + bnxt_dl_fw_reporters_destroy(bp, true); bnxt_dl_unregister(bp); } @@ -11892,8 +11899,10 @@ static int bnxt_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) if (rc) goto init_err_cleanup_tc; - if (BNXT_PF(bp)) + if (BNXT_PF(bp)) { bnxt_dl_register(bp); + bnxt_dl_fw_reporters_create(bp); + } netdev_info(dev, "%s found at mem %lx, node addr %pM\n", board_info[ent->driver_data].name, diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_devlink.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_devlink.c index 1e7c7c39718c8..136953a0ecaa1 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_devlink.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_devlink.c @@ -125,21 +125,15 @@ struct devlink_health_reporter_ops bnxt_dl_fw_fatal_reporter_ops = { .recover = bnxt_fw_fatal_recover, }; -static void bnxt_dl_fw_reporters_create(struct bnxt *bp) +void bnxt_dl_fw_reporters_create(struct bnxt *bp) { struct bnxt_fw_health *health = bp->fw_health; - if (!health) + if (!bp->dl || !health) return; - health->fw_reporter = - devlink_health_reporter_create(bp->dl, &bnxt_dl_fw_reporter_ops, - 0, false, bp); - if (IS_ERR(health->fw_reporter)) { - netdev_warn(bp->dev, "Failed to create FW health reporter, rc = %ld\n", - PTR_ERR(health->fw_reporter)); - health->fw_reporter = NULL; - } + if (!(bp->fw_cap & BNXT_FW_CAP_HOT_RESET) || health->fw_reset_reporter) + goto err_recovery; health->fw_reset_reporter = devlink_health_reporter_create(bp->dl, @@ -149,8 +143,30 @@ static void bnxt_dl_fw_reporters_create(struct bnxt *bp) netdev_warn(bp->dev, "Failed to create FW fatal health reporter, rc = %ld\n", PTR_ERR(health->fw_reset_reporter)); health->fw_reset_reporter = NULL; + bp->fw_cap &= ~BNXT_FW_CAP_HOT_RESET; } +err_recovery: + if (!(bp->fw_cap & BNXT_FW_CAP_ERROR_RECOVERY)) + return; + + if (!health->fw_reporter) { + health->fw_reporter = + devlink_health_reporter_create(bp->dl, + &bnxt_dl_fw_reporter_ops, + 0, false, bp); + if (IS_ERR(health->fw_reporter)) { + netdev_warn(bp->dev, "Failed to create FW health reporter, rc = %ld\n", + PTR_ERR(health->fw_reporter)); + health->fw_reporter = NULL; + bp->fw_cap &= ~BNXT_FW_CAP_ERROR_RECOVERY; + return; + } + } + + if (health->fw_fatal_reporter) + return; + health->fw_fatal_reporter = devlink_health_reporter_create(bp->dl, &bnxt_dl_fw_fatal_reporter_ops, @@ -159,24 +175,35 @@ static void bnxt_dl_fw_reporters_create(struct bnxt *bp) netdev_warn(bp->dev, "Failed to create FW fatal health reporter, rc = %ld\n", PTR_ERR(health->fw_fatal_reporter)); health->fw_fatal_reporter = NULL; + bp->fw_cap &= ~BNXT_FW_CAP_ERROR_RECOVERY; } } -static void bnxt_dl_fw_reporters_destroy(struct bnxt *bp) +void bnxt_dl_fw_reporters_destroy(struct bnxt *bp, bool all) { struct bnxt_fw_health *health = bp->fw_health; - if (!health) + if (!bp->dl || !health) return; - if (health->fw_reporter) - devlink_health_reporter_destroy(health->fw_reporter); - - if (health->fw_reset_reporter) + if ((all || !(bp->fw_cap & BNXT_FW_CAP_HOT_RESET)) && + health->fw_reset_reporter) { devlink_health_reporter_destroy(health->fw_reset_reporter); + health->fw_reset_reporter = NULL; + } - if (health->fw_fatal_reporter) + if ((bp->fw_cap & BNXT_FW_CAP_ERROR_RECOVERY) && !all) + return; + + if (health->fw_reporter) { + devlink_health_reporter_destroy(health->fw_reporter); + health->fw_reporter = NULL; + } + + if (health->fw_fatal_reporter) { devlink_health_reporter_destroy(health->fw_fatal_reporter); + health->fw_fatal_reporter = NULL; + } } void bnxt_devlink_health_report(struct bnxt *bp, unsigned long event) @@ -502,8 +529,6 @@ int bnxt_dl_register(struct bnxt *bp) devlink_params_publish(dl); - bnxt_dl_fw_reporters_create(bp); - return 0; err_dl_port_unreg: @@ -526,7 +551,6 @@ void bnxt_dl_unregister(struct bnxt *bp) if (!dl) return; - bnxt_dl_fw_reporters_destroy(bp); devlink_port_params_unregister(&bp->dl_port, bnxt_dl_port_params, ARRAY_SIZE(bnxt_dl_port_params)); devlink_port_unregister(&bp->dl_port); diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_devlink.h b/drivers/net/ethernet/broadcom/bnxt/bnxt_devlink.h index 665d4bdcd8c02..6db6c3dac4728 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_devlink.h +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_devlink.h @@ -58,6 +58,8 @@ struct bnxt_dl_nvm_param { void bnxt_devlink_health_report(struct bnxt *bp, unsigned long event); void bnxt_dl_health_status_update(struct bnxt *bp, bool healthy); +void bnxt_dl_fw_reporters_create(struct bnxt *bp); +void bnxt_dl_fw_reporters_destroy(struct bnxt *bp, bool all); int bnxt_dl_register(struct bnxt *bp); void bnxt_dl_unregister(struct bnxt *bp); -- GitLab From 7e334fc8003c7a38372cc98e7be6082670a47d29 Mon Sep 17 00:00:00 2001 From: Vasundhara Volam Date: Tue, 10 Dec 2019 02:49:13 -0500 Subject: [PATCH 0436/1437] bnxt_en: Add missing devlink health reporters for VFs. The VF driver also needs to create the health reporters since VFs are also involved in firmware reset and recovery. Modify bnxt_dl_register() and bnxt_dl_unregister() so that they can be called by the VFs to register/unregister devlink. Only the PF will register the devlink parameters. With devlink registered, we can now create the health reporters on the VFs. Fixes: 6763c779c2d8 ("bnxt_en: Add new FW devlink_health_reporter") Signed-off-by: Vasundhara Volam Signed-off-by: Michael Chan Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bnxt/bnxt.c | 13 ++++------- .../net/ethernet/broadcom/bnxt/bnxt_devlink.c | 23 ++++++++++++++----- 2 files changed, 22 insertions(+), 14 deletions(-) diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index 819b7d72f9de5..a754903b27eee 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -11417,12 +11417,11 @@ static void bnxt_remove_one(struct pci_dev *pdev) struct net_device *dev = pci_get_drvdata(pdev); struct bnxt *bp = netdev_priv(dev); - if (BNXT_PF(bp)) { + if (BNXT_PF(bp)) bnxt_sriov_disable(bp); - bnxt_dl_fw_reporters_destroy(bp, true); - bnxt_dl_unregister(bp); - } + bnxt_dl_fw_reporters_destroy(bp, true); + bnxt_dl_unregister(bp); pci_disable_pcie_error_reporting(pdev); unregister_netdev(dev); bnxt_shutdown_tc(bp); @@ -11899,10 +11898,8 @@ static int bnxt_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) if (rc) goto init_err_cleanup_tc; - if (BNXT_PF(bp)) { - bnxt_dl_register(bp); - bnxt_dl_fw_reporters_create(bp); - } + bnxt_dl_register(bp); + bnxt_dl_fw_reporters_create(bp); netdev_info(dev, "%s found at mem %lx, node addr %pM\n", board_info[ent->driver_data].name, diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_devlink.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_devlink.c index 136953a0ecaa1..3eedd44772182 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_devlink.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_devlink.c @@ -270,6 +270,8 @@ static const struct devlink_ops bnxt_dl_ops = { .flash_update = bnxt_dl_flash_update, }; +static const struct devlink_ops bnxt_vf_dl_ops; + enum bnxt_dl_param_id { BNXT_DEVLINK_PARAM_ID_BASE = DEVLINK_PARAM_GENERIC_ID_MAX, BNXT_DEVLINK_PARAM_ID_GRE_VER_CHECK, @@ -483,7 +485,10 @@ int bnxt_dl_register(struct bnxt *bp) return -ENOTSUPP; } - dl = devlink_alloc(&bnxt_dl_ops, sizeof(struct bnxt_dl)); + if (BNXT_PF(bp)) + dl = devlink_alloc(&bnxt_dl_ops, sizeof(struct bnxt_dl)); + else + dl = devlink_alloc(&bnxt_vf_dl_ops, sizeof(struct bnxt_dl)); if (!dl) { netdev_warn(bp->dev, "devlink_alloc failed"); return -ENOMEM; @@ -502,6 +507,9 @@ int bnxt_dl_register(struct bnxt *bp) goto err_dl_free; } + if (!BNXT_PF(bp)) + return 0; + rc = devlink_params_register(dl, bnxt_dl_params, ARRAY_SIZE(bnxt_dl_params)); if (rc) { @@ -551,11 +559,14 @@ void bnxt_dl_unregister(struct bnxt *bp) if (!dl) return; - devlink_port_params_unregister(&bp->dl_port, bnxt_dl_port_params, - ARRAY_SIZE(bnxt_dl_port_params)); - devlink_port_unregister(&bp->dl_port); - devlink_params_unregister(dl, bnxt_dl_params, - ARRAY_SIZE(bnxt_dl_params)); + if (BNXT_PF(bp)) { + devlink_port_params_unregister(&bp->dl_port, + bnxt_dl_port_params, + ARRAY_SIZE(bnxt_dl_port_params)); + devlink_port_unregister(&bp->dl_port); + devlink_params_unregister(dl, bnxt_dl_params, + ARRAY_SIZE(bnxt_dl_params)); + } devlink_unregister(dl); devlink_free(dl); } -- GitLab From d5162f341e9625d00a275d5cbe55432e6627c3bf Mon Sep 17 00:00:00 2001 From: Tuong Lien Date: Tue, 10 Dec 2019 15:21:02 +0700 Subject: [PATCH 0437/1437] tipc: fix name table rbtree issues The current rbtree for service ranges in the name table is built based on the 'lower' & 'upper' range values resulting in a flaw in the rbtree searching. Some issues have been observed in case of range overlapping: Case #1: unable to withdraw a name entry: After some name services are bound, all of them are withdrawn by user but one remains in the name table forever. This corrupts the table and that service becomes dummy i.e. no real port. E.g. / {22, 22} / / ---> {10, 50} / \ / \ {10, 30} {20, 60} The node {10, 30} cannot be removed since the rbtree searching stops at the node's ancestor i.e. {10, 50}, so starting from it will never reach the finding node. Case #2: failed to send data in some cases: E.g. Two service ranges: {20, 60}, {10, 50} are bound. The rbtree for this service will be one of the two cases below depending on the order of the bindings: {20, 60} {10, 50} <-- / \ / \ / \ / \ {10, 50} NIL <-- NIL {20, 60} (a) (b) Now, try to send some data to service {30}, there will be two results: (a): Failed, no route to host. (b): Ok. The reason is that the rbtree searching will stop at the pointing node as shown above. Case #3: Same as case #2b above but if the data sending's scope is local and the {10, 50} is published by a peer node, then it will result in 'no route to host' even though the other {20, 60} is for example on the local node which should be able to get the data. The issues are actually due to the way we built the rbtree. This commit fixes it by introducing an additional field to each node - named 'max', which is the largest 'upper' of that node subtree. The 'max' value for each subtrees will be propagated correctly whenever a node is inserted/ removed or the tree is rebalanced by the augmented rbtree callbacks. By this way, we can change the rbtree searching appoarch to solve the issues above. Another benefit from this is that we can now improve the searching for a next range matching e.g. in case of multicast, so get rid of the unneeded looping over all nodes in the tree. Acked-by: Jon Maloy Signed-off-by: Tuong Lien Signed-off-by: David S. Miller --- net/tipc/name_table.c | 279 +++++++++++++++++++++++++++--------------- 1 file changed, 179 insertions(+), 100 deletions(-) diff --git a/net/tipc/name_table.c b/net/tipc/name_table.c index 92d04dc2a44b7..359b2bc888cfc 100644 --- a/net/tipc/name_table.c +++ b/net/tipc/name_table.c @@ -36,6 +36,7 @@ #include #include +#include #include "core.h" #include "netlink.h" #include "name_table.h" @@ -51,6 +52,7 @@ * @lower: service range lower bound * @upper: service range upper bound * @tree_node: member of service range RB tree + * @max: largest 'upper' in this node subtree * @local_publ: list of identical publications made from this node * Used by closest_first lookup and multicast lookup algorithm * @all_publ: all publications identical to this one, whatever node and scope @@ -60,6 +62,7 @@ struct service_range { u32 lower; u32 upper; struct rb_node tree_node; + u32 max; struct list_head local_publ; struct list_head all_publ; }; @@ -84,6 +87,130 @@ struct tipc_service { struct rcu_head rcu; }; +#define service_range_upper(sr) ((sr)->upper) +RB_DECLARE_CALLBACKS_MAX(static, sr_callbacks, + struct service_range, tree_node, u32, max, + service_range_upper) + +#define service_range_entry(rbtree_node) \ + (container_of(rbtree_node, struct service_range, tree_node)) + +#define service_range_overlap(sr, start, end) \ + ((sr)->lower <= (end) && (sr)->upper >= (start)) + +/** + * service_range_foreach_match - iterate over tipc service rbtree for each + * range match + * @sr: the service range pointer as a loop cursor + * @sc: the pointer to tipc service which holds the service range rbtree + * @start, end: the range (end >= start) for matching + */ +#define service_range_foreach_match(sr, sc, start, end) \ + for (sr = service_range_match_first((sc)->ranges.rb_node, \ + start, \ + end); \ + sr; \ + sr = service_range_match_next(&(sr)->tree_node, \ + start, \ + end)) + +/** + * service_range_match_first - find first service range matching a range + * @n: the root node of service range rbtree for searching + * @start, end: the range (end >= start) for matching + * + * Return: the leftmost service range node in the rbtree that overlaps the + * specific range if any. Otherwise, returns NULL. + */ +static struct service_range *service_range_match_first(struct rb_node *n, + u32 start, u32 end) +{ + struct service_range *sr; + struct rb_node *l, *r; + + /* Non overlaps in tree at all? */ + if (!n || service_range_entry(n)->max < start) + return NULL; + + while (n) { + l = n->rb_left; + if (l && service_range_entry(l)->max >= start) { + /* A leftmost overlap range node must be one in the left + * subtree. If not, it has lower > end, then nodes on + * the right side cannot satisfy the condition either. + */ + n = l; + continue; + } + + /* No one in the left subtree can match, return if this node is + * an overlap i.e. leftmost. + */ + sr = service_range_entry(n); + if (service_range_overlap(sr, start, end)) + return sr; + + /* Ok, try to lookup on the right side */ + r = n->rb_right; + if (sr->lower <= end && + r && service_range_entry(r)->max >= start) { + n = r; + continue; + } + break; + } + + return NULL; +} + +/** + * service_range_match_next - find next service range matching a range + * @n: a node in service range rbtree from which the searching starts + * @start, end: the range (end >= start) for matching + * + * Return: the next service range node to the given node in the rbtree that + * overlaps the specific range if any. Otherwise, returns NULL. + */ +static struct service_range *service_range_match_next(struct rb_node *n, + u32 start, u32 end) +{ + struct service_range *sr; + struct rb_node *p, *r; + + while (n) { + r = n->rb_right; + if (r && service_range_entry(r)->max >= start) + /* A next overlap range node must be one in the right + * subtree. If not, it has lower > end, then any next + * successor (- an ancestor) of this node cannot + * satisfy the condition either. + */ + return service_range_match_first(r, start, end); + + /* No one in the right subtree can match, go up to find an + * ancestor of this node which is parent of a left-hand child. + */ + while ((p = rb_parent(n)) && n == p->rb_right) + n = p; + if (!p) + break; + + /* Return if this ancestor is an overlap */ + sr = service_range_entry(p); + if (service_range_overlap(sr, start, end)) + return sr; + + /* Ok, try to lookup more from this ancestor */ + if (sr->lower <= end) { + n = p; + continue; + } + break; + } + + return NULL; +} + static int hash(int x) { return x & (TIPC_NAMETBL_SIZE - 1); @@ -139,84 +266,51 @@ static struct tipc_service *tipc_service_create(u32 type, struct hlist_head *hd) return service; } -/** - * tipc_service_first_range - find first service range in tree matching instance - * - * Very time-critical, so binary search through range rb tree - */ -static struct service_range *tipc_service_first_range(struct tipc_service *sc, - u32 instance) -{ - struct rb_node *n = sc->ranges.rb_node; - struct service_range *sr; - - while (n) { - sr = container_of(n, struct service_range, tree_node); - if (sr->lower > instance) - n = n->rb_left; - else if (sr->upper < instance) - n = n->rb_right; - else - return sr; - } - return NULL; -} - /* tipc_service_find_range - find service range matching publication parameters */ static struct service_range *tipc_service_find_range(struct tipc_service *sc, u32 lower, u32 upper) { - struct rb_node *n = sc->ranges.rb_node; struct service_range *sr; - sr = tipc_service_first_range(sc, lower); - if (!sr) - return NULL; - - /* Look for exact match */ - for (n = &sr->tree_node; n; n = rb_next(n)) { - sr = container_of(n, struct service_range, tree_node); - if (sr->upper == upper) - break; + service_range_foreach_match(sr, sc, lower, upper) { + /* Look for exact match */ + if (sr->lower == lower && sr->upper == upper) + return sr; } - if (!n || sr->lower != lower || sr->upper != upper) - return NULL; - return sr; + return NULL; } static struct service_range *tipc_service_create_range(struct tipc_service *sc, u32 lower, u32 upper) { struct rb_node **n, *parent = NULL; - struct service_range *sr, *tmp; + struct service_range *sr; n = &sc->ranges.rb_node; while (*n) { - tmp = container_of(*n, struct service_range, tree_node); parent = *n; - tmp = container_of(parent, struct service_range, tree_node); - if (lower < tmp->lower) - n = &(*n)->rb_left; - else if (lower > tmp->lower) - n = &(*n)->rb_right; - else if (upper < tmp->upper) - n = &(*n)->rb_left; - else if (upper > tmp->upper) - n = &(*n)->rb_right; + sr = service_range_entry(parent); + if (lower == sr->lower && upper == sr->upper) + return sr; + if (sr->max < upper) + sr->max = upper; + if (lower <= sr->lower) + n = &parent->rb_left; else - return tmp; + n = &parent->rb_right; } sr = kzalloc(sizeof(*sr), GFP_ATOMIC); if (!sr) return NULL; sr->lower = lower; sr->upper = upper; + sr->max = upper; INIT_LIST_HEAD(&sr->local_publ); INIT_LIST_HEAD(&sr->all_publ); rb_link_node(&sr->tree_node, parent, n); - rb_insert_color(&sr->tree_node, &sc->ranges); + rb_insert_augmented(&sr->tree_node, &sc->ranges, &sr_callbacks); return sr; } @@ -310,7 +404,6 @@ static void tipc_service_subscribe(struct tipc_service *service, struct list_head publ_list; struct service_range *sr; struct tipc_name_seq ns; - struct rb_node *n; u32 filter; ns.type = tipc_sub_read(sb, seq.type); @@ -325,13 +418,7 @@ static void tipc_service_subscribe(struct tipc_service *service, return; INIT_LIST_HEAD(&publ_list); - for (n = rb_first(&service->ranges); n; n = rb_next(n)) { - sr = container_of(n, struct service_range, tree_node); - if (sr->lower > ns.upper) - break; - if (!tipc_sub_check_overlap(&ns, sr->lower, sr->upper)) - continue; - + service_range_foreach_match(sr, service, ns.lower, ns.upper) { first = NULL; list_for_each_entry(p, &sr->all_publ, all_publ) { if (filter & TIPC_SUB_PORTS) @@ -425,7 +512,7 @@ struct publication *tipc_nametbl_remove_publ(struct net *net, u32 type, /* Remove service range item if this was its last publication */ if (list_empty(&sr->all_publ)) { - rb_erase(&sr->tree_node, &sc->ranges); + rb_erase_augmented(&sr->tree_node, &sc->ranges, &sr_callbacks); kfree(sr); } @@ -473,34 +560,39 @@ u32 tipc_nametbl_translate(struct net *net, u32 type, u32 instance, u32 *dnode) rcu_read_lock(); sc = tipc_service_find(net, type); if (unlikely(!sc)) - goto not_found; + goto exit; spin_lock_bh(&sc->lock); - sr = tipc_service_first_range(sc, instance); - if (unlikely(!sr)) - goto no_match; - - /* Select lookup algorithm: local, closest-first or round-robin */ - if (*dnode == self) { - list = &sr->local_publ; - if (list_empty(list)) - goto no_match; - p = list_first_entry(list, struct publication, local_publ); - list_move_tail(&p->local_publ, &sr->local_publ); - } else if (legacy && !*dnode && !list_empty(&sr->local_publ)) { - list = &sr->local_publ; - p = list_first_entry(list, struct publication, local_publ); - list_move_tail(&p->local_publ, &sr->local_publ); - } else { - list = &sr->all_publ; - p = list_first_entry(list, struct publication, all_publ); - list_move_tail(&p->all_publ, &sr->all_publ); + service_range_foreach_match(sr, sc, instance, instance) { + /* Select lookup algo: local, closest-first or round-robin */ + if (*dnode == self) { + list = &sr->local_publ; + if (list_empty(list)) + continue; + p = list_first_entry(list, struct publication, + local_publ); + list_move_tail(&p->local_publ, &sr->local_publ); + } else if (legacy && !*dnode && !list_empty(&sr->local_publ)) { + list = &sr->local_publ; + p = list_first_entry(list, struct publication, + local_publ); + list_move_tail(&p->local_publ, &sr->local_publ); + } else { + list = &sr->all_publ; + p = list_first_entry(list, struct publication, + all_publ); + list_move_tail(&p->all_publ, &sr->all_publ); + } + port = p->port; + node = p->node; + /* Todo: as for legacy, pick the first matching range only, a + * "true" round-robin will be performed as needed. + */ + break; } - port = p->port; - node = p->node; -no_match: spin_unlock_bh(&sc->lock); -not_found: + +exit: rcu_read_unlock(); *dnode = node; return port; @@ -523,7 +615,8 @@ bool tipc_nametbl_lookup(struct net *net, u32 type, u32 instance, u32 scope, spin_lock_bh(&sc->lock); - sr = tipc_service_first_range(sc, instance); + /* Todo: a full search i.e. service_range_foreach_match() instead? */ + sr = service_range_match_first(sc->ranges.rb_node, instance, instance); if (!sr) goto no_match; @@ -552,7 +645,6 @@ void tipc_nametbl_mc_lookup(struct net *net, u32 type, u32 lower, u32 upper, struct service_range *sr; struct tipc_service *sc; struct publication *p; - struct rb_node *n; rcu_read_lock(); sc = tipc_service_find(net, type); @@ -560,13 +652,7 @@ void tipc_nametbl_mc_lookup(struct net *net, u32 type, u32 lower, u32 upper, goto exit; spin_lock_bh(&sc->lock); - - for (n = rb_first(&sc->ranges); n; n = rb_next(n)) { - sr = container_of(n, struct service_range, tree_node); - if (sr->upper < lower) - continue; - if (sr->lower > upper) - break; + service_range_foreach_match(sr, sc, lower, upper) { list_for_each_entry(p, &sr->local_publ, local_publ) { if (p->scope == scope || (!exact && p->scope < scope)) tipc_dest_push(dports, 0, p->port); @@ -587,7 +673,6 @@ void tipc_nametbl_lookup_dst_nodes(struct net *net, u32 type, u32 lower, struct service_range *sr; struct tipc_service *sc; struct publication *p; - struct rb_node *n; rcu_read_lock(); sc = tipc_service_find(net, type); @@ -595,13 +680,7 @@ void tipc_nametbl_lookup_dst_nodes(struct net *net, u32 type, u32 lower, goto exit; spin_lock_bh(&sc->lock); - - for (n = rb_first(&sc->ranges); n; n = rb_next(n)) { - sr = container_of(n, struct service_range, tree_node); - if (sr->upper < lower) - continue; - if (sr->lower > upper) - break; + service_range_foreach_match(sr, sc, lower, upper) { list_for_each_entry(p, &sr->all_publ, all_publ) { tipc_nlist_add(nodes, p->node); } @@ -799,7 +878,7 @@ static void tipc_service_delete(struct net *net, struct tipc_service *sc) tipc_service_remove_publ(sr, p->node, p->key); kfree_rcu(p, rcu); } - rb_erase(&sr->tree_node, &sc->ranges); + rb_erase_augmented(&sr->tree_node, &sc->ranges, &sr_callbacks); kfree(sr); } hlist_del_init_rcu(&sc->service_list); -- GitLab From dca4a17d24ee9d878836ce5eb8dc25be1ffa5729 Mon Sep 17 00:00:00 2001 From: Tuong Lien Date: Tue, 10 Dec 2019 15:21:03 +0700 Subject: [PATCH 0438/1437] tipc: fix potential hanging after b/rcast changing In commit c55c8edafa91 ("tipc: smooth change between replicast and broadcast"), we allow instant switching between replicast and broadcast by sending a dummy 'SYN' packet on the last used link to synchronize packets on the links. The 'SYN' message is an object of link congestion also, so if that happens, a 'SOCK_WAKEUP' will be scheduled to be sent back to the socket... However, in that commit, we simply use the same socket 'cong_link_cnt' counter for both the 'SYN' & normal payload message sending. Therefore, if both the replicast & broadcast links are congested, the counter will be not updated correctly but overwritten by the latter congestion. Later on, when the 'SOCK_WAKEUP' messages are processed, the counter is reduced one by one and eventually overflowed. Consequently, further activities on the socket will only wait for the false congestion signal to disappear but never been met. Because sending the 'SYN' message is vital for the mechanism, it should be done anyway. This commit fixes the issue by marking the message with an error code e.g. 'TIPC_ERR_NO_PORT', so its sending should not face a link congestion, there is no need to touch the socket 'cong_link_cnt' either. In addition, in the event of any error (e.g. -ENOBUFS), we will purge the entire payload message queue and make a return immediately. Fixes: c55c8edafa91 ("tipc: smooth change between replicast and broadcast") Acked-by: Jon Maloy Signed-off-by: Tuong Lien Signed-off-by: David S. Miller --- net/tipc/bcast.c | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/net/tipc/bcast.c b/net/tipc/bcast.c index 55aeba681cf4e..656ebc79c64eb 100644 --- a/net/tipc/bcast.c +++ b/net/tipc/bcast.c @@ -305,17 +305,17 @@ static int tipc_rcast_xmit(struct net *net, struct sk_buff_head *pkts, * @skb: socket buffer to copy * @method: send method to be used * @dests: destination nodes for message. - * @cong_link_cnt: returns number of encountered congested destination links * Returns 0 if success, otherwise errno */ static int tipc_mcast_send_sync(struct net *net, struct sk_buff *skb, struct tipc_mc_method *method, - struct tipc_nlist *dests, - u16 *cong_link_cnt) + struct tipc_nlist *dests) { struct tipc_msg *hdr, *_hdr; struct sk_buff_head tmpq; struct sk_buff *_skb; + u16 cong_link_cnt; + int rc = 0; /* Is a cluster supporting with new capabilities ? */ if (!(tipc_net(net)->capabilities & TIPC_MCAST_RBCTL)) @@ -343,18 +343,19 @@ static int tipc_mcast_send_sync(struct net *net, struct sk_buff *skb, _hdr = buf_msg(_skb); msg_set_size(_hdr, MCAST_H_SIZE); msg_set_is_rcast(_hdr, !msg_is_rcast(hdr)); + msg_set_errcode(_hdr, TIPC_ERR_NO_PORT); __skb_queue_head_init(&tmpq); __skb_queue_tail(&tmpq, _skb); if (method->rcast) - tipc_bcast_xmit(net, &tmpq, cong_link_cnt); + rc = tipc_bcast_xmit(net, &tmpq, &cong_link_cnt); else - tipc_rcast_xmit(net, &tmpq, dests, cong_link_cnt); + rc = tipc_rcast_xmit(net, &tmpq, dests, &cong_link_cnt); /* This queue should normally be empty by now */ __skb_queue_purge(&tmpq); - return 0; + return rc; } /* tipc_mcast_xmit - deliver message to indicated destination nodes @@ -396,9 +397,14 @@ int tipc_mcast_xmit(struct net *net, struct sk_buff_head *pkts, msg_set_is_rcast(hdr, method->rcast); /* Switch method ? */ - if (rcast != method->rcast) - tipc_mcast_send_sync(net, skb, method, - dests, cong_link_cnt); + if (rcast != method->rcast) { + rc = tipc_mcast_send_sync(net, skb, method, dests); + if (unlikely(rc)) { + pr_err("Unable to send SYN: method %d, rc %d\n", + rcast, rc); + goto exit; + } + } if (method->rcast) rc = tipc_rcast_xmit(net, pkts, dests, cong_link_cnt); -- GitLab From abc9b4e0549b93fdaff56e9532bc49a2d7b04955 Mon Sep 17 00:00:00 2001 From: Tuong Lien Date: Tue, 10 Dec 2019 15:21:04 +0700 Subject: [PATCH 0439/1437] tipc: fix retrans failure due to wrong destination When a user message is sent, TIPC will check if the socket has faced a congestion at link layer. If that happens, it will make a sleep to wait for the congestion to disappear. This leaves a gap for other users to take over the socket (e.g. multi threads) since the socket is released as well. Also, in case of connectionless (e.g. SOCK_RDM), user is free to send messages to various destinations (e.g. via 'sendto()'), then the socket's preformatted header has to be updated correspondingly prior to the actual payload message building. Unfortunately, the latter action is done before the first action which causes a condition issue that the destination of a certain message can be modified incorrectly in the middle, leading to wrong destination when that message is built. Consequently, when the message is sent to the link layer, it gets stuck there forever because the peer node will simply reject it. After a number of retransmission attempts, the link is eventually taken down and the retransmission failure is reported. This commit fixes the problem by rearranging the order of actions to prevent the race condition from occurring, so the message building is 'atomic' and its header will not be modified by anyone. Fixes: 365ad353c256 ("tipc: reduce risk of user starvation during link congestion") Acked-by: Jon Maloy Signed-off-by: Tuong Lien Signed-off-by: David S. Miller --- net/tipc/socket.c | 32 ++++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/net/tipc/socket.c b/net/tipc/socket.c index 41688da233aba..6552f986774ca 100644 --- a/net/tipc/socket.c +++ b/net/tipc/socket.c @@ -1364,8 +1364,8 @@ static int __tipc_sendmsg(struct socket *sock, struct msghdr *m, size_t dlen) struct tipc_msg *hdr = &tsk->phdr; struct tipc_name_seq *seq; struct sk_buff_head pkts; - u32 dport, dnode = 0; - u32 type, inst; + u32 dport = 0, dnode = 0; + u32 type = 0, inst = 0; int mtu, rc; if (unlikely(dlen > TIPC_MAX_USER_MSG_SIZE)) @@ -1418,23 +1418,11 @@ static int __tipc_sendmsg(struct socket *sock, struct msghdr *m, size_t dlen) type = dest->addr.name.name.type; inst = dest->addr.name.name.instance; dnode = dest->addr.name.domain; - msg_set_type(hdr, TIPC_NAMED_MSG); - msg_set_hdr_sz(hdr, NAMED_H_SIZE); - msg_set_nametype(hdr, type); - msg_set_nameinst(hdr, inst); - msg_set_lookup_scope(hdr, tipc_node2scope(dnode)); dport = tipc_nametbl_translate(net, type, inst, &dnode); - msg_set_destnode(hdr, dnode); - msg_set_destport(hdr, dport); if (unlikely(!dport && !dnode)) return -EHOSTUNREACH; } else if (dest->addrtype == TIPC_ADDR_ID) { dnode = dest->addr.id.node; - msg_set_type(hdr, TIPC_DIRECT_MSG); - msg_set_lookup_scope(hdr, 0); - msg_set_destnode(hdr, dnode); - msg_set_destport(hdr, dest->addr.id.ref); - msg_set_hdr_sz(hdr, BASIC_H_SIZE); } else { return -EINVAL; } @@ -1445,6 +1433,22 @@ static int __tipc_sendmsg(struct socket *sock, struct msghdr *m, size_t dlen) if (unlikely(rc)) return rc; + if (dest->addrtype == TIPC_ADDR_NAME) { + msg_set_type(hdr, TIPC_NAMED_MSG); + msg_set_hdr_sz(hdr, NAMED_H_SIZE); + msg_set_nametype(hdr, type); + msg_set_nameinst(hdr, inst); + msg_set_lookup_scope(hdr, tipc_node2scope(dnode)); + msg_set_destnode(hdr, dnode); + msg_set_destport(hdr, dport); + } else { /* TIPC_ADDR_ID */ + msg_set_type(hdr, TIPC_DIRECT_MSG); + msg_set_lookup_scope(hdr, 0); + msg_set_destnode(hdr, dnode); + msg_set_destport(hdr, dest->addr.id.ref); + msg_set_hdr_sz(hdr, BASIC_H_SIZE); + } + __skb_queue_head_init(&pkts); mtu = tipc_node_get_mtu(net, dnode, tsk->portid, false); rc = tipc_msg_build(hdr, m, 0, dlen, mtu, &pkts); -- GitLab From 31e4ccc99eda8a5a7e6902c98bee6e78ffd3edb9 Mon Sep 17 00:00:00 2001 From: Tuong Lien Date: Tue, 10 Dec 2019 15:21:05 +0700 Subject: [PATCH 0440/1437] tipc: fix use-after-free in tipc_disc_rcv() In the function 'tipc_disc_rcv()', the 'msg_peer_net_hash()' is called to read the header data field but after the message skb has been freed, that might result in a garbage value... This commit fixes it by defining a new local variable to store the data first, just like the other header fields' handling. Fixes: f73b12812a3d ("tipc: improve throughput between nodes in netns") Acked-by: Jon Maloy Signed-off-by: Tuong Lien Signed-off-by: David S. Miller --- net/tipc/discover.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/net/tipc/discover.c b/net/tipc/discover.c index b043e8c6397a8..bfe43da127c04 100644 --- a/net/tipc/discover.c +++ b/net/tipc/discover.c @@ -194,6 +194,7 @@ void tipc_disc_rcv(struct net *net, struct sk_buff *skb, { struct tipc_net *tn = tipc_net(net); struct tipc_msg *hdr = buf_msg(skb); + u32 pnet_hash = msg_peer_net_hash(hdr); u16 caps = msg_node_capabilities(hdr); bool legacy = tn->legacy_addr_format; u32 sugg = msg_sugg_node_addr(hdr); @@ -242,9 +243,8 @@ void tipc_disc_rcv(struct net *net, struct sk_buff *skb, return; if (!tipc_in_scope(legacy, b->domain, src)) return; - tipc_node_check_dest(net, src, peer_id, b, caps, signature, - msg_peer_net_hash(hdr), &maddr, &respond, - &dupl_addr); + tipc_node_check_dest(net, src, peer_id, b, caps, signature, pnet_hash, + &maddr, &respond, &dupl_addr); if (dupl_addr) disc_dupl_alert(b, src, &maddr); if (!respond) -- GitLab From 24dee0c7478d1a1e00abdf5625b7f921467325dc Mon Sep 17 00:00:00 2001 From: Netanel Belgazal Date: Tue, 10 Dec 2019 11:27:44 +0000 Subject: [PATCH 0441/1437] net: ena: fix napi handler misbehavior when the napi budget is zero In netpoll the napi handler could be called with budget equal to zero. Current ENA napi handler doesn't take that into consideration. The napi handler handles Rx packets in a do-while loop. Currently, the budget check happens only after decrementing the budget, therefore the napi handler, in rare cases, could run over MAX_INT packets. In addition to that, this moves all budget related variables to int calculation and stop mixing u32 to avoid ambiguity Fixes: 1738cd3ed342 ("net: ena: Add a driver for Amazon Elastic Network Adapters (ENA)") Signed-off-by: Netanel Belgazal Signed-off-by: David S. Miller --- drivers/net/ethernet/amazon/ena/ena_netdev.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/amazon/ena/ena_netdev.c b/drivers/net/ethernet/amazon/ena/ena_netdev.c index d46a912002ff2..948583fdcc286 100644 --- a/drivers/net/ethernet/amazon/ena/ena_netdev.c +++ b/drivers/net/ethernet/amazon/ena/ena_netdev.c @@ -1238,8 +1238,8 @@ static int ena_io_poll(struct napi_struct *napi, int budget) struct ena_napi *ena_napi = container_of(napi, struct ena_napi, napi); struct ena_ring *tx_ring, *rx_ring; - u32 tx_work_done; - u32 rx_work_done; + int tx_work_done; + int rx_work_done = 0; int tx_budget; int napi_comp_call = 0; int ret; @@ -1256,7 +1256,11 @@ static int ena_io_poll(struct napi_struct *napi, int budget) } tx_work_done = ena_clean_tx_irq(tx_ring, tx_budget); - rx_work_done = ena_clean_rx_irq(rx_ring, napi, budget); + /* On netpoll the budget is zero and the handler should only clean the + * tx completions. + */ + if (likely(budget)) + rx_work_done = ena_clean_rx_irq(rx_ring, napi, budget); /* If the device is about to reset or down, avoid unmask * the interrupt and return 0 so NAPI won't reschedule -- GitLab From 5815bdfd7f54739be9abed1301d55f5e74d7ad1f Mon Sep 17 00:00:00 2001 From: Hui Wang Date: Wed, 11 Dec 2019 13:13:21 +0800 Subject: [PATCH 0442/1437] ALSA: hda/realtek - Line-out jack doesn't work on a Dell AIO After applying the fixup ALC274_FIXUP_DELL_AIO_LINEOUT_VERB, the Line-out jack works well. And instead of adding a new set of pin definition in the pin_fixup_tbl, we put a more generic matching entry in the fallback_pin_fixup_tbl. Cc: Signed-off-by: Hui Wang Link: https://lore.kernel.org/r/20191211051321.5883-1-hui.wang@canonical.com Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 6d6e34b3b3aa5..dbfafee97931a 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -7643,11 +7643,6 @@ static const struct snd_hda_pin_quirk alc269_pin_fixup_tbl[] = { {0x1a, 0x90a70130}, {0x1b, 0x90170110}, {0x21, 0x03211020}), - SND_HDA_PIN_QUIRK(0x10ec0274, 0x1028, "Dell", ALC274_FIXUP_DELL_AIO_LINEOUT_VERB, - {0x12, 0xb7a60130}, - {0x13, 0xb8a61140}, - {0x16, 0x90170110}, - {0x21, 0x04211020}), SND_HDA_PIN_QUIRK(0x10ec0280, 0x103c, "HP", ALC280_FIXUP_HP_GPIO4, {0x12, 0x90a60130}, {0x14, 0x90170110}, @@ -7841,6 +7836,9 @@ static const struct snd_hda_pin_quirk alc269_fallback_pin_fixup_tbl[] = { SND_HDA_PIN_QUIRK(0x10ec0236, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE, {0x19, 0x40000000}, {0x1a, 0x40000000}), + SND_HDA_PIN_QUIRK(0x10ec0274, 0x1028, "Dell", ALC274_FIXUP_DELL_AIO_LINEOUT_VERB, + {0x19, 0x40000000}, + {0x1a, 0x40000000}), {} }; -- GitLab From 346da4d2c7ea39de65487b249aaa4733317a40ec Mon Sep 17 00:00:00 2001 From: Flavio Leitner Date: Thu, 5 Dec 2019 03:03:44 +0100 Subject: [PATCH 0443/1437] sched/cputime, proc/stat: Fix incorrect guest nice cpustat value The value being used for guest_nice should be CPUTIME_GUEST_NICE and not CPUTIME_USER. Fixes: 26dae145a76c ("procfs: Use all-in-one vtime aware kcpustat accessor") Signed-off-by: Flavio Leitner Signed-off-by: Frederic Weisbecker Cc: Peter Zijlstra Link: https://lkml.kernel.org/r/20191205020344.14940-1-frederic@kernel.org Signed-off-by: Ingo Molnar --- fs/proc/stat.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/proc/stat.c b/fs/proc/stat.c index 37bdbec5b4020..fd931d3e77be5 100644 --- a/fs/proc/stat.c +++ b/fs/proc/stat.c @@ -134,7 +134,7 @@ static int show_stat(struct seq_file *p, void *v) softirq += cpustat[CPUTIME_SOFTIRQ]; steal += cpustat[CPUTIME_STEAL]; guest += cpustat[CPUTIME_GUEST]; - guest_nice += cpustat[CPUTIME_USER]; + guest_nice += cpustat[CPUTIME_GUEST_NICE]; sum += kstat_cpu_irqs_sum(i); sum += arch_irq_stat_cpu(i); @@ -175,7 +175,7 @@ static int show_stat(struct seq_file *p, void *v) softirq = cpustat[CPUTIME_SOFTIRQ]; steal = cpustat[CPUTIME_STEAL]; guest = cpustat[CPUTIME_GUEST]; - guest_nice = cpustat[CPUTIME_USER]; + guest_nice = cpustat[CPUTIME_GUEST_NICE]; seq_printf(p, "cpu%d", i); seq_put_decimal_ull(p, " ", nsec_to_clock_t(user)); seq_put_decimal_ull(p, " ", nsec_to_clock_t(nice)); -- GitLab From 125ad46f3861520fdebd15c38e4bc412a7add01c Mon Sep 17 00:00:00 2001 From: Christoph Niedermaier Date: Thu, 5 Dec 2019 09:38:51 +0000 Subject: [PATCH 0444/1437] ARM: imx: Correct ocotp id for serial number support of i.MX6ULL/ULZ SoCs After the commit 8267ff89b713 ("ARM: imx: Add serial number support for i.MX6/7 SoCs") the kernel doesn't start on i.MX6ULL/ULZ SoC. Tested on next-20191205. For i.MX6ULL/ULZ the variable "ocotp_compat" is set to "fsl,imx6ul-ocotp", but with commit ffbc34bf0e9c ("nvmem: imx-ocotp: Implement i.MX6ULL/ULZ support") and commit f243bc821ee3 ("ARM: dts: imx6ull: Fix i.MX6ULL/ULZ ocotp compatible") the value "fsl,imx6ull-ocotp" is already defined and set in device tree... By setting "ocotp_compat" to "fsl,imx6ull-ocotp" the kernel does boot. Fixes: 8267ff89b713 ("ARM: imx: Add serial number support for i.MX6/7 SoCs") Signed-off-by: Christoph Niedermaier Reviewed-by: Fabio Estevam Signed-off-by: Shawn Guo --- arch/arm/mach-imx/cpu.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/arm/mach-imx/cpu.c b/arch/arm/mach-imx/cpu.c index d8118031c51f7..0fb0c4a264c31 100644 --- a/arch/arm/mach-imx/cpu.c +++ b/arch/arm/mach-imx/cpu.c @@ -148,11 +148,11 @@ struct device * __init imx_soc_device_init(void) soc_id = "i.MX6UL"; break; case MXC_CPU_IMX6ULL: - ocotp_compat = "fsl,imx6ul-ocotp"; + ocotp_compat = "fsl,imx6ull-ocotp"; soc_id = "i.MX6ULL"; break; case MXC_CPU_IMX6ULZ: - ocotp_compat = "fsl,imx6ul-ocotp"; + ocotp_compat = "fsl,imx6ull-ocotp"; soc_id = "i.MX6ULZ"; break; case MXC_CPU_IMX6SLL: -- GitLab From c3a5fd15ed0c1494435e4e35fbee734ae46b5073 Mon Sep 17 00:00:00 2001 From: Peng Fan Date: Mon, 9 Dec 2019 08:19:55 +0000 Subject: [PATCH 0445/1437] clk: imx: pll14xx: fix clk_pll14xx_wait_lock The usage of readl_poll_timeout is wrong, the 3rd parameter(cond) should be "val & LOCK_STATUS" not "val & LOCK_TIMEOUT_US", It is not check whether the pll locked, LOCK_STATUS reflects the mask, not LOCK_TIMEOUT_US. Fixes: 8646d4dcc7fb ("clk: imx: Add PLLs driver for imx8mm soc") Cc: Reviewed-by: Abel Vesa Signed-off-by: Peng Fan Signed-off-by: Shawn Guo --- drivers/clk/imx/clk-pll14xx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/clk/imx/clk-pll14xx.c b/drivers/clk/imx/clk-pll14xx.c index 5c458199060a6..3636c8035c7d9 100644 --- a/drivers/clk/imx/clk-pll14xx.c +++ b/drivers/clk/imx/clk-pll14xx.c @@ -159,7 +159,7 @@ static int clk_pll14xx_wait_lock(struct clk_pll14xx *pll) { u32 val; - return readl_poll_timeout(pll->base, val, val & LOCK_TIMEOUT_US, 0, + return readl_poll_timeout(pll->base, val, val & LOCK_STATUS, 0, LOCK_TIMEOUT_US); } -- GitLab From 8c9312a925ad859daefd0f443ef3b6dc7157d881 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Wed, 6 Nov 2019 22:21:01 +0100 Subject: [PATCH 0446/1437] i2c: add helper to check if a client has a driver attached As a preparation for an API conversion, factor out something frequently used in the media subsystem. As an improvement, it bails out on both, NULL and ERRPTR to handle the old and new API. Signed-off-by: Wolfram Sang Signed-off-by: Wolfram Sang --- include/linux/i2c.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/include/linux/i2c.h b/include/linux/i2c.h index 91954324f985e..582ef05ec07ed 100644 --- a/include/linux/i2c.h +++ b/include/linux/i2c.h @@ -851,6 +851,11 @@ extern void i2c_del_driver(struct i2c_driver *driver); #define i2c_add_driver(driver) \ i2c_register_driver(THIS_MODULE, driver) +static inline bool i2c_client_has_driver(struct i2c_client *client) +{ + return !IS_ERR_OR_NULL(client) && client->dev.driver; +} + /* call the i2c_client->command() of all attached clients with * the given arguments */ extern void i2c_clients_command(struct i2c_adapter *adap, -- GitLab From f8c63edfd78905320e86b6b2be2b7a5ac768fa4e Mon Sep 17 00:00:00 2001 From: Fredrik Noring Date: Tue, 10 Dec 2019 18:29:05 +0100 Subject: [PATCH 0447/1437] USB: Fix incorrect DMA allocations for local memory pool drivers Fix commit 7b81cb6bddd2 ("usb: add a HCD_DMA flag instead of guestimating DMA capabilities") where local memory USB drivers erroneously allocate DMA memory instead of pool memory, causing OHCI Unrecoverable Error, disabled HC died; cleaning up The order between hcd_uses_dma() and hcd->localmem_pool is now arranged as in hcd_buffer_alloc() and hcd_buffer_free(), with the test for hcd->localmem_pool placed first. As an alternative, one might consider adjusting hcd_uses_dma() with static inline bool hcd_uses_dma(struct usb_hcd *hcd) { - return IS_ENABLED(CONFIG_HAS_DMA) && (hcd->driver->flags & HCD_DMA); + return IS_ENABLED(CONFIG_HAS_DMA) && + (hcd->driver->flags & HCD_DMA) && + (hcd->localmem_pool == NULL); } One can also consider unsetting HCD_DMA for local memory pool drivers. Fixes: 7b81cb6bddd2 ("usb: add a HCD_DMA flag instead of guestimating DMA capabilities") Cc: stable Signed-off-by: Fredrik Noring Link: https://lore.kernel.org/r/20191210172905.GA52526@sx9 Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hcd.c | 42 +++++++++++++++++----------------- drivers/usb/storage/scsiglue.c | 3 ++- 2 files changed, 23 insertions(+), 22 deletions(-) diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index 281568d464f97..aa45840d82730 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -1409,7 +1409,17 @@ int usb_hcd_map_urb_for_dma(struct usb_hcd *hcd, struct urb *urb, if (usb_endpoint_xfer_control(&urb->ep->desc)) { if (hcd->self.uses_pio_for_control) return ret; - if (hcd_uses_dma(hcd)) { + if (hcd->localmem_pool) { + ret = hcd_alloc_coherent( + urb->dev->bus, mem_flags, + &urb->setup_dma, + (void **)&urb->setup_packet, + sizeof(struct usb_ctrlrequest), + DMA_TO_DEVICE); + if (ret) + return ret; + urb->transfer_flags |= URB_SETUP_MAP_LOCAL; + } else if (hcd_uses_dma(hcd)) { if (object_is_on_stack(urb->setup_packet)) { WARN_ONCE(1, "setup packet is on stack\n"); return -EAGAIN; @@ -1424,23 +1434,22 @@ int usb_hcd_map_urb_for_dma(struct usb_hcd *hcd, struct urb *urb, urb->setup_dma)) return -EAGAIN; urb->transfer_flags |= URB_SETUP_MAP_SINGLE; - } else if (hcd->localmem_pool) { - ret = hcd_alloc_coherent( - urb->dev->bus, mem_flags, - &urb->setup_dma, - (void **)&urb->setup_packet, - sizeof(struct usb_ctrlrequest), - DMA_TO_DEVICE); - if (ret) - return ret; - urb->transfer_flags |= URB_SETUP_MAP_LOCAL; } } dir = usb_urb_dir_in(urb) ? DMA_FROM_DEVICE : DMA_TO_DEVICE; if (urb->transfer_buffer_length != 0 && !(urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP)) { - if (hcd_uses_dma(hcd)) { + if (hcd->localmem_pool) { + ret = hcd_alloc_coherent( + urb->dev->bus, mem_flags, + &urb->transfer_dma, + &urb->transfer_buffer, + urb->transfer_buffer_length, + dir); + if (ret == 0) + urb->transfer_flags |= URB_MAP_LOCAL; + } else if (hcd_uses_dma(hcd)) { if (urb->num_sgs) { int n; @@ -1491,15 +1500,6 @@ int usb_hcd_map_urb_for_dma(struct usb_hcd *hcd, struct urb *urb, else urb->transfer_flags |= URB_DMA_MAP_SINGLE; } - } else if (hcd->localmem_pool) { - ret = hcd_alloc_coherent( - urb->dev->bus, mem_flags, - &urb->transfer_dma, - &urb->transfer_buffer, - urb->transfer_buffer_length, - dir); - if (ret == 0) - urb->transfer_flags |= URB_MAP_LOCAL; } if (ret && (urb->transfer_flags & (URB_SETUP_MAP_SINGLE | URB_SETUP_MAP_LOCAL))) diff --git a/drivers/usb/storage/scsiglue.c b/drivers/usb/storage/scsiglue.c index 66a4dcbbb1fc9..f4c2359abb1b0 100644 --- a/drivers/usb/storage/scsiglue.c +++ b/drivers/usb/storage/scsiglue.c @@ -135,7 +135,8 @@ static int slave_configure(struct scsi_device *sdev) * For such controllers we need to make sure the block layer sets * up bounce buffers in addressable memory. */ - if (!hcd_uses_dma(bus_to_hcd(us->pusb_dev->bus))) + if (!hcd_uses_dma(bus_to_hcd(us->pusb_dev->bus)) || + (bus_to_hcd(us->pusb_dev->bus)->localmem_pool != NULL)) blk_queue_bounce_limit(sdev->request_queue, BLK_BOUNCE_HIGH); /* -- GitLab From 6d44694dc7c72e82e0f45d114a9c9e3f3e2aeab7 Mon Sep 17 00:00:00 2001 From: Zhenyu Wang Date: Mon, 2 Dec 2019 15:01:08 +0800 Subject: [PATCH 0448/1437] drm/i915/gvt: use vgpu lock for active state setting Need to align with deactivate, should only use vgpu's lock for active state setting instead of gvt lock. Fixes: f25a49ab8ab9 ("drm/i915/gvt: Use vgpu_lock to protect per vgpu access") Cc: Colin Xu Reviewed-by: Colin Xu Signed-off-by: Zhenyu Wang Link: http://patchwork.freedesktop.org/patch/msgid/20191202070109.73924-2-zhenyuw@linux.intel.com --- drivers/gpu/drm/i915/gvt/vgpu.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/gvt/vgpu.c b/drivers/gpu/drm/i915/gvt/vgpu.c index d5a6e4e3d0fd7..85bd9bf4f6eee 100644 --- a/drivers/gpu/drm/i915/gvt/vgpu.c +++ b/drivers/gpu/drm/i915/gvt/vgpu.c @@ -212,9 +212,9 @@ static void intel_gvt_update_vgpu_types(struct intel_gvt *gvt) */ void intel_gvt_activate_vgpu(struct intel_vgpu *vgpu) { - mutex_lock(&vgpu->gvt->lock); + mutex_lock(&vgpu->vgpu_lock); vgpu->active = true; - mutex_unlock(&vgpu->gvt->lock); + mutex_unlock(&vgpu->vgpu_lock); } /** -- GitLab From bc7bc688366a4f2544699e40cce45393b7f24408 Mon Sep 17 00:00:00 2001 From: Kent Gibson Date: Wed, 11 Dec 2019 08:46:31 +0800 Subject: [PATCH 0449/1437] gpio: mockup: Fix usage of new GPIO_LINE_DIRECTION Restore the external behavior of gpio-mockup to what it was prior to the change to using GPIO_LINE_DIRECTION. Fixes: e42615ec233b ("gpio: Use new GPIO_LINE_DIRECTION") Signed-off-by: Kent Gibson Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-mockup.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/gpio/gpio-mockup.c b/drivers/gpio/gpio-mockup.c index 56d647a30e3ea..c4fdc192ea4e6 100644 --- a/drivers/gpio/gpio-mockup.c +++ b/drivers/gpio/gpio-mockup.c @@ -226,7 +226,7 @@ static int gpio_mockup_get_direction(struct gpio_chip *gc, unsigned int offset) int direction; mutex_lock(&chip->lock); - direction = !chip->lines[offset].dir; + direction = chip->lines[offset].dir; mutex_unlock(&chip->lock); return direction; @@ -395,7 +395,7 @@ static int gpio_mockup_probe(struct platform_device *pdev) struct gpio_chip *gc; struct device *dev; const char *name; - int rv, base; + int rv, base, i; u16 ngpio; dev = &pdev->dev; @@ -447,6 +447,9 @@ static int gpio_mockup_probe(struct platform_device *pdev) if (!chip->lines) return -ENOMEM; + for (i = 0; i < gc->ngpio; i++) + chip->lines[i].dir = GPIO_LINE_DIRECTION_IN; + if (device_property_read_bool(dev, "named-gpio-lines")) { rv = gpio_mockup_name_lines(dev, chip); if (rv) -- GitLab From 11e299de3aced4ea23a9fb1fef6c983c8d516302 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 10 Dec 2019 21:28:31 +0100 Subject: [PATCH 0450/1437] gpio: aspeed: avoid return type warning gcc has a hard time tracking whether BUG_ON(1) ends execution or not: drivers/gpio/gpio-aspeed-sgpio.c: In function 'bank_reg': drivers/gpio/gpio-aspeed-sgpio.c:112:1: error: control reaches end of non-void function [-Werror=return-type] Use the simpler BUG() that gcc knows cannot continue. Fixes: f8b410e3695a ("gpio: aspeed-sgpio: Rename and add Kconfig/Makefile") Signed-off-by: Arnd Bergmann Acked-by: Andrew Jeffery Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-aspeed-sgpio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpio/gpio-aspeed-sgpio.c b/drivers/gpio/gpio-aspeed-sgpio.c index 7e99860ca447e..8319812593e31 100644 --- a/drivers/gpio/gpio-aspeed-sgpio.c +++ b/drivers/gpio/gpio-aspeed-sgpio.c @@ -107,7 +107,7 @@ static void __iomem *bank_reg(struct aspeed_sgpio *gpio, return gpio->base + bank->irq_regs + GPIO_IRQ_STATUS; default: /* acturally if code runs to here, it's an error case */ - BUG_ON(1); + BUG(); } } -- GitLab From 9cc7a18bcbd47e82960a059127497ab09c4abd05 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 10 Dec 2019 20:54:01 +0100 Subject: [PATCH 0451/1437] gpio: xgs-iproc: remove __exit annotation for iproc_gpio_remove When built into the kernel, the driver causes a link problem: `iproc_gpio_remove' referenced in section `.data' of drivers/gpio/gpio-xgs-iproc.o: defined in discarded section `.exit.text' of drivers/gpio/gpio-xgs-iproc.o Remove the incorrect annotation. Fixes: 6a41b6c5fc20 ("gpio: Add xgs-iproc driver") Signed-off-by: Arnd Bergmann Reviewed-by: Chris Packham Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-xgs-iproc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpio/gpio-xgs-iproc.c b/drivers/gpio/gpio-xgs-iproc.c index 773e5c24309e4..b21c2e436b610 100644 --- a/drivers/gpio/gpio-xgs-iproc.c +++ b/drivers/gpio/gpio-xgs-iproc.c @@ -280,7 +280,7 @@ static int iproc_gpio_probe(struct platform_device *pdev) return 0; } -static int __exit iproc_gpio_remove(struct platform_device *pdev) +static int iproc_gpio_remove(struct platform_device *pdev) { struct iproc_gpio_chip *chip; -- GitLab From 961f8209c8d5ef5d33da42e6656d7c8179899da0 Mon Sep 17 00:00:00 2001 From: Michael Walle Date: Tue, 10 Dec 2019 00:43:46 +0100 Subject: [PATCH 0452/1437] arm64: dts: ls1028a: fix typo in TMU calibration data The temperature sensor may jump backwards because there is a wrong calibration value. Both values have to be monotonically increasing. Fix it. This was tested on a custom board. Fixes: 571cebfe8e2b ("arm64: dts: ls1028a: Add Thermal Monitor Unit node") Signed-off-by: Michael Walle Acked-by: Tang Yuantian Signed-off-by: Shawn Guo --- arch/arm64/boot/dts/freescale/fsl-ls1028a.dtsi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm64/boot/dts/freescale/fsl-ls1028a.dtsi b/arch/arm64/boot/dts/freescale/fsl-ls1028a.dtsi index 8e8a77eb596ae..06082c9325315 100644 --- a/arch/arm64/boot/dts/freescale/fsl-ls1028a.dtsi +++ b/arch/arm64/boot/dts/freescale/fsl-ls1028a.dtsi @@ -584,7 +584,7 @@ 0x00010004 0x0000003d 0x00010005 0x00000045 0x00010006 0x0000004d - 0x00010007 0x00000045 + 0x00010007 0x00000055 0x00010008 0x0000005e 0x00010009 0x00000066 0x0001000a 0x0000006e -- GitLab From 44fe5cb32c7599a4059931a98794e0418619aa96 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 4 Dec 2019 14:31:35 +0200 Subject: [PATCH 0453/1437] MAINTAINERS: Append missed file to the database When gpiolib.h internal header had been split to few, the commit 77cb907abe6c ("gpiolib: acpi: Split ACPI stuff to gpiolib-acpi.h") in particular missed the MAINTAINERS database update. Do it here. Fixes: 77cb907abe6c ("gpiolib: acpi: Split ACPI stuff to gpiolib-acpi.h") Signed-off-by: Andy Shevchenko Acked-by: Mika Westerberg Signed-off-by: Bartosz Golaszewski --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index bd5847e802def..302757d8b54f7 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -7031,6 +7031,7 @@ L: linux-acpi@vger.kernel.org S: Maintained F: Documentation/firmware-guide/acpi/gpio-properties.rst F: drivers/gpio/gpiolib-acpi.c +F: drivers/gpio/gpiolib-acpi.h GPIO IR Transmitter M: Sean Young -- GitLab From 256efaea1fdc4e38970489197409a26125ee0aaa Mon Sep 17 00:00:00 2001 From: Russell King Date: Sat, 7 Dec 2019 16:20:18 +0000 Subject: [PATCH 0454/1437] gpiolib: fix up emulated open drain outputs gpiolib has a corner case with open drain outputs that are emulated. When such outputs are outputting a logic 1, emulation will set the hardware to input mode, which will cause gpiod_get_direction() to report that it is in input mode. This is different from the behaviour with a true open-drain output. Unify the semantics here. Cc: Suggested-by: Linus Walleij Signed-off-by: Russell King Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 9913886ede904..8a08a9d4f071f 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -220,6 +220,14 @@ int gpiod_get_direction(struct gpio_desc *desc) chip = gpiod_to_chip(desc); offset = gpio_chip_hwgpio(desc); + /* + * Open drain emulation using input mode may incorrectly report + * input here, fix that up. + */ + if (test_bit(FLAG_OPEN_DRAIN, &desc->flags) && + test_bit(FLAG_IS_OUT, &desc->flags)) + return 0; + if (!chip->get_direction) return -ENOTSUPP; -- GitLab From 2688302bf1924ebccab2a3b298dd50b4f744d48d Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 9 Dec 2019 14:35:44 +0200 Subject: [PATCH 0455/1437] gpio: pca953x: Switch to bitops in IRQ callbacks Since we have driver converted to use bitmap API we must use traditional bit operations (set_bit(), clear_bit(), etc.) against it. Currently IRQ callbacks are missed in the conversion and thus broken. Let's fix it right here right now. Fixes: 35d13d94893f ("gpio: pca953x: convert to use bitmap API") Signed-off-by: Andy Shevchenko Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-pca953x.c | 26 ++++++++++---------------- 1 file changed, 10 insertions(+), 16 deletions(-) diff --git a/drivers/gpio/gpio-pca953x.c b/drivers/gpio/gpio-pca953x.c index 6652bee01966d..9853547e72766 100644 --- a/drivers/gpio/gpio-pca953x.c +++ b/drivers/gpio/gpio-pca953x.c @@ -568,16 +568,18 @@ static void pca953x_irq_mask(struct irq_data *d) { struct gpio_chip *gc = irq_data_get_irq_chip_data(d); struct pca953x_chip *chip = gpiochip_get_data(gc); + irq_hw_number_t hwirq = irqd_to_hwirq(d); - chip->irq_mask[d->hwirq / BANK_SZ] &= ~BIT(d->hwirq % BANK_SZ); + clear_bit(hwirq, chip->irq_mask); } static void pca953x_irq_unmask(struct irq_data *d) { struct gpio_chip *gc = irq_data_get_irq_chip_data(d); struct pca953x_chip *chip = gpiochip_get_data(gc); + irq_hw_number_t hwirq = irqd_to_hwirq(d); - chip->irq_mask[d->hwirq / BANK_SZ] |= BIT(d->hwirq % BANK_SZ); + set_bit(hwirq, chip->irq_mask); } static int pca953x_irq_set_wake(struct irq_data *d, unsigned int on) @@ -635,8 +637,7 @@ static int pca953x_irq_set_type(struct irq_data *d, unsigned int type) { struct gpio_chip *gc = irq_data_get_irq_chip_data(d); struct pca953x_chip *chip = gpiochip_get_data(gc); - int bank_nb = d->hwirq / BANK_SZ; - u8 mask = BIT(d->hwirq % BANK_SZ); + irq_hw_number_t hwirq = irqd_to_hwirq(d); if (!(type & IRQ_TYPE_EDGE_BOTH)) { dev_err(&chip->client->dev, "irq %d: unsupported type %d\n", @@ -644,15 +645,8 @@ static int pca953x_irq_set_type(struct irq_data *d, unsigned int type) return -EINVAL; } - if (type & IRQ_TYPE_EDGE_FALLING) - chip->irq_trig_fall[bank_nb] |= mask; - else - chip->irq_trig_fall[bank_nb] &= ~mask; - - if (type & IRQ_TYPE_EDGE_RISING) - chip->irq_trig_raise[bank_nb] |= mask; - else - chip->irq_trig_raise[bank_nb] &= ~mask; + assign_bit(hwirq, chip->irq_trig_fall, type & IRQ_TYPE_EDGE_FALLING); + assign_bit(hwirq, chip->irq_trig_raise, type & IRQ_TYPE_EDGE_RISING); return 0; } @@ -661,10 +655,10 @@ static void pca953x_irq_shutdown(struct irq_data *d) { struct gpio_chip *gc = irq_data_get_irq_chip_data(d); struct pca953x_chip *chip = gpiochip_get_data(gc); - u8 mask = BIT(d->hwirq % BANK_SZ); + irq_hw_number_t hwirq = irqd_to_hwirq(d); - chip->irq_trig_raise[d->hwirq / BANK_SZ] &= ~mask; - chip->irq_trig_fall[d->hwirq / BANK_SZ] &= ~mask; + clear_bit(hwirq, chip->irq_trig_raise); + clear_bit(hwirq, chip->irq_trig_fall); } static bool pca953x_irq_pending(struct pca953x_chip *chip, unsigned long *pending) -- GitLab From 6f7c41374b62fd80bbd8aae3536c43688c54d95e Mon Sep 17 00:00:00 2001 From: Tetsuo Handa Date: Mon, 25 Nov 2019 10:46:51 +0900 Subject: [PATCH 0456/1437] tomoyo: Don't use nifty names on sockets. syzbot is reporting that use of SOCKET_I()->sk from open() can result in use after free problem [1], for socket's inode is still reachable via /proc/pid/fd/n despite destruction of SOCKET_I()->sk already completed. At first I thought that this race condition applies to only open/getattr permission checks. But James Morris has pointed out that there are more permission checks where this race condition applies to. Thus, get rid of tomoyo_get_socket_name() instead of conditionally bypassing permission checks on sockets. As a side effect of this patch, "socket:[family=\$:type=\$:protocol=\$]" in the policy files has to be rewritten to "socket:[\$]". [1] https://syzkaller.appspot.com/bug?id=73d590010454403d55164cca23bd0565b1eb3b74 Signed-off-by: Tetsuo Handa Reported-by: syzbot Reported-by: James Morris --- security/tomoyo/realpath.c | 32 +------------------------------- 1 file changed, 1 insertion(+), 31 deletions(-) diff --git a/security/tomoyo/realpath.c b/security/tomoyo/realpath.c index e7832448d721d..bf38fc1b59b28 100644 --- a/security/tomoyo/realpath.c +++ b/security/tomoyo/realpath.c @@ -217,31 +217,6 @@ out: return ERR_PTR(-ENOMEM); } -/** - * tomoyo_get_socket_name - Get the name of a socket. - * - * @path: Pointer to "struct path". - * @buffer: Pointer to buffer to return value in. - * @buflen: Sizeof @buffer. - * - * Returns the buffer. - */ -static char *tomoyo_get_socket_name(const struct path *path, char * const buffer, - const int buflen) -{ - struct inode *inode = d_backing_inode(path->dentry); - struct socket *sock = inode ? SOCKET_I(inode) : NULL; - struct sock *sk = sock ? sock->sk : NULL; - - if (sk) { - snprintf(buffer, buflen, "socket:[family=%u:type=%u:protocol=%u]", - sk->sk_family, sk->sk_type, sk->sk_protocol); - } else { - snprintf(buffer, buflen, "socket:[unknown]"); - } - return buffer; -} - /** * tomoyo_realpath_from_path - Returns realpath(3) of the given pathname but ignores chroot'ed root. * @@ -279,12 +254,7 @@ char *tomoyo_realpath_from_path(const struct path *path) break; /* To make sure that pos is '\0' terminated. */ buf[buf_len - 1] = '\0'; - /* Get better name for socket. */ - if (sb->s_magic == SOCKFS_MAGIC) { - pos = tomoyo_get_socket_name(path, buf, buf_len - 1); - goto encode; - } - /* For "pipe:[\$]". */ + /* For "pipe:[\$]" and "socket:[\$]". */ if (dentry->d_op && dentry->d_op->d_dname) { pos = dentry->d_op->d_dname(dentry, buf, buf_len - 1); goto encode; -- GitLab From 96bc4432f5ade1045521f3b247f516b1478166bd Mon Sep 17 00:00:00 2001 From: Paul Chaignon Date: Mon, 9 Dec 2019 19:52:07 +0100 Subject: [PATCH 0457/1437] bpf, riscv: Limit to 33 tail calls MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit All BPF JIT compilers except RISC-V's and MIPS' enforce a 33-tail calls limit at runtime. In addition, a test was recently added, in tailcalls2, to check this limit. This patch updates the tail call limit in RISC-V's JIT compiler to allow 33 tail calls. I tested it using the above selftest on an emulated RISCV64. Fixes: 2353ecc6f91f ("bpf, riscv: add BPF JIT for RV64G") Reported-by: Mahshid Khezri Signed-off-by: Paul Chaignon Signed-off-by: Daniel Borkmann Acked-by: Björn Töpel Acked-by: Martin KaFai Lau Link: https://lore.kernel.org/bpf/966fe384383bf23a0ee1efe8d7291c78a3fb832b.1575916815.git.paul.chaignon@gmail.com --- arch/riscv/net/bpf_jit_comp.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/riscv/net/bpf_jit_comp.c b/arch/riscv/net/bpf_jit_comp.c index 5451ef3845f29..7fbf56aab6610 100644 --- a/arch/riscv/net/bpf_jit_comp.c +++ b/arch/riscv/net/bpf_jit_comp.c @@ -631,14 +631,14 @@ static int emit_bpf_tail_call(int insn, struct rv_jit_context *ctx) return -1; emit(rv_bgeu(RV_REG_A2, RV_REG_T1, off >> 1), ctx); - /* if (--TCC < 0) + /* if (TCC-- < 0) * goto out; */ emit(rv_addi(RV_REG_T1, tcc, -1), ctx); off = (tc_ninsn - (ctx->ninsns - start_insn)) << 2; if (is_13b_check(off, insn)) return -1; - emit(rv_blt(RV_REG_T1, RV_REG_ZERO, off >> 1), ctx); + emit(rv_blt(tcc, RV_REG_ZERO, off >> 1), ctx); /* prog = array->ptrs[index]; * if (!prog) -- GitLab From e49e6f6db04e915dccb494ae10fa14888fea6f89 Mon Sep 17 00:00:00 2001 From: Paul Chaignon Date: Mon, 9 Dec 2019 19:52:52 +0100 Subject: [PATCH 0458/1437] bpf, mips: Limit to 33 tail calls All BPF JIT compilers except RISC-V's and MIPS' enforce a 33-tail calls limit at runtime. In addition, a test was recently added, in tailcalls2, to check this limit. This patch updates the tail call limit in MIPS' JIT compiler to allow 33 tail calls. Fixes: b6bd53f9c4e8 ("MIPS: Add missing file for eBPF JIT.") Reported-by: Mahshid Khezri Signed-off-by: Paul Chaignon Signed-off-by: Daniel Borkmann Acked-by: Martin KaFai Lau Link: https://lore.kernel.org/bpf/b8eb2caac1c25453c539248e56ca22f74b5316af.1575916815.git.paul.chaignon@gmail.com --- arch/mips/net/ebpf_jit.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/arch/mips/net/ebpf_jit.c b/arch/mips/net/ebpf_jit.c index 46b76751f3a5f..3ec69d9cbe88e 100644 --- a/arch/mips/net/ebpf_jit.c +++ b/arch/mips/net/ebpf_jit.c @@ -604,6 +604,7 @@ static void emit_const_to_reg(struct jit_ctx *ctx, int dst, u64 value) static int emit_bpf_tail_call(struct jit_ctx *ctx, int this_idx) { int off, b_off; + int tcc_reg; ctx->flags |= EBPF_SEEN_TC; /* @@ -616,14 +617,14 @@ static int emit_bpf_tail_call(struct jit_ctx *ctx, int this_idx) b_off = b_imm(this_idx + 1, ctx); emit_instr(ctx, bne, MIPS_R_AT, MIPS_R_ZERO, b_off); /* - * if (--TCC < 0) + * if (TCC-- < 0) * goto out; */ /* Delay slot */ - emit_instr(ctx, daddiu, MIPS_R_T5, - (ctx->flags & EBPF_TCC_IN_V1) ? MIPS_R_V1 : MIPS_R_S4, -1); + tcc_reg = (ctx->flags & EBPF_TCC_IN_V1) ? MIPS_R_V1 : MIPS_R_S4; + emit_instr(ctx, daddiu, MIPS_R_T5, tcc_reg, -1); b_off = b_imm(this_idx + 1, ctx); - emit_instr(ctx, bltz, MIPS_R_T5, b_off); + emit_instr(ctx, bltz, tcc_reg, b_off); /* * prog = array->ptrs[index]; * if (prog == NULL) -- GitLab From 4c80c7bc583a87ded5f61906f81256b57c795806 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 10 Dec 2019 21:35:46 +0100 Subject: [PATCH 0459/1437] bpf: Fix build in minimal configurations, again Building with -Werror showed another failure: kernel/bpf/btf.c: In function 'btf_get_prog_ctx_type.isra.31': kernel/bpf/btf.c:3508:63: error: array subscript 0 is above array bounds of 'u8[0]' {aka 'unsigned char[0]'} [-Werror=array-bounds] ctx_type = btf_type_member(conv_struct) + bpf_ctx_convert_map[prog_type] * 2; I don't actually understand why the array is empty, but a similar fix has addressed a related problem, so I suppose we can do the same thing here. Fixes: ce27709b8162 ("bpf: Fix build in minimal configurations") Signed-off-by: Arnd Bergmann Signed-off-by: Daniel Borkmann Acked-by: Martin KaFai Lau Link: https://lore.kernel.org/bpf/20191210203553.2941035-1-arnd@arndb.de --- kernel/bpf/btf.c | 1 + 1 file changed, 1 insertion(+) diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c index 7d40da240891d..ed20758847247 100644 --- a/kernel/bpf/btf.c +++ b/kernel/bpf/btf.c @@ -3470,6 +3470,7 @@ static u8 bpf_ctx_convert_map[] = { [_id] = __ctx_convert##_id, #include #undef BPF_PROG_TYPE + 0, /* avoid empty array */ }; #undef BPF_MAP_TYPE -- GitLab From 5b79bcdf03628a3a9ee04d9cd5fabcf61a8e20be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Toke=20H=C3=B8iland-J=C3=B8rgensen?= Date: Tue, 10 Dec 2019 19:14:12 +0100 Subject: [PATCH 0460/1437] bpftool: Don't crash on missing jited insns or ksyms MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When the kptr_restrict sysctl is set, the kernel can fail to return jited_ksyms or jited_prog_insns, but still have positive values in nr_jited_ksyms and jited_prog_len. This causes bpftool to crash when trying to dump the program because it only checks the len fields not the actual pointers to the instructions and ksyms. Fix this by adding the missing checks. Fixes: 71bb428fe2c1 ("tools: bpf: add bpftool") Fixes: f84192ee00b7 ("tools: bpftool: resolve calls without using imm field") Signed-off-by: Toke Høiland-Jørgensen Signed-off-by: Daniel Borkmann Acked-by: Martin KaFai Lau Link: https://lore.kernel.org/bpf/20191210181412.151226-1-toke@redhat.com --- tools/bpf/bpftool/prog.c | 2 +- tools/bpf/bpftool/xlated_dumper.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/bpf/bpftool/prog.c b/tools/bpf/bpftool/prog.c index 4535c863d2cd9..2ce9c5ba19347 100644 --- a/tools/bpf/bpftool/prog.c +++ b/tools/bpf/bpftool/prog.c @@ -493,7 +493,7 @@ static int do_dump(int argc, char **argv) info = &info_linear->info; if (mode == DUMP_JITED) { - if (info->jited_prog_len == 0) { + if (info->jited_prog_len == 0 || !info->jited_prog_insns) { p_info("no instructions returned"); goto err_free; } diff --git a/tools/bpf/bpftool/xlated_dumper.c b/tools/bpf/bpftool/xlated_dumper.c index 494d7ae3614de..5b91ee65a0802 100644 --- a/tools/bpf/bpftool/xlated_dumper.c +++ b/tools/bpf/bpftool/xlated_dumper.c @@ -174,7 +174,7 @@ static const char *print_call(void *private_data, struct kernel_sym *sym; if (insn->src_reg == BPF_PSEUDO_CALL && - (__u32) insn->imm < dd->nr_jited_ksyms) + (__u32) insn->imm < dd->nr_jited_ksyms && dd->jited_ksyms) address = dd->jited_ksyms[insn->imm]; sym = kernel_syms_search(dd, address); -- GitLab From b444268801a29b10c9edea037efcf4c7c4db9283 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Wed, 11 Dec 2019 10:06:45 -0300 Subject: [PATCH 0461/1437] tools headers kvm: Sync linux/kvm.h with the kernel sources To pick up the changes from: 22945688acd4 ("KVM: PPC: Book3S HV: Support reset of secure guest") No tools changes are caused by this, as the only defines so far used from these files are for syscall arg pretty printing are: $ grep KVM tools/perf/trace/beauty/*.sh tools/perf/trace/beauty/kvm_ioctl.sh:regex='^#[[:space:]]*define[[:space:]]+KVM_(\w+)[[:space:]]+_IO[RW]*\([[:space:]]*KVMIO[[:space:]]*,[[:space:]]*(0x[[:xdigit:]]+).*' $ This addresses these tools/perf build warnings: Warning: Kernel ABI header at 'tools/include/uapi/linux/kvm.h' differs from latest version at 'include/uapi/linux/kvm.h' diff -u tools/include/uapi/linux/kvm.h include/uapi/linux/kvm.h Cc: Adrian Hunter Cc: Bharata B Rao Cc: Jiri Olsa Cc: Namhyung Kim Cc: Paul Mackerras Link: https://lkml.kernel.org/n/tip-bdbe4x02johhul05a03o27zj@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/include/uapi/linux/kvm.h | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/include/uapi/linux/kvm.h b/tools/include/uapi/linux/kvm.h index e6f17c8e2dbac..f0a16b4adbbd6 100644 --- a/tools/include/uapi/linux/kvm.h +++ b/tools/include/uapi/linux/kvm.h @@ -1348,6 +1348,7 @@ struct kvm_s390_ucas_mapping { #define KVM_PPC_GET_CPU_CHAR _IOR(KVMIO, 0xb1, struct kvm_ppc_cpu_char) /* Available with KVM_CAP_PMU_EVENT_FILTER */ #define KVM_SET_PMU_EVENT_FILTER _IOW(KVMIO, 0xb2, struct kvm_pmu_event_filter) +#define KVM_PPC_SVM_OFF _IO(KVMIO, 0xb3) /* ioctl for vm fd */ #define KVM_CREATE_DEVICE _IOWR(KVMIO, 0xe0, struct kvm_create_device) -- GitLab From 63341ab03706e11a31e3dd8ccc0fbc9beaf723f0 Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Wed, 11 Dec 2019 12:11:52 +0100 Subject: [PATCH 0462/1437] virtio-balloon: fix managed page counts when migrating pages between zones In case we have to migrate a ballon page to a newpage of another zone, the managed page count of both zones is wrong. Paired with memory offlining (which will adjust the managed page count), we can trigger kernel crashes and all kinds of different symptoms. One way to reproduce: 1. Start a QEMU guest with 4GB, no NUMA 2. Hotplug a 1GB DIMM and online the memory to ZONE_NORMAL 3. Inflate the balloon to 1GB 4. Unplug the DIMM (be quick, otherwise unmovable data ends up on it) 5. Observe /proc/zoneinfo Node 0, zone Normal pages free 16810 min 24848885473806 low 18471592959183339 high 36918337032892872 spanned 262144 present 262144 managed 18446744073709533486 6. Do anything that requires some memory (e.g., inflate the balloon some more). The OOM goes crazy and the system crashes [ 238.324946] Out of memory: Killed process 537 (login) total-vm:27584kB, anon-rss:860kB, file-rss:0kB, shmem-rss:00 [ 238.338585] systemd invoked oom-killer: gfp_mask=0x100cca(GFP_HIGHUSER_MOVABLE), order=0, oom_score_adj=0 [ 238.339420] CPU: 0 PID: 1 Comm: systemd Tainted: G D W 5.4.0-next-20191204+ #75 [ 238.340139] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS rel-1.12.0-59-gc9ba5276e321-prebuilt.qemu4 [ 238.341121] Call Trace: [ 238.341337] dump_stack+0x8f/0xd0 [ 238.341630] dump_header+0x61/0x5ea [ 238.341942] oom_kill_process.cold+0xb/0x10 [ 238.342299] out_of_memory+0x24d/0x5a0 [ 238.342625] __alloc_pages_slowpath+0xd12/0x1020 [ 238.343024] __alloc_pages_nodemask+0x391/0x410 [ 238.343407] pagecache_get_page+0xc3/0x3a0 [ 238.343757] filemap_fault+0x804/0xc30 [ 238.344083] ? ext4_filemap_fault+0x28/0x42 [ 238.344444] ext4_filemap_fault+0x30/0x42 [ 238.344789] __do_fault+0x37/0x1a0 [ 238.345087] __handle_mm_fault+0x104d/0x1ab0 [ 238.345450] handle_mm_fault+0x169/0x360 [ 238.345790] do_user_addr_fault+0x20d/0x490 [ 238.346154] do_page_fault+0x31/0x210 [ 238.346468] async_page_fault+0x43/0x50 [ 238.346797] RIP: 0033:0x7f47eba4197e [ 238.347110] Code: Bad RIP value. [ 238.347387] RSP: 002b:00007ffd7c0c1890 EFLAGS: 00010293 [ 238.347834] RAX: 0000000000000002 RBX: 000055d196a20a20 RCX: 00007f47eba4197e [ 238.348437] RDX: 0000000000000033 RSI: 00007ffd7c0c18c0 RDI: 0000000000000004 [ 238.349047] RBP: 00007ffd7c0c1c20 R08: 0000000000000000 R09: 0000000000000033 [ 238.349660] R10: 00000000ffffffff R11: 0000000000000293 R12: 0000000000000001 [ 238.350261] R13: ffffffffffffffff R14: 0000000000000000 R15: 00007ffd7c0c18c0 [ 238.350878] Mem-Info: [ 238.351085] active_anon:3121 inactive_anon:51 isolated_anon:0 [ 238.351085] active_file:12 inactive_file:7 isolated_file:0 [ 238.351085] unevictable:0 dirty:0 writeback:0 unstable:0 [ 238.351085] slab_reclaimable:5565 slab_unreclaimable:10170 [ 238.351085] mapped:3 shmem:111 pagetables:155 bounce:0 [ 238.351085] free:720717 free_pcp:2 free_cma:0 [ 238.353757] Node 0 active_anon:12484kB inactive_anon:204kB active_file:48kB inactive_file:28kB unevictable:0kB iss [ 238.355979] Node 0 DMA free:11556kB min:36kB low:48kB high:60kB reserved_highatomic:0KB active_anon:152kB inactivB [ 238.358345] lowmem_reserve[]: 0 2955 2884 2884 2884 [ 238.358761] Node 0 DMA32 free:2677864kB min:7004kB low:10028kB high:13052kB reserved_highatomic:0KB active_anon:0B [ 238.361202] lowmem_reserve[]: 0 0 72057594037927865 72057594037927865 72057594037927865 [ 238.361888] Node 0 Normal free:193448kB min:99395541895224kB low:73886371836733356kB high:147673348131571488kB reB [ 238.364765] lowmem_reserve[]: 0 0 0 0 0 [ 238.365101] Node 0 DMA: 7*4kB (U) 5*8kB (UE) 6*16kB (UME) 2*32kB (UM) 1*64kB (U) 2*128kB (UE) 3*256kB (UME) 2*512B [ 238.366379] Node 0 DMA32: 0*4kB 1*8kB (U) 2*16kB (UM) 2*32kB (UM) 2*64kB (UM) 1*128kB (U) 1*256kB (U) 1*512kB (U)B [ 238.367654] Node 0 Normal: 1985*4kB (UME) 1321*8kB (UME) 844*16kB (UME) 524*32kB (UME) 300*64kB (UME) 138*128kB (B [ 238.369184] Node 0 hugepages_total=0 hugepages_free=0 hugepages_surp=0 hugepages_size=2048kB [ 238.369915] 130 total pagecache pages [ 238.370241] 0 pages in swap cache [ 238.370533] Swap cache stats: add 0, delete 0, find 0/0 [ 238.370981] Free swap = 0kB [ 238.371239] Total swap = 0kB [ 238.371488] 1048445 pages RAM [ 238.371756] 0 pages HighMem/MovableOnly [ 238.372090] 306992 pages reserved [ 238.372376] 0 pages cma reserved [ 238.372661] 0 pages hwpoisoned In another instance (older kernel), I was able to observe this (negative page count :/): [ 180.896971] Offlined Pages 32768 [ 182.667462] Offlined Pages 32768 [ 184.408117] Offlined Pages 32768 [ 186.026321] Offlined Pages 32768 [ 187.684861] Offlined Pages 32768 [ 189.227013] Offlined Pages 32768 [ 190.830303] Offlined Pages 32768 [ 190.833071] Built 1 zonelists, mobility grouping on. Total pages: -36920272750453009 In another instance (older kernel), I was no longer able to start any process: [root@vm ~]# [ 214.348068] Offlined Pages 32768 [ 215.973009] Offlined Pages 32768 cat /proc/meminfo -bash: fork: Cannot allocate memory [root@vm ~]# cat /proc/meminfo -bash: fork: Cannot allocate memory Fix it by properly adjusting the managed page count when migrating if the zone changed. The managed page count of the zones now looks after unplug of the DIMM (and after deflating the balloon) just like before inflating the balloon (and plugging+onlining the DIMM). We'll temporarily modify the totalram page count. If this ever becomes a problem, we can fine tune by providing helpers that don't touch the totalram pages (e.g., adjust_zone_managed_page_count()). Please note that fixing up the managed page count is only necessary when we adjusted the managed page count when inflating - only if we don't have VIRTIO_BALLOON_F_DEFLATE_ON_OOM. With that feature, the managed page count is not touched when inflating/deflating. Reported-by: Yumei Huang Fixes: 3dcc0571cd64 ("mm: correctly update zone->managed_pages") Cc: # v3.11+ Cc: "Michael S. Tsirkin" Cc: Jason Wang Cc: Jiang Liu Cc: Andrew Morton Cc: Igor Mammedov Cc: virtualization@lists.linux-foundation.org Signed-off-by: David Hildenbrand Signed-off-by: Michael S. Tsirkin --- drivers/virtio/virtio_balloon.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/drivers/virtio/virtio_balloon.c b/drivers/virtio/virtio_balloon.c index e05679c478e2b..9f4117766bb1a 100644 --- a/drivers/virtio/virtio_balloon.c +++ b/drivers/virtio/virtio_balloon.c @@ -721,6 +721,17 @@ static int virtballoon_migratepage(struct balloon_dev_info *vb_dev_info, get_page(newpage); /* balloon reference */ + /* + * When we migrate a page to a different zone and adjusted the + * managed page count when inflating, we have to fixup the count of + * both involved zones. + */ + if (!virtio_has_feature(vb->vdev, VIRTIO_BALLOON_F_DEFLATE_ON_OOM) && + page_zone(page) != page_zone(newpage)) { + adjust_managed_page_count(page, 1); + adjust_managed_page_count(newpage, -1); + } + /* balloon's page migration 1st step -- inflate "newpage" */ spin_lock_irqsave(&vb_dev_info->pages_lock, flags); balloon_page_insert(vb_dev_info, newpage); -- GitLab From 2a946fa1c8bc26df03bacebb6811b656d5e0cf1c Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Tue, 19 Nov 2019 05:21:47 -0500 Subject: [PATCH 0463/1437] virtio_balloon: name cleanups free_page_order is a confusing name. It's not a page order actually, it's the order of the block of memory we are hinting. Rename to hint_block_order. Also, rename SIZE to BYTES to make it clear it's the block size in bytes. Signed-off-by: Michael S. Tsirkin Reviewed-by: Wei Wang Reviewed-by: David Hildenbrand --- drivers/virtio/virtio_balloon.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/drivers/virtio/virtio_balloon.c b/drivers/virtio/virtio_balloon.c index 9f4117766bb1a..23119b3a71bd9 100644 --- a/drivers/virtio/virtio_balloon.c +++ b/drivers/virtio/virtio_balloon.c @@ -32,10 +32,10 @@ #define VIRTIO_BALLOON_FREE_PAGE_ALLOC_FLAG (__GFP_NORETRY | __GFP_NOWARN | \ __GFP_NOMEMALLOC) /* The order of free page blocks to report to host */ -#define VIRTIO_BALLOON_FREE_PAGE_ORDER (MAX_ORDER - 1) +#define VIRTIO_BALLOON_HINT_BLOCK_ORDER (MAX_ORDER - 1) /* The size of a free page block in bytes */ -#define VIRTIO_BALLOON_FREE_PAGE_SIZE \ - (1 << (VIRTIO_BALLOON_FREE_PAGE_ORDER + PAGE_SHIFT)) +#define VIRTIO_BALLOON_HINT_BLOCK_BYTES \ + (1 << (VIRTIO_BALLOON_HINT_BLOCK_ORDER + PAGE_SHIFT)) #ifdef CONFIG_BALLOON_COMPACTION static struct vfsmount *balloon_mnt; @@ -380,7 +380,7 @@ static unsigned long return_free_pages_to_mm(struct virtio_balloon *vb, if (!page) break; free_pages((unsigned long)page_address(page), - VIRTIO_BALLOON_FREE_PAGE_ORDER); + VIRTIO_BALLOON_HINT_BLOCK_ORDER); } vb->num_free_page_blocks -= num_returned; spin_unlock_irq(&vb->free_page_list_lock); @@ -582,7 +582,7 @@ static int get_free_page_and_send(struct virtio_balloon *vb) ; page = alloc_pages(VIRTIO_BALLOON_FREE_PAGE_ALLOC_FLAG, - VIRTIO_BALLOON_FREE_PAGE_ORDER); + VIRTIO_BALLOON_HINT_BLOCK_ORDER); /* * When the allocation returns NULL, it indicates that we have got all * the possible free pages, so return -EINTR to stop. @@ -591,13 +591,13 @@ static int get_free_page_and_send(struct virtio_balloon *vb) return -EINTR; p = page_address(page); - sg_init_one(&sg, p, VIRTIO_BALLOON_FREE_PAGE_SIZE); + sg_init_one(&sg, p, VIRTIO_BALLOON_HINT_BLOCK_BYTES); /* There is always 1 entry reserved for the cmd id to use. */ if (vq->num_free > 1) { err = virtqueue_add_inbuf(vq, &sg, 1, p, GFP_KERNEL); if (unlikely(err)) { free_pages((unsigned long)p, - VIRTIO_BALLOON_FREE_PAGE_ORDER); + VIRTIO_BALLOON_HINT_BLOCK_ORDER); return err; } virtqueue_kick(vq); @@ -610,7 +610,7 @@ static int get_free_page_and_send(struct virtio_balloon *vb) * The vq has no available entry to add this page block, so * just free it. */ - free_pages((unsigned long)p, VIRTIO_BALLOON_FREE_PAGE_ORDER); + free_pages((unsigned long)p, VIRTIO_BALLOON_HINT_BLOCK_ORDER); } return 0; @@ -776,11 +776,11 @@ static unsigned long shrink_free_pages(struct virtio_balloon *vb, unsigned long blocks_to_free, blocks_freed; pages_to_free = round_up(pages_to_free, - 1 << VIRTIO_BALLOON_FREE_PAGE_ORDER); - blocks_to_free = pages_to_free >> VIRTIO_BALLOON_FREE_PAGE_ORDER; + 1 << VIRTIO_BALLOON_HINT_BLOCK_ORDER); + blocks_to_free = pages_to_free >> VIRTIO_BALLOON_HINT_BLOCK_ORDER; blocks_freed = return_free_pages_to_mm(vb, blocks_to_free); - return blocks_freed << VIRTIO_BALLOON_FREE_PAGE_ORDER; + return blocks_freed << VIRTIO_BALLOON_HINT_BLOCK_ORDER; } static unsigned long leak_balloon_pages(struct virtio_balloon *vb, @@ -837,7 +837,7 @@ static unsigned long virtio_balloon_shrinker_count(struct shrinker *shrinker, unsigned long count; count = vb->num_pages / VIRTIO_BALLOON_PAGES_PER_PAGE; - count += vb->num_free_page_blocks << VIRTIO_BALLOON_FREE_PAGE_ORDER; + count += vb->num_free_page_blocks << VIRTIO_BALLOON_HINT_BLOCK_ORDER; return count; } -- GitLab From 63b9b80e9f5b2c463d98d6e550e0d0e3ace66033 Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Tue, 19 Nov 2019 05:25:24 -0500 Subject: [PATCH 0464/1437] virtio_balloon: divide/multiply instead of shifts We managed to get confused about the shift direction at least once. Let's switch to division/multiplcation instead. Add a number of pages macro for this purpose. We still keep the order macro around too since this is what alloc/free pages want. Signed-off-by: Michael S. Tsirkin Reviewed-by: Wei Wang Reviewed-by: David Hildenbrand --- drivers/virtio/virtio_balloon.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/virtio/virtio_balloon.c b/drivers/virtio/virtio_balloon.c index 23119b3a71bd9..93f995f6cf364 100644 --- a/drivers/virtio/virtio_balloon.c +++ b/drivers/virtio/virtio_balloon.c @@ -36,6 +36,7 @@ /* The size of a free page block in bytes */ #define VIRTIO_BALLOON_HINT_BLOCK_BYTES \ (1 << (VIRTIO_BALLOON_HINT_BLOCK_ORDER + PAGE_SHIFT)) +#define VIRTIO_BALLOON_HINT_BLOCK_PAGES (1 << VIRTIO_BALLOON_HINT_BLOCK_ORDER) #ifdef CONFIG_BALLOON_COMPACTION static struct vfsmount *balloon_mnt; @@ -776,11 +777,11 @@ static unsigned long shrink_free_pages(struct virtio_balloon *vb, unsigned long blocks_to_free, blocks_freed; pages_to_free = round_up(pages_to_free, - 1 << VIRTIO_BALLOON_HINT_BLOCK_ORDER); - blocks_to_free = pages_to_free >> VIRTIO_BALLOON_HINT_BLOCK_ORDER; + VIRTIO_BALLOON_HINT_BLOCK_PAGES); + blocks_to_free = pages_to_free / VIRTIO_BALLOON_HINT_BLOCK_PAGES; blocks_freed = return_free_pages_to_mm(vb, blocks_to_free); - return blocks_freed << VIRTIO_BALLOON_HINT_BLOCK_ORDER; + return blocks_freed * VIRTIO_BALLOON_HINT_BLOCK_PAGES; } static unsigned long leak_balloon_pages(struct virtio_balloon *vb, @@ -837,7 +838,7 @@ static unsigned long virtio_balloon_shrinker_count(struct shrinker *shrinker, unsigned long count; count = vb->num_pages / VIRTIO_BALLOON_PAGES_PER_PAGE; - count += vb->num_free_page_blocks << VIRTIO_BALLOON_HINT_BLOCK_ORDER; + count += vb->num_free_page_blocks * VIRTIO_BALLOON_HINT_BLOCK_PAGES; return count; } -- GitLab From c453312857ba41129db3558f5428405bbbb8f1a4 Mon Sep 17 00:00:00 2001 From: Russell King Date: Mon, 9 Dec 2019 11:17:30 +0000 Subject: [PATCH 0465/1437] ARM: net: bpf: Improve prologue code sequence Improve the prologue code sequence to be able to take advantage of 64-bit stores, changing the code from: push {r4, r5, r6, r7, r8, r9, fp, lr} mov fp, sp sub ip, sp, #80 ; 0x50 sub sp, sp, #600 ; 0x258 str ip, [fp, #-100] ; 0xffffff9c mov r6, #0 str r6, [fp, #-96] ; 0xffffffa0 mov r4, #0 mov r3, r4 mov r2, r0 str r4, [fp, #-104] ; 0xffffff98 str r4, [fp, #-108] ; 0xffffff94 to the tighter: push {r4, r5, r6, r7, r8, r9, fp, lr} mov fp, sp mov r3, #0 sub r2, sp, #80 ; 0x50 sub sp, sp, #600 ; 0x258 strd r2, [fp, #-100] ; 0xffffff9c mov r2, #0 strd r2, [fp, #-108] ; 0xffffff94 mov r2, r0 resulting in a saving of three instructions. Signed-off-by: Russell King Signed-off-by: Daniel Borkmann Link: https://lore.kernel.org/bpf/E1ieH2g-0004ih-Rb@rmk-PC.armlinux.org.uk --- arch/arm/net/bpf_jit_32.c | 30 +++++++++++++----------------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/arch/arm/net/bpf_jit_32.c b/arch/arm/net/bpf_jit_32.c index 97dc386e3cb8e..cc29869d12a3e 100644 --- a/arch/arm/net/bpf_jit_32.c +++ b/arch/arm/net/bpf_jit_32.c @@ -1260,12 +1260,9 @@ static inline void emit_push_r64(const s8 src[], struct jit_ctx *ctx) static void build_prologue(struct jit_ctx *ctx) { - const s8 r0 = bpf2a32[BPF_REG_0][1]; - const s8 r2 = bpf2a32[BPF_REG_1][1]; - const s8 r3 = bpf2a32[BPF_REG_1][0]; - const s8 r4 = bpf2a32[BPF_REG_6][1]; - const s8 fplo = bpf2a32[BPF_REG_FP][1]; - const s8 fphi = bpf2a32[BPF_REG_FP][0]; + const s8 arm_r0 = bpf2a32[BPF_REG_0][1]; + const s8 *bpf_r1 = bpf2a32[BPF_REG_1]; + const s8 *bpf_fp = bpf2a32[BPF_REG_FP]; const s8 *tcc = bpf2a32[TCALL_CNT]; /* Save callee saved registers. */ @@ -1278,8 +1275,10 @@ static void build_prologue(struct jit_ctx *ctx) emit(ARM_PUSH(CALLEE_PUSH_MASK), ctx); emit(ARM_MOV_R(ARM_FP, ARM_SP), ctx); #endif - /* Save frame pointer for later */ - emit(ARM_SUB_I(ARM_IP, ARM_SP, SCRATCH_SIZE), ctx); + /* mov r3, #0 */ + /* sub r2, sp, #SCRATCH_SIZE */ + emit(ARM_MOV_I(bpf_r1[0], 0), ctx); + emit(ARM_SUB_I(bpf_r1[1], ARM_SP, SCRATCH_SIZE), ctx); ctx->stack_size = imm8m(STACK_SIZE); @@ -1287,18 +1286,15 @@ static void build_prologue(struct jit_ctx *ctx) emit(ARM_SUB_I(ARM_SP, ARM_SP, ctx->stack_size), ctx); /* Set up BPF prog stack base register */ - emit_a32_mov_r(fplo, ARM_IP, ctx); - emit_a32_mov_i(fphi, 0, ctx); + emit_a32_mov_r64(true, bpf_fp, bpf_r1, ctx); - /* mov r4, 0 */ - emit(ARM_MOV_I(r4, 0), ctx); + /* Initialize Tail Count */ + emit(ARM_MOV_I(bpf_r1[1], 0), ctx); + emit_a32_mov_r64(true, tcc, bpf_r1, ctx); /* Move BPF_CTX to BPF_R1 */ - emit(ARM_MOV_R(r3, r4), ctx); - emit(ARM_MOV_R(r2, r0), ctx); - /* Initialize Tail Count */ - emit(ARM_STR_I(r4, ARM_FP, EBPF_SCRATCH_TO_ARM_FP(tcc[0])), ctx); - emit(ARM_STR_I(r4, ARM_FP, EBPF_SCRATCH_TO_ARM_FP(tcc[1])), ctx); + emit(ARM_MOV_R(bpf_r1[1], arm_r0), ctx); + /* end of prologue */ } -- GitLab From 09c4708d3cf42b2b2211b1aee82e7baf446a24e8 Mon Sep 17 00:00:00 2001 From: Andrii Nakryiko Date: Mon, 9 Dec 2019 14:40:22 -0800 Subject: [PATCH 0466/1437] libbpf: Bump libpf current version to v0.0.7 New development cycles starts, bump to v0.0.7 proactively. Signed-off-by: Andrii Nakryiko Signed-off-by: Daniel Borkmann Acked-by: Martin KaFai Lau Link: https://lore.kernel.org/bpf/20191209224022.3544519-1-andriin@fb.com --- tools/lib/bpf/libbpf.map | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tools/lib/bpf/libbpf.map b/tools/lib/bpf/libbpf.map index 8ddc2c40e482d..495df575f87f8 100644 --- a/tools/lib/bpf/libbpf.map +++ b/tools/lib/bpf/libbpf.map @@ -208,3 +208,6 @@ LIBBPF_0.0.6 { btf__find_by_name_kind; libbpf_find_vmlinux_btf_id; } LIBBPF_0.0.5; + +LIBBPF_0.0.7 { +} LIBBPF_0.0.6; -- GitLab From b590cb5f802dc20c72f507f7fbe6737222d0afba Mon Sep 17 00:00:00 2001 From: Stanislav Fomichev Date: Tue, 10 Dec 2019 11:19:33 -0800 Subject: [PATCH 0467/1437] bpf: Switch to offsetofend in BPF_PROG_TEST_RUN Switch existing pattern of "offsetof(..., member) + FIELD_SIZEOF(..., member)' to "offsetofend(..., member)" which does exactly what we need without all the copy-paste. Suggested-by: Andrii Nakryiko Signed-off-by: Stanislav Fomichev Signed-off-by: Daniel Borkmann Acked-by: Andrii Nakryiko Link: https://lore.kernel.org/bpf/20191210191933.105321-1-sdf@google.com --- net/bpf/test_run.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/net/bpf/test_run.c b/net/bpf/test_run.c index 915c2d6f7fb9d..85c8cbbada922 100644 --- a/net/bpf/test_run.c +++ b/net/bpf/test_run.c @@ -252,22 +252,19 @@ static int convert___skb_to_skb(struct sk_buff *skb, struct __sk_buff *__skb) /* priority is allowed */ - if (!range_is_zero(__skb, offsetof(struct __sk_buff, priority) + - FIELD_SIZEOF(struct __sk_buff, priority), + if (!range_is_zero(__skb, offsetofend(struct __sk_buff, priority), offsetof(struct __sk_buff, cb))) return -EINVAL; /* cb is allowed */ - if (!range_is_zero(__skb, offsetof(struct __sk_buff, cb) + - FIELD_SIZEOF(struct __sk_buff, cb), + if (!range_is_zero(__skb, offsetofend(struct __sk_buff, cb), offsetof(struct __sk_buff, tstamp))) return -EINVAL; /* tstamp is allowed */ - if (!range_is_zero(__skb, offsetof(struct __sk_buff, tstamp) + - FIELD_SIZEOF(struct __sk_buff, tstamp), + if (!range_is_zero(__skb, offsetofend(struct __sk_buff, tstamp), sizeof(struct __sk_buff))) return -EINVAL; @@ -437,8 +434,7 @@ static int verify_user_bpf_flow_keys(struct bpf_flow_keys *ctx) /* flags is allowed */ - if (!range_is_zero(ctx, offsetof(struct bpf_flow_keys, flags) + - FIELD_SIZEOF(struct bpf_flow_keys, flags), + if (!range_is_zero(ctx, offsetofend(struct bpf_flow_keys, flags), sizeof(struct bpf_flow_keys))) return -EINVAL; -- GitLab From 750bde2fd4ffacc297473c36d6fdb29c395b06aa Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Thu, 21 Nov 2019 07:10:41 +0000 Subject: [PATCH 0468/1437] drm/i915: Serialise with remote retirement Since retirement may be running in a worker on another CPU, it may be skipped in the local intel_gt_wait_for_idle(). To ensure the state is consistent for our sanity checks upon load, serialise with the remote retirer by waiting on the timeline->mutex. Outside of this use case, e.g. on suspend or module unload, we expect the slack to be picked up by intel_gt_pm_wait_for_idle() and so prefer to put the special case serialisation with retirement in its single user, for now at least. Signed-off-by: Chris Wilson Cc: Tvrtko Ursulin Reviewed-by: Tvrtko Ursulin Link: https://patchwork.freedesktop.org/patch/msgid/20191121071044.97798-2-chris@chris-wilson.co.uk (cherry picked from commit 2d0fb251360ab7eccbffd99f6933a2a4de678d52) Fixes: 093b92287363 ("drm/i915: Split i915_active.mutex into an irq-safe spinlock for the rbtree") Closes: https://gitlab.freedesktop.org/drm/intel/issues/754 Signed-off-by: Joonas Lahtinen --- drivers/gpu/drm/i915/i915_gem.c | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index b9eb6b3149b71..d034fa413164b 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -45,6 +45,7 @@ #include "gem/i915_gem_context.h" #include "gem/i915_gem_ioctls.h" #include "gem/i915_gem_pm.h" +#include "gt/intel_context.h" #include "gt/intel_engine_user.h" #include "gt/intel_gt.h" #include "gt/intel_gt_pm.h" @@ -1053,6 +1054,18 @@ out: return err; } +static int __intel_context_flush_retire(struct intel_context *ce) +{ + struct intel_timeline *tl; + + tl = intel_context_timeline_lock(ce); + if (IS_ERR(tl)) + return PTR_ERR(tl); + + intel_context_timeline_unlock(tl); + return 0; +} + static int __intel_engines_record_defaults(struct intel_gt *gt) { struct i915_request *requests[I915_NUM_ENGINES] = {}; @@ -1121,13 +1134,20 @@ err_rq: if (!rq) continue; - /* We want to be able to unbind the state from the GGTT */ - GEM_BUG_ON(intel_context_is_pinned(rq->hw_context)); - + GEM_BUG_ON(!test_bit(CONTEXT_ALLOC_BIT, + &rq->hw_context->flags)); state = rq->hw_context->state; if (!state) continue; + /* Serialise with retirement on another CPU */ + err = __intel_context_flush_retire(rq->hw_context); + if (err) + goto out; + + /* We want to be able to unbind the state from the GGTT */ + GEM_BUG_ON(intel_context_is_pinned(rq->hw_context)); + /* * As we will hold a reference to the logical state, it will * not be torn down with the context, and importantly the -- GitLab From ce91f1a43b37463f517155bdfbd525eb43adbd1a Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Wed, 11 Dec 2019 16:20:02 +0200 Subject: [PATCH 0469/1437] xhci: Fix memory leak in xhci_add_in_port() When xHCI is part of Alpine or Titan Ridge Thunderbolt controller and the xHCI device is hot-removed as a result of unplugging a dock for example, the driver leaks memory it allocates for xhci->usb3_rhub.psi and xhci->usb2_rhub.psi in xhci_add_in_port() as reported by kmemleak: unreferenced object 0xffff922c24ef42f0 (size 16): comm "kworker/u16:2", pid 178, jiffies 4294711640 (age 956.620s) hex dump (first 16 bytes): 21 00 0c 00 12 00 dc 05 23 00 e0 01 00 00 00 00 !.......#....... backtrace: [<000000007ac80914>] xhci_mem_init+0xcf8/0xeb7 [<0000000001b6d775>] xhci_init+0x7c/0x160 [<00000000db443fe3>] xhci_gen_setup+0x214/0x340 [<00000000fdffd320>] xhci_pci_setup+0x48/0x110 [<00000000541e1e03>] usb_add_hcd.cold+0x265/0x747 [<00000000ca47a56b>] usb_hcd_pci_probe+0x219/0x3b4 [<0000000021043861>] xhci_pci_probe+0x24/0x1c0 [<00000000b9231f25>] local_pci_probe+0x3d/0x70 [<000000006385c9d7>] pci_device_probe+0xd0/0x150 [<0000000070241068>] really_probe+0xf5/0x3c0 [<0000000061f35c0a>] driver_probe_device+0x58/0x100 [<000000009da11198>] bus_for_each_drv+0x79/0xc0 [<000000009ce45f69>] __device_attach+0xda/0x160 [<00000000df201aaf>] pci_bus_add_device+0x46/0x70 [<0000000088a1bc48>] pci_bus_add_devices+0x27/0x60 [<00000000ad9ee708>] pci_bus_add_devices+0x52/0x60 unreferenced object 0xffff922c24ef3318 (size 8): comm "kworker/u16:2", pid 178, jiffies 4294711640 (age 956.620s) hex dump (first 8 bytes): 34 01 05 00 35 41 0a 00 4...5A.. backtrace: [<000000007ac80914>] xhci_mem_init+0xcf8/0xeb7 [<0000000001b6d775>] xhci_init+0x7c/0x160 [<00000000db443fe3>] xhci_gen_setup+0x214/0x340 [<00000000fdffd320>] xhci_pci_setup+0x48/0x110 [<00000000541e1e03>] usb_add_hcd.cold+0x265/0x747 [<00000000ca47a56b>] usb_hcd_pci_probe+0x219/0x3b4 [<0000000021043861>] xhci_pci_probe+0x24/0x1c0 [<00000000b9231f25>] local_pci_probe+0x3d/0x70 [<000000006385c9d7>] pci_device_probe+0xd0/0x150 [<0000000070241068>] really_probe+0xf5/0x3c0 [<0000000061f35c0a>] driver_probe_device+0x58/0x100 [<000000009da11198>] bus_for_each_drv+0x79/0xc0 [<000000009ce45f69>] __device_attach+0xda/0x160 [<00000000df201aaf>] pci_bus_add_device+0x46/0x70 [<0000000088a1bc48>] pci_bus_add_devices+0x27/0x60 [<00000000ad9ee708>] pci_bus_add_devices+0x52/0x60 Fix this by calling kfree() for the both psi objects in xhci_mem_cleanup(). Cc: # 4.4+ Fixes: 47189098f8be ("xhci: parse xhci protocol speed ID list for usb 3.1 usage") Signed-off-by: Mika Westerberg Signed-off-by: Mathias Nyman Link: https://lore.kernel.org/r/20191211142007.8847-2-mathias.nyman@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-mem.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index e16eda6e2b8b2..3b1388fa2f36e 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -1909,13 +1909,17 @@ no_bw: xhci->usb3_rhub.num_ports = 0; xhci->num_active_eps = 0; kfree(xhci->usb2_rhub.ports); + kfree(xhci->usb2_rhub.psi); kfree(xhci->usb3_rhub.ports); + kfree(xhci->usb3_rhub.psi); kfree(xhci->hw_ports); kfree(xhci->rh_bw); kfree(xhci->ext_caps); xhci->usb2_rhub.ports = NULL; + xhci->usb2_rhub.psi = NULL; xhci->usb3_rhub.ports = NULL; + xhci->usb3_rhub.psi = NULL; xhci->hw_ports = NULL; xhci->rh_bw = NULL; xhci->ext_caps = NULL; -- GitLab From 057d476fff778f1d3b9f861fdb5437ea1a3cfc99 Mon Sep 17 00:00:00 2001 From: Mathias Nyman Date: Wed, 11 Dec 2019 16:20:03 +0200 Subject: [PATCH 0470/1437] xhci: fix USB3 device initiated resume race with roothub autosuspend A race in xhci USB3 remote wake handling may force device back to suspend after it initiated resume siganaling, causing a missed resume event or warm reset of device. When a USB3 link completes resume signaling and goes to enabled (UO) state a interrupt is issued and the interrupt handler will clear the bus_state->port_remote_wakeup resume flag, allowing bus suspend. If the USB3 roothub thread just finished reading port status before the interrupt, finding ports still in suspended (U3) state, but hasn't yet started suspending the hub, then the xhci interrupt handler will clear the flag that prevented roothub suspend and allow bus to suspend, forcing all port links back to suspended (U3) state. Example case: usb_runtime_suspend() # because all ports still show suspended U3 usb_suspend_both() hub_suspend(); # successful as hub->wakeup_bits not set yet ==> INTERRUPT xhci_irq() handle_port_status() clear bus_state->port_remote_wakeup usb_wakeup_notification() sets hub->wakeup_bits; kick_hub_wq() <== END INTERRUPT hcd_bus_suspend() xhci_bus_suspend() # success as port_remote_wakeup bits cleared Fix this by increasing roothub usage count during port resume to prevent roothub autosuspend, and by making sure bus_state->port_remote_wakeup flag is only cleared after resume completion is visible, i.e. after xhci roothub returned U0 or other non-U3 link state link on a get port status request. Issue rootcaused by Chiasheng Lee Cc: Cc: Lee, Hou-hsun Reported-by: Lee, Chiasheng Signed-off-by: Mathias Nyman Link: https://lore.kernel.org/r/20191211142007.8847-3-mathias.nyman@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-hub.c | 10 ++++++++++ drivers/usb/host/xhci-ring.c | 3 +-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c index b7d23c4387569..4b870cd6c5758 100644 --- a/drivers/usb/host/xhci-hub.c +++ b/drivers/usb/host/xhci-hub.c @@ -920,11 +920,13 @@ static void xhci_get_usb3_port_status(struct xhci_port *port, u32 *status, { struct xhci_bus_state *bus_state; struct xhci_hcd *xhci; + struct usb_hcd *hcd; u32 link_state; u32 portnum; bus_state = &port->rhub->bus_state; xhci = hcd_to_xhci(port->rhub->hcd); + hcd = port->rhub->hcd; link_state = portsc & PORT_PLS_MASK; portnum = port->hcd_portnum; @@ -952,6 +954,14 @@ static void xhci_get_usb3_port_status(struct xhci_port *port, u32 *status, bus_state->suspended_ports &= ~(1 << portnum); } + /* remote wake resume signaling complete */ + if (bus_state->port_remote_wakeup & (1 << portnum) && + link_state != XDEV_RESUME && + link_state != XDEV_RECOVERY) { + bus_state->port_remote_wakeup &= ~(1 << portnum); + usb_hcd_end_port_resume(&hcd->self, portnum); + } + xhci_hub_report_usb3_link_state(xhci, status, portsc); xhci_del_comp_mod_timer(xhci, portsc, portnum); } diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 6475c3d3b43b6..9ebaa8e132a9e 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -1628,7 +1628,6 @@ static void handle_port_status(struct xhci_hcd *xhci, slot_id = xhci_find_slot_id_by_port(hcd, xhci, hcd_portnum + 1); if (slot_id && xhci->devs[slot_id]) xhci->devs[slot_id]->flags |= VDEV_PORT_ERROR; - bus_state->port_remote_wakeup &= ~(1 << hcd_portnum); } if ((portsc & PORT_PLC) && (portsc & PORT_PLS_MASK) == XDEV_RESUME) { @@ -1648,6 +1647,7 @@ static void handle_port_status(struct xhci_hcd *xhci, */ bus_state->port_remote_wakeup |= 1 << hcd_portnum; xhci_test_and_clear_bit(xhci, port, PORT_PLC); + usb_hcd_start_port_resume(&hcd->self, hcd_portnum); xhci_set_link_state(xhci, port, XDEV_U0); /* Need to wait until the next link state change * indicates the device is actually in U0. @@ -1688,7 +1688,6 @@ static void handle_port_status(struct xhci_hcd *xhci, if (slot_id && xhci->devs[slot_id]) xhci_ring_device(xhci, slot_id); if (bus_state->port_remote_wakeup & (1 << hcd_portnum)) { - bus_state->port_remote_wakeup &= ~(1 << hcd_portnum); xhci_test_and_clear_bit(xhci, port, PORT_PLC); usb_wakeup_notification(hcd->self.root_hub, hcd_portnum + 1); -- GitLab From f2c710f7dca8457e88b4ac9de2060f011254f9dd Mon Sep 17 00:00:00 2001 From: Henry Lin Date: Wed, 11 Dec 2019 16:20:04 +0200 Subject: [PATCH 0471/1437] usb: xhci: only set D3hot for pci device Xhci driver cannot call pci_set_power_state() on non-pci xhci host controllers. For example, NVIDIA Tegra XHCI host controller which acts as platform device with XHCI_SPURIOUS_WAKEUP quirk set in some platform hits this issue during shutdown. Cc: Fixes: 638298dc66ea ("xhci: Fix spurious wakeups after S5 on Haswell") Signed-off-by: Henry Lin Signed-off-by: Mathias Nyman Link: https://lore.kernel.org/r/20191211142007.8847-4-mathias.nyman@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-pci.c | 13 +++++++++++++ drivers/usb/host/xhci.c | 7 ++----- drivers/usb/host/xhci.h | 1 + 3 files changed, 16 insertions(+), 5 deletions(-) diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c index a0025d23b2573..2907fe4d78ddd 100644 --- a/drivers/usb/host/xhci-pci.c +++ b/drivers/usb/host/xhci-pci.c @@ -521,6 +521,18 @@ static int xhci_pci_resume(struct usb_hcd *hcd, bool hibernated) } #endif /* CONFIG_PM */ +static void xhci_pci_shutdown(struct usb_hcd *hcd) +{ + struct xhci_hcd *xhci = hcd_to_xhci(hcd); + struct pci_dev *pdev = to_pci_dev(hcd->self.controller); + + xhci_shutdown(hcd); + + /* Yet another workaround for spurious wakeups at shutdown with HSW */ + if (xhci->quirks & XHCI_SPURIOUS_WAKEUP) + pci_set_power_state(pdev, PCI_D3hot); +} + /*-------------------------------------------------------------------------*/ /* PCI driver selection metadata; PCI hotplugging uses this */ @@ -556,6 +568,7 @@ static int __init xhci_pci_init(void) #ifdef CONFIG_PM xhci_pci_hc_driver.pci_suspend = xhci_pci_suspend; xhci_pci_hc_driver.pci_resume = xhci_pci_resume; + xhci_pci_hc_driver.shutdown = xhci_pci_shutdown; #endif return pci_register_driver(&xhci_pci_driver); } diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 6721d059f58a1..c5ee562c4c74b 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -770,7 +770,7 @@ static void xhci_stop(struct usb_hcd *hcd) * * This will only ever be called with the main usb_hcd (the USB3 roothub). */ -static void xhci_shutdown(struct usb_hcd *hcd) +void xhci_shutdown(struct usb_hcd *hcd) { struct xhci_hcd *xhci = hcd_to_xhci(hcd); @@ -789,11 +789,8 @@ static void xhci_shutdown(struct usb_hcd *hcd) xhci_dbg_trace(xhci, trace_xhci_dbg_init, "xhci_shutdown completed - status = %x", readl(&xhci->op_regs->status)); - - /* Yet another workaround for spurious wakeups at shutdown with HSW */ - if (xhci->quirks & XHCI_SPURIOUS_WAKEUP) - pci_set_power_state(to_pci_dev(hcd->self.sysdev), PCI_D3hot); } +EXPORT_SYMBOL_GPL(xhci_shutdown); #ifdef CONFIG_PM static void xhci_save_registers(struct xhci_hcd *xhci) diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index dc6f62a4b1979..13d8838cd552b 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -2050,6 +2050,7 @@ int xhci_start(struct xhci_hcd *xhci); int xhci_reset(struct xhci_hcd *xhci); int xhci_run(struct usb_hcd *hcd); int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks); +void xhci_shutdown(struct usb_hcd *hcd); void xhci_init_driver(struct hc_driver *drv, const struct xhci_driver_overrides *over); int xhci_disable_slot(struct xhci_hcd *xhci, u32 slot_id); -- GitLab From 7c67cf6658cec70d8a43229f2ce74ca1443dc95e Mon Sep 17 00:00:00 2001 From: Kai-Heng Feng Date: Wed, 11 Dec 2019 16:20:05 +0200 Subject: [PATCH 0472/1437] xhci: Increase STS_HALT timeout in xhci_suspend() I've recently observed failed xHCI suspend attempt on AMD Raven Ridge system: kernel: xhci_hcd 0000:04:00.4: WARN: xHC CMD_RUN timeout kernel: PM: suspend_common(): xhci_pci_suspend+0x0/0xd0 returns -110 kernel: PM: pci_pm_suspend(): hcd_pci_suspend+0x0/0x30 returns -110 kernel: PM: dpm_run_callback(): pci_pm_suspend+0x0/0x150 returns -110 kernel: PM: Device 0000:04:00.4 failed to suspend async: error -110 Similar to commit ac343366846a ("xhci: Increase STS_SAVE timeout in xhci_suspend()") we also need to increase the HALT timeout to make it be able to suspend again. Cc: # 5.2+ Fixes: f7fac17ca925 ("xhci: Convert xhci_handshake() to use readl_poll_timeout_atomic()") Signed-off-by: Kai-Heng Feng Signed-off-by: Mathias Nyman Link: https://lore.kernel.org/r/20191211142007.8847-5-mathias.nyman@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index c5ee562c4c74b..dbac0fa9748d5 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -970,7 +970,7 @@ static bool xhci_pending_portevent(struct xhci_hcd *xhci) int xhci_suspend(struct xhci_hcd *xhci, bool do_wakeup) { int rc = 0; - unsigned int delay = XHCI_MAX_HALT_USEC; + unsigned int delay = XHCI_MAX_HALT_USEC * 2; struct usb_hcd *hcd = xhci_to_hcd(xhci); u32 command; u32 res; -- GitLab From 7ff11162808cc2ec66353fc012c58bb449c892c3 Mon Sep 17 00:00:00 2001 From: Mathias Nyman Date: Wed, 11 Dec 2019 16:20:06 +0200 Subject: [PATCH 0473/1437] xhci: handle some XHCI_TRUST_TX_LENGTH quirks cases as default behaviour. xhci driver claims it needs XHCI_TRUST_TX_LENGTH quirk for both Broadcom/Cavium and a Renesas xHC controllers. The quirk was inteded for handling false "success" complete event for transfers that had data left untransferred. These transfers should complete with "short packet" events instead. In these two new cases the false "success" completion is reported after a "short packet" if the TD consists of several TRBs. xHCI specs 4.10.1.1.2 say remaining TRBs should report "short packet" as well after the first short packet in a TD, but this issue seems so common it doesn't make sense to add the quirk for all vendors. Turn these events into short packets automatically instead. This gets rid of the "The WARN Successful completion on short TX for slot 1 ep 1: needs XHCI_TRUST_TX_LENGTH quirk" warning in many cases. Cc: Reported-by: Eli Billauer Reported-by: Ard Biesheuvel Tested-by: Eli Billauer Tested-by: Ard Biesheuvel Signed-off-by: Mathias Nyman Link: https://lore.kernel.org/r/20191211142007.8847-6-mathias.nyman@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-ring.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 9ebaa8e132a9e..d23f7408c81f1 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -2381,7 +2381,8 @@ static int handle_tx_event(struct xhci_hcd *xhci, case COMP_SUCCESS: if (EVENT_TRB_LEN(le32_to_cpu(event->transfer_len)) == 0) break; - if (xhci->quirks & XHCI_TRUST_TX_LENGTH) + if (xhci->quirks & XHCI_TRUST_TX_LENGTH || + ep_ring->last_td_was_short) trb_comp_code = COMP_SHORT_PACKET; else xhci_warn_ratelimited(xhci, -- GitLab From bd82873f23c9a6ad834348f8b83f3b6a5bca2c65 Mon Sep 17 00:00:00 2001 From: Mathias Nyman Date: Wed, 11 Dec 2019 16:20:07 +0200 Subject: [PATCH 0474/1437] xhci: make sure interrupts are restored to correct state spin_unlock_irqrestore() might be called with stale flags after reading port status, possibly restoring interrupts to a incorrect state. If a usb2 port just finished resuming while the port status is read the spin lock will be temporary released and re-acquired in a separate function. The flags parameter is passed as value instead of a pointer, not updating flags properly before the final spin_unlock_irqrestore() is called. Cc: # v3.12+ Fixes: 8b3d45705e54 ("usb: Fix xHCI host issues on remote wakeup.") Signed-off-by: Mathias Nyman Link: https://lore.kernel.org/r/20191211142007.8847-7-mathias.nyman@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-hub.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c index 4b870cd6c5758..7a3a29e5e9d29 100644 --- a/drivers/usb/host/xhci-hub.c +++ b/drivers/usb/host/xhci-hub.c @@ -806,7 +806,7 @@ static void xhci_del_comp_mod_timer(struct xhci_hcd *xhci, u32 status, static int xhci_handle_usb2_port_link_resume(struct xhci_port *port, u32 *status, u32 portsc, - unsigned long flags) + unsigned long *flags) { struct xhci_bus_state *bus_state; struct xhci_hcd *xhci; @@ -860,11 +860,11 @@ static int xhci_handle_usb2_port_link_resume(struct xhci_port *port, xhci_test_and_clear_bit(xhci, port, PORT_PLC); xhci_set_link_state(xhci, port, XDEV_U0); - spin_unlock_irqrestore(&xhci->lock, flags); + spin_unlock_irqrestore(&xhci->lock, *flags); time_left = wait_for_completion_timeout( &bus_state->rexit_done[wIndex], msecs_to_jiffies(XHCI_MAX_REXIT_TIMEOUT_MS)); - spin_lock_irqsave(&xhci->lock, flags); + spin_lock_irqsave(&xhci->lock, *flags); if (time_left) { slot_id = xhci_find_slot_id_by_port(hcd, xhci, @@ -967,7 +967,7 @@ static void xhci_get_usb3_port_status(struct xhci_port *port, u32 *status, } static void xhci_get_usb2_port_status(struct xhci_port *port, u32 *status, - u32 portsc, unsigned long flags) + u32 portsc, unsigned long *flags) { struct xhci_bus_state *bus_state; u32 link_state; @@ -1017,7 +1017,7 @@ static void xhci_get_usb2_port_status(struct xhci_port *port, u32 *status, static u32 xhci_get_port_status(struct usb_hcd *hcd, struct xhci_bus_state *bus_state, u16 wIndex, u32 raw_port_status, - unsigned long flags) + unsigned long *flags) __releases(&xhci->lock) __acquires(&xhci->lock) { @@ -1140,7 +1140,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, } trace_xhci_get_port_status(wIndex, temp); status = xhci_get_port_status(hcd, bus_state, wIndex, temp, - flags); + &flags); if (status == 0xffffffff) goto error; -- GitLab From 71bbcd2eaf042ec3f10122e3f3b1be67dbfb1c16 Mon Sep 17 00:00:00 2001 From: Arnaud Pouliquen Date: Fri, 22 Nov 2019 13:54:02 +0100 Subject: [PATCH 0475/1437] dt-bindings: remoteproc: stm32: add wakeup-source property If the optional wdg interrupt is defined, then this property may be defined. Signed-off-by: Arnaud Pouliquen Signed-off-by: Rob Herring --- .../devicetree/bindings/remoteproc/st,stm32-rproc.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Documentation/devicetree/bindings/remoteproc/st,stm32-rproc.yaml b/Documentation/devicetree/bindings/remoteproc/st,stm32-rproc.yaml index acf18d170352d..c0d83865e933a 100644 --- a/Documentation/devicetree/bindings/remoteproc/st,stm32-rproc.yaml +++ b/Documentation/devicetree/bindings/remoteproc/st,stm32-rproc.yaml @@ -50,6 +50,8 @@ properties: description: Should contain the WWDG1 watchdog reset interrupt maxItems: 1 + wakeup-source: true + mboxes: description: This property is required only if the rpmsg/virtio functionality is used. -- GitLab From a2ce3217d3a448e5cf434026131633cf32ed2234 Mon Sep 17 00:00:00 2001 From: Grygorii Strashko Date: Wed, 27 Nov 2019 17:55:26 +0200 Subject: [PATCH 0476/1437] dt-bindings: net: ti: cpsw-switch: update to fix comments After original patch was merged there were additional comments/requests provided by Rob Herring [1]. Mostly they are related to json-schema usage, and this patch fixes them. Also SPDX-License-Identifier has been changed to (GPL-2.0-only OR BSD-2-Clause) as requested. [1] https://lkml.org/lkml/2019/11/21/875 Fixes: ef63fe72f698 ("dt-bindings: net: ti: add new cpsw switch driver bindings") Signed-off-by: Grygorii Strashko [robh: Remove 2 more maxItems that aren't necessary] Signed-off-by: Rob Herring --- .../bindings/net/ti,cpsw-switch.yaml | 22 ++++++------------- 1 file changed, 7 insertions(+), 15 deletions(-) diff --git a/Documentation/devicetree/bindings/net/ti,cpsw-switch.yaml b/Documentation/devicetree/bindings/net/ti,cpsw-switch.yaml index 81ae8cafabc14..ac8c76369a867 100644 --- a/Documentation/devicetree/bindings/net/ti,cpsw-switch.yaml +++ b/Documentation/devicetree/bindings/net/ti,cpsw-switch.yaml @@ -1,4 +1,4 @@ -# SPDX-License-Identifier: GPL-2.0 +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) %YAML 1.2 --- $id: http://devicetree.org/schemas/net/ti,cpsw-switch.yaml# @@ -44,7 +44,6 @@ properties: description: CPSW functional clock clock-names: - maxItems: 1 items: - const: fck @@ -70,7 +69,6 @@ properties: Phandle to the system control device node which provides access to efuse IO range with MAC addresses - ethernet-ports: type: object properties: @@ -82,8 +80,6 @@ properties: patternProperties: "^port@[0-9]+$": type: object - minItems: 1 - maxItems: 2 description: CPSW external ports allOf: @@ -91,23 +87,20 @@ properties: properties: reg: - maxItems: 1 - enum: [1, 2] + items: + - enum: [1, 2] description: CPSW port number phys: - $ref: /schemas/types.yaml#definitions/phandle-array maxItems: 1 description: phandle on phy-gmii-sel PHY label: - $ref: /schemas/types.yaml#/definitions/string-array - maxItems: 1 description: label associated with this port ti,dual-emac-pvid: - $ref: /schemas/types.yaml#/definitions/uint32 - maxItems: 1 + allOf: + - $ref: /schemas/types.yaml#/definitions/uint32 minimum: 1 maximum: 1024 description: @@ -136,7 +129,6 @@ properties: description: CPTS reference clock clock-names: - maxItems: 1 items: - const: cpts @@ -201,7 +193,7 @@ examples: phys = <&phy_gmii_sel 1>; phy-handle = <ðphy0_sw>; phy-mode = "rgmii"; - ti,dual_emac_pvid = <1>; + ti,dual-emac-pvid = <1>; }; cpsw_port2: port@2 { @@ -211,7 +203,7 @@ examples: phys = <&phy_gmii_sel 2>; phy-handle = <ðphy1_sw>; phy-mode = "rgmii"; - ti,dual_emac_pvid = <2>; + ti,dual-emac-pvid = <2>; }; }; -- GitLab From 05267c7eac12627fae3f25dfd203bfdb9941f9ca Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Wed, 11 Dec 2019 10:09:24 -0300 Subject: [PATCH 0477/1437] perf arch: Make the default get_cpuid() return compatible error Some of the functions calling get_cpuid() propagate back the error it returns, and all are using errno (positive) values, make the weak default get_cpuid() function return ENOSYS to be consistent and to allow checking if this is an arch not providing this function or if a provided one is having trouble getting the cpuid, to decide if the warning should be provided to the user or just a debug message should be emitted. Reviewed-by: Mark Rutland Tested-by: Mark Rutland Tested-by: John Garry # arm64 Acked-by: Jiri Olsa Cc: Adrian Hunter Cc: Namhyung Kim Cc: Will Deacon Link: https://lkml.kernel.org/n/tip-lxwjr0cd2eggzx04a780ffrv@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/header.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index becc2d1094237..4d39a75551a0b 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c @@ -850,7 +850,7 @@ int __weak strcmp_cpuid_str(const char *mapcpuid, const char *cpuid) */ int __weak get_cpuid(char *buffer __maybe_unused, size_t sz __maybe_unused) { - return -1; + return ENOSYS; /* Not implemented */ } static int write_cpuid(struct feat_fd *ff, -- GitLab From 61208e6e1003b3fd8d2d1f2a72ec27be43955c0b Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Wed, 11 Dec 2019 10:21:59 -0300 Subject: [PATCH 0478/1437] perf top: Do not bail out when perf_env__read_cpuid() returns ENOSYS 'perf top' stopped working on hw architectures that do not provide a get_cpuid() implementation and thus fallback to the weak get_cpuid() default function. This is done because at annotation time we may need it in the arch specific annotation init routine, but that is only being used by arches that do provide a get_cpuid() implementation: $ find tools/ -name "*.[ch]" | xargs grep 'evlist->env' tools/perf/builtin-top.c: top.evlist->env = &perf_env; tools/perf/util/evsel.c: return evsel->evlist->env; tools/perf/util/s390-cpumsf.c: sf->machine_type = s390_cpumsf_get_type(session->evlist->env->cpuid); tools/perf/util/header.c: session->evlist->env = &header->env; tools/perf/util/sample-raw.c: const char *arch_pf = perf_env__arch(evlist->env); $ $ find tools/perf/arch -name "*.[ch]" | xargs grep -w get_cpuid tools/perf/arch/x86/util/auxtrace.c: ret = get_cpuid(buffer, sizeof(buffer)); tools/perf/arch/x86/util/header.c:get_cpuid(char *buffer, size_t sz) tools/perf/arch/powerpc/util/header.c:get_cpuid(char *buffer, size_t sz) tools/perf/arch/s390/util/header.c: * Implementation of get_cpuid(). tools/perf/arch/s390/util/header.c:int get_cpuid(char *buffer, size_t sz) tools/perf/arch/s390/util/header.c: if (buf && get_cpuid(buf, 128)) $ For 'report' or 'script', i.e. tools working on perf.data files, that is setup while reading the header, its just top that needs to explicitely read it at tool start. Fixes: 608127f73779 ("perf top: Initialize perf_env->cpuid, needed by the per arch annotation init routine") Reported-by: John Garry Analysed-by: Jiri Olsa Reviewed-by: Mark Rutland Tested-by: Mark Rutland Tested-by: John Garry # arm64 Acked-by: Jiri Olsa Cc: Adrian Hunter Cc: Namhyung Kim Cc: Will Deacon Link: https://lkml.kernel.org/n/tip-lxwjr0cd2eggzx04a780ffrv@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-top.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index dc80044bc46f4..795e353de0950 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c @@ -1568,9 +1568,13 @@ int cmd_top(int argc, const char **argv) */ status = perf_env__read_cpuid(&perf_env); if (status) { - pr_err("Couldn't read the cpuid for this machine: %s\n", - str_error_r(errno, errbuf, sizeof(errbuf))); - goto out_delete_evlist; + /* + * Some arches do not provide a get_cpuid(), so just use pr_debug, otherwise + * warn the user explicitely. + */ + eprintf(status == ENOSYS ? 1 : 0, verbose, + "Couldn't read the cpuid for this machine: %s\n", + str_error_r(errno, errbuf, sizeof(errbuf))); } top.evlist->env = &perf_env; -- GitLab From 0dd674efaf63bc6bfd89909814db618abe1e7039 Mon Sep 17 00:00:00 2001 From: Ravi Bangoria Date: Wed, 4 Dec 2019 21:51:21 +0530 Subject: [PATCH 0479/1437] perf/x86/pmu-events: Fix Kernel_Utilization metric Kernel Utilization should divide ref cycles spent in kernel with total ref cycles. Signed-off-by: Ravi Bangoria Reviewed-by: Andi Kleen Cc: Alexander Shishkin Cc: Haiyan Song Cc: Jiri Olsa Cc: Kan Liang Cc: Namhyung Kim Link: http://lore.kernel.org/lkml/20191204162121.29998-1-ravi.bangoria@linux.ibm.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/pmu-events/arch/x86/broadwell/bdw-metrics.json | 2 +- tools/perf/pmu-events/arch/x86/broadwellde/bdwde-metrics.json | 2 +- tools/perf/pmu-events/arch/x86/broadwellx/bdx-metrics.json | 2 +- tools/perf/pmu-events/arch/x86/cascadelakex/clx-metrics.json | 2 +- tools/perf/pmu-events/arch/x86/haswell/hsw-metrics.json | 2 +- tools/perf/pmu-events/arch/x86/haswellx/hsx-metrics.json | 2 +- tools/perf/pmu-events/arch/x86/ivybridge/ivb-metrics.json | 2 +- tools/perf/pmu-events/arch/x86/ivytown/ivt-metrics.json | 2 +- tools/perf/pmu-events/arch/x86/jaketown/jkt-metrics.json | 2 +- tools/perf/pmu-events/arch/x86/sandybridge/snb-metrics.json | 2 +- tools/perf/pmu-events/arch/x86/skylake/skl-metrics.json | 2 +- tools/perf/pmu-events/arch/x86/skylakex/skx-metrics.json | 2 +- 12 files changed, 12 insertions(+), 12 deletions(-) diff --git a/tools/perf/pmu-events/arch/x86/broadwell/bdw-metrics.json b/tools/perf/pmu-events/arch/x86/broadwell/bdw-metrics.json index bc7151d639d7e..45a34ce4fe89f 100644 --- a/tools/perf/pmu-events/arch/x86/broadwell/bdw-metrics.json +++ b/tools/perf/pmu-events/arch/x86/broadwell/bdw-metrics.json @@ -297,7 +297,7 @@ }, { "BriefDescription": "Fraction of cycles spent in Kernel mode", - "MetricExpr": "CPU_CLK_UNHALTED.REF_TSC:u / CPU_CLK_UNHALTED.REF_TSC", + "MetricExpr": "CPU_CLK_UNHALTED.REF_TSC:k / CPU_CLK_UNHALTED.REF_TSC", "MetricGroup": "Summary", "MetricName": "Kernel_Utilization" }, diff --git a/tools/perf/pmu-events/arch/x86/broadwellde/bdwde-metrics.json b/tools/perf/pmu-events/arch/x86/broadwellde/bdwde-metrics.json index 49c5f123d8115..961fe4395758e 100644 --- a/tools/perf/pmu-events/arch/x86/broadwellde/bdwde-metrics.json +++ b/tools/perf/pmu-events/arch/x86/broadwellde/bdwde-metrics.json @@ -115,7 +115,7 @@ }, { "BriefDescription": "Fraction of cycles spent in Kernel mode", - "MetricExpr": "CPU_CLK_UNHALTED.REF_TSC:u / CPU_CLK_UNHALTED.REF_TSC", + "MetricExpr": "CPU_CLK_UNHALTED.REF_TSC:k / CPU_CLK_UNHALTED.REF_TSC", "MetricGroup": "Summary", "MetricName": "Kernel_Utilization" }, diff --git a/tools/perf/pmu-events/arch/x86/broadwellx/bdx-metrics.json b/tools/perf/pmu-events/arch/x86/broadwellx/bdx-metrics.json index 113d19e926784..746734ce09be7 100644 --- a/tools/perf/pmu-events/arch/x86/broadwellx/bdx-metrics.json +++ b/tools/perf/pmu-events/arch/x86/broadwellx/bdx-metrics.json @@ -297,7 +297,7 @@ }, { "BriefDescription": "Fraction of cycles spent in Kernel mode", - "MetricExpr": "CPU_CLK_UNHALTED.REF_TSC:u / CPU_CLK_UNHALTED.REF_TSC", + "MetricExpr": "CPU_CLK_UNHALTED.REF_TSC:k / CPU_CLK_UNHALTED.REF_TSC", "MetricGroup": "Summary", "MetricName": "Kernel_Utilization" }, diff --git a/tools/perf/pmu-events/arch/x86/cascadelakex/clx-metrics.json b/tools/perf/pmu-events/arch/x86/cascadelakex/clx-metrics.json index 2ba32af9bc366..f94653229dd46 100644 --- a/tools/perf/pmu-events/arch/x86/cascadelakex/clx-metrics.json +++ b/tools/perf/pmu-events/arch/x86/cascadelakex/clx-metrics.json @@ -315,7 +315,7 @@ }, { "BriefDescription": "Fraction of cycles spent in Kernel mode", - "MetricExpr": "CPU_CLK_UNHALTED.REF_TSC:u / CPU_CLK_UNHALTED.REF_TSC", + "MetricExpr": "CPU_CLK_UNHALTED.REF_TSC:k / CPU_CLK_UNHALTED.REF_TSC", "MetricGroup": "Summary", "MetricName": "Kernel_Utilization" }, diff --git a/tools/perf/pmu-events/arch/x86/haswell/hsw-metrics.json b/tools/perf/pmu-events/arch/x86/haswell/hsw-metrics.json index c80f16fde6d0e..5402cd3120f9d 100644 --- a/tools/perf/pmu-events/arch/x86/haswell/hsw-metrics.json +++ b/tools/perf/pmu-events/arch/x86/haswell/hsw-metrics.json @@ -267,7 +267,7 @@ }, { "BriefDescription": "Fraction of cycles spent in Kernel mode", - "MetricExpr": "CPU_CLK_UNHALTED.REF_TSC:u / CPU_CLK_UNHALTED.REF_TSC", + "MetricExpr": "CPU_CLK_UNHALTED.REF_TSC:k / CPU_CLK_UNHALTED.REF_TSC", "MetricGroup": "Summary", "MetricName": "Kernel_Utilization" }, diff --git a/tools/perf/pmu-events/arch/x86/haswellx/hsx-metrics.json b/tools/perf/pmu-events/arch/x86/haswellx/hsx-metrics.json index e501729c3dd11..832f3cb40b34f 100644 --- a/tools/perf/pmu-events/arch/x86/haswellx/hsx-metrics.json +++ b/tools/perf/pmu-events/arch/x86/haswellx/hsx-metrics.json @@ -267,7 +267,7 @@ }, { "BriefDescription": "Fraction of cycles spent in Kernel mode", - "MetricExpr": "CPU_CLK_UNHALTED.REF_TSC:u / CPU_CLK_UNHALTED.REF_TSC", + "MetricExpr": "CPU_CLK_UNHALTED.REF_TSC:k / CPU_CLK_UNHALTED.REF_TSC", "MetricGroup": "Summary", "MetricName": "Kernel_Utilization" }, diff --git a/tools/perf/pmu-events/arch/x86/ivybridge/ivb-metrics.json b/tools/perf/pmu-events/arch/x86/ivybridge/ivb-metrics.json index e2446966b6513..d69b2a8fc0bc7 100644 --- a/tools/perf/pmu-events/arch/x86/ivybridge/ivb-metrics.json +++ b/tools/perf/pmu-events/arch/x86/ivybridge/ivb-metrics.json @@ -285,7 +285,7 @@ }, { "BriefDescription": "Fraction of cycles spent in Kernel mode", - "MetricExpr": "CPU_CLK_UNHALTED.REF_TSC:u / CPU_CLK_UNHALTED.REF_TSC", + "MetricExpr": "CPU_CLK_UNHALTED.REF_TSC:k / CPU_CLK_UNHALTED.REF_TSC", "MetricGroup": "Summary", "MetricName": "Kernel_Utilization" }, diff --git a/tools/perf/pmu-events/arch/x86/ivytown/ivt-metrics.json b/tools/perf/pmu-events/arch/x86/ivytown/ivt-metrics.json index 9294769dec64e..5f465fd81315d 100644 --- a/tools/perf/pmu-events/arch/x86/ivytown/ivt-metrics.json +++ b/tools/perf/pmu-events/arch/x86/ivytown/ivt-metrics.json @@ -285,7 +285,7 @@ }, { "BriefDescription": "Fraction of cycles spent in Kernel mode", - "MetricExpr": "CPU_CLK_UNHALTED.REF_TSC:u / CPU_CLK_UNHALTED.REF_TSC", + "MetricExpr": "CPU_CLK_UNHALTED.REF_TSC:k / CPU_CLK_UNHALTED.REF_TSC", "MetricGroup": "Summary", "MetricName": "Kernel_Utilization" }, diff --git a/tools/perf/pmu-events/arch/x86/jaketown/jkt-metrics.json b/tools/perf/pmu-events/arch/x86/jaketown/jkt-metrics.json index 603ff9c2e9a17..3e909b3060035 100644 --- a/tools/perf/pmu-events/arch/x86/jaketown/jkt-metrics.json +++ b/tools/perf/pmu-events/arch/x86/jaketown/jkt-metrics.json @@ -171,7 +171,7 @@ }, { "BriefDescription": "Fraction of cycles spent in Kernel mode", - "MetricExpr": "CPU_CLK_UNHALTED.REF_TSC:u / CPU_CLK_UNHALTED.REF_TSC", + "MetricExpr": "CPU_CLK_UNHALTED.REF_TSC:k / CPU_CLK_UNHALTED.REF_TSC", "MetricGroup": "Summary", "MetricName": "Kernel_Utilization" }, diff --git a/tools/perf/pmu-events/arch/x86/sandybridge/snb-metrics.json b/tools/perf/pmu-events/arch/x86/sandybridge/snb-metrics.json index c6b485b3a2cba..50c0532357520 100644 --- a/tools/perf/pmu-events/arch/x86/sandybridge/snb-metrics.json +++ b/tools/perf/pmu-events/arch/x86/sandybridge/snb-metrics.json @@ -171,7 +171,7 @@ }, { "BriefDescription": "Fraction of cycles spent in Kernel mode", - "MetricExpr": "CPU_CLK_UNHALTED.REF_TSC:u / CPU_CLK_UNHALTED.REF_TSC", + "MetricExpr": "CPU_CLK_UNHALTED.REF_TSC:k / CPU_CLK_UNHALTED.REF_TSC", "MetricGroup": "Summary", "MetricName": "Kernel_Utilization" }, diff --git a/tools/perf/pmu-events/arch/x86/skylake/skl-metrics.json b/tools/perf/pmu-events/arch/x86/skylake/skl-metrics.json index 0ca539bb60f62..e7feb60f9fa99 100644 --- a/tools/perf/pmu-events/arch/x86/skylake/skl-metrics.json +++ b/tools/perf/pmu-events/arch/x86/skylake/skl-metrics.json @@ -303,7 +303,7 @@ }, { "BriefDescription": "Fraction of cycles spent in Kernel mode", - "MetricExpr": "CPU_CLK_UNHALTED.REF_TSC:u / CPU_CLK_UNHALTED.REF_TSC", + "MetricExpr": "CPU_CLK_UNHALTED.REF_TSC:k / CPU_CLK_UNHALTED.REF_TSC", "MetricGroup": "Summary", "MetricName": "Kernel_Utilization" }, diff --git a/tools/perf/pmu-events/arch/x86/skylakex/skx-metrics.json b/tools/perf/pmu-events/arch/x86/skylakex/skx-metrics.json index 047d7e11aa6f8..21d7a0c2c2e83 100644 --- a/tools/perf/pmu-events/arch/x86/skylakex/skx-metrics.json +++ b/tools/perf/pmu-events/arch/x86/skylakex/skx-metrics.json @@ -315,7 +315,7 @@ }, { "BriefDescription": "Fraction of cycles spent in Kernel mode", - "MetricExpr": "CPU_CLK_UNHALTED.REF_TSC:u / CPU_CLK_UNHALTED.REF_TSC", + "MetricExpr": "CPU_CLK_UNHALTED.REF_TSC:k / CPU_CLK_UNHALTED.REF_TSC", "MetricGroup": "Summary", "MetricName": "Kernel_Utilization" }, -- GitLab From eb573e746b9d4f0921dcb2449be3df41dae3caea Mon Sep 17 00:00:00 2001 From: Kajol Jain Date: Wed, 20 Nov 2019 14:10:59 +0530 Subject: [PATCH 0480/1437] perf metricgroup: Fix printing event names of metric group with multiple events Commit f01642e4912b ("perf metricgroup: Support multiple events for metricgroup") introduced support for multiple events in a metric group. But with the current upstream, metric events names are not printed properly In power9 platform: command:# ./perf stat --metric-only -M translation -C 0 -I 1000 sleep 2 1.000208486 2.000368863 2.001400558 Similarly in skylake platform: command:./perf stat --metric-only -M Power -I 1000 1.000579994 2.002189493 With current upstream version, issue is with event name comparison logic in find_evsel_group(). Current logic is to compare events belonging to a metric group to the events in perf_evlist. Since the break statement is missing in the loop used for comparison between metric group and perf_evlist events, the loop continues to execute even after getting a pattern match, and end up in discarding the matches. Incase of single metric event belongs to metric group, its working fine, because in case of single event once it compare all events it reaches to end of perf_evlist. Example for single metric event in power9 platform: command:# ./perf stat --metric-only -M branches_per_inst -I 1000 sleep 1 1.000094653 0.2 1.001337059 0.0 This patch fixes the issue by making sure once we found all events belongs to that metric event matched in find_evsel_group(), we successfully break from that loop by adding corresponding condition. With this patch: In power9 platform: command:# ./perf stat --metric-only -M translation -C 0 -I 1000 sleep 2 result:# time derat_4k_miss_rate_percent derat_4k_miss_ratio derat_miss_ratio derat_64k_miss_rate_percent derat_64k_miss_ratio dslb_miss_rate_percent islb_miss_rate_percent 1.000135672 0.0 0.3 1.0 0.0 0.2 0.0 0.0 2.000380617 0.0 0.0 0.0 0.0 0.0 0.0 0.0 command:# ./perf stat --metric-only -M Power -I 1000 Similarly in skylake platform: result:# time Turbo_Utilization C3_Core_Residency C6_Core_Residency C7_Core_Residency C2_Pkg_Residency C3_Pkg_Residency C6_Pkg_Residency C7_Pkg_Residency 1.000563580 0.3 0.0 2.6 44.2 21.9 0.0 0.0 0.0 2.002235027 0.4 0.0 2.7 43.0 20.7 0.0 0.0 0.0 Committer testing: Before: [root@seventh ~]# perf stat --metric-only -M Power -I 1000 # time 1.000383223 2.001168182 3.001968545 4.002741200 5.003442022 ^C 5.777687244 [root@seventh ~]# After the patch: [root@seventh ~]# perf stat --metric-only -M Power -I 1000 # time Turbo_Utilization C3_Core_Residency C6_Core_Residency C7_Core_Residency C2_Pkg_Residency C3_Pkg_Residency C6_Pkg_Residency C7_Pkg_Residency 1.000406577 0.4 0.1 1.4 97.0 0.0 0.0 0.0 0.0 2.001481572 0.3 0.0 0.6 97.9 0.0 0.0 0.0 0.0 3.002332585 0.2 0.0 1.0 97.5 0.0 0.0 0.0 0.0 4.003196624 0.2 0.0 0.3 98.6 0.0 0.0 0.0 0.0 5.004063851 0.3 0.0 0.7 97.7 0.0 0.0 0.0 0.0 ^C 5.471260276 0.2 0.0 0.5 49.3 0.0 0.0 0.0 0.0 [root@seventh ~]# [root@seventh ~]# dmesg | grep -i skylake [ 0.187807] Performance Events: PEBS fmt3+, Skylake events, 32-deep LBR, full-width counters, Intel PMU driver. [root@seventh ~]# Fixes: f01642e4912b ("perf metricgroup: Support multiple events for metricgroup") Signed-off-by: Kajol Jain Reviewed-by: Ravi Bangoria Tested-by: Arnaldo Carvalho de Melo Cc: Alexander Shishkin Cc: Andi Kleen Cc: Anju T Sudhakar Cc: Jin Yao Cc: Jiri Olsa Cc: Kan Liang Cc: Madhavan Srinivasan Cc: Peter Zijlstra Link: http://lore.kernel.org/lkml/20191120084059.24458-1-kjain@linux.ibm.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/metricgroup.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/tools/perf/util/metricgroup.c b/tools/perf/util/metricgroup.c index 6a4d350d5cdba..02aee946b6c14 100644 --- a/tools/perf/util/metricgroup.c +++ b/tools/perf/util/metricgroup.c @@ -103,8 +103,11 @@ static struct evsel *find_evsel_group(struct evlist *perf_evlist, if (!strcmp(ev->name, ids[i])) { if (!metric_events[i]) metric_events[i] = ev; + i++; + if (i == idnum) + break; } else { - if (++i == idnum) { + if (i + 1 == idnum) { /* Discard the whole match and start again */ i = 0; memset(metric_events, 0, @@ -124,7 +127,7 @@ static struct evsel *find_evsel_group(struct evlist *perf_evlist, } } - if (i != idnum - 1) { + if (i != idnum) { /* Not whole match */ return NULL; } -- GitLab From 28707826877f84bce0977845ea529cbdd08e4e8d Mon Sep 17 00:00:00 2001 From: Michael Petlan Date: Sun, 8 Dec 2019 17:20:56 +0100 Subject: [PATCH 0481/1437] perf header: Fix false warning when there are no duplicate cache entries Before this patch, perf expected that there might be NPROC*4 unique cache entries at max, however, it also expected that some of them would be shared and/or of the same size, thus the final number of entries would be reduced to be lower than NPROC*4. In case the number of entries hadn't been reduced (was NPROC*4), the warning was printed. However, some systems might have unusual cache topology, such as the following two-processor KVM guest: cpu level shared_cpu_list size 0 1 0 32K 0 1 0 64K 0 2 0 512K 0 3 0 8192K 1 1 1 32K 1 1 1 64K 1 2 1 512K 1 3 1 8192K This KVM guest has 8 (NPROC*4) unique cache entries, which used to make perf printing the message, although there actually aren't "way too many cpu caches". v2: Removing unused argument. v3: Unifying the way we obtain number of cpus. v4: Removed '& UINT_MAX' construct which is redundant. Signed-off-by: Michael Petlan Acked-by: Jiri Olsa LPU-Reference: 20191208162056.20772-1-mpetlan@redhat.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/header.c | 21 ++++++--------------- 1 file changed, 6 insertions(+), 15 deletions(-) diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index 4d39a75551a0b..93ad27830e2bd 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c @@ -1089,21 +1089,18 @@ static void cpu_cache_level__fprintf(FILE *out, struct cpu_cache_level *c) fprintf(out, "L%d %-15s %8s [%s]\n", c->level, c->type, c->size, c->map); } -static int build_caches(struct cpu_cache_level caches[], u32 size, u32 *cntp) +#define MAX_CACHE_LVL 4 + +static int build_caches(struct cpu_cache_level caches[], u32 *cntp) { u32 i, cnt = 0; - long ncpus; u32 nr, cpu; u16 level; - ncpus = sysconf(_SC_NPROCESSORS_CONF); - if (ncpus < 0) - return -1; - - nr = (u32)(ncpus & UINT_MAX); + nr = cpu__max_cpu(); for (cpu = 0; cpu < nr; cpu++) { - for (level = 0; level < 10; level++) { + for (level = 0; level < MAX_CACHE_LVL; level++) { struct cpu_cache_level c; int err; @@ -1123,18 +1120,12 @@ static int build_caches(struct cpu_cache_level caches[], u32 size, u32 *cntp) caches[cnt++] = c; else cpu_cache_level__free(&c); - - if (WARN_ONCE(cnt == size, "way too many cpu caches..")) - goto out; } } - out: *cntp = cnt; return 0; } -#define MAX_CACHE_LVL 4 - static int write_cache(struct feat_fd *ff, struct evlist *evlist __maybe_unused) { @@ -1143,7 +1134,7 @@ static int write_cache(struct feat_fd *ff, u32 cnt = 0, i, version = 1; int ret; - ret = build_caches(caches, max_caches, &cnt); + ret = build_caches(caches, &cnt); if (ret) goto out; -- GitLab From bae141f54be83b06652c1d47e50e4e75ed4e9c7e Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Fri, 6 Dec 2019 22:49:34 +0100 Subject: [PATCH 0482/1437] bpf: Emit audit messages upon successful prog load and unload Allow for audit messages to be emitted upon BPF program load and unload for having a timeline of events. The load itself is in syscall context, so additional info about the process initiating the BPF prog creation can be logged and later directly correlated to the unload event. The only info really needed from BPF side is the globally unique prog ID where then audit user space tooling can query / dump all info needed about the specific BPF program right upon load event and enrich the record, thus these changes needed here can be kept small and non-intrusive to the core. Raw example output: # auditctl -D # auditctl -a always,exit -F arch=x86_64 -S bpf # ausearch --start recent -m 1334 ... ---- time->Wed Nov 27 16:04:13 2019 type=PROCTITLE msg=audit(1574867053.120:84664): proctitle="./bpf" type=SYSCALL msg=audit(1574867053.120:84664): arch=c000003e syscall=321 \ success=yes exit=3 a0=5 a1=7ffea484fbe0 a2=70 a3=0 items=0 ppid=7477 \ pid=12698 auid=1001 uid=1001 gid=1001 euid=1001 suid=1001 fsuid=1001 \ egid=1001 sgid=1001 fsgid=1001 tty=pts2 ses=4 comm="bpf" \ exe="/home/jolsa/auditd/audit-testsuite/tests/bpf/bpf" \ subj=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 key=(null) type=UNKNOWN[1334] msg=audit(1574867053.120:84664): prog-id=76 op=LOAD ---- time->Wed Nov 27 16:04:13 2019 type=UNKNOWN[1334] msg=audit(1574867053.120:84665): prog-id=76 op=UNLOAD ... Signed-off-by: Daniel Borkmann Co-developed-by: Jiri Olsa Signed-off-by: Jiri Olsa Acked-by: Paul Moore Link: https://lore.kernel.org/bpf/20191206214934.11319-1-jolsa@kernel.org --- include/uapi/linux/audit.h | 1 + kernel/bpf/syscall.c | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/include/uapi/linux/audit.h b/include/uapi/linux/audit.h index 3ad935527177b..a534d71e689ac 100644 --- a/include/uapi/linux/audit.h +++ b/include/uapi/linux/audit.h @@ -116,6 +116,7 @@ #define AUDIT_FANOTIFY 1331 /* Fanotify access decision */ #define AUDIT_TIME_INJOFFSET 1332 /* Timekeeping offset injected */ #define AUDIT_TIME_ADJNTPVAL 1333 /* NTP value adjustment */ +#define AUDIT_BPF 1334 /* BPF subsystem */ #define AUDIT_AVC 1400 /* SE Linux avc denial or grant */ #define AUDIT_SELINUX_ERR 1401 /* Internal SE Linux Errors */ diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c index e3461ec595706..66b90eaf99fe8 100644 --- a/kernel/bpf/syscall.c +++ b/kernel/bpf/syscall.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #define IS_FD_ARRAY(map) ((map)->map_type == BPF_MAP_TYPE_PERF_EVENT_ARRAY || \ @@ -1306,6 +1307,36 @@ static int find_prog_type(enum bpf_prog_type type, struct bpf_prog *prog) return 0; } +enum bpf_audit { + BPF_AUDIT_LOAD, + BPF_AUDIT_UNLOAD, + BPF_AUDIT_MAX, +}; + +static const char * const bpf_audit_str[BPF_AUDIT_MAX] = { + [BPF_AUDIT_LOAD] = "LOAD", + [BPF_AUDIT_UNLOAD] = "UNLOAD", +}; + +static void bpf_audit_prog(const struct bpf_prog *prog, unsigned int op) +{ + struct audit_context *ctx = NULL; + struct audit_buffer *ab; + + if (WARN_ON_ONCE(op >= BPF_AUDIT_MAX)) + return; + if (audit_enabled == AUDIT_OFF) + return; + if (op == BPF_AUDIT_LOAD) + ctx = audit_context(); + ab = audit_log_start(ctx, GFP_ATOMIC, AUDIT_BPF); + if (unlikely(!ab)) + return; + audit_log_format(ab, "prog-id=%u op=%s", + prog->aux->id, bpf_audit_str[op]); + audit_log_end(ab); +} + int __bpf_prog_charge(struct user_struct *user, u32 pages) { unsigned long memlock_limit = rlimit(RLIMIT_MEMLOCK) >> PAGE_SHIFT; @@ -1421,6 +1452,7 @@ static void __bpf_prog_put(struct bpf_prog *prog, bool do_idr_lock) { if (atomic64_dec_and_test(&prog->aux->refcnt)) { perf_event_bpf_event(prog, PERF_BPF_EVENT_PROG_UNLOAD, 0); + bpf_audit_prog(prog, BPF_AUDIT_UNLOAD); /* bpf_prog_free_id() must be called first */ bpf_prog_free_id(prog, do_idr_lock); __bpf_prog_put_noref(prog, true); @@ -1830,6 +1862,7 @@ static int bpf_prog_load(union bpf_attr *attr, union bpf_attr __user *uattr) */ bpf_prog_kallsyms_add(prog); perf_event_bpf_event(prog, PERF_BPF_EVENT_PROG_LOAD, 0); + bpf_audit_prog(prog, BPF_AUDIT_LOAD); err = bpf_prog_new_fd(prog); if (err < 0) -- GitLab From 100a21100bbb2bbc82fc4273e152c96e5c6c5d12 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Tue, 10 Dec 2019 11:07:25 +0100 Subject: [PATCH 0483/1437] regulator: max77650: add of_match table We need the of_match table if we want to use the compatible string in the pmic's child node and get the regulator driver loaded automatically. Signed-off-by: Bartosz Golaszewski Link: https://lore.kernel.org/r/20191210100725.11005-1-brgl@bgdev.pl Signed-off-by: Mark Brown --- drivers/regulator/max77650-regulator.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/regulator/max77650-regulator.c b/drivers/regulator/max77650-regulator.c index e57fc9197d620..ac89a412f665e 100644 --- a/drivers/regulator/max77650-regulator.c +++ b/drivers/regulator/max77650-regulator.c @@ -386,9 +386,16 @@ static int max77650_regulator_probe(struct platform_device *pdev) return 0; } +static const struct of_device_id max77650_regulator_of_match[] = { + { .compatible = "maxim,max77650-regulator" }, + { } +}; +MODULE_DEVICE_TABLE(of, max77650_regulator_of_match); + static struct platform_driver max77650_regulator_driver = { .driver = { .name = "max77650-regulator", + .of_match_table = max77650_regulator_of_match, }, .probe = max77650_regulator_probe, }; -- GitLab From c52c91bb9aa6bd8c38dbf9776158e33038aedd43 Mon Sep 17 00:00:00 2001 From: Vignesh Raghavendra Date: Wed, 11 Dec 2019 21:22:16 +0530 Subject: [PATCH 0484/1437] spi: spi-ti-qspi: Fix a bug when accessing non default CS When switching ChipSelect from default CS0 to any other CS, driver fails to update the bits in system control module register that control which CS is mapped for MMIO access. This causes reads to fail when driver tries to access QSPI flash on CS1/2/3. Fix this by updating appropriate bits whenever active CS changes. Reported-by: Andreas Dannenberg Signed-off-by: Vignesh Raghavendra Link: https://lore.kernel.org/r/20191211155216.30212-1-vigneshr@ti.com Signed-off-by: Mark Brown --- drivers/spi/spi-ti-qspi.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/spi/spi-ti-qspi.c b/drivers/spi/spi-ti-qspi.c index 3cb65371ae3bd..66dcb61285392 100644 --- a/drivers/spi/spi-ti-qspi.c +++ b/drivers/spi/spi-ti-qspi.c @@ -62,6 +62,7 @@ struct ti_qspi { u32 dc; bool mmap_enabled; + int current_cs; }; #define QSPI_PID (0x0) @@ -487,6 +488,7 @@ static void ti_qspi_enable_memory_map(struct spi_device *spi) MEM_CS_EN(spi->chip_select)); } qspi->mmap_enabled = true; + qspi->current_cs = spi->chip_select; } static void ti_qspi_disable_memory_map(struct spi_device *spi) @@ -498,6 +500,7 @@ static void ti_qspi_disable_memory_map(struct spi_device *spi) regmap_update_bits(qspi->ctrl_base, qspi->ctrl_reg, MEM_CS_MASK, 0); qspi->mmap_enabled = false; + qspi->current_cs = -1; } static void ti_qspi_setup_mmap_read(struct spi_device *spi, u8 opcode, @@ -543,7 +546,7 @@ static int ti_qspi_exec_mem_op(struct spi_mem *mem, mutex_lock(&qspi->list_lock); - if (!qspi->mmap_enabled) + if (!qspi->mmap_enabled || qspi->current_cs != mem->spi->chip_select) ti_qspi_enable_memory_map(mem->spi); ti_qspi_setup_mmap_read(mem->spi, op->cmd.opcode, op->data.buswidth, op->addr.nbytes, op->dummy.nbytes); @@ -799,6 +802,7 @@ no_dma: } } qspi->mmap_enabled = false; + qspi->current_cs = -1; ret = devm_spi_register_master(&pdev->dev, master); if (!ret) -- GitLab From c6b16761c6908d3dc167a0a566578b4b0b972905 Mon Sep 17 00:00:00 2001 From: Tomi Valkeinen Date: Thu, 14 Nov 2019 11:39:48 +0200 Subject: [PATCH 0485/1437] ARM: dts: am437x-gp/epos-evm: fix panel compatible The LCD panel on AM4 GP EVMs and ePOS boards seems to be osd070t1718-19ts. The current dts files say osd057T0559-34ts. Possibly the panel has changed since the early EVMs, or there has been a mistake with the panel type. Update the DT files accordingly. Acked-by: Laurent Pinchart Signed-off-by: Tomi Valkeinen Signed-off-by: Tony Lindgren --- arch/arm/boot/dts/am437x-gp-evm.dts | 2 +- arch/arm/boot/dts/am43x-epos-evm.dts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/arm/boot/dts/am437x-gp-evm.dts b/arch/arm/boot/dts/am437x-gp-evm.dts index cae4500194fec..811c8cae315b5 100644 --- a/arch/arm/boot/dts/am437x-gp-evm.dts +++ b/arch/arm/boot/dts/am437x-gp-evm.dts @@ -86,7 +86,7 @@ }; lcd0: display { - compatible = "osddisplays,osd057T0559-34ts", "panel-dpi"; + compatible = "osddisplays,osd070t1718-19ts", "panel-dpi"; label = "lcd"; backlight = <&lcd_bl>; diff --git a/arch/arm/boot/dts/am43x-epos-evm.dts b/arch/arm/boot/dts/am43x-epos-evm.dts index 95314121d1115..078cb473fa7dc 100644 --- a/arch/arm/boot/dts/am43x-epos-evm.dts +++ b/arch/arm/boot/dts/am43x-epos-evm.dts @@ -42,7 +42,7 @@ }; lcd0: display { - compatible = "osddisplays,osd057T0559-34ts", "panel-dpi"; + compatible = "osddisplays,osd070t1718-19ts", "panel-dpi"; label = "lcd"; backlight = <&lcd_bl>; -- GitLab From 8ecad8c0d1f87d3912c5ce9b65c56c32a38ac527 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 10 Dec 2019 21:34:00 +0100 Subject: [PATCH 0486/1437] ARM: mmp: include the correct cputype.h The file was moved, causing a build error: In file included from /git/arm-soc/arch/arm/mach-mmp/pxa168.c:28: arch/arm/mach-mmp/pxa168.h:22:10: fatal error: cputype.h: No such file or directory Include it from the new location. Link: https://lore.kernel.org/r/20191210203409.2875880-1-arnd@arndb.de Fixes: 32adcaa010fa ("ARM: mmp: move cputype.h to include/linux/soc/") Signed-off-by: Arnd Bergmann Signed-off-by: Olof Johansson --- arch/arm/mach-mmp/pxa168.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/mach-mmp/pxa168.h b/arch/arm/mach-mmp/pxa168.h index 0331c58b07a26..dff651b9f252d 100644 --- a/arch/arm/mach-mmp/pxa168.h +++ b/arch/arm/mach-mmp/pxa168.h @@ -17,9 +17,9 @@ extern void pxa168_clear_keypad_wakeup(void); #include #include #include +#include #include "devices.h" -#include "cputype.h" extern struct pxa_device_desc pxa168_device_uart1; extern struct pxa_device_desc pxa168_device_uart2; -- GitLab From 158d58335393af3956a9c06f0816ee75ed1f1447 Mon Sep 17 00:00:00 2001 From: David Howells Date: Mon, 9 Dec 2019 15:04:45 +0000 Subject: [PATCH 0487/1437] afs: Fix mountpoint parsing Each AFS mountpoint has strings that define the target to be mounted. This is required to end in a dot that is supposed to be stripped off. The string can include suffixes of ".readonly" or ".backup" - which are supposed to come before the terminal dot. To add to the confusion, the "fs lsmount" afs utility does not show the terminal dot when displaying the string. The kernel mount source string parser, however, assumes that the terminal dot marks the suffix and that the suffix is always "" and is thus ignored. In most cases, there is no suffix and this is not a problem - but if there is a suffix, it is lost and this affects the ability to mount the correct volume. The command line mount command, on the other hand, is expected not to include a terminal dot - so the problem doesn't arise there. Fix this by making sure that the dot exists and then stripping it when passing the string to the mount configuration. Fixes: bec5eb614130 ("AFS: Implement an autocell mount capability [ver #2]") Reported-by: Jonathan Billings Signed-off-by: David Howells Reviewed-by: Marc Dionne Tested-by: Jonathan Billings --- fs/afs/mntpt.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/fs/afs/mntpt.c b/fs/afs/mntpt.c index f532d6d3bd28c..79bc5f1338edf 100644 --- a/fs/afs/mntpt.c +++ b/fs/afs/mntpt.c @@ -126,7 +126,7 @@ static int afs_mntpt_set_params(struct fs_context *fc, struct dentry *mntpt) if (src_as->cell) ctx->cell = afs_get_cell(src_as->cell); - if (size > PAGE_SIZE - 1) + if (size < 2 || size > PAGE_SIZE - 1) return -EINVAL; page = read_mapping_page(d_inode(mntpt)->i_mapping, 0, NULL); @@ -140,7 +140,9 @@ static int afs_mntpt_set_params(struct fs_context *fc, struct dentry *mntpt) } buf = kmap(page); - ret = vfs_parse_fs_string(fc, "source", buf, size); + ret = -EINVAL; + if (buf[size - 1] == '.') + ret = vfs_parse_fs_string(fc, "source", buf, size - 1); kunmap(page); put_page(page); if (ret < 0) -- GitLab From 3a019636e6b00997594f91339411fa6bbd2c6d33 Mon Sep 17 00:00:00 2001 From: Michael Ellerman Date: Fri, 6 Dec 2019 14:17:38 +1100 Subject: [PATCH 0488/1437] selftests: Fix dangling documentation references to kselftest_module.sh Commit c78fd76f2b67 ("selftests: Move kselftest_module.sh into kselftest/") moved kselftest_module.sh but missed updating a few references to the path in documentation. Fixes: c78fd76f2b67 ("selftests: Move kselftest_module.sh into kselftest/") Signed-off-by: Michael Ellerman Acked-by: Kees Cook Signed-off-by: Shuah Khan --- Documentation/dev-tools/kselftest.rst | 8 ++++---- tools/testing/selftests/kselftest/module.sh | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Documentation/dev-tools/kselftest.rst b/Documentation/dev-tools/kselftest.rst index ecdfdc9d4b032..61ae13c44f915 100644 --- a/Documentation/dev-tools/kselftest.rst +++ b/Documentation/dev-tools/kselftest.rst @@ -203,12 +203,12 @@ Test Module Kselftest tests the kernel from userspace. Sometimes things need testing from within the kernel, one method of doing this is to create a test module. We can tie the module into the kselftest framework by -using a shell script test runner. ``kselftest_module.sh`` is designed +using a shell script test runner. ``kselftest/module.sh`` is designed to facilitate this process. There is also a header file provided to assist writing kernel modules that are for use with kselftest: - ``tools/testing/kselftest/kselftest_module.h`` -- ``tools/testing/kselftest/kselftest_module.sh`` +- ``tools/testing/kselftest/kselftest/module.sh`` How to use ---------- @@ -247,7 +247,7 @@ A bare bones test module might look like this: #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - #include "../tools/testing/selftests/kselftest_module.h" + #include "../tools/testing/selftests/kselftest/module.h" KSTM_MODULE_GLOBALS(); @@ -276,7 +276,7 @@ Example test script #!/bin/bash # SPDX-License-Identifier: GPL-2.0+ - $(dirname $0)/../kselftest_module.sh "foo" test_foo + $(dirname $0)/../kselftest/module.sh "foo" test_foo Test Harness diff --git a/tools/testing/selftests/kselftest/module.sh b/tools/testing/selftests/kselftest/module.sh index 18e1c7992d30d..fb4733faff128 100755 --- a/tools/testing/selftests/kselftest/module.sh +++ b/tools/testing/selftests/kselftest/module.sh @@ -9,7 +9,7 @@ # # #!/bin/sh # SPDX-License-Identifier: GPL-2.0+ -# $(dirname $0)/../kselftest_module.sh "description" module_name +# $(dirname $0)/../kselftest/module.sh "description" module_name # # Example: tools/testing/selftests/lib/printf.sh -- GitLab From d187801d1a46519d2a322f879f7c8f85c685372e Mon Sep 17 00:00:00 2001 From: SeongJae Park Date: Mon, 2 Dec 2019 12:42:20 +0100 Subject: [PATCH 0489/1437] kselftest/runner: Print new line in print of timeout log If a timeout failure occurs, kselftest kills the test process and prints the timeout log. If the test process has killed while printing a log that ends with new line, the timeout log can be printed in middle of the test process output so that it can be seems like a comment, as below: # test_process_log not ok 3 selftests: timers: nsleep-lat # TIMEOUT This commit avoids such problem by printing one more line before the TIMEOUT failure log. Signed-off-by: SeongJae Park Acked-by: Kees Cook Signed-off-by: Shuah Khan --- tools/testing/selftests/kselftest/runner.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/testing/selftests/kselftest/runner.sh b/tools/testing/selftests/kselftest/runner.sh index 84de7bc74f2cf..a8d20cbb711cf 100644 --- a/tools/testing/selftests/kselftest/runner.sh +++ b/tools/testing/selftests/kselftest/runner.sh @@ -79,6 +79,7 @@ run_one() if [ $rc -eq $skip_rc ]; then \ echo "not ok $test_num $TEST_HDR_MSG # SKIP" elif [ $rc -eq $timeout_rc ]; then \ + echo "#" echo "not ok $test_num $TEST_HDR_MSG # TIMEOUT" else echo "not ok $test_num $TEST_HDR_MSG # exit=$rc" -- GitLab From 4eac734486fd431e0756cc5e929f140911a36a53 Mon Sep 17 00:00:00 2001 From: SeongJae Park Date: Mon, 2 Dec 2019 12:42:21 +0100 Subject: [PATCH 0490/1437] kselftest: Support old perl versions On an old perl such as v5.10.1, `kselftest/prefix.pl` gives below error message: Can't locate object method "autoflush" via package "IO::Handle" at kselftest/prefix.pl line 10. This commit fixes the error by explicitly specifying the use of the `IO::Handle` package. Signed-off-by: SeongJae Park Acked-by: Kees Cook Signed-off-by: Shuah Khan --- tools/testing/selftests/kselftest/prefix.pl | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/testing/selftests/kselftest/prefix.pl b/tools/testing/selftests/kselftest/prefix.pl index ec7e481181835..31f7c2a0a8bd4 100755 --- a/tools/testing/selftests/kselftest/prefix.pl +++ b/tools/testing/selftests/kselftest/prefix.pl @@ -3,6 +3,7 @@ # Prefix all lines with "# ", unbuffered. Command being piped in may need # to have unbuffering forced with "stdbuf -i0 -o0 -e0 $cmd". use strict; +use IO::Handle; binmode STDIN; binmode STDOUT; -- GitLab From 1da4bd9f9d187f53618890d7b66b9628bbec3c70 Mon Sep 17 00:00:00 2001 From: David Howells Date: Wed, 11 Dec 2019 08:56:04 +0000 Subject: [PATCH 0491/1437] afs: Fix creation calls in the dynamic root to fail with EOPNOTSUPP Fix the lookup method on the dynamic root directory such that creation calls, such as mkdir, open(O_CREAT), symlink, etc. fail with EOPNOTSUPP rather than failing with some odd error (such as EEXIST). lookup() itself tries to create automount directories when it is invoked. These are cached locally in RAM and not committed to storage. Signed-off-by: David Howells Reviewed-by: Marc Dionne Tested-by: Jonathan Billings --- fs/afs/dynroot.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/fs/afs/dynroot.c b/fs/afs/dynroot.c index 4150280509fff..7503899c0a1b5 100644 --- a/fs/afs/dynroot.c +++ b/fs/afs/dynroot.c @@ -136,6 +136,9 @@ static struct dentry *afs_dynroot_lookup(struct inode *dir, struct dentry *dentr ASSERTCMP(d_inode(dentry), ==, NULL); + if (flags & LOOKUP_CREATE) + return ERR_PTR(-EOPNOTSUPP); + if (dentry->d_name.len >= AFSNAMEMAX) { _leave(" = -ENAMETOOLONG"); return ERR_PTR(-ENAMETOOLONG); -- GitLab From 106bc79843c3c6f4f00753d1f46e54e815f99377 Mon Sep 17 00:00:00 2001 From: David Howells Date: Wed, 11 Dec 2019 08:06:08 +0000 Subject: [PATCH 0492/1437] afs: Fix missing cell comparison in afs_test_super() Fix missing cell comparison in afs_test_super(). Without this, any pair volumes that have the same volume ID will share a superblock, no matter the cell, unless they're in different network namespaces. Normally, most users will only deal with a single cell and so they won't see this. Even if they do look into a second cell, they won't see a problem unless they happen to hit a volume with the same ID as one they've already got mounted. Before the patch: # ls /afs/grand.central.org/archive linuxdev/ mailman/ moin/ mysql/ pipermail/ stage/ twiki/ # ls /afs/kth.se/ linuxdev/ mailman/ moin/ mysql/ pipermail/ stage/ twiki/ # cat /proc/mounts | grep afs none /afs afs rw,relatime,dyn,autocell 0 0 #grand.central.org:root.cell /afs/grand.central.org afs ro,relatime 0 0 #grand.central.org:root.archive /afs/grand.central.org/archive afs ro,relatime 0 0 #grand.central.org:root.archive /afs/kth.se afs ro,relatime 0 0 After the patch: # ls /afs/grand.central.org/archive linuxdev/ mailman/ moin/ mysql/ pipermail/ stage/ twiki/ # ls /afs/kth.se/ admin/ common/ install/ OldFiles/ service/ system/ bakrestores/ home/ misc/ pkg/ src/ wsadmin/ # cat /proc/mounts | grep afs none /afs afs rw,relatime,dyn,autocell 0 0 #grand.central.org:root.cell /afs/grand.central.org afs ro,relatime 0 0 #grand.central.org:root.archive /afs/grand.central.org/archive afs ro,relatime 0 0 #kth.se:root.cell /afs/kth.se afs ro,relatime 0 0 Fixes: ^1da177e4c3f4 ("Linux-2.6.12-rc2") Reported-by: Carsten Jacobi Signed-off-by: David Howells Reviewed-by: Marc Dionne Tested-by: Jonathan Billings cc: Todd DeSantis --- fs/afs/super.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/afs/super.c b/fs/afs/super.c index d9a6036b70b9f..7f8a9b3137bff 100644 --- a/fs/afs/super.c +++ b/fs/afs/super.c @@ -404,6 +404,7 @@ static int afs_test_super(struct super_block *sb, struct fs_context *fc) return (as->net_ns == fc->net_ns && as->volume && as->volume->vid == ctx->volume->vid && + as->cell == ctx->cell && !as->dyn_root); } -- GitLab From 50559800b76a7a2a46da303100da639536261808 Mon Sep 17 00:00:00 2001 From: David Howells Date: Wed, 11 Dec 2019 08:58:59 +0000 Subject: [PATCH 0493/1437] afs: Show volume name in /proc/net/afs//volumes Show the name of each volume in /proc/net/afs//volumes to make it easier to work out the name corresponding to a volume ID. This makes it easier to work out which mounts in /proc/mounts correspond to which volume ID. Signed-off-by: David Howells Reviewed-by: Marc Dionne --- fs/afs/proc.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/fs/afs/proc.c b/fs/afs/proc.c index fba2ec3a3a9c9..468e1713bce13 100644 --- a/fs/afs/proc.c +++ b/fs/afs/proc.c @@ -213,13 +213,14 @@ static int afs_proc_cell_volumes_show(struct seq_file *m, void *v) /* Display header on line 1 */ if (v == &cell->proc_volumes) { - seq_puts(m, "USE VID TY\n"); + seq_puts(m, "USE VID TY NAME\n"); return 0; } - seq_printf(m, "%3d %08llx %s\n", + seq_printf(m, "%3d %08llx %s %s\n", atomic_read(&vol->usage), vol->vid, - afs_vol_types[vol->type]); + afs_vol_types[vol->type], + vol->name); return 0; } -- GitLab From a7ede3d16808b8f3915c8572d783530a82b2f027 Mon Sep 17 00:00:00 2001 From: Guoqing Jiang Date: Wed, 27 Nov 2019 17:57:50 +0100 Subject: [PATCH 0494/1437] raid5: need to set STRIPE_HANDLE for batch head With commit 6ce220dd2f8ea71d6afc29b9a7524c12e39f374a ("raid5: don't set STRIPE_HANDLE to stripe which is in batch list"), we don't want to set STRIPE_HANDLE flag for sh which is already in batch list. However, the stripe which is the head of batch list should set this flag, otherwise panic could happen inside init_stripe at BUG_ON(sh->batch_head), it is reproducible with raid5 on top of nvdimm devices per Xiao oberserved. Thanks for Xiao's effort to verify the change. Fixes: 6ce220dd2f8ea ("raid5: don't set STRIPE_HANDLE to stripe which is in batch list") Reported-by: Xiao Ni Tested-by: Xiao Ni Signed-off-by: Guoqing Jiang Signed-off-by: Song Liu --- drivers/md/raid5.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c index f0fc538bfe597..d4d3b67ffbba7 100644 --- a/drivers/md/raid5.c +++ b/drivers/md/raid5.c @@ -5726,7 +5726,7 @@ static bool raid5_make_request(struct mddev *mddev, struct bio * bi) do_flush = false; } - if (!sh->batch_head) + if (!sh->batch_head || sh == sh->batch_head) set_bit(STRIPE_HANDLE, &sh->state); clear_bit(STRIPE_DELAYED, &sh->state); if ((!sh->batch_head || sh == sh->batch_head) && -- GitLab From 028288df635f5a9addd48ac4677b720192747944 Mon Sep 17 00:00:00 2001 From: Zhiqiang Liu Date: Tue, 10 Dec 2019 10:42:25 +0800 Subject: [PATCH 0495/1437] md: raid1: check rdev before reference in raid1_sync_request func In raid1_sync_request func, rdev should be checked before reference. Signed-off-by: Zhiqiang Liu Signed-off-by: Song Liu --- drivers/md/raid1.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c index a409ab6f30bc3..201fd8aec59ac 100644 --- a/drivers/md/raid1.c +++ b/drivers/md/raid1.c @@ -2782,7 +2782,7 @@ static sector_t raid1_sync_request(struct mddev *mddev, sector_t sector_nr, write_targets++; } } - if (bio->bi_end_io) { + if (rdev && bio->bi_end_io) { atomic_inc(&rdev->nr_pending); bio->bi_iter.bi_sector = sector_nr + rdev->data_offset; bio_set_dev(bio, rdev->bdev); -- GitLab From 3b7436cc9449d5ff7fa1c1fd5bc3edb6402ff5b8 Mon Sep 17 00:00:00 2001 From: Yufen Yu Date: Tue, 10 Dec 2019 15:01:29 +0800 Subject: [PATCH 0496/1437] md: make sure desc_nr less than MD_SB_DISKS For super_90_load, we need to make sure 'desc_nr' less than MD_SB_DISKS, avoiding invalid memory access of 'sb->disks'. Fixes: 228fc7d76db6 ("md: avoid invalid memory access for array sb->dev_roles") Signed-off-by: Yufen Yu Signed-off-by: Song Liu --- drivers/md/md.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/md/md.c b/drivers/md/md.c index 805b33e274967..4e7c9f398bc66 100644 --- a/drivers/md/md.c +++ b/drivers/md/md.c @@ -1159,6 +1159,7 @@ static int super_90_load(struct md_rdev *rdev, struct md_rdev *refdev, int minor /* not spare disk, or LEVEL_MULTIPATH */ if (sb->level == LEVEL_MULTIPATH || (rdev->desc_nr >= 0 && + rdev->desc_nr < MD_SB_DISKS && sb->disks[rdev->desc_nr].state & ((1< Date: Tue, 3 Dec 2019 17:52:03 +0100 Subject: [PATCH 0497/1437] s390/spinlock: remove confusing comment in arch_spin_lock_wait arch_spin_lock_wait does not take steal time into consideration. Signed-off-by: Vasily Gorbik --- arch/s390/lib/spinlock.c | 1 - 1 file changed, 1 deletion(-) diff --git a/arch/s390/lib/spinlock.c b/arch/s390/lib/spinlock.c index ce1e4bbe53aaf..9b2dab5a69f99 100644 --- a/arch/s390/lib/spinlock.c +++ b/arch/s390/lib/spinlock.c @@ -242,7 +242,6 @@ static inline void arch_spin_lock_classic(arch_spinlock_t *lp) void arch_spin_lock_wait(arch_spinlock_t *lp) { - /* Use classic spinlocks + niai if the steal time is >= 10% */ if (test_cpu_flag(CIF_DEDICATED_CPU)) arch_spin_lock_queued(lp); else -- GitLab From 7e914fd17e9a750d8896e5645c84e75e52d77a4b Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Mon, 2 Dec 2019 09:02:15 +0000 Subject: [PATCH 0498/1437] s390/test_unwind: fix spelling mistake "reqister" -> "register" There is a spelling mistake in a pr_info message. Fix it. Link: https://lkml.kernel.org/r/20191202090215.28766-1-colin.king@canonical.com Signed-off-by: Colin Ian King Signed-off-by: Vasily Gorbik --- arch/s390/lib/test_unwind.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/s390/lib/test_unwind.c b/arch/s390/lib/test_unwind.c index bda7ac0ddd297..32b7a30b2485d 100644 --- a/arch/s390/lib/test_unwind.c +++ b/arch/s390/lib/test_unwind.c @@ -238,7 +238,7 @@ static int test_unwind_irq(struct unwindme *u) { preempt_disable(); if (register_external_irq(EXT_IRQ_CLK_COMP, unwindme_irq_handler)) { - pr_info("Couldn't reqister external interrupt handler"); + pr_info("Couldn't register external interrupt handler"); return -1; } u->task = current; -- GitLab From 39d4a501a9ef55c57b51e3ef07fc2aeed7f30b3b Mon Sep 17 00:00:00 2001 From: Thomas Richter Date: Thu, 28 Nov 2019 10:26:41 +0100 Subject: [PATCH 0499/1437] s390/cpum_sf: Adjust sampling interval to avoid hitting sample limits Function perf_event_ever_overflow() and perf_event_account_interrupt() are called every time samples are processed by the interrupt handler. However function perf_event_account_interrupt() has checks to avoid being flooded with interrupts (more then 1000 samples are received per task_tick). Samples are then dropped and a PERF_RECORD_THROTTLED is added to the perf data. The perf subsystem limit calculation is: maximum sample frequency := 100000 --> 1 samples per 10 us task_tick = 10ms = 10000us --> 1000 samples per task_tick The work flow is measurement_alert() uses SDBT head and each SBDT points to 511 SDB pages, each with 126 sample entries. After processing 8 SBDs and for each valid sample calling: perf_event_overflow() perf_event_account_interrupts() there is a considerable amount of samples being dropped, especially when the sample frequency is very high and near the 100000 limit. To avoid the high amount of samples being dropped near the end of a task_tick time frame, increment the sampling interval in case of dropped events. The CPU Measurement sampling facility on the s390 supports only intervals, specifiing how many CPU cycles have to be executed before a sample is generated. Increase the interval when the samples being generated hit the task_tick limit. Signed-off-by: Thomas Richter Signed-off-by: Vasily Gorbik --- arch/s390/kernel/perf_cpum_sf.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/arch/s390/kernel/perf_cpum_sf.c b/arch/s390/kernel/perf_cpum_sf.c index c07fdcd737266..3f6b02ee73ece 100644 --- a/arch/s390/kernel/perf_cpum_sf.c +++ b/arch/s390/kernel/perf_cpum_sf.c @@ -1315,6 +1315,22 @@ static void hw_perf_event_update(struct perf_event *event, int flush_all) if (sampl_overflow) OVERFLOW_REG(hwc) = DIV_ROUND_UP(OVERFLOW_REG(hwc) + sampl_overflow, 1 + num_sdb); + + /* Perf_event_overflow() and perf_event_account_interrupt() limit + * the interrupt rate to an upper limit. Roughly 1000 samples per + * task tick. + * Hitting this limit results in a large number + * of throttled REF_REPORT_THROTTLE entries and the samples + * are dropped. + * Slightly increase the interval to avoid hitting this limit. + */ + if (event_overflow) { + SAMPL_RATE(hwc) += DIV_ROUND_UP(SAMPL_RATE(hwc), 10); + debug_sprintf_event(sfdbg, 1, "%s: rate adjustment %ld\n", + __func__, + DIV_ROUND_UP(SAMPL_RATE(hwc), 10)); + } + if (sampl_overflow || event_overflow) debug_sprintf_event(sfdbg, 4, "%s: " "overflows: sample %llu event %llu" -- GitLab From 0539ad0b22877225095d8adef0c376f52cc23834 Mon Sep 17 00:00:00 2001 From: Thomas Richter Date: Fri, 29 Nov 2019 15:24:25 +0100 Subject: [PATCH 0500/1437] s390/cpum_sf: Avoid SBD overflow condition in irq handler The s390 CPU Measurement sampling facility has an overflow condition which fires when all entries in a SBD are used. The measurement alert interrupt is triggered and reads out all samples in this SDB. It then tests the successor SDB, if this SBD is not full, the interrupt handler does not read any samples at all from this SDB The design waits for the hardware to fill this SBD and then trigger another meassurement alert interrupt. This scheme works nicely until an perf_event_overflow() function call discards the sample due to a too high sampling rate. The interrupt handler has logic to read out a partially filled SDB when the perf event overflow condition in linux common code is met. This causes the CPUM sampling measurement hardware and the PMU device driver to operate on the same SBD's trailer entry. This should not happen. This can be seen here using this trace: cpumsf_pmu_add: tear:0xb5286000 hw_perf_event_update: sdbt 0xb5286000 full 1 over 0 flush_all:0 hw_perf_event_update: sdbt 0xb5286008 full 0 over 0 flush_all:0 above shows 1. interrupt hw_perf_event_update: sdbt 0xb5286008 full 1 over 0 flush_all:0 hw_perf_event_update: sdbt 0xb5286008 full 0 over 0 flush_all:0 above shows 2. interrupt ... this goes on fine until... hw_perf_event_update: sdbt 0xb5286068 full 1 over 0 flush_all:0 perf_push_sample1: overflow one or more samples read from the IRQ handler are rejected by perf_event_overflow() and the IRQ handler advances to the next SDB and modifies the trailer entry of a partially filled SDB. hw_perf_event_update: sdbt 0xb5286070 full 0 over 0 flush_all:1 timestamp: 14:32:52.519953 Next time the IRQ handler is called for this SDB the trailer entry shows an overflow count of 19 missed entries. hw_perf_event_update: sdbt 0xb5286070 full 1 over 19 flush_all:1 timestamp: 14:32:52.970058 Remove access to a follow on SDB when event overflow happened. Signed-off-by: Thomas Richter Signed-off-by: Vasily Gorbik --- arch/s390/kernel/perf_cpum_sf.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/arch/s390/kernel/perf_cpum_sf.c b/arch/s390/kernel/perf_cpum_sf.c index 3f6b02ee73ece..77d93c534284d 100644 --- a/arch/s390/kernel/perf_cpum_sf.c +++ b/arch/s390/kernel/perf_cpum_sf.c @@ -1303,12 +1303,6 @@ static void hw_perf_event_update(struct perf_event *event, int flush_all) */ if (flush_all && done) break; - - /* If an event overflow happened, discard samples by - * processing any remaining sample-data-blocks. - */ - if (event_overflow) - flush_all = 1; } /* Account sample overflows in the event hardware structure */ -- GitLab From 157309a97015d8b32d65847d8a6d493d1a958e93 Mon Sep 17 00:00:00 2001 From: Christian Borntraeger Date: Fri, 6 Dec 2019 08:27:51 +0100 Subject: [PATCH 0501/1437] s390/uv: use EOPNOTSUPP instead of ENOTSUPP ENOTSUP is just an internal kernel error and should never reach userspace. The return value of the share function is not exported to userspace, but to avoid giving bad examples let us use EOPNOTSUPP: Suggested-by: Heiko Carstens Acked-by: Janosch Frank Signed-off-by: Christian Borntraeger Signed-off-by: Vasily Gorbik --- arch/s390/include/asm/uv.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/s390/include/asm/uv.h b/arch/s390/include/asm/uv.h index ef3c00b049ab4..4093a2856929a 100644 --- a/arch/s390/include/asm/uv.h +++ b/arch/s390/include/asm/uv.h @@ -86,7 +86,7 @@ static inline int share(unsigned long addr, u16 cmd) }; if (!is_prot_virt_guest()) - return -ENOTSUPP; + return -EOPNOTSUPP; /* * Sharing is page wise, if we encounter addresses that are * not page aligned, we assume something went wrong. If -- GitLab From 1b68ac8678a8e9993deebd55014cbe803e78ca02 Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Fri, 29 Nov 2019 12:59:59 +0100 Subject: [PATCH 0502/1437] s390: remove last diag 0x44 caller diag 0x44 is a voluntary undirected yield of a virtual CPU. This has caused a lot of performance issues in the past. There is only one caller left, and that one is only executed if diag 0x9c (directed yield) is not present. Given that all hypervisors implement diag 0x9c anyway, remove the last diag 0x44 to avoid that more callers will be added. Worst case that could happen now, if diag 0x9c is not present, is that a virtual CPU would loop a bit instead of giving its time slice up. diag 0x44 statistics in debugfs are kept and will always be zero, so that user space can tell that there are no calls. Signed-off-by: Heiko Carstens Signed-off-by: Vasily Gorbik --- arch/s390/include/asm/setup.h | 2 -- arch/s390/kernel/early.c | 16 ---------------- arch/s390/kernel/smp.c | 13 +++++-------- 3 files changed, 5 insertions(+), 26 deletions(-) diff --git a/arch/s390/include/asm/setup.h b/arch/s390/include/asm/setup.h index 6dc6c4fbc8e2b..69289e99cabdc 100644 --- a/arch/s390/include/asm/setup.h +++ b/arch/s390/include/asm/setup.h @@ -27,7 +27,6 @@ #define MACHINE_FLAG_DIAG9C BIT(3) #define MACHINE_FLAG_ESOP BIT(4) #define MACHINE_FLAG_IDTE BIT(5) -#define MACHINE_FLAG_DIAG44 BIT(6) #define MACHINE_FLAG_EDAT1 BIT(7) #define MACHINE_FLAG_EDAT2 BIT(8) #define MACHINE_FLAG_TOPOLOGY BIT(10) @@ -94,7 +93,6 @@ extern unsigned long __swsusp_reset_dma; #define MACHINE_HAS_DIAG9C (S390_lowcore.machine_flags & MACHINE_FLAG_DIAG9C) #define MACHINE_HAS_ESOP (S390_lowcore.machine_flags & MACHINE_FLAG_ESOP) #define MACHINE_HAS_IDTE (S390_lowcore.machine_flags & MACHINE_FLAG_IDTE) -#define MACHINE_HAS_DIAG44 (S390_lowcore.machine_flags & MACHINE_FLAG_DIAG44) #define MACHINE_HAS_EDAT1 (S390_lowcore.machine_flags & MACHINE_FLAG_EDAT1) #define MACHINE_HAS_EDAT2 (S390_lowcore.machine_flags & MACHINE_FLAG_EDAT2) #define MACHINE_HAS_TOPOLOGY (S390_lowcore.machine_flags & MACHINE_FLAG_TOPOLOGY) diff --git a/arch/s390/kernel/early.c b/arch/s390/kernel/early.c index db32a55daaec6..cd241ee66eff4 100644 --- a/arch/s390/kernel/early.c +++ b/arch/s390/kernel/early.c @@ -204,21 +204,6 @@ static __init void detect_diag9c(void) S390_lowcore.machine_flags |= MACHINE_FLAG_DIAG9C; } -static __init void detect_diag44(void) -{ - int rc; - - diag_stat_inc(DIAG_STAT_X044); - asm volatile( - " diag 0,0,0x44\n" - "0: la %0,0\n" - "1:\n" - EX_TABLE(0b,1b) - : "=d" (rc) : "0" (-EOPNOTSUPP) : "cc"); - if (!rc) - S390_lowcore.machine_flags |= MACHINE_FLAG_DIAG44; -} - static __init void detect_machine_facilities(void) { if (test_facility(8)) { @@ -331,7 +316,6 @@ void __init startup_init(void) setup_arch_string(); setup_boot_command_line(); detect_diag9c(); - detect_diag44(); detect_machine_facilities(); save_vector_registers(); setup_topology(); diff --git a/arch/s390/kernel/smp.c b/arch/s390/kernel/smp.c index 2794cad9312e3..a08bd2522dd95 100644 --- a/arch/s390/kernel/smp.c +++ b/arch/s390/kernel/smp.c @@ -413,14 +413,11 @@ EXPORT_SYMBOL(arch_vcpu_is_preempted); void smp_yield_cpu(int cpu) { - if (MACHINE_HAS_DIAG9C) { - diag_stat_inc_norecursion(DIAG_STAT_X09C); - asm volatile("diag %0,0,0x9c" - : : "d" (pcpu_devices[cpu].address)); - } else if (MACHINE_HAS_DIAG44 && !smp_cpu_mtid) { - diag_stat_inc_norecursion(DIAG_STAT_X044); - asm volatile("diag 0,0,0x44"); - } + if (!MACHINE_HAS_DIAG9C) + return; + diag_stat_inc_norecursion(DIAG_STAT_X09C); + asm volatile("diag %0,0,0x9c" + : : "d" (pcpu_devices[cpu].address)); } /* -- GitLab From 3e39ce266a273e5751e5792aee248cb8d9630c8b Mon Sep 17 00:00:00 2001 From: Vasily Gorbik Date: Fri, 2 Aug 2019 12:42:59 +0200 Subject: [PATCH 0503/1437] s390/kasan: add KASAN_VMALLOC support Add KASAN_VMALLOC support which now enables vmalloc memory area access checks as well as enables usage of VMAP_STACK under kasan. KASAN_VMALLOC changes the way vmalloc and modules areas shadow memory is handled. With this new approach only top level page tables are pre-populated and lower levels are filled dynamically upon memory allocation. Acked-by: Ilya Leoshkevich Signed-off-by: Vasily Gorbik --- arch/s390/Kconfig | 1 + arch/s390/mm/kasan_init.c | 68 ++++++++++++++++++++++++++++++++------- 2 files changed, 57 insertions(+), 12 deletions(-) diff --git a/arch/s390/Kconfig b/arch/s390/Kconfig index d4051e88e6250..bc88841d335d2 100644 --- a/arch/s390/Kconfig +++ b/arch/s390/Kconfig @@ -124,6 +124,7 @@ config S390 select HAVE_ARCH_JUMP_LABEL select HAVE_ARCH_JUMP_LABEL_RELATIVE select HAVE_ARCH_KASAN + select HAVE_ARCH_KASAN_VMALLOC select CPU_NO_EFFICIENT_FFS if !HAVE_MARCH_Z9_109_FEATURES select HAVE_ARCH_SECCOMP_FILTER select HAVE_ARCH_SOFT_DIRTY diff --git a/arch/s390/mm/kasan_init.c b/arch/s390/mm/kasan_init.c index 460f255729402..06345616a6466 100644 --- a/arch/s390/mm/kasan_init.c +++ b/arch/s390/mm/kasan_init.c @@ -82,7 +82,8 @@ static pte_t * __init kasan_early_pte_alloc(void) enum populate_mode { POPULATE_ONE2ONE, POPULATE_MAP, - POPULATE_ZERO_SHADOW + POPULATE_ZERO_SHADOW, + POPULATE_SHALLOW }; static void __init kasan_early_vmemmap_populate(unsigned long address, unsigned long end, @@ -116,6 +117,12 @@ static void __init kasan_early_vmemmap_populate(unsigned long address, pgd_populate(&init_mm, pg_dir, p4_dir); } + if (IS_ENABLED(CONFIG_KASAN_S390_4_LEVEL_PAGING) && + mode == POPULATE_SHALLOW) { + address = (address + P4D_SIZE) & P4D_MASK; + continue; + } + p4_dir = p4d_offset(pg_dir, address); if (p4d_none(*p4_dir)) { if (mode == POPULATE_ZERO_SHADOW && @@ -130,6 +137,12 @@ static void __init kasan_early_vmemmap_populate(unsigned long address, p4d_populate(&init_mm, p4_dir, pu_dir); } + if (!IS_ENABLED(CONFIG_KASAN_S390_4_LEVEL_PAGING) && + mode == POPULATE_SHALLOW) { + address = (address + PUD_SIZE) & PUD_MASK; + continue; + } + pu_dir = pud_offset(p4_dir, address); if (pud_none(*pu_dir)) { if (mode == POPULATE_ZERO_SHADOW && @@ -195,6 +208,9 @@ static void __init kasan_early_vmemmap_populate(unsigned long address, page = kasan_early_shadow_page; pte_val(*pt_dir) = __pa(page) | pgt_prot_zero; break; + case POPULATE_SHALLOW: + /* should never happen */ + break; } } address += PAGE_SIZE; @@ -313,22 +329,50 @@ void __init kasan_early_init(void) init_mm.pgd = early_pg_dir; /* * Current memory layout: - * +- 0 -------------+ +- shadow start -+ - * | 1:1 ram mapping | /| 1/8 ram | - * +- end of ram ----+ / +----------------+ - * | ... gap ... |/ | kasan | - * +- shadow start --+ | zero | - * | 1/8 addr space | | page | - * +- shadow end -+ | mapping | - * | ... gap ... |\ | (untracked) | - * +- modules vaddr -+ \ +----------------+ - * | 2Gb | \| unmapped | allocated per module - * +-----------------+ +- shadow end ---+ + * +- 0 -------------+ +- shadow start -+ + * | 1:1 ram mapping | /| 1/8 ram | + * | | / | | + * +- end of ram ----+ / +----------------+ + * | ... gap ... | / | | + * | |/ | kasan | + * +- shadow start --+ | zero | + * | 1/8 addr space | | page | + * +- shadow end -+ | mapping | + * | ... gap ... |\ | (untracked) | + * +- vmalloc area -+ \ | | + * | vmalloc_size | \ | | + * +- modules vaddr -+ \ +----------------+ + * | 2Gb | \| unmapped | allocated per module + * +-----------------+ +- shadow end ---+ + * + * Current memory layout (KASAN_VMALLOC): + * +- 0 -------------+ +- shadow start -+ + * | 1:1 ram mapping | /| 1/8 ram | + * | | / | | + * +- end of ram ----+ / +----------------+ + * | ... gap ... | / | kasan | + * | |/ | zero | + * +- shadow start --+ | page | + * | 1/8 addr space | | mapping | + * +- shadow end -+ | (untracked) | + * | ... gap ... |\ | | + * +- vmalloc area -+ \ +- vmalloc area -+ + * | vmalloc_size | \ |shallow populate| + * +- modules vaddr -+ \ +- modules area -+ + * | 2Gb | \|shallow populate| + * +-----------------+ +- shadow end ---+ */ /* populate kasan shadow (for identity mapping and zero page mapping) */ kasan_early_vmemmap_populate(__sha(0), __sha(memsize), POPULATE_MAP); if (IS_ENABLED(CONFIG_MODULES)) untracked_mem_end = vmax - MODULES_LEN; + if (IS_ENABLED(CONFIG_KASAN_VMALLOC)) { + untracked_mem_end = vmax - vmalloc_size - MODULES_LEN; + /* shallowly populate kasan shadow for vmalloc and modules */ + kasan_early_vmemmap_populate(__sha(untracked_mem_end), + __sha(vmax), POPULATE_SHALLOW); + } + /* populate kasan shadow for untracked memory */ kasan_early_vmemmap_populate(__sha(max_physmem_end), __sha(untracked_mem_end), POPULATE_ZERO_SHADOW); -- GitLab From 02cca5769f5e07748c839168dc0b5f21bbacff08 Mon Sep 17 00:00:00 2001 From: "Tianci.Yin" Date: Wed, 11 Dec 2019 10:43:07 +0800 Subject: [PATCH 0504/1437] drm/amdgpu/gfx10: update gfx golden settings add registers: mmSPI_CONFIG_CNTL Reviewed-by: Feifei Xu Signed-off-by: Tianci.Yin Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdgpu/gfx_v10_0.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v10_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v10_0.c index f2c1b026397b9..1405ecd9a489f 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfx_v10_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gfx_v10_0.c @@ -121,6 +121,7 @@ static const struct soc15_reg_golden golden_settings_gc_10_1[] = SOC15_REG_GOLDEN_VALUE(GC, 0, mmPA_SC_ENHANCE_1, 0x00400000, 0x04440000), SOC15_REG_GOLDEN_VALUE(GC, 0, mmPA_SC_LINE_STIPPLE_STATE, 0x0000ff0f, 0x00000000), SOC15_REG_GOLDEN_VALUE(GC, 0, mmRMI_SPARE, 0xffffffff, 0xffff3101), + SOC15_REG_GOLDEN_VALUE(GC, 0, mmSPI_CONFIG_CNTL, 0x001f0000, 0x00070104), SOC15_REG_GOLDEN_VALUE(GC, 0, mmSQ_ALU_CLK_CTRL, 0xffffffff, 0xffffffff), SOC15_REG_GOLDEN_VALUE(GC, 0, mmSQ_ARB_CONFIG, 0x00000100, 0x00000130), SOC15_REG_GOLDEN_VALUE(GC, 0, mmSQ_LDS_CLK_CTRL, 0xffffffff, 0xffffffff), -- GitLab From 5714a2026f0c6eeea1a51d20a4561088f2ebe9ac Mon Sep 17 00:00:00 2001 From: "Tianci.Yin" Date: Wed, 11 Dec 2019 10:52:14 +0800 Subject: [PATCH 0505/1437] drm/amdgpu/gfx10: update gfx golden settings for navi14 add registers: mmSPI_CONFIG_CNTL Reviewed-by: Feifei Xu Signed-off-by: Tianci.Yin Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdgpu/gfx_v10_0.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v10_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v10_0.c index 1405ecd9a489f..e6d342136d22d 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfx_v10_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gfx_v10_0.c @@ -167,6 +167,7 @@ static const struct soc15_reg_golden golden_settings_gc_10_1_1[] = SOC15_REG_GOLDEN_VALUE(GC, 0, mmPA_SC_ENHANCE_1, 0x00400000, 0x04440000), SOC15_REG_GOLDEN_VALUE(GC, 0, mmPA_SC_LINE_STIPPLE_STATE, 0x0000ff0f, 0x00000000), SOC15_REG_GOLDEN_VALUE(GC, 0, mmRMI_SPARE, 0xffffffff, 0xffff3101), + SOC15_REG_GOLDEN_VALUE(GC, 0, mmSPI_CONFIG_CNTL, 0x001f0000, 0x00070105), SOC15_REG_GOLDEN_VALUE(GC, 0, mmSQ_ALU_CLK_CTRL, 0xffffffff, 0xffffffff), SOC15_REG_GOLDEN_VALUE(GC, 0, mmSQ_ARB_CONFIG, 0x00000133, 0x00000130), SOC15_REG_GOLDEN_VALUE(GC, 0, mmSQ_LDS_CLK_CTRL, 0xffffffff, 0xffffffff), -- GitLab From 847b0d87950867306967d619d7e5a4db03fdb218 Mon Sep 17 00:00:00 2001 From: "Tianci.Yin" Date: Wed, 11 Dec 2019 19:55:49 +0800 Subject: [PATCH 0506/1437] drm/amdgpu/gfx10: update gfx golden settings add registers: mmPA_SC_BINNER_TIMEOUT_COUNTER and mmPA_SC_ENHANCE_2 Reviewed-by: Feifei Xu Signed-off-by: Tianci.Yin Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdgpu/gfx_v10_0.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v10_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v10_0.c index e6d342136d22d..048e79a1f041c 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfx_v10_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gfx_v10_0.c @@ -117,8 +117,10 @@ static const struct soc15_reg_golden golden_settings_gc_10_1[] = SOC15_REG_GOLDEN_VALUE(GC, 0, mmGL2C_CGTT_SCLK_CTRL, 0x10000000, 0x10000100), SOC15_REG_GOLDEN_VALUE(GC, 0, mmGL2C_CTRL2, 0xffffffff, 0x1402002f), SOC15_REG_GOLDEN_VALUE(GC, 0, mmGL2C_CTRL3, 0xffff9fff, 0x00001188), + SOC15_REG_GOLDEN_VALUE(GC, 0, mmPA_SC_BINNER_TIMEOUT_COUNTER, 0xffffffff, 0x00000800), SOC15_REG_GOLDEN_VALUE(GC, 0, mmPA_SC_ENHANCE, 0x3fffffff, 0x08000009), SOC15_REG_GOLDEN_VALUE(GC, 0, mmPA_SC_ENHANCE_1, 0x00400000, 0x04440000), + SOC15_REG_GOLDEN_VALUE(GC, 0, mmPA_SC_ENHANCE_2, 0x00000800, 0x00000820), SOC15_REG_GOLDEN_VALUE(GC, 0, mmPA_SC_LINE_STIPPLE_STATE, 0x0000ff0f, 0x00000000), SOC15_REG_GOLDEN_VALUE(GC, 0, mmRMI_SPARE, 0xffffffff, 0xffff3101), SOC15_REG_GOLDEN_VALUE(GC, 0, mmSPI_CONFIG_CNTL, 0x001f0000, 0x00070104), -- GitLab From 69897d3425674b06d1e822f7491d5c40a5ed1a2f Mon Sep 17 00:00:00 2001 From: "Tianci.Yin" Date: Wed, 11 Dec 2019 19:57:43 +0800 Subject: [PATCH 0507/1437] drm/amdgpu/gfx10: update gfx golden settings for navi14 add registers: mmPA_SC_BINNER_TIMEOUT_COUNTER and mmPA_SC_ENHANCE_2 Reviewed-by: Feifei Xu Signed-off-by: Tianci.Yin Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdgpu/gfx_v10_0.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v10_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v10_0.c index 048e79a1f041c..ba9e53a1abc3a 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfx_v10_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gfx_v10_0.c @@ -165,8 +165,10 @@ static const struct soc15_reg_golden golden_settings_gc_10_1_1[] = SOC15_REG_GOLDEN_VALUE(GC, 0, mmGL2C_CGTT_SCLK_CTRL, 0xffff0fff, 0x10000100), SOC15_REG_GOLDEN_VALUE(GC, 0, mmGL2C_CTRL2, 0xffffffff, 0x1402002f), SOC15_REG_GOLDEN_VALUE(GC, 0, mmGL2C_CTRL3, 0xffffbfff, 0x00000188), + SOC15_REG_GOLDEN_VALUE(GC, 0, mmPA_SC_BINNER_TIMEOUT_COUNTER, 0xffffffff, 0x00000800), SOC15_REG_GOLDEN_VALUE(GC, 0, mmPA_SC_ENHANCE, 0x3fffffff, 0x08000009), SOC15_REG_GOLDEN_VALUE(GC, 0, mmPA_SC_ENHANCE_1, 0x00400000, 0x04440000), + SOC15_REG_GOLDEN_VALUE(GC, 0, mmPA_SC_ENHANCE_2, 0x00000800, 0x00000820), SOC15_REG_GOLDEN_VALUE(GC, 0, mmPA_SC_LINE_STIPPLE_STATE, 0x0000ff0f, 0x00000000), SOC15_REG_GOLDEN_VALUE(GC, 0, mmRMI_SPARE, 0xffffffff, 0xffff3101), SOC15_REG_GOLDEN_VALUE(GC, 0, mmSPI_CONFIG_CNTL, 0x001f0000, 0x00070105), -- GitLab From bd95c1445274c5e126ffd8998fbba9f3732a579e Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Wed, 11 Dec 2019 10:09:03 -0500 Subject: [PATCH 0508/1437] drm/amdgpu: fix license on Kconfig and Makefiles MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit amdgpu is MIT licensed. Fixes: ec8f24b7faaf3d ("treewide: Add SPDX license identifier - Makefile/Kconfig") Reviewed-by: Christian König Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/acp/Kconfig | 2 +- drivers/gpu/drm/amd/amdgpu/Kconfig | 2 +- drivers/gpu/drm/amd/amdkfd/Kconfig | 2 +- drivers/gpu/drm/amd/display/Kconfig | 2 +- drivers/gpu/drm/amd/display/dc/dcn20/Makefile | 1 + drivers/gpu/drm/amd/display/dc/dcn21/Makefile | 1 + drivers/gpu/drm/amd/display/dc/dsc/Makefile | 1 + 7 files changed, 7 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/amd/acp/Kconfig b/drivers/gpu/drm/amd/acp/Kconfig index d968c24714125..0d12ebf661743 100644 --- a/drivers/gpu/drm/amd/acp/Kconfig +++ b/drivers/gpu/drm/amd/acp/Kconfig @@ -1,4 +1,4 @@ -# SPDX-License-Identifier: GPL-2.0-only +# SPDX-License-Identifier: MIT menu "ACP (Audio CoProcessor) Configuration" config DRM_AMD_ACP diff --git a/drivers/gpu/drm/amd/amdgpu/Kconfig b/drivers/gpu/drm/amd/amdgpu/Kconfig index 2e98c016cb47d..9375e7f124205 100644 --- a/drivers/gpu/drm/amd/amdgpu/Kconfig +++ b/drivers/gpu/drm/amd/amdgpu/Kconfig @@ -1,4 +1,4 @@ -# SPDX-License-Identifier: GPL-2.0-only +# SPDX-License-Identifier: MIT config DRM_AMDGPU_SI bool "Enable amdgpu support for SI parts" depends on DRM_AMDGPU diff --git a/drivers/gpu/drm/amd/amdkfd/Kconfig b/drivers/gpu/drm/amd/amdkfd/Kconfig index ba0e68057a892..b3672d10ea54b 100644 --- a/drivers/gpu/drm/amd/amdkfd/Kconfig +++ b/drivers/gpu/drm/amd/amdkfd/Kconfig @@ -1,4 +1,4 @@ -# SPDX-License-Identifier: GPL-2.0-only +# SPDX-License-Identifier: MIT # # Heterogenous system architecture configuration # diff --git a/drivers/gpu/drm/amd/display/Kconfig b/drivers/gpu/drm/amd/display/Kconfig index 313183b800328..ae161fe86ebb6 100644 --- a/drivers/gpu/drm/amd/display/Kconfig +++ b/drivers/gpu/drm/amd/display/Kconfig @@ -1,4 +1,4 @@ -# SPDX-License-Identifier: GPL-2.0-only +# SPDX-License-Identifier: MIT menu "Display Engine Configuration" depends on DRM && DRM_AMDGPU diff --git a/drivers/gpu/drm/amd/display/dc/dcn20/Makefile b/drivers/gpu/drm/amd/display/dc/dcn20/Makefile index 63f3bddba7daa..10b47986526bd 100644 --- a/drivers/gpu/drm/amd/display/dc/dcn20/Makefile +++ b/drivers/gpu/drm/amd/display/dc/dcn20/Makefile @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: MIT # # Makefile for DCN. diff --git a/drivers/gpu/drm/amd/display/dc/dcn21/Makefile b/drivers/gpu/drm/amd/display/dc/dcn21/Makefile index 14113ccf498d2..5b8c17564bc19 100644 --- a/drivers/gpu/drm/amd/display/dc/dcn21/Makefile +++ b/drivers/gpu/drm/amd/display/dc/dcn21/Makefile @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: MIT # # Makefile for DCN21. diff --git a/drivers/gpu/drm/amd/display/dc/dsc/Makefile b/drivers/gpu/drm/amd/display/dc/dsc/Makefile index 970737217e53a..641ffb7cfaed4 100644 --- a/drivers/gpu/drm/amd/display/dc/dsc/Makefile +++ b/drivers/gpu/drm/amd/display/dc/dsc/Makefile @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: MIT # # Makefile for the 'dsc' sub-component of DAL. -- GitLab From d1c6a2aa02af09238ad09493eb3c8685ccc3fe12 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Wed, 11 Dec 2019 11:46:19 -0800 Subject: [PATCH 0509/1437] pipe: simplify signal handling in pipe_read() and add comments There's no need to separately check for signals while inside the locked region, since we're going to do "wait_event_interruptible()" right afterwards anyway, and the error handling is much simpler there. The check for whether we had already read anything was also redundant, since we no longer do the odd merging of reads when there are pending writers. But perhaps more importantly, this adds commentary about why we still need to wake up possible writers even though we didn't read any data, and why we can skip all the finishing touches now if we get a signal (or had a signal pending) while waiting for more data. [ This is a split-out cleanup from my "make pipe IO use exclusive wait queues" thing, which I can't apply because it triggers a nasty bug in the GNU make jobserver - Linus ] Signed-off-by: Linus Torvalds --- fs/pipe.c | 36 +++++++++++++++++++++++++++++------- 1 file changed, 29 insertions(+), 7 deletions(-) diff --git a/fs/pipe.c b/fs/pipe.c index 87109e761fa5e..04d004ee2e8c6 100644 --- a/fs/pipe.c +++ b/fs/pipe.c @@ -364,17 +364,39 @@ pipe_read(struct kiocb *iocb, struct iov_iter *to) ret = -EAGAIN; break; } - if (signal_pending(current)) { - if (!ret) - ret = -ERESTARTSYS; - break; - } __pipe_unlock(pipe); - if (was_full) { + + /* + * We only get here if we didn't actually read anything. + * + * However, we could have seen (and removed) a zero-sized + * pipe buffer, and might have made space in the buffers + * that way. + * + * You can't make zero-sized pipe buffers by doing an empty + * write (not even in packet mode), but they can happen if + * the writer gets an EFAULT when trying to fill a buffer + * that already got allocated and inserted in the buffer + * array. + * + * So we still need to wake up any pending writers in the + * _very_ unlikely case that the pipe was full, but we got + * no data. + */ + if (unlikely(was_full)) { wake_up_interruptible_sync_poll(&pipe->wait, EPOLLOUT | EPOLLWRNORM); kill_fasync(&pipe->fasync_writers, SIGIO, POLL_OUT); } - wait_event_interruptible(pipe->wait, pipe_readable(pipe)); + + /* + * But because we didn't read anything, at this point we can + * just return directly with -ERESTARTSYS if we're interrupted, + * since we've done any required wakeups and there's no need + * to mark anything accessed. And we've dropped the lock. + */ + if (wait_event_interruptible(pipe->wait, pipe_readable(pipe)) < 0) + return -ERESTARTSYS; + __pipe_lock(pipe); was_full = pipe_full(pipe->head, pipe->tail, pipe->max_usage); } -- GitLab From 99528efd1926f2acfd9660d0f48f528ee9048425 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Wed, 11 Dec 2019 13:18:37 -0800 Subject: [PATCH 0510/1437] xfs: fix Sphinx documentation warning Fix Sphinx documentation format warning by not indenting so much. Documentation/admin-guide/xfs.rst:257: WARNING: Block quote ends without a blank line; unexpected unindent. Signed-off-by: Randy Dunlap Cc: Darrick J. Wong Cc: linux-xfs@vger.kernel.org Reviewed-by: Darrick J. Wong Signed-off-by: Darrick J. Wong --- Documentation/admin-guide/xfs.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/admin-guide/xfs.rst b/Documentation/admin-guide/xfs.rst index fb5b39f73059d..ad911be5b5e93 100644 --- a/Documentation/admin-guide/xfs.rst +++ b/Documentation/admin-guide/xfs.rst @@ -253,7 +253,7 @@ The following sysctls are available for the XFS filesystem: pool. fs.xfs.speculative_prealloc_lifetime - (Units: seconds Min: 1 Default: 300 Max: 86400) + (Units: seconds Min: 1 Default: 300 Max: 86400) The interval at which the background scanning for inodes with unused speculative preallocation runs. The scan removes unused preallocation from clean inodes and releases -- GitLab From d0c2204135a0cdbc607c94c481cf1ccb2f659aa7 Mon Sep 17 00:00:00 2001 From: Brian Foster Date: Wed, 11 Dec 2019 13:18:38 -0800 Subject: [PATCH 0511/1437] xfs: stabilize insert range start boundary to avoid COW writeback race generic/522 (fsx) occasionally fails with a file corruption due to an insert range operation. The primary characteristic of the corruption is a misplaced insert range operation that differs from the requested target offset. The reason for this behavior is a race between the extent shift sequence of an insert range and a COW writeback completion that causes a front merge with the first extent in the shift. The shift preparation function flushes and unmaps from the target offset of the operation to the end of the file to ensure no modifications can be made and page cache is invalidated before file data is shifted. An insert range operation then splits the extent at the target offset, if necessary, and begins to shift the start offset of each extent starting from the end of the file to the start offset. The shift sequence operates at extent level and so depends on the preparation sequence to guarantee no changes can be made to the target range during the shift. If the block immediately prior to the target offset was dirty and shared, however, it can undergo writeback and move from the COW fork to the data fork at any point during the shift. If the block is contiguous with the block at the start offset of the insert range, it can front merge and alter the start offset of the extent. Once the shift sequence reaches the target offset, it shifts based on the latest start offset and silently changes the target offset of the operation and corrupts the file. To address this problem, update the shift preparation code to stabilize the start boundary along with the full range of the insert. Also update the existing corruption check to fail if any extent is shifted with a start offset behind the target offset of the insert range. This prevents insert from racing with COW writeback completion and fails loudly in the event of an unexpected extent shift. Signed-off-by: Brian Foster Reviewed-by: Darrick J. Wong Signed-off-by: Darrick J. Wong --- fs/xfs/libxfs/xfs_bmap.c | 3 +-- fs/xfs/xfs_bmap_util.c | 12 ++++++++++++ 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/fs/xfs/libxfs/xfs_bmap.c b/fs/xfs/libxfs/xfs_bmap.c index a9ad1f991ba36..4a802b3abe77c 100644 --- a/fs/xfs/libxfs/xfs_bmap.c +++ b/fs/xfs/libxfs/xfs_bmap.c @@ -5972,8 +5972,7 @@ xfs_bmap_insert_extents( goto del_cursor; } - if (XFS_IS_CORRUPT(mp, - stop_fsb >= got.br_startoff + got.br_blockcount)) { + if (XFS_IS_CORRUPT(mp, stop_fsb > got.br_startoff)) { error = -EFSCORRUPTED; goto del_cursor; } diff --git a/fs/xfs/xfs_bmap_util.c b/fs/xfs/xfs_bmap_util.c index 2efd78a9719ea..e62fb5216341c 100644 --- a/fs/xfs/xfs_bmap_util.c +++ b/fs/xfs/xfs_bmap_util.c @@ -992,6 +992,7 @@ xfs_prepare_shift( struct xfs_inode *ip, loff_t offset) { + struct xfs_mount *mp = ip->i_mount; int error; /* @@ -1004,6 +1005,17 @@ xfs_prepare_shift( return error; } + /* + * Shift operations must stabilize the start block offset boundary along + * with the full range of the operation. If we don't, a COW writeback + * completion could race with an insert, front merge with the start + * extent (after split) during the shift and corrupt the file. Start + * with the block just prior to the start to stabilize the boundary. + */ + offset = round_down(offset, 1 << mp->m_sb.sb_blocklog); + if (offset) + offset -= (1 << mp->m_sb.sb_blocklog); + /* * Writeback and invalidate cache for the remainder of the file as we're * about to shift down every extent from offset to EOF. -- GitLab From 9e3aa61ae3e01ce1ce6361a41ef725e1f4d1d2bf Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Wed, 11 Dec 2019 15:55:43 -0700 Subject: [PATCH 0512/1437] io_uring: ensure we return -EINVAL on unknown opcode If we submit an unknown opcode and have fd == -1, io_op_needs_file() will return true as we default to needing a file. Then when we go and assign the file, we find the 'fd' invalid and return -EBADF. We really should be returning -EINVAL for that case, as we normally do for unsupported opcodes. Change io_op_needs_file() to have the following return values: 0 - does not need a file 1 - does need a file < 0 - error value and use this to pass back the right value for this invalid case. Signed-off-by: Jens Axboe --- fs/io_uring.c | 21 ++++++++++++------- include/uapi/linux/io_uring.h | 39 ++++++++++++++++++++--------------- 2 files changed, 36 insertions(+), 24 deletions(-) diff --git a/fs/io_uring.c b/fs/io_uring.c index 42de210be6310..9b1833fedc5cc 100644 --- a/fs/io_uring.c +++ b/fs/io_uring.c @@ -3062,7 +3062,12 @@ static void io_wq_submit_work(struct io_wq_work **workptr) } } -static bool io_op_needs_file(const struct io_uring_sqe *sqe) +static bool io_req_op_valid(int op) +{ + return op >= IORING_OP_NOP && op < IORING_OP_LAST; +} + +static int io_op_needs_file(const struct io_uring_sqe *sqe) { int op = READ_ONCE(sqe->opcode); @@ -3073,9 +3078,11 @@ static bool io_op_needs_file(const struct io_uring_sqe *sqe) case IORING_OP_TIMEOUT_REMOVE: case IORING_OP_ASYNC_CANCEL: case IORING_OP_LINK_TIMEOUT: - return false; + return 0; default: - return true; + if (io_req_op_valid(op)) + return 1; + return -EINVAL; } } @@ -3092,7 +3099,7 @@ static int io_req_set_file(struct io_submit_state *state, struct io_kiocb *req) { struct io_ring_ctx *ctx = req->ctx; unsigned flags; - int fd; + int fd, ret; flags = READ_ONCE(req->sqe->flags); fd = READ_ONCE(req->sqe->fd); @@ -3100,8 +3107,9 @@ static int io_req_set_file(struct io_submit_state *state, struct io_kiocb *req) if (flags & IOSQE_IO_DRAIN) req->flags |= REQ_F_IO_DRAIN; - if (!io_op_needs_file(req->sqe)) - return 0; + ret = io_op_needs_file(req->sqe); + if (ret <= 0) + return ret; if (flags & IOSQE_FIXED_FILE) { if (unlikely(!ctx->file_table || @@ -3312,7 +3320,6 @@ static inline void io_queue_link_head(struct io_kiocb *req) io_queue_sqe(req); } - #define SQE_VALID_FLAGS (IOSQE_FIXED_FILE|IOSQE_IO_DRAIN|IOSQE_IO_LINK| \ IOSQE_IO_HARDLINK) diff --git a/include/uapi/linux/io_uring.h b/include/uapi/linux/io_uring.h index ea231366f5fd7..a3300e1b9a010 100644 --- a/include/uapi/linux/io_uring.h +++ b/include/uapi/linux/io_uring.h @@ -58,23 +58,28 @@ struct io_uring_sqe { #define IORING_SETUP_SQ_AFF (1U << 2) /* sq_thread_cpu is valid */ #define IORING_SETUP_CQSIZE (1U << 3) /* app defines CQ size */ -#define IORING_OP_NOP 0 -#define IORING_OP_READV 1 -#define IORING_OP_WRITEV 2 -#define IORING_OP_FSYNC 3 -#define IORING_OP_READ_FIXED 4 -#define IORING_OP_WRITE_FIXED 5 -#define IORING_OP_POLL_ADD 6 -#define IORING_OP_POLL_REMOVE 7 -#define IORING_OP_SYNC_FILE_RANGE 8 -#define IORING_OP_SENDMSG 9 -#define IORING_OP_RECVMSG 10 -#define IORING_OP_TIMEOUT 11 -#define IORING_OP_TIMEOUT_REMOVE 12 -#define IORING_OP_ACCEPT 13 -#define IORING_OP_ASYNC_CANCEL 14 -#define IORING_OP_LINK_TIMEOUT 15 -#define IORING_OP_CONNECT 16 +enum { + IORING_OP_NOP, + IORING_OP_READV, + IORING_OP_WRITEV, + IORING_OP_FSYNC, + IORING_OP_READ_FIXED, + IORING_OP_WRITE_FIXED, + IORING_OP_POLL_ADD, + IORING_OP_POLL_REMOVE, + IORING_OP_SYNC_FILE_RANGE, + IORING_OP_SENDMSG, + IORING_OP_RECVMSG, + IORING_OP_TIMEOUT, + IORING_OP_TIMEOUT_REMOVE, + IORING_OP_ACCEPT, + IORING_OP_ASYNC_CANCEL, + IORING_OP_LINK_TIMEOUT, + IORING_OP_CONNECT, + + /* this goes last, obviously */ + IORING_OP_LAST, +}; /* * sqe->fsync_flags -- GitLab From b91e014f078e2e4f24778680e28dbbdecc7f0eb9 Mon Sep 17 00:00:00 2001 From: Alexei Starovoitov Date: Sun, 8 Dec 2019 16:01:13 -0800 Subject: [PATCH 0513/1437] bpf: Make BPF trampoline use register_ftrace_direct() API Make BPF trampoline attach its generated assembly code to kernel functions via register_ftrace_direct() API. It helps ftrace-based tracers co-exist with BPF trampoline on the same kernel function. It also switches attaching logic from arch specific text_poke to generic ftrace that is available on many architectures. text_poke is still necessary for bpf-to-bpf attach and for bpf_tail_call optimization. Signed-off-by: Alexei Starovoitov Acked-by: Daniel Borkmann Link: https://lore.kernel.org/bpf/20191209000114.1876138-3-ast@kernel.org --- include/linux/bpf.h | 1 + kernel/bpf/trampoline.c | 64 +++++++++++++++++++++++++++++++++++++---- 2 files changed, 59 insertions(+), 6 deletions(-) diff --git a/include/linux/bpf.h b/include/linux/bpf.h index 35903f148be59..ac7de52915093 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -461,6 +461,7 @@ struct bpf_trampoline { struct { struct btf_func_model model; void *addr; + bool ftrace_managed; } func; /* list of BPF programs using this trampoline */ struct hlist_head progs_hlist[BPF_TRAMP_MAX]; diff --git a/kernel/bpf/trampoline.c b/kernel/bpf/trampoline.c index 7e89f1f49d771..23b0d5cfd47eb 100644 --- a/kernel/bpf/trampoline.c +++ b/kernel/bpf/trampoline.c @@ -3,6 +3,7 @@ #include #include #include +#include /* btf_vmlinux has ~22k attachable functions. 1k htab is enough. */ #define TRAMPOLINE_HASH_BITS 10 @@ -59,6 +60,60 @@ out: return tr; } +static int is_ftrace_location(void *ip) +{ + long addr; + + addr = ftrace_location((long)ip); + if (!addr) + return 0; + if (WARN_ON_ONCE(addr != (long)ip)) + return -EFAULT; + return 1; +} + +static int unregister_fentry(struct bpf_trampoline *tr, void *old_addr) +{ + void *ip = tr->func.addr; + int ret; + + if (tr->func.ftrace_managed) + ret = unregister_ftrace_direct((long)ip, (long)old_addr); + else + ret = bpf_arch_text_poke(ip, BPF_MOD_CALL, old_addr, NULL); + return ret; +} + +static int modify_fentry(struct bpf_trampoline *tr, void *old_addr, void *new_addr) +{ + void *ip = tr->func.addr; + int ret; + + if (tr->func.ftrace_managed) + ret = modify_ftrace_direct((long)ip, (long)old_addr, (long)new_addr); + else + ret = bpf_arch_text_poke(ip, BPF_MOD_CALL, old_addr, new_addr); + return ret; +} + +/* first time registering */ +static int register_fentry(struct bpf_trampoline *tr, void *new_addr) +{ + void *ip = tr->func.addr; + int ret; + + ret = is_ftrace_location(ip); + if (ret < 0) + return ret; + tr->func.ftrace_managed = ret; + + if (tr->func.ftrace_managed) + ret = register_ftrace_direct((long)ip, (long)new_addr); + else + ret = bpf_arch_text_poke(ip, BPF_MOD_CALL, NULL, new_addr); + return ret; +} + /* Each call __bpf_prog_enter + call bpf_func + call __bpf_prog_exit is ~50 * bytes on x86. Pick a number to fit into PAGE_SIZE / 2 */ @@ -77,8 +132,7 @@ static int bpf_trampoline_update(struct bpf_trampoline *tr) int err; if (fentry_cnt + fexit_cnt == 0) { - err = bpf_arch_text_poke(tr->func.addr, BPF_MOD_CALL, - old_image, NULL); + err = unregister_fentry(tr, old_image); tr->selector = 0; goto out; } @@ -105,12 +159,10 @@ static int bpf_trampoline_update(struct bpf_trampoline *tr) if (tr->selector) /* progs already running at this address */ - err = bpf_arch_text_poke(tr->func.addr, BPF_MOD_CALL, - old_image, new_image); + err = modify_fentry(tr, old_image, new_image); else /* first time registering */ - err = bpf_arch_text_poke(tr->func.addr, BPF_MOD_CALL, NULL, - new_image); + err = register_fentry(tr, new_image); if (err) goto out; tr->selector++; -- GitLab From 7f193c2519a3dcfd61cd5e676b2959370f4b15be Mon Sep 17 00:00:00 2001 From: Alexei Starovoitov Date: Sun, 8 Dec 2019 16:01:14 -0800 Subject: [PATCH 0514/1437] selftests/bpf: Test function_graph tracer and bpf trampoline together Add simple test script to execute funciton graph tracer while BPF trampoline attaches and detaches from the functions being graph traced. Signed-off-by: Alexei Starovoitov Acked-by: Daniel Borkmann Link: https://lore.kernel.org/bpf/20191209000114.1876138-4-ast@kernel.org --- tools/testing/selftests/bpf/test_ftrace.sh | 39 ++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100755 tools/testing/selftests/bpf/test_ftrace.sh diff --git a/tools/testing/selftests/bpf/test_ftrace.sh b/tools/testing/selftests/bpf/test_ftrace.sh new file mode 100755 index 0000000000000..20de7bb873bc7 --- /dev/null +++ b/tools/testing/selftests/bpf/test_ftrace.sh @@ -0,0 +1,39 @@ +#!/bin/bash + +TR=/sys/kernel/debug/tracing/ +clear_trace() { # reset trace output + echo > $TR/trace +} + +disable_tracing() { # stop trace recording + echo 0 > $TR/tracing_on +} + +enable_tracing() { # start trace recording + echo 1 > $TR/tracing_on +} + +reset_tracer() { # reset the current tracer + echo nop > $TR/current_tracer +} + +disable_tracing +clear_trace + +echo "" > $TR/set_ftrace_filter +echo '*printk* *console* *wake* *serial* *lock*' > $TR/set_ftrace_notrace + +echo "bpf_prog_test*" > $TR/set_graph_function +echo "" > $TR/set_graph_notrace + +echo function_graph > $TR/current_tracer + +enable_tracing +./test_progs -t fentry +./test_progs -t fexit +disable_tracing +clear_trace + +reset_tracer + +exit 0 -- GitLab From bba1b2a890253528c45aa66cf856f289a215bfbc Mon Sep 17 00:00:00 2001 From: "Daniel T. Lee" Date: Thu, 5 Dec 2019 17:01:13 +0900 Subject: [PATCH 0515/1437] samples: bpf: Replace symbol compare of trace_event Previously, when this sample is added, commit 1c47910ef8013 ("samples/bpf: add perf_event+bpf example"), a symbol 'sys_read' and 'sys_write' has been used without no prefixes. But currently there are no exact symbols with these under kallsyms and this leads to failure. This commit changes exact compare to substring compare to keep compatible with exact symbol or prefixed symbol. Fixes: 1c47910ef8013 ("samples/bpf: add perf_event+bpf example") Signed-off-by: Daniel T. Lee Signed-off-by: Alexei Starovoitov Link: https://lore.kernel.org/bpf/20191205080114.19766-2-danieltimlee@gmail.com --- samples/bpf/trace_event_user.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/samples/bpf/trace_event_user.c b/samples/bpf/trace_event_user.c index 16a16eadd5098..749a50f2f9f32 100644 --- a/samples/bpf/trace_event_user.c +++ b/samples/bpf/trace_event_user.c @@ -37,9 +37,9 @@ static void print_ksym(__u64 addr) } printf("%s;", sym->name); - if (!strcmp(sym->name, "sys_read")) + if (!strstr(sym->name, "sys_read")) sys_read_seen = true; - else if (!strcmp(sym->name, "sys_write")) + else if (!strstr(sym->name, "sys_write")) sys_write_seen = true; } -- GitLab From fe3300897cbfd76c6cb825776e5ac0ca50a91ca4 Mon Sep 17 00:00:00 2001 From: "Daniel T. Lee" Date: Thu, 5 Dec 2019 17:01:14 +0900 Subject: [PATCH 0516/1437] samples: bpf: fix syscall_tp due to unused syscall Currently, open() is called from the user program and it calls the syscall 'sys_openat', not the 'sys_open'. This leads to an error of the program of user side, due to the fact that the counter maps are zero since no function such 'sys_open' is called. This commit adds the kernel bpf program which are attached to the tracepoint 'sys_enter_openat' and 'sys_enter_openat'. Fixes: 1da236b6be963 ("bpf: add a test case for syscalls/sys_{enter|exit}_* tracepoints") Signed-off-by: Daniel T. Lee Signed-off-by: Alexei Starovoitov --- samples/bpf/syscall_tp_kern.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/samples/bpf/syscall_tp_kern.c b/samples/bpf/syscall_tp_kern.c index 1d78819ffef19..630ce8c4d5a23 100644 --- a/samples/bpf/syscall_tp_kern.c +++ b/samples/bpf/syscall_tp_kern.c @@ -47,13 +47,27 @@ static __always_inline void count(void *map) SEC("tracepoint/syscalls/sys_enter_open") int trace_enter_open(struct syscalls_enter_open_args *ctx) { - count((void *)&enter_open_map); + count(&enter_open_map); + return 0; +} + +SEC("tracepoint/syscalls/sys_enter_openat") +int trace_enter_open_at(struct syscalls_enter_open_args *ctx) +{ + count(&enter_open_map); return 0; } SEC("tracepoint/syscalls/sys_exit_open") int trace_enter_exit(struct syscalls_exit_open_args *ctx) { - count((void *)&exit_open_map); + count(&exit_open_map); + return 0; +} + +SEC("tracepoint/syscalls/sys_exit_openat") +int trace_enter_exit_at(struct syscalls_exit_open_args *ctx) +{ + count(&exit_open_map); return 0; } -- GitLab From 81c22041d9f19df07b9cba95e3cd02e0f41bc1e1 Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Mon, 9 Dec 2019 16:08:03 +0100 Subject: [PATCH 0517/1437] bpf, x86, arm64: Enable jit by default when not built as always-on After Spectre 2 fix via 290af86629b2 ("bpf: introduce BPF_JIT_ALWAYS_ON config") most major distros use BPF_JIT_ALWAYS_ON configuration these days which compiles out the BPF interpreter entirely and always enables the JIT. Also given recent fix in e1608f3fa857 ("bpf: Avoid setting bpf insns pages read-only when prog is jited"), we additionally avoid fragmenting the direct map for the BPF insns pages sitting in the general data heap since they are not used during execution. Latter is only needed when run through the interpreter. Since both x86 and arm64 JITs have seen a lot of exposure over the years, are generally most up to date and maintained, there is more downside in !BPF_JIT_ALWAYS_ON configurations to have the interpreter enabled by default rather than the JIT. Add a ARCH_WANT_DEFAULT_BPF_JIT config which archs can use to set the bpf_jit_{enable,kallsyms} to 1. Back in the days the bpf_jit_kallsyms knob was set to 0 by default since major distros still had /proc/kallsyms addresses exposed to unprivileged user space which is not the case anymore. Hence both knobs are set via BPF_JIT_DEFAULT_ON which is set to 'y' in case of BPF_JIT_ALWAYS_ON or ARCH_WANT_DEFAULT_BPF_JIT. Signed-off-by: Daniel Borkmann Signed-off-by: Alexei Starovoitov Acked-by: Will Deacon Acked-by: Martin KaFai Lau Link: https://lore.kernel.org/bpf/f78ad24795c2966efcc2ee19025fa3459f622185.1575903816.git.daniel@iogearbox.net --- arch/arm64/Kconfig | 1 + arch/x86/Kconfig | 1 + init/Kconfig | 7 +++++++ kernel/bpf/core.c | 4 ++-- 4 files changed, 11 insertions(+), 2 deletions(-) diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index b1b4476ddb834..29d03459de209 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -69,6 +69,7 @@ config ARM64 select ARCH_SUPPORTS_INT128 if CC_HAS_INT128 && (GCC_VERSION >= 50000 || CC_IS_CLANG) select ARCH_SUPPORTS_NUMA_BALANCING select ARCH_WANT_COMPAT_IPC_PARSE_VERSION if COMPAT + select ARCH_WANT_DEFAULT_BPF_JIT select ARCH_WANT_DEFAULT_TOPDOWN_MMAP_LAYOUT select ARCH_WANT_FRAME_POINTERS select ARCH_WANT_HUGE_PMD_SHARE if ARM64_4K_PAGES || (ARM64_16K_PAGES && !ARM64_VA_BITS_36) diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index 5e89499536606..1f6a0388a65f1 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -93,6 +93,7 @@ config X86 select ARCH_USE_QUEUED_RWLOCKS select ARCH_USE_QUEUED_SPINLOCKS select ARCH_WANT_BATCHED_UNMAP_TLB_FLUSH + select ARCH_WANT_DEFAULT_BPF_JIT if X86_64 select ARCH_WANTS_DYNAMIC_TASK_STRUCT select ARCH_WANT_HUGE_PMD_SHARE select ARCH_WANTS_THP_SWAP if X86_64 diff --git a/init/Kconfig b/init/Kconfig index a34064a031a5e..890aaa62efde5 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -1604,6 +1604,9 @@ config BPF_SYSCALL Enable the bpf() system call that allows to manipulate eBPF programs and maps via file descriptors. +config ARCH_WANT_DEFAULT_BPF_JIT + bool + config BPF_JIT_ALWAYS_ON bool "Permanently enable BPF JIT and remove BPF interpreter" depends on BPF_SYSCALL && HAVE_EBPF_JIT && BPF_JIT @@ -1611,6 +1614,10 @@ config BPF_JIT_ALWAYS_ON Enables BPF JIT and removes BPF interpreter to avoid speculative execution of BPF instructions by the interpreter +config BPF_JIT_DEFAULT_ON + def_bool ARCH_WANT_DEFAULT_BPF_JIT || BPF_JIT_ALWAYS_ON + depends on HAVE_EBPF_JIT && BPF_JIT + config USERFAULTFD bool "Enable userfaultfd() system call" depends on MMU diff --git a/kernel/bpf/core.c b/kernel/bpf/core.c index 49e32acad7d80..2ff01a7161284 100644 --- a/kernel/bpf/core.c +++ b/kernel/bpf/core.c @@ -520,9 +520,9 @@ void bpf_prog_kallsyms_del_all(struct bpf_prog *fp) #ifdef CONFIG_BPF_JIT /* All BPF JIT sysctl knobs here. */ -int bpf_jit_enable __read_mostly = IS_BUILTIN(CONFIG_BPF_JIT_ALWAYS_ON); +int bpf_jit_enable __read_mostly = IS_BUILTIN(CONFIG_BPF_JIT_DEFAULT_ON); +int bpf_jit_kallsyms __read_mostly = IS_BUILTIN(CONFIG_BPF_JIT_DEFAULT_ON); int bpf_jit_harden __read_mostly; -int bpf_jit_kallsyms __read_mostly; long bpf_jit_limit __read_mostly; static __always_inline void -- GitLab From c1468554776229d0db69e74a9aaf6f7e7095fd51 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Wed, 13 Nov 2019 16:22:06 -0800 Subject: [PATCH 0518/1437] tools/testing/nvdimm: Fix mock support for ioremap After commit d092a8707326 "arch: rely on asm-generic/io.h for default ioremap_* definitions" the ioremap_nocache() symbol has been replaced with ioremap(). Update the mocked symbol list for nvdimm testing. Link: https://lore.kernel.org/r/157369090817.2974548.10148423996292973088.stgit@dwillia2-desk3.amr.corp.intel.com Fixes: d092a8707326 ("arch: rely on asm-generic/io.h for default ioremap_* definitions") Signed-off-by: Dan Williams --- tools/testing/nvdimm/Kbuild | 1 + tools/testing/nvdimm/test/iomap.c | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/tools/testing/nvdimm/Kbuild b/tools/testing/nvdimm/Kbuild index c4a9196d794c9..6aca8d5be1593 100644 --- a/tools/testing/nvdimm/Kbuild +++ b/tools/testing/nvdimm/Kbuild @@ -5,6 +5,7 @@ ldflags-y += --wrap=devm_ioremap_nocache ldflags-y += --wrap=devm_memremap ldflags-y += --wrap=devm_memunmap ldflags-y += --wrap=ioremap_nocache +ldflags-y += --wrap=ioremap ldflags-y += --wrap=iounmap ldflags-y += --wrap=memunmap ldflags-y += --wrap=__devm_request_region diff --git a/tools/testing/nvdimm/test/iomap.c b/tools/testing/nvdimm/test/iomap.c index 3f55f2f99112f..6271ac757a4b2 100644 --- a/tools/testing/nvdimm/test/iomap.c +++ b/tools/testing/nvdimm/test/iomap.c @@ -193,6 +193,12 @@ void __iomem *__wrap_ioremap_nocache(resource_size_t offset, unsigned long size) } EXPORT_SYMBOL(__wrap_ioremap_nocache); +void __iomem *__wrap_ioremap(resource_size_t offset, unsigned long size) +{ + return __nfit_test_ioremap(offset, size, ioremap); +} +EXPORT_SYMBOL(__wrap_ioremap); + void __iomem *__wrap_ioremap_wc(resource_size_t offset, unsigned long size) { return __nfit_test_ioremap(offset, size, ioremap_wc); -- GitLab From 3f0fb37b22b460e3dec62bee284932881574acb9 Mon Sep 17 00:00:00 2001 From: Michael Walle Date: Wed, 11 Dec 2019 18:11:45 +0100 Subject: [PATCH 0519/1437] arm64: dts: ls1028a: fix reboot node The reboot register isn't located inside the DCFG controller, but in its own RST controller. Fix it. Fixes: 8897f3255c9c ("arm64: dts: Add support for NXP LS1028A SoC") Signed-off-by: Michael Walle Acked-by: Li Yang Signed-off-by: Shawn Guo --- arch/arm64/boot/dts/freescale/fsl-ls1028a.dtsi | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/arch/arm64/boot/dts/freescale/fsl-ls1028a.dtsi b/arch/arm64/boot/dts/freescale/fsl-ls1028a.dtsi index 06082c9325315..13a3cbe89b5a6 100644 --- a/arch/arm64/boot/dts/freescale/fsl-ls1028a.dtsi +++ b/arch/arm64/boot/dts/freescale/fsl-ls1028a.dtsi @@ -88,7 +88,7 @@ reboot { compatible ="syscon-reboot"; - regmap = <&dcfg>; + regmap = <&rst>; offset = <0xb0>; mask = <0x02>; }; @@ -178,6 +178,12 @@ big-endian; }; + rst: syscon@1e60000 { + compatible = "syscon"; + reg = <0x0 0x1e60000 0x0 0x10000>; + little-endian; + }; + scfg: syscon@1fc0000 { compatible = "fsl,ls1028a-scfg", "syscon"; reg = <0x0 0x1fc0000 0x0 0x10000>; -- GitLab From 62cfe242db8c60551f36214ebb8858ce0a543841 Mon Sep 17 00:00:00 2001 From: Leonard Crestez Date: Wed, 11 Dec 2019 17:02:47 +0200 Subject: [PATCH 0520/1437] ARM: dts: imx6ul-evk: Fix peripheral regulator Many peripherals are affected by gpio5/2, not just sensors. One of those is ethernet phy so network boot is current broken. Fix by renaming reg_sensors and marking it as "always on". Also add a comment asking for careful testing if this is to be made dynamic in the future. The "peri_3v3" naming is similar to imx6sx-sdb and regulator-name is same string as in schematics (VPERI_3V3). Fixes: 09e2b1048954 ("ARM: dts: imx6ul-14x14-evk: Add sensors' GPIO regulator") Signed-off-by: Leonard Crestez Reviewed-by: Marco Felsch Signed-off-by: Shawn Guo --- arch/arm/boot/dts/imx6ul-14x14-evk.dtsi | 26 +++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/arch/arm/boot/dts/imx6ul-14x14-evk.dtsi b/arch/arm/boot/dts/imx6ul-14x14-evk.dtsi index e5dafb49ef128..212144511b661 100644 --- a/arch/arm/boot/dts/imx6ul-14x14-evk.dtsi +++ b/arch/arm/boot/dts/imx6ul-14x14-evk.dtsi @@ -30,14 +30,26 @@ enable-active-high; }; - reg_sensors: regulator-sensors { + reg_peri_3v3: regulator-peri-3v3 { compatible = "regulator-fixed"; pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_sensors_reg>; - regulator-name = "sensors-supply"; + pinctrl-0 = <&pinctrl_peri_3v3>; + regulator-name = "VPERI_3V3"; regulator-min-microvolt = <3300000>; regulator-max-microvolt = <3300000>; gpio = <&gpio5 2 GPIO_ACTIVE_LOW>; + /* + * If you want to want to make this dynamic please + * check schematics and test all affected peripherals: + * + * - sensors + * - ethernet phy + * - can + * - bluetooth + * - wm8960 audio codec + * - ov5640 camera + */ + regulator-always-on; }; reg_can_3v3: regulator-can-3v3 { @@ -140,6 +152,7 @@ pinctrl-0 = <&pinctrl_enet1>; phy-mode = "rmii"; phy-handle = <ðphy0>; + phy-supply = <®_peri_3v3>; status = "okay"; }; @@ -148,6 +161,7 @@ pinctrl-0 = <&pinctrl_enet2>; phy-mode = "rmii"; phy-handle = <ðphy1>; + phy-supply = <®_peri_3v3>; status = "okay"; mdio { @@ -193,8 +207,8 @@ magnetometer@e { compatible = "fsl,mag3110"; reg = <0x0e>; - vdd-supply = <®_sensors>; - vddio-supply = <®_sensors>; + vdd-supply = <®_peri_3v3>; + vddio-supply = <®_peri_3v3>; }; }; @@ -462,7 +476,7 @@ >; }; - pinctrl_sensors_reg: sensorsreggrp { + pinctrl_peri_3v3: peri3v3grp { fsl,pins = < MX6UL_PAD_SNVS_TAMPER2__GPIO5_IO02 0x1b0b0 >; -- GitLab From 46db63abb79524209c15c683feccfba116746757 Mon Sep 17 00:00:00 2001 From: Leonard Crestez Date: Tue, 10 Dec 2019 19:34:20 +0200 Subject: [PATCH 0521/1437] ARM: imx_v6_v7_defconfig: Explicitly restore CONFIG_DEBUG_FS This is currently off and that's not desirable: default imx config is meant to be generally useful for development and debugging. Running git bisect between v5.4 and v5.5-rc1 finds this started from commit 0e4a459f56c3 ("tracing: Remove unnecessary DEBUG_FS dependency") Explicit CONFIG_DEBUG_FS=y was earlier removed by commit c29d541f590c ("ARM: imx_v6_v7_defconfig: Remove unneeded options") A very similar fix was required before: commit 7e9eb6268809 ("ARM: imx_v6_v7_defconfig: Explicitly restore CONFIG_DEBUG_FS") Signed-off-by: Leonard Crestez Signed-off-by: Shawn Guo --- arch/arm/configs/imx_v6_v7_defconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm/configs/imx_v6_v7_defconfig b/arch/arm/configs/imx_v6_v7_defconfig index 26d6dee67aa6d..3608e55eaecdf 100644 --- a/arch/arm/configs/imx_v6_v7_defconfig +++ b/arch/arm/configs/imx_v6_v7_defconfig @@ -462,6 +462,7 @@ CONFIG_FONT_8x8=y CONFIG_FONT_8x16=y CONFIG_PRINTK_TIME=y CONFIG_MAGIC_SYSRQ=y +CONFIG_DEBUG_FS=y # CONFIG_SCHED_DEBUG is not set CONFIG_PROVE_LOCKING=y # CONFIG_DEBUG_BUGVERBOSE is not set -- GitLab From 7947e3238b64c00c396e6f5738f94c4d653bc6a2 Mon Sep 17 00:00:00 2001 From: Leonard Crestez Date: Tue, 10 Dec 2019 23:49:28 +0200 Subject: [PATCH 0522/1437] ARM: imx: Fix boot crash if ocotp is not found The imx_soc_device_init functions tries to fetch the ocotp regmap in order to soc serial number. If regmap fetch fails then a message is printed but regmap_read is called anyway and the system crashes. Failing to lookup ocotp regmap shouldn't be a fatal boot error so check that the pointer is valid. Only side-effect of ocotp lookup failure now is that serial number will be reported as all-zeros which is acceptable. Cc: stable@vger.kernel.org Fixes: 8267ff89b713 ("ARM: imx: Add serial number support for i.MX6/7 SoCs") Signed-off-by: Leonard Crestez Tested-by: Christoph Niedermaier Signed-off-by: Shawn Guo --- arch/arm/mach-imx/cpu.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/arch/arm/mach-imx/cpu.c b/arch/arm/mach-imx/cpu.c index 0fb0c4a264c31..871f98342d50e 100644 --- a/arch/arm/mach-imx/cpu.c +++ b/arch/arm/mach-imx/cpu.c @@ -84,7 +84,7 @@ struct device * __init imx_soc_device_init(void) const char *ocotp_compat = NULL; struct soc_device *soc_dev; struct device_node *root; - struct regmap *ocotp; + struct regmap *ocotp = NULL; const char *soc_id; u64 soc_uid = 0; u32 val; @@ -175,7 +175,9 @@ struct device * __init imx_soc_device_init(void) ocotp = syscon_regmap_lookup_by_compatible(ocotp_compat); if (IS_ERR(ocotp)) pr_err("%s: failed to find %s regmap!\n", __func__, ocotp_compat); + } + if (!IS_ERR_OR_NULL(ocotp)) { regmap_read(ocotp, OCOTP_UID_H, &val); soc_uid = val; regmap_read(ocotp, OCOTP_UID_L, &val); -- GitLab From 84faa307249b341f6ad8de3e1869d77a65e26669 Mon Sep 17 00:00:00 2001 From: "Jason A. Donenfeld" Date: Wed, 11 Dec 2019 10:26:39 +0100 Subject: [PATCH 0523/1437] crypto: arm/curve25519 - add arch-specific key generation function Somehow this was forgotten when Zinc was being split into oddly shaped pieces, resulting in linker errors. The x86_64 glue has a specific key generation implementation, but the Arm one does not. However, it can still receive the NEON speedups by calling the ordinary DH function using the base point. Signed-off-by: Jason A. Donenfeld Acked-by: Ard Biesheuvel Signed-off-by: Herbert Xu --- arch/arm/crypto/curve25519-glue.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/arch/arm/crypto/curve25519-glue.c b/arch/arm/crypto/curve25519-glue.c index f3f42cf3b8937..776ae07e04697 100644 --- a/arch/arm/crypto/curve25519-glue.c +++ b/arch/arm/crypto/curve25519-glue.c @@ -38,6 +38,13 @@ void curve25519_arch(u8 out[CURVE25519_KEY_SIZE], } EXPORT_SYMBOL(curve25519_arch); +void curve25519_base_arch(u8 pub[CURVE25519_KEY_SIZE], + const u8 secret[CURVE25519_KEY_SIZE]) +{ + return curve25519_arch(pub, secret, curve25519_base_point); +} +EXPORT_SYMBOL(curve25519_base_arch); + static int curve25519_set_secret(struct crypto_kpp *tfm, const void *buf, unsigned int len) { -- GitLab From e32ea127d81c12882f39c2783d78634597ff21a2 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Thu, 12 Dec 2019 16:34:03 +0800 Subject: [PATCH 0524/1437] nios2: Fix ioremap Commit 5ace77e0b41a ("nios2: remove __ioremap") removed the following code, with the argument that cacheflag is always 0 and the expression would therefore always be false. if (IS_MAPPABLE_UNCACHEABLE(phys_addr) && IS_MAPPABLE_UNCACHEABLE(last_addr) && !(cacheflag & _PAGE_CACHED)) return (void __iomem *)(CONFIG_NIOS2_IO_REGION_BASE + phys_addr); This did not take the "!" in the expression into account. Result is that nios2 images no longer boot. Restoring the removed code fixes the problem. Fixes: 5ace77e0b41a ("nios2: remove __ioremap") Cc: Christoph Hellwig Signed-off-by: Guenter Roeck Reviewed-by: Christoph Hellwig Signed-off-by: Ley Foon Tan --- arch/nios2/mm/ioremap.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/arch/nios2/mm/ioremap.c b/arch/nios2/mm/ioremap.c index b56af759dcdfc..819bdfcc2e714 100644 --- a/arch/nios2/mm/ioremap.c +++ b/arch/nios2/mm/ioremap.c @@ -138,6 +138,14 @@ void __iomem *ioremap(unsigned long phys_addr, unsigned long size) return NULL; } + /* + * Map uncached objects in the low part of address space to + * CONFIG_NIOS2_IO_REGION_BASE + */ + if (IS_MAPPABLE_UNCACHEABLE(phys_addr) && + IS_MAPPABLE_UNCACHEABLE(last_addr)) + return (void __iomem *)(CONFIG_NIOS2_IO_REGION_BASE + phys_addr); + /* Mappings have to be page-aligned */ offset = phys_addr & ~PAGE_MASK; phys_addr &= PAGE_MASK; -- GitLab From e27da6a6a4da9e5dd711e64cfd9d13ec3cc39d27 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Thu, 12 Dec 2019 09:53:29 +0200 Subject: [PATCH 0525/1437] interconnect: qcom: Fix Kconfig indentation Adjust indentation from spaces to tab (+optional two spaces) as in coding style with command like: $ sed -e 's/^ /\t/' -i */Kconfig Signed-off-by: Krzysztof Kozlowski Signed-off-by: Georgi Djakov Link: https://lore.kernel.org/r/20191212075332.16202-2-georgi.djakov@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/interconnect/qcom/Kconfig | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/interconnect/qcom/Kconfig b/drivers/interconnect/qcom/Kconfig index c49afbea3458a..2f9304d1db496 100644 --- a/drivers/interconnect/qcom/Kconfig +++ b/drivers/interconnect/qcom/Kconfig @@ -6,13 +6,13 @@ config INTERCONNECT_QCOM Support for Qualcomm's Network-on-Chip interconnect hardware. config INTERCONNECT_QCOM_MSM8974 - tristate "Qualcomm MSM8974 interconnect driver" - depends on INTERCONNECT_QCOM - depends on QCOM_SMD_RPM - select INTERCONNECT_QCOM_SMD_RPM - help - This is a driver for the Qualcomm Network-on-Chip on msm8974-based - platforms. + tristate "Qualcomm MSM8974 interconnect driver" + depends on INTERCONNECT_QCOM + depends on QCOM_SMD_RPM + select INTERCONNECT_QCOM_SMD_RPM + help + This is a driver for the Qualcomm Network-on-Chip on msm8974-based + platforms. config INTERCONNECT_QCOM_QCS404 tristate "Qualcomm QCS404 interconnect driver" -- GitLab From b29b8113bb41285eb7ed55ce0c65017b5c0240f7 Mon Sep 17 00:00:00 2001 From: Georgi Djakov Date: Thu, 12 Dec 2019 09:53:30 +0200 Subject: [PATCH 0526/1437] interconnect: qcom: sdm845: Walk the list safely on node removal As we will remove items off the list using list_del(), we need to use the safe version of list_for_each_entry(). Fixes: b5d2f741077a ("interconnect: qcom: Add sdm845 interconnect provider driver") Reported-by: Dmitry Osipenko Reviewed-by: Bjorn Andersson Signed-off-by: Georgi Djakov Cc: # v5.3+ Link: https://lore.kernel.org/r/20191212075332.16202-3-georgi.djakov@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/interconnect/qcom/sdm845.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/interconnect/qcom/sdm845.c b/drivers/interconnect/qcom/sdm845.c index 502a6c22b41ee..387267ee96485 100644 --- a/drivers/interconnect/qcom/sdm845.c +++ b/drivers/interconnect/qcom/sdm845.c @@ -868,9 +868,9 @@ static int qnoc_remove(struct platform_device *pdev) { struct qcom_icc_provider *qp = platform_get_drvdata(pdev); struct icc_provider *provider = &qp->provider; - struct icc_node *n; + struct icc_node *n, *tmp; - list_for_each_entry(n, &provider->nodes, node_list) { + list_for_each_entry_safe(n, tmp, &provider->nodes, node_list) { icc_node_del(n); icc_node_destroy(n->id); } -- GitLab From f39488ea2a75c49634c8611090f58734f61eee7c Mon Sep 17 00:00:00 2001 From: Georgi Djakov Date: Thu, 12 Dec 2019 09:53:31 +0200 Subject: [PATCH 0527/1437] interconnect: qcom: qcs404: Walk the list safely on node removal As we will remove items off the list using list_del(), we need to use the safe version of list_for_each_entry(). Fixes: 5e4e6c4d3ae0 ("interconnect: qcom: Add QCS404 interconnect provider driver") Reported-by: Dmitry Osipenko Reviewed-by: Bjorn Andersson Signed-off-by: Georgi Djakov Cc: # v5.4 Link: https://lore.kernel.org/r/20191212075332.16202-4-georgi.djakov@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/interconnect/qcom/qcs404.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/interconnect/qcom/qcs404.c b/drivers/interconnect/qcom/qcs404.c index b4966d8f3348d..8e0735a870400 100644 --- a/drivers/interconnect/qcom/qcs404.c +++ b/drivers/interconnect/qcom/qcs404.c @@ -414,7 +414,7 @@ static int qnoc_probe(struct platform_device *pdev) struct icc_provider *provider; struct qcom_icc_node **qnodes; struct qcom_icc_provider *qp; - struct icc_node *node; + struct icc_node *node, *tmp; size_t num_nodes, i; int ret; @@ -494,7 +494,7 @@ static int qnoc_probe(struct platform_device *pdev) return 0; err: - list_for_each_entry(node, &provider->nodes, node_list) { + list_for_each_entry_safe(node, tmp, &provider->nodes, node_list) { icc_node_del(node); icc_node_destroy(node->id); } @@ -508,9 +508,9 @@ static int qnoc_remove(struct platform_device *pdev) { struct qcom_icc_provider *qp = platform_get_drvdata(pdev); struct icc_provider *provider = &qp->provider; - struct icc_node *n; + struct icc_node *n, *tmp; - list_for_each_entry(n, &provider->nodes, node_list) { + list_for_each_entry_safe(n, tmp, &provider->nodes, node_list) { icc_node_del(n); icc_node_destroy(n->id); } -- GitLab From 3e42fe5c73108a6e68c98bc0df937888e35af97f Mon Sep 17 00:00:00 2001 From: Georgi Djakov Date: Thu, 12 Dec 2019 09:53:32 +0200 Subject: [PATCH 0528/1437] interconnect: qcom: msm8974: Walk the list safely on node removal As we will remove items off the list using list_del(), we need to use the safe version of list_for_each_entry(). Fixes: 4e60a9568dc6 ("interconnect: qcom: add msm8974 driver") Reported-by: Dmitry Osipenko Reviewed-by: Bjorn Andersson Signed-off-by: Georgi Djakov Link: https://lore.kernel.org/r/20191212075332.16202-5-georgi.djakov@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/interconnect/qcom/msm8974.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/interconnect/qcom/msm8974.c b/drivers/interconnect/qcom/msm8974.c index ce599a0c83d95..bf8bd1aee358d 100644 --- a/drivers/interconnect/qcom/msm8974.c +++ b/drivers/interconnect/qcom/msm8974.c @@ -652,7 +652,7 @@ static int msm8974_icc_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct icc_onecell_data *data; struct icc_provider *provider; - struct icc_node *node; + struct icc_node *node, *tmp; size_t num_nodes, i; int ret; @@ -732,7 +732,7 @@ static int msm8974_icc_probe(struct platform_device *pdev) return 0; err_del_icc: - list_for_each_entry(node, &provider->nodes, node_list) { + list_for_each_entry_safe(node, tmp, &provider->nodes, node_list) { icc_node_del(node); icc_node_destroy(node->id); } @@ -748,9 +748,9 @@ static int msm8974_icc_remove(struct platform_device *pdev) { struct msm8974_icc_provider *qp = platform_get_drvdata(pdev); struct icc_provider *provider = &qp->provider; - struct icc_node *n; + struct icc_node *n, *tmp; - list_for_each_entry(n, &provider->nodes, node_list) { + list_for_each_entry_safe(n, tmp, &provider->nodes, node_list) { icc_node_del(n); icc_node_destroy(n->id); } -- GitLab From 1ce74e96c2407df2b5867e5d45a70aacb8923c14 Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Thu, 12 Dec 2019 09:40:49 +0000 Subject: [PATCH 0529/1437] KVM: arm64: Ensure 'params' is initialised when looking up sys register MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit 4b927b94d5df ("KVM: arm/arm64: vgic: Introduce find_reg_by_id()") introduced 'find_reg_by_id()', which looks up a system register only if the 'id' index parameter identifies a valid system register. As part of the patch, existing callers of 'find_reg()' were ported over to the new interface, but this breaks 'index_to_sys_reg_desc()' in the case that the initial lookup in the vCPU target table fails because we will then call into 'find_reg()' for the system register table with an uninitialised 'param' as the key to the lookup. GCC 10 is bright enough to spot this (amongst a tonne of false positives, but hey!): | arch/arm64/kvm/sys_regs.c: In function ‘index_to_sys_reg_desc.part.0.isra’: | arch/arm64/kvm/sys_regs.c:983:33: warning: ‘params.Op2’ may be used uninitialized in this function [-Wmaybe-uninitialized] | 983 | (u32)(x)->CRn, (u32)(x)->CRm, (u32)(x)->Op2); | [...] Revert the hunk of 4b927b94d5df which breaks 'index_to_sys_reg_desc()' so that the old behaviour of checking the index upfront is restored. Fixes: 4b927b94d5df ("KVM: arm/arm64: vgic: Introduce find_reg_by_id()") Signed-off-by: Will Deacon Signed-off-by: Marc Zyngier Cc: Link: https://lore.kernel.org/r/20191212094049.12437-1-will@kernel.org --- arch/arm64/kvm/sys_regs.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c index bd2ac3796d8d0..d78b726d47222 100644 --- a/arch/arm64/kvm/sys_regs.c +++ b/arch/arm64/kvm/sys_regs.c @@ -2364,8 +2364,11 @@ static const struct sys_reg_desc *index_to_sys_reg_desc(struct kvm_vcpu *vcpu, if ((id & KVM_REG_ARM_COPROC_MASK) != KVM_REG_ARM64_SYSREG) return NULL; + if (!index_to_params(id, ¶ms)) + return NULL; + table = get_target_table(vcpu->arch.target, true, &num); - r = find_reg_by_id(id, ¶ms, table, num); + r = find_reg(¶ms, table, num); if (!r) r = find_reg(¶ms, sys_reg_descs, ARRAY_SIZE(sys_reg_descs)); -- GitLab From 3c3caae4cd6e122472efcf64759ff6392fb6bce2 Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Thu, 12 Dec 2019 12:37:13 +0300 Subject: [PATCH 0530/1437] usb: dwc3: pci: add ID for the Intel Comet Lake -H variant The original ID that was added for Comet Lake PCH was actually for the -LP (low power) variant even though the constant for it said CMLH. Changing that while at it. Signed-off-by: Heikki Krogerus Acked-by: Felipe Balbi Cc: stable Link: https://lore.kernel.org/r/20191212093713.60614-1-heikki.krogerus@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/dwc3/dwc3-pci.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/usb/dwc3/dwc3-pci.c b/drivers/usb/dwc3/dwc3-pci.c index 023f0357efd77..294276f7deb9e 100644 --- a/drivers/usb/dwc3/dwc3-pci.c +++ b/drivers/usb/dwc3/dwc3-pci.c @@ -29,7 +29,8 @@ #define PCI_DEVICE_ID_INTEL_BXT_M 0x1aaa #define PCI_DEVICE_ID_INTEL_APL 0x5aaa #define PCI_DEVICE_ID_INTEL_KBP 0xa2b0 -#define PCI_DEVICE_ID_INTEL_CMLH 0x02ee +#define PCI_DEVICE_ID_INTEL_CMLLP 0x02ee +#define PCI_DEVICE_ID_INTEL_CMLH 0x06ee #define PCI_DEVICE_ID_INTEL_GLK 0x31aa #define PCI_DEVICE_ID_INTEL_CNPLP 0x9dee #define PCI_DEVICE_ID_INTEL_CNPH 0xa36e @@ -308,6 +309,9 @@ static const struct pci_device_id dwc3_pci_id_table[] = { { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_MRFLD), (kernel_ulong_t) &dwc3_pci_mrfld_properties, }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_CMLLP), + (kernel_ulong_t) &dwc3_pci_intel_properties, }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_CMLH), (kernel_ulong_t) &dwc3_pci_intel_properties, }, -- GitLab From 0e4f7f920a5c6bfe5e851e989f27b35a0cc7fb7e Mon Sep 17 00:00:00 2001 From: Leo Yan Date: Wed, 27 Nov 2019 22:15:43 +0800 Subject: [PATCH 0531/1437] tty: serial: msm_serial: Fix lockup for sysrq and oops As the commit 677fe555cbfb ("serial: imx: Fix recursive locking bug") has mentioned the uart driver might cause recursive locking between normal printing and the kernel debugging facilities (e.g. sysrq and oops). In the commit it gave out suggestion for fixing recursive locking issue: "The solution is to avoid locking in the sysrq case and trylock in the oops_in_progress case." This patch follows the suggestion (also used the exactly same code with other serial drivers, e.g. amba-pl011.c) to fix the recursive locking issue, this can avoid stuck caused by deadlock and print out log for sysrq and oops. Fixes: 04896a77a97b ("msm_serial: serial driver for MSM7K onboard serial peripheral.") Signed-off-by: Leo Yan Reviewed-by: Jeffrey Hugo Link: https://lore.kernel.org/r/20191127141544.4277-2-leo.yan@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/msm_serial.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/drivers/tty/serial/msm_serial.c b/drivers/tty/serial/msm_serial.c index 1cbae0768b1fd..f6c45a796433d 100644 --- a/drivers/tty/serial/msm_serial.c +++ b/drivers/tty/serial/msm_serial.c @@ -1580,6 +1580,7 @@ static void __msm_console_write(struct uart_port *port, const char *s, int num_newlines = 0; bool replaced = false; void __iomem *tf; + int locked = 1; if (is_uartdm) tf = port->membase + UARTDM_TF; @@ -1592,7 +1593,13 @@ static void __msm_console_write(struct uart_port *port, const char *s, num_newlines++; count += num_newlines; - spin_lock(&port->lock); + if (port->sysrq) + locked = 0; + else if (oops_in_progress) + locked = spin_trylock(&port->lock); + else + spin_lock(&port->lock); + if (is_uartdm) msm_reset_dm_count(port, count); @@ -1628,7 +1635,9 @@ static void __msm_console_write(struct uart_port *port, const char *s, iowrite32_rep(tf, buf, 1); i += num_chars; } - spin_unlock(&port->lock); + + if (locked) + spin_unlock(&port->lock); } static void msm_console_write(struct console *co, const char *s, -- GitLab From abeb2e9414d7e3a0d8417bc3b13d7172513ea8a0 Mon Sep 17 00:00:00 2001 From: Yonghan Ye Date: Wed, 4 Dec 2019 20:00:07 +0800 Subject: [PATCH 0532/1437] serial: sprd: Add clearing break interrupt operation A break interrupt will be generated if the RX line was pulled low, which means some abnomal behaviors occurred of the UART. In this case, we still need to clear this break interrupt status, otherwise it will cause irq storm to crash the whole system. Fixes: b7396a38fb28 ("tty/serial: Add Spreadtrum sc9836-uart driver support") Signed-off-by: Yonghan Ye Cc: stable Signed-off-by: Baolin Wang Link: https://lore.kernel.org/r/925e51b73099c90158e080b8f5bed9b3b38c4548.1575460601.git.baolin.wang7@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sprd_serial.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/tty/serial/sprd_serial.c b/drivers/tty/serial/sprd_serial.c index 31df235025623..f60a59d9bf271 100644 --- a/drivers/tty/serial/sprd_serial.c +++ b/drivers/tty/serial/sprd_serial.c @@ -679,6 +679,9 @@ static irqreturn_t sprd_handle_irq(int irq, void *dev_id) if (ims & SPRD_IMSR_TIMEOUT) serial_out(port, SPRD_ICLR, SPRD_ICLR_TIMEOUT); + if (ims & SPRD_IMSR_BREAK_DETECT) + serial_out(port, SPRD_ICLR, SPRD_IMSR_BREAK_DETECT); + if (ims & (SPRD_IMSR_RX_FIFO_FULL | SPRD_IMSR_BREAK_DETECT | SPRD_IMSR_TIMEOUT)) sprd_rx(port); -- GitLab From 5e787dbf659fe77d56215be74044f85e01b3920f Mon Sep 17 00:00:00 2001 From: Dominik Brodowski Date: Tue, 23 Oct 2018 22:10:35 +0200 Subject: [PATCH 0533/1437] devtmpfs: use do_mount() instead of ksys_mount() In devtmpfs, do_mount() can be called directly instead of complex wrapping by ksys_mount(): - the first and third arguments are const strings in the kernel, and do not need to be copied over from userspace; - the fifth argument is NULL, and therefore no page needs to be copied over from userspace; - the second and fourth argument are passed through anyway. Signed-off-by: Dominik Brodowski --- drivers/base/devtmpfs.c | 6 +++--- include/linux/device.h | 4 ++-- init/do_mounts.c | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/base/devtmpfs.c b/drivers/base/devtmpfs.c index 30d0523014e0d..6cdbf15312387 100644 --- a/drivers/base/devtmpfs.c +++ b/drivers/base/devtmpfs.c @@ -359,7 +359,7 @@ static int handle_remove(const char *nodename, struct device *dev) * If configured, or requested by the commandline, devtmpfs will be * auto-mounted after the kernel mounted the root filesystem. */ -int devtmpfs_mount(const char *mntdir) +int devtmpfs_mount(void) { int err; @@ -369,7 +369,7 @@ int devtmpfs_mount(const char *mntdir) if (!thread) return 0; - err = ksys_mount("devtmpfs", mntdir, "devtmpfs", MS_SILENT, NULL); + err = do_mount("devtmpfs", "dev", "devtmpfs", MS_SILENT, NULL); if (err) printk(KERN_INFO "devtmpfs: error mounting %i\n", err); else @@ -394,7 +394,7 @@ static int devtmpfsd(void *p) *err = ksys_unshare(CLONE_NEWNS); if (*err) goto out; - *err = ksys_mount("devtmpfs", "/", "devtmpfs", MS_SILENT, NULL); + *err = do_mount("devtmpfs", "/", "devtmpfs", MS_SILENT, NULL); if (*err) goto out; ksys_chdir("/.."); /* will traverse into overmounted root */ diff --git a/include/linux/device.h b/include/linux/device.h index e226030c1df3c..96ff76731e93d 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -1666,11 +1666,11 @@ extern bool kill_device(struct device *dev); #ifdef CONFIG_DEVTMPFS extern int devtmpfs_create_node(struct device *dev); extern int devtmpfs_delete_node(struct device *dev); -extern int devtmpfs_mount(const char *mntdir); +extern int devtmpfs_mount(void); #else static inline int devtmpfs_create_node(struct device *dev) { return 0; } static inline int devtmpfs_delete_node(struct device *dev) { return 0; } -static inline int devtmpfs_mount(const char *mountpoint) { return 0; } +static inline int devtmpfs_mount(void) { return 0; } #endif /* drivers/base/power/shutdown.c */ diff --git a/init/do_mounts.c b/init/do_mounts.c index af9cda887a23f..43f6d098c8806 100644 --- a/init/do_mounts.c +++ b/init/do_mounts.c @@ -670,7 +670,7 @@ void __init prepare_namespace(void) mount_root(); out: - devtmpfs_mount("dev"); + devtmpfs_mount(); ksys_mount(".", "/", NULL, MS_MOVE, NULL); ksys_chroot("."); } -- GitLab From d4440aac83d12f87df9bcc51e992b9c28c7f4fa5 Mon Sep 17 00:00:00 2001 From: Dominik Brodowski Date: Wed, 27 Nov 2019 20:24:14 +0100 Subject: [PATCH 0534/1437] initrd: use do_mount() instead of ksys_mount() All three calls to ksys_mount() in initrd-related kernel code can be switched over to do_mount(): - the first and third arguments are const strings in the kernel, and do not need to be copied over from userspace; - the fifth argument is NULL, and therefore no page needs to be, copied over from userspace; - the second and fourth argument are passed through anyway. Signed-off-by: Dominik Brodowski --- init/do_mounts_initrd.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/init/do_mounts_initrd.c b/init/do_mounts_initrd.c index a9c6cc56f505e..3bf7b74153ab7 100644 --- a/init/do_mounts_initrd.c +++ b/init/do_mounts_initrd.c @@ -54,7 +54,7 @@ static int init_linuxrc(struct subprocess_info *info, struct cred *new) ksys_dup(0); /* move initrd over / and chdir/chroot in initrd root */ ksys_chdir("/root"); - ksys_mount(".", "/", NULL, MS_MOVE, NULL); + do_mount(".", "/", NULL, MS_MOVE, NULL); ksys_chroot("."); ksys_setsid(); return 0; @@ -89,7 +89,7 @@ static void __init handle_initrd(void) current->flags &= ~PF_FREEZER_SKIP; /* move initrd to rootfs' /old */ - ksys_mount("..", ".", NULL, MS_MOVE, NULL); + do_mount("..", ".", NULL, MS_MOVE, NULL); /* switch root and cwd back to / of rootfs */ ksys_chroot(".."); @@ -103,7 +103,7 @@ static void __init handle_initrd(void) mount_root(); printk(KERN_NOTICE "Trying to move old root to /initrd ... "); - error = ksys_mount("/old", "/root/initrd", NULL, MS_MOVE, NULL); + error = do_mount("/old", "/root/initrd", NULL, MS_MOVE, NULL); if (!error) printk("okay\n"); else { -- GitLab From cccaa5e33525fc07f4a2ce0518e50b9ddf435e47 Mon Sep 17 00:00:00 2001 From: Dominik Brodowski Date: Tue, 23 Oct 2018 22:41:09 +0200 Subject: [PATCH 0535/1437] init: use do_mount() instead of ksys_mount() In prepare_namespace(), do_mount() can be used instead of ksys_mount() as the first and third argument are const strings in the kernel, the second and fourth argument are passed through anyway, and the fifth argument is NULL. In do_mount_root(), ksys_mount() is called with the first and third argument being already kernelspace strings, which do not need to be copied over from userspace to kernelspace (again). The second and fourth arguments are passed through to do_mount() anyway. The fifth argument, while already residing in kernelspace, needs to be put into a page of its own. Then, do_mount() can be used instead of ksys_mount(). Once this is done, there are no in-kernel users to ksys_mount() left, which can therefore be removed. Signed-off-by: Dominik Brodowski --- fs/namespace.c | 10 ++-------- include/linux/syscalls.h | 2 -- init/do_mounts.c | 28 ++++++++++++++++++++++------ 3 files changed, 24 insertions(+), 16 deletions(-) diff --git a/fs/namespace.c b/fs/namespace.c index 2fd0c8bcb8c14..be601d3a80080 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -3325,8 +3325,8 @@ struct dentry *mount_subtree(struct vfsmount *m, const char *name) } EXPORT_SYMBOL(mount_subtree); -int ksys_mount(const char __user *dev_name, const char __user *dir_name, - const char __user *type, unsigned long flags, void __user *data) +SYSCALL_DEFINE5(mount, char __user *, dev_name, char __user *, dir_name, + char __user *, type, unsigned long, flags, void __user *, data) { int ret; char *kernel_type; @@ -3359,12 +3359,6 @@ out_type: return ret; } -SYSCALL_DEFINE5(mount, char __user *, dev_name, char __user *, dir_name, - char __user *, type, unsigned long, flags, void __user *, data) -{ - return ksys_mount(dev_name, dir_name, type, flags, data); -} - /* * Create a kernel mount representation for a new, prepared superblock * (specified by fs_fd) and attach to an open_tree-like file descriptor. diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h index d0391cc2dae90..5262b7a76d392 100644 --- a/include/linux/syscalls.h +++ b/include/linux/syscalls.h @@ -1231,8 +1231,6 @@ asmlinkage long sys_ni_syscall(void); * the ksys_xyzyyz() functions prototyped below. */ -int ksys_mount(const char __user *dev_name, const char __user *dir_name, - const char __user *type, unsigned long flags, void __user *data); int ksys_umount(char __user *name, int flags); int ksys_dup(unsigned int fildes); int ksys_chroot(const char __user *filename); diff --git a/init/do_mounts.c b/init/do_mounts.c index 43f6d098c8806..f55cbd9cb8183 100644 --- a/init/do_mounts.c +++ b/init/do_mounts.c @@ -387,12 +387,25 @@ static void __init get_fs_names(char *page) *s = '\0'; } -static int __init do_mount_root(char *name, char *fs, int flags, void *data) +static int __init do_mount_root(const char *name, const char *fs, + const int flags, const void *data) { struct super_block *s; - int err = ksys_mount(name, "/root", fs, flags, data); - if (err) - return err; + char *data_page; + struct page *p; + int ret; + + /* do_mount() requires a full page as fifth argument */ + p = alloc_page(GFP_KERNEL); + if (!p) + return -ENOMEM; + + data_page = page_address(p); + strncpy(data_page, data, PAGE_SIZE - 1); + + ret = do_mount(name, "/root", fs, flags, data_page); + if (ret) + goto out; ksys_chdir("/root"); s = current->fs->pwd.dentry->d_sb; @@ -402,7 +415,10 @@ static int __init do_mount_root(char *name, char *fs, int flags, void *data) s->s_type->name, sb_rdonly(s) ? " readonly" : "", MAJOR(ROOT_DEV), MINOR(ROOT_DEV)); - return 0; + +out: + put_page(p); + return ret; } void __init mount_block_root(char *name, int flags) @@ -671,7 +687,7 @@ void __init prepare_namespace(void) mount_root(); out: devtmpfs_mount(); - ksys_mount(".", "/", NULL, MS_MOVE, NULL); + do_mount(".", "/", NULL, MS_MOVE, NULL); ksys_chroot("."); } -- GitLab From a680aea00d170695d6542150a6f8841b5c2b8f1e Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Wed, 11 Dec 2019 11:39:35 -0500 Subject: [PATCH 0536/1437] Revert "drm/amdgpu: dont schedule jobs while in reset" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit f2efc6e60089c99c342a6b7da47f1037e06c4296. This was fixed properly for 5.5, but came back via 5.4 merge into drm-next, so revert it again. Reviewed-by: Christian König Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdgpu/amdgpu_ctx.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ctx.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ctx.c index 2cdaf3b2a7217..6614d8a6f4c8d 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ctx.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ctx.c @@ -604,11 +604,8 @@ void amdgpu_ctx_mgr_entity_fini(struct amdgpu_ctx_mgr *mgr) continue; } - for (i = 0; i < num_entities; i++) { - mutex_lock(&ctx->adev->lock_reset); + for (i = 0; i < num_entities; i++) drm_sched_entity_fini(&ctx->entities[0][i].entity); - mutex_unlock(&ctx->adev->lock_reset); - } } } -- GitLab From 413fc385a594ea6eb08843be33939057ddfdae76 Mon Sep 17 00:00:00 2001 From: changzhu Date: Tue, 10 Dec 2019 10:23:09 +0800 Subject: [PATCH 0537/1437] drm/amdgpu: avoid using invalidate semaphore for picasso It may cause timeout waiting for sem acquire in VM flush when using invalidate semaphore for picasso. So it needs to avoid using invalidate semaphore for piasso. Signed-off-by: changzhu Reviewed-by: Huang Rui Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdgpu/gmc_v9_0.c | 28 +++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/gmc_v9_0.c b/drivers/gpu/drm/amd/amdgpu/gmc_v9_0.c index 3c355fb5d2b47..d444cfb63af1f 100644 --- a/drivers/gpu/drm/amd/amdgpu/gmc_v9_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gmc_v9_0.c @@ -468,8 +468,11 @@ static void gmc_v9_0_flush_gpu_tlb(struct amdgpu_device *adev, uint32_t vmid, */ /* TODO: It needs to continue working on debugging with semaphore for GFXHUB as well. */ - if (vmhub == AMDGPU_MMHUB_0 || - vmhub == AMDGPU_MMHUB_1) { + if ((vmhub == AMDGPU_MMHUB_0 || + vmhub == AMDGPU_MMHUB_1) && + (!(adev->asic_type == CHIP_RAVEN && + adev->rev_id < 0x8 && + adev->pdev->device == 0x15d8))) { for (j = 0; j < adev->usec_timeout; j++) { /* a read return value of 1 means semaphore acuqire */ tmp = RREG32_NO_KIQ(hub->vm_inv_eng0_sem + eng); @@ -499,8 +502,11 @@ static void gmc_v9_0_flush_gpu_tlb(struct amdgpu_device *adev, uint32_t vmid, } /* TODO: It needs to continue working on debugging with semaphore for GFXHUB as well. */ - if (vmhub == AMDGPU_MMHUB_0 || - vmhub == AMDGPU_MMHUB_1) + if ((vmhub == AMDGPU_MMHUB_0 || + vmhub == AMDGPU_MMHUB_1) && + (!(adev->asic_type == CHIP_RAVEN && + adev->rev_id < 0x8 && + adev->pdev->device == 0x15d8))) /* * add semaphore release after invalidation, * write with 0 means semaphore release @@ -531,8 +537,11 @@ static uint64_t gmc_v9_0_emit_flush_gpu_tlb(struct amdgpu_ring *ring, */ /* TODO: It needs to continue working on debugging with semaphore for GFXHUB as well. */ - if (ring->funcs->vmhub == AMDGPU_MMHUB_0 || - ring->funcs->vmhub == AMDGPU_MMHUB_1) + if ((ring->funcs->vmhub == AMDGPU_MMHUB_0 || + ring->funcs->vmhub == AMDGPU_MMHUB_1) && + (!(adev->asic_type == CHIP_RAVEN && + adev->rev_id < 0x8 && + adev->pdev->device == 0x15d8))) /* a read return value of 1 means semaphore acuqire */ amdgpu_ring_emit_reg_wait(ring, hub->vm_inv_eng0_sem + eng, 0x1, 0x1); @@ -548,8 +557,11 @@ static uint64_t gmc_v9_0_emit_flush_gpu_tlb(struct amdgpu_ring *ring, req, 1 << vmid); /* TODO: It needs to continue working on debugging with semaphore for GFXHUB as well. */ - if (ring->funcs->vmhub == AMDGPU_MMHUB_0 || - ring->funcs->vmhub == AMDGPU_MMHUB_1) + if ((ring->funcs->vmhub == AMDGPU_MMHUB_0 || + ring->funcs->vmhub == AMDGPU_MMHUB_1) && + (!(adev->asic_type == CHIP_RAVEN && + adev->rev_id < 0x8 && + adev->pdev->device == 0x15d8))) /* * add semaphore release after invalidation, * write with 0 means semaphore release -- GitLab From ecb6186cf78803663cbd7c70241bfe1af439ca03 Mon Sep 17 00:00:00 2001 From: Logan Gunthorpe Date: Tue, 10 Dec 2019 11:47:04 -0700 Subject: [PATCH 0538/1437] block: fix NULL pointer dereference in account statistics with IDE The IDE driver creates some passthru requests which never get submitted to the block layer in such a way that blk_account_io_start() gets called. However, the driver still calls __blk_mq_end_request() in ide_end_rq() which will call blk_account_io_completion() which tries to dereferences req->part which is never set. See ide_prep_sense() for an example of where these requests come from. To fix this, blk_account_io_completion() and blk_account_io_done() should do nothing if req->part is not set. The back trace of this bug is: BUG: kernel NULL pointer dereference, address: 000002ac #PF: supervisor write access in kernel mode #PF: error_code(0x0002) - not-present page *pde = 00000000 Oops: 0002 [#1] CPU: 0 PID: 237 Comm: kworker/0:1H Not tainted 5.4.0-rc2-00011-g48d9b0d43105e #1 Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.10.2-1 04/01/2014 Workqueue: kblockd drive_rq_insert_work EIP: blk_account_io_completion+0x7a/0xf0 Code: 89 54 24 08 31 d2 89 4c 24 04 31 c9 c7 04 24 02 00 00 00 c1 ee 09 e8 f5 21 a6 ff e8 70 5c a7 ff 8b 53 60 8d 04 bd 00 00 00 00 <01> b4 02 ac 02 00 00 8b 9a 88 02 00 00 85 db 74 11 85 d2 74 51 8b EAX: 00000000 EBX: f5b80000 ECX: 00000000 EDX: 00000000 ESI: 00000000 EDI: 00000000 EBP: f3031e70 ESP: f3031e54 DS: 007b ES: 007b FS: 0000 GS: 0000 SS: 0068 EFLAGS: 00010046 CR0: 80050033 CR2: 000002ac CR3: 03c25000 CR4: 000406d0 Call Trace: blk_update_request+0x85/0x420 ide_end_rq+0x38/0xa0 ide_complete_rq+0x3d/0x70 cdrom_newpc_intr+0x258/0xba0 ide_intr+0x135/0x250 __handle_irq_event_percpu+0x3e/0x250 handle_irq_event_percpu+0x1f/0x50 handle_irq_event+0x32/0x60 handle_level_irq+0x6c/0x110 handle_irq+0x72/0xa0 do_IRQ+0x45/0xad common_interrupt+0x115/0x11c Fixes: 48d9b0d43105 ("block: account statistics for passthrough requests") Reported-by: kernel test robot Signed-off-by: Logan Gunthorpe Signed-off-by: Jens Axboe --- block/blk-core.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/block/blk-core.c b/block/blk-core.c index a1e228752083f..68c309ce67352 100644 --- a/block/blk-core.c +++ b/block/blk-core.c @@ -1310,7 +1310,7 @@ EXPORT_SYMBOL_GPL(blk_rq_err_bytes); void blk_account_io_completion(struct request *req, unsigned int bytes) { - if (blk_do_io_stat(req)) { + if (req->part && blk_do_io_stat(req)) { const int sgrp = op_stat_group(req_op(req)); struct hd_struct *part; @@ -1328,7 +1328,8 @@ void blk_account_io_done(struct request *req, u64 now) * normal IO on queueing nor completion. Accounting the * containing request is enough. */ - if (blk_do_io_stat(req) && !(req->rq_flags & RQF_FLUSH_SEQ)) { + if (req->part && blk_do_io_stat(req) && + !(req->rq_flags & RQF_FLUSH_SEQ)) { const int sgrp = op_stat_group(req_op(req)); struct hd_struct *part; -- GitLab From eb45f2110b036e4e35d3f3aaee1c2ccf49d92425 Mon Sep 17 00:00:00 2001 From: Andrew Jeffery Date: Mon, 2 Dec 2019 15:31:10 +1030 Subject: [PATCH 0539/1437] pinctrl: aspeed-g6: Fix LPC/eSPI mux configuration Early revisions of the AST2600 datasheet are conflicted about the state of the LPC/eSPI strapping bit (SCU510[6]). Conversations with ASPEED determined that the reference pinmux configuration tables were in error and the SCU documentation contained the correct configuration. Update the driver to reflect the state described in the SCU documentation. Fixes: 2eda1cdec49f ("pinctrl: aspeed: Add AST2600 pinmux support") Signed-off-by: Andrew Jeffery Link: https://lore.kernel.org/r/20191202050110.15340-1-andrew@aj.id.au Signed-off-by: Linus Walleij --- drivers/pinctrl/aspeed/pinctrl-aspeed-g6.c | 24 ++++++++-------------- 1 file changed, 8 insertions(+), 16 deletions(-) diff --git a/drivers/pinctrl/aspeed/pinctrl-aspeed-g6.c b/drivers/pinctrl/aspeed/pinctrl-aspeed-g6.c index c6800d220920e..bb07024d22edc 100644 --- a/drivers/pinctrl/aspeed/pinctrl-aspeed-g6.c +++ b/drivers/pinctrl/aspeed/pinctrl-aspeed-g6.c @@ -1088,60 +1088,52 @@ SSSF_PIN_DECL(AF15, GPIOV7, LPCSMI, SIG_DESC_SET(SCU434, 15)); #define AB7 176 SIG_EXPR_LIST_DECL_SESG(AB7, LAD0, LPC, SIG_DESC_SET(SCU434, 16), - SIG_DESC_CLEAR(SCU510, 6)); -SIG_EXPR_LIST_DECL_SESG(AB7, ESPID0, ESPI, SIG_DESC_SET(SCU434, 16), SIG_DESC_SET(SCU510, 6)); +SIG_EXPR_LIST_DECL_SESG(AB7, ESPID0, ESPI, SIG_DESC_SET(SCU434, 16)); PIN_DECL_2(AB7, GPIOW0, LAD0, ESPID0); #define AB8 177 SIG_EXPR_LIST_DECL_SESG(AB8, LAD1, LPC, SIG_DESC_SET(SCU434, 17), - SIG_DESC_CLEAR(SCU510, 6)); -SIG_EXPR_LIST_DECL_SESG(AB8, ESPID1, ESPI, SIG_DESC_SET(SCU434, 17), SIG_DESC_SET(SCU510, 6)); +SIG_EXPR_LIST_DECL_SESG(AB8, ESPID1, ESPI, SIG_DESC_SET(SCU434, 17)); PIN_DECL_2(AB8, GPIOW1, LAD1, ESPID1); #define AC8 178 SIG_EXPR_LIST_DECL_SESG(AC8, LAD2, LPC, SIG_DESC_SET(SCU434, 18), - SIG_DESC_CLEAR(SCU510, 6)); -SIG_EXPR_LIST_DECL_SESG(AC8, ESPID2, ESPI, SIG_DESC_SET(SCU434, 18), SIG_DESC_SET(SCU510, 6)); +SIG_EXPR_LIST_DECL_SESG(AC8, ESPID2, ESPI, SIG_DESC_SET(SCU434, 18)); PIN_DECL_2(AC8, GPIOW2, LAD2, ESPID2); #define AC7 179 SIG_EXPR_LIST_DECL_SESG(AC7, LAD3, LPC, SIG_DESC_SET(SCU434, 19), - SIG_DESC_CLEAR(SCU510, 6)); -SIG_EXPR_LIST_DECL_SESG(AC7, ESPID3, ESPI, SIG_DESC_SET(SCU434, 19), SIG_DESC_SET(SCU510, 6)); +SIG_EXPR_LIST_DECL_SESG(AC7, ESPID3, ESPI, SIG_DESC_SET(SCU434, 19)); PIN_DECL_2(AC7, GPIOW3, LAD3, ESPID3); #define AE7 180 SIG_EXPR_LIST_DECL_SESG(AE7, LCLK, LPC, SIG_DESC_SET(SCU434, 20), - SIG_DESC_CLEAR(SCU510, 6)); -SIG_EXPR_LIST_DECL_SESG(AE7, ESPICK, ESPI, SIG_DESC_SET(SCU434, 20), SIG_DESC_SET(SCU510, 6)); +SIG_EXPR_LIST_DECL_SESG(AE7, ESPICK, ESPI, SIG_DESC_SET(SCU434, 20)); PIN_DECL_2(AE7, GPIOW4, LCLK, ESPICK); #define AF7 181 SIG_EXPR_LIST_DECL_SESG(AF7, LFRAME, LPC, SIG_DESC_SET(SCU434, 21), - SIG_DESC_CLEAR(SCU510, 6)); -SIG_EXPR_LIST_DECL_SESG(AF7, ESPICS, ESPI, SIG_DESC_SET(SCU434, 21), SIG_DESC_SET(SCU510, 6)); +SIG_EXPR_LIST_DECL_SESG(AF7, ESPICS, ESPI, SIG_DESC_SET(SCU434, 21)); PIN_DECL_2(AF7, GPIOW5, LFRAME, ESPICS); #define AD7 182 SIG_EXPR_LIST_DECL_SESG(AD7, LSIRQ, LSIRQ, SIG_DESC_SET(SCU434, 22), - SIG_DESC_CLEAR(SCU510, 6)); -SIG_EXPR_LIST_DECL_SESG(AD7, ESPIALT, ESPIALT, SIG_DESC_SET(SCU434, 22), SIG_DESC_SET(SCU510, 6)); +SIG_EXPR_LIST_DECL_SESG(AD7, ESPIALT, ESPIALT, SIG_DESC_SET(SCU434, 22)); PIN_DECL_2(AD7, GPIOW6, LSIRQ, ESPIALT); FUNC_GROUP_DECL(LSIRQ, AD7); FUNC_GROUP_DECL(ESPIALT, AD7); #define AD8 183 SIG_EXPR_LIST_DECL_SESG(AD8, LPCRST, LPC, SIG_DESC_SET(SCU434, 23), - SIG_DESC_CLEAR(SCU510, 6)); -SIG_EXPR_LIST_DECL_SESG(AD8, ESPIRST, ESPI, SIG_DESC_SET(SCU434, 23), SIG_DESC_SET(SCU510, 6)); +SIG_EXPR_LIST_DECL_SESG(AD8, ESPIRST, ESPI, SIG_DESC_SET(SCU434, 23)); PIN_DECL_2(AD8, GPIOW7, LPCRST, ESPIRST); FUNC_GROUP_DECL(LPC, AB7, AB8, AC8, AC7, AE7, AF7, AD8); -- GitLab From e709ed70d122e94cb426b1e1f905829eae19a009 Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Thu, 12 Dec 2019 08:20:10 -0800 Subject: [PATCH 0540/1437] bus: ti-sysc: Fix missing reset delay handling We have dts property for "ti,sysc-delay-us", and we're using it, but the wait after OCP softreset only happens if devices are probed in legacy mode. Let's add a delay after writing the OCP softreset when specified. Fixes: e0db94fe87da ("bus: ti-sysc: Make OCP reset work for sysstatus and sysconfig reset bits") Cc: Keerthy Signed-off-by: Tony Lindgren --- drivers/bus/ti-sysc.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/bus/ti-sysc.c b/drivers/bus/ti-sysc.c index 563707779e188..f4d1597df0a27 100644 --- a/drivers/bus/ti-sysc.c +++ b/drivers/bus/ti-sysc.c @@ -1584,6 +1584,10 @@ static int sysc_reset(struct sysc *ddata) sysc_val |= sysc_mask; sysc_write(ddata, sysc_offset, sysc_val); + if (ddata->cfg.srst_udelay) + usleep_range(ddata->cfg.srst_udelay, + ddata->cfg.srst_udelay * 2); + if (ddata->clk_enable_quirk) ddata->clk_enable_quirk(ddata); -- GitLab From 6d674e28f642e3ff676fbae2d8d1b872814d32b6 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Wed, 11 Dec 2019 16:56:48 +0000 Subject: [PATCH 0541/1437] KVM: arm/arm64: Properly handle faulting of device mappings A device mapping is normally always mapped at Stage-2, since there is very little gain in having it faulted in. Nonetheless, it is possible to end-up in a situation where the device mapping has been removed from Stage-2 (userspace munmaped the VFIO region, and the MMU notifier did its job), but present in a userspace mapping (userpace has mapped it back at the same address). In such a situation, the device mapping will be demand-paged as the guest performs memory accesses. This requires to be careful when dealing with mapping size, cache management, and to handle potential execution of a device mapping. Reported-by: Alexandru Elisei Signed-off-by: Marc Zyngier Tested-by: Alexandru Elisei Reviewed-by: James Morse Cc: stable@vger.kernel.org Link: https://lore.kernel.org/r/20191211165651.7889-2-maz@kernel.org --- virt/kvm/arm/mmu.c | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/virt/kvm/arm/mmu.c b/virt/kvm/arm/mmu.c index a48994af70b87..0b32a904a1bb3 100644 --- a/virt/kvm/arm/mmu.c +++ b/virt/kvm/arm/mmu.c @@ -38,6 +38,11 @@ static unsigned long io_map_base; #define KVM_S2PTE_FLAG_IS_IOMAP (1UL << 0) #define KVM_S2_FLAG_LOGGING_ACTIVE (1UL << 1) +static bool is_iomap(unsigned long flags) +{ + return flags & KVM_S2PTE_FLAG_IS_IOMAP; +} + static bool memslot_is_logging(struct kvm_memory_slot *memslot) { return memslot->dirty_bitmap && !(memslot->flags & KVM_MEM_READONLY); @@ -1698,6 +1703,7 @@ static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa, vma_pagesize = vma_kernel_pagesize(vma); if (logging_active || + (vma->vm_flags & VM_PFNMAP) || !fault_supports_stage2_huge_mapping(memslot, hva, vma_pagesize)) { force_pte = true; vma_pagesize = PAGE_SIZE; @@ -1760,6 +1766,9 @@ static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa, writable = false; } + if (exec_fault && is_iomap(flags)) + return -ENOEXEC; + spin_lock(&kvm->mmu_lock); if (mmu_notifier_retry(kvm, mmu_seq)) goto out_unlock; @@ -1781,7 +1790,7 @@ static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa, if (writable) kvm_set_pfn_dirty(pfn); - if (fault_status != FSC_PERM) + if (fault_status != FSC_PERM && !is_iomap(flags)) clean_dcache_guest_page(pfn, vma_pagesize); if (exec_fault) @@ -1948,9 +1957,8 @@ int kvm_handle_guest_abort(struct kvm_vcpu *vcpu, struct kvm_run *run) if (kvm_is_error_hva(hva) || (write_fault && !writable)) { if (is_iabt) { /* Prefetch Abort on I/O address */ - kvm_inject_pabt(vcpu, kvm_vcpu_get_hfar(vcpu)); - ret = 1; - goto out_unlock; + ret = -ENOEXEC; + goto out; } /* @@ -1992,6 +2000,11 @@ int kvm_handle_guest_abort(struct kvm_vcpu *vcpu, struct kvm_run *run) ret = user_mem_abort(vcpu, fault_ipa, memslot, hva, fault_status); if (ret == 0) ret = 1; +out: + if (ret == -ENOEXEC) { + kvm_inject_pabt(vcpu, kvm_vcpu_get_hfar(vcpu)); + ret = 1; + } out_unlock: srcu_read_unlock(&vcpu->kvm->srcu, idx); return ret; -- GitLab From 5addeae1bedc4c126b179f61e43e039bb373581f Mon Sep 17 00:00:00 2001 From: Guoqing Jiang Date: Thu, 12 Dec 2019 16:52:00 +0100 Subject: [PATCH 0542/1437] blk-cgroup: remove blkcg_drain_queue Since blk_drain_queue had already been removed, so this function is not needed anymore. Signed-off-by: Guoqing Jiang Signed-off-by: Jens Axboe --- block/blk-cgroup.c | 20 -------------------- include/linux/blk-cgroup.h | 2 -- 2 files changed, 22 deletions(-) diff --git a/block/blk-cgroup.c b/block/blk-cgroup.c index 708dea92dac8c..a229b94d53908 100644 --- a/block/blk-cgroup.c +++ b/block/blk-cgroup.c @@ -1061,26 +1061,6 @@ err_unlock: return PTR_ERR(blkg); } -/** - * blkcg_drain_queue - drain blkcg part of request_queue - * @q: request_queue to drain - * - * Called from blk_drain_queue(). Responsible for draining blkcg part. - */ -void blkcg_drain_queue(struct request_queue *q) -{ - lockdep_assert_held(&q->queue_lock); - - /* - * @q could be exiting and already have destroyed all blkgs as - * indicated by NULL root_blkg. If so, don't confuse policies. - */ - if (!q->root_blkg) - return; - - blk_throtl_drain(q); -} - /** * blkcg_exit_queue - exit and release blkcg part of request_queue * @q: request_queue being released diff --git a/include/linux/blk-cgroup.h b/include/linux/blk-cgroup.h index 19394c77ed995..e4a6949fd1716 100644 --- a/include/linux/blk-cgroup.h +++ b/include/linux/blk-cgroup.h @@ -188,7 +188,6 @@ struct blkcg_gq *__blkg_lookup_create(struct blkcg *blkcg, struct blkcg_gq *blkg_lookup_create(struct blkcg *blkcg, struct request_queue *q); int blkcg_init_queue(struct request_queue *q); -void blkcg_drain_queue(struct request_queue *q); void blkcg_exit_queue(struct request_queue *q); /* Blkio controller policy registration */ @@ -720,7 +719,6 @@ static inline struct blkcg_gq *blkg_lookup(struct blkcg *blkcg, void *key) { ret static inline struct blkcg_gq *blk_queue_root_blkg(struct request_queue *q) { return NULL; } static inline int blkcg_init_queue(struct request_queue *q) { return 0; } -static inline void blkcg_drain_queue(struct request_queue *q) { } static inline void blkcg_exit_queue(struct request_queue *q) { } static inline int blkcg_policy_register(struct blkcg_policy *pol) { return 0; } static inline void blkcg_policy_unregister(struct blkcg_policy *pol) { } -- GitLab From d4d8140176972fdb3f00bffc88e63af781de8d67 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 11 Dec 2019 11:30:50 +0100 Subject: [PATCH 0543/1437] cpuidle: Drop unnecessary type cast in cpuidle_poll_time() The data type of the target_residency_ns field in struct cpuidle_state is u64, so it does not need to be cast into u64. Get rid of the unnecessary type cast. Signed-off-by: Rafael J. Wysocki Acked-by: Daniel Lezcano Signed-off-by: Rafael J. Wysocki --- drivers/cpuidle/cpuidle.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/cpuidle/cpuidle.c b/drivers/cpuidle/cpuidle.c index 15877b4311438..33d19c8eb0272 100644 --- a/drivers/cpuidle/cpuidle.c +++ b/drivers/cpuidle/cpuidle.c @@ -381,7 +381,7 @@ u64 cpuidle_poll_time(struct cpuidle_driver *drv, if (dev->states_usage[i].disable) continue; - limit_ns = (u64)drv->states[i].target_residency_ns; + limit_ns = drv->states[i].target_residency_ns; break; } -- GitLab From 85572c2c4a45a541e880e087b5b17a48198b2416 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 11 Dec 2019 11:28:41 +0100 Subject: [PATCH 0544/1437] cpufreq: Avoid leaving stale IRQ work items during CPU offline The scheduler code calling cpufreq_update_util() may run during CPU offline on the target CPU after the IRQ work lists have been flushed for it, so the target CPU should be prevented from running code that may queue up an IRQ work item on it at that point. Unfortunately, that may not be the case if dvfs_possible_from_any_cpu is set for at least one cpufreq policy in the system, because that allows the CPU going offline to run the utilization update callback of the cpufreq governor on behalf of another (online) CPU in some cases. If that happens, the cpufreq governor callback may queue up an IRQ work on the CPU running it, which is going offline, and the IRQ work may not be flushed after that point. Moreover, that IRQ work cannot be flushed until the "offlining" CPU goes back online, so if any other CPU calls irq_work_sync() to wait for the completion of that IRQ work, it will have to wait until the "offlining" CPU is back online and that may not happen forever. In particular, a system-wide deadlock may occur during CPU online as a result of that. The failing scenario is as follows. CPU0 is the boot CPU, so it creates a cpufreq policy and becomes the "leader" of it (policy->cpu). It cannot go offline, because it is the boot CPU. Next, other CPUs join the cpufreq policy as they go online and they leave it when they go offline. The last CPU to go offline, say CPU3, may queue up an IRQ work while running the governor callback on behalf of CPU0 after leaving the cpufreq policy because of the dvfs_possible_from_any_cpu effect described above. Then, CPU0 is the only online CPU in the system and the stale IRQ work is still queued on CPU3. When, say, CPU1 goes back online, it will run irq_work_sync() to wait for that IRQ work to complete and so it will wait for CPU3 to go back online (which may never happen even in principle), but (worse yet) CPU0 is waiting for CPU1 at that point too and a system-wide deadlock occurs. To address this problem notice that CPUs which cannot run cpufreq utilization update code for themselves (for example, because they have left the cpufreq policies that they belonged to), should also be prevented from running that code on behalf of the other CPUs that belong to a cpufreq policy with dvfs_possible_from_any_cpu set and so in that case the cpufreq_update_util_data pointer of the CPU running the code must not be NULL as well as for the CPU which is the target of the cpufreq utilization update in progress. Accordingly, change cpufreq_this_cpu_can_update() into a regular function in kernel/sched/cpufreq.c (instead of a static inline in a header file) and make it check the cpufreq_update_util_data pointer of the local CPU if dvfs_possible_from_any_cpu is set for the target cpufreq policy. Also update the schedutil governor to do the cpufreq_this_cpu_can_update() check in the non-fast-switch case too to avoid the stale IRQ work issues. Fixes: 99d14d0e16fa ("cpufreq: Process remote callbacks from any CPU if the platform permits") Link: https://lore.kernel.org/linux-pm/20191121093557.bycvdo4xyinbc5cb@vireshk-i7/ Reported-by: Anson Huang Tested-by: Anson Huang Cc: 4.14+ # 4.14+ Signed-off-by: Rafael J. Wysocki Acked-by: Viresh Kumar Tested-by: Peng Fan (i.MX8QXP-MEK) Signed-off-by: Rafael J. Wysocki --- include/linux/cpufreq.h | 11 ----------- include/linux/sched/cpufreq.h | 3 +++ kernel/sched/cpufreq.c | 18 ++++++++++++++++++ kernel/sched/cpufreq_schedutil.c | 8 +++----- 4 files changed, 24 insertions(+), 16 deletions(-) diff --git a/include/linux/cpufreq.h b/include/linux/cpufreq.h index 92d5fdc8154ee..31b1b0e03df8c 100644 --- a/include/linux/cpufreq.h +++ b/include/linux/cpufreq.h @@ -595,17 +595,6 @@ struct governor_attr { size_t count); }; -static inline bool cpufreq_this_cpu_can_update(struct cpufreq_policy *policy) -{ - /* - * Allow remote callbacks if: - * - dvfs_possible_from_any_cpu flag is set - * - the local and remote CPUs share cpufreq policy - */ - return policy->dvfs_possible_from_any_cpu || - cpumask_test_cpu(smp_processor_id(), policy->cpus); -} - /********************************************************************* * FREQUENCY TABLE HELPERS * *********************************************************************/ diff --git a/include/linux/sched/cpufreq.h b/include/linux/sched/cpufreq.h index afa940cd50dc5..cc6bcc1e96bc6 100644 --- a/include/linux/sched/cpufreq.h +++ b/include/linux/sched/cpufreq.h @@ -12,6 +12,8 @@ #define SCHED_CPUFREQ_MIGRATION (1U << 1) #ifdef CONFIG_CPU_FREQ +struct cpufreq_policy; + struct update_util_data { void (*func)(struct update_util_data *data, u64 time, unsigned int flags); }; @@ -20,6 +22,7 @@ void cpufreq_add_update_util_hook(int cpu, struct update_util_data *data, void (*func)(struct update_util_data *data, u64 time, unsigned int flags)); void cpufreq_remove_update_util_hook(int cpu); +bool cpufreq_this_cpu_can_update(struct cpufreq_policy *policy); static inline unsigned long map_util_freq(unsigned long util, unsigned long freq, unsigned long cap) diff --git a/kernel/sched/cpufreq.c b/kernel/sched/cpufreq.c index b5dcd1d83c7fa..7c2fe50fd76dd 100644 --- a/kernel/sched/cpufreq.c +++ b/kernel/sched/cpufreq.c @@ -5,6 +5,8 @@ * Copyright (C) 2016, Intel Corporation * Author: Rafael J. Wysocki */ +#include + #include "sched.h" DEFINE_PER_CPU(struct update_util_data __rcu *, cpufreq_update_util_data); @@ -57,3 +59,19 @@ void cpufreq_remove_update_util_hook(int cpu) rcu_assign_pointer(per_cpu(cpufreq_update_util_data, cpu), NULL); } EXPORT_SYMBOL_GPL(cpufreq_remove_update_util_hook); + +/** + * cpufreq_this_cpu_can_update - Check if cpufreq policy can be updated. + * @policy: cpufreq policy to check. + * + * Return 'true' if: + * - the local and remote CPUs share @policy, + * - dvfs_possible_from_any_cpu is set in @policy and the local CPU is not going + * offline (in which case it is not expected to run cpufreq updates any more). + */ +bool cpufreq_this_cpu_can_update(struct cpufreq_policy *policy) +{ + return cpumask_test_cpu(smp_processor_id(), policy->cpus) || + (policy->dvfs_possible_from_any_cpu && + rcu_dereference_sched(*this_cpu_ptr(&cpufreq_update_util_data))); +} diff --git a/kernel/sched/cpufreq_schedutil.c b/kernel/sched/cpufreq_schedutil.c index 322ca8860f548..9b8916fd00a2a 100644 --- a/kernel/sched/cpufreq_schedutil.c +++ b/kernel/sched/cpufreq_schedutil.c @@ -82,12 +82,10 @@ static bool sugov_should_update_freq(struct sugov_policy *sg_policy, u64 time) * by the hardware, as calculating the frequency is pointless if * we cannot in fact act on it. * - * For the slow switching platforms, the kthread is always scheduled on - * the right set of CPUs and any CPU can find the next frequency and - * schedule the kthread. + * This is needed on the slow switching platforms too to prevent CPUs + * going offline from leaving stale IRQ work items behind. */ - if (sg_policy->policy->fast_switch_enabled && - !cpufreq_this_cpu_can_update(sg_policy->policy)) + if (!cpufreq_this_cpu_can_update(sg_policy->policy)) return false; if (unlikely(sg_policy->limits_changed)) { -- GitLab From b49a733d684e0096340b93e9dfd471f0e3ddc06d Mon Sep 17 00:00:00 2001 From: Dominik Brodowski Date: Tue, 23 Oct 2018 16:00:10 +0200 Subject: [PATCH 0545/1437] init: unify opening /dev/console as stdin/stdout/stderr Merge the two instances where /dev/console is opened as stdin/stdout/stderr. Signed-off-by: Dominik Brodowski --- include/linux/initrd.h | 2 ++ init/do_mounts_initrd.c | 5 +---- init/main.c | 17 ++++++++++++----- 3 files changed, 15 insertions(+), 9 deletions(-) diff --git a/include/linux/initrd.h b/include/linux/initrd.h index d77fe34fb00a0..aa59143557286 100644 --- a/include/linux/initrd.h +++ b/include/linux/initrd.h @@ -28,3 +28,5 @@ extern unsigned int real_root_dev; extern char __initramfs_start[]; extern unsigned long __initramfs_size; + +void console_on_rootfs(void); diff --git a/init/do_mounts_initrd.c b/init/do_mounts_initrd.c index 3bf7b74153ab7..dab8b1151b569 100644 --- a/init/do_mounts_initrd.c +++ b/init/do_mounts_initrd.c @@ -48,10 +48,7 @@ early_param("initrd", early_initrd); static int init_linuxrc(struct subprocess_info *info, struct cred *new) { ksys_unshare(CLONE_FS | CLONE_FILES); - /* stdin/stdout/stderr for /linuxrc */ - ksys_open("/dev/console", O_RDWR, 0); - ksys_dup(0); - ksys_dup(0); + console_on_rootfs(); /* move initrd over / and chdir/chroot in initrd root */ ksys_chdir("/root"); do_mount(".", "/", NULL, MS_MOVE, NULL); diff --git a/init/main.c b/init/main.c index 91f6ebb30ef04..2cd736059416f 100644 --- a/init/main.c +++ b/init/main.c @@ -1155,6 +1155,17 @@ static int __ref kernel_init(void *unused) "See Linux Documentation/admin-guide/init.rst for guidance."); } +void console_on_rootfs(void) +{ + /* Open the /dev/console as stdin, this should never fail */ + if (ksys_open((const char __user *) "/dev/console", O_RDWR, 0) < 0) + pr_err("Warning: unable to open an initial console.\n"); + + /* create stdout/stderr */ + (void) ksys_dup(0); + (void) ksys_dup(0); +} + static noinline void __init kernel_init_freeable(void) { /* @@ -1190,12 +1201,8 @@ static noinline void __init kernel_init_freeable(void) do_basic_setup(); - /* Open the /dev/console on the rootfs, this should never fail */ - if (ksys_open((const char __user *) "/dev/console", O_RDWR, 0) < 0) - pr_err("Warning: unable to open an initial console.\n"); + console_on_rootfs(); - (void) ksys_dup(0); - (void) ksys_dup(0); /* * check if there is an early userspace init. If yes, let it do all * the work -- GitLab From 8243186f0cc7c57cf9d6a110cd7315c44e3e0be8 Mon Sep 17 00:00:00 2001 From: Dominik Brodowski Date: Tue, 23 Oct 2018 16:24:09 +0200 Subject: [PATCH 0546/1437] fs: remove ksys_dup() ksys_dup() is used only at one place in the kernel, namely to duplicate fd 0 of /dev/console to stdout and stderr. The same functionality can be achieved by using functions already available within the kernel namespace. Signed-off-by: Dominik Brodowski --- fs/file.c | 7 +------ include/linux/syscalls.h | 1 - init/main.c | 26 ++++++++++++++++++++------ 3 files changed, 21 insertions(+), 13 deletions(-) diff --git a/fs/file.c b/fs/file.c index 3da91a112babe..2f4fcf985079d 100644 --- a/fs/file.c +++ b/fs/file.c @@ -960,7 +960,7 @@ SYSCALL_DEFINE2(dup2, unsigned int, oldfd, unsigned int, newfd) return ksys_dup3(oldfd, newfd, 0); } -int ksys_dup(unsigned int fildes) +SYSCALL_DEFINE1(dup, unsigned int, fildes) { int ret = -EBADF; struct file *file = fget_raw(fildes); @@ -975,11 +975,6 @@ int ksys_dup(unsigned int fildes) return ret; } -SYSCALL_DEFINE1(dup, unsigned int, fildes) -{ - return ksys_dup(fildes); -} - int f_dupfd(unsigned int from, struct file *file, unsigned flags) { int err; diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h index 5262b7a76d392..2960dedcfde8a 100644 --- a/include/linux/syscalls.h +++ b/include/linux/syscalls.h @@ -1232,7 +1232,6 @@ asmlinkage long sys_ni_syscall(void); */ int ksys_umount(char __user *name, int flags); -int ksys_dup(unsigned int fildes); int ksys_chroot(const char __user *filename); ssize_t ksys_write(unsigned int fd, const char __user *buf, size_t count); int ksys_chdir(const char __user *filename); diff --git a/init/main.c b/init/main.c index 2cd736059416f..ec3a1463ac692 100644 --- a/init/main.c +++ b/init/main.c @@ -93,6 +93,7 @@ #include #include #include +#include #include #include @@ -1157,13 +1158,26 @@ static int __ref kernel_init(void *unused) void console_on_rootfs(void) { - /* Open the /dev/console as stdin, this should never fail */ - if (ksys_open((const char __user *) "/dev/console", O_RDWR, 0) < 0) - pr_err("Warning: unable to open an initial console.\n"); + struct file *file; + unsigned int i; + + /* Open /dev/console in kernelspace, this should never fail */ + file = filp_open("/dev/console", O_RDWR, 0); + if (!file) + goto err_out; + + /* create stdin/stdout/stderr, this should never fail */ + for (i = 0; i < 3; i++) { + if (f_dupfd(i, file, 0) != i) + goto err_out; + } + + return; - /* create stdout/stderr */ - (void) ksys_dup(0); - (void) ksys_dup(0); +err_out: + /* no panic -- this might not be fatal */ + pr_err("Warning: unable to open an initial console.\n"); + return; } static noinline void __init kernel_init_freeable(void) -- GitLab From 20032b63586ac6c28c936dff696981159913a13f Mon Sep 17 00:00:00 2001 From: Cristian Birsan Date: Thu, 12 Dec 2019 13:52:47 +0200 Subject: [PATCH 0547/1437] net: usb: lan78xx: Fix suspend/resume PHY register access error Lan78xx driver accesses the PHY registers through MDIO bus over USB connection. When performing a suspend/resume, the PHY registers can be accessed before the USB connection is resumed. This will generate an error and will prevent the device to resume correctly. This patch adds the dependency between the MDIO bus and USB device to allow correct handling of suspend/resume. Fixes: ce85e13ad6ef ("lan78xx: Update to use phylib instead of mii_if_info.") Signed-off-by: Cristian Birsan Signed-off-by: David S. Miller --- drivers/net/usb/lan78xx.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/usb/lan78xx.c b/drivers/net/usb/lan78xx.c index cf1f3f0a4b9be..d7bf1918ca625 100644 --- a/drivers/net/usb/lan78xx.c +++ b/drivers/net/usb/lan78xx.c @@ -1808,6 +1808,7 @@ static int lan78xx_mdio_init(struct lan78xx_net *dev) dev->mdiobus->read = lan78xx_mdiobus_read; dev->mdiobus->write = lan78xx_mdiobus_write; dev->mdiobus->name = "lan78xx-mdiobus"; + dev->mdiobus->parent = &dev->udev->dev; snprintf(dev->mdiobus->id, MII_BUS_ID_SIZE, "usb-%03d:%03d", dev->udev->bus->busnum, dev->udev->devnum); -- GitLab From 0af67e49b018e7280a4227bfe7b6005bc9d3e442 Mon Sep 17 00:00:00 2001 From: Manish Chopra Date: Thu, 12 Dec 2019 06:49:28 -0800 Subject: [PATCH 0548/1437] qede: Fix multicast mac configuration Driver doesn't accommodate the configuration for max number of multicast mac addresses, in such particular case it leaves the device with improper/invalid multicast configuration state, causing connectivity issues (in lacp bonding like scenarios). Signed-off-by: Manish Chopra Signed-off-by: Ariel Elior Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/qede/qede_filter.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/qlogic/qede/qede_filter.c b/drivers/net/ethernet/qlogic/qede/qede_filter.c index d6cfe4ffbaf3d..d1ce4531d01a2 100644 --- a/drivers/net/ethernet/qlogic/qede/qede_filter.c +++ b/drivers/net/ethernet/qlogic/qede/qede_filter.c @@ -1230,7 +1230,7 @@ qede_configure_mcast_filtering(struct net_device *ndev, netif_addr_lock_bh(ndev); mc_count = netdev_mc_count(ndev); - if (mc_count < 64) { + if (mc_count <= 64) { netdev_for_each_mc_addr(ha, ndev) { ether_addr_copy(temp, ha->addr); temp += ETH_ALEN; -- GitLab From 33df2f1929df4a1cb13303e344fbf8a75f0dc41f Mon Sep 17 00:00:00 2001 From: Mark Zhang Date: Thu, 12 Dec 2019 11:12:12 +0200 Subject: [PATCH 0549/1437] RDMA/counter: Prevent auto-binding a QP which are not tracked with res Some QPs (e.g. XRC QP) are not tracked in kernel, in this case they have an invalid res and should not be bound to any dynamically-allocated counter in auto mode. This fixes below call trace: BUG: kernel NULL pointer dereference, address: 0000000000000390 PGD 80000001a7233067 P4D 80000001a7233067 PUD 1a7215067 PMD 0 Oops: 0000 [#1] SMP PTI CPU: 2 PID: 24822 Comm: ibv_xsrq_pingpo Not tainted 5.4.0-rc5+ #21 Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.10.2-2.fc27 04/01/2014 RIP: 0010:rdma_counter_bind_qp_auto+0x142/0x270 [ib_core] Code: e1 48 85 c0 48 89 c2 0f 84 bc 00 00 00 49 8b 06 48 39 42 48 75 d6 40 3a aa 90 00 00 00 75 cd 49 8b 86 00 01 00 00 48 8b 4a 28 <8b> 80 90 03 00 00 39 81 90 03 00 00 75 b4 85 c0 74 b0 48 8b 04 24 RSP: 0018:ffffc900003f39c0 EFLAGS: 00010246 RAX: 0000000000000000 RBX: 0000000000000001 RCX: 0000000000000000 RDX: ffff88820020ec00 RSI: 0000000000000004 RDI: ffffffffffffffc0 RBP: 0000000000000001 R08: ffff888224149ff0 R09: ffffc900003f3968 R10: ffffffffffffffff R11: ffff8882249c5848 R12: ffffffffffffffff R13: ffff88821d5aca50 R14: ffff8881f7690800 R15: ffff8881ff890000 FS: 00007fe53a3e1740(0000) GS:ffff888237b00000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 0000000000000390 CR3: 00000001a7292006 CR4: 00000000003606a0 DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400 Call Trace: _ib_modify_qp+0x3a4/0x3f0 [ib_core] ? lookup_get_idr_uobject.part.8+0x23/0x40 [ib_uverbs] modify_qp+0x322/0x3e0 [ib_uverbs] ib_uverbs_modify_qp+0x43/0x70 [ib_uverbs] ib_uverbs_handler_UVERBS_METHOD_INVOKE_WRITE+0xb1/0xf0 [ib_uverbs] ib_uverbs_run_method+0x6be/0x760 [ib_uverbs] ? uverbs_disassociate_api+0xd0/0xd0 [ib_uverbs] ib_uverbs_cmd_verbs+0x18d/0x3a0 [ib_uverbs] ? get_acl+0x1a/0x120 ? __alloc_pages_nodemask+0x15d/0x2c0 ib_uverbs_ioctl+0xa7/0x110 [ib_uverbs] do_vfs_ioctl+0xa5/0x610 ksys_ioctl+0x60/0x90 __x64_sys_ioctl+0x16/0x20 do_syscall_64+0x48/0x110 entry_SYSCALL_64_after_hwframe+0x44/0xa9 Fixes: 99fa331dc862 ("RDMA/counter: Add "auto" configuration mode support") Signed-off-by: Mark Zhang Reviewed-by: Maor Gottlieb Reviewed-by: Ido Kalir Signed-off-by: Leon Romanovsky Link: https://lore.kernel.org/r/20191212091214.315005-2-leon@kernel.org Signed-off-by: Doug Ledford --- drivers/infiniband/core/counters.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/infiniband/core/counters.c b/drivers/infiniband/core/counters.c index 8434ec082c3ae..2257d7f7810fd 100644 --- a/drivers/infiniband/core/counters.c +++ b/drivers/infiniband/core/counters.c @@ -286,6 +286,9 @@ int rdma_counter_bind_qp_auto(struct ib_qp *qp, u8 port) struct rdma_counter *counter; int ret; + if (!qp->res.valid) + return 0; + if (!rdma_is_port_valid(dev, port)) return -EINVAL; -- GitLab From 89f988d93c62384758b19323c886db917a80c371 Mon Sep 17 00:00:00 2001 From: Parav Pandit Date: Thu, 12 Dec 2019 11:12:13 +0200 Subject: [PATCH 0550/1437] IB/mlx4: Follow mirror sequence of device add during device removal Current code device add sequence is: ib_register_device() ib_mad_init() init_sriov_init() register_netdev_notifier() Therefore, the remove sequence should be, unregister_netdev_notifier() close_sriov() mad_cleanup() ib_unregister_device() However it is not above. Hence, make do above remove sequence. Fixes: fa417f7b520ee ("IB/mlx4: Add support for IBoE") Signed-off-by: Parav Pandit Reviewed-by: Maor Gottlieb Signed-off-by: Leon Romanovsky Link: https://lore.kernel.org/r/20191212091214.315005-3-leon@kernel.org Signed-off-by: Doug Ledford --- drivers/infiniband/hw/mlx4/main.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/infiniband/hw/mlx4/main.c b/drivers/infiniband/hw/mlx4/main.c index 0b5dc1d5928f0..34055cbab38cf 100644 --- a/drivers/infiniband/hw/mlx4/main.c +++ b/drivers/infiniband/hw/mlx4/main.c @@ -3018,16 +3018,17 @@ static void mlx4_ib_remove(struct mlx4_dev *dev, void *ibdev_ptr) ibdev->ib_active = false; flush_workqueue(wq); - mlx4_ib_close_sriov(ibdev); - mlx4_ib_mad_cleanup(ibdev); - ib_unregister_device(&ibdev->ib_dev); - mlx4_ib_diag_cleanup(ibdev); if (ibdev->iboe.nb.notifier_call) { if (unregister_netdevice_notifier(&ibdev->iboe.nb)) pr_warn("failure unregistering notifier\n"); ibdev->iboe.nb.notifier_call = NULL; } + mlx4_ib_close_sriov(ibdev); + mlx4_ib_mad_cleanup(ibdev); + ib_unregister_device(&ibdev->ib_dev); + mlx4_ib_diag_cleanup(ibdev); + mlx4_qp_release_range(dev, ibdev->steer_qpn_base, ibdev->steer_qpn_count); kfree(ibdev->ib_uc_qpns_bitmap); -- GitLab From ed9085fed9d95d5921582e3c8474f3736c5d2782 Mon Sep 17 00:00:00 2001 From: Maor Gottlieb Date: Thu, 12 Dec 2019 11:12:14 +0200 Subject: [PATCH 0551/1437] IB/mlx5: Fix steering rule of drop and count There are two flow rule destinations: QP and packet. While users are setting DROP packet rule, the QP should not be set as a destination. Fixes: 3b3233fbf02e ("IB/mlx5: Add flow counters binding support") Signed-off-by: Maor Gottlieb Reviewed-by: Raed Salem Signed-off-by: Leon Romanovsky Link: https://lore.kernel.org/r/20191212091214.315005-4-leon@kernel.org Signed-off-by: Doug Ledford --- drivers/infiniband/hw/mlx5/main.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/drivers/infiniband/hw/mlx5/main.c b/drivers/infiniband/hw/mlx5/main.c index 51100350b6880..2f5a159cbe1c9 100644 --- a/drivers/infiniband/hw/mlx5/main.c +++ b/drivers/infiniband/hw/mlx5/main.c @@ -3544,10 +3544,6 @@ static struct mlx5_ib_flow_handler *_create_flow_rule(struct mlx5_ib_dev *dev, } INIT_LIST_HEAD(&handler->list); - if (dst) { - memcpy(&dest_arr[0], dst, sizeof(*dst)); - dest_num++; - } for (spec_index = 0; spec_index < flow_attr->num_of_specs; spec_index++) { err = parse_flow_attr(dev->mdev, spec, @@ -3560,6 +3556,11 @@ static struct mlx5_ib_flow_handler *_create_flow_rule(struct mlx5_ib_dev *dev, ib_flow += ((union ib_flow_spec *)ib_flow)->size; } + if (dst && !(flow_act.action & MLX5_FLOW_CONTEXT_ACTION_DROP)) { + memcpy(&dest_arr[0], dst, sizeof(*dst)); + dest_num++; + } + if (!flow_is_multicast_only(flow_attr)) set_underlay_qp(dev, spec, underlay_qpn); @@ -3600,10 +3601,8 @@ static struct mlx5_ib_flow_handler *_create_flow_rule(struct mlx5_ib_dev *dev, } if (flow_act.action & MLX5_FLOW_CONTEXT_ACTION_DROP) { - if (!(flow_act.action & MLX5_FLOW_CONTEXT_ACTION_COUNT)) { + if (!dest_num) rule_dst = NULL; - dest_num = 0; - } } else { if (is_egress) flow_act.action |= MLX5_FLOW_CONTEXT_ACTION_ALLOW; -- GitLab From 90f6452ca58d436de4f69b423ecd75a109aa9766 Mon Sep 17 00:00:00 2001 From: changzhu Date: Tue, 10 Dec 2019 22:00:59 +0800 Subject: [PATCH 0552/1437] drm/amdgpu: add invalidate semaphore limit for SRIOV and picasso in gmc9 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It may fail to load guest driver in round 2 or cause Xstart problem when using invalidate semaphore for SRIOV or picasso. So it needs avoid using invalidate semaphore for SRIOV and picasso. Signed-off-by: changzhu Reviewed-by: Christian König Reviewed-by: Huang Rui Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdgpu/gmc_v9_0.c | 44 +++++++++++++++------------ 1 file changed, 24 insertions(+), 20 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/gmc_v9_0.c b/drivers/gpu/drm/amd/amdgpu/gmc_v9_0.c index d444cfb63af1f..a5b68b5e452fb 100644 --- a/drivers/gpu/drm/amd/amdgpu/gmc_v9_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gmc_v9_0.c @@ -416,6 +416,24 @@ static uint32_t gmc_v9_0_get_invalidate_req(unsigned int vmid, return req; } +/** + * gmc_v9_0_use_invalidate_semaphore - judge whether to use semaphore + * + * @adev: amdgpu_device pointer + * @vmhub: vmhub type + * + */ +static bool gmc_v9_0_use_invalidate_semaphore(struct amdgpu_device *adev, + uint32_t vmhub) +{ + return ((vmhub == AMDGPU_MMHUB_0 || + vmhub == AMDGPU_MMHUB_1) && + (!amdgpu_sriov_vf(adev)) && + (!(adev->asic_type == CHIP_RAVEN && + adev->rev_id < 0x8 && + adev->pdev->device == 0x15d8))); +} + /* * GART * VMID 0 is the physical GPU addresses as used by the kernel. @@ -435,6 +453,7 @@ static uint32_t gmc_v9_0_get_invalidate_req(unsigned int vmid, static void gmc_v9_0_flush_gpu_tlb(struct amdgpu_device *adev, uint32_t vmid, uint32_t vmhub, uint32_t flush_type) { + bool use_semaphore = gmc_v9_0_use_invalidate_semaphore(adev, vmhub); const unsigned eng = 17; u32 j, tmp; struct amdgpu_vmhub *hub; @@ -468,11 +487,7 @@ static void gmc_v9_0_flush_gpu_tlb(struct amdgpu_device *adev, uint32_t vmid, */ /* TODO: It needs to continue working on debugging with semaphore for GFXHUB as well. */ - if ((vmhub == AMDGPU_MMHUB_0 || - vmhub == AMDGPU_MMHUB_1) && - (!(adev->asic_type == CHIP_RAVEN && - adev->rev_id < 0x8 && - adev->pdev->device == 0x15d8))) { + if (use_semaphore) { for (j = 0; j < adev->usec_timeout; j++) { /* a read return value of 1 means semaphore acuqire */ tmp = RREG32_NO_KIQ(hub->vm_inv_eng0_sem + eng); @@ -502,11 +517,7 @@ static void gmc_v9_0_flush_gpu_tlb(struct amdgpu_device *adev, uint32_t vmid, } /* TODO: It needs to continue working on debugging with semaphore for GFXHUB as well. */ - if ((vmhub == AMDGPU_MMHUB_0 || - vmhub == AMDGPU_MMHUB_1) && - (!(adev->asic_type == CHIP_RAVEN && - adev->rev_id < 0x8 && - adev->pdev->device == 0x15d8))) + if (use_semaphore) /* * add semaphore release after invalidation, * write with 0 means semaphore release @@ -524,6 +535,7 @@ static void gmc_v9_0_flush_gpu_tlb(struct amdgpu_device *adev, uint32_t vmid, static uint64_t gmc_v9_0_emit_flush_gpu_tlb(struct amdgpu_ring *ring, unsigned vmid, uint64_t pd_addr) { + bool use_semaphore = gmc_v9_0_use_invalidate_semaphore(ring->adev, ring->funcs->vmhub); struct amdgpu_device *adev = ring->adev; struct amdgpu_vmhub *hub = &adev->vmhub[ring->funcs->vmhub]; uint32_t req = gmc_v9_0_get_invalidate_req(vmid, 0); @@ -537,11 +549,7 @@ static uint64_t gmc_v9_0_emit_flush_gpu_tlb(struct amdgpu_ring *ring, */ /* TODO: It needs to continue working on debugging with semaphore for GFXHUB as well. */ - if ((ring->funcs->vmhub == AMDGPU_MMHUB_0 || - ring->funcs->vmhub == AMDGPU_MMHUB_1) && - (!(adev->asic_type == CHIP_RAVEN && - adev->rev_id < 0x8 && - adev->pdev->device == 0x15d8))) + if (use_semaphore) /* a read return value of 1 means semaphore acuqire */ amdgpu_ring_emit_reg_wait(ring, hub->vm_inv_eng0_sem + eng, 0x1, 0x1); @@ -557,11 +565,7 @@ static uint64_t gmc_v9_0_emit_flush_gpu_tlb(struct amdgpu_ring *ring, req, 1 << vmid); /* TODO: It needs to continue working on debugging with semaphore for GFXHUB as well. */ - if ((ring->funcs->vmhub == AMDGPU_MMHUB_0 || - ring->funcs->vmhub == AMDGPU_MMHUB_1) && - (!(adev->asic_type == CHIP_RAVEN && - adev->rev_id < 0x8 && - adev->pdev->device == 0x15d8))) + if (use_semaphore) /* * add semaphore release after invalidation, * write with 0 means semaphore release -- GitLab From f271fe1856138d402e0438f994ccae95f9044b2c Mon Sep 17 00:00:00 2001 From: changzhu Date: Tue, 10 Dec 2019 22:50:16 +0800 Subject: [PATCH 0553/1437] drm/amdgpu: add invalidate semaphore limit for SRIOV in gmc10 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It may fail to load guest driver in round 2 when using invalidate semaphore for SRIOV. So it needs to avoid using invalidate semaphore for SRIOV. Signed-off-by: changzhu Reviewed-by: Christian König Reviewed-by: Huang Rui Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdgpu/gmc_v10_0.c | 29 +++++++++++++++++++------- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/gmc_v10_0.c b/drivers/gpu/drm/amd/amdgpu/gmc_v10_0.c index 2324695074463..f5725336a5f26 100644 --- a/drivers/gpu/drm/amd/amdgpu/gmc_v10_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gmc_v10_0.c @@ -219,6 +219,21 @@ static uint32_t gmc_v10_0_get_invalidate_req(unsigned int vmid, return req; } +/** + * gmc_v10_0_use_invalidate_semaphore - judge whether to use semaphore + * + * @adev: amdgpu_device pointer + * @vmhub: vmhub type + * + */ +static bool gmc_v10_0_use_invalidate_semaphore(struct amdgpu_device *adev, + uint32_t vmhub) +{ + return ((vmhub == AMDGPU_MMHUB_0 || + vmhub == AMDGPU_MMHUB_1) && + (!amdgpu_sriov_vf(adev))); +} + /* * GART * VMID 0 is the physical GPU addresses as used by the kernel. @@ -229,6 +244,7 @@ static uint32_t gmc_v10_0_get_invalidate_req(unsigned int vmid, static void gmc_v10_0_flush_vm_hub(struct amdgpu_device *adev, uint32_t vmid, unsigned int vmhub, uint32_t flush_type) { + bool use_semaphore = gmc_v10_0_use_invalidate_semaphore(adev, vmhub); struct amdgpu_vmhub *hub = &adev->vmhub[vmhub]; u32 tmp = gmc_v10_0_get_invalidate_req(vmid, flush_type); /* Use register 17 for GART */ @@ -244,8 +260,7 @@ static void gmc_v10_0_flush_vm_hub(struct amdgpu_device *adev, uint32_t vmid, */ /* TODO: It needs to continue working on debugging with semaphore for GFXHUB as well. */ - if (vmhub == AMDGPU_MMHUB_0 || - vmhub == AMDGPU_MMHUB_1) { + if (use_semaphore) { for (i = 0; i < adev->usec_timeout; i++) { /* a read return value of 1 means semaphore acuqire */ tmp = RREG32_NO_KIQ(hub->vm_inv_eng0_sem + eng); @@ -278,8 +293,7 @@ static void gmc_v10_0_flush_vm_hub(struct amdgpu_device *adev, uint32_t vmid, } /* TODO: It needs to continue working on debugging with semaphore for GFXHUB as well. */ - if (vmhub == AMDGPU_MMHUB_0 || - vmhub == AMDGPU_MMHUB_1) + if (use_semaphore) /* * add semaphore release after invalidation, * write with 0 means semaphore release @@ -369,6 +383,7 @@ error_alloc: static uint64_t gmc_v10_0_emit_flush_gpu_tlb(struct amdgpu_ring *ring, unsigned vmid, uint64_t pd_addr) { + bool use_semaphore = gmc_v10_0_use_invalidate_semaphore(ring->adev, ring->funcs->vmhub); struct amdgpu_vmhub *hub = &ring->adev->vmhub[ring->funcs->vmhub]; uint32_t req = gmc_v10_0_get_invalidate_req(vmid, 0); unsigned eng = ring->vm_inv_eng; @@ -381,8 +396,7 @@ static uint64_t gmc_v10_0_emit_flush_gpu_tlb(struct amdgpu_ring *ring, */ /* TODO: It needs to continue working on debugging with semaphore for GFXHUB as well. */ - if (ring->funcs->vmhub == AMDGPU_MMHUB_0 || - ring->funcs->vmhub == AMDGPU_MMHUB_1) + if (use_semaphore) /* a read return value of 1 means semaphore acuqire */ amdgpu_ring_emit_reg_wait(ring, hub->vm_inv_eng0_sem + eng, 0x1, 0x1); @@ -398,8 +412,7 @@ static uint64_t gmc_v10_0_emit_flush_gpu_tlb(struct amdgpu_ring *ring, req, 1 << vmid); /* TODO: It needs to continue working on debugging with semaphore for GFXHUB as well. */ - if (ring->funcs->vmhub == AMDGPU_MMHUB_0 || - ring->funcs->vmhub == AMDGPU_MMHUB_1) + if (use_semaphore) /* * add semaphore release after invalidation, * write with 0 means semaphore release -- GitLab From ca01e7987463e8675f223c366e262e82f633481a Mon Sep 17 00:00:00 2001 From: Enric Balletbo i Serra Date: Wed, 11 Dec 2019 10:34:50 +0100 Subject: [PATCH 0554/1437] PCI: rockchip: Fix IO outbound ATU register number Since 62240a88004b ("PCI: rockchip: Drop storing driver private outbound resource data), the offset calculation is wrong to access the register number to program the IO outbound ATU. Fix this by computing the ATU IO register number based on the number of MEM registers, not the size of the IO region. This causes 'synchronous external aborts' like the following: mwifiex_pcie 0000:01:00.0: enabling device (0000 -> 0002) mwifiex_pcie: PCI memory map Virt0: 00000000a573ad00 PCI memory map Virt2: 00000000783126c4 Internal error: synchronous external abort: 96000210 [#1] PREEMPT SMP Modules linked in: mwifiex_pcie(+) mwifiex uvcvideo cfg80211 atmel_mxt_ts videobuf2_vmalloc ... CPU: 2 PID: 269 Comm: systemd-udevd Not tainted 5.4.0+ #327 Hardware name: Google Kevin (DT) pstate: 60000005 (nZCv daif -PAN -UAO) pc : mwifiex_register_dev+0x264/0x3f8 [mwifiex_pcie] lr : mwifiex_register_dev+0x150/0x3f8 [mwifiex_pcie] sp : ffff800012073860 x29: ffff800012073860 x28: ffff8000100a2e28 x27: ffff8000118b6210 x26: ffff800008f57458 x25: ffff0000ecfda000 x24: 0000000000000001 x23: ffff0000e9905080 x22: ffff800008f5d000 x21: ffff0000eecea078 x20: ffff0000e9905080 x19: ffff0000eecea000 x18: 0000000000000001 x17: 0000000000000000 x16: 0000000000000000 x15: ffffffffffffffff x14: ffff8000118998c8 x13: ffff000000000000 x12: 0000000000000008 x11: 0101010101010101 x10: ffff7f7fffff7fff x9 : 0000000000000000 x8 : ffff0000e3c24240 x7 : 0000000000000000 x6 : ffff0000e3c24148 x5 : ffff0000e3c24148 x4 : ffff0000e7975ec8 x3 : 0000000000000001 x2 : 0000000000002b42 x1 : ffff800012c00008 x0 : ffff0000e9905080 Call trace: mwifiex_register_dev+0x264/0x3f8 [mwifiex_pcie] mwifiex_add_card+0x2f8/0x430 [mwifiex] mwifiex_pcie_probe+0x98/0x148 [mwifiex_pcie] pci_device_probe+0x110/0x1a8 ... Code: a8c67bfd d65f03c0 f942ac01 91002021 (b9400021) Suggested-by: Lorenzo Pieralisi Fixes: 62240a88004b ("PCI: rockchip: Drop storing driver private outbound resource data) Link: https://lore.kernel.org/r/20191211093450.7481-1-enric.balletbo@collabora.com Reported-by: Enric Balletbo i Serra Reported-by: Vicente Bergas Tested-by: Vicente Bergas Signed-off-by: Enric Balletbo i Serra Signed-off-by: Bjorn Helgaas Acked-by: Andrew Murray --- drivers/pci/controller/pcie-rockchip-host.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/pci/controller/pcie-rockchip-host.c b/drivers/pci/controller/pcie-rockchip-host.c index d9b63bfa5dd78..94af6f5828a3d 100644 --- a/drivers/pci/controller/pcie-rockchip-host.c +++ b/drivers/pci/controller/pcie-rockchip-host.c @@ -834,10 +834,12 @@ static int rockchip_pcie_cfg_atu(struct rockchip_pcie *rockchip) if (!entry) return -ENODEV; + /* store the register number offset to program RC io outbound ATU */ + offset = size >> 20; + size = resource_size(entry->res); pci_addr = entry->res->start - entry->offset; - offset = size >> 20; for (reg_no = 0; reg_no < (size >> 20); reg_no++) { err = rockchip_pcie_prog_ob_atu(rockchip, reg_no + 1 + offset, -- GitLab From 601f0093f2647db67be40b62e13cd0660990a7c8 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Wed, 9 Oct 2019 16:04:43 -0700 Subject: [PATCH 0555/1437] KEYS: remove CONFIG_KEYS_COMPAT KEYS_COMPAT now always takes the value of COMPAT && KEYS. But the security/keys/ directory is only compiled if KEYS is enabled, so in practice KEYS_COMPAT is the same as COMPAT. Therefore, remove the unnecessary KEYS_COMPAT and just use COMPAT directly. (Also remove an outdated comment from compat.c.) Reviewed-by: James Morris Signed-off-by: Eric Biggers Reviewed-by: Jarkko Sakkinen Tested-by: Jarkko Sakkinen Signed-off-by: Jarkko Sakkinen --- security/keys/Kconfig | 4 ---- security/keys/Makefile | 2 +- security/keys/compat.c | 5 ----- security/keys/internal.h | 4 ++-- 4 files changed, 3 insertions(+), 12 deletions(-) diff --git a/security/keys/Kconfig b/security/keys/Kconfig index dd313438fecf9..47c041563d41c 100644 --- a/security/keys/Kconfig +++ b/security/keys/Kconfig @@ -21,10 +21,6 @@ config KEYS If you are unsure as to whether this is required, answer N. -config KEYS_COMPAT - def_bool y - depends on COMPAT && KEYS - config KEYS_REQUEST_CACHE bool "Enable temporary caching of the last request_key() result" depends on KEYS diff --git a/security/keys/Makefile b/security/keys/Makefile index 074f27538f559..5f40807f05b3d 100644 --- a/security/keys/Makefile +++ b/security/keys/Makefile @@ -17,7 +17,7 @@ obj-y := \ request_key_auth.o \ user_defined.o compat-obj-$(CONFIG_KEY_DH_OPERATIONS) += compat_dh.o -obj-$(CONFIG_KEYS_COMPAT) += compat.o $(compat-obj-y) +obj-$(CONFIG_COMPAT) += compat.o $(compat-obj-y) obj-$(CONFIG_PROC_FS) += proc.o obj-$(CONFIG_SYSCTL) += sysctl.o obj-$(CONFIG_PERSISTENT_KEYRINGS) += persistent.o diff --git a/security/keys/compat.c b/security/keys/compat.c index 9bcc404131aa0..b975f8f11124b 100644 --- a/security/keys/compat.c +++ b/security/keys/compat.c @@ -46,11 +46,6 @@ static long compat_keyctl_instantiate_key_iov( /* * The key control system call, 32-bit compatibility version for 64-bit archs - * - * This should only be called if the 64-bit arch uses weird pointers in 32-bit - * mode or doesn't guarantee that the top 32-bits of the argument registers on - * taking a 32-bit syscall are zero. If you can, you should call sys_keyctl() - * directly. */ COMPAT_SYSCALL_DEFINE5(keyctl, u32, option, u32, arg2, u32, arg3, u32, arg4, u32, arg5) diff --git a/security/keys/internal.h b/security/keys/internal.h index c039373488bd9..ba3e2da14ceff 100644 --- a/security/keys/internal.h +++ b/security/keys/internal.h @@ -264,7 +264,7 @@ extern long keyctl_dh_compute(struct keyctl_dh_params __user *, char __user *, size_t, struct keyctl_kdf_params __user *); extern long __keyctl_dh_compute(struct keyctl_dh_params __user *, char __user *, size_t, struct keyctl_kdf_params *); -#ifdef CONFIG_KEYS_COMPAT +#ifdef CONFIG_COMPAT extern long compat_keyctl_dh_compute(struct keyctl_dh_params __user *params, char __user *buffer, size_t buflen, struct compat_keyctl_kdf_params __user *kdf); @@ -279,7 +279,7 @@ static inline long keyctl_dh_compute(struct keyctl_dh_params __user *params, return -EOPNOTSUPP; } -#ifdef CONFIG_KEYS_COMPAT +#ifdef CONFIG_COMPAT static inline long compat_keyctl_dh_compute( struct keyctl_dh_params __user *params, char __user *buffer, size_t buflen, -- GitLab From bea37414453eb08d4ceffeb60a9d490dbc930cea Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Wed, 9 Oct 2019 16:03:49 -0700 Subject: [PATCH 0556/1437] KEYS: asymmetric: return ENOMEM if akcipher_request_alloc() fails No error code was being set on this error path. Cc: stable@vger.kernel.org Fixes: ad4b1eb5fb33 ("KEYS: asym_tpm: Implement encryption operation [ver #2]") Fixes: c08fed737126 ("KEYS: Implement encrypt, decrypt and sign for software asymmetric key [ver #2]") Reviewed-by: James Morris Signed-off-by: Eric Biggers Reviewed-by: Jarkko Sakkinen Signed-off-by: Jarkko Sakkinen --- crypto/asymmetric_keys/asym_tpm.c | 1 + crypto/asymmetric_keys/public_key.c | 1 + 2 files changed, 2 insertions(+) diff --git a/crypto/asymmetric_keys/asym_tpm.c b/crypto/asymmetric_keys/asym_tpm.c index d16d893bd1959..378b18b9bc342 100644 --- a/crypto/asymmetric_keys/asym_tpm.c +++ b/crypto/asymmetric_keys/asym_tpm.c @@ -470,6 +470,7 @@ static int tpm_key_encrypt(struct tpm_key *tk, if (ret < 0) goto error_free_tfm; + ret = -ENOMEM; req = akcipher_request_alloc(tfm, GFP_KERNEL); if (!req) goto error_free_tfm; diff --git a/crypto/asymmetric_keys/public_key.c b/crypto/asymmetric_keys/public_key.c index 364b9df9d631f..d7f43d4ea925a 100644 --- a/crypto/asymmetric_keys/public_key.c +++ b/crypto/asymmetric_keys/public_key.c @@ -184,6 +184,7 @@ static int software_key_eds_op(struct kernel_pkey_params *params, if (IS_ERR(tfm)) return PTR_ERR(tfm); + ret = -ENOMEM; req = akcipher_request_alloc(tfm, GFP_KERNEL); if (!req) goto error_free_tfm; -- GitLab From 679152d3a32e305c213f83160c328c37566ae8bc Mon Sep 17 00:00:00 2001 From: Andrii Nakryiko Date: Thu, 12 Dec 2019 09:19:18 -0800 Subject: [PATCH 0557/1437] libbpf: Fix printf compilation warnings on ppc64le arch On ppc64le __u64 and __s64 are defined as long int and unsigned long int, respectively. This causes compiler to emit warning when %lld/%llu are used to printf 64-bit numbers. Fix this by casting to size_t/ssize_t with %zu and %zd format specifiers, respectively. v1->v2: - use size_t/ssize_t instead of custom typedefs (Martin). Fixes: 1f8e2bcb2cd5 ("libbpf: Refactor relocation handling") Fixes: abd29c931459 ("libbpf: allow specifying map definitions using BTF") Signed-off-by: Andrii Nakryiko Signed-off-by: Alexei Starovoitov Acked-by: Martin KaFai Lau Link: https://lore.kernel.org/bpf/20191212171918.638010-1-andriin@fb.com --- tools/lib/bpf/libbpf.c | 37 +++++++++++++++++++------------------ 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c index 3f09772192f16..920d4e06a5f92 100644 --- a/tools/lib/bpf/libbpf.c +++ b/tools/lib/bpf/libbpf.c @@ -1242,15 +1242,15 @@ static int bpf_object__init_user_btf_map(struct bpf_object *obj, } sz = btf__resolve_size(obj->btf, t->type); if (sz < 0) { - pr_warn("map '%s': can't determine key size for type [%u]: %lld.\n", - map_name, t->type, sz); + pr_warn("map '%s': can't determine key size for type [%u]: %zd.\n", + map_name, t->type, (ssize_t)sz); return sz; } - pr_debug("map '%s': found key [%u], sz = %lld.\n", - map_name, t->type, sz); + pr_debug("map '%s': found key [%u], sz = %zd.\n", + map_name, t->type, (ssize_t)sz); if (map->def.key_size && map->def.key_size != sz) { - pr_warn("map '%s': conflicting key size %u != %lld.\n", - map_name, map->def.key_size, sz); + pr_warn("map '%s': conflicting key size %u != %zd.\n", + map_name, map->def.key_size, (ssize_t)sz); return -EINVAL; } map->def.key_size = sz; @@ -1285,15 +1285,15 @@ static int bpf_object__init_user_btf_map(struct bpf_object *obj, } sz = btf__resolve_size(obj->btf, t->type); if (sz < 0) { - pr_warn("map '%s': can't determine value size for type [%u]: %lld.\n", - map_name, t->type, sz); + pr_warn("map '%s': can't determine value size for type [%u]: %zd.\n", + map_name, t->type, (ssize_t)sz); return sz; } - pr_debug("map '%s': found value [%u], sz = %lld.\n", - map_name, t->type, sz); + pr_debug("map '%s': found value [%u], sz = %zd.\n", + map_name, t->type, (ssize_t)sz); if (map->def.value_size && map->def.value_size != sz) { - pr_warn("map '%s': conflicting value size %u != %lld.\n", - map_name, map->def.value_size, sz); + pr_warn("map '%s': conflicting value size %u != %zd.\n", + map_name, map->def.value_size, (ssize_t)sz); return -EINVAL; } map->def.value_size = sz; @@ -1817,7 +1817,8 @@ static int bpf_program__record_reloc(struct bpf_program *prog, return -LIBBPF_ERRNO__RELOC; } if (sym->st_value % 8) { - pr_warn("bad call relo offset: %llu\n", (__u64)sym->st_value); + pr_warn("bad call relo offset: %zu\n", + (size_t)sym->st_value); return -LIBBPF_ERRNO__RELOC; } reloc_desc->type = RELO_CALL; @@ -1859,8 +1860,8 @@ static int bpf_program__record_reloc(struct bpf_program *prog, break; } if (map_idx >= nr_maps) { - pr_warn("map relo failed to find map for sec %u, off %llu\n", - shdr_idx, (__u64)sym->st_value); + pr_warn("map relo failed to find map for sec %u, off %zu\n", + shdr_idx, (size_t)sym->st_value); return -LIBBPF_ERRNO__RELOC; } reloc_desc->type = RELO_LD64; @@ -1941,9 +1942,9 @@ bpf_program__collect_reloc(struct bpf_program *prog, GElf_Shdr *shdr, name = elf_strptr(obj->efile.elf, obj->efile.strtabidx, sym.st_name) ? : ""; - pr_debug("relo for shdr %u, symb %llu, value %llu, type %d, bind %d, name %d (\'%s\'), insn %u\n", - (__u32)sym.st_shndx, (__u64)GELF_R_SYM(rel.r_info), - (__u64)sym.st_value, GELF_ST_TYPE(sym.st_info), + pr_debug("relo for shdr %u, symb %zu, value %zu, type %d, bind %d, name %d (\'%s\'), insn %u\n", + (__u32)sym.st_shndx, (size_t)GELF_R_SYM(rel.r_info), + (size_t)sym.st_value, GELF_ST_TYPE(sym.st_info), GELF_ST_BIND(sym.st_info), sym.st_name, name, insn_idx); -- GitLab From 7a763d18ff2a75cfd1014800327f4120840bef09 Mon Sep 17 00:00:00 2001 From: Yishai Hadas Date: Thu, 12 Dec 2019 12:02:36 +0200 Subject: [PATCH 0558/1437] IB/core: Introduce rdma_user_mmap_entry_insert_range() API Introduce rdma_user_mmap_entry_insert_range() API to be used once the required key for the given entry should be in a given range. Signed-off-by: Yishai Hadas Signed-off-by: Leon Romanovsky Link: https://lore.kernel.org/r/20191212100237.330654-2-leon@kernel.org Signed-off-by: Doug Ledford --- drivers/infiniband/core/ib_core_uverbs.c | 48 +++++++++++++++++++----- include/rdma/ib_verbs.h | 5 +++ 2 files changed, 44 insertions(+), 9 deletions(-) diff --git a/drivers/infiniband/core/ib_core_uverbs.c b/drivers/infiniband/core/ib_core_uverbs.c index f509c478b469d..b7cb59844ece4 100644 --- a/drivers/infiniband/core/ib_core_uverbs.c +++ b/drivers/infiniband/core/ib_core_uverbs.c @@ -238,28 +238,32 @@ void rdma_user_mmap_entry_remove(struct rdma_user_mmap_entry *entry) EXPORT_SYMBOL(rdma_user_mmap_entry_remove); /** - * rdma_user_mmap_entry_insert() - Insert an entry to the mmap_xa + * rdma_user_mmap_entry_insert_range() - Insert an entry to the mmap_xa + * in a given range. * * @ucontext: associated user context. * @entry: the entry to insert into the mmap_xa * @length: length of the address that will be mmapped + * @min_pgoff: minimum pgoff to be returned + * @max_pgoff: maximum pgoff to be returned * * This function should be called by drivers that use the rdma_user_mmap * interface for implementing their mmap syscall A database of mmap offsets is * handled in the core and helper functions are provided to insert entries * into the database and extract entries when the user calls mmap with the - * given offset. The function allocates a unique page offset that should be - * provided to user, the user will use the offset to retrieve information such - * as address to be mapped and how. + * given offset. The function allocates a unique page offset in a given range + * that should be provided to user, the user will use the offset to retrieve + * information such as address to be mapped and how. * * Return: 0 on success and -ENOMEM on failure */ -int rdma_user_mmap_entry_insert(struct ib_ucontext *ucontext, - struct rdma_user_mmap_entry *entry, - size_t length) +int rdma_user_mmap_entry_insert_range(struct ib_ucontext *ucontext, + struct rdma_user_mmap_entry *entry, + size_t length, u32 min_pgoff, + u32 max_pgoff) { struct ib_uverbs_file *ufile = ucontext->ufile; - XA_STATE(xas, &ucontext->mmap_xa, 0); + XA_STATE(xas, &ucontext->mmap_xa, min_pgoff); u32 xa_first, xa_last, npages; int err; u32 i; @@ -285,7 +289,7 @@ int rdma_user_mmap_entry_insert(struct ib_ucontext *ucontext, entry->npages = npages; while (true) { /* First find an empty index */ - xas_find_marked(&xas, U32_MAX, XA_FREE_MARK); + xas_find_marked(&xas, max_pgoff, XA_FREE_MARK); if (xas.xa_node == XAS_RESTART) goto err_unlock; @@ -332,4 +336,30 @@ err_unlock: mutex_unlock(&ufile->umap_lock); return -ENOMEM; } +EXPORT_SYMBOL(rdma_user_mmap_entry_insert_range); + +/** + * rdma_user_mmap_entry_insert() - Insert an entry to the mmap_xa. + * + * @ucontext: associated user context. + * @entry: the entry to insert into the mmap_xa + * @length: length of the address that will be mmapped + * + * This function should be called by drivers that use the rdma_user_mmap + * interface for handling user mmapped addresses. The database is handled in + * the core and helper functions are provided to insert entries into the + * database and extract entries when the user calls mmap with the given offset. + * The function allocates a unique page offset that should be provided to user, + * the user will use the offset to retrieve information such as address to + * be mapped and how. + * + * Return: 0 on success and -ENOMEM on failure + */ +int rdma_user_mmap_entry_insert(struct ib_ucontext *ucontext, + struct rdma_user_mmap_entry *entry, + size_t length) +{ + return rdma_user_mmap_entry_insert_range(ucontext, entry, length, 0, + U32_MAX); +} EXPORT_SYMBOL(rdma_user_mmap_entry_insert); diff --git a/include/rdma/ib_verbs.h b/include/rdma/ib_verbs.h index cacb48faf6706..5608e14e3aadf 100644 --- a/include/rdma/ib_verbs.h +++ b/include/rdma/ib_verbs.h @@ -2832,6 +2832,11 @@ int rdma_user_mmap_io(struct ib_ucontext *ucontext, struct vm_area_struct *vma, int rdma_user_mmap_entry_insert(struct ib_ucontext *ucontext, struct rdma_user_mmap_entry *entry, size_t length); +int rdma_user_mmap_entry_insert_range(struct ib_ucontext *ucontext, + struct rdma_user_mmap_entry *entry, + size_t length, u32 min_pgoff, + u32 max_pgoff); + struct rdma_user_mmap_entry * rdma_user_mmap_entry_get_pgoff(struct ib_ucontext *ucontext, unsigned long pgoff); -- GitLab From dc2316eba73ff03da6dde082a372c6b5209304c5 Mon Sep 17 00:00:00 2001 From: Yishai Hadas Date: Thu, 12 Dec 2019 12:02:37 +0200 Subject: [PATCH 0559/1437] IB/mlx5: Fix device memory flows Fix device memory flows so that only once there will be no live mmaped VA to a given allocation the matching object will be destroyed. This prevents a potential scenario that existing VA that was mmaped by one process might still be used post its deallocation despite that it's owned now by other process. The above is achieved by integrating with IB core APIs to manage mmap/munmap. Only once the refcount will become 0 the DM object and its underlay area will be freed. Fixes: 3b113a1ec3d4 ("IB/mlx5: Support device memory type attribute") Signed-off-by: Yishai Hadas Signed-off-by: Leon Romanovsky Link: https://lore.kernel.org/r/20191212100237.330654-3-leon@kernel.org Signed-off-by: Doug Ledford --- drivers/infiniband/hw/mlx5/cmd.c | 16 ++-- drivers/infiniband/hw/mlx5/cmd.h | 2 +- drivers/infiniband/hw/mlx5/main.c | 120 ++++++++++++++++++--------- drivers/infiniband/hw/mlx5/mlx5_ib.h | 19 ++++- 4 files changed, 105 insertions(+), 52 deletions(-) diff --git a/drivers/infiniband/hw/mlx5/cmd.c b/drivers/infiniband/hw/mlx5/cmd.c index 4937947400cd0..4c26492ab8a32 100644 --- a/drivers/infiniband/hw/mlx5/cmd.c +++ b/drivers/infiniband/hw/mlx5/cmd.c @@ -157,7 +157,7 @@ int mlx5_cmd_alloc_memic(struct mlx5_dm *dm, phys_addr_t *addr, return -ENOMEM; } -int mlx5_cmd_dealloc_memic(struct mlx5_dm *dm, phys_addr_t addr, u64 length) +void mlx5_cmd_dealloc_memic(struct mlx5_dm *dm, phys_addr_t addr, u64 length) { struct mlx5_core_dev *dev = dm->dev; u64 hw_start_addr = MLX5_CAP64_DEV_MEM(dev, memic_bar_start_addr); @@ -175,15 +175,13 @@ int mlx5_cmd_dealloc_memic(struct mlx5_dm *dm, phys_addr_t addr, u64 length) MLX5_SET(dealloc_memic_in, in, memic_size, length); err = mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out)); + if (err) + return; - if (!err) { - spin_lock(&dm->lock); - bitmap_clear(dm->memic_alloc_pages, - start_page_idx, num_pages); - spin_unlock(&dm->lock); - } - - return err; + spin_lock(&dm->lock); + bitmap_clear(dm->memic_alloc_pages, + start_page_idx, num_pages); + spin_unlock(&dm->lock); } int mlx5_cmd_query_ext_ppcnt_counters(struct mlx5_core_dev *dev, void *out) diff --git a/drivers/infiniband/hw/mlx5/cmd.h b/drivers/infiniband/hw/mlx5/cmd.h index 169cab4915e3f..945ebce736134 100644 --- a/drivers/infiniband/hw/mlx5/cmd.h +++ b/drivers/infiniband/hw/mlx5/cmd.h @@ -46,7 +46,7 @@ int mlx5_cmd_modify_cong_params(struct mlx5_core_dev *mdev, void *in, int in_size); int mlx5_cmd_alloc_memic(struct mlx5_dm *dm, phys_addr_t *addr, u64 length, u32 alignment); -int mlx5_cmd_dealloc_memic(struct mlx5_dm *dm, phys_addr_t addr, u64 length); +void mlx5_cmd_dealloc_memic(struct mlx5_dm *dm, phys_addr_t addr, u64 length); void mlx5_cmd_dealloc_pd(struct mlx5_core_dev *dev, u32 pdn, u16 uid); void mlx5_cmd_destroy_tir(struct mlx5_core_dev *dev, u32 tirn, u16 uid); void mlx5_cmd_destroy_tis(struct mlx5_core_dev *dev, u32 tisn, u16 uid); diff --git a/drivers/infiniband/hw/mlx5/main.c b/drivers/infiniband/hw/mlx5/main.c index 2f5a159cbe1c9..997cbfe4b90ce 100644 --- a/drivers/infiniband/hw/mlx5/main.c +++ b/drivers/infiniband/hw/mlx5/main.c @@ -2074,6 +2074,24 @@ static int mlx5_ib_mmap_clock_info_page(struct mlx5_ib_dev *dev, virt_to_page(dev->mdev->clock_info)); } +static void mlx5_ib_mmap_free(struct rdma_user_mmap_entry *entry) +{ + struct mlx5_user_mmap_entry *mentry = to_mmmap(entry); + struct mlx5_ib_dev *dev = to_mdev(entry->ucontext->device); + struct mlx5_ib_dm *mdm; + + switch (mentry->mmap_flag) { + case MLX5_IB_MMAP_TYPE_MEMIC: + mdm = container_of(mentry, struct mlx5_ib_dm, mentry); + mlx5_cmd_dealloc_memic(&dev->dm, mdm->dev_addr, + mdm->size); + kfree(mdm); + break; + default: + WARN_ON(true); + } +} + static int uar_mmap(struct mlx5_ib_dev *dev, enum mlx5_ib_mmap_cmd cmd, struct vm_area_struct *vma, struct mlx5_ib_ucontext *context) @@ -2186,26 +2204,55 @@ free_bfreg: return err; } -static int dm_mmap(struct ib_ucontext *context, struct vm_area_struct *vma) +static int add_dm_mmap_entry(struct ib_ucontext *context, + struct mlx5_ib_dm *mdm, + u64 address) +{ + mdm->mentry.mmap_flag = MLX5_IB_MMAP_TYPE_MEMIC; + mdm->mentry.address = address; + return rdma_user_mmap_entry_insert_range( + context, &mdm->mentry.rdma_entry, + mdm->size, + MLX5_IB_MMAP_DEVICE_MEM << 16, + (MLX5_IB_MMAP_DEVICE_MEM << 16) + (1UL << 16) - 1); +} + +static unsigned long mlx5_vma_to_pgoff(struct vm_area_struct *vma) +{ + unsigned long idx; + u8 command; + + command = get_command(vma->vm_pgoff); + idx = get_extended_index(vma->vm_pgoff); + + return (command << 16 | idx); +} + +static int mlx5_ib_mmap_offset(struct mlx5_ib_dev *dev, + struct vm_area_struct *vma, + struct ib_ucontext *ucontext) { - struct mlx5_ib_ucontext *mctx = to_mucontext(context); - struct mlx5_ib_dev *dev = to_mdev(context->device); - u16 page_idx = get_extended_index(vma->vm_pgoff); - size_t map_size = vma->vm_end - vma->vm_start; - u32 npages = map_size >> PAGE_SHIFT; + struct mlx5_user_mmap_entry *mentry; + struct rdma_user_mmap_entry *entry; + unsigned long pgoff; + pgprot_t prot; phys_addr_t pfn; + int ret; - if (find_next_zero_bit(mctx->dm_pages, page_idx + npages, page_idx) != - page_idx + npages) + pgoff = mlx5_vma_to_pgoff(vma); + entry = rdma_user_mmap_entry_get_pgoff(ucontext, pgoff); + if (!entry) return -EINVAL; - pfn = ((dev->mdev->bar_addr + - MLX5_CAP64_DEV_MEM(dev->mdev, memic_bar_start_addr)) >> - PAGE_SHIFT) + - page_idx; - return rdma_user_mmap_io(context, vma, pfn, map_size, - pgprot_writecombine(vma->vm_page_prot), - NULL); + mentry = to_mmmap(entry); + pfn = (mentry->address >> PAGE_SHIFT); + prot = pgprot_writecombine(vma->vm_page_prot); + ret = rdma_user_mmap_io(ucontext, vma, pfn, + entry->npages * PAGE_SIZE, + prot, + entry); + rdma_user_mmap_entry_put(&mentry->rdma_entry); + return ret; } static int mlx5_ib_mmap(struct ib_ucontext *ibcontext, struct vm_area_struct *vma) @@ -2248,11 +2295,8 @@ static int mlx5_ib_mmap(struct ib_ucontext *ibcontext, struct vm_area_struct *vm case MLX5_IB_MMAP_CLOCK_INFO: return mlx5_ib_mmap_clock_info_page(dev, vma, context); - case MLX5_IB_MMAP_DEVICE_MEM: - return dm_mmap(ibcontext, vma); - default: - return -EINVAL; + return mlx5_ib_mmap_offset(dev, vma, ibcontext); } return 0; @@ -2288,8 +2332,9 @@ static int handle_alloc_dm_memic(struct ib_ucontext *ctx, { struct mlx5_dm *dm_db = &to_mdev(ctx->device)->dm; u64 start_offset; - u32 page_idx; + u16 page_idx; int err; + u64 address; dm->size = roundup(attr->length, MLX5_MEMIC_BASE_SIZE); @@ -2298,28 +2343,30 @@ static int handle_alloc_dm_memic(struct ib_ucontext *ctx, if (err) return err; - page_idx = (dm->dev_addr - pci_resource_start(dm_db->dev->pdev, 0) - - MLX5_CAP64_DEV_MEM(dm_db->dev, memic_bar_start_addr)) >> - PAGE_SHIFT; + address = dm->dev_addr & PAGE_MASK; + err = add_dm_mmap_entry(ctx, dm, address); + if (err) + goto err_dealloc; + page_idx = dm->mentry.rdma_entry.start_pgoff & 0xFFFF; err = uverbs_copy_to(attrs, MLX5_IB_ATTR_ALLOC_DM_RESP_PAGE_INDEX, - &page_idx, sizeof(page_idx)); + &page_idx, + sizeof(page_idx)); if (err) - goto err_dealloc; + goto err_copy; start_offset = dm->dev_addr & ~PAGE_MASK; err = uverbs_copy_to(attrs, MLX5_IB_ATTR_ALLOC_DM_RESP_START_OFFSET, &start_offset, sizeof(start_offset)); if (err) - goto err_dealloc; - - bitmap_set(to_mucontext(ctx)->dm_pages, page_idx, - DIV_ROUND_UP(dm->size, PAGE_SIZE)); + goto err_copy; return 0; +err_copy: + rdma_user_mmap_entry_remove(&dm->mentry.rdma_entry); err_dealloc: mlx5_cmd_dealloc_memic(dm_db, dm->dev_addr, dm->size); @@ -2423,23 +2470,13 @@ int mlx5_ib_dealloc_dm(struct ib_dm *ibdm, struct uverbs_attr_bundle *attrs) struct mlx5_ib_ucontext *ctx = rdma_udata_to_drv_context( &attrs->driver_udata, struct mlx5_ib_ucontext, ibucontext); struct mlx5_core_dev *dev = to_mdev(ibdm->device)->mdev; - struct mlx5_dm *dm_db = &to_mdev(ibdm->device)->dm; struct mlx5_ib_dm *dm = to_mdm(ibdm); - u32 page_idx; int ret; switch (dm->type) { case MLX5_IB_UAPI_DM_TYPE_MEMIC: - ret = mlx5_cmd_dealloc_memic(dm_db, dm->dev_addr, dm->size); - if (ret) - return ret; - - page_idx = (dm->dev_addr - pci_resource_start(dev->pdev, 0) - - MLX5_CAP64_DEV_MEM(dev, memic_bar_start_addr)) >> - PAGE_SHIFT; - bitmap_clear(ctx->dm_pages, page_idx, - DIV_ROUND_UP(dm->size, PAGE_SIZE)); - break; + rdma_user_mmap_entry_remove(&dm->mentry.rdma_entry); + return 0; case MLX5_IB_UAPI_DM_TYPE_STEERING_SW_ICM: ret = mlx5_dm_sw_icm_dealloc(dev, MLX5_SW_ICM_TYPE_STEERING, dm->size, ctx->devx_uid, dm->dev_addr, @@ -6235,6 +6272,7 @@ static const struct ib_device_ops mlx5_ib_dev_ops = { .map_mr_sg = mlx5_ib_map_mr_sg, .map_mr_sg_pi = mlx5_ib_map_mr_sg_pi, .mmap = mlx5_ib_mmap, + .mmap_free = mlx5_ib_mmap_free, .modify_cq = mlx5_ib_modify_cq, .modify_device = mlx5_ib_modify_device, .modify_port = mlx5_ib_modify_port, diff --git a/drivers/infiniband/hw/mlx5/mlx5_ib.h b/drivers/infiniband/hw/mlx5/mlx5_ib.h index 5986953ec2fac..b06f32ff5748f 100644 --- a/drivers/infiniband/hw/mlx5/mlx5_ib.h +++ b/drivers/infiniband/hw/mlx5/mlx5_ib.h @@ -118,6 +118,10 @@ enum { MLX5_MEMIC_BASE_SIZE = 1 << MLX5_MEMIC_BASE_ALIGN, }; +enum mlx5_ib_mmap_type { + MLX5_IB_MMAP_TYPE_MEMIC = 1, +}; + #define MLX5_LOG_SW_ICM_BLOCK_SIZE(dev) \ (MLX5_CAP_DEV_MEM(dev, log_sw_icm_alloc_granularity)) #define MLX5_SW_ICM_BLOCK_SIZE(dev) (1 << MLX5_LOG_SW_ICM_BLOCK_SIZE(dev)) @@ -135,7 +139,6 @@ struct mlx5_ib_ucontext { u32 tdn; u64 lib_caps; - DECLARE_BITMAP(dm_pages, MLX5_MAX_MEMIC_PAGES); u16 devx_uid; /* For RoCE LAG TX affinity */ atomic_t tx_port_affinity; @@ -556,6 +559,12 @@ enum mlx5_ib_mtt_access_flags { MLX5_IB_MTT_WRITE = (1 << 1), }; +struct mlx5_user_mmap_entry { + struct rdma_user_mmap_entry rdma_entry; + u8 mmap_flag; + u64 address; +}; + struct mlx5_ib_dm { struct ib_dm ibdm; phys_addr_t dev_addr; @@ -567,6 +576,7 @@ struct mlx5_ib_dm { } icm_dm; /* other dm types specific params should be added here */ }; + struct mlx5_user_mmap_entry mentry; }; #define MLX5_IB_MTT_PRESENT (MLX5_IB_MTT_READ | MLX5_IB_MTT_WRITE) @@ -1101,6 +1111,13 @@ to_mflow_act(struct ib_flow_action *ibact) return container_of(ibact, struct mlx5_ib_flow_action, ib_action); } +static inline struct mlx5_user_mmap_entry * +to_mmmap(struct rdma_user_mmap_entry *rdma_entry) +{ + return container_of(rdma_entry, + struct mlx5_user_mmap_entry, rdma_entry); +} + int mlx5_ib_db_map_user(struct mlx5_ib_ucontext *context, struct ib_udata *udata, unsigned long virt, struct mlx5_db *db); -- GitLab From 5c7404bb30bc5877e578336c58998a999c410622 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 9 Dec 2019 10:32:48 +0100 Subject: [PATCH 0560/1437] dt-bindings: Change maintainer address While my email address has changed for a while, all the schemas I contributed still have the old one unfortunately. Update it. Signed-off-by: Maxime Ripard Signed-off-by: Rob Herring --- Documentation/devicetree/bindings/arm/sunxi.yaml | 2 +- .../devicetree/bindings/bus/allwinner,sun50i-a64-de2.yaml | 2 +- .../devicetree/bindings/bus/allwinner,sun8i-a23-rsb.yaml | 2 +- .../devicetree/bindings/clock/allwinner,sun4i-a10-ccu.yaml | 2 +- .../devicetree/bindings/crypto/allwinner,sun4i-a10-crypto.yaml | 2 +- .../bindings/display/allwinner,sun6i-a31-mipi-dsi.yaml | 2 +- .../devicetree/bindings/display/panel/ronbo,rb070d30.yaml | 2 +- .../devicetree/bindings/dma/allwinner,sun4i-a10-dma.yaml | 2 +- .../devicetree/bindings/dma/allwinner,sun50i-a64-dma.yaml | 2 +- .../devicetree/bindings/dma/allwinner,sun6i-a31-dma.yaml | 2 +- .../devicetree/bindings/i2c/allwinner,sun6i-a31-p2wi.yaml | 2 +- .../devicetree/bindings/iio/adc/allwinner,sun8i-a33-ths.yaml | 2 +- .../bindings/input/allwinner,sun4i-a10-lradc-keys.yaml | 2 +- .../bindings/interrupt-controller/allwinner,sun4i-a10-ic.yaml | 2 +- .../interrupt-controller/allwinner,sun7i-a20-sc-nmi.yaml | 2 +- .../devicetree/bindings/media/allwinner,sun4i-a10-csi.yaml | 2 +- .../devicetree/bindings/media/allwinner,sun4i-a10-ir.yaml | 2 +- .../devicetree/bindings/mfd/allwinner,sun4i-a10-ts.yaml | 2 +- .../devicetree/bindings/mmc/allwinner,sun4i-a10-mmc.yaml | 2 +- .../devicetree/bindings/mtd/allwinner,sun4i-a10-nand.yaml | 2 +- .../devicetree/bindings/net/allwinner,sun4i-a10-emac.yaml | 2 +- .../devicetree/bindings/net/allwinner,sun4i-a10-mdio.yaml | 2 +- .../devicetree/bindings/net/allwinner,sun7i-a20-gmac.yaml | 2 +- .../devicetree/bindings/net/allwinner,sun8i-a83t-emac.yaml | 2 +- .../devicetree/bindings/net/can/allwinner,sun4i-a10-can.yaml | 2 +- .../devicetree/bindings/nvmem/allwinner,sun4i-a10-sid.yaml | 2 +- .../devicetree/bindings/phy/allwinner,sun6i-a31-mipi-dphy.yaml | 2 +- .../bindings/pinctrl/allwinner,sun4i-a10-pinctrl.yaml | 2 +- .../devicetree/bindings/pwm/allwinner,sun4i-a10-pwm.yaml | 2 +- .../devicetree/bindings/rtc/allwinner,sun4i-a10-rtc.yaml | 2 +- .../devicetree/bindings/rtc/allwinner,sun6i-a31-rtc.yaml | 2 +- .../devicetree/bindings/serio/allwinner,sun4i-a10-ps2.yaml | 2 +- .../devicetree/bindings/sound/allwinner,sun4i-a10-codec.yaml | 2 +- .../devicetree/bindings/sound/allwinner,sun4i-a10-i2s.yaml | 2 +- .../devicetree/bindings/sound/allwinner,sun4i-a10-spdif.yaml | 2 +- .../bindings/sound/allwinner,sun50i-a64-codec-analog.yaml | 2 +- .../bindings/sound/allwinner,sun8i-a23-codec-analog.yaml | 2 +- .../devicetree/bindings/sound/allwinner,sun8i-a33-codec.yaml | 2 +- .../devicetree/bindings/spi/allwinner,sun4i-a10-spi.yaml | 2 +- .../devicetree/bindings/spi/allwinner,sun6i-a31-spi.yaml | 2 +- .../devicetree/bindings/timer/allwinner,sun4i-a10-timer.yaml | 2 +- .../devicetree/bindings/timer/allwinner,sun5i-a13-hstimer.yaml | 2 +- .../devicetree/bindings/usb/allwinner,sun4i-a10-musb.yaml | 2 +- .../devicetree/bindings/watchdog/allwinner,sun4i-a10-wdt.yaml | 2 +- 44 files changed, 44 insertions(+), 44 deletions(-) diff --git a/Documentation/devicetree/bindings/arm/sunxi.yaml b/Documentation/devicetree/bindings/arm/sunxi.yaml index 8a1e38a1d7ab1..cffe8bb0bad10 100644 --- a/Documentation/devicetree/bindings/arm/sunxi.yaml +++ b/Documentation/devicetree/bindings/arm/sunxi.yaml @@ -8,7 +8,7 @@ title: Allwinner platforms device tree bindings maintainers: - Chen-Yu Tsai - - Maxime Ripard + - Maxime Ripard properties: $nodename: diff --git a/Documentation/devicetree/bindings/bus/allwinner,sun50i-a64-de2.yaml b/Documentation/devicetree/bindings/bus/allwinner,sun50i-a64-de2.yaml index d2a872286437e..f0b3d30fbb76f 100644 --- a/Documentation/devicetree/bindings/bus/allwinner,sun50i-a64-de2.yaml +++ b/Documentation/devicetree/bindings/bus/allwinner,sun50i-a64-de2.yaml @@ -8,7 +8,7 @@ title: Allwinner A64 Display Engine Bus Device Tree Bindings maintainers: - Chen-Yu Tsai - - Maxime Ripard + - Maxime Ripard properties: $nodename: diff --git a/Documentation/devicetree/bindings/bus/allwinner,sun8i-a23-rsb.yaml b/Documentation/devicetree/bindings/bus/allwinner,sun8i-a23-rsb.yaml index be32f087c5293..9fe11ceecdba0 100644 --- a/Documentation/devicetree/bindings/bus/allwinner,sun8i-a23-rsb.yaml +++ b/Documentation/devicetree/bindings/bus/allwinner,sun8i-a23-rsb.yaml @@ -8,7 +8,7 @@ title: Allwinner A23 RSB Device Tree Bindings maintainers: - Chen-Yu Tsai - - Maxime Ripard + - Maxime Ripard properties: "#address-cells": diff --git a/Documentation/devicetree/bindings/clock/allwinner,sun4i-a10-ccu.yaml b/Documentation/devicetree/bindings/clock/allwinner,sun4i-a10-ccu.yaml index 64938fdaea554..4d382128b711c 100644 --- a/Documentation/devicetree/bindings/clock/allwinner,sun4i-a10-ccu.yaml +++ b/Documentation/devicetree/bindings/clock/allwinner,sun4i-a10-ccu.yaml @@ -8,7 +8,7 @@ title: Allwinner Clock Control Unit Device Tree Bindings maintainers: - Chen-Yu Tsai - - Maxime Ripard + - Maxime Ripard properties: "#clock-cells": diff --git a/Documentation/devicetree/bindings/crypto/allwinner,sun4i-a10-crypto.yaml b/Documentation/devicetree/bindings/crypto/allwinner,sun4i-a10-crypto.yaml index 80b3e7350a732..33c7842917f62 100644 --- a/Documentation/devicetree/bindings/crypto/allwinner,sun4i-a10-crypto.yaml +++ b/Documentation/devicetree/bindings/crypto/allwinner,sun4i-a10-crypto.yaml @@ -8,7 +8,7 @@ title: Allwinner A10 Security System Device Tree Bindings maintainers: - Chen-Yu Tsai - - Maxime Ripard + - Maxime Ripard properties: compatible: diff --git a/Documentation/devicetree/bindings/display/allwinner,sun6i-a31-mipi-dsi.yaml b/Documentation/devicetree/bindings/display/allwinner,sun6i-a31-mipi-dsi.yaml index dafc0980c4fa7..0f7074977c04d 100644 --- a/Documentation/devicetree/bindings/display/allwinner,sun6i-a31-mipi-dsi.yaml +++ b/Documentation/devicetree/bindings/display/allwinner,sun6i-a31-mipi-dsi.yaml @@ -8,7 +8,7 @@ title: Allwinner A31 MIPI-DSI Controller Device Tree Bindings maintainers: - Chen-Yu Tsai - - Maxime Ripard + - Maxime Ripard properties: "#address-cells": true diff --git a/Documentation/devicetree/bindings/display/panel/ronbo,rb070d30.yaml b/Documentation/devicetree/bindings/display/panel/ronbo,rb070d30.yaml index 0e7987f1cdb79..d67617f6f74ab 100644 --- a/Documentation/devicetree/bindings/display/panel/ronbo,rb070d30.yaml +++ b/Documentation/devicetree/bindings/display/panel/ronbo,rb070d30.yaml @@ -7,7 +7,7 @@ $schema: http://devicetree.org/meta-schemas/core.yaml# title: Ronbo RB070D30 DSI Display Panel maintainers: - - Maxime Ripard + - Maxime Ripard properties: compatible: diff --git a/Documentation/devicetree/bindings/dma/allwinner,sun4i-a10-dma.yaml b/Documentation/devicetree/bindings/dma/allwinner,sun4i-a10-dma.yaml index 15abc0f9429fc..83808199657b7 100644 --- a/Documentation/devicetree/bindings/dma/allwinner,sun4i-a10-dma.yaml +++ b/Documentation/devicetree/bindings/dma/allwinner,sun4i-a10-dma.yaml @@ -8,7 +8,7 @@ title: Allwinner A10 DMA Controller Device Tree Bindings maintainers: - Chen-Yu Tsai - - Maxime Ripard + - Maxime Ripard allOf: - $ref: "dma-controller.yaml#" diff --git a/Documentation/devicetree/bindings/dma/allwinner,sun50i-a64-dma.yaml b/Documentation/devicetree/bindings/dma/allwinner,sun50i-a64-dma.yaml index 387d599522c70..9e53472be1947 100644 --- a/Documentation/devicetree/bindings/dma/allwinner,sun50i-a64-dma.yaml +++ b/Documentation/devicetree/bindings/dma/allwinner,sun50i-a64-dma.yaml @@ -8,7 +8,7 @@ title: Allwinner A64 DMA Controller Device Tree Bindings maintainers: - Chen-Yu Tsai - - Maxime Ripard + - Maxime Ripard allOf: - $ref: "dma-controller.yaml#" diff --git a/Documentation/devicetree/bindings/dma/allwinner,sun6i-a31-dma.yaml b/Documentation/devicetree/bindings/dma/allwinner,sun6i-a31-dma.yaml index 740b7f9b535b2..c1676b96daac7 100644 --- a/Documentation/devicetree/bindings/dma/allwinner,sun6i-a31-dma.yaml +++ b/Documentation/devicetree/bindings/dma/allwinner,sun6i-a31-dma.yaml @@ -8,7 +8,7 @@ title: Allwinner A31 DMA Controller Device Tree Bindings maintainers: - Chen-Yu Tsai - - Maxime Ripard + - Maxime Ripard allOf: - $ref: "dma-controller.yaml#" diff --git a/Documentation/devicetree/bindings/i2c/allwinner,sun6i-a31-p2wi.yaml b/Documentation/devicetree/bindings/i2c/allwinner,sun6i-a31-p2wi.yaml index 9346ef6ba61b6..6097e8ac46c1f 100644 --- a/Documentation/devicetree/bindings/i2c/allwinner,sun6i-a31-p2wi.yaml +++ b/Documentation/devicetree/bindings/i2c/allwinner,sun6i-a31-p2wi.yaml @@ -8,7 +8,7 @@ title: Allwinner A31 P2WI (Push/Pull 2 Wires Interface) Device Tree Bindings maintainers: - Chen-Yu Tsai - - Maxime Ripard + - Maxime Ripard allOf: - $ref: /schemas/i2c/i2c-controller.yaml# diff --git a/Documentation/devicetree/bindings/iio/adc/allwinner,sun8i-a33-ths.yaml b/Documentation/devicetree/bindings/iio/adc/allwinner,sun8i-a33-ths.yaml index d74962c0f5ae3..15c514b83583b 100644 --- a/Documentation/devicetree/bindings/iio/adc/allwinner,sun8i-a33-ths.yaml +++ b/Documentation/devicetree/bindings/iio/adc/allwinner,sun8i-a33-ths.yaml @@ -8,7 +8,7 @@ title: Allwinner A33 Thermal Sensor Device Tree Bindings maintainers: - Chen-Yu Tsai - - Maxime Ripard + - Maxime Ripard properties: "#io-channel-cells": diff --git a/Documentation/devicetree/bindings/input/allwinner,sun4i-a10-lradc-keys.yaml b/Documentation/devicetree/bindings/input/allwinner,sun4i-a10-lradc-keys.yaml index b3bd8ef7fbd61..5b3b71c9c0183 100644 --- a/Documentation/devicetree/bindings/input/allwinner,sun4i-a10-lradc-keys.yaml +++ b/Documentation/devicetree/bindings/input/allwinner,sun4i-a10-lradc-keys.yaml @@ -8,7 +8,7 @@ title: Allwinner A10 LRADC Device Tree Bindings maintainers: - Chen-Yu Tsai - - Maxime Ripard + - Maxime Ripard properties: compatible: diff --git a/Documentation/devicetree/bindings/interrupt-controller/allwinner,sun4i-a10-ic.yaml b/Documentation/devicetree/bindings/interrupt-controller/allwinner,sun4i-a10-ic.yaml index 23a202d24e437..953d875b5e74a 100644 --- a/Documentation/devicetree/bindings/interrupt-controller/allwinner,sun4i-a10-ic.yaml +++ b/Documentation/devicetree/bindings/interrupt-controller/allwinner,sun4i-a10-ic.yaml @@ -8,7 +8,7 @@ title: Allwinner A10 Interrupt Controller Device Tree Bindings maintainers: - Chen-Yu Tsai - - Maxime Ripard + - Maxime Ripard allOf: - $ref: /schemas/interrupt-controller.yaml# diff --git a/Documentation/devicetree/bindings/interrupt-controller/allwinner,sun7i-a20-sc-nmi.yaml b/Documentation/devicetree/bindings/interrupt-controller/allwinner,sun7i-a20-sc-nmi.yaml index 8cd08cfb25bef..cf09055da78b2 100644 --- a/Documentation/devicetree/bindings/interrupt-controller/allwinner,sun7i-a20-sc-nmi.yaml +++ b/Documentation/devicetree/bindings/interrupt-controller/allwinner,sun7i-a20-sc-nmi.yaml @@ -8,7 +8,7 @@ title: Allwinner A20 Non-Maskable Interrupt Controller Device Tree Bindings maintainers: - Chen-Yu Tsai - - Maxime Ripard + - Maxime Ripard allOf: - $ref: /schemas/interrupt-controller.yaml# diff --git a/Documentation/devicetree/bindings/media/allwinner,sun4i-a10-csi.yaml b/Documentation/devicetree/bindings/media/allwinner,sun4i-a10-csi.yaml index d3e423fcb6c2e..0f6374ceaa697 100644 --- a/Documentation/devicetree/bindings/media/allwinner,sun4i-a10-csi.yaml +++ b/Documentation/devicetree/bindings/media/allwinner,sun4i-a10-csi.yaml @@ -8,7 +8,7 @@ title: Allwinner A10 CMOS Sensor Interface (CSI) Device Tree Bindings maintainers: - Chen-Yu Tsai - - Maxime Ripard + - Maxime Ripard description: |- The Allwinner A10 and later has a CMOS Sensor Interface to retrieve diff --git a/Documentation/devicetree/bindings/media/allwinner,sun4i-a10-ir.yaml b/Documentation/devicetree/bindings/media/allwinner,sun4i-a10-ir.yaml index dea36d68cdbed..7838804700d66 100644 --- a/Documentation/devicetree/bindings/media/allwinner,sun4i-a10-ir.yaml +++ b/Documentation/devicetree/bindings/media/allwinner,sun4i-a10-ir.yaml @@ -8,7 +8,7 @@ title: Allwinner A10 Infrared Controller Device Tree Bindings maintainers: - Chen-Yu Tsai - - Maxime Ripard + - Maxime Ripard allOf: - $ref: "rc.yaml#" diff --git a/Documentation/devicetree/bindings/mfd/allwinner,sun4i-a10-ts.yaml b/Documentation/devicetree/bindings/mfd/allwinner,sun4i-a10-ts.yaml index 4b1a09acb98b7..39afacc447b20 100644 --- a/Documentation/devicetree/bindings/mfd/allwinner,sun4i-a10-ts.yaml +++ b/Documentation/devicetree/bindings/mfd/allwinner,sun4i-a10-ts.yaml @@ -8,7 +8,7 @@ title: Allwinner A10 Resistive Touchscreen Controller Device Tree Bindings maintainers: - Chen-Yu Tsai - - Maxime Ripard + - Maxime Ripard properties: "#thermal-sensor-cells": diff --git a/Documentation/devicetree/bindings/mmc/allwinner,sun4i-a10-mmc.yaml b/Documentation/devicetree/bindings/mmc/allwinner,sun4i-a10-mmc.yaml index 64bca41031d51..e82c9a07b6fb0 100644 --- a/Documentation/devicetree/bindings/mmc/allwinner,sun4i-a10-mmc.yaml +++ b/Documentation/devicetree/bindings/mmc/allwinner,sun4i-a10-mmc.yaml @@ -11,7 +11,7 @@ allOf: maintainers: - Chen-Yu Tsai - - Maxime Ripard + - Maxime Ripard properties: "#address-cells": true diff --git a/Documentation/devicetree/bindings/mtd/allwinner,sun4i-a10-nand.yaml b/Documentation/devicetree/bindings/mtd/allwinner,sun4i-a10-nand.yaml index b5b3cf5b1ac23..5d3fa412aabd3 100644 --- a/Documentation/devicetree/bindings/mtd/allwinner,sun4i-a10-nand.yaml +++ b/Documentation/devicetree/bindings/mtd/allwinner,sun4i-a10-nand.yaml @@ -11,7 +11,7 @@ allOf: maintainers: - Chen-Yu Tsai - - Maxime Ripard + - Maxime Ripard properties: "#address-cells": true diff --git a/Documentation/devicetree/bindings/net/allwinner,sun4i-a10-emac.yaml b/Documentation/devicetree/bindings/net/allwinner,sun4i-a10-emac.yaml index ae4796ec50a0a..8d8560a67abf2 100644 --- a/Documentation/devicetree/bindings/net/allwinner,sun4i-a10-emac.yaml +++ b/Documentation/devicetree/bindings/net/allwinner,sun4i-a10-emac.yaml @@ -11,7 +11,7 @@ allOf: maintainers: - Chen-Yu Tsai - - Maxime Ripard + - Maxime Ripard properties: compatible: diff --git a/Documentation/devicetree/bindings/net/allwinner,sun4i-a10-mdio.yaml b/Documentation/devicetree/bindings/net/allwinner,sun4i-a10-mdio.yaml index e5562c525ed94..767193ec1d328 100644 --- a/Documentation/devicetree/bindings/net/allwinner,sun4i-a10-mdio.yaml +++ b/Documentation/devicetree/bindings/net/allwinner,sun4i-a10-mdio.yaml @@ -8,7 +8,7 @@ title: Allwinner A10 MDIO Controller Device Tree Bindings maintainers: - Chen-Yu Tsai - - Maxime Ripard + - Maxime Ripard allOf: - $ref: "mdio.yaml#" diff --git a/Documentation/devicetree/bindings/net/allwinner,sun7i-a20-gmac.yaml b/Documentation/devicetree/bindings/net/allwinner,sun7i-a20-gmac.yaml index f683b7104e3eb..703d0d8868846 100644 --- a/Documentation/devicetree/bindings/net/allwinner,sun7i-a20-gmac.yaml +++ b/Documentation/devicetree/bindings/net/allwinner,sun7i-a20-gmac.yaml @@ -11,7 +11,7 @@ allOf: maintainers: - Chen-Yu Tsai - - Maxime Ripard + - Maxime Ripard properties: compatible: diff --git a/Documentation/devicetree/bindings/net/allwinner,sun8i-a83t-emac.yaml b/Documentation/devicetree/bindings/net/allwinner,sun8i-a83t-emac.yaml index 11654d4b80fb7..db36b4d864844 100644 --- a/Documentation/devicetree/bindings/net/allwinner,sun8i-a83t-emac.yaml +++ b/Documentation/devicetree/bindings/net/allwinner,sun8i-a83t-emac.yaml @@ -8,7 +8,7 @@ title: Allwinner A83t EMAC Device Tree Bindings maintainers: - Chen-Yu Tsai - - Maxime Ripard + - Maxime Ripard properties: compatible: diff --git a/Documentation/devicetree/bindings/net/can/allwinner,sun4i-a10-can.yaml b/Documentation/devicetree/bindings/net/can/allwinner,sun4i-a10-can.yaml index 770af7c461140..a95960ee3feba 100644 --- a/Documentation/devicetree/bindings/net/can/allwinner,sun4i-a10-can.yaml +++ b/Documentation/devicetree/bindings/net/can/allwinner,sun4i-a10-can.yaml @@ -8,7 +8,7 @@ title: Allwinner A10 CAN Controller Device Tree Bindings maintainers: - Chen-Yu Tsai - - Maxime Ripard + - Maxime Ripard properties: compatible: diff --git a/Documentation/devicetree/bindings/nvmem/allwinner,sun4i-a10-sid.yaml b/Documentation/devicetree/bindings/nvmem/allwinner,sun4i-a10-sid.yaml index 659b02002a35c..daf1321d76ad8 100644 --- a/Documentation/devicetree/bindings/nvmem/allwinner,sun4i-a10-sid.yaml +++ b/Documentation/devicetree/bindings/nvmem/allwinner,sun4i-a10-sid.yaml @@ -8,7 +8,7 @@ title: Allwinner A10 Security ID Device Tree Bindings maintainers: - Chen-Yu Tsai - - Maxime Ripard + - Maxime Ripard allOf: - $ref: "nvmem.yaml#" diff --git a/Documentation/devicetree/bindings/phy/allwinner,sun6i-a31-mipi-dphy.yaml b/Documentation/devicetree/bindings/phy/allwinner,sun6i-a31-mipi-dphy.yaml index fa46670de2992..230d74f22136c 100644 --- a/Documentation/devicetree/bindings/phy/allwinner,sun6i-a31-mipi-dphy.yaml +++ b/Documentation/devicetree/bindings/phy/allwinner,sun6i-a31-mipi-dphy.yaml @@ -8,7 +8,7 @@ title: Allwinner A31 MIPI D-PHY Controller Device Tree Bindings maintainers: - Chen-Yu Tsai - - Maxime Ripard + - Maxime Ripard properties: "#phy-cells": diff --git a/Documentation/devicetree/bindings/pinctrl/allwinner,sun4i-a10-pinctrl.yaml b/Documentation/devicetree/bindings/pinctrl/allwinner,sun4i-a10-pinctrl.yaml index cd0503b6fe36f..bfefd09d8c1e4 100644 --- a/Documentation/devicetree/bindings/pinctrl/allwinner,sun4i-a10-pinctrl.yaml +++ b/Documentation/devicetree/bindings/pinctrl/allwinner,sun4i-a10-pinctrl.yaml @@ -8,7 +8,7 @@ title: Allwinner A10 Pin Controller Device Tree Bindings maintainers: - Chen-Yu Tsai - - Maxime Ripard + - Maxime Ripard properties: "#gpio-cells": diff --git a/Documentation/devicetree/bindings/pwm/allwinner,sun4i-a10-pwm.yaml b/Documentation/devicetree/bindings/pwm/allwinner,sun4i-a10-pwm.yaml index 0ac52f83a58cc..4a21fe77ee1d3 100644 --- a/Documentation/devicetree/bindings/pwm/allwinner,sun4i-a10-pwm.yaml +++ b/Documentation/devicetree/bindings/pwm/allwinner,sun4i-a10-pwm.yaml @@ -8,7 +8,7 @@ title: Allwinner A10 PWM Device Tree Bindings maintainers: - Chen-Yu Tsai - - Maxime Ripard + - Maxime Ripard properties: "#pwm-cells": diff --git a/Documentation/devicetree/bindings/rtc/allwinner,sun4i-a10-rtc.yaml b/Documentation/devicetree/bindings/rtc/allwinner,sun4i-a10-rtc.yaml index 46d69c32b89b8..478b0234e8fa9 100644 --- a/Documentation/devicetree/bindings/rtc/allwinner,sun4i-a10-rtc.yaml +++ b/Documentation/devicetree/bindings/rtc/allwinner,sun4i-a10-rtc.yaml @@ -11,7 +11,7 @@ allOf: maintainers: - Chen-Yu Tsai - - Maxime Ripard + - Maxime Ripard properties: compatible: diff --git a/Documentation/devicetree/bindings/rtc/allwinner,sun6i-a31-rtc.yaml b/Documentation/devicetree/bindings/rtc/allwinner,sun6i-a31-rtc.yaml index d7a57ec4a6400..37c2a601c3fa8 100644 --- a/Documentation/devicetree/bindings/rtc/allwinner,sun6i-a31-rtc.yaml +++ b/Documentation/devicetree/bindings/rtc/allwinner,sun6i-a31-rtc.yaml @@ -8,7 +8,7 @@ title: Allwinner A31 RTC Device Tree Bindings maintainers: - Chen-Yu Tsai - - Maxime Ripard + - Maxime Ripard properties: "#clock-cells": diff --git a/Documentation/devicetree/bindings/serio/allwinner,sun4i-a10-ps2.yaml b/Documentation/devicetree/bindings/serio/allwinner,sun4i-a10-ps2.yaml index ee9712f1c97d4..2ecab8ed702a2 100644 --- a/Documentation/devicetree/bindings/serio/allwinner,sun4i-a10-ps2.yaml +++ b/Documentation/devicetree/bindings/serio/allwinner,sun4i-a10-ps2.yaml @@ -8,7 +8,7 @@ title: Allwinner A10 PS2 Host Controller Device Tree Bindings maintainers: - Chen-Yu Tsai - - Maxime Ripard + - Maxime Ripard description: A20 PS2 is dual role controller (PS2 host and PS2 device). These diff --git a/Documentation/devicetree/bindings/sound/allwinner,sun4i-a10-codec.yaml b/Documentation/devicetree/bindings/sound/allwinner,sun4i-a10-codec.yaml index b8f89c7258ebc..ea1d2efb2aaa3 100644 --- a/Documentation/devicetree/bindings/sound/allwinner,sun4i-a10-codec.yaml +++ b/Documentation/devicetree/bindings/sound/allwinner,sun4i-a10-codec.yaml @@ -8,7 +8,7 @@ title: Allwinner A10 Codec Device Tree Bindings maintainers: - Chen-Yu Tsai - - Maxime Ripard + - Maxime Ripard properties: "#sound-dai-cells": diff --git a/Documentation/devicetree/bindings/sound/allwinner,sun4i-a10-i2s.yaml b/Documentation/devicetree/bindings/sound/allwinner,sun4i-a10-i2s.yaml index eb3992138eecc..112ae00d63c1d 100644 --- a/Documentation/devicetree/bindings/sound/allwinner,sun4i-a10-i2s.yaml +++ b/Documentation/devicetree/bindings/sound/allwinner,sun4i-a10-i2s.yaml @@ -8,7 +8,7 @@ title: Allwinner A10 I2S Controller Device Tree Bindings maintainers: - Chen-Yu Tsai - - Maxime Ripard + - Maxime Ripard properties: "#sound-dai-cells": diff --git a/Documentation/devicetree/bindings/sound/allwinner,sun4i-a10-spdif.yaml b/Documentation/devicetree/bindings/sound/allwinner,sun4i-a10-spdif.yaml index 38d4cede0860d..444a432912bb4 100644 --- a/Documentation/devicetree/bindings/sound/allwinner,sun4i-a10-spdif.yaml +++ b/Documentation/devicetree/bindings/sound/allwinner,sun4i-a10-spdif.yaml @@ -10,7 +10,7 @@ maintainers: - Chen-Yu Tsai - Liam Girdwood - Mark Brown - - Maxime Ripard + - Maxime Ripard properties: "#sound-dai-cells": diff --git a/Documentation/devicetree/bindings/sound/allwinner,sun50i-a64-codec-analog.yaml b/Documentation/devicetree/bindings/sound/allwinner,sun50i-a64-codec-analog.yaml index f290eb72a8781..3b764415c9abf 100644 --- a/Documentation/devicetree/bindings/sound/allwinner,sun50i-a64-codec-analog.yaml +++ b/Documentation/devicetree/bindings/sound/allwinner,sun50i-a64-codec-analog.yaml @@ -8,7 +8,7 @@ title: Allwinner A64 Analog Codec Device Tree Bindings maintainers: - Chen-Yu Tsai - - Maxime Ripard + - Maxime Ripard properties: compatible: diff --git a/Documentation/devicetree/bindings/sound/allwinner,sun8i-a23-codec-analog.yaml b/Documentation/devicetree/bindings/sound/allwinner,sun8i-a23-codec-analog.yaml index 85305b4c2729b..9718358826abd 100644 --- a/Documentation/devicetree/bindings/sound/allwinner,sun8i-a23-codec-analog.yaml +++ b/Documentation/devicetree/bindings/sound/allwinner,sun8i-a23-codec-analog.yaml @@ -8,7 +8,7 @@ title: Allwinner A23 Analog Codec Device Tree Bindings maintainers: - Chen-Yu Tsai - - Maxime Ripard + - Maxime Ripard properties: compatible: diff --git a/Documentation/devicetree/bindings/sound/allwinner,sun8i-a33-codec.yaml b/Documentation/devicetree/bindings/sound/allwinner,sun8i-a33-codec.yaml index 5e7cc05bbff1d..55d28268d2f4f 100644 --- a/Documentation/devicetree/bindings/sound/allwinner,sun8i-a33-codec.yaml +++ b/Documentation/devicetree/bindings/sound/allwinner,sun8i-a33-codec.yaml @@ -8,7 +8,7 @@ title: Allwinner A33 Codec Device Tree Bindings maintainers: - Chen-Yu Tsai - - Maxime Ripard + - Maxime Ripard properties: "#sound-dai-cells": diff --git a/Documentation/devicetree/bindings/spi/allwinner,sun4i-a10-spi.yaml b/Documentation/devicetree/bindings/spi/allwinner,sun4i-a10-spi.yaml index 6d1329c281707..8036499112f5a 100644 --- a/Documentation/devicetree/bindings/spi/allwinner,sun4i-a10-spi.yaml +++ b/Documentation/devicetree/bindings/spi/allwinner,sun4i-a10-spi.yaml @@ -11,7 +11,7 @@ allOf: maintainers: - Chen-Yu Tsai - - Maxime Ripard + - Maxime Ripard properties: "#address-cells": true diff --git a/Documentation/devicetree/bindings/spi/allwinner,sun6i-a31-spi.yaml b/Documentation/devicetree/bindings/spi/allwinner,sun6i-a31-spi.yaml index f36c46d236d7a..0565dc49e4494 100644 --- a/Documentation/devicetree/bindings/spi/allwinner,sun6i-a31-spi.yaml +++ b/Documentation/devicetree/bindings/spi/allwinner,sun6i-a31-spi.yaml @@ -11,7 +11,7 @@ allOf: maintainers: - Chen-Yu Tsai - - Maxime Ripard + - Maxime Ripard properties: "#address-cells": true diff --git a/Documentation/devicetree/bindings/timer/allwinner,sun4i-a10-timer.yaml b/Documentation/devicetree/bindings/timer/allwinner,sun4i-a10-timer.yaml index 20adc1c8e9ccc..23e989e097663 100644 --- a/Documentation/devicetree/bindings/timer/allwinner,sun4i-a10-timer.yaml +++ b/Documentation/devicetree/bindings/timer/allwinner,sun4i-a10-timer.yaml @@ -8,7 +8,7 @@ title: Allwinner A10 Timer Device Tree Bindings maintainers: - Chen-Yu Tsai - - Maxime Ripard + - Maxime Ripard properties: compatible: diff --git a/Documentation/devicetree/bindings/timer/allwinner,sun5i-a13-hstimer.yaml b/Documentation/devicetree/bindings/timer/allwinner,sun5i-a13-hstimer.yaml index dfa0c41fd261d..40fc4bcb31457 100644 --- a/Documentation/devicetree/bindings/timer/allwinner,sun5i-a13-hstimer.yaml +++ b/Documentation/devicetree/bindings/timer/allwinner,sun5i-a13-hstimer.yaml @@ -8,7 +8,7 @@ title: Allwinner A13 High-Speed Timer Device Tree Bindings maintainers: - Chen-Yu Tsai - - Maxime Ripard + - Maxime Ripard properties: compatible: diff --git a/Documentation/devicetree/bindings/usb/allwinner,sun4i-a10-musb.yaml b/Documentation/devicetree/bindings/usb/allwinner,sun4i-a10-musb.yaml index 0af70fc8de5a7..d9207bf9d8946 100644 --- a/Documentation/devicetree/bindings/usb/allwinner,sun4i-a10-musb.yaml +++ b/Documentation/devicetree/bindings/usb/allwinner,sun4i-a10-musb.yaml @@ -8,7 +8,7 @@ title: Allwinner A10 mUSB OTG Controller Device Tree Bindings maintainers: - Chen-Yu Tsai - - Maxime Ripard + - Maxime Ripard properties: compatible: diff --git a/Documentation/devicetree/bindings/watchdog/allwinner,sun4i-a10-wdt.yaml b/Documentation/devicetree/bindings/watchdog/allwinner,sun4i-a10-wdt.yaml index 3a54f58683a0e..e8f2263761081 100644 --- a/Documentation/devicetree/bindings/watchdog/allwinner,sun4i-a10-wdt.yaml +++ b/Documentation/devicetree/bindings/watchdog/allwinner,sun4i-a10-wdt.yaml @@ -11,7 +11,7 @@ allOf: maintainers: - Chen-Yu Tsai - - Maxime Ripard + - Maxime Ripard properties: compatible: -- GitLab From 70c812106c87981a087fafacab004aee37193405 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Wed, 11 Dec 2019 08:51:01 -0600 Subject: [PATCH 0561/1437] dt-bindings: memory-controllers: tegra: Fix type references Json-schema requires a $ref to be under an 'allOf' if there are additional constraints otherwise the additional constraints are ignored. (Note that this behavior will be changed in draft8.) Fixes: 641262f5e1ed ("dt-bindings: memory: Add binding for NVIDIA Tegra30 External Memory Controller") Fixes: 785685b7a106 ("dt-bindings: memory: Add binding for NVIDIA Tegra30 Memory Controller") Fixes: 8da65c377b21 ("dt-bindings: memory: tegra30: Convert to Tegra124 YAML") Cc: Thierry Reding Cc: Jonathan Hunter Cc: linux-tegra@vger.kernel.org Reviewed-by: Dmitry Osipenko Signed-off-by: Rob Herring --- .../bindings/memory-controllers/nvidia,tegra124-mc.yaml | 3 ++- .../bindings/memory-controllers/nvidia,tegra30-emc.yaml | 9 ++++++--- .../bindings/memory-controllers/nvidia,tegra30-mc.yaml | 3 ++- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/Documentation/devicetree/bindings/memory-controllers/nvidia,tegra124-mc.yaml b/Documentation/devicetree/bindings/memory-controllers/nvidia,tegra124-mc.yaml index 30d9fb193d7fc..22a94b6fdbdee 100644 --- a/Documentation/devicetree/bindings/memory-controllers/nvidia,tegra124-mc.yaml +++ b/Documentation/devicetree/bindings/memory-controllers/nvidia,tegra124-mc.yaml @@ -60,7 +60,8 @@ patternProperties: maximum: 1066000000 nvidia,emem-configuration: - $ref: /schemas/types.yaml#/definitions/uint32-array + allOf: + - $ref: /schemas/types.yaml#/definitions/uint32-array description: | Values to be written to the EMEM register block. See section "15.6.1 MC Registers" in the TRM. diff --git a/Documentation/devicetree/bindings/memory-controllers/nvidia,tegra30-emc.yaml b/Documentation/devicetree/bindings/memory-controllers/nvidia,tegra30-emc.yaml index 7fe0ca14e324f..e4135bac69573 100644 --- a/Documentation/devicetree/bindings/memory-controllers/nvidia,tegra30-emc.yaml +++ b/Documentation/devicetree/bindings/memory-controllers/nvidia,tegra30-emc.yaml @@ -56,7 +56,8 @@ patternProperties: maximum: 900000000 nvidia,emc-auto-cal-interval: - $ref: /schemas/types.yaml#/definitions/uint32 + allOf: + - $ref: /schemas/types.yaml#/definitions/uint32 description: Pad calibration interval in microseconds. minimum: 0 @@ -78,7 +79,8 @@ patternProperties: Mode Register 0. nvidia,emc-zcal-cnt-long: - $ref: /schemas/types.yaml#/definitions/uint32 + allOf: + - $ref: /schemas/types.yaml#/definitions/uint32 description: Number of EMC clocks to wait before issuing any commands after sending ZCAL_MRW_CMD. @@ -96,7 +98,8 @@ patternProperties: FBIO "read" FIFO periodic resetting enabled. nvidia,emc-configuration: - $ref: /schemas/types.yaml#/definitions/uint32-array + allOf: + - $ref: /schemas/types.yaml#/definitions/uint32-array description: EMC timing characterization data. These are the registers (see section "18.13.2 EMC Registers" in the TRM) whose values diff --git a/Documentation/devicetree/bindings/memory-controllers/nvidia,tegra30-mc.yaml b/Documentation/devicetree/bindings/memory-controllers/nvidia,tegra30-mc.yaml index 84fd57bcf0dcd..4b9196c832915 100644 --- a/Documentation/devicetree/bindings/memory-controllers/nvidia,tegra30-mc.yaml +++ b/Documentation/devicetree/bindings/memory-controllers/nvidia,tegra30-mc.yaml @@ -77,7 +77,8 @@ patternProperties: maximum: 900000000 nvidia,emem-configuration: - $ref: /schemas/types.yaml#/definitions/uint32-array + allOf: + - $ref: /schemas/types.yaml#/definitions/uint32-array description: | Values to be written to the EMEM register block. See section "18.13.1 MC Registers" in the TRM. -- GitLab From ee9b280e17dce51c44e1d04d11eb0a4acd0ee1a9 Mon Sep 17 00:00:00 2001 From: Saravana Kannan Date: Mon, 9 Dec 2019 11:31:19 -0800 Subject: [PATCH 0562/1437] of/platform: Unconditionally pause/resume sync state during kernel init Commit 5e6669387e22 ("of/platform: Pause/resume sync state during init and of_platform_populate()") paused/resumed sync state during init only if Linux had parsed and populated a devicetree. However, the check for that (of_have_populated_dt()) can change after of_platform_default_populate_init() executes. One example of this is when devicetree unittests are enabled. This causes an unmatched pause/resume of sync state. To avoid this, just unconditionally pause/resume sync state during init. Fixes: 5e6669387e22 ("of/platform: Pause/resume sync state during init and of_platform_populate()") Reported-by: kernel test robot Signed-off-by: Saravana Kannan Reviewed-by: Frank Rowand Signed-off-by: Rob Herring --- drivers/of/platform.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/of/platform.c b/drivers/of/platform.c index d93891a05f603..3371e4a062483 100644 --- a/drivers/of/platform.c +++ b/drivers/of/platform.c @@ -518,10 +518,11 @@ static int __init of_platform_default_populate_init(void) { struct device_node *node; + device_links_supplier_sync_state_pause(); + if (!of_have_populated_dt()) return -ENODEV; - device_links_supplier_sync_state_pause(); /* * Handle certain compatibles explicitly, since we don't want to create * platform_devices for every node in /reserved-memory with a @@ -545,8 +546,7 @@ arch_initcall_sync(of_platform_default_populate_init); static int __init of_platform_sync_state_init(void) { - if (of_have_populated_dt()) - device_links_supplier_sync_state_resume(); + device_links_supplier_sync_state_resume(); return 0; } late_initcall_sync(of_platform_sync_state_init); -- GitLab From 66d9506440bb05289eb4867059e7b8c6ed209717 Mon Sep 17 00:00:00 2001 From: Jerome Brunet Date: Tue, 3 Dec 2019 09:08:05 +0100 Subject: [PATCH 0563/1437] clk: walk orphan list on clock provider registration So far, we walked the orphan list every time a new clock was registered in CCF. This was fine since the clocks were only referenced by name. Now that the clock can be referenced through DT, it is not enough: * Controller A register first a reference clocks from controller B through DT. * Controller B register all its clocks then register the provider. Each time controller B registers a new clock, the orphan list is walked but it can't match since the provider is registered yet. When the provider is finally registered, the orphan list is not walked unless another clock is registered afterward. This can lead to situation where some clocks remain orphaned even if the parent is available. Walking the orphan list on provider registration solves the problem. Reported-by: Jian Hu Fixes: fc0c209c147f ("clk: Allow parents to be specified without string names") Signed-off-by: Jerome Brunet Link: https://lkml.kernel.org/r/20191203080805.104628-1-jbrunet@baylibre.com Signed-off-by: Stephen Boyd --- drivers/clk/clk.c | 62 ++++++++++++++++++++++++++++++----------------- 1 file changed, 40 insertions(+), 22 deletions(-) diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index b68e200829f20..ae2795b30e060 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -3249,6 +3249,41 @@ static inline void clk_debug_unregister(struct clk_core *core) } #endif +static void clk_core_reparent_orphans_nolock(void) +{ + struct clk_core *orphan; + struct hlist_node *tmp2; + + /* + * walk the list of orphan clocks and reparent any that newly finds a + * parent. + */ + hlist_for_each_entry_safe(orphan, tmp2, &clk_orphan_list, child_node) { + struct clk_core *parent = __clk_init_parent(orphan); + + /* + * We need to use __clk_set_parent_before() and _after() to + * to properly migrate any prepare/enable count of the orphan + * clock. This is important for CLK_IS_CRITICAL clocks, which + * are enabled during init but might not have a parent yet. + */ + if (parent) { + /* update the clk tree topology */ + __clk_set_parent_before(orphan, parent); + __clk_set_parent_after(orphan, parent, NULL); + __clk_recalc_accuracies(orphan); + __clk_recalc_rates(orphan, 0); + } + } +} + +static void clk_core_reparent_orphans(void) +{ + clk_prepare_lock(); + clk_core_reparent_orphans_nolock(); + clk_prepare_unlock(); +} + /** * __clk_core_init - initialize the data structures in a struct clk_core * @core: clk_core being initialized @@ -3259,8 +3294,6 @@ static inline void clk_debug_unregister(struct clk_core *core) static int __clk_core_init(struct clk_core *core) { int ret; - struct clk_core *orphan; - struct hlist_node *tmp2; unsigned long rate; if (!core) @@ -3407,27 +3440,8 @@ static int __clk_core_init(struct clk_core *core) clk_enable_unlock(flags); } - /* - * walk the list of orphan clocks and reparent any that newly finds a - * parent. - */ - hlist_for_each_entry_safe(orphan, tmp2, &clk_orphan_list, child_node) { - struct clk_core *parent = __clk_init_parent(orphan); + clk_core_reparent_orphans_nolock(); - /* - * We need to use __clk_set_parent_before() and _after() to - * to properly migrate any prepare/enable count of the orphan - * clock. This is important for CLK_IS_CRITICAL clocks, which - * are enabled during init but might not have a parent yet. - */ - if (parent) { - /* update the clk tree topology */ - __clk_set_parent_before(orphan, parent); - __clk_set_parent_after(orphan, parent, NULL); - __clk_recalc_accuracies(orphan); - __clk_recalc_rates(orphan, 0); - } - } kref_init(&core->ref); out: @@ -4274,6 +4288,8 @@ int of_clk_add_provider(struct device_node *np, mutex_unlock(&of_clk_mutex); pr_debug("Added clock from %pOF\n", np); + clk_core_reparent_orphans(); + ret = of_clk_set_defaults(np, true); if (ret < 0) of_clk_del_provider(np); @@ -4309,6 +4325,8 @@ int of_clk_add_hw_provider(struct device_node *np, mutex_unlock(&of_clk_mutex); pr_debug("Added clk_hw provider from %pOF\n", np); + clk_core_reparent_orphans(); + ret = of_clk_set_defaults(np, true); if (ret < 0) of_clk_del_provider(np); -- GitLab From 4fc0a3ca721da2bfbab0fdf248bd76d7e6bc232f Mon Sep 17 00:00:00 2001 From: Zhenyu Wang Date: Thu, 12 Dec 2019 16:46:14 +0800 Subject: [PATCH 0564/1437] drm/i915/gvt: set guest display buffer as readonly We shouldn't allow write for exposed guest display buffer which doesn't make sense. So explicitly set read only flag for display dmabuf allocated object. Fixes: e546e281d33d ("drm/i915/gvt: Dmabuf support for GVT-g") Cc: Tina Zhang Acked-by: Tina Zhang Signed-off-by: Zhenyu Wang Link: http://patchwork.freedesktop.org/patch/msgid/20191212084614.1100-1-zhenyuw@linux.intel.com --- drivers/gpu/drm/i915/gvt/dmabuf.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpu/drm/i915/gvt/dmabuf.c b/drivers/gpu/drm/i915/gvt/dmabuf.c index e451298d11c32..ee0f3094e6d03 100644 --- a/drivers/gpu/drm/i915/gvt/dmabuf.c +++ b/drivers/gpu/drm/i915/gvt/dmabuf.c @@ -163,6 +163,7 @@ static struct drm_i915_gem_object *vgpu_create_gem(struct drm_device *dev, drm_gem_private_object_init(dev, &obj->base, roundup(info->size, PAGE_SIZE)); i915_gem_object_init(obj, &intel_vgpu_gem_ops, &lock_class); + i915_gem_object_set_readonly(obj); obj->read_domains = I915_GEM_DOMAIN_GTT; obj->write_domain = 0; -- GitLab From 9f674c811740b5db4b34668b72d47f6e7b879b0a Mon Sep 17 00:00:00 2001 From: Tina Zhang Date: Fri, 13 Dec 2019 11:23:14 +0800 Subject: [PATCH 0565/1437] drm/i915/gvt: Pin vgpu dma address before using Dma-buf display uses the vgpu dma address saved in the guest part GGTT table which is updated by vCPU thread. In host side, when the dma address is used by qemu ui thread, gvt-g must make sure the dma address is validated before letting it go to the HW. Invalid guest dma address will easily cause DMA fault and make GPU hang. v2: Rebase Fixes: e546e281d33d ("drm/i915/gvt: Dmabuf support for GVT-g") Acked-by: Zhenyu Wang Signed-off-by: Tina Zhang Signed-off-by: Zhenyu Wang Link: http://patchwork.freedesktop.org/patch/msgid/20191212141342.3417-1-tina.zhang@intel.com --- drivers/gpu/drm/i915/gvt/dmabuf.c | 63 ++++++++++++++++++++++++++-- drivers/gpu/drm/i915/gvt/hypercall.h | 2 + drivers/gpu/drm/i915/gvt/kvmgt.c | 23 ++++++++++ drivers/gpu/drm/i915/gvt/mpt.h | 15 +++++++ 4 files changed, 99 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/i915/gvt/dmabuf.c b/drivers/gpu/drm/i915/gvt/dmabuf.c index ee0f3094e6d03..2477a1e5a1669 100644 --- a/drivers/gpu/drm/i915/gvt/dmabuf.c +++ b/drivers/gpu/drm/i915/gvt/dmabuf.c @@ -36,13 +36,32 @@ #define GEN8_DECODE_PTE(pte) (pte & GENMASK_ULL(63, 12)) +static int vgpu_pin_dma_address(struct intel_vgpu *vgpu, + unsigned long size, + dma_addr_t dma_addr) +{ + int ret = 0; + + if (intel_gvt_hypervisor_dma_pin_guest_page(vgpu, dma_addr)) + ret = -EINVAL; + + return ret; +} + +static void vgpu_unpin_dma_address(struct intel_vgpu *vgpu, + dma_addr_t dma_addr) +{ + intel_gvt_hypervisor_dma_unmap_guest_page(vgpu, dma_addr); +} + static int vgpu_gem_get_pages( struct drm_i915_gem_object *obj) { struct drm_i915_private *dev_priv = to_i915(obj->base.dev); + struct intel_vgpu *vgpu; struct sg_table *st; struct scatterlist *sg; - int i, ret; + int i, j, ret; gen8_pte_t __iomem *gtt_entries; struct intel_vgpu_fb_info *fb_info; u32 page_num; @@ -51,6 +70,10 @@ static int vgpu_gem_get_pages( if (WARN_ON(!fb_info)) return -ENODEV; + vgpu = fb_info->obj->vgpu; + if (WARN_ON(!vgpu)) + return -ENODEV; + st = kmalloc(sizeof(*st), GFP_KERNEL); if (unlikely(!st)) return -ENOMEM; @@ -64,21 +87,53 @@ static int vgpu_gem_get_pages( gtt_entries = (gen8_pte_t __iomem *)dev_priv->ggtt.gsm + (fb_info->start >> PAGE_SHIFT); for_each_sg(st->sgl, sg, page_num, i) { + dma_addr_t dma_addr = + GEN8_DECODE_PTE(readq(>t_entries[i])); + if (vgpu_pin_dma_address(vgpu, PAGE_SIZE, dma_addr)) { + ret = -EINVAL; + goto out; + } + sg->offset = 0; sg->length = PAGE_SIZE; - sg_dma_address(sg) = - GEN8_DECODE_PTE(readq(>t_entries[i])); sg_dma_len(sg) = PAGE_SIZE; + sg_dma_address(sg) = dma_addr; } __i915_gem_object_set_pages(obj, st, PAGE_SIZE); +out: + if (ret) { + dma_addr_t dma_addr; + + for_each_sg(st->sgl, sg, i, j) { + dma_addr = sg_dma_address(sg); + if (dma_addr) + vgpu_unpin_dma_address(vgpu, dma_addr); + } + sg_free_table(st); + kfree(st); + } + + return ret; - return 0; } static void vgpu_gem_put_pages(struct drm_i915_gem_object *obj, struct sg_table *pages) { + struct scatterlist *sg; + + if (obj->base.dma_buf) { + struct intel_vgpu_fb_info *fb_info = obj->gvt_info; + struct intel_vgpu_dmabuf_obj *obj = fb_info->obj; + struct intel_vgpu *vgpu = obj->vgpu; + int i; + + for_each_sg(pages->sgl, sg, fb_info->size, i) + vgpu_unpin_dma_address(vgpu, + sg_dma_address(sg)); + } + sg_free_table(pages); kfree(pages); } diff --git a/drivers/gpu/drm/i915/gvt/hypercall.h b/drivers/gpu/drm/i915/gvt/hypercall.h index 4862fb12778e0..b19a3b1ea4c11 100644 --- a/drivers/gpu/drm/i915/gvt/hypercall.h +++ b/drivers/gpu/drm/i915/gvt/hypercall.h @@ -62,6 +62,8 @@ struct intel_gvt_mpt { unsigned long size, dma_addr_t *dma_addr); void (*dma_unmap_guest_page)(unsigned long handle, dma_addr_t dma_addr); + int (*dma_pin_guest_page)(unsigned long handle, dma_addr_t dma_addr); + int (*map_gfn_to_mfn)(unsigned long handle, unsigned long gfn, unsigned long mfn, unsigned int nr, bool map); int (*set_trap_area)(unsigned long handle, u64 start, u64 end, diff --git a/drivers/gpu/drm/i915/gvt/kvmgt.c b/drivers/gpu/drm/i915/gvt/kvmgt.c index 04a5a0d908239..3259a1fa69e10 100644 --- a/drivers/gpu/drm/i915/gvt/kvmgt.c +++ b/drivers/gpu/drm/i915/gvt/kvmgt.c @@ -1916,6 +1916,28 @@ err_unlock: return ret; } +static int kvmgt_dma_pin_guest_page(unsigned long handle, dma_addr_t dma_addr) +{ + struct kvmgt_guest_info *info; + struct gvt_dma *entry; + int ret = 0; + + if (!handle_valid(handle)) + return -ENODEV; + + info = (struct kvmgt_guest_info *)handle; + + mutex_lock(&info->vgpu->vdev.cache_lock); + entry = __gvt_cache_find_dma_addr(info->vgpu, dma_addr); + if (entry) + kref_get(&entry->ref); + else + ret = -ENOMEM; + mutex_unlock(&info->vgpu->vdev.cache_lock); + + return ret; +} + static void __gvt_dma_release(struct kref *ref) { struct gvt_dma *entry = container_of(ref, typeof(*entry), ref); @@ -2027,6 +2049,7 @@ static struct intel_gvt_mpt kvmgt_mpt = { .gfn_to_mfn = kvmgt_gfn_to_pfn, .dma_map_guest_page = kvmgt_dma_map_guest_page, .dma_unmap_guest_page = kvmgt_dma_unmap_guest_page, + .dma_pin_guest_page = kvmgt_dma_pin_guest_page, .set_opregion = kvmgt_set_opregion, .set_edid = kvmgt_set_edid, .get_vfio_device = kvmgt_get_vfio_device, diff --git a/drivers/gpu/drm/i915/gvt/mpt.h b/drivers/gpu/drm/i915/gvt/mpt.h index 0f94401281230..9ad224df9c68b 100644 --- a/drivers/gpu/drm/i915/gvt/mpt.h +++ b/drivers/gpu/drm/i915/gvt/mpt.h @@ -254,6 +254,21 @@ static inline void intel_gvt_hypervisor_dma_unmap_guest_page( intel_gvt_host.mpt->dma_unmap_guest_page(vgpu->handle, dma_addr); } +/** + * intel_gvt_hypervisor_dma_pin_guest_page - pin guest dma buf + * @vgpu: a vGPU + * @dma_addr: guest dma addr + * + * Returns: + * 0 on success, negative error code if failed. + */ +static inline int +intel_gvt_hypervisor_dma_pin_guest_page(struct intel_vgpu *vgpu, + dma_addr_t dma_addr) +{ + return intel_gvt_host.mpt->dma_pin_guest_page(vgpu->handle, dma_addr); +} + /** * intel_gvt_hypervisor_map_gfn_to_mfn - map a GFN region to MFN * @vgpu: a vGPU -- GitLab From e0fc5b1153b81571d6ff80902a54c3eb13319b33 Mon Sep 17 00:00:00 2001 From: Steve French Date: Mon, 9 Dec 2019 22:34:10 -0600 Subject: [PATCH 0566/1437] SMB3: Fix crash in SMB2_open_init due to uninitialized field in compounding path Ran into an intermittent crash in SMB2_open_init+0x2f6/0x970 due to oparms.cifs_sb not being initialized when called from: smb2_compound_op+0x45d/0x1690 Zero the whole oparms struct in the compounding path before setting up the oparms so we don't risk any uninitialized fields. Fixes: fdef665ba44a ("smb3: fix mode passed in on create for modetosid mount option") Signed-off-by: Steve French Acked-by: Ronnie Sahlberg Reviewed-by: Pavel Shilovsky --- fs/cifs/smb2inode.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/cifs/smb2inode.c b/fs/cifs/smb2inode.c index 18c7a33adcebf..5ef5e97a6d13e 100644 --- a/fs/cifs/smb2inode.c +++ b/fs/cifs/smb2inode.c @@ -95,6 +95,7 @@ smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon, goto finished; } + memset(&oparms, 0, sizeof(struct cifs_open_parms)); oparms.tcon = tcon; oparms.desired_access = desired_access; oparms.disposition = create_disposition; -- GitLab From d9191319358da13ee6a332fb9bf745f2181a612a Mon Sep 17 00:00:00 2001 From: Pavel Shilovsky Date: Tue, 10 Dec 2019 11:44:52 -0800 Subject: [PATCH 0567/1437] CIFS: Close cached root handle only if it has a lease SMB2_tdis() checks if a root handle is valid in order to decide whether it needs to close the handle or not. However if another thread has reference for the handle, it may end up with putting the reference twice. The extra reference that we want to put during the tree disconnect is the reference that has a directory lease. So, track the fact that we have a directory lease and close the handle only in that case. Signed-off-by: Pavel Shilovsky Reviewed-by: Ronnie Sahlberg Signed-off-by: Steve French --- fs/cifs/cifsglob.h | 2 +- fs/cifs/cifssmb.c | 3 +++ fs/cifs/smb2ops.c | 19 ++++++++++++++++++- fs/cifs/smb2pdu.c | 3 +-- fs/cifs/smb2proto.h | 2 ++ 5 files changed, 25 insertions(+), 4 deletions(-) diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index fd0262ce5ad5b..ce9bac756c2a1 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -1061,7 +1061,7 @@ cap_unix(struct cifs_ses *ses) struct cached_fid { bool is_valid:1; /* Do we have a useable root fid */ bool file_all_info_is_valid:1; - + bool has_lease:1; struct kref refcount; struct cifs_fid *fid; struct mutex fid_mutex; diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c index 4f554f019a989..cc86a67225d1b 100644 --- a/fs/cifs/cifssmb.c +++ b/fs/cifs/cifssmb.c @@ -42,6 +42,7 @@ #include "cifsproto.h" #include "cifs_unicode.h" #include "cifs_debug.h" +#include "smb2proto.h" #include "fscache.h" #include "smbdirect.h" #ifdef CONFIG_CIFS_DFS_UPCALL @@ -112,6 +113,8 @@ cifs_mark_open_files_invalid(struct cifs_tcon *tcon) mutex_lock(&tcon->crfid.fid_mutex); tcon->crfid.is_valid = false; + /* cached handle is not valid, so SMB2_CLOSE won't be sent below */ + close_shroot_lease_locked(&tcon->crfid); memset(tcon->crfid.fid, 0, sizeof(struct cifs_fid)); mutex_unlock(&tcon->crfid.fid_mutex); diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c index a5c96bc522cb3..6250370c11702 100644 --- a/fs/cifs/smb2ops.c +++ b/fs/cifs/smb2ops.c @@ -616,6 +616,7 @@ smb2_close_cached_fid(struct kref *ref) cfid->fid->volatile_fid); cfid->is_valid = false; cfid->file_all_info_is_valid = false; + cfid->has_lease = false; } } @@ -626,13 +627,28 @@ void close_shroot(struct cached_fid *cfid) mutex_unlock(&cfid->fid_mutex); } +void close_shroot_lease_locked(struct cached_fid *cfid) +{ + if (cfid->has_lease) { + cfid->has_lease = false; + kref_put(&cfid->refcount, smb2_close_cached_fid); + } +} + +void close_shroot_lease(struct cached_fid *cfid) +{ + mutex_lock(&cfid->fid_mutex); + close_shroot_lease_locked(cfid); + mutex_unlock(&cfid->fid_mutex); +} + void smb2_cached_lease_break(struct work_struct *work) { struct cached_fid *cfid = container_of(work, struct cached_fid, lease_break); - close_shroot(cfid); + close_shroot_lease(cfid); } /* @@ -773,6 +789,7 @@ int open_shroot(unsigned int xid, struct cifs_tcon *tcon, struct cifs_fid *pfid) /* BB TBD check to see if oplock level check can be removed below */ if (o_rsp->OplockLevel == SMB2_OPLOCK_LEVEL_LEASE) { kref_get(&tcon->crfid.refcount); + tcon->crfid.has_lease = true; smb2_parse_contexts(server, o_rsp, &oparms.fid->epoch, oparms.fid->lease_key, &oplock, NULL); diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c index d2658f51ff608..9434f6dd8df32 100644 --- a/fs/cifs/smb2pdu.c +++ b/fs/cifs/smb2pdu.c @@ -1847,8 +1847,7 @@ SMB2_tdis(const unsigned int xid, struct cifs_tcon *tcon) if ((tcon->need_reconnect) || (tcon->ses->need_reconnect)) return 0; - if (tcon->crfid.is_valid) - close_shroot(&tcon->crfid); + close_shroot_lease(&tcon->crfid); rc = smb2_plain_req_init(SMB2_TREE_DISCONNECT, tcon, (void **) &req, &total_len); diff --git a/fs/cifs/smb2proto.h b/fs/cifs/smb2proto.h index a18272c987fed..27d29f2eb6c82 100644 --- a/fs/cifs/smb2proto.h +++ b/fs/cifs/smb2proto.h @@ -70,6 +70,8 @@ extern int smb3_handle_read_data(struct TCP_Server_Info *server, extern int open_shroot(unsigned int xid, struct cifs_tcon *tcon, struct cifs_fid *pfid); extern void close_shroot(struct cached_fid *cfid); +extern void close_shroot_lease(struct cached_fid *cfid); +extern void close_shroot_lease_locked(struct cached_fid *cfid); extern void move_smb2_info_to_cifs(FILE_ALL_INFO *dst, struct smb2_file_all_info *src); extern int smb2_query_path_info(const unsigned int xid, struct cifs_tcon *tcon, -- GitLab From fa2ac657f9783f0891b2935490afe9a7fd29d3fa Mon Sep 17 00:00:00 2001 From: Paul Durrant Date: Tue, 10 Dec 2019 14:53:05 +0000 Subject: [PATCH 0568/1437] xen-blkback: prevent premature module unload MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Objects allocated by xen_blkif_alloc come from the 'blkif_cache' kmem cache. This cache is destoyed when xen-blkif is unloaded so it is necessary to wait for the deferred free routine used for such objects to complete. This necessity was missed in commit 14855954f636 "xen-blkback: allow module to be cleanly unloaded". This patch fixes the problem by taking/releasing extra module references in xen_blkif_alloc/free() respectively. Signed-off-by: Paul Durrant Reviewed-by: Roger Pau Monné Signed-off-by: Juergen Gross --- drivers/block/xen-blkback/xenbus.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/block/xen-blkback/xenbus.c b/drivers/block/xen-blkback/xenbus.c index e8c5c54e1d265..d6a6adfd5159d 100644 --- a/drivers/block/xen-blkback/xenbus.c +++ b/drivers/block/xen-blkback/xenbus.c @@ -171,6 +171,15 @@ static struct xen_blkif *xen_blkif_alloc(domid_t domid) blkif->domid = domid; atomic_set(&blkif->refcnt, 1); init_completion(&blkif->drain_complete); + + /* + * Because freeing back to the cache may be deferred, it is not + * safe to unload the module (and hence destroy the cache) until + * this has completed. To prevent premature unloading, take an + * extra module reference here and release only when the object + * has been freed back to the cache. + */ + __module_get(THIS_MODULE); INIT_WORK(&blkif->free_work, xen_blkif_deferred_free); return blkif; @@ -320,6 +329,7 @@ static void xen_blkif_free(struct xen_blkif *blkif) /* Make sure everything is drained before shutting down */ kmem_cache_free(xen_blkif_cachep, blkif); + module_put(THIS_MODULE); } int __init xen_blkif_interface_init(void) -- GitLab From c673ec61ade89bf2f417960f986bc25671762efb Mon Sep 17 00:00:00 2001 From: Juergen Gross Date: Thu, 12 Dec 2019 15:17:50 +0100 Subject: [PATCH 0569/1437] xen/balloon: fix ballooned page accounting without hotplug enabled When CONFIG_XEN_BALLOON_MEMORY_HOTPLUG is not defined reserve_additional_memory() will set balloon_stats.target_pages to a wrong value in case there are still some ballooned pages allocated via alloc_xenballooned_pages(). This will result in balloon_process() no longer be triggered when ballooned pages are freed in batches. Reported-by: Nicholas Tsirakis Signed-off-by: Juergen Gross Reviewed-by: Boris Ostrovsky Signed-off-by: Juergen Gross --- drivers/xen/balloon.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/xen/balloon.c b/drivers/xen/balloon.c index 5bae515c8e25c..bed90d612e484 100644 --- a/drivers/xen/balloon.c +++ b/drivers/xen/balloon.c @@ -395,7 +395,8 @@ static struct notifier_block xen_memory_nb = { #else static enum bp_state reserve_additional_memory(void) { - balloon_stats.target_pages = balloon_stats.current_pages; + balloon_stats.target_pages = balloon_stats.current_pages + + balloon_stats.target_unpopulated; return BP_ECANCELED; } #endif /* CONFIG_XEN_BALLOON_MEMORY_HOTPLUG */ -- GitLab From 753ffad3d6243303994227854d951ff5c70fa9e0 Mon Sep 17 00:00:00 2001 From: Fredrik Olofsson Date: Tue, 19 Nov 2019 14:34:51 +0100 Subject: [PATCH 0570/1437] mac80211: fix TID field in monitor mode transmit Fix overwriting of the qos_ctrl.tid field for encrypted frames injected on a monitor interface. While qos_ctrl.tid is not encrypted, it's used as an input into the encryption algorithm so it's protected, and thus cannot be modified after encryption. For injected frames, the encryption may already have been done in userspace, so we cannot change any fields. Before passing the frame to the driver, the qos_ctrl.tid field is updated from skb->priority. Prior to dbd50a851c50 skb->priority was updated in ieee80211_select_queue_80211(), but this function is no longer always called. Update skb->priority in ieee80211_monitor_start_xmit() so that the value is stored, and when later code 'modifies' the TID it really sets it to the same value as before, preserving the encryption. Fixes: dbd50a851c50 ("mac80211: only allocate one queue when using iTXQs") Signed-off-by: Fredrik Olofsson Link: https://lore.kernel.org/r/20191119133451.14711-1-fredrik.olofsson@anyfinetworks.com [rewrite commit message based on our discussion] Signed-off-by: Johannes Berg --- net/mac80211/tx.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index b696b9136f4c1..38b58a00db46c 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -2256,6 +2256,15 @@ netdev_tx_t ieee80211_monitor_start_xmit(struct sk_buff *skb, payload[7]); } + /* + * Initialize skb->priority for QoS frames. This is put in the TID field + * of the frame before passing it to the driver. + */ + if (ieee80211_is_data_qos(hdr->frame_control)) { + u8 *p = ieee80211_get_qos_ctl(hdr); + skb->priority = *p & IEEE80211_QOS_CTL_TAG1D_MASK; + } + memset(info, 0, sizeof(*info)); info->flags = IEEE80211_TX_CTL_REQ_TX_STATUS | -- GitLab From 56cb31e185adb61f930743a9b70e700a43625386 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20B=C3=BChler?= Date: Tue, 26 Nov 2019 11:05:44 +0100 Subject: [PATCH 0571/1437] cfg80211: fix double-free after changing network namespace MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If wdev->wext.keys was initialized it didn't get reset to NULL on unregister (and it doesn't get set in cfg80211_init_wdev either), but wdev is reused if unregister was triggered through cfg80211_switch_netns. The next unregister (for whatever reason) will try to free wdev->wext.keys again. Signed-off-by: Stefan Bühler Link: https://lore.kernel.org/r/20191126100543.782023-1-stefan.buehler@tik.uni-stuttgart.de Signed-off-by: Johannes Berg --- net/wireless/core.c | 1 + 1 file changed, 1 insertion(+) diff --git a/net/wireless/core.c b/net/wireless/core.c index 350513744575a..3e25229a059de 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -1102,6 +1102,7 @@ static void __cfg80211_unregister_wdev(struct wireless_dev *wdev, bool sync) #ifdef CONFIG_CFG80211_WEXT kzfree(wdev->wext.keys); + wdev->wext.keys = NULL; #endif /* only initialized if we have a netdev */ if (wdev->netdev) -- GitLab From e548f749b096133d6da3dd7235ecfc6bd67be306 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Tue, 26 Nov 2019 15:09:39 +0300 Subject: [PATCH 0572/1437] mac80211: airtime: Fix an off by one in ieee80211_calc_rx_airtime() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This code was copied from mt76 and inherited an off by one bug from there. The > should be >= so that we don't read one element beyond the end of the array. Fixes: db3e1c40cf2f ("mac80211: Import airtime calculation code from mt76") Reported-by: Toke Høiland-Jørgensen Signed-off-by: Dan Carpenter Acked-by: Toke Høiland-Jørgensen Link: https://lore.kernel.org/r/20191126120910.ftr4t7me3by32aiz@kili.mountain Signed-off-by: Johannes Berg --- net/mac80211/airtime.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/mac80211/airtime.c b/net/mac80211/airtime.c index 63cb0028b02d2..9fc2968856c0b 100644 --- a/net/mac80211/airtime.c +++ b/net/mac80211/airtime.c @@ -442,7 +442,7 @@ u32 ieee80211_calc_rx_airtime(struct ieee80211_hw *hw, return 0; sband = hw->wiphy->bands[status->band]; - if (!sband || status->rate_idx > sband->n_bitrates) + if (!sband || status->rate_idx >= sband->n_bitrates) return 0; rate = &sband->bitrates[status->rate_idx]; -- GitLab From 911bde0fe5ccd7e55760be9d6dcc67a8850fcc12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Toke=20H=C3=B8iland-J=C3=B8rgensen?= Date: Thu, 12 Dec 2019 12:14:37 +0100 Subject: [PATCH 0573/1437] mac80211: Turn AQL into an NL80211_EXT_FEATURE MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instead of just having an airtime flag in debugfs, turn AQL into a proper NL80211_EXT_FEATURE, so drivers can turn it on when they are ready, and so we also expose the presence of the feature to userspace. This also has the effect of flipping the default, so drivers have to opt in to using AQL instead of getting it by default with TXQs. To keep functionality the same as pre-patch, we set this feature for ath10k (which is where it is needed the most). While we're at it, split out the debugfs interface so AQL gets its own per-station debugfs file instead of using the 'airtime' file. [Johannes:] This effectively disables AQL for iwlwifi, where it fixes a number of issues: * TSO in iwlwifi is causing underflows and associated warnings in AQL * HE (802.11ax) rates aren't reported properly so at HE rates, AQL could never have a valid estimate (it'd use 6 Mbps instead of up to 2400!) Signed-off-by: Toke Høiland-Jørgensen Link: https://lore.kernel.org/r/20191212111437.224294-1-toke@redhat.com Fixes: 3ace10f5b5ad ("mac80211: Implement Airtime-based Queue Limit (AQL)") Signed-off-by: Johannes Berg --- drivers/net/wireless/ath/ath10k/mac.c | 1 + include/uapi/linux/nl80211.h | 5 ++ net/mac80211/debugfs_sta.c | 76 ++++++++++++++++++++------- net/mac80211/main.c | 4 +- net/mac80211/sta_info.c | 3 ++ net/mac80211/sta_info.h | 1 - net/mac80211/tx.c | 4 +- 7 files changed, 70 insertions(+), 24 deletions(-) diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index 83cc8778ca1e3..978f0037ed522 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -8958,6 +8958,7 @@ int ath10k_mac_register(struct ath10k *ar) wiphy_ext_feature_set(ar->hw->wiphy, NL80211_EXT_FEATURE_VHT_IBSS); wiphy_ext_feature_set(ar->hw->wiphy, NL80211_EXT_FEATURE_SET_SCAN_DWELL); + wiphy_ext_feature_set(ar->hw->wiphy, NL80211_EXT_FEATURE_AQL); if (test_bit(WMI_SERVICE_TX_DATA_ACK_RSSI, ar->wmi.svc_map) || test_bit(WMI_SERVICE_HTT_MGMT_TX_COMP_VALID_FLAGS, ar->wmi.svc_map)) diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 341e0e8cae462..5eab191607f86 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -5517,6 +5517,10 @@ enum nl80211_feature_flags { * with VLAN tagged frames and separate VLAN-specific netdevs added using * vconfig similarly to the Ethernet case. * + * @NL80211_EXT_FEATURE_AQL: The driver supports the Airtime Queue Limit (AQL) + * feature, which prevents bufferbloat by using the expected transmission + * time to limit the amount of data buffered in the hardware. + * * @NUM_NL80211_EXT_FEATURES: number of extended features. * @MAX_NL80211_EXT_FEATURES: highest extended feature index. */ @@ -5563,6 +5567,7 @@ enum nl80211_ext_feature_index { NL80211_EXT_FEATURE_STA_TX_PWR, NL80211_EXT_FEATURE_SAE_OFFLOAD, NL80211_EXT_FEATURE_VLAN_OFFLOAD, + NL80211_EXT_FEATURE_AQL, /* add new features before the definition below */ NUM_NL80211_EXT_FEATURES, diff --git a/net/mac80211/debugfs_sta.c b/net/mac80211/debugfs_sta.c index b3c9001d1f43d..c80b1e163ea4e 100644 --- a/net/mac80211/debugfs_sta.c +++ b/net/mac80211/debugfs_sta.c @@ -201,8 +201,6 @@ static ssize_t sta_airtime_read(struct file *file, char __user *userbuf, char *buf = kzalloc(bufsz, GFP_KERNEL), *p = buf; u64 rx_airtime = 0, tx_airtime = 0; s64 deficit[IEEE80211_NUM_ACS]; - u32 q_depth[IEEE80211_NUM_ACS]; - u32 q_limit_l[IEEE80211_NUM_ACS], q_limit_h[IEEE80211_NUM_ACS]; ssize_t rv; int ac; @@ -214,6 +212,56 @@ static ssize_t sta_airtime_read(struct file *file, char __user *userbuf, rx_airtime += sta->airtime[ac].rx_airtime; tx_airtime += sta->airtime[ac].tx_airtime; deficit[ac] = sta->airtime[ac].deficit; + spin_unlock_bh(&local->active_txq_lock[ac]); + } + + p += scnprintf(p, bufsz + buf - p, + "RX: %llu us\nTX: %llu us\nWeight: %u\n" + "Deficit: VO: %lld us VI: %lld us BE: %lld us BK: %lld us\n", + rx_airtime, tx_airtime, sta->airtime_weight, + deficit[0], deficit[1], deficit[2], deficit[3]); + + rv = simple_read_from_buffer(userbuf, count, ppos, buf, p - buf); + kfree(buf); + return rv; +} + +static ssize_t sta_airtime_write(struct file *file, const char __user *userbuf, + size_t count, loff_t *ppos) +{ + struct sta_info *sta = file->private_data; + struct ieee80211_local *local = sta->sdata->local; + int ac; + + for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) { + spin_lock_bh(&local->active_txq_lock[ac]); + sta->airtime[ac].rx_airtime = 0; + sta->airtime[ac].tx_airtime = 0; + sta->airtime[ac].deficit = sta->airtime_weight; + spin_unlock_bh(&local->active_txq_lock[ac]); + } + + return count; +} +STA_OPS_RW(airtime); + +static ssize_t sta_aql_read(struct file *file, char __user *userbuf, + size_t count, loff_t *ppos) +{ + struct sta_info *sta = file->private_data; + struct ieee80211_local *local = sta->sdata->local; + size_t bufsz = 400; + char *buf = kzalloc(bufsz, GFP_KERNEL), *p = buf; + u32 q_depth[IEEE80211_NUM_ACS]; + u32 q_limit_l[IEEE80211_NUM_ACS], q_limit_h[IEEE80211_NUM_ACS]; + ssize_t rv; + int ac; + + if (!buf) + return -ENOMEM; + + for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) { + spin_lock_bh(&local->active_txq_lock[ac]); q_limit_l[ac] = sta->airtime[ac].aql_limit_low; q_limit_h[ac] = sta->airtime[ac].aql_limit_high; spin_unlock_bh(&local->active_txq_lock[ac]); @@ -221,12 +269,8 @@ static ssize_t sta_airtime_read(struct file *file, char __user *userbuf, } p += scnprintf(p, bufsz + buf - p, - "RX: %llu us\nTX: %llu us\nWeight: %u\n" - "Deficit: VO: %lld us VI: %lld us BE: %lld us BK: %lld us\n" "Q depth: VO: %u us VI: %u us BE: %u us BK: %u us\n" "Q limit[low/high]: VO: %u/%u VI: %u/%u BE: %u/%u BK: %u/%u\n", - rx_airtime, tx_airtime, sta->airtime_weight, - deficit[0], deficit[1], deficit[2], deficit[3], q_depth[0], q_depth[1], q_depth[2], q_depth[3], q_limit_l[0], q_limit_h[0], q_limit_l[1], q_limit_h[1], q_limit_l[2], q_limit_h[2], q_limit_l[3], q_limit_h[3]), @@ -236,11 +280,10 @@ static ssize_t sta_airtime_read(struct file *file, char __user *userbuf, return rv; } -static ssize_t sta_airtime_write(struct file *file, const char __user *userbuf, +static ssize_t sta_aql_write(struct file *file, const char __user *userbuf, size_t count, loff_t *ppos) { struct sta_info *sta = file->private_data; - struct ieee80211_local *local = sta->sdata->local; u32 ac, q_limit_l, q_limit_h; char _buf[100] = {}, *buf = _buf; @@ -251,7 +294,7 @@ static ssize_t sta_airtime_write(struct file *file, const char __user *userbuf, return -EFAULT; buf[sizeof(_buf) - 1] = '\0'; - if (sscanf(buf, "queue limit %u %u %u", &ac, &q_limit_l, &q_limit_h) + if (sscanf(buf, "limit %u %u %u", &ac, &q_limit_l, &q_limit_h) != 3) return -EINVAL; @@ -261,17 +304,10 @@ static ssize_t sta_airtime_write(struct file *file, const char __user *userbuf, sta->airtime[ac].aql_limit_low = q_limit_l; sta->airtime[ac].aql_limit_high = q_limit_h; - for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) { - spin_lock_bh(&local->active_txq_lock[ac]); - sta->airtime[ac].rx_airtime = 0; - sta->airtime[ac].tx_airtime = 0; - sta->airtime[ac].deficit = sta->airtime_weight; - spin_unlock_bh(&local->active_txq_lock[ac]); - } - return count; } -STA_OPS_RW(airtime); +STA_OPS_RW(aql); + static ssize_t sta_agg_status_read(struct file *file, char __user *userbuf, size_t count, loff_t *ppos) @@ -996,6 +1032,10 @@ void ieee80211_sta_debugfs_add(struct sta_info *sta) NL80211_EXT_FEATURE_AIRTIME_FAIRNESS)) DEBUGFS_ADD(airtime); + if (wiphy_ext_feature_isset(local->hw.wiphy, + NL80211_EXT_FEATURE_AQL)) + DEBUGFS_ADD(aql); + debugfs_create_xul("driver_buffered_tids", 0400, sta->debugfs_dir, &sta->driver_buffered_tids); diff --git a/net/mac80211/main.c b/net/mac80211/main.c index 6cca0853f183f..4c2b5ba3ac094 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -672,9 +672,7 @@ struct ieee80211_hw *ieee80211_alloc_hw_nm(size_t priv_data_len, IEEE80211_DEFAULT_AQL_TXQ_LIMIT_H; } - local->airtime_flags = AIRTIME_USE_TX | - AIRTIME_USE_RX | - AIRTIME_USE_AQL; + local->airtime_flags = AIRTIME_USE_TX | AIRTIME_USE_RX; local->aql_threshold = IEEE80211_AQL_THRESHOLD; atomic_set(&local->aql_total_pending_airtime, 0); diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index 8eafd81e97b48..0f5f406788852 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -1916,6 +1916,9 @@ void ieee80211_sta_update_pending_airtime(struct ieee80211_local *local, { int tx_pending; + if (!wiphy_ext_feature_isset(local->hw.wiphy, NL80211_EXT_FEATURE_AQL)) + return; + if (!tx_completed) { if (sta) atomic_add(tx_airtime, diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h index ad5d8a4ae56d1..c00e28585f9db 100644 --- a/net/mac80211/sta_info.h +++ b/net/mac80211/sta_info.h @@ -127,7 +127,6 @@ enum ieee80211_agg_stop_reason { /* Debugfs flags to enable/disable use of RX/TX airtime in scheduler */ #define AIRTIME_USE_TX BIT(0) #define AIRTIME_USE_RX BIT(1) -#define AIRTIME_USE_AQL BIT(2) struct airtime_info { u64 rx_airtime; diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 38b58a00db46c..a8a7306a1f56c 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -3677,7 +3677,7 @@ begin: IEEE80211_SKB_CB(skb)->control.vif = vif; - if (local->airtime_flags & AIRTIME_USE_AQL) { + if (wiphy_ext_feature_isset(local->hw.wiphy, NL80211_EXT_FEATURE_AQL)) { u32 airtime; airtime = ieee80211_calc_expected_tx_airtime(hw, vif, txq->sta, @@ -3799,7 +3799,7 @@ bool ieee80211_txq_airtime_check(struct ieee80211_hw *hw, struct sta_info *sta; struct ieee80211_local *local = hw_to_local(hw); - if (!(local->airtime_flags & AIRTIME_USE_AQL)) + if (!wiphy_ext_feature_isset(local->hw.wiphy, NL80211_EXT_FEATURE_AQL)) return true; if (!txq->sta) -- GitLab From 6ba2fd391ac58c1a26874f10c3054a1ea4aca2d0 Mon Sep 17 00:00:00 2001 From: Alexandre Torgue Date: Wed, 4 Dec 2019 15:41:06 +0100 Subject: [PATCH 0574/1437] pinctrl: pinmux: fix a possible null pointer in pinmux_can_be_used_for_gpio This commit adds a check on ops pointer to avoid a kernel panic when ops->strict is used. Indeed, on some pinctrl driver (at least for pinctrl-stmfx) the pinmux ops is not implemented. Let's assume than gpio can be used in this case. Fixes: 472a61e777fe ("pinctrl/gpio: Take MUX usage into account") Signed-off-by: Alexandre Torgue Link: https://lore.kernel.org/r/20191204144106.10876-1-alexandre.torgue@st.com Signed-off-by: Linus Walleij --- drivers/pinctrl/pinmux.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pinctrl/pinmux.c b/drivers/pinctrl/pinmux.c index e914f6efd39e8..9503ddf2edc76 100644 --- a/drivers/pinctrl/pinmux.c +++ b/drivers/pinctrl/pinmux.c @@ -85,7 +85,7 @@ bool pinmux_can_be_used_for_gpio(struct pinctrl_dev *pctldev, unsigned pin) const struct pinmux_ops *ops = pctldev->desc->pmxops; /* Can't inspect pin, assume it can be used */ - if (!desc) + if (!desc || !ops) return true; if (ops->strict && desc->mux_usecount) -- GitLab From d5a362149c4db2939a3d97e63fd2108489c60b41 Mon Sep 17 00:00:00 2001 From: Rahul Tanwar Date: Thu, 5 Dec 2019 11:01:31 +0800 Subject: [PATCH 0575/1437] pinctrl: Modify Kconfig to fix linker error Fix below linker error ld: drivers/pinctrl/pinctrl-equilibrium.o: in function `pinconf_generic_dt_node_to_map_all': pinctrl-equilibrium.c:(.text+0xb): undefined reference to `pinconf_generic_dt_node_to_map' Caused by below commit 1948d5c51dba ("pinctrl: Add pinmux & GPIO controller driver for a new SoC") by adding 'depends on OF' in Kconfig driver entry. Reported-by: Randy Dunlap > Signed-off-by: Rahul Tanwar Link: https://lore.kernel.org/r/ba937f271d1a2173828a2325990d62cb36d61595.1575514110.git.rahul.tanwar@linux.intel.com Acked-by: Randy Dunlap # build-tested Signed-off-by: Linus Walleij --- drivers/pinctrl/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig index 3bfbf2ff6e2bd..ba0cad4bd0720 100644 --- a/drivers/pinctrl/Kconfig +++ b/drivers/pinctrl/Kconfig @@ -422,6 +422,7 @@ config PINCTRL_TB10X config PINCTRL_EQUILIBRIUM tristate "Generic pinctrl and GPIO driver for Intel Lightning Mountain SoC" + depends on OF select PINMUX select PINCONF select GPIOLIB -- GitLab From 14c73bd344da60abaf7da3ea2e7733ddda35bbac Mon Sep 17 00:00:00 2001 From: Srikar Dronamraju Date: Thu, 5 Dec 2019 14:02:17 +0530 Subject: [PATCH 0576/1437] powerpc/vcpu: Assume dedicated processors as non-preempt With commit 247f2f6f3c70 ("sched/core: Don't schedule threads on pre-empted vCPUs"), the scheduler avoids preempted vCPUs to schedule tasks on wakeup. This leads to wrong choice of CPU, which in-turn leads to larger wakeup latencies. Eventually, it leads to performance regression in latency sensitive benchmarks like soltp, schbench etc. On Powerpc, vcpu_is_preempted() only looks at yield_count. If the yield_count is odd, the vCPU is assumed to be preempted. However yield_count is increased whenever the LPAR enters CEDE state (idle). So any CPU that has entered CEDE state is assumed to be preempted. Even if vCPU of dedicated LPAR is preempted/donated, it should have right of first-use since they are supposed to own the vCPU. On a Power9 System with 32 cores: # lscpu Architecture: ppc64le Byte Order: Little Endian CPU(s): 128 On-line CPU(s) list: 0-127 Thread(s) per core: 8 Core(s) per socket: 1 Socket(s): 16 NUMA node(s): 2 Model: 2.2 (pvr 004e 0202) Model name: POWER9 (architected), altivec supported Hypervisor vendor: pHyp Virtualization type: para L1d cache: 32K L1i cache: 32K L2 cache: 512K L3 cache: 10240K NUMA node0 CPU(s): 0-63 NUMA node1 CPU(s): 64-127 # perf stat -a -r 5 ./schbench v5.4 v5.4 + patch Latency percentiles (usec) Latency percentiles (usec) 50.0000th: 45 50.0th: 45 75.0000th: 62 75.0th: 63 90.0000th: 71 90.0th: 74 95.0000th: 77 95.0th: 78 *99.0000th: 91 *99.0th: 82 99.5000th: 707 99.5th: 83 99.9000th: 6920 99.9th: 86 min=0, max=10048 min=0, max=96 Latency percentiles (usec) Latency percentiles (usec) 50.0000th: 45 50.0th: 46 75.0000th: 61 75.0th: 64 90.0000th: 72 90.0th: 75 95.0000th: 79 95.0th: 79 *99.0000th: 691 *99.0th: 83 99.5000th: 3972 99.5th: 85 99.9000th: 8368 99.9th: 91 min=0, max=16606 min=0, max=117 Latency percentiles (usec) Latency percentiles (usec) 50.0000th: 45 50.0th: 46 75.0000th: 61 75.0th: 64 90.0000th: 71 90.0th: 75 95.0000th: 77 95.0th: 79 *99.0000th: 106 *99.0th: 83 99.5000th: 2364 99.5th: 84 99.9000th: 7480 99.9th: 90 min=0, max=10001 min=0, max=95 Latency percentiles (usec) Latency percentiles (usec) 50.0000th: 45 50.0th: 47 75.0000th: 62 75.0th: 65 90.0000th: 72 90.0th: 75 95.0000th: 78 95.0th: 79 *99.0000th: 93 *99.0th: 84 99.5000th: 108 99.5th: 85 99.9000th: 6792 99.9th: 90 min=0, max=17681 min=0, max=117 Latency percentiles (usec) Latency percentiles (usec) 50.0000th: 46 50.0th: 45 75.0000th: 62 75.0th: 64 90.0000th: 73 90.0th: 75 95.0000th: 79 95.0th: 79 *99.0000th: 113 *99.0th: 82 99.5000th: 2724 99.5th: 83 99.9000th: 6184 99.9th: 93 min=0, max=9887 min=0, max=111 Performance counter stats for 'system wide' (5 runs): context-switches 43,373 ( +- 0.40% ) 44,597 ( +- 0.55% ) cpu-migrations 1,211 ( +- 5.04% ) 220 ( +- 6.23% ) page-faults 15,983 ( +- 5.21% ) 15,360 ( +- 3.38% ) Waiman Long suggested using static_keys. Fixes: 247f2f6f3c70 ("sched/core: Don't schedule threads on pre-empted vCPUs") Cc: stable@vger.kernel.org # v4.18+ Reported-by: Parth Shah Reported-by: Ihor Pasichnyk Tested-by: Juri Lelli Acked-by: Waiman Long Reviewed-by: Gautham R. Shenoy Signed-off-by: Srikar Dronamraju Acked-by: Phil Auld Reviewed-by: Vaidyanathan Srinivasan Tested-by: Parth Shah [mpe: Move the key and setting of the key to pseries/setup.c] Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20191213035036.6913-1-mpe@ellerman.id.au --- arch/powerpc/include/asm/spinlock.h | 4 +++- arch/powerpc/platforms/pseries/setup.c | 7 +++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/arch/powerpc/include/asm/spinlock.h b/arch/powerpc/include/asm/spinlock.h index e9a960e28f3c8..cac95a3f30c26 100644 --- a/arch/powerpc/include/asm/spinlock.h +++ b/arch/powerpc/include/asm/spinlock.h @@ -36,10 +36,12 @@ #endif #ifdef CONFIG_PPC_PSERIES +DECLARE_STATIC_KEY_FALSE(shared_processor); + #define vcpu_is_preempted vcpu_is_preempted static inline bool vcpu_is_preempted(int cpu) { - if (!firmware_has_feature(FW_FEATURE_SPLPAR)) + if (!static_branch_unlikely(&shared_processor)) return false; return !!(be32_to_cpu(lppaca_of(cpu).yield_count) & 1); } diff --git a/arch/powerpc/platforms/pseries/setup.c b/arch/powerpc/platforms/pseries/setup.c index 0a40201f315ff..0c8421dd01ab5 100644 --- a/arch/powerpc/platforms/pseries/setup.c +++ b/arch/powerpc/platforms/pseries/setup.c @@ -74,6 +74,9 @@ #include "pseries.h" #include "../../../../drivers/pci/pci.h" +DEFINE_STATIC_KEY_FALSE(shared_processor); +EXPORT_SYMBOL_GPL(shared_processor); + int CMO_PrPSP = -1; int CMO_SecPSP = -1; unsigned long CMO_PageSize = (ASM_CONST(1) << IOMMU_PAGE_SHIFT_4K); @@ -758,6 +761,10 @@ static void __init pSeries_setup_arch(void) if (firmware_has_feature(FW_FEATURE_LPAR)) { vpa_init(boot_cpuid); + + if (lppaca_shared_proc(get_lppaca())) + static_branch_enable(&shared_processor); + ppc_md.power_save = pseries_lpar_idle; ppc_md.enable_pmcs = pseries_lpar_enable_pmcs; #ifdef CONFIG_PCI_IOV -- GitLab From 656c21d6af5d9279dd7b51ca7a4a71008127044b Mon Sep 17 00:00:00 2001 From: Srikar Dronamraju Date: Thu, 5 Dec 2019 14:02:18 +0530 Subject: [PATCH 0577/1437] powerpc/shared: Use static key to detect shared processor With the static key shared processor available, is_shared_processor() can return without having to query the lppaca structure. Signed-off-by: Srikar Dronamraju Acked-by: Phil Auld Acked-by: Waiman Long Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20191213035036.6913-2-mpe@ellerman.id.au --- arch/powerpc/include/asm/spinlock.h | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/arch/powerpc/include/asm/spinlock.h b/arch/powerpc/include/asm/spinlock.h index cac95a3f30c26..1b55fc08f8532 100644 --- a/arch/powerpc/include/asm/spinlock.h +++ b/arch/powerpc/include/asm/spinlock.h @@ -112,13 +112,8 @@ static inline void splpar_rw_yield(arch_rwlock_t *lock) {}; static inline bool is_shared_processor(void) { -/* - * LPPACA is only available on Pseries so guard anything LPPACA related to - * allow other platforms (which include this common header) to compile. - */ -#ifdef CONFIG_PPC_PSERIES - return (IS_ENABLED(CONFIG_PPC_SPLPAR) && - lppaca_shared_proc(local_paca->lppaca_ptr)); +#ifdef CONFIG_PPC_SPLPAR + return static_branch_unlikely(&shared_processor); #else return false; #endif -- GitLab From 8fabc623238e68b3ac63c0dd1657bf86c1fa33af Mon Sep 17 00:00:00 2001 From: Mike Rapoport Date: Wed, 4 Dec 2019 14:35:24 +0200 Subject: [PATCH 0578/1437] powerpc: Ensure that swiotlb buffer is allocated from low memory Some powerpc platforms (e.g. 85xx) limit DMA-able memory way below 4G. If a system has more physical memory than this limit, the swiotlb buffer is not addressable because it is allocated from memblock using top-down mode. Force memblock to bottom-up mode before calling swiotlb_init() to ensure that the swiotlb buffer is DMA-able. Reported-by: Christian Zigotzky Signed-off-by: Mike Rapoport Reviewed-by: Christoph Hellwig Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20191204123524.22919-1-rppt@kernel.org --- arch/powerpc/mm/mem.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/arch/powerpc/mm/mem.c b/arch/powerpc/mm/mem.c index 9488b63dfc872..617c2777926fc 100644 --- a/arch/powerpc/mm/mem.c +++ b/arch/powerpc/mm/mem.c @@ -289,6 +289,14 @@ void __init mem_init(void) BUILD_BUG_ON(MMU_PAGE_COUNT > 16); #ifdef CONFIG_SWIOTLB + /* + * Some platforms (e.g. 85xx) limit DMA-able memory way below + * 4G. We force memblock to bottom-up mode to ensure that the + * memory allocated in swiotlb_init() is DMA-able. + * As it's the last memblock allocation, no need to reset it + * back to to-down. + */ + memblock_set_bottom_up(true); swiotlb_init(0); #endif -- GitLab From 37d02592f11bb76e4ab1dcaa5b8a2a0715403207 Mon Sep 17 00:00:00 2001 From: Nikolay Borisov Date: Mon, 18 Nov 2019 14:16:44 +0200 Subject: [PATCH 0579/1437] btrfs: Fix error messages in qgroup_rescan_init The branch of qgroup_rescan_init which is executed from the mount path prints wrong errors messages. The textual print out in case BTRFS_QGROUP_STATUS_FLAG_RESCAN/BTRFS_QGROUP_STATUS_FLAG_ON are not set are transposed. Fix it by exchanging their place. Signed-off-by: Nikolay Borisov Reviewed-by: David Sterba Signed-off-by: David Sterba --- fs/btrfs/qgroup.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/btrfs/qgroup.c b/fs/btrfs/qgroup.c index 93aeb2e539a4c..d4282e12f2a64 100644 --- a/fs/btrfs/qgroup.c +++ b/fs/btrfs/qgroup.c @@ -3232,12 +3232,12 @@ qgroup_rescan_init(struct btrfs_fs_info *fs_info, u64 progress_objectid, if (!(fs_info->qgroup_flags & BTRFS_QGROUP_STATUS_FLAG_RESCAN)) { btrfs_warn(fs_info, - "qgroup rescan init failed, qgroup is not enabled"); + "qgroup rescan init failed, qgroup rescan is not queued"); ret = -EINVAL; } else if (!(fs_info->qgroup_flags & BTRFS_QGROUP_STATUS_FLAG_ON)) { btrfs_warn(fs_info, - "qgroup rescan init failed, qgroup rescan is not queued"); + "qgroup rescan init failed, qgroup is not enabled"); ret = -EINVAL; } -- GitLab From fcb970581dd900675c4371c2b688a57924a8368c Mon Sep 17 00:00:00 2001 From: Filipe Manana Date: Thu, 5 Dec 2019 16:57:39 +0000 Subject: [PATCH 0580/1437] Btrfs: fix cloning range with a hole when using the NO_HOLES feature When using the NO_HOLES feature if we clone a range that contains a hole and a temporary ENOSPC happens while dropping extents from the target inode's range, we can end up failing and aborting the transaction with -EEXIST or with a corrupt file extent item, that has a length greater than it should and overlaps with other extents. For example when cloning the following range from inode A to inode B: Inode A: extent A1 extent A2 [ ----------- ] [ hole, implicit, 4MB length ] [ ------------- ] 0 1MB 5MB 6MB Range to clone: [1MB, 6MB) Inode B: extent B1 extent B2 extent B3 extent B4 [ ---------- ] [ --------- ] [ ---------- ] [ ---------- ] 0 1MB 1MB 2MB 2MB 5MB 5MB 6MB Target range: [1MB, 6MB) (same as source, to make it easier to explain) The following can happen: 1) btrfs_punch_hole_range() gets -ENOSPC from __btrfs_drop_extents(); 2) At that point, 'cur_offset' is set to 1MB and __btrfs_drop_extents() set 'drop_end' to 2MB, meaning it was able to drop only extent B2; 3) We then compute 'clone_len' as 'drop_end' - 'cur_offset' = 2MB - 1MB = 1MB; 4) We then attempt to insert a file extent item at inode B with a file offset of 5MB, which is the value of clone_info->file_offset. This fails with error -EEXIST because there's already an extent at that offset (extent B4); 5) We abort the current transaction with -EEXIST and return that error to user space as well. Another example, for extent corruption: Inode A: extent A1 extent A2 [ ----------- ] [ hole, implicit, 10MB length ] [ ------------- ] 0 1MB 11MB 12MB Inode B: extent B1 extent B2 [ ----------- ] [ --------- ] [ ----------------------------- ] 0 1MB 1MB 5MB 5MB 12MB Target range: [1MB, 12MB) (same as source, to make it easier to explain) 1) btrfs_punch_hole_range() gets -ENOSPC from __btrfs_drop_extents(); 2) At that point, 'cur_offset' is set to 1MB and __btrfs_drop_extents() set 'drop_end' to 5MB, meaning it was able to drop only extent B2; 3) We then compute 'clone_len' as 'drop_end' - 'cur_offset' = 5MB - 1MB = 4MB; 4) We then insert a file extent item at inode B with a file offset of 11MB which is the value of clone_info->file_offset, and a length of 4MB (the value of 'clone_len'). So we get 2 extents items with ranges that overlap and an extent length of 4MB, larger then the extent A2 from inode A (1MB length); 5) After that we end the transaction, balance the btree dirty pages and then start another or join the previous transaction. It might happen that the transaction which inserted the incorrect extent was committed by another task so we end up with extent corruption if a power failure happens. So fix this by making sure we attempt to insert the extent to clone at the destination inode only if we are past dropping the sub-range that corresponds to a hole. Fixes: 690a5dbfc51315 ("Btrfs: fix ENOSPC errors, leading to transaction aborts, when cloning extents") Signed-off-by: Filipe Manana Signed-off-by: David Sterba --- fs/btrfs/file.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c index 0cb43b6827897..8d47c76b7bd10 100644 --- a/fs/btrfs/file.c +++ b/fs/btrfs/file.c @@ -2599,8 +2599,8 @@ int btrfs_punch_hole_range(struct inode *inode, struct btrfs_path *path, } } - if (clone_info) { - u64 clone_len = drop_end - cur_offset; + if (clone_info && drop_end > clone_info->file_offset) { + u64 clone_len = drop_end - clone_info->file_offset; ret = btrfs_insert_clone_extent(trans, inode, path, clone_info, clone_len); -- GitLab From fa2cdb1762d15f701b83efa60b04f0d04e71bf89 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Mon, 9 Dec 2019 11:13:27 +0100 Subject: [PATCH 0581/1437] ARM: shmobile: defconfig: Restore debugfs support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since commit 0e4a459f56c32d3e ("tracing: Remove unnecessary DEBUG_FS dependency"), CONFIG_DEBUG_FS is no longer auto-enabled. This breaks booting Debian 9, as systemd needs debugfs: [FAILED] Failed to mount /sys/kernel/debug. See 'systemctl status sys-kernel-debug.mount' for details. [DEPEND] Dependency failed for Local File Systems. ... You are in emergGive root password for maintenance (or press Control-D to continue): Fix this by enabling CONFIG_DEBUG_FS explicitly. See also commit 18977008f44c66bd ("ARM: multi_v7_defconfig: Restore debugfs support"). Signed-off-by: Geert Uytterhoeven Reviewed-by: Niklas Söderlund Link: https://lore.kernel.org/r/20191209101327.26571-1-geert+renesas@glider.be --- arch/arm/configs/shmobile_defconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm/configs/shmobile_defconfig b/arch/arm/configs/shmobile_defconfig index bda57cafa2bcb..de3830443613e 100644 --- a/arch/arm/configs/shmobile_defconfig +++ b/arch/arm/configs/shmobile_defconfig @@ -212,4 +212,5 @@ CONFIG_DMA_CMA=y CONFIG_CMA_SIZE_MBYTES=64 CONFIG_PRINTK_TIME=y # CONFIG_ENABLE_MUST_CHECK is not set +CONFIG_DEBUG_FS=y CONFIG_DEBUG_KERNEL=y -- GitLab From f72ff01df9cf5db25c76674cac16605992d15467 Mon Sep 17 00:00:00 2001 From: Josef Bacik Date: Tue, 19 Nov 2019 13:59:35 -0500 Subject: [PATCH 0582/1437] btrfs: do not call synchronize_srcu() in inode_tree_del Testing with the new fsstress uncovered a pretty nasty deadlock with lookup and snapshot deletion. Process A unlink -> final iput -> inode_tree_del -> synchronize_srcu(subvol_srcu) Process B btrfs_lookup <- srcu_read_lock() acquired here -> btrfs_iget -> find inode that has I_FREEING set -> __wait_on_freeing_inode() We're holding the srcu_read_lock() while doing the iget in order to make sure our fs root doesn't go away, and then we are waiting for the inode to finish freeing. However because the free'ing process is doing a synchronize_srcu() we deadlock. Fix this by dropping the synchronize_srcu() in inode_tree_del(). We don't need people to stop accessing the fs root at this point, we're only adding our empty root to the dead roots list. A larger much more invasive fix is forthcoming to address how we deal with fs roots, but this fixes the immediate problem. Fixes: 76dda93c6ae2 ("Btrfs: add snapshot/subvolume destroy ioctl") CC: stable@vger.kernel.org # 4.4+ Signed-off-by: Josef Bacik Reviewed-by: David Sterba Signed-off-by: David Sterba --- fs/btrfs/inode.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 56032c518b267..5766c2d19896a 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -5728,7 +5728,6 @@ static void inode_tree_add(struct inode *inode) static void inode_tree_del(struct inode *inode) { - struct btrfs_fs_info *fs_info = btrfs_sb(inode->i_sb); struct btrfs_root *root = BTRFS_I(inode)->root; int empty = 0; @@ -5741,7 +5740,6 @@ static void inode_tree_del(struct inode *inode) spin_unlock(&root->inode_lock); if (empty && btrfs_root_refs(&root->root_item) == 0) { - synchronize_srcu(&fs_info->subvol_srcu); spin_lock(&root->inode_lock); empty = RB_EMPTY_ROOT(&root->inode_tree); spin_unlock(&root->inode_lock); -- GitLab From db8fe64f9ce61d1d89d3c3c34d111a43afb9f053 Mon Sep 17 00:00:00 2001 From: Josef Bacik Date: Tue, 19 Nov 2019 13:59:00 -0500 Subject: [PATCH 0583/1437] btrfs: handle error in btrfs_cache_block_group We have a BUG_ON(ret < 0) in find_free_extent from btrfs_cache_block_group. If we fail to allocate our ctl we'll just panic, which is not good. Instead just go on to another block group. If we fail to find a block group we don't want to return ENOSPC, because really we got a ENOMEM and that's the root of the problem. Save our return from btrfs_cache_block_group(), and then if we still fail to make our allocation return that ret so we get the right error back. Tested with inject-error.py from bcc. Reviewed-by: Johannes Thumshirn Signed-off-by: Josef Bacik Reviewed-by: David Sterba Signed-off-by: David Sterba --- fs/btrfs/extent-tree.c | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c index 153f71a5bba91..18df434bfe52f 100644 --- a/fs/btrfs/extent-tree.c +++ b/fs/btrfs/extent-tree.c @@ -3799,6 +3799,7 @@ static noinline int find_free_extent(struct btrfs_fs_info *fs_info, u64 flags, int delalloc) { int ret = 0; + int cache_block_group_error = 0; struct btrfs_free_cluster *last_ptr = NULL; struct btrfs_block_group *block_group = NULL; struct find_free_extent_ctl ffe_ctl = {0}; @@ -3958,7 +3959,20 @@ have_block_group: if (unlikely(!ffe_ctl.cached)) { ffe_ctl.have_caching_bg = true; ret = btrfs_cache_block_group(block_group, 0); - BUG_ON(ret < 0); + + /* + * If we get ENOMEM here or something else we want to + * try other block groups, because it may not be fatal. + * However if we can't find anything else we need to + * save our return here so that we return the actual + * error that caused problems, not ENOSPC. + */ + if (ret < 0) { + if (!cache_block_group_error) + cache_block_group_error = ret; + ret = 0; + goto loop; + } ret = 0; } @@ -4045,7 +4059,7 @@ loop: if (ret > 0) goto search; - if (ret == -ENOSPC) { + if (ret == -ENOSPC && !cache_block_group_error) { /* * Use ffe_ctl->total_free_space as fallback if we can't find * any contiguous hole. @@ -4056,6 +4070,8 @@ loop: space_info->max_extent_size = ffe_ctl.max_extent_size; spin_unlock(&space_info->lock); ins->offset = ffe_ctl.max_extent_size; + } else if (ret == -ENOSPC) { + ret = cache_block_group_error; } return ret; } -- GitLab From 943eb3bf25f4a7b745dd799e031be276aa104d82 Mon Sep 17 00:00:00 2001 From: Josef Bacik Date: Tue, 19 Nov 2019 13:59:20 -0500 Subject: [PATCH 0584/1437] btrfs: don't double lock the subvol_sem for rename exchange If we're rename exchanging two subvols we'll try to lock this lock twice, which is bad. Just lock once if either of the ino's are subvols. Fixes: cdd1fedf8261 ("btrfs: add support for RENAME_EXCHANGE and RENAME_WHITEOUT") CC: stable@vger.kernel.org # 4.4+ Signed-off-by: Josef Bacik Reviewed-by: David Sterba Signed-off-by: David Sterba --- fs/btrfs/inode.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 5766c2d19896a..e3c76645cad76 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -9554,9 +9554,8 @@ static int btrfs_rename_exchange(struct inode *old_dir, btrfs_init_log_ctx(&ctx_dest, new_inode); /* close the race window with snapshot create/destroy ioctl */ - if (old_ino == BTRFS_FIRST_FREE_OBJECTID) - down_read(&fs_info->subvol_sem); - if (new_ino == BTRFS_FIRST_FREE_OBJECTID) + if (old_ino == BTRFS_FIRST_FREE_OBJECTID || + new_ino == BTRFS_FIRST_FREE_OBJECTID) down_read(&fs_info->subvol_sem); /* @@ -9790,9 +9789,8 @@ out_fail: ret = ret ? ret : ret2; } out_notrans: - if (new_ino == BTRFS_FIRST_FREE_OBJECTID) - up_read(&fs_info->subvol_sem); - if (old_ino == BTRFS_FIRST_FREE_OBJECTID) + if (new_ino == BTRFS_FIRST_FREE_OBJECTID || + old_ino == BTRFS_FIRST_FREE_OBJECTID) up_read(&fs_info->subvol_sem); ASSERT(list_empty(&ctx_root.list)); -- GitLab From 994bf9cd78aa382c0c3c70b7cf9c00f7529176c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20F=C3=A4rber?= Date: Fri, 8 Nov 2019 22:38:52 +0100 Subject: [PATCH 0585/1437] btrfs: tree-checker: Fix error format string for size_t MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Argument BTRFS_FILE_EXTENT_INLINE_DATA_START is defined as offsetof(), which returns type size_t, so we need %zu instead of %lu. This fixes a build warning on 32-bit ARM: ../fs/btrfs/tree-checker.c: In function 'check_extent_data_item': ../fs/btrfs/tree-checker.c:230:43: warning: format '%lu' expects argument of type 'long unsigned int', but argument 5 has type 'unsigned int' [-Wformat=] 230 | "invalid item size, have %u expect [%lu, %u)", | ~~^ | long unsigned int | %u Fixes: 153a6d299956 ("btrfs: tree-checker: Check item size before reading file extent type") Acked-by: Geert Uytterhoeven Signed-off-by: Andreas Färber Reviewed-by: David Sterba Signed-off-by: David Sterba --- fs/btrfs/tree-checker.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/btrfs/tree-checker.c b/fs/btrfs/tree-checker.c index 493d4d9e0f79c..092b8ece36d72 100644 --- a/fs/btrfs/tree-checker.c +++ b/fs/btrfs/tree-checker.c @@ -227,7 +227,7 @@ static int check_extent_data_item(struct extent_buffer *leaf, */ if (item_size < BTRFS_FILE_EXTENT_INLINE_DATA_START) { file_extent_err(leaf, slot, - "invalid item size, have %u expect [%lu, %u)", + "invalid item size, have %u expect [%zu, %u)", item_size, BTRFS_FILE_EXTENT_INLINE_DATA_START, SZ_4K); return -EUCLEAN; -- GitLab From cf93e15eca0bcb457b6a85b3480c1e379407dd26 Mon Sep 17 00:00:00 2001 From: David Sterba Date: Wed, 27 Nov 2019 16:10:54 +0100 Subject: [PATCH 0586/1437] btrfs: fix devs_max constraints for raid1c3 and raid1c4 The value 0 for devs_max means to spread the allocated chunks over all available devices, eg. stripe for RAID0 or RAID5. This got mistakenly copied to the RAID1C3/4 profiles. The intention is to have exactly 3 and 4 copies respectively. Fixes: 47e6f7423b91 ("btrfs: add support for 3-copy replication (raid1c3)") Fixes: 8d6fac0087e5 ("btrfs: add support for 4-copy replication (raid1c4)") Signed-off-by: David Sterba --- fs/btrfs/volumes.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c index d8e5560db285a..a6d3f08bfff37 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c @@ -61,7 +61,7 @@ const struct btrfs_raid_attr btrfs_raid_array[BTRFS_NR_RAID_TYPES] = { [BTRFS_RAID_RAID1C3] = { .sub_stripes = 1, .dev_stripes = 1, - .devs_max = 0, + .devs_max = 3, .devs_min = 3, .tolerated_failures = 2, .devs_increment = 3, @@ -73,7 +73,7 @@ const struct btrfs_raid_attr btrfs_raid_array[BTRFS_NR_RAID_TYPES] = { [BTRFS_RAID_RAID1C4] = { .sub_stripes = 1, .dev_stripes = 1, - .devs_max = 0, + .devs_max = 4, .devs_min = 4, .tolerated_failures = 3, .devs_increment = 4, -- GitLab From b6293c821ea8fa2a631a2112cd86cd435effeb8b Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Tue, 3 Dec 2019 14:24:58 +0300 Subject: [PATCH 0587/1437] btrfs: return error pointer from alloc_test_extent_buffer Callers of alloc_test_extent_buffer have not correctly interpreted the return value as error pointer, as alloc_test_extent_buffer should behave as alloc_extent_buffer. The self-tests were unaffected but btrfs_find_create_tree_block could call both functions and that would cause problems up in the call chain. Fixes: faa2dbf004e8 ("Btrfs: add sanity tests for new qgroup accounting code") CC: stable@vger.kernel.org # 4.4+ Signed-off-by: Dan Carpenter Reviewed-by: David Sterba Signed-off-by: David Sterba --- fs/btrfs/extent_io.c | 6 ++++-- fs/btrfs/tests/free-space-tree-tests.c | 4 ++-- fs/btrfs/tests/qgroup-tests.c | 4 ++-- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c index eb8bd0258360f..2f4802f405a24 100644 --- a/fs/btrfs/extent_io.c +++ b/fs/btrfs/extent_io.c @@ -5074,12 +5074,14 @@ struct extent_buffer *alloc_test_extent_buffer(struct btrfs_fs_info *fs_info, return eb; eb = alloc_dummy_extent_buffer(fs_info, start); if (!eb) - return NULL; + return ERR_PTR(-ENOMEM); eb->fs_info = fs_info; again: ret = radix_tree_preload(GFP_NOFS); - if (ret) + if (ret) { + exists = ERR_PTR(ret); goto free_eb; + } spin_lock(&fs_info->buffer_lock); ret = radix_tree_insert(&fs_info->buffer_radix, start >> PAGE_SHIFT, eb); diff --git a/fs/btrfs/tests/free-space-tree-tests.c b/fs/btrfs/tests/free-space-tree-tests.c index 1a846bf6e197b..914eea5ba6a7a 100644 --- a/fs/btrfs/tests/free-space-tree-tests.c +++ b/fs/btrfs/tests/free-space-tree-tests.c @@ -452,9 +452,9 @@ static int run_test(test_func_t test_func, int bitmaps, u32 sectorsize, root->fs_info->tree_root = root; root->node = alloc_test_extent_buffer(root->fs_info, nodesize); - if (!root->node) { + if (IS_ERR(root->node)) { test_std_err(TEST_ALLOC_EXTENT_BUFFER); - ret = -ENOMEM; + ret = PTR_ERR(root->node); goto out; } btrfs_set_header_level(root->node, 0); diff --git a/fs/btrfs/tests/qgroup-tests.c b/fs/btrfs/tests/qgroup-tests.c index 09aaca1efd62a..ac035a6fa003d 100644 --- a/fs/btrfs/tests/qgroup-tests.c +++ b/fs/btrfs/tests/qgroup-tests.c @@ -484,9 +484,9 @@ int btrfs_test_qgroups(u32 sectorsize, u32 nodesize) * *cough*backref walking code*cough* */ root->node = alloc_test_extent_buffer(root->fs_info, nodesize); - if (!root->node) { + if (IS_ERR(root->node)) { test_err("couldn't allocate dummy buffer"); - ret = -ENOMEM; + ret = PTR_ERR(root->node); goto out; } btrfs_set_header_level(root->node, 0); -- GitLab From 40e046acbd2f369cfbf93c3413639c66514cec2d Mon Sep 17 00:00:00 2001 From: Filipe Manana Date: Thu, 5 Dec 2019 16:58:30 +0000 Subject: [PATCH 0588/1437] Btrfs: fix missing data checksums after replaying a log tree When logging a file that has shared extents (reflinked with other files or with itself), we can end up logging multiple checksum items that cover overlapping ranges. This confuses the search for checksums at log replay time causing some checksums to never be added to the fs/subvolume tree. Consider the following example of a file that shares the same extent at offsets 0 and 256Kb: [ bytenr 13893632, offset 64Kb, len 64Kb ] 0 64Kb [ bytenr 13631488, offset 64Kb, len 192Kb ] 64Kb 256Kb [ bytenr 13893632, offset 0, len 256Kb ] 256Kb 512Kb When logging the inode, at tree-log.c:copy_items(), when processing the file extent item at offset 0, we log a checksum item covering the range 13959168 to 14024704, which corresponds to 13893632 + 64Kb and 13893632 + 64Kb + 64Kb, respectively. Later when processing the extent item at offset 256K, we log the checksums for the range from 13893632 to 14155776 (which corresponds to 13893632 + 256Kb). These checksums get merged with the checksum item for the range from 13631488 to 13893632 (13631488 + 256Kb), logged by a previous fsync. So after this we get the two following checksum items in the log tree: (...) item 6 key (EXTENT_CSUM EXTENT_CSUM 13631488) itemoff 3095 itemsize 512 range start 13631488 end 14155776 length 524288 item 7 key (EXTENT_CSUM EXTENT_CSUM 13959168) itemoff 3031 itemsize 64 range start 13959168 end 14024704 length 65536 The first one covers the range from the second one, they overlap. So far this does not cause a problem after replaying the log, because when replaying the file extent item for offset 256K, we copy all the checksums for the extent 13893632 from the log tree to the fs/subvolume tree, since searching for an checksum item for bytenr 13893632 leaves us at the first checksum item, which covers the whole range of the extent. However if we write 64Kb to file offset 256Kb for example, we will not be able to find and copy the checksums for the last 128Kb of the extent at bytenr 13893632, referenced by the file range 384Kb to 512Kb. After writing 64Kb into file offset 256Kb we get the following extent layout for our file: [ bytenr 13893632, offset 64K, len 64Kb ] 0 64Kb [ bytenr 13631488, offset 64Kb, len 192Kb ] 64Kb 256Kb [ bytenr 14155776, offset 0, len 64Kb ] 256Kb 320Kb [ bytenr 13893632, offset 64Kb, len 192Kb ] 320Kb 512Kb After fsync'ing the file, if we have a power failure and then mount the filesystem to replay the log, the following happens: 1) When replaying the file extent item for file offset 320Kb, we lookup for the checksums for the extent range from 13959168 (13893632 + 64Kb) to 14155776 (13893632 + 256Kb), through a call to btrfs_lookup_csums_range(); 2) btrfs_lookup_csums_range() finds the checksum item that starts precisely at offset 13959168 (item 7 in the log tree, shown before); 3) However that checksum item only covers 64Kb of data, and not 192Kb of data; 4) As a result only the checksums for the first 64Kb of data referenced by the file extent item are found and copied to the fs/subvolume tree. The remaining 128Kb of data, file range 384Kb to 512Kb, doesn't get the corresponding data checksums found and copied to the fs/subvolume tree. 5) After replaying the log userspace will not be able to read the file range from 384Kb to 512Kb, because the checksums are missing and resulting in an -EIO error. The following steps reproduce this scenario: $ mkfs.btrfs -f /dev/sdc $ mount /dev/sdc /mnt/sdc $ xfs_io -f -c "pwrite -S 0xa3 0 256K" /mnt/sdc/foobar $ xfs_io -c "fsync" /mnt/sdc/foobar $ xfs_io -c "pwrite -S 0xc7 256K 256K" /mnt/sdc/foobar $ xfs_io -c "reflink /mnt/sdc/foobar 320K 0 64K" /mnt/sdc/foobar $ xfs_io -c "fsync" /mnt/sdc/foobar $ xfs_io -c "pwrite -S 0xe5 256K 64K" /mnt/sdc/foobar $ xfs_io -c "fsync" /mnt/sdc/foobar $ mount /dev/sdc /mnt/sdc $ md5sum /mnt/sdc/foobar md5sum: /mnt/sdc/foobar: Input/output error $ dmesg | tail [165305.003464] BTRFS info (device sdc): no csum found for inode 257 start 401408 [165305.004014] BTRFS info (device sdc): no csum found for inode 257 start 405504 [165305.004559] BTRFS info (device sdc): no csum found for inode 257 start 409600 [165305.005101] BTRFS info (device sdc): no csum found for inode 257 start 413696 [165305.005627] BTRFS info (device sdc): no csum found for inode 257 start 417792 [165305.006134] BTRFS info (device sdc): no csum found for inode 257 start 421888 [165305.006625] BTRFS info (device sdc): no csum found for inode 257 start 425984 [165305.007278] BTRFS info (device sdc): no csum found for inode 257 start 430080 [165305.008248] BTRFS warning (device sdc): csum failed root 5 ino 257 off 393216 csum 0x1337385e expected csum 0x00000000 mirror 1 [165305.009550] BTRFS warning (device sdc): csum failed root 5 ino 257 off 393216 csum 0x1337385e expected csum 0x00000000 mirror 1 Fix this simply by deleting first any checksums, from the log tree, for the range of the extent we are logging at copy_items(). This ensures we do not get checksum items in the log tree that have overlapping ranges. This is a long time issue that has been present since we have the clone (and deduplication) ioctl, and can happen both when an extent is shared between different files and within the same file. A test case for fstests follows soon. CC: stable@vger.kernel.org # 4.4+ Signed-off-by: Filipe Manana Signed-off-by: David Sterba --- fs/btrfs/ctree.h | 2 +- fs/btrfs/extent-tree.c | 7 ++++--- fs/btrfs/file-item.c | 7 +++++-- fs/btrfs/tree-log.c | 29 ++++++++++++++++++++++++++--- 4 files changed, 36 insertions(+), 9 deletions(-) diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h index b2e8fd8a8e599..54efb21c27272 100644 --- a/fs/btrfs/ctree.h +++ b/fs/btrfs/ctree.h @@ -2787,7 +2787,7 @@ struct btrfs_inode_extref *btrfs_find_name_in_ext_backref( /* file-item.c */ struct btrfs_dio_private; int btrfs_del_csums(struct btrfs_trans_handle *trans, - struct btrfs_fs_info *fs_info, u64 bytenr, u64 len); + struct btrfs_root *root, u64 bytenr, u64 len); blk_status_t btrfs_lookup_bio_sums(struct inode *inode, struct bio *bio, u8 *dst); blk_status_t btrfs_lookup_bio_sums_dio(struct inode *inode, struct bio *bio, diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c index 18df434bfe52f..274318e9114ee 100644 --- a/fs/btrfs/extent-tree.c +++ b/fs/btrfs/extent-tree.c @@ -1869,8 +1869,8 @@ static int cleanup_ref_head(struct btrfs_trans_handle *trans, btrfs_pin_extent(fs_info, head->bytenr, head->num_bytes, 1); if (head->is_data) { - ret = btrfs_del_csums(trans, fs_info, head->bytenr, - head->num_bytes); + ret = btrfs_del_csums(trans, fs_info->csum_root, + head->bytenr, head->num_bytes); } } @@ -3175,7 +3175,8 @@ static int __btrfs_free_extent(struct btrfs_trans_handle *trans, btrfs_release_path(path); if (is_data) { - ret = btrfs_del_csums(trans, info, bytenr, num_bytes); + ret = btrfs_del_csums(trans, info->csum_root, bytenr, + num_bytes); if (ret) { btrfs_abort_transaction(trans, ret); goto out; diff --git a/fs/btrfs/file-item.c b/fs/btrfs/file-item.c index 3270a40b0777b..b1bfdc5c1387a 100644 --- a/fs/btrfs/file-item.c +++ b/fs/btrfs/file-item.c @@ -590,9 +590,9 @@ static noinline void truncate_one_csum(struct btrfs_fs_info *fs_info, * range of bytes. */ int btrfs_del_csums(struct btrfs_trans_handle *trans, - struct btrfs_fs_info *fs_info, u64 bytenr, u64 len) + struct btrfs_root *root, u64 bytenr, u64 len) { - struct btrfs_root *root = fs_info->csum_root; + struct btrfs_fs_info *fs_info = trans->fs_info; struct btrfs_path *path; struct btrfs_key key; u64 end_byte = bytenr + len; @@ -602,6 +602,9 @@ int btrfs_del_csums(struct btrfs_trans_handle *trans, u16 csum_size = btrfs_super_csum_size(fs_info->super_copy); int blocksize_bits = fs_info->sb->s_blocksize_bits; + ASSERT(root == fs_info->csum_root || + root->root_key.objectid == BTRFS_TREE_LOG_OBJECTID); + path = btrfs_alloc_path(); if (!path) return -ENOMEM; diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c index 6f757361db533..79866f1b33d6d 100644 --- a/fs/btrfs/tree-log.c +++ b/fs/btrfs/tree-log.c @@ -808,7 +808,8 @@ static noinline int replay_one_extent(struct btrfs_trans_handle *trans, struct btrfs_ordered_sum, list); if (!ret) - ret = btrfs_del_csums(trans, fs_info, + ret = btrfs_del_csums(trans, + fs_info->csum_root, sums->bytenr, sums->len); if (!ret) @@ -3909,6 +3910,28 @@ static int log_inode_item(struct btrfs_trans_handle *trans, return 0; } +static int log_csums(struct btrfs_trans_handle *trans, + struct btrfs_root *log_root, + struct btrfs_ordered_sum *sums) +{ + int ret; + + /* + * Due to extent cloning, we might have logged a csum item that covers a + * subrange of a cloned extent, and later we can end up logging a csum + * item for a larger subrange of the same extent or the entire range. + * This would leave csum items in the log tree that cover the same range + * and break the searches for checksums in the log tree, resulting in + * some checksums missing in the fs/subvolume tree. So just delete (or + * trim and adjust) any existing csum items in the log for this range. + */ + ret = btrfs_del_csums(trans, log_root, sums->bytenr, sums->len); + if (ret) + return ret; + + return btrfs_csum_file_blocks(trans, log_root, sums); +} + static noinline int copy_items(struct btrfs_trans_handle *trans, struct btrfs_inode *inode, struct btrfs_path *dst_path, @@ -4054,7 +4077,7 @@ static noinline int copy_items(struct btrfs_trans_handle *trans, struct btrfs_ordered_sum, list); if (!ret) - ret = btrfs_csum_file_blocks(trans, log, sums); + ret = log_csums(trans, log, sums); list_del(&sums->list); kfree(sums); } @@ -4274,7 +4297,7 @@ static int log_extent_csums(struct btrfs_trans_handle *trans, struct btrfs_ordered_sum, list); if (!ret) - ret = btrfs_csum_file_blocks(trans, log_root, sums); + ret = log_csums(trans, log_root, sums); list_del(&sums->list); kfree(sums); } -- GitLab From ad1d8c439978ede77cbf73cbdd11bafe810421a5 Mon Sep 17 00:00:00 2001 From: Filipe Manana Date: Mon, 2 Dec 2019 11:01:03 +0000 Subject: [PATCH 0589/1437] Btrfs: make tree checker detect checksum items with overlapping ranges Having checksum items, either on the checksums tree or in a log tree, that represent ranges that overlap each other is a sign of a corruption. Such case confuses the checksum lookup code and can result in not being able to find checksums or find stale checksums. So add a check for such case. This is motivated by a recent fix for a case where a log tree had checksum items covering ranges that overlap each other due to extent cloning, and resulted in missing checksums after replaying the log tree. It also helps detect past issues such as stale and outdated checksums due to overlapping, commit 27b9a8122ff71a ("Btrfs: fix csum tree corruption, duplicate and outdated checksums"). CC: stable@vger.kernel.org # 4.4+ Signed-off-by: Filipe Manana Signed-off-by: David Sterba --- fs/btrfs/tree-checker.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/fs/btrfs/tree-checker.c b/fs/btrfs/tree-checker.c index 092b8ece36d72..97f3520b8d98d 100644 --- a/fs/btrfs/tree-checker.c +++ b/fs/btrfs/tree-checker.c @@ -332,7 +332,7 @@ static int check_extent_data_item(struct extent_buffer *leaf, } static int check_csum_item(struct extent_buffer *leaf, struct btrfs_key *key, - int slot) + int slot, struct btrfs_key *prev_key) { struct btrfs_fs_info *fs_info = leaf->fs_info; u32 sectorsize = fs_info->sectorsize; @@ -356,6 +356,20 @@ static int check_csum_item(struct extent_buffer *leaf, struct btrfs_key *key, btrfs_item_size_nr(leaf, slot), csumsize); return -EUCLEAN; } + if (slot > 0 && prev_key->type == BTRFS_EXTENT_CSUM_KEY) { + u64 prev_csum_end; + u32 prev_item_size; + + prev_item_size = btrfs_item_size_nr(leaf, slot - 1); + prev_csum_end = (prev_item_size / csumsize) * sectorsize; + prev_csum_end += prev_key->offset; + if (prev_csum_end > key->offset) { + generic_err(leaf, slot - 1, +"csum end range (%llu) goes beyond the start range (%llu) of the next csum item", + prev_csum_end, key->offset); + return -EUCLEAN; + } + } return 0; } @@ -1355,7 +1369,7 @@ static int check_leaf_item(struct extent_buffer *leaf, ret = check_extent_data_item(leaf, key, slot, prev_key); break; case BTRFS_EXTENT_CSUM_KEY: - ret = check_csum_item(leaf, key, slot); + ret = check_csum_item(leaf, key, slot, prev_key); break; case BTRFS_DIR_ITEM_KEY: case BTRFS_DIR_INDEX_KEY: -- GitLab From 6609fee8897ac475378388238456c84298bff802 Mon Sep 17 00:00:00 2001 From: Filipe Manana Date: Fri, 6 Dec 2019 12:27:39 +0000 Subject: [PATCH 0590/1437] Btrfs: fix removal logic of the tree mod log that leads to use-after-free issues When a tree mod log user no longer needs to use the tree it calls btrfs_put_tree_mod_seq() to remove itself from the list of users and delete all no longer used elements of the tree's red black tree, which should be all elements with a sequence number less then our equals to the caller's sequence number. However the logic is broken because it can delete and free elements from the red black tree that have a sequence number greater then the caller's sequence number: 1) At a point in time we have sequence numbers 1, 2, 3 and 4 in the tree mod log; 2) The task which got assigned the sequence number 1 calls btrfs_put_tree_mod_seq(); 3) Sequence number 1 is deleted from the list of sequence numbers; 4) The current minimum sequence number is computed to be the sequence number 2; 5) A task using sequence number 2 is at tree_mod_log_rewind() and gets a pointer to one of its elements from the red black tree through a call to tree_mod_log_search(); 6) The task with sequence number 1 iterates the red black tree of tree modification elements and deletes (and frees) all elements with a sequence number less then or equals to 2 (the computed minimum sequence number) - it ends up only leaving elements with sequence numbers of 3 and 4; 7) The task with sequence number 2 now uses the pointer to its element, already freed by the other task, at __tree_mod_log_rewind(), resulting in a use-after-free issue. When CONFIG_DEBUG_PAGEALLOC=y it produces a trace like the following: [16804.546854] general protection fault: 0000 [#1] PREEMPT SMP DEBUG_PAGEALLOC PTI [16804.547451] CPU: 0 PID: 28257 Comm: pool Tainted: G W 5.4.0-rc8-btrfs-next-51 #1 [16804.548059] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS rel-1.12.0-0-ga698c8995f-prebuilt.qemu.org 04/01/2014 [16804.548666] RIP: 0010:rb_next+0x16/0x50 (...) [16804.550581] RSP: 0018:ffffb948418ef9b0 EFLAGS: 00010202 [16804.551227] RAX: 6b6b6b6b6b6b6b6b RBX: ffff90e0247f6600 RCX: 6b6b6b6b6b6b6b6b [16804.551873] RDX: 0000000000000000 RSI: 0000000000000000 RDI: ffff90e0247f6600 [16804.552504] RBP: ffff90dffe0d4688 R08: 0000000000000001 R09: 0000000000000000 [16804.553136] R10: ffff90dffa4a0040 R11: 0000000000000000 R12: 000000000000002e [16804.553768] R13: ffff90e0247f6600 R14: 0000000000001663 R15: ffff90dff77862b8 [16804.554399] FS: 00007f4b197ae700(0000) GS:ffff90e036a00000(0000) knlGS:0000000000000000 [16804.555039] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 [16804.555683] CR2: 00007f4b10022000 CR3: 00000002060e2004 CR4: 00000000003606f0 [16804.556336] DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 [16804.556968] DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400 [16804.557583] Call Trace: [16804.558207] __tree_mod_log_rewind+0xbf/0x280 [btrfs] [16804.558835] btrfs_search_old_slot+0x105/0xd00 [btrfs] [16804.559468] resolve_indirect_refs+0x1eb/0xc70 [btrfs] [16804.560087] ? free_extent_buffer.part.19+0x5a/0xc0 [btrfs] [16804.560700] find_parent_nodes+0x388/0x1120 [btrfs] [16804.561310] btrfs_check_shared+0x115/0x1c0 [btrfs] [16804.561916] ? extent_fiemap+0x59d/0x6d0 [btrfs] [16804.562518] extent_fiemap+0x59d/0x6d0 [btrfs] [16804.563112] ? __might_fault+0x11/0x90 [16804.563706] do_vfs_ioctl+0x45a/0x700 [16804.564299] ksys_ioctl+0x70/0x80 [16804.564885] ? trace_hardirqs_off_thunk+0x1a/0x20 [16804.565461] __x64_sys_ioctl+0x16/0x20 [16804.566020] do_syscall_64+0x5c/0x250 [16804.566580] entry_SYSCALL_64_after_hwframe+0x49/0xbe [16804.567153] RIP: 0033:0x7f4b1ba2add7 (...) [16804.568907] RSP: 002b:00007f4b197adc88 EFLAGS: 00000246 ORIG_RAX: 0000000000000010 [16804.569513] RAX: ffffffffffffffda RBX: 00007f4b100210d8 RCX: 00007f4b1ba2add7 [16804.570133] RDX: 00007f4b100210d8 RSI: 00000000c020660b RDI: 0000000000000003 [16804.570726] RBP: 000055de05a6cfe0 R08: 0000000000000000 R09: 00007f4b197add44 [16804.571314] R10: 0000000000000000 R11: 0000000000000246 R12: 00007f4b197add48 [16804.571905] R13: 00007f4b197add40 R14: 00007f4b100210d0 R15: 00007f4b197add50 (...) [16804.575623] ---[ end trace 87317359aad4ba50 ]--- Fix this by making btrfs_put_tree_mod_seq() skip deletion of elements that have a sequence number equals to the computed minimum sequence number, and not just elements with a sequence number greater then that minimum. Fixes: bd989ba359f2ac ("Btrfs: add tree modification log functions") CC: stable@vger.kernel.org # 4.4+ Reviewed-by: Josef Bacik Signed-off-by: Filipe Manana Signed-off-by: David Sterba --- fs/btrfs/ctree.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/btrfs/ctree.c b/fs/btrfs/ctree.c index 5b6e86aaf2e1a..24658b5a57870 100644 --- a/fs/btrfs/ctree.c +++ b/fs/btrfs/ctree.c @@ -379,7 +379,7 @@ void btrfs_put_tree_mod_seq(struct btrfs_fs_info *fs_info, for (node = rb_first(tm_root); node; node = next) { next = rb_next(node); tm = rb_entry(node, struct tree_mod_elem, node); - if (tm->seq > min_seq) + if (tm->seq >= min_seq) continue; rb_erase(node, tm_root); kfree(tm); -- GitLab From 147271e35ba267506dde6550f58ccf8d287db3ef Mon Sep 17 00:00:00 2001 From: Filipe Manana Date: Thu, 5 Dec 2019 16:58:41 +0000 Subject: [PATCH 0591/1437] Btrfs: fix hole extent items with a zero size after range cloning Normally when cloning a file range if we find an implicit hole at the end of the range we assume it is because the NO_HOLES feature is enabled. However that is not always the case. One well known case [1] is when we have a power failure after mixing buffered and direct IO writes against the same file. In such cases we need to punch a hole in the destination file, and if the NO_HOLES feature is not enabled, we need to insert explicit file extent items to represent the hole. After commit 690a5dbfc51315 ("Btrfs: fix ENOSPC errors, leading to transaction aborts, when cloning extents"), we started to insert file extent items representing the hole with an item size of 0, which is invalid and should be 53 bytes (the size of a btrfs_file_extent_item structure), resulting in all sorts of corruptions and invalid memory accesses. This is detected by the tree checker when we attempt to write a leaf to disk. The problem can be sporadically triggered by test case generic/561 from fstests. That test case does not exercise power failure and creates a new filesystem when it starts, so it does not use a filesystem created by any previous test that tests power failure. However the test does both buffered and direct IO writes (through fsstress) and it's precisely that which is creating the implicit holes in files. That happens even before the commit mentioned earlier. I need to investigate why we get those implicit holes to check if there is a real problem or not. For now this change fixes the regression of introducing file extent items with an item size of 0 bytes. Fix the issue by calling btrfs_punch_hole_range() without passing a btrfs_clone_extent_info structure, which ensures file extent items are inserted to represent the hole with a correct item size. We were passing a btrfs_clone_extent_info with a value of 0 for its 'item_size' field, which was causing the insertion of file extent items with an item size of 0. [1] https://www.spinics.net/lists/linux-btrfs/msg75350.html Reported-by: David Sterba Fixes: 690a5dbfc51315 ("Btrfs: fix ENOSPC errors, leading to transaction aborts, when cloning extents") Signed-off-by: Filipe Manana Reviewed-by: Josef Bacik Signed-off-by: David Sterba --- fs/btrfs/ioctl.c | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index a1ee0b775e652..3418decb9e612 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -3720,24 +3720,18 @@ process_slot: ret = 0; if (last_dest_end < destoff + len) { - struct btrfs_clone_extent_info clone_info = { 0 }; /* - * We have an implicit hole (NO_HOLES feature is enabled) that - * fully or partially overlaps our cloning range at its end. + * We have an implicit hole that fully or partially overlaps our + * cloning range at its end. This means that we either have the + * NO_HOLES feature enabled or the implicit hole happened due to + * mixing buffered and direct IO writes against this file. */ btrfs_release_path(path); path->leave_spinning = 0; - /* - * We are dealing with a hole and our clone_info already has a - * disk_offset of 0, we only need to fill the data length and - * file offset. - */ - clone_info.data_len = destoff + len - last_dest_end; - clone_info.file_offset = last_dest_end; ret = btrfs_punch_hole_range(inode, path, last_dest_end, destoff + len - 1, - &clone_info, &trans); + NULL, &trans); if (ret) goto out; -- GitLab From c7e54b5102bf3614cadb9ca32d7be73bad6cecf0 Mon Sep 17 00:00:00 2001 From: Josef Bacik Date: Fri, 6 Dec 2019 09:37:15 -0500 Subject: [PATCH 0592/1437] btrfs: abort transaction after failed inode updates in create_subvol We can just abort the transaction here, and in fact do that for every other failure in this function except these two cases. CC: stable@vger.kernel.org # 4.4+ Reviewed-by: Filipe Manana Reviewed-by: Johannes Thumshirn Signed-off-by: Josef Bacik Reviewed-by: David Sterba Signed-off-by: David Sterba --- fs/btrfs/ioctl.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index 3418decb9e612..18e328ce4b549 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -704,11 +704,17 @@ static noinline int create_subvol(struct inode *dir, btrfs_i_size_write(BTRFS_I(dir), dir->i_size + namelen * 2); ret = btrfs_update_inode(trans, root, dir); - BUG_ON(ret); + if (ret) { + btrfs_abort_transaction(trans, ret); + goto fail; + } ret = btrfs_add_root_ref(trans, objectid, root->root_key.objectid, btrfs_ino(BTRFS_I(dir)), index, name, namelen); - BUG_ON(ret); + if (ret) { + btrfs_abort_transaction(trans, ret); + goto fail; + } ret = btrfs_uuid_tree_add(trans, root_item->uuid, BTRFS_UUID_KEY_SUBVOL, objectid); -- GitLab From 714cd3e8cba6841220dce9063a7388a81de03825 Mon Sep 17 00:00:00 2001 From: Josef Bacik Date: Fri, 6 Dec 2019 11:39:00 -0500 Subject: [PATCH 0593/1437] btrfs: handle ENOENT in btrfs_uuid_tree_iterate If we get an -ENOENT back from btrfs_uuid_iter_rem when iterating the uuid tree we'll just continue and do btrfs_next_item(). However we've done a btrfs_release_path() at this point and no longer have a valid path. So increment the key and go back and do a normal search. CC: stable@vger.kernel.org # 4.4+ Reviewed-by: Filipe Manana Reviewed-by: Johannes Thumshirn Signed-off-by: Josef Bacik Reviewed-by: David Sterba Signed-off-by: David Sterba --- fs/btrfs/uuid-tree.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fs/btrfs/uuid-tree.c b/fs/btrfs/uuid-tree.c index 91caab63bdf52..76b84f2397b1b 100644 --- a/fs/btrfs/uuid-tree.c +++ b/fs/btrfs/uuid-tree.c @@ -324,6 +324,8 @@ again_search_slot: } if (ret < 0 && ret != -ENOENT) goto out; + key.offset++; + goto again_search_slot; } item_size -= sizeof(subid_le); offset += sizeof(subid_le); -- GitLab From 9bc574de590510eff899c3ca8dbaf013566b5efe Mon Sep 17 00:00:00 2001 From: Josef Bacik Date: Fri, 6 Dec 2019 09:37:17 -0500 Subject: [PATCH 0594/1437] btrfs: skip log replay on orphaned roots My fsstress modifications coupled with generic/475 uncovered a failure to mount and replay the log if we hit a orphaned root. We do not want to replay the log for an orphan root, but it's completely legitimate to have an orphaned root with a log attached. Fix this by simply skipping replaying the log. We still need to pin it's root node so that we do not overwrite it while replaying other logs, as we re-read the log root at every stage of the replay. CC: stable@vger.kernel.org # 4.4+ Reviewed-by: Filipe Manana Signed-off-by: Josef Bacik Signed-off-by: David Sterba --- fs/btrfs/tree-log.c | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c index 79866f1b33d6d..d3f115909ff05 100644 --- a/fs/btrfs/tree-log.c +++ b/fs/btrfs/tree-log.c @@ -6317,9 +6317,28 @@ again: wc.replay_dest = btrfs_read_fs_root_no_name(fs_info, &tmp_key); if (IS_ERR(wc.replay_dest)) { ret = PTR_ERR(wc.replay_dest); + + /* + * We didn't find the subvol, likely because it was + * deleted. This is ok, simply skip this log and go to + * the next one. + * + * We need to exclude the root because we can't have + * other log replays overwriting this log as we'll read + * it back in a few more times. This will keep our + * block from being modified, and we'll just bail for + * each subsequent pass. + */ + if (ret == -ENOENT) + ret = btrfs_pin_extent_for_log_replay(fs_info, + log->node->start, + log->node->len); free_extent_buffer(log->node); free_extent_buffer(log->commit_root); kfree(log); + + if (!ret) + goto next; btrfs_handle_fs_error(fs_info, ret, "Couldn't read target root for tree log recovery."); goto error; @@ -6351,7 +6370,6 @@ again: &root->highest_objectid); } - key.offset = found_key.offset - 1; wc.replay_dest->log_root = NULL; free_extent_buffer(log->node); free_extent_buffer(log->commit_root); @@ -6359,9 +6377,10 @@ again: if (ret) goto error; - +next: if (found_key.offset == 0) break; + key.offset = found_key.offset - 1; } btrfs_release_path(path); -- GitLab From ca1aa2818a53875cfdd175fb5e9a2984e997cce9 Mon Sep 17 00:00:00 2001 From: Josef Bacik Date: Fri, 6 Dec 2019 09:37:18 -0500 Subject: [PATCH 0595/1437] btrfs: do not leak reloc root if we fail to read the fs root If we fail to read the fs root corresponding with a reloc root we'll just break out and free the reloc roots. But we remove our current reloc_root from this list higher up, which means we'll leak this reloc_root. Fix this by adding ourselves back to the reloc_roots list so we are properly cleaned up. CC: stable@vger.kernel.org # 4.4+ Reviewed-by: Filipe Manana Reviewed-by: Johannes Thumshirn Signed-off-by: Josef Bacik Reviewed-by: David Sterba Signed-off-by: David Sterba --- fs/btrfs/relocation.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/btrfs/relocation.c b/fs/btrfs/relocation.c index d897a8e5e430e..c58245797f30a 100644 --- a/fs/btrfs/relocation.c +++ b/fs/btrfs/relocation.c @@ -4552,6 +4552,7 @@ int btrfs_recover_relocation(struct btrfs_root *root) fs_root = read_fs_root(fs_info, reloc_root->root_key.offset); if (IS_ERR(fs_root)) { err = PTR_ERR(fs_root); + list_add_tail(&reloc_root->root_list, &reloc_roots); goto out_free; } -- GitLab From fbd542971aa1e9ec33212afe1d9b4f1106cd85a1 Mon Sep 17 00:00:00 2001 From: Anand Jain Date: Thu, 5 Dec 2019 19:39:07 +0800 Subject: [PATCH 0596/1437] btrfs: send: remove WARN_ON for readonly mount We log warning if root::orphan_cleanup_state is not set to ORPHAN_CLEANUP_DONE in btrfs_ioctl_send(). However if the filesystem is mounted as readonly we skip the orphan item cleanup during the lookup and root::orphan_cleanup_state remains at the init state 0 instead of ORPHAN_CLEANUP_DONE (2). So during send in btrfs_ioctl_send() we hit the warning as below. WARN_ON(send_root->orphan_cleanup_state != ORPHAN_CLEANUP_DONE); WARNING: CPU: 0 PID: 2616 at /Volumes/ws/btrfs-devel/fs/btrfs/send.c:7090 btrfs_ioctl_send+0xb2f/0x18c0 [btrfs] :: RIP: 0010:btrfs_ioctl_send+0xb2f/0x18c0 [btrfs] :: Call Trace: :: _btrfs_ioctl_send+0x7b/0x110 [btrfs] btrfs_ioctl+0x150a/0x2b00 [btrfs] :: do_vfs_ioctl+0xa9/0x620 ? __fget+0xac/0xe0 ksys_ioctl+0x60/0x90 __x64_sys_ioctl+0x16/0x20 do_syscall_64+0x49/0x130 entry_SYSCALL_64_after_hwframe+0x44/0xa9 Reproducer: mkfs.btrfs -fq /dev/sdb mount /dev/sdb /btrfs btrfs subvolume create /btrfs/sv1 btrfs subvolume snapshot -r /btrfs/sv1 /btrfs/ss1 umount /btrfs mount -o ro /dev/sdb /btrfs btrfs send /btrfs/ss1 -f /tmp/f The warning exists because having orphan inodes could confuse send and cause it to fail or produce incorrect streams. The two cases that would cause such send failures, which are already fixed are: 1) Inodes that were unlinked - these are orphanized and remain with a link count of 0. These caused send operations to fail because it expected to always find at least one path for an inode. However this is no longer a problem since send is now able to deal with such inodes since commit 46b2f4590aab ("Btrfs: fix send failure when root has deleted files still open") and treats them as having been completely removed (the state after an orphan cleanup is performed). 2) Inodes that were in the process of being truncated. These resulted in send not knowing about the truncation and potentially issue write operations full of zeroes for the range from the new file size to the old file size. This is no longer a problem because we no longer create orphan items for truncation since commit f7e9e8fc792f ("Btrfs: stop creating orphan items for truncate"). As such before these commits, the WARN_ON here provided a clue in case something went wrong. Instead of being a warning against the root::orphan_cleanup_state value, it could have been more accurate by checking if there were actually any orphan items, and then issue a warning only if any exists, but that would be more expensive to check. Since orphanized inodes no longer cause problems for send, just remove the warning. Reported-by: Christoph Anton Mitterer Link: https://lore.kernel.org/linux-btrfs/21cb5e8d059f6e1496a903fa7bfc0a297e2f5370.camel@scientia.net/ CC: stable@vger.kernel.org # 4.19+ Suggested-by: Filipe Manana Reviewed-by: Filipe Manana Signed-off-by: Anand Jain Signed-off-by: David Sterba --- fs/btrfs/send.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/fs/btrfs/send.c b/fs/btrfs/send.c index ae2db5eb15499..091e5bc8c7ea9 100644 --- a/fs/btrfs/send.c +++ b/fs/btrfs/send.c @@ -7083,12 +7083,6 @@ long btrfs_ioctl_send(struct file *mnt_file, struct btrfs_ioctl_send_args *arg) send_root->send_in_progress++; spin_unlock(&send_root->root_item_lock); - /* - * This is done when we lookup the root, it should already be complete - * by the time we get here. - */ - WARN_ON(send_root->orphan_cleanup_state != ORPHAN_CLEANUP_DONE); - /* * Userspace tools do the checks and warn the user if it's * not RO. -- GitLab From 67d69ccdf389f3b7fabb00e2d81473a915b3b64d Mon Sep 17 00:00:00 2001 From: Jakub Sitnicki Date: Thu, 12 Dec 2019 11:22:50 +0100 Subject: [PATCH 0597/1437] libbpf: Recognize SK_REUSEPORT programs from section name Allow loading BPF object files that contain SK_REUSEPORT programs without having to manually set the program type before load if the the section name is set to "sk_reuseport". Makes user-space code needed to load SK_REUSEPORT BPF program more concise. Signed-off-by: Jakub Sitnicki Signed-off-by: Alexei Starovoitov Link: https://lore.kernel.org/bpf/20191212102259.418536-2-jakub@cloudflare.com --- tools/lib/bpf/libbpf.c | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c index 920d4e06a5f92..b99c0a9b77563 100644 --- a/tools/lib/bpf/libbpf.c +++ b/tools/lib/bpf/libbpf.c @@ -4983,6 +4983,7 @@ static const struct { enum bpf_attach_type attach_type; } section_names[] = { BPF_PROG_SEC("socket", BPF_PROG_TYPE_SOCKET_FILTER), + BPF_PROG_SEC("sk_reuseport", BPF_PROG_TYPE_SK_REUSEPORT), BPF_PROG_SEC("kprobe/", BPF_PROG_TYPE_KPROBE), BPF_PROG_SEC("uprobe/", BPF_PROG_TYPE_KPROBE), BPF_PROG_SEC("kretprobe/", BPF_PROG_TYPE_KPROBE), -- GitLab From 1fbcef929d7d71321745e61e8c20b0a02bd38cf1 Mon Sep 17 00:00:00 2001 From: Jakub Sitnicki Date: Thu, 12 Dec 2019 11:22:51 +0100 Subject: [PATCH 0598/1437] selftests/bpf: Let libbpf determine program type from section name Now that libbpf can recognize SK_REUSEPORT programs, we no longer have to pass a prog_type hint before loading the object file. Signed-off-by: Jakub Sitnicki Signed-off-by: Alexei Starovoitov Link: https://lore.kernel.org/bpf/20191212102259.418536-3-jakub@cloudflare.com --- .../selftests/bpf/progs/test_select_reuseport_kern.c | 2 +- tools/testing/selftests/bpf/test_select_reuseport.c | 12 +++--------- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/tools/testing/selftests/bpf/progs/test_select_reuseport_kern.c b/tools/testing/selftests/bpf/progs/test_select_reuseport_kern.c index ea7d84f012352..b1f09f5bb1cfb 100644 --- a/tools/testing/selftests/bpf/progs/test_select_reuseport_kern.c +++ b/tools/testing/selftests/bpf/progs/test_select_reuseport_kern.c @@ -62,7 +62,7 @@ struct { goto done; \ }) -SEC("select_by_skb_data") +SEC("sk_reuseport") int _select_by_skb_data(struct sk_reuseport_md *reuse_md) { __u32 linum, index = 0, flags = 0, index_zero = 0; diff --git a/tools/testing/selftests/bpf/test_select_reuseport.c b/tools/testing/selftests/bpf/test_select_reuseport.c index 7566c13eb51a7..1e3cfe1cb28a2 100644 --- a/tools/testing/selftests/bpf/test_select_reuseport.c +++ b/tools/testing/selftests/bpf/test_select_reuseport.c @@ -87,19 +87,11 @@ static void prepare_bpf_obj(void) struct bpf_program *prog; struct bpf_map *map; int err; - struct bpf_object_open_attr attr = { - .file = "test_select_reuseport_kern.o", - .prog_type = BPF_PROG_TYPE_SK_REUSEPORT, - }; - obj = bpf_object__open_xattr(&attr); + obj = bpf_object__open("test_select_reuseport_kern.o"); CHECK(IS_ERR_OR_NULL(obj), "open test_select_reuseport_kern.o", "obj:%p PTR_ERR(obj):%ld\n", obj, PTR_ERR(obj)); - prog = bpf_program__next(NULL, obj); - CHECK(!prog, "get first bpf_program", "!prog\n"); - bpf_program__set_type(prog, attr.prog_type); - map = bpf_object__find_map_by_name(obj, "outer_map"); CHECK(!map, "find outer_map", "!map\n"); err = bpf_map__reuse_fd(map, outer_map); @@ -108,6 +100,8 @@ static void prepare_bpf_obj(void) err = bpf_object__load(obj); CHECK(err, "load bpf_object", "err:%d\n", err); + prog = bpf_program__next(NULL, obj); + CHECK(!prog, "get first bpf_program", "!prog\n"); select_by_skb_data_prog = bpf_program__fd(prog); CHECK(select_by_skb_data_prog == -1, "get prog fd", "select_by_skb_data_prog:%d\n", select_by_skb_data_prog); -- GitLab From 11f80355d4d27c7c798fc9890b4056c5d98af992 Mon Sep 17 00:00:00 2001 From: Jakub Sitnicki Date: Thu, 12 Dec 2019 11:22:52 +0100 Subject: [PATCH 0599/1437] selftests/bpf: Use sa_family_t everywhere in reuseport tests Update the only function that is not using sa_family_t in this source file. Signed-off-by: Jakub Sitnicki Signed-off-by: Alexei Starovoitov Link: https://lore.kernel.org/bpf/20191212102259.418536-4-jakub@cloudflare.com --- tools/testing/selftests/bpf/test_select_reuseport.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/testing/selftests/bpf/test_select_reuseport.c b/tools/testing/selftests/bpf/test_select_reuseport.c index 1e3cfe1cb28a2..a295a087a0261 100644 --- a/tools/testing/selftests/bpf/test_select_reuseport.c +++ b/tools/testing/selftests/bpf/test_select_reuseport.c @@ -643,7 +643,7 @@ static void prepare_sk_fds(int type, sa_family_t family, bool inany) } } -static void setup_per_test(int type, unsigned short family, bool inany) +static void setup_per_test(int type, sa_family_t family, bool inany) { int ovr = -1, err; @@ -680,12 +680,12 @@ static void test_all(void) const int types[] = { SOCK_STREAM, SOCK_DGRAM, SOCK_STREAM }; const char * const type_strings[] = { "TCP", "UDP", "TCP" }; const char * const family_strings[] = { "IPv6", "IPv4" }; - const unsigned short families[] = { AF_INET6, AF_INET }; + const sa_family_t families[] = { AF_INET6, AF_INET }; const bool bind_inany[] = { false, false, true }; int t, f, err; for (f = 0; f < ARRAY_SIZE(families); f++) { - unsigned short family = families[f]; + sa_family_t family = families[f]; for (t = 0; t < ARRAY_SIZE(types); t++) { bool inany = bind_inany[t]; -- GitLab From a9ce4cf4e47c55ab40641b92551bc9e1de4bfa61 Mon Sep 17 00:00:00 2001 From: Jakub Sitnicki Date: Thu, 12 Dec 2019 11:22:53 +0100 Subject: [PATCH 0600/1437] selftests/bpf: Add helpers for getting socket family & type name Having string arrays to map socket family & type to a name prevents us from unrolling the test runner loop in the subsequent patch. Introduce helpers that do the same thing. Signed-off-by: Jakub Sitnicki Signed-off-by: Alexei Starovoitov Link: https://lore.kernel.org/bpf/20191212102259.418536-5-jakub@cloudflare.com --- .../selftests/bpf/test_select_reuseport.c | 28 +++++++++++++++++-- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/tools/testing/selftests/bpf/test_select_reuseport.c b/tools/testing/selftests/bpf/test_select_reuseport.c index a295a087a0261..ef98a7de6704e 100644 --- a/tools/testing/selftests/bpf/test_select_reuseport.c +++ b/tools/testing/selftests/bpf/test_select_reuseport.c @@ -674,12 +674,34 @@ static void cleanup(void) bpf_object__close(obj); } +static const char *family_str(sa_family_t family) +{ + switch (family) { + case AF_INET: + return "IPv4"; + case AF_INET6: + return "IPv6"; + default: + return "unknown"; + } +} + +static const char *sotype_str(int sotype) +{ + switch (sotype) { + case SOCK_STREAM: + return "TCP"; + case SOCK_DGRAM: + return "UDP"; + default: + return "unknown"; + } +} + static void test_all(void) { /* Extra SOCK_STREAM to test bind_inany==true */ const int types[] = { SOCK_STREAM, SOCK_DGRAM, SOCK_STREAM }; - const char * const type_strings[] = { "TCP", "UDP", "TCP" }; - const char * const family_strings[] = { "IPv6", "IPv4" }; const sa_family_t families[] = { AF_INET6, AF_INET }; const bool bind_inany[] = { false, false, true }; int t, f, err; @@ -692,7 +714,7 @@ static void test_all(void) int type = types[t]; printf("######## %s/%s %s ########\n", - family_strings[f], type_strings[t], + family_str(family), sotype_str(type), inany ? " INANY " : "LOOPBACK"); setup_per_test(type, family, inany); -- GitLab From 9936338258308aa008387dc4f9fd3f5fec6c046a Mon Sep 17 00:00:00 2001 From: Jakub Sitnicki Date: Thu, 12 Dec 2019 11:22:54 +0100 Subject: [PATCH 0601/1437] selftests/bpf: Unroll the main loop in reuseport test Prepare for iterating over individual tests without introducing another nested loop in the main test function. Signed-off-by: Jakub Sitnicki Signed-off-by: Alexei Starovoitov Link: https://lore.kernel.org/bpf/20191212102259.418536-6-jakub@cloudflare.com --- .../selftests/bpf/test_select_reuseport.c | 73 +++++++++++-------- 1 file changed, 41 insertions(+), 32 deletions(-) diff --git a/tools/testing/selftests/bpf/test_select_reuseport.c b/tools/testing/selftests/bpf/test_select_reuseport.c index ef98a7de6704e..63ce2e75e7580 100644 --- a/tools/testing/selftests/bpf/test_select_reuseport.c +++ b/tools/testing/selftests/bpf/test_select_reuseport.c @@ -698,47 +698,56 @@ static const char *sotype_str(int sotype) } } -static void test_all(void) +static void test_config(int type, sa_family_t family, bool inany) { - /* Extra SOCK_STREAM to test bind_inany==true */ - const int types[] = { SOCK_STREAM, SOCK_DGRAM, SOCK_STREAM }; - const sa_family_t families[] = { AF_INET6, AF_INET }; - const bool bind_inany[] = { false, false, true }; - int t, f, err; + int err; - for (f = 0; f < ARRAY_SIZE(families); f++) { - sa_family_t family = families[f]; + printf("######## %s/%s %s ########\n", + family_str(family), sotype_str(type), + inany ? " INANY " : "LOOPBACK"); - for (t = 0; t < ARRAY_SIZE(types); t++) { - bool inany = bind_inany[t]; - int type = types[t]; + setup_per_test(type, family, inany); - printf("######## %s/%s %s ########\n", - family_str(family), sotype_str(type), - inany ? " INANY " : "LOOPBACK"); + test_err_inner_map(type, family); - setup_per_test(type, family, inany); + /* Install reuseport_array to the outer_map */ + err = bpf_map_update_elem(outer_map, &index_zero, + &reuseport_array, BPF_ANY); + CHECK(err == -1, "update_elem(outer_map)", + "err:%d errno:%d\n", err, errno); - test_err_inner_map(type, family); + test_err_skb_data(type, family); + test_err_sk_select_port(type, family); + test_pass(type, family); + test_syncookie(type, family); + test_pass_on_err(type, family); + /* Must be the last test */ + test_detach_bpf(type, family); - /* Install reuseport_array to the outer_map */ - err = bpf_map_update_elem(outer_map, &index_zero, - &reuseport_array, BPF_ANY); - CHECK(err == -1, "update_elem(outer_map)", - "err:%d errno:%d\n", err, errno); + cleanup_per_test(); + printf("\n"); +} - test_err_skb_data(type, family); - test_err_sk_select_port(type, family); - test_pass(type, family); - test_syncookie(type, family); - test_pass_on_err(type, family); - /* Must be the last test */ - test_detach_bpf(type, family); +#define BIND_INANY true - cleanup_per_test(); - printf("\n"); - } - } +static void test_all(void) +{ + const struct config { + int sotype; + sa_family_t family; + bool inany; + } configs[] = { + { SOCK_STREAM, AF_INET }, + { SOCK_STREAM, AF_INET, BIND_INANY }, + { SOCK_STREAM, AF_INET6 }, + { SOCK_STREAM, AF_INET6, BIND_INANY }, + { SOCK_DGRAM, AF_INET }, + { SOCK_DGRAM, AF_INET6 }, + }; + const struct config *c; + + for (c = configs; c < configs + ARRAY_SIZE(configs); c++) + test_config(c->sotype, c->family, c->inany); } int main(int argc, const char **argv) -- GitLab From ce7cb5f3921cdf0f65ab877764265c6c0be34c3c Mon Sep 17 00:00:00 2001 From: Jakub Sitnicki Date: Thu, 12 Dec 2019 11:22:55 +0100 Subject: [PATCH 0602/1437] selftests/bpf: Run reuseport tests in a loop Prepare for switching reuseport tests to test_progs framework. Loop over the tests and perform setup/cleanup for each test separately, remembering that with test_progs we can select tests to run. Signed-off-by: Jakub Sitnicki Signed-off-by: Alexei Starovoitov Link: https://lore.kernel.org/bpf/20191212102259.418536-7-jakub@cloudflare.com --- .../selftests/bpf/test_select_reuseport.c | 55 ++++++++++++------- 1 file changed, 34 insertions(+), 21 deletions(-) diff --git a/tools/testing/selftests/bpf/test_select_reuseport.c b/tools/testing/selftests/bpf/test_select_reuseport.c index 63ce2e75e7580..cfff958da570b 100644 --- a/tools/testing/selftests/bpf/test_select_reuseport.c +++ b/tools/testing/selftests/bpf/test_select_reuseport.c @@ -643,7 +643,8 @@ static void prepare_sk_fds(int type, sa_family_t family, bool inany) } } -static void setup_per_test(int type, sa_family_t family, bool inany) +static void setup_per_test(int type, sa_family_t family, bool inany, + bool no_inner_map) { int ovr = -1, err; @@ -652,9 +653,18 @@ static void setup_per_test(int type, sa_family_t family, bool inany) BPF_ANY); CHECK(err == -1, "update_elem(tmp_index_ovr_map, 0, -1)", "err:%d errno:%d\n", err, errno); + + /* Install reuseport_array to outer_map? */ + if (no_inner_map) + return; + + err = bpf_map_update_elem(outer_map, &index_zero, &reuseport_array, + BPF_ANY); + CHECK(err == -1, "update_elem(outer_map, 0, reuseport_array)", + "err:%d errno:%d\n", err, errno); } -static void cleanup_per_test(void) +static void cleanup_per_test(bool no_inner_map) { int i, err; @@ -662,6 +672,10 @@ static void cleanup_per_test(void) close(sk_fds[i]); close(epfd); + /* Delete reuseport_array from outer_map? */ + if (no_inner_map) + return; + err = bpf_map_delete_elem(outer_map, &index_zero); CHECK(err == -1, "delete_elem(outer_map)", "err:%d errno:%d\n", err, errno); @@ -700,31 +714,30 @@ static const char *sotype_str(int sotype) static void test_config(int type, sa_family_t family, bool inany) { - int err; + const struct test { + void (*fn)(int sotype, sa_family_t family); + bool no_inner_map; + } tests[] = { + { test_err_inner_map, true /* no_inner_map */ }, + { test_err_skb_data }, + { test_err_sk_select_port }, + { test_pass }, + { test_syncookie }, + { test_pass_on_err }, + { test_detach_bpf }, + }; + const struct test *t; printf("######## %s/%s %s ########\n", family_str(family), sotype_str(type), inany ? " INANY " : "LOOPBACK"); - setup_per_test(type, family, inany); - - test_err_inner_map(type, family); - - /* Install reuseport_array to the outer_map */ - err = bpf_map_update_elem(outer_map, &index_zero, - &reuseport_array, BPF_ANY); - CHECK(err == -1, "update_elem(outer_map)", - "err:%d errno:%d\n", err, errno); - - test_err_skb_data(type, family); - test_err_sk_select_port(type, family); - test_pass(type, family); - test_syncookie(type, family); - test_pass_on_err(type, family); - /* Must be the last test */ - test_detach_bpf(type, family); + for (t = tests; t < tests + ARRAY_SIZE(tests); t++) { + setup_per_test(type, family, inany, t->no_inner_map); + t->fn(type, family); + cleanup_per_test(t->no_inner_map); + } - cleanup_per_test(); printf("\n"); } -- GitLab From 9af6c84435d0252e532c7f8bb70f1ed64a22ae2a Mon Sep 17 00:00:00 2001 From: Jakub Sitnicki Date: Thu, 12 Dec 2019 11:22:56 +0100 Subject: [PATCH 0603/1437] selftests/bpf: Propagate errors during setup for reuseport tests Prepare for switching reuseport tests to test_progs framework, where we don't have the luxury to terminate the process on failure. Modify setup helpers to signal failure via the return value with the help of a macro similar to the one currently in use by the tests. Signed-off-by: Jakub Sitnicki Signed-off-by: Alexei Starovoitov Link: https://lore.kernel.org/bpf/20191212102259.418536-8-jakub@cloudflare.com --- .../selftests/bpf/test_select_reuseport.c | 136 +++++++++++------- 1 file changed, 85 insertions(+), 51 deletions(-) diff --git a/tools/testing/selftests/bpf/test_select_reuseport.c b/tools/testing/selftests/bpf/test_select_reuseport.c index cfff958da570b..cc35816b7b34f 100644 --- a/tools/testing/selftests/bpf/test_select_reuseport.c +++ b/tools/testing/selftests/bpf/test_select_reuseport.c @@ -32,7 +32,7 @@ static int result_map, tmp_index_ovr_map, linum_map, data_check_map; static enum result expected_results[NR_RESULTS]; static int sk_fds[REUSEPORT_ARRAY_SIZE]; -static int reuseport_array, outer_map; +static int reuseport_array = -1, outer_map = -1; static int select_by_skb_data_prog; static int saved_tcp_syncookie; static struct bpf_object *obj; @@ -55,7 +55,16 @@ static union sa46 { } \ }) -static void create_maps(void) +#define RET_ERR(condition, tag, format...) ({ \ + int __ret = !!(condition); \ + if (__ret) { \ + printf("%s(%d):FAIL:%s ", __func__, __LINE__, tag); \ + printf(format); \ + return -1; \ + } \ +}) + +static int create_maps(void) { struct bpf_create_map_attr attr = {}; @@ -67,8 +76,8 @@ static void create_maps(void) attr.max_entries = REUSEPORT_ARRAY_SIZE; reuseport_array = bpf_create_map_xattr(&attr); - CHECK(reuseport_array == -1, "creating reuseport_array", - "reuseport_array:%d errno:%d\n", reuseport_array, errno); + RET_ERR(reuseport_array == -1, "creating reuseport_array", + "reuseport_array:%d errno:%d\n", reuseport_array, errno); /* Creating outer_map */ attr.name = "outer_map"; @@ -78,57 +87,61 @@ static void create_maps(void) attr.max_entries = 1; attr.inner_map_fd = reuseport_array; outer_map = bpf_create_map_xattr(&attr); - CHECK(outer_map == -1, "creating outer_map", - "outer_map:%d errno:%d\n", outer_map, errno); + RET_ERR(outer_map == -1, "creating outer_map", + "outer_map:%d errno:%d\n", outer_map, errno); + + return 0; } -static void prepare_bpf_obj(void) +static int prepare_bpf_obj(void) { struct bpf_program *prog; struct bpf_map *map; int err; obj = bpf_object__open("test_select_reuseport_kern.o"); - CHECK(IS_ERR_OR_NULL(obj), "open test_select_reuseport_kern.o", - "obj:%p PTR_ERR(obj):%ld\n", obj, PTR_ERR(obj)); + RET_ERR(IS_ERR_OR_NULL(obj), "open test_select_reuseport_kern.o", + "obj:%p PTR_ERR(obj):%ld\n", obj, PTR_ERR(obj)); map = bpf_object__find_map_by_name(obj, "outer_map"); - CHECK(!map, "find outer_map", "!map\n"); + RET_ERR(!map, "find outer_map", "!map\n"); err = bpf_map__reuse_fd(map, outer_map); - CHECK(err, "reuse outer_map", "err:%d\n", err); + RET_ERR(err, "reuse outer_map", "err:%d\n", err); err = bpf_object__load(obj); - CHECK(err, "load bpf_object", "err:%d\n", err); + RET_ERR(err, "load bpf_object", "err:%d\n", err); prog = bpf_program__next(NULL, obj); - CHECK(!prog, "get first bpf_program", "!prog\n"); + RET_ERR(!prog, "get first bpf_program", "!prog\n"); select_by_skb_data_prog = bpf_program__fd(prog); - CHECK(select_by_skb_data_prog == -1, "get prog fd", - "select_by_skb_data_prog:%d\n", select_by_skb_data_prog); + RET_ERR(select_by_skb_data_prog == -1, "get prog fd", + "select_by_skb_data_prog:%d\n", select_by_skb_data_prog); map = bpf_object__find_map_by_name(obj, "result_map"); - CHECK(!map, "find result_map", "!map\n"); + RET_ERR(!map, "find result_map", "!map\n"); result_map = bpf_map__fd(map); - CHECK(result_map == -1, "get result_map fd", - "result_map:%d\n", result_map); + RET_ERR(result_map == -1, "get result_map fd", + "result_map:%d\n", result_map); map = bpf_object__find_map_by_name(obj, "tmp_index_ovr_map"); - CHECK(!map, "find tmp_index_ovr_map", "!map\n"); + RET_ERR(!map, "find tmp_index_ovr_map\n", "!map"); tmp_index_ovr_map = bpf_map__fd(map); - CHECK(tmp_index_ovr_map == -1, "get tmp_index_ovr_map fd", - "tmp_index_ovr_map:%d\n", tmp_index_ovr_map); + RET_ERR(tmp_index_ovr_map == -1, "get tmp_index_ovr_map fd", + "tmp_index_ovr_map:%d\n", tmp_index_ovr_map); map = bpf_object__find_map_by_name(obj, "linum_map"); - CHECK(!map, "find linum_map", "!map\n"); + RET_ERR(!map, "find linum_map", "!map\n"); linum_map = bpf_map__fd(map); - CHECK(linum_map == -1, "get linum_map fd", - "linum_map:%d\n", linum_map); + RET_ERR(linum_map == -1, "get linum_map fd", + "linum_map:%d\n", linum_map); map = bpf_object__find_map_by_name(obj, "data_check_map"); - CHECK(!map, "find data_check_map", "!map\n"); + RET_ERR(!map, "find data_check_map", "!map\n"); data_check_map = bpf_map__fd(map); - CHECK(data_check_map == -1, "get data_check_map fd", - "data_check_map:%d\n", data_check_map); + RET_ERR(data_check_map == -1, "get data_check_map fd", + "data_check_map:%d\n", data_check_map); + + return 0; } static void sa46_init_loopback(union sa46 *sa, sa_family_t family) @@ -157,31 +170,34 @@ static int read_int_sysctl(const char *sysctl) int fd, ret; fd = open(sysctl, 0); - CHECK(fd == -1, "open(sysctl)", "sysctl:%s fd:%d errno:%d\n", - sysctl, fd, errno); + RET_ERR(fd == -1, "open(sysctl)", + "sysctl:%s fd:%d errno:%d\n", sysctl, fd, errno); ret = read(fd, buf, sizeof(buf)); - CHECK(ret <= 0, "read(sysctl)", "sysctl:%s ret:%d errno:%d\n", - sysctl, ret, errno); - close(fd); + RET_ERR(ret <= 0, "read(sysctl)", + "sysctl:%s ret:%d errno:%d\n", sysctl, ret, errno); + close(fd); return atoi(buf); } -static void write_int_sysctl(const char *sysctl, int v) +static int write_int_sysctl(const char *sysctl, int v) { int fd, ret, size; char buf[16]; fd = open(sysctl, O_RDWR); - CHECK(fd == -1, "open(sysctl)", "sysctl:%s fd:%d errno:%d\n", - sysctl, fd, errno); + RET_ERR(fd == -1, "open(sysctl)", + "sysctl:%s fd:%d errno:%d\n", sysctl, fd, errno); size = snprintf(buf, sizeof(buf), "%d", v); ret = write(fd, buf, size); - CHECK(ret != size, "write(sysctl)", - "sysctl:%s ret:%d size:%d errno:%d\n", sysctl, ret, size, errno); + RET_ERR(ret != size, "write(sysctl)", + "sysctl:%s ret:%d size:%d errno:%d\n", + sysctl, ret, size, errno); + close(fd); + return 0; } static void restore_sysctls(void) @@ -190,22 +206,25 @@ static void restore_sysctls(void) write_int_sysctl(TCP_SYNCOOKIE_SYSCTL, saved_tcp_syncookie); } -static void enable_fastopen(void) +static int enable_fastopen(void) { int fo; fo = read_int_sysctl(TCP_FO_SYSCTL); - write_int_sysctl(TCP_FO_SYSCTL, fo | 7); + if (fo < 0) + return -1; + + return write_int_sysctl(TCP_FO_SYSCTL, fo | 7); } -static void enable_syncookie(void) +static int enable_syncookie(void) { - write_int_sysctl(TCP_SYNCOOKIE_SYSCTL, 2); + return write_int_sysctl(TCP_SYNCOOKIE_SYSCTL, 2); } -static void disable_syncookie(void) +static int disable_syncookie(void) { - write_int_sysctl(TCP_SYNCOOKIE_SYSCTL, 0); + return write_int_sysctl(TCP_SYNCOOKIE_SYSCTL, 0); } static __u32 get_linum(void) @@ -683,9 +702,12 @@ static void cleanup_per_test(bool no_inner_map) static void cleanup(void) { - close(outer_map); - close(reuseport_array); - bpf_object__close(obj); + if (outer_map != -1) + close(outer_map); + if (reuseport_array != -1) + close(reuseport_array); + if (obj) + bpf_object__close(obj); } static const char *family_str(sa_family_t family) @@ -765,16 +787,28 @@ static void test_all(void) int main(int argc, const char **argv) { - create_maps(); - prepare_bpf_obj(); + int ret = EXIT_FAILURE; + + if (create_maps()) + goto out; + if (prepare_bpf_obj()) + goto out; + saved_tcp_fo = read_int_sysctl(TCP_FO_SYSCTL); saved_tcp_syncookie = read_int_sysctl(TCP_SYNCOOKIE_SYSCTL); - enable_fastopen(); - disable_syncookie(); + if (saved_tcp_syncookie < 0 || saved_tcp_syncookie < 0) + goto out; atexit(restore_sysctls); + if (enable_fastopen()) + goto out; + if (disable_syncookie()) + goto out; + test_all(); + ret = EXIT_SUCCESS; +out: cleanup(); - return 0; + return ret; } -- GitLab From 250a91d48ae7616672a34b58388620a554a00180 Mon Sep 17 00:00:00 2001 From: Jakub Sitnicki Date: Thu, 12 Dec 2019 11:22:57 +0100 Subject: [PATCH 0604/1437] selftests/bpf: Pull up printing the test name into test runner Again, prepare for switching reuseport tests to test_progs framework. test_progs framework will print the subtest name for us if we set it. Signed-off-by: Jakub Sitnicki Signed-off-by: Alexei Starovoitov Link: https://lore.kernel.org/bpf/20191212102259.418536-9-jakub@cloudflare.com --- .../selftests/bpf/test_select_reuseport.c | 27 +++++++++---------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/tools/testing/selftests/bpf/test_select_reuseport.c b/tools/testing/selftests/bpf/test_select_reuseport.c index cc35816b7b34f..0d5687feb6891 100644 --- a/tools/testing/selftests/bpf/test_select_reuseport.c +++ b/tools/testing/selftests/bpf/test_select_reuseport.c @@ -441,7 +441,6 @@ static void test_err_inner_map(int type, sa_family_t family) .pass_on_failure = 0, }; - printf("%s: ", __func__); expected_results[DROP_ERR_INNER_MAP]++; do_test(type, family, &cmd, DROP_ERR_INNER_MAP); printf("OK\n"); @@ -449,7 +448,6 @@ static void test_err_inner_map(int type, sa_family_t family) static void test_err_skb_data(int type, sa_family_t family) { - printf("%s: ", __func__); expected_results[DROP_ERR_SKB_DATA]++; do_test(type, family, NULL, DROP_ERR_SKB_DATA); printf("OK\n"); @@ -462,7 +460,6 @@ static void test_err_sk_select_port(int type, sa_family_t family) .pass_on_failure = 0, }; - printf("%s: ", __func__); expected_results[DROP_ERR_SK_SELECT_REUSEPORT]++; do_test(type, family, &cmd, DROP_ERR_SK_SELECT_REUSEPORT); printf("OK\n"); @@ -473,7 +470,6 @@ static void test_pass(int type, sa_family_t family) struct cmd cmd; int i; - printf("%s: ", __func__); cmd.pass_on_failure = 0; for (i = 0; i < REUSEPORT_ARRAY_SIZE; i++) { expected_results[PASS]++; @@ -494,7 +490,6 @@ static void test_syncookie(int type, sa_family_t family) if (type != SOCK_STREAM) return; - printf("%s: ", __func__); /* * +1 for TCP-SYN and * +1 for the TCP-ACK (ack the syncookie) @@ -530,7 +525,6 @@ static void test_pass_on_err(int type, sa_family_t family) .pass_on_failure = 1, }; - printf("%s: ", __func__); expected_results[PASS_ERR_SK_SELECT_REUSEPORT] += 1; do_test(type, family, &cmd, PASS_ERR_SK_SELECT_REUSEPORT); printf("OK\n"); @@ -545,7 +539,6 @@ static void test_detach_bpf(int type, sa_family_t family) struct cmd cmd = {}; int optvalue = 0; - printf("%s: ", __func__); err = setsockopt(sk_fds[0], SOL_SOCKET, SO_DETACH_REUSEPORT_BPF, &optvalue, sizeof(optvalue)); CHECK(err == -1, "setsockopt(SO_DETACH_REUSEPORT_BPF)", @@ -584,7 +577,7 @@ static void test_detach_bpf(int type, sa_family_t family) printf("OK\n"); close(cli_fd); #else - printf("%s: SKIP\n", __func__); + printf("SKIP\n"); #endif } @@ -734,19 +727,22 @@ static const char *sotype_str(int sotype) } } +#define TEST_INIT(fn, ...) { fn, #fn, __VA_ARGS__ } + static void test_config(int type, sa_family_t family, bool inany) { const struct test { void (*fn)(int sotype, sa_family_t family); + const char *name; bool no_inner_map; } tests[] = { - { test_err_inner_map, true /* no_inner_map */ }, - { test_err_skb_data }, - { test_err_sk_select_port }, - { test_pass }, - { test_syncookie }, - { test_pass_on_err }, - { test_detach_bpf }, + TEST_INIT(test_err_inner_map, true /* no_inner_map */), + TEST_INIT(test_err_skb_data), + TEST_INIT(test_err_sk_select_port), + TEST_INIT(test_pass), + TEST_INIT(test_syncookie), + TEST_INIT(test_pass_on_err), + TEST_INIT(test_detach_bpf), }; const struct test *t; @@ -756,6 +752,7 @@ static void test_config(int type, sa_family_t family, bool inany) for (t = tests; t < tests + ARRAY_SIZE(tests); t++) { setup_per_test(type, family, inany, t->no_inner_map); + printf("%s: ", t->name); t->fn(type, family); cleanup_per_test(t->no_inner_map); } -- GitLab From 415bb4e125b68a3aaccb8381baaa4a06dd1dcb27 Mon Sep 17 00:00:00 2001 From: Jakub Sitnicki Date: Thu, 12 Dec 2019 11:22:58 +0100 Subject: [PATCH 0605/1437] selftests/bpf: Move reuseport tests under prog_tests/ Do a pure move the show the actual work needed to adapt the tests in subsequent patch at the cost of breaking test_progs build for the moment. Signed-off-by: Jakub Sitnicki Signed-off-by: Alexei Starovoitov Link: https://lore.kernel.org/bpf/20191212102259.418536-10-jakub@cloudflare.com --- tools/testing/selftests/bpf/Makefile | 2 +- .../{test_select_reuseport.c => prog_tests/select_reuseport.c} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename tools/testing/selftests/bpf/{test_select_reuseport.c => prog_tests/select_reuseport.c} (100%) diff --git a/tools/testing/selftests/bpf/Makefile b/tools/testing/selftests/bpf/Makefile index e0fe01d9ec33b..90de7d75b5c64 100644 --- a/tools/testing/selftests/bpf/Makefile +++ b/tools/testing/selftests/bpf/Makefile @@ -28,7 +28,7 @@ LDLIBS += -lcap -lelf -lrt -lpthread TEST_GEN_PROGS = test_verifier test_tag test_maps test_lru_map test_lpm_map test_progs \ test_align test_verifier_log test_dev_cgroup test_tcpbpf_user \ test_sock test_btf test_sockmap get_cgroup_id_user test_socket_cookie \ - test_cgroup_storage test_select_reuseport \ + test_cgroup_storage \ test_netcnt test_tcpnotify_user test_sock_fields test_sysctl test_hashmap \ test_cgroup_attach test_progs-no_alu32 diff --git a/tools/testing/selftests/bpf/test_select_reuseport.c b/tools/testing/selftests/bpf/prog_tests/select_reuseport.c similarity index 100% rename from tools/testing/selftests/bpf/test_select_reuseport.c rename to tools/testing/selftests/bpf/prog_tests/select_reuseport.c -- GitLab From 7ee0d4e97b889c0478af9c1a6e5af658b181423f Mon Sep 17 00:00:00 2001 From: Jakub Sitnicki Date: Thu, 12 Dec 2019 11:22:59 +0100 Subject: [PATCH 0606/1437] selftests/bpf: Switch reuseport tests for test_progs framework The tests were originally written in abort-on-error style. With the switch to test_progs we can no longer do that. So at the risk of not cleaning up some resource on failure, we now return to the caller on error. That said, failure inside one test should not affect others because we run setup/cleanup before/after every test. Signed-off-by: Jakub Sitnicki Signed-off-by: Alexei Starovoitov Link: https://lore.kernel.org/bpf/20191212102259.418536-11-jakub@cloudflare.com --- .../bpf/prog_tests/select_reuseport.c | 267 +++++++++--------- 1 file changed, 131 insertions(+), 136 deletions(-) diff --git a/tools/testing/selftests/bpf/prog_tests/select_reuseport.c b/tools/testing/selftests/bpf/prog_tests/select_reuseport.c index 0d5687feb6891..2c37ae7dc2143 100644 --- a/tools/testing/selftests/bpf/prog_tests/select_reuseport.c +++ b/tools/testing/selftests/bpf/prog_tests/select_reuseport.c @@ -20,8 +20,11 @@ #include #include "bpf_rlimit.h" #include "bpf_util.h" + +#include "test_progs.h" #include "test_select_reuseport_common.h" +#define MAX_TEST_NAME 80 #define MIN_TCPHDR_LEN 20 #define UDPHDR_LEN 8 @@ -34,9 +37,9 @@ static enum result expected_results[NR_RESULTS]; static int sk_fds[REUSEPORT_ARRAY_SIZE]; static int reuseport_array = -1, outer_map = -1; static int select_by_skb_data_prog; -static int saved_tcp_syncookie; +static int saved_tcp_syncookie = -1; static struct bpf_object *obj; -static int saved_tcp_fo; +static int saved_tcp_fo = -1; static __u32 index_zero; static int epfd; @@ -46,20 +49,16 @@ static union sa46 { sa_family_t family; } srv_sa; -#define CHECK(condition, tag, format...) ({ \ - int __ret = !!(condition); \ - if (__ret) { \ - printf("%s(%d):FAIL:%s ", __func__, __LINE__, tag); \ - printf(format); \ - exit(-1); \ +#define RET_IF(condition, tag, format...) ({ \ + if (CHECK_FAIL(condition)) { \ + printf(tag " " format); \ + return; \ } \ }) #define RET_ERR(condition, tag, format...) ({ \ - int __ret = !!(condition); \ - if (__ret) { \ - printf("%s(%d):FAIL:%s ", __func__, __LINE__, tag); \ - printf(format); \ + if (CHECK_FAIL(condition)) { \ + printf(tag " " format); \ return -1; \ } \ }) @@ -202,8 +201,10 @@ static int write_int_sysctl(const char *sysctl, int v) static void restore_sysctls(void) { - write_int_sysctl(TCP_FO_SYSCTL, saved_tcp_fo); - write_int_sysctl(TCP_SYNCOOKIE_SYSCTL, saved_tcp_syncookie); + if (saved_tcp_fo != -1) + write_int_sysctl(TCP_FO_SYSCTL, saved_tcp_fo); + if (saved_tcp_syncookie != -1) + write_int_sysctl(TCP_SYNCOOKIE_SYSCTL, saved_tcp_syncookie); } static int enable_fastopen(void) @@ -227,14 +228,14 @@ static int disable_syncookie(void) return write_int_sysctl(TCP_SYNCOOKIE_SYSCTL, 0); } -static __u32 get_linum(void) +static long get_linum(void) { __u32 linum; int err; err = bpf_map_lookup_elem(linum_map, &index_zero, &linum); - CHECK(err == -1, "lookup_elem(linum_map)", "err:%d errno:%d\n", - err, errno); + RET_ERR(err == -1, "lookup_elem(linum_map)", "err:%d errno:%d\n", + err, errno); return linum; } @@ -250,12 +251,12 @@ static void check_data(int type, sa_family_t family, const struct cmd *cmd, addrlen = sizeof(cli_sa); err = getsockname(cli_fd, (struct sockaddr *)&cli_sa, &addrlen); - CHECK(err == -1, "getsockname(cli_fd)", "err:%d errno:%d\n", - err, errno); + RET_IF(err == -1, "getsockname(cli_fd)", "err:%d errno:%d\n", + err, errno); err = bpf_map_lookup_elem(data_check_map, &index_zero, &result); - CHECK(err == -1, "lookup_elem(data_check_map)", "err:%d errno:%d\n", - err, errno); + RET_IF(err == -1, "lookup_elem(data_check_map)", "err:%d errno:%d\n", + err, errno); if (type == SOCK_STREAM) { expected.len = MIN_TCPHDR_LEN; @@ -297,22 +298,22 @@ static void check_data(int type, sa_family_t family, const struct cmd *cmd, printf("expected: (0x%x, %u, %u)\n", expected.eth_protocol, expected.ip_protocol, expected.bind_inany); - CHECK(1, "data_check result != expected", - "bpf_prog_linum:%u\n", get_linum()); + RET_IF(1, "data_check result != expected", + "bpf_prog_linum:%ld\n", get_linum()); } - CHECK(!result.hash, "data_check result.hash empty", - "result.hash:%u", result.hash); + RET_IF(!result.hash, "data_check result.hash empty", + "result.hash:%u", result.hash); expected.len += cmd ? sizeof(*cmd) : 0; if (type == SOCK_STREAM) - CHECK(expected.len > result.len, "expected.len > result.len", - "expected.len:%u result.len:%u bpf_prog_linum:%u\n", - expected.len, result.len, get_linum()); + RET_IF(expected.len > result.len, "expected.len > result.len", + "expected.len:%u result.len:%u bpf_prog_linum:%ld\n", + expected.len, result.len, get_linum()); else - CHECK(expected.len != result.len, "expected.len != result.len", - "expected.len:%u result.len:%u bpf_prog_linum:%u\n", - expected.len, result.len, get_linum()); + RET_IF(expected.len != result.len, "expected.len != result.len", + "expected.len:%u result.len:%u bpf_prog_linum:%ld\n", + expected.len, result.len, get_linum()); } static void check_results(void) @@ -323,8 +324,8 @@ static void check_results(void) for (i = 0; i < NR_RESULTS; i++) { err = bpf_map_lookup_elem(result_map, &i, &results[i]); - CHECK(err == -1, "lookup_elem(result_map)", - "i:%u err:%d errno:%d\n", i, err, errno); + RET_IF(err == -1, "lookup_elem(result_map)", + "i:%u err:%d errno:%d\n", i, err, errno); } for (i = 0; i < NR_RESULTS; i++) { @@ -350,10 +351,10 @@ static void check_results(void) printf(", %u", expected_results[i]); printf("]\n"); - CHECK(expected_results[broken] != results[broken], - "unexpected result", - "expected_results[%u] != results[%u] bpf_prog_linum:%u\n", - broken, broken, get_linum()); + RET_IF(expected_results[broken] != results[broken], + "unexpected result", + "expected_results[%u] != results[%u] bpf_prog_linum:%ld\n", + broken, broken, get_linum()); } static int send_data(int type, sa_family_t family, void *data, size_t len, @@ -363,17 +364,17 @@ static int send_data(int type, sa_family_t family, void *data, size_t len, int fd, err; fd = socket(family, type, 0); - CHECK(fd == -1, "socket()", "fd:%d errno:%d\n", fd, errno); + RET_ERR(fd == -1, "socket()", "fd:%d errno:%d\n", fd, errno); sa46_init_loopback(&cli_sa, family); err = bind(fd, (struct sockaddr *)&cli_sa, sizeof(cli_sa)); - CHECK(fd == -1, "bind(cli_sa)", "err:%d errno:%d\n", err, errno); + RET_ERR(fd == -1, "bind(cli_sa)", "err:%d errno:%d\n", err, errno); err = sendto(fd, data, len, MSG_FASTOPEN, (struct sockaddr *)&srv_sa, sizeof(srv_sa)); - CHECK(err != len && expected >= PASS, - "sendto()", "family:%u err:%d errno:%d expected:%d\n", - family, err, errno, expected); + RET_ERR(err != len && expected >= PASS, + "sendto()", "family:%u err:%d errno:%d expected:%d\n", + family, err, errno, expected); return fd; } @@ -388,47 +389,49 @@ static void do_test(int type, sa_family_t family, struct cmd *cmd, cli_fd = send_data(type, family, cmd, cmd ? sizeof(*cmd) : 0, expected); + if (cli_fd < 0) + return; nev = epoll_wait(epfd, &ev, 1, expected >= PASS ? 5 : 0); - CHECK((nev <= 0 && expected >= PASS) || - (nev > 0 && expected < PASS), - "nev <> expected", - "nev:%d expected:%d type:%d family:%d data:(%d, %d)\n", - nev, expected, type, family, - cmd ? cmd->reuseport_index : -1, - cmd ? cmd->pass_on_failure : -1); + RET_IF((nev <= 0 && expected >= PASS) || + (nev > 0 && expected < PASS), + "nev <> expected", + "nev:%d expected:%d type:%d family:%d data:(%d, %d)\n", + nev, expected, type, family, + cmd ? cmd->reuseport_index : -1, + cmd ? cmd->pass_on_failure : -1); check_results(); check_data(type, family, cmd, cli_fd); if (expected < PASS) return; - CHECK(expected != PASS_ERR_SK_SELECT_REUSEPORT && - cmd->reuseport_index != ev.data.u32, - "check cmd->reuseport_index", - "cmd:(%u, %u) ev.data.u32:%u\n", - cmd->pass_on_failure, cmd->reuseport_index, ev.data.u32); + RET_IF(expected != PASS_ERR_SK_SELECT_REUSEPORT && + cmd->reuseport_index != ev.data.u32, + "check cmd->reuseport_index", + "cmd:(%u, %u) ev.data.u32:%u\n", + cmd->pass_on_failure, cmd->reuseport_index, ev.data.u32); srv_fd = sk_fds[ev.data.u32]; if (type == SOCK_STREAM) { int new_fd = accept(srv_fd, NULL, 0); - CHECK(new_fd == -1, "accept(srv_fd)", - "ev.data.u32:%u new_fd:%d errno:%d\n", - ev.data.u32, new_fd, errno); + RET_IF(new_fd == -1, "accept(srv_fd)", + "ev.data.u32:%u new_fd:%d errno:%d\n", + ev.data.u32, new_fd, errno); nread = recv(new_fd, &rcv_cmd, sizeof(rcv_cmd), MSG_DONTWAIT); - CHECK(nread != sizeof(rcv_cmd), - "recv(new_fd)", - "ev.data.u32:%u nread:%zd sizeof(rcv_cmd):%zu errno:%d\n", - ev.data.u32, nread, sizeof(rcv_cmd), errno); + RET_IF(nread != sizeof(rcv_cmd), + "recv(new_fd)", + "ev.data.u32:%u nread:%zd sizeof(rcv_cmd):%zu errno:%d\n", + ev.data.u32, nread, sizeof(rcv_cmd), errno); close(new_fd); } else { nread = recv(srv_fd, &rcv_cmd, sizeof(rcv_cmd), MSG_DONTWAIT); - CHECK(nread != sizeof(rcv_cmd), - "recv(sk_fds)", - "ev.data.u32:%u nread:%zd sizeof(rcv_cmd):%zu errno:%d\n", - ev.data.u32, nread, sizeof(rcv_cmd), errno); + RET_IF(nread != sizeof(rcv_cmd), + "recv(sk_fds)", + "ev.data.u32:%u nread:%zd sizeof(rcv_cmd):%zu errno:%d\n", + ev.data.u32, nread, sizeof(rcv_cmd), errno); } close(cli_fd); @@ -443,14 +446,12 @@ static void test_err_inner_map(int type, sa_family_t family) expected_results[DROP_ERR_INNER_MAP]++; do_test(type, family, &cmd, DROP_ERR_INNER_MAP); - printf("OK\n"); } static void test_err_skb_data(int type, sa_family_t family) { expected_results[DROP_ERR_SKB_DATA]++; do_test(type, family, NULL, DROP_ERR_SKB_DATA); - printf("OK\n"); } static void test_err_sk_select_port(int type, sa_family_t family) @@ -462,7 +463,6 @@ static void test_err_sk_select_port(int type, sa_family_t family) expected_results[DROP_ERR_SK_SELECT_REUSEPORT]++; do_test(type, family, &cmd, DROP_ERR_SK_SELECT_REUSEPORT); - printf("OK\n"); } static void test_pass(int type, sa_family_t family) @@ -476,7 +476,6 @@ static void test_pass(int type, sa_family_t family) cmd.reuseport_index = i; do_test(type, family, &cmd, PASS); } - printf("OK\n"); } static void test_syncookie(int type, sa_family_t family) @@ -505,17 +504,16 @@ static void test_syncookie(int type, sa_family_t family) */ err = bpf_map_update_elem(tmp_index_ovr_map, &index_zero, &tmp_index, BPF_ANY); - CHECK(err == -1, "update_elem(tmp_index_ovr_map, 0, 1)", - "err:%d errno:%d\n", err, errno); + RET_IF(err == -1, "update_elem(tmp_index_ovr_map, 0, 1)", + "err:%d errno:%d\n", err, errno); do_test(type, family, &cmd, PASS); err = bpf_map_lookup_elem(tmp_index_ovr_map, &index_zero, &tmp_index); - CHECK(err == -1 || tmp_index != -1, - "lookup_elem(tmp_index_ovr_map)", - "err:%d errno:%d tmp_index:%d\n", - err, errno, tmp_index); + RET_IF(err == -1 || tmp_index != -1, + "lookup_elem(tmp_index_ovr_map)", + "err:%d errno:%d tmp_index:%d\n", + err, errno, tmp_index); disable_syncookie(); - printf("OK\n"); } static void test_pass_on_err(int type, sa_family_t family) @@ -527,7 +525,6 @@ static void test_pass_on_err(int type, sa_family_t family) expected_results[PASS_ERR_SK_SELECT_REUSEPORT] += 1; do_test(type, family, &cmd, PASS_ERR_SK_SELECT_REUSEPORT); - printf("OK\n"); } static void test_detach_bpf(int type, sa_family_t family) @@ -541,43 +538,45 @@ static void test_detach_bpf(int type, sa_family_t family) err = setsockopt(sk_fds[0], SOL_SOCKET, SO_DETACH_REUSEPORT_BPF, &optvalue, sizeof(optvalue)); - CHECK(err == -1, "setsockopt(SO_DETACH_REUSEPORT_BPF)", - "err:%d errno:%d\n", err, errno); + RET_IF(err == -1, "setsockopt(SO_DETACH_REUSEPORT_BPF)", + "err:%d errno:%d\n", err, errno); err = setsockopt(sk_fds[1], SOL_SOCKET, SO_DETACH_REUSEPORT_BPF, &optvalue, sizeof(optvalue)); - CHECK(err == 0 || errno != ENOENT, "setsockopt(SO_DETACH_REUSEPORT_BPF)", - "err:%d errno:%d\n", err, errno); + RET_IF(err == 0 || errno != ENOENT, + "setsockopt(SO_DETACH_REUSEPORT_BPF)", + "err:%d errno:%d\n", err, errno); for (i = 0; i < NR_RESULTS; i++) { err = bpf_map_lookup_elem(result_map, &i, &tmp); - CHECK(err == -1, "lookup_elem(result_map)", - "i:%u err:%d errno:%d\n", i, err, errno); + RET_IF(err == -1, "lookup_elem(result_map)", + "i:%u err:%d errno:%d\n", i, err, errno); nr_run_before += tmp; } cli_fd = send_data(type, family, &cmd, sizeof(cmd), PASS); + if (cli_fd < 0) + return; nev = epoll_wait(epfd, &ev, 1, 5); - CHECK(nev <= 0, "nev <= 0", - "nev:%d expected:1 type:%d family:%d data:(0, 0)\n", - nev, type, family); + RET_IF(nev <= 0, "nev <= 0", + "nev:%d expected:1 type:%d family:%d data:(0, 0)\n", + nev, type, family); for (i = 0; i < NR_RESULTS; i++) { err = bpf_map_lookup_elem(result_map, &i, &tmp); - CHECK(err == -1, "lookup_elem(result_map)", - "i:%u err:%d errno:%d\n", i, err, errno); + RET_IF(err == -1, "lookup_elem(result_map)", + "i:%u err:%d errno:%d\n", i, err, errno); nr_run_after += tmp; } - CHECK(nr_run_before != nr_run_after, - "nr_run_before != nr_run_after", - "nr_run_before:%u nr_run_after:%u\n", - nr_run_before, nr_run_after); + RET_IF(nr_run_before != nr_run_after, + "nr_run_before != nr_run_after", + "nr_run_before:%u nr_run_after:%u\n", + nr_run_before, nr_run_after); - printf("OK\n"); close(cli_fd); #else - printf("SKIP\n"); + test__skip(); #endif } @@ -600,58 +599,58 @@ static void prepare_sk_fds(int type, sa_family_t family, bool inany) */ for (i = first; i >= 0; i--) { sk_fds[i] = socket(family, type, 0); - CHECK(sk_fds[i] == -1, "socket()", "sk_fds[%d]:%d errno:%d\n", - i, sk_fds[i], errno); + RET_IF(sk_fds[i] == -1, "socket()", "sk_fds[%d]:%d errno:%d\n", + i, sk_fds[i], errno); err = setsockopt(sk_fds[i], SOL_SOCKET, SO_REUSEPORT, &optval, sizeof(optval)); - CHECK(err == -1, "setsockopt(SO_REUSEPORT)", - "sk_fds[%d] err:%d errno:%d\n", - i, err, errno); + RET_IF(err == -1, "setsockopt(SO_REUSEPORT)", + "sk_fds[%d] err:%d errno:%d\n", + i, err, errno); if (i == first) { err = setsockopt(sk_fds[i], SOL_SOCKET, SO_ATTACH_REUSEPORT_EBPF, &select_by_skb_data_prog, sizeof(select_by_skb_data_prog)); - CHECK(err == -1, "setsockopt(SO_ATTACH_REUEPORT_EBPF)", - "err:%d errno:%d\n", err, errno); + RET_IF(err == -1, "setsockopt(SO_ATTACH_REUEPORT_EBPF)", + "err:%d errno:%d\n", err, errno); } err = bind(sk_fds[i], (struct sockaddr *)&srv_sa, addrlen); - CHECK(err == -1, "bind()", "sk_fds[%d] err:%d errno:%d\n", - i, err, errno); + RET_IF(err == -1, "bind()", "sk_fds[%d] err:%d errno:%d\n", + i, err, errno); if (type == SOCK_STREAM) { err = listen(sk_fds[i], 10); - CHECK(err == -1, "listen()", - "sk_fds[%d] err:%d errno:%d\n", - i, err, errno); + RET_IF(err == -1, "listen()", + "sk_fds[%d] err:%d errno:%d\n", + i, err, errno); } err = bpf_map_update_elem(reuseport_array, &i, &sk_fds[i], BPF_NOEXIST); - CHECK(err == -1, "update_elem(reuseport_array)", - "sk_fds[%d] err:%d errno:%d\n", i, err, errno); + RET_IF(err == -1, "update_elem(reuseport_array)", + "sk_fds[%d] err:%d errno:%d\n", i, err, errno); if (i == first) { socklen_t addrlen = sizeof(srv_sa); err = getsockname(sk_fds[i], (struct sockaddr *)&srv_sa, &addrlen); - CHECK(err == -1, "getsockname()", - "sk_fds[%d] err:%d errno:%d\n", i, err, errno); + RET_IF(err == -1, "getsockname()", + "sk_fds[%d] err:%d errno:%d\n", i, err, errno); } } epfd = epoll_create(1); - CHECK(epfd == -1, "epoll_create(1)", - "epfd:%d errno:%d\n", epfd, errno); + RET_IF(epfd == -1, "epoll_create(1)", + "epfd:%d errno:%d\n", epfd, errno); ev.events = EPOLLIN; for (i = 0; i < REUSEPORT_ARRAY_SIZE; i++) { ev.data.u32 = i; err = epoll_ctl(epfd, EPOLL_CTL_ADD, sk_fds[i], &ev); - CHECK(err, "epoll_ctl(EPOLL_CTL_ADD)", "sk_fds[%d]\n", i); + RET_IF(err, "epoll_ctl(EPOLL_CTL_ADD)", "sk_fds[%d]\n", i); } } @@ -663,8 +662,8 @@ static void setup_per_test(int type, sa_family_t family, bool inany, prepare_sk_fds(type, family, inany); err = bpf_map_update_elem(tmp_index_ovr_map, &index_zero, &ovr, BPF_ANY); - CHECK(err == -1, "update_elem(tmp_index_ovr_map, 0, -1)", - "err:%d errno:%d\n", err, errno); + RET_IF(err == -1, "update_elem(tmp_index_ovr_map, 0, -1)", + "err:%d errno:%d\n", err, errno); /* Install reuseport_array to outer_map? */ if (no_inner_map) @@ -672,8 +671,8 @@ static void setup_per_test(int type, sa_family_t family, bool inany, err = bpf_map_update_elem(outer_map, &index_zero, &reuseport_array, BPF_ANY); - CHECK(err == -1, "update_elem(outer_map, 0, reuseport_array)", - "err:%d errno:%d\n", err, errno); + RET_IF(err == -1, "update_elem(outer_map, 0, reuseport_array)", + "err:%d errno:%d\n", err, errno); } static void cleanup_per_test(bool no_inner_map) @@ -689,8 +688,8 @@ static void cleanup_per_test(bool no_inner_map) return; err = bpf_map_delete_elem(outer_map, &index_zero); - CHECK(err == -1, "delete_elem(outer_map)", - "err:%d errno:%d\n", err, errno); + RET_IF(err == -1, "delete_elem(outer_map)", + "err:%d errno:%d\n", err, errno); } static void cleanup(void) @@ -729,7 +728,7 @@ static const char *sotype_str(int sotype) #define TEST_INIT(fn, ...) { fn, #fn, __VA_ARGS__ } -static void test_config(int type, sa_family_t family, bool inany) +static void test_config(int sotype, sa_family_t family, bool inany) { const struct test { void (*fn)(int sotype, sa_family_t family); @@ -744,20 +743,21 @@ static void test_config(int type, sa_family_t family, bool inany) TEST_INIT(test_pass_on_err), TEST_INIT(test_detach_bpf), }; + char s[MAX_TEST_NAME]; const struct test *t; - printf("######## %s/%s %s ########\n", - family_str(family), sotype_str(type), - inany ? " INANY " : "LOOPBACK"); - for (t = tests; t < tests + ARRAY_SIZE(tests); t++) { - setup_per_test(type, family, inany, t->no_inner_map); - printf("%s: ", t->name); - t->fn(type, family); + snprintf(s, sizeof(s), "%s/%s %s %s", + family_str(family), sotype_str(sotype), + inany ? "INANY" : "LOOPBACK", t->name); + + if (!test__start_subtest(s)) + continue; + + setup_per_test(sotype, family, inany, t->no_inner_map); + t->fn(sotype, family); cleanup_per_test(t->no_inner_map); } - - printf("\n"); } #define BIND_INANY true @@ -782,10 +782,8 @@ static void test_all(void) test_config(c->sotype, c->family, c->inany); } -int main(int argc, const char **argv) +void test_select_reuseport(void) { - int ret = EXIT_FAILURE; - if (create_maps()) goto out; if (prepare_bpf_obj()) @@ -795,7 +793,6 @@ int main(int argc, const char **argv) saved_tcp_syncookie = read_int_sysctl(TCP_SYNCOOKIE_SYSCTL); if (saved_tcp_syncookie < 0 || saved_tcp_syncookie < 0) goto out; - atexit(restore_sysctls); if (enable_fastopen()) goto out; @@ -803,9 +800,7 @@ int main(int argc, const char **argv) goto out; test_all(); - - ret = EXIT_SUCCESS; out: cleanup(); - return ret; + restore_sysctls(); } -- GitLab From 6803ee25f0ead1e836808acb14396bb9a9849113 Mon Sep 17 00:00:00 2001 From: Andrii Nakryiko Date: Wed, 11 Dec 2019 17:35:48 -0800 Subject: [PATCH 0607/1437] libbpf: Extract and generalize CPU mask parsing logic This logic is re-used for parsing a set of online CPUs. Having it as an isolated piece of code working with input string makes it conveninent to test this logic as well. While refactoring, also improve the robustness of original implementation. Signed-off-by: Andrii Nakryiko Signed-off-by: Alexei Starovoitov Link: https://lore.kernel.org/bpf/20191212013548.1690564-1-andriin@fb.com --- tools/lib/bpf/libbpf.c | 125 +++++++++++++++++++++----------- tools/lib/bpf/libbpf_internal.h | 2 + 2 files changed, 86 insertions(+), 41 deletions(-) diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c index b99c0a9b77563..98455e8aa6dbb 100644 --- a/tools/lib/bpf/libbpf.c +++ b/tools/lib/bpf/libbpf.c @@ -6523,61 +6523,104 @@ void bpf_program__bpil_offs_to_addr(struct bpf_prog_info_linear *info_linear) } } -int libbpf_num_possible_cpus(void) +int parse_cpu_mask_str(const char *s, bool **mask, int *mask_sz) { - static const char *fcpu = "/sys/devices/system/cpu/possible"; - int len = 0, n = 0, il = 0, ir = 0; - unsigned int start = 0, end = 0; - int tmp_cpus = 0; - static int cpus; - char buf[128]; - int error = 0; - int fd = -1; + int err = 0, n, len, start, end = -1; + bool *tmp; - tmp_cpus = READ_ONCE(cpus); - if (tmp_cpus > 0) - return tmp_cpus; + *mask = NULL; + *mask_sz = 0; + + /* Each sub string separated by ',' has format \d+-\d+ or \d+ */ + while (*s) { + if (*s == ',' || *s == '\n') { + s++; + continue; + } + n = sscanf(s, "%d%n-%d%n", &start, &len, &end, &len); + if (n <= 0 || n > 2) { + pr_warn("Failed to get CPU range %s: %d\n", s, n); + err = -EINVAL; + goto cleanup; + } else if (n == 1) { + end = start; + } + if (start < 0 || start > end) { + pr_warn("Invalid CPU range [%d,%d] in %s\n", + start, end, s); + err = -EINVAL; + goto cleanup; + } + tmp = realloc(*mask, end + 1); + if (!tmp) { + err = -ENOMEM; + goto cleanup; + } + *mask = tmp; + memset(tmp + *mask_sz, 0, start - *mask_sz); + memset(tmp + start, 1, end - start + 1); + *mask_sz = end + 1; + s += len; + } + if (!*mask_sz) { + pr_warn("Empty CPU range\n"); + return -EINVAL; + } + return 0; +cleanup: + free(*mask); + *mask = NULL; + return err; +} + +int parse_cpu_mask_file(const char *fcpu, bool **mask, int *mask_sz) +{ + int fd, err = 0, len; + char buf[128]; fd = open(fcpu, O_RDONLY); if (fd < 0) { - error = errno; - pr_warn("Failed to open file %s: %s\n", fcpu, strerror(error)); - return -error; + err = -errno; + pr_warn("Failed to open cpu mask file %s: %d\n", fcpu, err); + return err; } len = read(fd, buf, sizeof(buf)); close(fd); if (len <= 0) { - error = len ? errno : EINVAL; - pr_warn("Failed to read # of possible cpus from %s: %s\n", - fcpu, strerror(error)); - return -error; + err = len ? -errno : -EINVAL; + pr_warn("Failed to read cpu mask from %s: %d\n", fcpu, err); + return err; } - if (len == sizeof(buf)) { - pr_warn("File %s size overflow\n", fcpu); - return -EOVERFLOW; + if (len >= sizeof(buf)) { + pr_warn("CPU mask is too big in file %s\n", fcpu); + return -E2BIG; } buf[len] = '\0'; - for (ir = 0, tmp_cpus = 0; ir <= len; ir++) { - /* Each sub string separated by ',' has format \d+-\d+ or \d+ */ - if (buf[ir] == ',' || buf[ir] == '\0') { - buf[ir] = '\0'; - n = sscanf(&buf[il], "%u-%u", &start, &end); - if (n <= 0) { - pr_warn("Failed to get # CPUs from %s\n", - &buf[il]); - return -EINVAL; - } else if (n == 1) { - end = start; - } - tmp_cpus += end - start + 1; - il = ir + 1; - } - } - if (tmp_cpus <= 0) { - pr_warn("Invalid #CPUs %d from %s\n", tmp_cpus, fcpu); - return -EINVAL; + return parse_cpu_mask_str(buf, mask, mask_sz); +} + +int libbpf_num_possible_cpus(void) +{ + static const char *fcpu = "/sys/devices/system/cpu/possible"; + static int cpus; + int err, n, i, tmp_cpus; + bool *mask; + + tmp_cpus = READ_ONCE(cpus); + if (tmp_cpus > 0) + return tmp_cpus; + + err = parse_cpu_mask_file(fcpu, &mask, &n); + if (err) + return err; + + tmp_cpus = 0; + for (i = 0; i < n; i++) { + if (mask[i]) + tmp_cpus++; } + free(mask); WRITE_ONCE(cpus, tmp_cpus); return tmp_cpus; diff --git a/tools/lib/bpf/libbpf_internal.h b/tools/lib/bpf/libbpf_internal.h index 97ac17a64a58d..f4f10715db406 100644 --- a/tools/lib/bpf/libbpf_internal.h +++ b/tools/lib/bpf/libbpf_internal.h @@ -95,6 +95,8 @@ static inline bool libbpf_validate_opts(const char *opts, #define OPTS_GET(opts, field, fallback_value) \ (OPTS_HAS(opts, field) ? (opts)->field : fallback_value) +int parse_cpu_mask_str(const char *s, bool **mask, int *mask_sz); +int parse_cpu_mask_file(const char *fcpu, bool **mask, int *mask_sz); int libbpf__load_raw_btf(const char *raw_types, size_t types_len, const char *str_sec, size_t str_len); -- GitLab From 099bc4812f09155da77eeb960a983470249c9ce1 Mon Sep 17 00:00:00 2001 From: Christophe Leroy Date: Mon, 9 Dec 2019 06:19:08 +0000 Subject: [PATCH 0608/1437] powerpc/irq: fix stack overflow verification Before commit 0366a1c70b89 ("powerpc/irq: Run softirqs off the top of the irq stack"), check_stack_overflow() was called by do_IRQ(), before switching to the irq stack. In that commit, do_IRQ() was renamed __do_irq(), and is now executing on the irq stack, so check_stack_overflow() has just become almost useless. Move check_stack_overflow() call in do_IRQ() to do the check while still on the current stack. Fixes: 0366a1c70b89 ("powerpc/irq: Run softirqs off the top of the irq stack") Cc: stable@vger.kernel.org Signed-off-by: Christophe Leroy Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/e033aa8116ab12b7ca9a9c75189ad0741e3b9b5f.1575872340.git.christophe.leroy@c-s.fr --- arch/powerpc/kernel/irq.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/powerpc/kernel/irq.c b/arch/powerpc/kernel/irq.c index 5645bc9cbc09a..add67498c126b 100644 --- a/arch/powerpc/kernel/irq.c +++ b/arch/powerpc/kernel/irq.c @@ -619,8 +619,6 @@ void __do_irq(struct pt_regs *regs) trace_irq_entry(regs); - check_stack_overflow(); - /* * Query the platform PIC for the interrupt & ack it. * @@ -652,6 +650,8 @@ void do_IRQ(struct pt_regs *regs) irqsp = hardirq_ctx[raw_smp_processor_id()]; sirqsp = softirq_ctx[raw_smp_processor_id()]; + check_stack_overflow(); + /* Already there ? */ if (unlikely(cursp == irqsp || cursp == sirqsp)) { __do_irq(regs); -- GitLab From 913e73c77d48aeeb50c16450a653dca9c71ae2e2 Mon Sep 17 00:00:00 2001 From: Frederic Barrat Date: Mon, 9 Dec 2019 11:55:13 +0100 Subject: [PATCH 0609/1437] ocxl: Fix potential memory leak on context creation If we couldn't fully init a context, we were leaking memory. Fixes: b9721d275cc2 ("ocxl: Allow external drivers to use OpenCAPI contexts") Signed-off-by: Frederic Barrat Acked-by: Andrew Donnellan Reviewed-by: Greg Kurz Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20191209105513.8566-1-fbarrat@linux.ibm.com --- drivers/misc/ocxl/context.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/misc/ocxl/context.c b/drivers/misc/ocxl/context.c index 994563a078eb9..de8a66b9d76bb 100644 --- a/drivers/misc/ocxl/context.c +++ b/drivers/misc/ocxl/context.c @@ -10,18 +10,17 @@ int ocxl_context_alloc(struct ocxl_context **context, struct ocxl_afu *afu, int pasid; struct ocxl_context *ctx; - *context = kzalloc(sizeof(struct ocxl_context), GFP_KERNEL); - if (!*context) + ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); + if (!ctx) return -ENOMEM; - ctx = *context; - ctx->afu = afu; mutex_lock(&afu->contexts_lock); pasid = idr_alloc(&afu->contexts_idr, ctx, afu->pasid_base, afu->pasid_base + afu->pasid_max, GFP_KERNEL); if (pasid < 0) { mutex_unlock(&afu->contexts_lock); + kfree(ctx); return pasid; } afu->pasid_count++; @@ -43,6 +42,7 @@ int ocxl_context_alloc(struct ocxl_context **context, struct ocxl_afu *afu, * duration of the life of the context */ ocxl_afu_get(afu); + *context = ctx; return 0; } EXPORT_SYMBOL_GPL(ocxl_context_alloc); -- GitLab From 65bc4c4063ae36223375a920b7d116e54d7def34 Mon Sep 17 00:00:00 2001 From: Andrii Nakryiko Date: Wed, 11 Dec 2019 17:35:58 -0800 Subject: [PATCH 0610/1437] selftests/bpf: Add CPU mask parsing tests Add a bunch of test validating CPU mask parsing logic and error handling. Signed-off-by: Andrii Nakryiko Signed-off-by: Alexei Starovoitov Link: https://lore.kernel.org/bpf/20191212013559.1690898-1-andriin@fb.com --- .../selftests/bpf/prog_tests/cpu_mask.c | 78 +++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 tools/testing/selftests/bpf/prog_tests/cpu_mask.c diff --git a/tools/testing/selftests/bpf/prog_tests/cpu_mask.c b/tools/testing/selftests/bpf/prog_tests/cpu_mask.c new file mode 100644 index 0000000000000..1fa1bdbaffa9d --- /dev/null +++ b/tools/testing/selftests/bpf/prog_tests/cpu_mask.c @@ -0,0 +1,78 @@ +// SPDX-License-Identifier: GPL-2.0 +#include +#include +#include "libbpf_internal.h" + +static int duration = 0; + +static void validate_mask(int case_nr, const char *exp, bool *mask, int n) +{ + int i; + + for (i = 0; exp[i]; i++) { + if (exp[i] == '1') { + if (CHECK(i + 1 > n, "mask_short", + "case #%d: mask too short, got n=%d, need at least %d\n", + case_nr, n, i + 1)) + return; + CHECK(!mask[i], "cpu_not_set", + "case #%d: mask differs, expected cpu#%d SET\n", + case_nr, i); + } else { + CHECK(i < n && mask[i], "cpu_set", + "case #%d: mask differs, expected cpu#%d UNSET\n", + case_nr, i); + } + } + CHECK(i < n, "mask_long", + "case #%d: mask too long, got n=%d, expected at most %d\n", + case_nr, n, i); +} + +static struct { + const char *cpu_mask; + const char *expect; + bool fails; +} test_cases[] = { + { "0\n", "1", false }, + { "0,2\n", "101", false }, + { "0-2\n", "111", false }, + { "0-2,3-4\n", "11111", false }, + { "0", "1", false }, + { "0-2", "111", false }, + { "0,2", "101", false }, + { "0,1-3", "1111", false }, + { "0,1,2,3", "1111", false }, + { "0,2-3,5", "101101", false }, + { "3-3", "0001", false }, + { "2-4,6,9-10", "00111010011", false }, + /* failure cases */ + { "", "", true }, + { "0-", "", true }, + { "0 ", "", true }, + { "0_1", "", true }, + { "1-0", "", true }, + { "-1", "", true }, +}; + +void test_cpu_mask() +{ + int i, err, n; + bool *mask; + + for (i = 0; i < ARRAY_SIZE(test_cases); i++) { + mask = NULL; + err = parse_cpu_mask_str(test_cases[i].cpu_mask, &mask, &n); + if (test_cases[i].fails) { + CHECK(!err, "should_fail", + "case #%d: parsing should fail!\n", i + 1); + } else { + if (CHECK(err, "parse_err", + "case #%d: cpu mask parsing failed: %d\n", + i + 1, err)) + continue; + validate_mask(i + 1, test_cases[i].expect, mask, n); + } + free(mask); + } +} -- GitLab From 783b8f01f5942a786998f5577bd9ff3992f22a1a Mon Sep 17 00:00:00 2001 From: Andrii Nakryiko Date: Wed, 11 Dec 2019 17:36:09 -0800 Subject: [PATCH 0611/1437] libbpf: Don't attach perf_buffer to offline/missing CPUs It's quite common on some systems to have more CPUs enlisted as "possible", than there are (and could ever be) present/online CPUs. In such cases, perf_buffer creationg will fail due to inability to create perf event on missing CPU with error like this: libbpf: failed to open perf buffer event on cpu #16: No such device This patch fixes the logic of perf_buffer__new() to ignore CPUs that are missing or currently offline. In rare cases where user explicitly listed specific CPUs to connect to, behavior is unchanged: libbpf will try to open perf event buffer on specified CPU(s) anyways. Fixes: fb84b8224655 ("libbpf: add perf buffer API") Signed-off-by: Andrii Nakryiko Signed-off-by: Alexei Starovoitov Link: https://lore.kernel.org/bpf/20191212013609.1691168-1-andriin@fb.com --- tools/lib/bpf/libbpf.c | 32 +++++++++++++++++++++++++------- 1 file changed, 25 insertions(+), 7 deletions(-) diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c index 98455e8aa6dbb..27d5f7ecba32c 100644 --- a/tools/lib/bpf/libbpf.c +++ b/tools/lib/bpf/libbpf.c @@ -5946,7 +5946,7 @@ struct perf_buffer { size_t mmap_size; struct perf_cpu_buf **cpu_bufs; struct epoll_event *events; - int cpu_cnt; + int cpu_cnt; /* number of allocated CPU buffers */ int epoll_fd; /* perf event FD */ int map_fd; /* BPF_MAP_TYPE_PERF_EVENT_ARRAY BPF map FD */ }; @@ -6080,11 +6080,13 @@ perf_buffer__new_raw(int map_fd, size_t page_cnt, static struct perf_buffer *__perf_buffer__new(int map_fd, size_t page_cnt, struct perf_buffer_params *p) { + const char *online_cpus_file = "/sys/devices/system/cpu/online"; struct bpf_map_info map = {}; char msg[STRERR_BUFSIZE]; struct perf_buffer *pb; + bool *online = NULL; __u32 map_info_len; - int err, i; + int err, i, j, n; if (page_cnt & (page_cnt - 1)) { pr_warn("page count should be power of two, but is %zu\n", @@ -6153,20 +6155,32 @@ static struct perf_buffer *__perf_buffer__new(int map_fd, size_t page_cnt, goto error; } - for (i = 0; i < pb->cpu_cnt; i++) { + err = parse_cpu_mask_file(online_cpus_file, &online, &n); + if (err) { + pr_warn("failed to get online CPU mask: %d\n", err); + goto error; + } + + for (i = 0, j = 0; i < pb->cpu_cnt; i++) { struct perf_cpu_buf *cpu_buf; int cpu, map_key; cpu = p->cpu_cnt > 0 ? p->cpus[i] : i; map_key = p->cpu_cnt > 0 ? p->map_keys[i] : i; + /* in case user didn't explicitly requested particular CPUs to + * be attached to, skip offline/not present CPUs + */ + if (p->cpu_cnt <= 0 && (cpu >= n || !online[cpu])) + continue; + cpu_buf = perf_buffer__open_cpu_buf(pb, p->attr, cpu, map_key); if (IS_ERR(cpu_buf)) { err = PTR_ERR(cpu_buf); goto error; } - pb->cpu_bufs[i] = cpu_buf; + pb->cpu_bufs[j] = cpu_buf; err = bpf_map_update_elem(pb->map_fd, &map_key, &cpu_buf->fd, 0); @@ -6178,21 +6192,25 @@ static struct perf_buffer *__perf_buffer__new(int map_fd, size_t page_cnt, goto error; } - pb->events[i].events = EPOLLIN; - pb->events[i].data.ptr = cpu_buf; + pb->events[j].events = EPOLLIN; + pb->events[j].data.ptr = cpu_buf; if (epoll_ctl(pb->epoll_fd, EPOLL_CTL_ADD, cpu_buf->fd, - &pb->events[i]) < 0) { + &pb->events[j]) < 0) { err = -errno; pr_warn("failed to epoll_ctl cpu #%d perf FD %d: %s\n", cpu, cpu_buf->fd, libbpf_strerror_r(err, msg, sizeof(msg))); goto error; } + j++; } + pb->cpu_cnt = j; + free(online); return pb; error: + free(online); if (pb) perf_buffer__free(pb); return ERR_PTR(err); -- GitLab From 91cbdf740a476cf2c744169bf407de2e3ac1f3cf Mon Sep 17 00:00:00 2001 From: Andrii Nakryiko Date: Wed, 11 Dec 2019 17:36:20 -0800 Subject: [PATCH 0612/1437] selftests/bpf: Fix perf_buffer test on systems w/ offline CPUs Fix up perf_buffer.c selftest to take into account offline/missing CPUs. Fixes: ee5cf82ce04a ("selftests/bpf: test perf buffer API") Signed-off-by: Andrii Nakryiko Signed-off-by: Alexei Starovoitov Link: https://lore.kernel.org/bpf/20191212013621.1691858-1-andriin@fb.com --- .../selftests/bpf/prog_tests/perf_buffer.c | 29 +++++++++++++++---- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/tools/testing/selftests/bpf/prog_tests/perf_buffer.c b/tools/testing/selftests/bpf/prog_tests/perf_buffer.c index 3003fddc0613f..cf6c87936c69d 100644 --- a/tools/testing/selftests/bpf/prog_tests/perf_buffer.c +++ b/tools/testing/selftests/bpf/prog_tests/perf_buffer.c @@ -4,6 +4,7 @@ #include #include #include +#include "libbpf_internal.h" static void on_sample(void *ctx, int cpu, void *data, __u32 size) { @@ -19,7 +20,7 @@ static void on_sample(void *ctx, int cpu, void *data, __u32 size) void test_perf_buffer(void) { - int err, prog_fd, nr_cpus, i, duration = 0; + int err, prog_fd, on_len, nr_on_cpus = 0, nr_cpus, i, duration = 0; const char *prog_name = "kprobe/sys_nanosleep"; const char *file = "./test_perf_buffer.o"; struct perf_buffer_opts pb_opts = {}; @@ -29,15 +30,27 @@ void test_perf_buffer(void) struct bpf_object *obj; struct perf_buffer *pb; struct bpf_link *link; + bool *online; nr_cpus = libbpf_num_possible_cpus(); if (CHECK(nr_cpus < 0, "nr_cpus", "err %d\n", nr_cpus)) return; + err = parse_cpu_mask_file("/sys/devices/system/cpu/online", + &online, &on_len); + if (CHECK(err, "nr_on_cpus", "err %d\n", err)) + return; + + for (i = 0; i < on_len; i++) + if (online[i]) + nr_on_cpus++; + /* load program */ err = bpf_prog_load(file, BPF_PROG_TYPE_KPROBE, &obj, &prog_fd); - if (CHECK(err, "obj_load", "err %d errno %d\n", err, errno)) - return; + if (CHECK(err, "obj_load", "err %d errno %d\n", err, errno)) { + obj = NULL; + goto out_close; + } prog = bpf_object__find_program_by_title(obj, prog_name); if (CHECK(!prog, "find_probe", "prog '%s' not found\n", prog_name)) @@ -64,6 +77,11 @@ void test_perf_buffer(void) /* trigger kprobe on every CPU */ CPU_ZERO(&cpu_seen); for (i = 0; i < nr_cpus; i++) { + if (i >= on_len || !online[i]) { + printf("skipping offline CPU #%d\n", i); + continue; + } + CPU_ZERO(&cpu_set); CPU_SET(i, &cpu_set); @@ -81,8 +99,8 @@ void test_perf_buffer(void) if (CHECK(err < 0, "perf_buffer__poll", "err %d\n", err)) goto out_free_pb; - if (CHECK(CPU_COUNT(&cpu_seen) != nr_cpus, "seen_cpu_cnt", - "expect %d, seen %d\n", nr_cpus, CPU_COUNT(&cpu_seen))) + if (CHECK(CPU_COUNT(&cpu_seen) != nr_on_cpus, "seen_cpu_cnt", + "expect %d, seen %d\n", nr_on_cpus, CPU_COUNT(&cpu_seen))) goto out_free_pb; out_free_pb: @@ -91,4 +109,5 @@ out_detach: bpf_link__destroy(link); out_close: bpf_object__close(obj); + free(online); } -- GitLab From 98e8627efcada18ac043a77b9101b4b4c768090b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20T=C3=B6pel?= Date: Fri, 13 Dec 2019 18:51:07 +0100 Subject: [PATCH 0613/1437] bpf: Move trampoline JIT image allocation to a function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Refactor the image allocation in the BPF trampoline code into a separate function, so it can be shared with the BPF dispatcher in upcoming commits. Signed-off-by: Björn Töpel Signed-off-by: Alexei Starovoitov Link: https://lore.kernel.org/bpf/20191213175112.30208-2-bjorn.topel@gmail.com --- include/linux/bpf.h | 1 + kernel/bpf/trampoline.c | 24 +++++++++++++++++------- 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/include/linux/bpf.h b/include/linux/bpf.h index 35903f148be59..5d744828b399b 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -475,6 +475,7 @@ struct bpf_trampoline *bpf_trampoline_lookup(u64 key); int bpf_trampoline_link_prog(struct bpf_prog *prog); int bpf_trampoline_unlink_prog(struct bpf_prog *prog); void bpf_trampoline_put(struct bpf_trampoline *tr); +void *bpf_jit_alloc_exec_page(void); #else static inline struct bpf_trampoline *bpf_trampoline_lookup(u64 key) { diff --git a/kernel/bpf/trampoline.c b/kernel/bpf/trampoline.c index 7e89f1f49d771..5ee301ddbd005 100644 --- a/kernel/bpf/trampoline.c +++ b/kernel/bpf/trampoline.c @@ -13,6 +13,22 @@ static struct hlist_head trampoline_table[TRAMPOLINE_TABLE_SIZE]; /* serializes access to trampoline_table */ static DEFINE_MUTEX(trampoline_mutex); +void *bpf_jit_alloc_exec_page(void) +{ + void *image; + + image = bpf_jit_alloc_exec(PAGE_SIZE); + if (!image) + return NULL; + + set_vm_flush_reset_perms(image); + /* Keep image as writeable. The alternative is to keep flipping ro/rw + * everytime new program is attached or detached. + */ + set_memory_x((long)image, 1); + return image; +} + struct bpf_trampoline *bpf_trampoline_lookup(u64 key) { struct bpf_trampoline *tr; @@ -33,7 +49,7 @@ struct bpf_trampoline *bpf_trampoline_lookup(u64 key) goto out; /* is_root was checked earlier. No need for bpf_jit_charge_modmem() */ - image = bpf_jit_alloc_exec(PAGE_SIZE); + image = bpf_jit_alloc_exec_page(); if (!image) { kfree(tr); tr = NULL; @@ -47,12 +63,6 @@ struct bpf_trampoline *bpf_trampoline_lookup(u64 key) mutex_init(&tr->mutex); for (i = 0; i < BPF_TRAMP_MAX; i++) INIT_HLIST_HEAD(&tr->progs_hlist[i]); - - set_vm_flush_reset_perms(image); - /* Keep image as writeable. The alternative is to keep flipping ro/rw - * everytime new program is attached or detached. - */ - set_memory_x((long)image, 1); tr->image = image; out: mutex_unlock(&trampoline_mutex); -- GitLab From 75ccbef6369e94ecac696a152a998a978d41376b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20T=C3=B6pel?= Date: Fri, 13 Dec 2019 18:51:08 +0100 Subject: [PATCH 0614/1437] bpf: Introduce BPF dispatcher MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The BPF dispatcher is a multi-way branch code generator, mainly targeted for XDP programs. When an XDP program is executed via the bpf_prog_run_xdp(), it is invoked via an indirect call. The indirect call has a substantial performance impact, when retpolines are enabled. The dispatcher transform indirect calls to direct calls, and therefore avoids the retpoline. The dispatcher is generated using the BPF JIT, and relies on text poking provided by bpf_arch_text_poke(). The dispatcher hijacks a trampoline function it via the __fentry__ nop of the trampoline. One dispatcher instance currently supports up to 64 dispatch points. A user creates a dispatcher with its corresponding trampoline with the DEFINE_BPF_DISPATCHER macro. Signed-off-by: Björn Töpel Signed-off-by: Alexei Starovoitov Link: https://lore.kernel.org/bpf/20191213175112.30208-3-bjorn.topel@gmail.com --- arch/x86/net/bpf_jit_comp.c | 122 ++++++++++++++++++++++++++++ include/linux/bpf.h | 56 +++++++++++++ kernel/bpf/Makefile | 1 + kernel/bpf/dispatcher.c | 158 ++++++++++++++++++++++++++++++++++++ 4 files changed, 337 insertions(+) create mode 100644 kernel/bpf/dispatcher.c diff --git a/arch/x86/net/bpf_jit_comp.c b/arch/x86/net/bpf_jit_comp.c index b8be184272774..3ce7ad41bd6fe 100644 --- a/arch/x86/net/bpf_jit_comp.c +++ b/arch/x86/net/bpf_jit_comp.c @@ -10,10 +10,12 @@ #include #include #include +#include #include #include #include #include +#include static u8 *emit_code(u8 *ptr, u32 bytes, unsigned int len) { @@ -1530,6 +1532,126 @@ int arch_prepare_bpf_trampoline(void *image, struct btf_func_model *m, u32 flags return 0; } +static int emit_cond_near_jump(u8 **pprog, void *func, void *ip, u8 jmp_cond) +{ + u8 *prog = *pprog; + int cnt = 0; + s64 offset; + + offset = func - (ip + 2 + 4); + if (!is_simm32(offset)) { + pr_err("Target %p is out of range\n", func); + return -EINVAL; + } + EMIT2_off32(0x0F, jmp_cond + 0x10, offset); + *pprog = prog; + return 0; +} + +static int emit_fallback_jump(u8 **pprog) +{ + u8 *prog = *pprog; + int err = 0; + +#ifdef CONFIG_RETPOLINE + /* Note that this assumes the the compiler uses external + * thunks for indirect calls. Both clang and GCC use the same + * naming convention for external thunks. + */ + err = emit_jump(&prog, __x86_indirect_thunk_rdx, prog); +#else + int cnt = 0; + + EMIT2(0xFF, 0xE2); /* jmp rdx */ +#endif + *pprog = prog; + return err; +} + +static int emit_bpf_dispatcher(u8 **pprog, int a, int b, s64 *progs) +{ + int pivot, err, jg_bytes = 1, cnt = 0; + u8 *jg_reloc, *prog = *pprog; + s64 jg_offset; + + if (a == b) { + /* Leaf node of recursion, i.e. not a range of indices + * anymore. + */ + EMIT1(add_1mod(0x48, BPF_REG_3)); /* cmp rdx,func */ + if (!is_simm32(progs[a])) + return -1; + EMIT2_off32(0x81, add_1reg(0xF8, BPF_REG_3), + progs[a]); + err = emit_cond_near_jump(&prog, /* je func */ + (void *)progs[a], prog, + X86_JE); + if (err) + return err; + + err = emit_fallback_jump(&prog); /* jmp thunk/indirect */ + if (err) + return err; + + *pprog = prog; + return 0; + } + + /* Not a leaf node, so we pivot, and recursively descend into + * the lower and upper ranges. + */ + pivot = (b - a) / 2; + EMIT1(add_1mod(0x48, BPF_REG_3)); /* cmp rdx,func */ + if (!is_simm32(progs[a + pivot])) + return -1; + EMIT2_off32(0x81, add_1reg(0xF8, BPF_REG_3), progs[a + pivot]); + + if (pivot > 2) { /* jg upper_part */ + /* Require near jump. */ + jg_bytes = 4; + EMIT2_off32(0x0F, X86_JG + 0x10, 0); + } else { + EMIT2(X86_JG, 0); + } + jg_reloc = prog; + + err = emit_bpf_dispatcher(&prog, a, a + pivot, /* emit lower_part */ + progs); + if (err) + return err; + + jg_offset = prog - jg_reloc; + emit_code(jg_reloc - jg_bytes, jg_offset, jg_bytes); + + err = emit_bpf_dispatcher(&prog, a + pivot + 1, /* emit upper_part */ + b, progs); + if (err) + return err; + + *pprog = prog; + return 0; +} + +static int cmp_ips(const void *a, const void *b) +{ + const s64 *ipa = a; + const s64 *ipb = b; + + if (*ipa > *ipb) + return 1; + if (*ipa < *ipb) + return -1; + return 0; +} + +int arch_prepare_bpf_dispatcher(void *image, s64 *funcs, int num_funcs) +{ + u8 *prog = image; + + sort(funcs, num_funcs, sizeof(funcs[0]), cmp_ips, NULL); + return emit_bpf_dispatcher(&prog, 0, num_funcs - 1, funcs); +} + struct x64_jit_data { struct bpf_binary_header *header; int *addrs; diff --git a/include/linux/bpf.h b/include/linux/bpf.h index 5d744828b399b..53ae4a50abe4f 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -470,12 +470,61 @@ struct bpf_trampoline { void *image; u64 selector; }; + +#define BPF_DISPATCHER_MAX 64 /* Fits in 2048B */ + +struct bpf_dispatcher_prog { + struct bpf_prog *prog; + refcount_t users; +}; + +struct bpf_dispatcher { + /* dispatcher mutex */ + struct mutex mutex; + void *func; + struct bpf_dispatcher_prog progs[BPF_DISPATCHER_MAX]; + int num_progs; + void *image; + u32 image_off; +}; + #ifdef CONFIG_BPF_JIT struct bpf_trampoline *bpf_trampoline_lookup(u64 key); int bpf_trampoline_link_prog(struct bpf_prog *prog); int bpf_trampoline_unlink_prog(struct bpf_prog *prog); void bpf_trampoline_put(struct bpf_trampoline *tr); void *bpf_jit_alloc_exec_page(void); +#define BPF_DISPATCHER_INIT(name) { \ + .mutex = __MUTEX_INITIALIZER(name.mutex), \ + .func = &name##func, \ + .progs = {}, \ + .num_progs = 0, \ + .image = NULL, \ + .image_off = 0 \ +} + +#define DEFINE_BPF_DISPATCHER(name) \ + noinline unsigned int name##func( \ + const void *ctx, \ + const struct bpf_insn *insnsi, \ + unsigned int (*bpf_func)(const void *, \ + const struct bpf_insn *)) \ + { \ + return bpf_func(ctx, insnsi); \ + } \ + EXPORT_SYMBOL(name##func); \ + struct bpf_dispatcher name = BPF_DISPATCHER_INIT(name); +#define DECLARE_BPF_DISPATCHER(name) \ + unsigned int name##func( \ + const void *ctx, \ + const struct bpf_insn *insnsi, \ + unsigned int (*bpf_func)(const void *, \ + const struct bpf_insn *)); \ + extern struct bpf_dispatcher name; +#define BPF_DISPATCHER_FUNC(name) name##func +#define BPF_DISPATCHER_PTR(name) (&name) +void bpf_dispatcher_change_prog(struct bpf_dispatcher *d, struct bpf_prog *from, + struct bpf_prog *to); #else static inline struct bpf_trampoline *bpf_trampoline_lookup(u64 key) { @@ -490,6 +539,13 @@ static inline int bpf_trampoline_unlink_prog(struct bpf_prog *prog) return -ENOTSUPP; } static inline void bpf_trampoline_put(struct bpf_trampoline *tr) {} +#define DEFINE_BPF_DISPATCHER(name) +#define DECLARE_BPF_DISPATCHER(name) +#define BPF_DISPATCHER_FUNC(name) bpf_dispatcher_nopfunc +#define BPF_DISPATCHER_PTR(name) NULL +static inline void bpf_dispatcher_change_prog(struct bpf_dispatcher *d, + struct bpf_prog *from, + struct bpf_prog *to) {} #endif struct bpf_func_info_aux { diff --git a/kernel/bpf/Makefile b/kernel/bpf/Makefile index 3f671bf617e89..d4f330351f87d 100644 --- a/kernel/bpf/Makefile +++ b/kernel/bpf/Makefile @@ -8,6 +8,7 @@ obj-$(CONFIG_BPF_SYSCALL) += local_storage.o queue_stack_maps.o obj-$(CONFIG_BPF_SYSCALL) += disasm.o obj-$(CONFIG_BPF_JIT) += trampoline.o obj-$(CONFIG_BPF_SYSCALL) += btf.o +obj-$(CONFIG_BPF_JIT) += dispatcher.o ifeq ($(CONFIG_NET),y) obj-$(CONFIG_BPF_SYSCALL) += devmap.o obj-$(CONFIG_BPF_SYSCALL) += cpumap.o diff --git a/kernel/bpf/dispatcher.c b/kernel/bpf/dispatcher.c new file mode 100644 index 0000000000000..204ee61a39049 --- /dev/null +++ b/kernel/bpf/dispatcher.c @@ -0,0 +1,158 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* Copyright(c) 2019 Intel Corporation. */ + +#include +#include +#include + +/* The BPF dispatcher is a multiway branch code generator. The + * dispatcher is a mechanism to avoid the performance penalty of an + * indirect call, which is expensive when retpolines are enabled. A + * dispatch client registers a BPF program into the dispatcher, and if + * there is available room in the dispatcher a direct call to the BPF + * program will be generated. All calls to the BPF programs called via + * the dispatcher will then be a direct call, instead of an + * indirect. The dispatcher hijacks a trampoline function it via the + * __fentry__ of the trampoline. The trampoline function has the + * following signature: + * + * unsigned int trampoline(const void *ctx, const struct bpf_insn *insnsi, + * unsigned int (*bpf_func)(const void *, + * const struct bpf_insn *)); + */ + +static struct bpf_dispatcher_prog *bpf_dispatcher_find_prog( + struct bpf_dispatcher *d, struct bpf_prog *prog) +{ + int i; + + for (i = 0; i < BPF_DISPATCHER_MAX; i++) { + if (prog == d->progs[i].prog) + return &d->progs[i]; + } + return NULL; +} + +static struct bpf_dispatcher_prog *bpf_dispatcher_find_free( + struct bpf_dispatcher *d) +{ + return bpf_dispatcher_find_prog(d, NULL); +} + +static bool bpf_dispatcher_add_prog(struct bpf_dispatcher *d, + struct bpf_prog *prog) +{ + struct bpf_dispatcher_prog *entry; + + if (!prog) + return false; + + entry = bpf_dispatcher_find_prog(d, prog); + if (entry) { + refcount_inc(&entry->users); + return false; + } + + entry = bpf_dispatcher_find_free(d); + if (!entry) + return false; + + bpf_prog_inc(prog); + entry->prog = prog; + refcount_set(&entry->users, 1); + d->num_progs++; + return true; +} + +static bool bpf_dispatcher_remove_prog(struct bpf_dispatcher *d, + struct bpf_prog *prog) +{ + struct bpf_dispatcher_prog *entry; + + if (!prog) + return false; + + entry = bpf_dispatcher_find_prog(d, prog); + if (!entry) + return false; + + if (refcount_dec_and_test(&entry->users)) { + entry->prog = NULL; + bpf_prog_put(prog); + d->num_progs--; + return true; + } + return false; +} + +int __weak arch_prepare_bpf_dispatcher(void *image, s64 *funcs, int num_funcs) +{ + return -ENOTSUPP; +} + +static int bpf_dispatcher_prepare(struct bpf_dispatcher *d, void *image) +{ + s64 ips[BPF_DISPATCHER_MAX] = {}, *ipsp = &ips[0]; + int i; + + for (i = 0; i < BPF_DISPATCHER_MAX; i++) { + if (d->progs[i].prog) + *ipsp++ = (s64)(uintptr_t)d->progs[i].prog->bpf_func; + } + return arch_prepare_bpf_dispatcher(image, &ips[0], d->num_progs); +} + +static void bpf_dispatcher_update(struct bpf_dispatcher *d, int prev_num_progs) +{ + void *old, *new; + u32 noff; + int err; + + if (!prev_num_progs) { + old = NULL; + noff = 0; + } else { + old = d->image + d->image_off; + noff = d->image_off ^ (PAGE_SIZE / 2); + } + + new = d->num_progs ? d->image + noff : NULL; + if (new) { + if (bpf_dispatcher_prepare(d, new)) + return; + } + + err = bpf_arch_text_poke(d->func, BPF_MOD_JUMP, old, new); + if (err || !new) + return; + + d->image_off = noff; +} + +void bpf_dispatcher_change_prog(struct bpf_dispatcher *d, struct bpf_prog *from, + struct bpf_prog *to) +{ + bool changed = false; + int prev_num_progs; + + if (from == to) + return; + + mutex_lock(&d->mutex); + if (!d->image) { + d->image = bpf_jit_alloc_exec_page(); + if (!d->image) + goto out; + } + + prev_num_progs = d->num_progs; + changed |= bpf_dispatcher_remove_prog(d, from); + changed |= bpf_dispatcher_add_prog(d, to); + + if (!changed) + goto out; + + bpf_dispatcher_update(d, prev_num_progs); +out: + mutex_unlock(&d->mutex); +} -- GitLab From 7e6897f95935973c3253fd756135b5ea58043dc8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20T=C3=B6pel?= Date: Fri, 13 Dec 2019 18:51:09 +0100 Subject: [PATCH 0615/1437] bpf, xdp: Start using the BPF dispatcher for XDP MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit adds a BPF dispatcher for XDP. The dispatcher is updated from the XDP control-path, dev_xdp_install(), and used when an XDP program is run via bpf_prog_run_xdp(). Signed-off-by: Björn Töpel Signed-off-by: Alexei Starovoitov Link: https://lore.kernel.org/bpf/20191213175112.30208-4-bjorn.topel@gmail.com --- include/linux/bpf.h | 15 +++++++++++++++ include/linux/filter.h | 40 ++++++++++++++++++++++++---------------- kernel/bpf/syscall.c | 26 ++++++++++++++++++-------- net/core/dev.c | 19 ++++++++++++++++++- net/core/filter.c | 8 ++++++++ 5 files changed, 83 insertions(+), 25 deletions(-) diff --git a/include/linux/bpf.h b/include/linux/bpf.h index 53ae4a50abe4f..5970989b99d1f 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -488,6 +488,14 @@ struct bpf_dispatcher { u32 image_off; }; +static __always_inline unsigned int bpf_dispatcher_nopfunc( + const void *ctx, + const struct bpf_insn *insnsi, + unsigned int (*bpf_func)(const void *, + const struct bpf_insn *)) +{ + return bpf_func(ctx, insnsi); +} #ifdef CONFIG_BPF_JIT struct bpf_trampoline *bpf_trampoline_lookup(u64 key); int bpf_trampoline_link_prog(struct bpf_prog *prog); @@ -997,6 +1005,8 @@ int btf_distill_func_proto(struct bpf_verifier_log *log, int btf_check_func_arg_match(struct bpf_verifier_env *env, int subprog); +struct bpf_prog *bpf_prog_by_id(u32 id); + #else /* !CONFIG_BPF_SYSCALL */ static inline struct bpf_prog *bpf_prog_get(u32 ufd) { @@ -1128,6 +1138,11 @@ static inline int bpf_prog_test_run_flow_dissector(struct bpf_prog *prog, static inline void bpf_map_put(struct bpf_map *map) { } + +static inline struct bpf_prog *bpf_prog_by_id(u32 id) +{ + return ERR_PTR(-ENOTSUPP); +} #endif /* CONFIG_BPF_SYSCALL */ static inline struct bpf_prog *bpf_prog_get_type(u32 ufd, diff --git a/include/linux/filter.h b/include/linux/filter.h index a141cb07e76a7..37ac7025031dc 100644 --- a/include/linux/filter.h +++ b/include/linux/filter.h @@ -559,23 +559,26 @@ struct sk_filter { DECLARE_STATIC_KEY_FALSE(bpf_stats_enabled_key); -#define BPF_PROG_RUN(prog, ctx) ({ \ - u32 ret; \ - cant_sleep(); \ - if (static_branch_unlikely(&bpf_stats_enabled_key)) { \ - struct bpf_prog_stats *stats; \ - u64 start = sched_clock(); \ - ret = (*(prog)->bpf_func)(ctx, (prog)->insnsi); \ - stats = this_cpu_ptr(prog->aux->stats); \ - u64_stats_update_begin(&stats->syncp); \ - stats->cnt++; \ - stats->nsecs += sched_clock() - start; \ - u64_stats_update_end(&stats->syncp); \ - } else { \ - ret = (*(prog)->bpf_func)(ctx, (prog)->insnsi); \ - } \ +#define __BPF_PROG_RUN(prog, ctx, dfunc) ({ \ + u32 ret; \ + cant_sleep(); \ + if (static_branch_unlikely(&bpf_stats_enabled_key)) { \ + struct bpf_prog_stats *stats; \ + u64 start = sched_clock(); \ + ret = dfunc(ctx, (prog)->insnsi, (prog)->bpf_func); \ + stats = this_cpu_ptr(prog->aux->stats); \ + u64_stats_update_begin(&stats->syncp); \ + stats->cnt++; \ + stats->nsecs += sched_clock() - start; \ + u64_stats_update_end(&stats->syncp); \ + } else { \ + ret = dfunc(ctx, (prog)->insnsi, (prog)->bpf_func); \ + } \ ret; }) +#define BPF_PROG_RUN(prog, ctx) __BPF_PROG_RUN(prog, ctx, \ + bpf_dispatcher_nopfunc) + #define BPF_SKB_CB_LEN QDISC_CB_PRIV_LEN struct bpf_skb_data_end { @@ -699,6 +702,8 @@ static inline u32 bpf_prog_run_clear_cb(const struct bpf_prog *prog, return res; } +DECLARE_BPF_DISPATCHER(bpf_dispatcher_xdp) + static __always_inline u32 bpf_prog_run_xdp(const struct bpf_prog *prog, struct xdp_buff *xdp) { @@ -708,9 +713,12 @@ static __always_inline u32 bpf_prog_run_xdp(const struct bpf_prog *prog, * already takes rcu_read_lock() when fetching the program, so * it's not necessary here anymore. */ - return BPF_PROG_RUN(prog, xdp); + return __BPF_PROG_RUN(prog, xdp, + BPF_DISPATCHER_FUNC(bpf_dispatcher_xdp)); } +void bpf_prog_change_xdp(struct bpf_prog *prev_prog, struct bpf_prog *prog); + static inline u32 bpf_prog_insn_size(const struct bpf_prog *prog) { return prog->len * sizeof(struct bpf_insn); diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c index 66b90eaf99fe8..b08c362f4e020 100644 --- a/kernel/bpf/syscall.c +++ b/kernel/bpf/syscall.c @@ -2338,17 +2338,12 @@ static int bpf_obj_get_next_id(const union bpf_attr *attr, #define BPF_PROG_GET_FD_BY_ID_LAST_FIELD prog_id -static int bpf_prog_get_fd_by_id(const union bpf_attr *attr) +struct bpf_prog *bpf_prog_by_id(u32 id) { struct bpf_prog *prog; - u32 id = attr->prog_id; - int fd; - - if (CHECK_ATTR(BPF_PROG_GET_FD_BY_ID)) - return -EINVAL; - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; + if (!id) + return ERR_PTR(-ENOENT); spin_lock_bh(&prog_idr_lock); prog = idr_find(&prog_idr, id); @@ -2357,7 +2352,22 @@ static int bpf_prog_get_fd_by_id(const union bpf_attr *attr) else prog = ERR_PTR(-ENOENT); spin_unlock_bh(&prog_idr_lock); + return prog; +} + +static int bpf_prog_get_fd_by_id(const union bpf_attr *attr) +{ + struct bpf_prog *prog; + u32 id = attr->prog_id; + int fd; + + if (CHECK_ATTR(BPF_PROG_GET_FD_BY_ID)) + return -EINVAL; + + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + prog = bpf_prog_by_id(id); if (IS_ERR(prog)) return PTR_ERR(prog); diff --git a/net/core/dev.c b/net/core/dev.c index 2c277b8aba38b..255d3cf353608 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -8542,7 +8542,17 @@ static int dev_xdp_install(struct net_device *dev, bpf_op_t bpf_op, struct netlink_ext_ack *extack, u32 flags, struct bpf_prog *prog) { + bool non_hw = !(flags & XDP_FLAGS_HW_MODE); + struct bpf_prog *prev_prog = NULL; struct netdev_bpf xdp; + int err; + + if (non_hw) { + prev_prog = bpf_prog_by_id(__dev_xdp_query(dev, bpf_op, + XDP_QUERY_PROG)); + if (IS_ERR(prev_prog)) + prev_prog = NULL; + } memset(&xdp, 0, sizeof(xdp)); if (flags & XDP_FLAGS_HW_MODE) @@ -8553,7 +8563,14 @@ static int dev_xdp_install(struct net_device *dev, bpf_op_t bpf_op, xdp.flags = flags; xdp.prog = prog; - return bpf_op(dev, &xdp); + err = bpf_op(dev, &xdp); + if (!err && non_hw) + bpf_prog_change_xdp(prev_prog, prog); + + if (prev_prog) + bpf_prog_put(prev_prog); + + return err; } static void dev_xdp_uninstall(struct net_device *dev) diff --git a/net/core/filter.c b/net/core/filter.c index f1e703eed3d22..a411f7835deef 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -8940,3 +8940,11 @@ const struct bpf_verifier_ops sk_reuseport_verifier_ops = { const struct bpf_prog_ops sk_reuseport_prog_ops = { }; #endif /* CONFIG_INET */ + +DEFINE_BPF_DISPATCHER(bpf_dispatcher_xdp) + +void bpf_prog_change_xdp(struct bpf_prog *prev_prog, struct bpf_prog *prog) +{ + bpf_dispatcher_change_prog(BPF_DISPATCHER_PTR(bpf_dispatcher_xdp), + prev_prog, prog); +} -- GitLab From f23c4b3924d2e9382820ee677b68d42d5dd7b08b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20T=C3=B6pel?= Date: Fri, 13 Dec 2019 18:51:10 +0100 Subject: [PATCH 0616/1437] bpf: Start using the BPF dispatcher in BPF_TEST_RUN MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In order to properly exercise the BPF dispatcher, this commit adds BPF dispatcher usage to BPF_TEST_RUN when executing XDP programs. Signed-off-by: Björn Töpel Signed-off-by: Alexei Starovoitov Link: https://lore.kernel.org/bpf/20191213175112.30208-5-bjorn.topel@gmail.com --- net/bpf/test_run.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/net/bpf/test_run.c b/net/bpf/test_run.c index 85c8cbbada922..5016c538d3ce7 100644 --- a/net/bpf/test_run.c +++ b/net/bpf/test_run.c @@ -15,7 +15,7 @@ #include static int bpf_test_run(struct bpf_prog *prog, void *ctx, u32 repeat, - u32 *retval, u32 *time) + u32 *retval, u32 *time, bool xdp) { struct bpf_cgroup_storage *storage[MAX_BPF_CGROUP_STORAGE_TYPE] = { NULL }; enum bpf_cgroup_storage_type stype; @@ -41,7 +41,11 @@ static int bpf_test_run(struct bpf_prog *prog, void *ctx, u32 repeat, time_start = ktime_get_ns(); for (i = 0; i < repeat; i++) { bpf_cgroup_storage_set(storage); - *retval = BPF_PROG_RUN(prog, ctx); + + if (xdp) + *retval = bpf_prog_run_xdp(prog, ctx); + else + *retval = BPF_PROG_RUN(prog, ctx); if (signal_pending(current)) { ret = -EINTR; @@ -356,7 +360,7 @@ int bpf_prog_test_run_skb(struct bpf_prog *prog, const union bpf_attr *kattr, ret = convert___skb_to_skb(skb, ctx); if (ret) goto out; - ret = bpf_test_run(prog, skb, repeat, &retval, &duration); + ret = bpf_test_run(prog, skb, repeat, &retval, &duration, false); if (ret) goto out; if (!is_l2) { @@ -413,8 +417,8 @@ int bpf_prog_test_run_xdp(struct bpf_prog *prog, const union bpf_attr *kattr, rxqueue = __netif_get_rx_queue(current->nsproxy->net_ns->loopback_dev, 0); xdp.rxq = &rxqueue->xdp_rxq; - - ret = bpf_test_run(prog, &xdp, repeat, &retval, &duration); + bpf_prog_change_xdp(NULL, prog); + ret = bpf_test_run(prog, &xdp, repeat, &retval, &duration, true); if (ret) goto out; if (xdp.data != data + XDP_PACKET_HEADROOM + NET_IP_ALIGN || @@ -422,6 +426,7 @@ int bpf_prog_test_run_xdp(struct bpf_prog *prog, const union bpf_attr *kattr, size = xdp.data_end - xdp.data; ret = bpf_test_finish(kattr, uattr, xdp.data, size, retval, duration); out: + bpf_prog_change_xdp(prog, NULL); kfree(data); return ret; } -- GitLab From e754f5a6e36b63d6732f52adcbe4c447fd66896f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20T=C3=B6pel?= Date: Fri, 13 Dec 2019 18:51:11 +0100 Subject: [PATCH 0617/1437] selftests: bpf: Add xdp_perf test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The xdp_perf is a dummy XDP test, only used to measure the the cost of jumping into a naive XDP program one million times. To build and run the program: $ cd tools/testing/selftests/bpf $ make $ ./test_progs -v -t xdp_perf Signed-off-by: Björn Töpel Signed-off-by: Alexei Starovoitov Link: https://lore.kernel.org/bpf/20191213175112.30208-6-bjorn.topel@gmail.com --- .../selftests/bpf/prog_tests/xdp_perf.c | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 tools/testing/selftests/bpf/prog_tests/xdp_perf.c diff --git a/tools/testing/selftests/bpf/prog_tests/xdp_perf.c b/tools/testing/selftests/bpf/prog_tests/xdp_perf.c new file mode 100644 index 0000000000000..7185bee16fe4f --- /dev/null +++ b/tools/testing/selftests/bpf/prog_tests/xdp_perf.c @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: GPL-2.0 +#include + +void test_xdp_perf(void) +{ + const char *file = "./xdp_dummy.o"; + __u32 duration, retval, size; + struct bpf_object *obj; + char in[128], out[128]; + int err, prog_fd; + + err = bpf_prog_load(file, BPF_PROG_TYPE_XDP, &obj, &prog_fd); + if (CHECK_FAIL(err)) + return; + + err = bpf_prog_test_run(prog_fd, 1000000, &in[0], 128, + out, &size, &retval, &duration); + + CHECK(err || retval != XDP_PASS || size != 128, + "xdp-perf", + "err %d errno %d retval %d size %d\n", + err, errno, retval, size); + + bpf_object__close(obj); +} -- GitLab From 116eb788f57c9c35c40b29cfaa2607020de99a84 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20T=C3=B6pel?= Date: Fri, 13 Dec 2019 18:51:12 +0100 Subject: [PATCH 0618/1437] bpf, x86: Align dispatcher branch targets to 16B MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit >From Intel 64 and IA-32 Architectures Optimization Reference Manual, 3.4.1.4 Code Alignment, Assembly/Compiler Coding Rule 11: All branch targets should be 16-byte aligned. This commits aligns branch targets according to the Intel manual. The nops used to align branch targets make the dispatcher larger, and therefore the number of supported dispatch points/programs are descreased from 64 to 48. Signed-off-by: Björn Töpel Signed-off-by: Alexei Starovoitov Link: https://lore.kernel.org/bpf/20191213175112.30208-7-bjorn.topel@gmail.com --- arch/x86/net/bpf_jit_comp.c | 30 +++++++++++++++++++++++++++++- include/linux/bpf.h | 2 +- 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/arch/x86/net/bpf_jit_comp.c b/arch/x86/net/bpf_jit_comp.c index 3ce7ad41bd6fe..4c8a2d1f84700 100644 --- a/arch/x86/net/bpf_jit_comp.c +++ b/arch/x86/net/bpf_jit_comp.c @@ -1548,6 +1548,26 @@ static int emit_cond_near_jump(u8 **pprog, void *func, void *ip, u8 jmp_cond) return 0; } +static void emit_nops(u8 **pprog, unsigned int len) +{ + unsigned int i, noplen; + u8 *prog = *pprog; + int cnt = 0; + + while (len > 0) { + noplen = len; + + if (noplen > ASM_NOP_MAX) + noplen = ASM_NOP_MAX; + + for (i = 0; i < noplen; i++) + EMIT1(ideal_nops[noplen][i]); + len -= noplen; + } + + *pprog = prog; +} + static int emit_fallback_jump(u8 **pprog) { u8 *prog = *pprog; @@ -1570,8 +1590,8 @@ static int emit_fallback_jump(u8 **pprog) static int emit_bpf_dispatcher(u8 **pprog, int a, int b, s64 *progs) { + u8 *jg_reloc, *jg_target, *prog = *pprog; int pivot, err, jg_bytes = 1, cnt = 0; - u8 *jg_reloc, *prog = *pprog; s64 jg_offset; if (a == b) { @@ -1620,6 +1640,14 @@ static int emit_bpf_dispatcher(u8 **pprog, int a, int b, s64 *progs) if (err) return err; + /* From Intel 64 and IA-32 Architectures Optimization + * Reference Manual, 3.4.1.4 Code Alignment, Assembly/Compiler + * Coding Rule 11: All branch targets should be 16-byte + * aligned. + */ + jg_target = PTR_ALIGN(prog, 16); + if (jg_target != prog) + emit_nops(&prog, jg_target - prog); jg_offset = prog - jg_reloc; emit_code(jg_reloc - jg_bytes, jg_offset, jg_bytes); diff --git a/include/linux/bpf.h b/include/linux/bpf.h index 5970989b99d1f..d467983e61bb2 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -471,7 +471,7 @@ struct bpf_trampoline { u64 selector; }; -#define BPF_DISPATCHER_MAX 64 /* Fits in 2048B */ +#define BPF_DISPATCHER_MAX 48 /* Fits in 2048B */ struct bpf_dispatcher_prog { struct bpf_prog *prog; -- GitLab From 9c3194dd93b067d0a9fd84d516de69f438dbc9c2 Mon Sep 17 00:00:00 2001 From: Subash Abhinov Kasiviswanathan Date: Thu, 12 Dec 2019 04:00:15 +0000 Subject: [PATCH 0619/1437] MAINTAINERS: Add maintainers for rmnet Add myself and Sean as maintainers for rmnet driver. Signed-off-by: Sean Tranchetti Signed-off-by: Subash Abhinov Kasiviswanathan Signed-off-by: Jakub Kicinski --- MAINTAINERS | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index b908e56639e34..e34488f7baae1 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -13709,6 +13709,15 @@ L: linux-arm-msm@vger.kernel.org S: Maintained F: drivers/iommu/qcom_iommu.c +QUALCOMM RMNET DRIVER +M: Subash Abhinov Kasiviswanathan +M: Sean Tranchetti +L: netdev@vger.kernel.org +S: Maintained +F: drivers/net/ethernet/qualcomm/rmnet/ +F: Documentation/networking/device_drivers/qualcomm/rmnet.txt +F: include/linux/if_rmnet.h + QUALCOMM TSENS THERMAL DRIVER M: Amit Kucheria L: linux-pm@vger.kernel.org -- GitLab From 692b93af71fb6795887affd6a281e4f91407489f Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Thu, 12 Dec 2019 12:59:08 -0500 Subject: [PATCH 0620/1437] mailmap: add entry for myself I no longer work at Savoir-faire Linux but even though MAINTAINERS is up-to-date, some emails are still sent to my old email address. Signed-off-by: Vivien Didelot Signed-off-by: Jakub Kicinski --- .mailmap | 1 + 1 file changed, 1 insertion(+) diff --git a/.mailmap b/.mailmap index c24773db04a7a..d33bb3a3b70a5 100644 --- a/.mailmap +++ b/.mailmap @@ -265,6 +265,7 @@ Vinod Koul Viresh Kumar Viresh Kumar Viresh Kumar +Vivien Didelot Vlad Dogaru Vladimir Davydov Vladimir Davydov -- GitLab From 5133498f4ad1123a5ffd4c08df6431dab882cc32 Mon Sep 17 00:00:00 2001 From: Lorenz Bauer Date: Fri, 13 Dec 2019 18:08:17 +0000 Subject: [PATCH 0621/1437] bpf: Clear skb->tstamp in bpf_redirect when necessary Redirecting a packet from ingress to egress by using bpf_redirect breaks if the egress interface has an fq qdisc installed. This is the same problem as fixed in 'commit 8203e2d844d3 ("net: clear skb->tstamp in forwarding paths") Clear skb->tstamp when redirecting into the egress path. Fixes: 80b14dee2bea ("net: Add a new socket option for a future transmit time.") Fixes: fb420d5d91c1 ("tcp/fq: move back to CLOCK_MONOTONIC") Signed-off-by: Lorenz Bauer Signed-off-by: Alexei Starovoitov Reviewed-by: Eric Dumazet Link: https://lore.kernel.org/bpf/20191213180817.2510-1-lmb@cloudflare.com --- net/core/filter.c | 1 + 1 file changed, 1 insertion(+) diff --git a/net/core/filter.c b/net/core/filter.c index f1e703eed3d22..d914257763b5d 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -2055,6 +2055,7 @@ static inline int __bpf_tx_skb(struct net_device *dev, struct sk_buff *skb) } skb->dev = dev; + skb->tstamp = 0; dev_xmit_recursion_inc(); ret = dev_queue_xmit(skb); -- GitLab From 850a88cc4096fe1df407452ba2e4d28cf5b3eee9 Mon Sep 17 00:00:00 2001 From: Stanislav Fomichev Date: Fri, 13 Dec 2019 14:30:27 -0800 Subject: [PATCH 0622/1437] bpf: Expose __sk_buff wire_len/gso_segs to BPF_PROG_TEST_RUN wire_len should not be less than real len and is capped by GSO_MAX_SIZE. gso_segs is capped by GSO_MAX_SEGS. v2: * set wire_len to skb->len when passed wire_len is 0 (Alexei Starovoitov) Signed-off-by: Stanislav Fomichev Signed-off-by: Alexei Starovoitov Cc: Martin KaFai Lau Link: https://lore.kernel.org/bpf/20191213223028.161282-1-sdf@google.com --- net/bpf/test_run.c | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/net/bpf/test_run.c b/net/bpf/test_run.c index 5016c538d3ce7..93a9c87787e07 100644 --- a/net/bpf/test_run.c +++ b/net/bpf/test_run.c @@ -267,8 +267,10 @@ static int convert___skb_to_skb(struct sk_buff *skb, struct __sk_buff *__skb) return -EINVAL; /* tstamp is allowed */ + /* wire_len is allowed */ + /* gso_segs is allowed */ - if (!range_is_zero(__skb, offsetofend(struct __sk_buff, tstamp), + if (!range_is_zero(__skb, offsetofend(struct __sk_buff, gso_segs), sizeof(struct __sk_buff))) return -EINVAL; @@ -276,6 +278,19 @@ static int convert___skb_to_skb(struct sk_buff *skb, struct __sk_buff *__skb) skb->tstamp = __skb->tstamp; memcpy(&cb->data, __skb->cb, QDISC_CB_PRIV_LEN); + if (__skb->wire_len == 0) { + cb->pkt_len = skb->len; + } else { + if (__skb->wire_len < skb->len || + __skb->wire_len > GSO_MAX_SIZE) + return -EINVAL; + cb->pkt_len = __skb->wire_len; + } + + if (__skb->gso_segs > GSO_MAX_SEGS) + return -EINVAL; + skb_shinfo(skb)->gso_segs = __skb->gso_segs; + return 0; } @@ -289,6 +304,8 @@ static void convert_skb_to___skb(struct sk_buff *skb, struct __sk_buff *__skb) __skb->priority = skb->priority; __skb->tstamp = skb->tstamp; memcpy(__skb->cb, &cb->data, QDISC_CB_PRIV_LEN); + __skb->wire_len = cb->pkt_len; + __skb->gso_segs = skb_shinfo(skb)->gso_segs; } int bpf_prog_test_run_skb(struct bpf_prog *prog, const union bpf_attr *kattr, -- GitLab From a06bf42f5a95ff462401d59d0c08cf4620213647 Mon Sep 17 00:00:00 2001 From: Stanislav Fomichev Date: Fri, 13 Dec 2019 14:30:28 -0800 Subject: [PATCH 0623/1437] selftests/bpf: Test wire_len/gso_segs in BPF_PROG_TEST_RUN Make sure we can pass arbitrary data in wire_len/gso_segs. Signed-off-by: Stanislav Fomichev Signed-off-by: Alexei Starovoitov Acked-by: Martin KaFai Lau Link: https://lore.kernel.org/bpf/20191213223028.161282-2-sdf@google.com --- tools/testing/selftests/bpf/prog_tests/skb_ctx.c | 2 ++ tools/testing/selftests/bpf/progs/test_skb_ctx.c | 5 +++++ 2 files changed, 7 insertions(+) diff --git a/tools/testing/selftests/bpf/prog_tests/skb_ctx.c b/tools/testing/selftests/bpf/prog_tests/skb_ctx.c index a2eb8db8dafb6..edf5e8c7d400d 100644 --- a/tools/testing/selftests/bpf/prog_tests/skb_ctx.c +++ b/tools/testing/selftests/bpf/prog_tests/skb_ctx.c @@ -11,6 +11,8 @@ void test_skb_ctx(void) .cb[4] = 5, .priority = 6, .tstamp = 7, + .wire_len = 100, + .gso_segs = 8, }; struct bpf_prog_test_run_attr tattr = { .data_in = &pkt_v4, diff --git a/tools/testing/selftests/bpf/progs/test_skb_ctx.c b/tools/testing/selftests/bpf/progs/test_skb_ctx.c index 2a9f4c736ebc1..534fbf9a73445 100644 --- a/tools/testing/selftests/bpf/progs/test_skb_ctx.c +++ b/tools/testing/selftests/bpf/progs/test_skb_ctx.c @@ -18,5 +18,10 @@ int process(struct __sk_buff *skb) skb->priority++; skb->tstamp++; + if (skb->wire_len != 100) + return 1; + if (skb->gso_segs != 8) + return 1; + return 0; } -- GitLab From 6adc4601c2a1ac87b4ab8ed0cb55db6efd0264e8 Mon Sep 17 00:00:00 2001 From: Jonathan Lemon Date: Tue, 10 Dec 2019 08:39:46 -0800 Subject: [PATCH 0624/1437] bnxt: apply computed clamp value for coalece parameter After executing "ethtool -C eth0 rx-usecs-irq 0", the box becomes unresponsive, likely due to interrupt livelock. It appears that a minimum clamp value for the irq timer is computed, but is never applied. Fix by applying the corrected clamp value. Fixes: 74706afa712d ("bnxt_en: Update interrupt coalescing logic.") Signed-off-by: Jonathan Lemon Signed-off-by: Michael Chan Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/broadcom/bnxt/bnxt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index a754903b27eee..c779f9cf8822f 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -6190,7 +6190,7 @@ static void bnxt_hwrm_set_coal_params(struct bnxt *bp, tmr = bnxt_usec_to_coal_tmr(bp, hw_coal->coal_ticks_irq); val = clamp_t(u16, tmr, 1, coal_cap->cmpl_aggr_dma_tmr_during_int_max); - req->cmpl_aggr_dma_tmr_during_int = cpu_to_le16(tmr); + req->cmpl_aggr_dma_tmr_during_int = cpu_to_le16(val); req->enables |= cpu_to_le16(BNXT_COAL_CMPL_AGGR_TMR_DURING_INT_ENABLE); } -- GitLab From 03b06e3f83523ded51aa822f58c083fbde08feb5 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 10 Dec 2019 20:56:34 +0100 Subject: [PATCH 0625/1437] ptp: clockmatrix: add I2C dependency Without I2C, we get a link failure: drivers/ptp/ptp_clockmatrix.o: In function `idtcm_xfer.isra.3': ptp_clockmatrix.c:(.text+0xcc): undefined reference to `i2c_transfer' drivers/ptp/ptp_clockmatrix.o: In function `idtcm_driver_init': ptp_clockmatrix.c:(.init.text+0x14): undefined reference to `i2c_register_driver' drivers/ptp/ptp_clockmatrix.o: In function `idtcm_driver_exit': ptp_clockmatrix.c:(.exit.text+0x10): undefined reference to `i2c_del_driver' Fixes: 3a6ba7dc7799 ("ptp: Add a ptp clock driver for IDT ClockMatrix.") Signed-off-by: Arnd Bergmann Reviewed-by: Vincent Cheng Signed-off-by: Jakub Kicinski --- drivers/ptp/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/ptp/Kconfig b/drivers/ptp/Kconfig index b45d2b86d8ca2..b0d1b8d264fa5 100644 --- a/drivers/ptp/Kconfig +++ b/drivers/ptp/Kconfig @@ -121,7 +121,7 @@ config PTP_1588_CLOCK_KVM config PTP_1588_CLOCK_IDTCM tristate "IDT CLOCKMATRIX as PTP clock" - depends on PTP_1588_CLOCK + depends on PTP_1588_CLOCK && I2C default n help This driver adds support for using IDT CLOCKMATRIX(TM) as a PTP -- GitLab From 2beb6d2901a3f73106485d560c49981144aeacb1 Mon Sep 17 00:00:00 2001 From: Hangbin Liu Date: Wed, 11 Dec 2019 22:20:16 +0800 Subject: [PATCH 0626/1437] ipv6/addrconf: only check invalid header values when NETLINK_F_STRICT_CHK is set In commit 4b1373de73a3 ("net: ipv6: addr: perform strict checks also for doit handlers") we add strict check for inet6_rtm_getaddr(). But we did the invalid header values check before checking if NETLINK_F_STRICT_CHK is set. This may break backwards compatibility if user already set the ifm->ifa_prefixlen, ifm->ifa_flags, ifm->ifa_scope in their netlink code. I didn't move the nlmsg_len check because I thought it's a valid check. Reported-by: Jianlin Shi Fixes: 4b1373de73a3 ("net: ipv6: addr: perform strict checks also for doit handlers") Signed-off-by: Hangbin Liu Reviewed-by: David Ahern Signed-off-by: Jakub Kicinski --- net/ipv6/addrconf.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index 98d82305d6ded..39d861d003771 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -5231,16 +5231,16 @@ static int inet6_rtm_valid_getaddr_req(struct sk_buff *skb, return -EINVAL; } + if (!netlink_strict_get_check(skb)) + return nlmsg_parse_deprecated(nlh, sizeof(*ifm), tb, IFA_MAX, + ifa_ipv6_policy, extack); + ifm = nlmsg_data(nlh); if (ifm->ifa_prefixlen || ifm->ifa_flags || ifm->ifa_scope) { NL_SET_ERR_MSG_MOD(extack, "Invalid values in header for get address request"); return -EINVAL; } - if (!netlink_strict_get_check(skb)) - return nlmsg_parse_deprecated(nlh, sizeof(*ifm), tb, IFA_MAX, - ifa_ipv6_policy, extack); - err = nlmsg_parse_deprecated_strict(nlh, sizeof(*ifm), tb, IFA_MAX, ifa_ipv6_policy, extack); if (err) -- GitLab From 8f9cc1ee296275d27770245cbd247a4952bbb2be Mon Sep 17 00:00:00 2001 From: Thomas Falcon Date: Wed, 11 Dec 2019 09:38:39 -0600 Subject: [PATCH 0627/1437] net/ibmvnic: Fix typo in retry check This conditional is missing a bang, with the intent being to break when the retry count reaches zero. Fixes: 476d96ca9cc5 ("ibmvnic: Bound waits for device queries") Suggested-by: Juliet Kim Signed-off-by: Thomas Falcon Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/ibm/ibmvnic.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/ibm/ibmvnic.c b/drivers/net/ethernet/ibm/ibmvnic.c index c900807819244..830791ab4619c 100644 --- a/drivers/net/ethernet/ibm/ibmvnic.c +++ b/drivers/net/ethernet/ibm/ibmvnic.c @@ -184,7 +184,7 @@ static int ibmvnic_wait_for_completion(struct ibmvnic_adapter *adapter, netdev_err(netdev, "Device down!\n"); return -ENODEV; } - if (retry--) + if (!retry--) break; if (wait_for_completion_timeout(comp_done, div_timeout)) return 0; -- GitLab From 1d1997db870f4058676439ef7014390ba9e24eb2 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Tue, 10 Dec 2019 10:20:32 -0800 Subject: [PATCH 0628/1437] Revert "nfp: abm: fix memory leak in nfp_abm_u32_knode_replace" This reverts commit 78beef629fd9 ("nfp: abm: fix memory leak in nfp_abm_u32_knode_replace"). The quoted commit does not fix anything and resulted in a bogus CVE-2019-19076. If match is NULL then it is known there is no matching entry in list, hence, calling nfp_abm_u32_knode_delete() is pointless. Signed-off-by: Jakub Kicinski Reviewed-by: John Hurley --- drivers/net/ethernet/netronome/nfp/abm/cls.c | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/drivers/net/ethernet/netronome/nfp/abm/cls.c b/drivers/net/ethernet/netronome/nfp/abm/cls.c index 9f8a1f69c0c4c..23ebddfb95325 100644 --- a/drivers/net/ethernet/netronome/nfp/abm/cls.c +++ b/drivers/net/ethernet/netronome/nfp/abm/cls.c @@ -176,10 +176,8 @@ nfp_abm_u32_knode_replace(struct nfp_abm_link *alink, u8 mask, val; int err; - if (!nfp_abm_u32_check_knode(alink->abm, knode, proto, extack)) { - err = -EOPNOTSUPP; + if (!nfp_abm_u32_check_knode(alink->abm, knode, proto, extack)) goto err_delete; - } tos_off = proto == htons(ETH_P_IP) ? 16 : 20; @@ -200,18 +198,14 @@ nfp_abm_u32_knode_replace(struct nfp_abm_link *alink, if ((iter->val & cmask) == (val & cmask) && iter->band != knode->res->classid) { NL_SET_ERR_MSG_MOD(extack, "conflict with already offloaded filter"); - err = -EOPNOTSUPP; goto err_delete; } } if (!match) { match = kzalloc(sizeof(*match), GFP_KERNEL); - if (!match) { - err = -ENOMEM; - goto err_delete; - } - + if (!match) + return -ENOMEM; list_add(&match->list, &alink->dscp_map); } match->handle = knode->handle; @@ -227,7 +221,7 @@ nfp_abm_u32_knode_replace(struct nfp_abm_link *alink, err_delete: nfp_abm_u32_knode_delete(alink, knode); - return err; + return -EOPNOTSUPP; } static int nfp_abm_setup_tc_block_cb(enum tc_setup_type type, -- GitLab From 8dbd76e79a16b45b2ccb01d2f2e08dbf64e71e40 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Fri, 13 Dec 2019 18:20:41 -0800 Subject: [PATCH 0629/1437] tcp/dccp: fix possible race __inet_lookup_established() Michal Kubecek and Firo Yang did a very nice analysis of crashes happening in __inet_lookup_established(). Since a TCP socket can go from TCP_ESTABLISH to TCP_LISTEN (via a close()/socket()/listen() cycle) without a RCU grace period, I should not have changed listeners linkage in their hash table. They must use the nulls protocol (Documentation/RCU/rculist_nulls.txt), so that a lookup can detect a socket in a hash list was moved in another one. Since we added code in commit d296ba60d8e2 ("soreuseport: Resolve merge conflict for v4/v6 ordering fix"), we have to add hlist_nulls_add_tail_rcu() helper. Fixes: 3b24d854cb35 ("tcp/dccp: do not touch listener sk_refcnt under synflood") Signed-off-by: Eric Dumazet Reported-by: Michal Kubecek Reported-by: Firo Yang Reviewed-by: Michal Kubecek Link: https://lore.kernel.org/netdev/20191120083919.GH27852@unicorn.suse.cz/ Signed-off-by: Jakub Kicinski --- include/linux/rculist_nulls.h | 37 +++++++++++++++++++++++++++++++++++ include/net/inet_hashtables.h | 12 +++++++++--- include/net/sock.h | 5 +++++ net/ipv4/inet_diag.c | 3 ++- net/ipv4/inet_hashtables.c | 16 +++++++-------- net/ipv4/tcp_ipv4.c | 7 ++++--- 6 files changed, 65 insertions(+), 15 deletions(-) diff --git a/include/linux/rculist_nulls.h b/include/linux/rculist_nulls.h index bc8206a8f30e6..61974c4c566be 100644 --- a/include/linux/rculist_nulls.h +++ b/include/linux/rculist_nulls.h @@ -100,6 +100,43 @@ static inline void hlist_nulls_add_head_rcu(struct hlist_nulls_node *n, first->pprev = &n->next; } +/** + * hlist_nulls_add_tail_rcu + * @n: the element to add to the hash list. + * @h: the list to add to. + * + * Description: + * Adds the specified element to the specified hlist_nulls, + * while permitting racing traversals. + * + * The caller must take whatever precautions are necessary + * (such as holding appropriate locks) to avoid racing + * with another list-mutation primitive, such as hlist_nulls_add_head_rcu() + * or hlist_nulls_del_rcu(), running on this same list. + * However, it is perfectly legal to run concurrently with + * the _rcu list-traversal primitives, such as + * hlist_nulls_for_each_entry_rcu(), used to prevent memory-consistency + * problems on Alpha CPUs. Regardless of the type of CPU, the + * list-traversal primitive must be guarded by rcu_read_lock(). + */ +static inline void hlist_nulls_add_tail_rcu(struct hlist_nulls_node *n, + struct hlist_nulls_head *h) +{ + struct hlist_nulls_node *i, *last = NULL; + + /* Note: write side code, so rcu accessors are not needed. */ + for (i = h->first; !is_a_nulls(i); i = i->next) + last = i; + + if (last) { + n->next = last->next; + n->pprev = &last->next; + rcu_assign_pointer(hlist_next_rcu(last), n); + } else { + hlist_nulls_add_head_rcu(n, h); + } +} + /** * hlist_nulls_for_each_entry_rcu - iterate over rcu list of given type * @tpos: the type * to use as a loop cursor. diff --git a/include/net/inet_hashtables.h b/include/net/inet_hashtables.h index af2b4c065a042..d0019d3395cf1 100644 --- a/include/net/inet_hashtables.h +++ b/include/net/inet_hashtables.h @@ -103,13 +103,19 @@ struct inet_bind_hashbucket { struct hlist_head chain; }; -/* - * Sockets can be hashed in established or listening table +/* Sockets can be hashed in established or listening table. + * We must use different 'nulls' end-of-chain value for all hash buckets : + * A socket might transition from ESTABLISH to LISTEN state without + * RCU grace period. A lookup in ehash table needs to handle this case. */ +#define LISTENING_NULLS_BASE (1U << 29) struct inet_listen_hashbucket { spinlock_t lock; unsigned int count; - struct hlist_head head; + union { + struct hlist_head head; + struct hlist_nulls_head nulls_head; + }; }; /* This is for listening sockets, thus all sockets which possess wildcards. */ diff --git a/include/net/sock.h b/include/net/sock.h index 87d54ef57f004..04c274a20620b 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -722,6 +722,11 @@ static inline void __sk_nulls_add_node_rcu(struct sock *sk, struct hlist_nulls_h hlist_nulls_add_head_rcu(&sk->sk_nulls_node, list); } +static inline void __sk_nulls_add_node_tail_rcu(struct sock *sk, struct hlist_nulls_head *list) +{ + hlist_nulls_add_tail_rcu(&sk->sk_nulls_node, list); +} + static inline void sk_nulls_add_node_rcu(struct sock *sk, struct hlist_nulls_head *list) { sock_hold(sk); diff --git a/net/ipv4/inet_diag.c b/net/ipv4/inet_diag.c index af154977904c0..f11e997e517b6 100644 --- a/net/ipv4/inet_diag.c +++ b/net/ipv4/inet_diag.c @@ -911,11 +911,12 @@ void inet_diag_dump_icsk(struct inet_hashinfo *hashinfo, struct sk_buff *skb, for (i = s_i; i < INET_LHTABLE_SIZE; i++) { struct inet_listen_hashbucket *ilb; + struct hlist_nulls_node *node; num = 0; ilb = &hashinfo->listening_hash[i]; spin_lock(&ilb->lock); - sk_for_each(sk, &ilb->head) { + sk_nulls_for_each(sk, node, &ilb->nulls_head) { struct inet_sock *inet = inet_sk(sk); if (!net_eq(sock_net(sk), net)) diff --git a/net/ipv4/inet_hashtables.c b/net/ipv4/inet_hashtables.c index 83fb00153018f..2bbaaf0c71763 100644 --- a/net/ipv4/inet_hashtables.c +++ b/net/ipv4/inet_hashtables.c @@ -516,10 +516,11 @@ static int inet_reuseport_add_sock(struct sock *sk, struct inet_listen_hashbucket *ilb) { struct inet_bind_bucket *tb = inet_csk(sk)->icsk_bind_hash; + const struct hlist_nulls_node *node; struct sock *sk2; kuid_t uid = sock_i_uid(sk); - sk_for_each_rcu(sk2, &ilb->head) { + sk_nulls_for_each_rcu(sk2, node, &ilb->nulls_head) { if (sk2 != sk && sk2->sk_family == sk->sk_family && ipv6_only_sock(sk2) == ipv6_only_sock(sk) && @@ -555,9 +556,9 @@ int __inet_hash(struct sock *sk, struct sock *osk) } if (IS_ENABLED(CONFIG_IPV6) && sk->sk_reuseport && sk->sk_family == AF_INET6) - hlist_add_tail_rcu(&sk->sk_node, &ilb->head); + __sk_nulls_add_node_tail_rcu(sk, &ilb->nulls_head); else - hlist_add_head_rcu(&sk->sk_node, &ilb->head); + __sk_nulls_add_node_rcu(sk, &ilb->nulls_head); inet_hash2(hashinfo, sk); ilb->count++; sock_set_flag(sk, SOCK_RCU_FREE); @@ -606,11 +607,9 @@ void inet_unhash(struct sock *sk) reuseport_detach_sock(sk); if (ilb) { inet_unhash2(hashinfo, sk); - __sk_del_node_init(sk); - ilb->count--; - } else { - __sk_nulls_del_node_init_rcu(sk); + ilb->count--; } + __sk_nulls_del_node_init_rcu(sk); sock_prot_inuse_add(sock_net(sk), sk->sk_prot, -1); unlock: spin_unlock_bh(lock); @@ -750,7 +749,8 @@ void inet_hashinfo_init(struct inet_hashinfo *h) for (i = 0; i < INET_LHTABLE_SIZE; i++) { spin_lock_init(&h->listening_hash[i].lock); - INIT_HLIST_HEAD(&h->listening_hash[i].head); + INIT_HLIST_NULLS_HEAD(&h->listening_hash[i].nulls_head, + i + LISTENING_NULLS_BASE); h->listening_hash[i].count = 0; } diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 92282f98dc822..1c7326e04f9be 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -2147,13 +2147,14 @@ static void *listening_get_next(struct seq_file *seq, void *cur) struct tcp_iter_state *st = seq->private; struct net *net = seq_file_net(seq); struct inet_listen_hashbucket *ilb; + struct hlist_nulls_node *node; struct sock *sk = cur; if (!sk) { get_head: ilb = &tcp_hashinfo.listening_hash[st->bucket]; spin_lock(&ilb->lock); - sk = sk_head(&ilb->head); + sk = sk_nulls_head(&ilb->nulls_head); st->offset = 0; goto get_sk; } @@ -2161,9 +2162,9 @@ get_head: ++st->num; ++st->offset; - sk = sk_next(sk); + sk = sk_nulls_next(sk); get_sk: - sk_for_each_from(sk) { + sk_nulls_for_each_from(sk, node) { if (!net_eq(sock_net(sk), net)) continue; if (sk->sk_family == afinfo->family) -- GitLab From 5c9934b6767b16ba60be22ec3cbd4379ad64170d Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Thu, 12 Dec 2019 10:32:13 -0800 Subject: [PATCH 0630/1437] 6pack,mkiss: fix possible deadlock We got another syzbot report [1] that tells us we must use write_lock_irq()/write_unlock_irq() to avoid possible deadlock. [1] WARNING: inconsistent lock state 5.5.0-rc1-syzkaller #0 Not tainted -------------------------------- inconsistent {HARDIRQ-ON-W} -> {IN-HARDIRQ-R} usage. syz-executor826/9605 [HC1[1]:SC0[0]:HE0:SE1] takes: ffffffff8a128718 (disc_data_lock){+-..}, at: sp_get.isra.0+0x1d/0xf0 drivers/net/ppp/ppp_synctty.c:138 {HARDIRQ-ON-W} state was registered at: lock_acquire+0x190/0x410 kernel/locking/lockdep.c:4485 __raw_write_lock_bh include/linux/rwlock_api_smp.h:203 [inline] _raw_write_lock_bh+0x33/0x50 kernel/locking/spinlock.c:319 sixpack_close+0x1d/0x250 drivers/net/hamradio/6pack.c:657 tty_ldisc_close.isra.0+0x119/0x1a0 drivers/tty/tty_ldisc.c:489 tty_set_ldisc+0x230/0x6b0 drivers/tty/tty_ldisc.c:585 tiocsetd drivers/tty/tty_io.c:2337 [inline] tty_ioctl+0xe8d/0x14f0 drivers/tty/tty_io.c:2597 vfs_ioctl fs/ioctl.c:47 [inline] file_ioctl fs/ioctl.c:545 [inline] do_vfs_ioctl+0x977/0x14e0 fs/ioctl.c:732 ksys_ioctl+0xab/0xd0 fs/ioctl.c:749 __do_sys_ioctl fs/ioctl.c:756 [inline] __se_sys_ioctl fs/ioctl.c:754 [inline] __x64_sys_ioctl+0x73/0xb0 fs/ioctl.c:754 do_syscall_64+0xfa/0x790 arch/x86/entry/common.c:294 entry_SYSCALL_64_after_hwframe+0x49/0xbe irq event stamp: 3946 hardirqs last enabled at (3945): [] __raw_spin_unlock_irq include/linux/spinlock_api_smp.h:168 [inline] hardirqs last enabled at (3945): [] _raw_spin_unlock_irq+0x23/0x80 kernel/locking/spinlock.c:199 hardirqs last disabled at (3946): [] trace_hardirqs_off_thunk+0x1a/0x1c arch/x86/entry/thunk_64.S:42 softirqs last enabled at (2658): [] spin_unlock_bh include/linux/spinlock.h:383 [inline] softirqs last enabled at (2658): [] clusterip_netdev_event+0x46f/0x670 net/ipv4/netfilter/ipt_CLUSTERIP.c:222 softirqs last disabled at (2656): [] spin_lock_bh include/linux/spinlock.h:343 [inline] softirqs last disabled at (2656): [] clusterip_netdev_event+0x1bb/0x670 net/ipv4/netfilter/ipt_CLUSTERIP.c:196 other info that might help us debug this: Possible unsafe locking scenario: CPU0 ---- lock(disc_data_lock); lock(disc_data_lock); *** DEADLOCK *** 5 locks held by syz-executor826/9605: #0: ffff8880a905e198 (&tty->legacy_mutex){+.+.}, at: tty_lock+0xc7/0x130 drivers/tty/tty_mutex.c:19 #1: ffffffff899a56c0 (rcu_read_lock){....}, at: mutex_spin_on_owner+0x0/0x330 kernel/locking/mutex.c:413 #2: ffff8880a496a2b0 (&(&i->lock)->rlock){-.-.}, at: spin_lock include/linux/spinlock.h:338 [inline] #2: ffff8880a496a2b0 (&(&i->lock)->rlock){-.-.}, at: serial8250_interrupt+0x2d/0x1a0 drivers/tty/serial/8250/8250_core.c:116 #3: ffffffff8c104048 (&port_lock_key){-.-.}, at: serial8250_handle_irq.part.0+0x24/0x330 drivers/tty/serial/8250/8250_port.c:1823 #4: ffff8880a905e090 (&tty->ldisc_sem){++++}, at: tty_ldisc_ref+0x22/0x90 drivers/tty/tty_ldisc.c:288 stack backtrace: CPU: 1 PID: 9605 Comm: syz-executor826 Not tainted 5.5.0-rc1-syzkaller #0 Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 01/01/2011 Call Trace: __dump_stack lib/dump_stack.c:77 [inline] dump_stack+0x197/0x210 lib/dump_stack.c:118 print_usage_bug.cold+0x327/0x378 kernel/locking/lockdep.c:3101 valid_state kernel/locking/lockdep.c:3112 [inline] mark_lock_irq kernel/locking/lockdep.c:3309 [inline] mark_lock+0xbb4/0x1220 kernel/locking/lockdep.c:3666 mark_usage kernel/locking/lockdep.c:3554 [inline] __lock_acquire+0x1e55/0x4a00 kernel/locking/lockdep.c:3909 lock_acquire+0x190/0x410 kernel/locking/lockdep.c:4485 __raw_read_lock include/linux/rwlock_api_smp.h:149 [inline] _raw_read_lock+0x32/0x50 kernel/locking/spinlock.c:223 sp_get.isra.0+0x1d/0xf0 drivers/net/ppp/ppp_synctty.c:138 sixpack_write_wakeup+0x25/0x340 drivers/net/hamradio/6pack.c:402 tty_wakeup+0xe9/0x120 drivers/tty/tty_io.c:536 tty_port_default_wakeup+0x2b/0x40 drivers/tty/tty_port.c:50 tty_port_tty_wakeup+0x57/0x70 drivers/tty/tty_port.c:387 uart_write_wakeup+0x46/0x70 drivers/tty/serial/serial_core.c:104 serial8250_tx_chars+0x495/0xaf0 drivers/tty/serial/8250/8250_port.c:1761 serial8250_handle_irq.part.0+0x2a2/0x330 drivers/tty/serial/8250/8250_port.c:1834 serial8250_handle_irq drivers/tty/serial/8250/8250_port.c:1820 [inline] serial8250_default_handle_irq+0xc0/0x150 drivers/tty/serial/8250/8250_port.c:1850 serial8250_interrupt+0xf1/0x1a0 drivers/tty/serial/8250/8250_core.c:126 __handle_irq_event_percpu+0x15d/0x970 kernel/irq/handle.c:149 handle_irq_event_percpu+0x74/0x160 kernel/irq/handle.c:189 handle_irq_event+0xa7/0x134 kernel/irq/handle.c:206 handle_edge_irq+0x25e/0x8d0 kernel/irq/chip.c:830 generic_handle_irq_desc include/linux/irqdesc.h:156 [inline] do_IRQ+0xde/0x280 arch/x86/kernel/irq.c:250 common_interrupt+0xf/0xf arch/x86/entry/entry_64.S:607 RIP: 0010:cpu_relax arch/x86/include/asm/processor.h:685 [inline] RIP: 0010:mutex_spin_on_owner+0x247/0x330 kernel/locking/mutex.c:579 Code: c3 be 08 00 00 00 4c 89 e7 e8 e5 06 59 00 4c 89 e0 48 c1 e8 03 42 80 3c 38 00 0f 85 e1 00 00 00 49 8b 04 24 a8 01 75 96 f3 90 2f fe ff ff 0f 0b e8 0d 19 09 00 84 c0 0f 85 ff fd ff ff 48 c7 RSP: 0018:ffffc90001eafa20 EFLAGS: 00000246 ORIG_RAX: ffffffffffffffd7 RAX: 0000000000000000 RBX: ffff88809fd9e0c0 RCX: 1ffffffff13266dd RDX: 0000000000000000 RSI: 0000000000000008 RDI: 0000000000000000 RBP: ffffc90001eafa60 R08: 1ffff11013d22898 R09: ffffed1013d22899 R10: ffffed1013d22898 R11: ffff88809e9144c7 R12: ffff8880a905e138 R13: ffff88809e9144c0 R14: 0000000000000000 R15: dffffc0000000000 mutex_optimistic_spin kernel/locking/mutex.c:673 [inline] __mutex_lock_common kernel/locking/mutex.c:962 [inline] __mutex_lock+0x32b/0x13c0 kernel/locking/mutex.c:1106 mutex_lock_nested+0x16/0x20 kernel/locking/mutex.c:1121 tty_lock+0xc7/0x130 drivers/tty/tty_mutex.c:19 tty_release+0xb5/0xe90 drivers/tty/tty_io.c:1665 __fput+0x2ff/0x890 fs/file_table.c:280 ____fput+0x16/0x20 fs/file_table.c:313 task_work_run+0x145/0x1c0 kernel/task_work.c:113 exit_task_work include/linux/task_work.h:22 [inline] do_exit+0x8e7/0x2ef0 kernel/exit.c:797 do_group_exit+0x135/0x360 kernel/exit.c:895 __do_sys_exit_group kernel/exit.c:906 [inline] __se_sys_exit_group kernel/exit.c:904 [inline] __x64_sys_exit_group+0x44/0x50 kernel/exit.c:904 do_syscall_64+0xfa/0x790 arch/x86/entry/common.c:294 entry_SYSCALL_64_after_hwframe+0x49/0xbe RIP: 0033:0x43fef8 Code: Bad RIP value. RSP: 002b:00007ffdb07d2338 EFLAGS: 00000246 ORIG_RAX: 00000000000000e7 RAX: ffffffffffffffda RBX: 0000000000000000 RCX: 000000000043fef8 RDX: 0000000000000000 RSI: 000000000000003c RDI: 0000000000000000 RBP: 00000000004bf730 R08: 00000000000000e7 R09: ffffffffffffffd0 R10: 00000000004002c8 R11: 0000000000000246 R12: 0000000000000001 R13: 00000000006d1180 R14: 0000000000000000 R15: 0000000000000000 Fixes: 6e4e2f811bad ("6pack,mkiss: fix lock inconsistency") Signed-off-by: Eric Dumazet Reported-by: syzbot Cc: Arnd Bergmann Signed-off-by: Jakub Kicinski --- drivers/net/hamradio/6pack.c | 4 ++-- drivers/net/hamradio/mkiss.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/net/hamradio/6pack.c b/drivers/net/hamradio/6pack.c index 23281aeeb2226..71d6629e65c97 100644 --- a/drivers/net/hamradio/6pack.c +++ b/drivers/net/hamradio/6pack.c @@ -654,10 +654,10 @@ static void sixpack_close(struct tty_struct *tty) { struct sixpack *sp; - write_lock_bh(&disc_data_lock); + write_lock_irq(&disc_data_lock); sp = tty->disc_data; tty->disc_data = NULL; - write_unlock_bh(&disc_data_lock); + write_unlock_irq(&disc_data_lock); if (!sp) return; diff --git a/drivers/net/hamradio/mkiss.c b/drivers/net/hamradio/mkiss.c index c5bfa19ddb932..deef142151104 100644 --- a/drivers/net/hamradio/mkiss.c +++ b/drivers/net/hamradio/mkiss.c @@ -773,10 +773,10 @@ static void mkiss_close(struct tty_struct *tty) { struct mkiss *ax; - write_lock_bh(&disc_data_lock); + write_lock_irq(&disc_data_lock); ax = tty->disc_data; tty->disc_data = NULL; - write_unlock_bh(&disc_data_lock); + write_unlock_irq(&disc_data_lock); if (!ax) return; -- GitLab From 1f85e6267caca44b30c54711652b0726fadbb131 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Thu, 12 Dec 2019 12:55:29 -0800 Subject: [PATCH 0631/1437] tcp: do not send empty skb from tcp_write_xmit() Backport of commit fdfc5c8594c2 ("tcp: remove empty skb from write queue in error cases") in linux-4.14 stable triggered various bugs. One of them has been fixed in commit ba2ddb43f270 ("tcp: Don't dequeue SYN/FIN-segments from write-queue"), but we still have crashes in some occasions. Root-cause is that when tcp_sendmsg() has allocated a fresh skb and could not append a fragment before being blocked in sk_stream_wait_memory(), tcp_write_xmit() might be called and decide to send this fresh and empty skb. Sending an empty packet is not only silly, it might have caused many issues we had in the past with tp->packets_out being out of sync. Fixes: c65f7f00c587 ("[TCP]: Simplify SKB data portion allocation with NETIF_F_SG.") Signed-off-by: Eric Dumazet Cc: Christoph Paasch Acked-by: Neal Cardwell Cc: Jason Baron Acked-by: Soheil Hassas Yeganeh Signed-off-by: Jakub Kicinski --- net/ipv4/tcp_output.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index b184f03d74371..57f434a8e41ff 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -2438,6 +2438,14 @@ static bool tcp_write_xmit(struct sock *sk, unsigned int mss_now, int nonagle, if (tcp_small_queue_check(sk, skb, 0)) break; + /* Argh, we hit an empty skb(), presumably a thread + * is sleeping in sendmsg()/sk_stream_wait_memory(). + * We do not want to send a pure-ack packet and have + * a strange looking rtx queue with empty packet(s). + */ + if (TCP_SKB_CB(skb)->end_seq == TCP_SKB_CB(skb)->seq) + break; + if (unlikely(tcp_transmit_skb(sk, skb, 1, gfp))) break; -- GitLab From ee2aabd3fc2eef4c1a0ebdadccc76fbff74b94fc Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Thu, 12 Dec 2019 12:55:30 -0800 Subject: [PATCH 0632/1437] tcp: refine tcp_write_queue_empty() implementation Due to how tcp_sendmsg() is implemented, we can have an empty skb at the tail of the write queue. Most [1] tcp_write_queue_empty() callers want to know if there is anything to send (payload and/or FIN) Instead of checking if the sk_write_queue is empty, we need to test if tp->write_seq == tp->snd_nxt [1] tcp_send_fin() was the only caller that expected to see if an skb was in the write queue, I have changed the code to reuse the tcp_write_queue_tail() result. Signed-off-by: Eric Dumazet Cc: Neal Cardwell Acked-by: Soheil Hassas Yeganeh Signed-off-by: Jakub Kicinski --- include/net/tcp.h | 11 ++++++++++- net/ipv4/tcp_output.c | 5 +++-- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/include/net/tcp.h b/include/net/tcp.h index 86b9a87666488..e460ea7f767ba 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -1766,9 +1766,18 @@ static inline bool tcp_skb_is_last(const struct sock *sk, return skb_queue_is_last(&sk->sk_write_queue, skb); } +/** + * tcp_write_queue_empty - test if any payload (or FIN) is available in write queue + * @sk: socket + * + * Since the write queue can have a temporary empty skb in it, + * we must not use "return skb_queue_empty(&sk->sk_write_queue)" + */ static inline bool tcp_write_queue_empty(const struct sock *sk) { - return skb_queue_empty(&sk->sk_write_queue); + const struct tcp_sock *tp = tcp_sk(sk); + + return tp->write_seq == tp->snd_nxt; } static inline bool tcp_rtx_queue_empty(const struct sock *sk) diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index 57f434a8e41ff..36902d08473ec 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -3129,7 +3129,7 @@ void sk_forced_mem_schedule(struct sock *sk, int size) */ void tcp_send_fin(struct sock *sk) { - struct sk_buff *skb, *tskb = tcp_write_queue_tail(sk); + struct sk_buff *skb, *tskb, *tail = tcp_write_queue_tail(sk); struct tcp_sock *tp = tcp_sk(sk); /* Optimization, tack on the FIN if we have one skb in write queue and @@ -3137,6 +3137,7 @@ void tcp_send_fin(struct sock *sk) * Note: in the latter case, FIN packet will be sent after a timeout, * as TCP stack thinks it has already been transmitted. */ + tskb = tail; if (!tskb && tcp_under_memory_pressure(sk)) tskb = skb_rb_last(&sk->tcp_rtx_queue); @@ -3144,7 +3145,7 @@ void tcp_send_fin(struct sock *sk) TCP_SKB_CB(tskb)->tcp_flags |= TCPHDR_FIN; TCP_SKB_CB(tskb)->end_seq++; tp->write_seq++; - if (tcp_write_queue_empty(sk)) { + if (!tail) { /* This means tskb was already sent. * Pretend we included the FIN on previous transmit. * We need to set tp->snd_nxt to the value it would have -- GitLab From 216808c6ba6d00169fd2aa928ec3c0e63bef254f Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Thu, 12 Dec 2019 12:55:31 -0800 Subject: [PATCH 0633/1437] tcp: refine rule to allow EPOLLOUT generation under mem pressure At the time commit ce5ec440994b ("tcp: ensure epoll edge trigger wakeup when write queue is empty") was added to the kernel, we still had a single write queue, combining rtx and write queues. Once we moved the rtx queue into a separate rb-tree, testing if sk_write_queue is empty has been suboptimal. Indeed, if we have packets in the rtx queue, we probably want to delay the EPOLLOUT generation at the time incoming packets will free them, making room, but more importantly avoiding flooding application with EPOLLOUT events. Solution is to use tcp_rtx_and_write_queues_empty() helper. Fixes: 75c119afe14f ("tcp: implement rb-tree based retransmit queue") Signed-off-by: Eric Dumazet Cc: Jason Baron Cc: Neal Cardwell Acked-by: Soheil Hassas Yeganeh Signed-off-by: Jakub Kicinski --- net/ipv4/tcp.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index 8a39ee7948919..716938313a325 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -1087,8 +1087,7 @@ do_error: goto out; out_err: /* make sure we wake any epoll edge trigger waiter */ - if (unlikely(skb_queue_len(&sk->sk_write_queue) == 0 && - err == -EAGAIN)) { + if (unlikely(tcp_rtx_and_write_queues_empty(sk) && err == -EAGAIN)) { sk->sk_write_space(sk); tcp_chrono_stop(sk, TCP_CHRONO_SNDBUF_LIMITED); } @@ -1419,8 +1418,7 @@ out_err: sock_zerocopy_put_abort(uarg, true); err = sk_stream_error(sk, flags, err); /* make sure we wake any epoll edge trigger waiter */ - if (unlikely(skb_queue_len(&sk->sk_write_queue) == 0 && - err == -EAGAIN)) { + if (unlikely(tcp_rtx_and_write_queues_empty(sk) && err == -EAGAIN)) { sk->sk_write_space(sk); tcp_chrono_stop(sk, TCP_CHRONO_SNDBUF_LIMITED); } -- GitLab From eefb8c124fd969e9a174ff2bedff86aa305a7438 Mon Sep 17 00:00:00 2001 From: Dmitry Golovin Date: Thu, 5 Dec 2019 00:54:41 +0200 Subject: [PATCH 0634/1437] x86/boot: kbuild: allow readelf executable to be specified Introduce a new READELF variable to top-level Makefile, so the name of readelf binary can be specified. Before this change the name of the binary was hardcoded to "$(CROSS_COMPILE)readelf" which might not be present for every toolchain. This allows to build with LLVM Object Reader by using make parameter READELF=llvm-readelf. Link: https://github.com/ClangBuiltLinux/linux/issues/771 Signed-off-by: Dmitry Golovin Reviewed-by: Nick Desaulniers Signed-off-by: Masahiro Yamada --- Makefile | 3 ++- arch/x86/boot/compressed/Makefile | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 73e3c28029277..01072d06b7cdc 100644 --- a/Makefile +++ b/Makefile @@ -414,6 +414,7 @@ STRIP = $(CROSS_COMPILE)strip OBJCOPY = $(CROSS_COMPILE)objcopy OBJDUMP = $(CROSS_COMPILE)objdump OBJSIZE = $(CROSS_COMPILE)size +READELF = $(CROSS_COMPILE)readelf PAHOLE = pahole LEX = flex YACC = bison @@ -472,7 +473,7 @@ GCC_PLUGINS_CFLAGS := CLANG_FLAGS := export ARCH SRCARCH CONFIG_SHELL BASH HOSTCC KBUILD_HOSTCFLAGS CROSS_COMPILE AS LD CC -export CPP AR NM STRIP OBJCOPY OBJDUMP OBJSIZE PAHOLE LEX YACC AWK INSTALLKERNEL +export CPP AR NM STRIP OBJCOPY OBJDUMP OBJSIZE READELF PAHOLE LEX YACC AWK INSTALLKERNEL export PERL PYTHON PYTHON2 PYTHON3 CHECK CHECKFLAGS MAKE UTS_MACHINE HOSTCXX export KBUILD_HOSTCXXFLAGS KBUILD_HOSTLDFLAGS KBUILD_HOSTLDLIBS LDFLAGS_MODULE diff --git a/arch/x86/boot/compressed/Makefile b/arch/x86/boot/compressed/Makefile index aa976adb7094e..1dac210f7d446 100644 --- a/arch/x86/boot/compressed/Makefile +++ b/arch/x86/boot/compressed/Makefile @@ -103,7 +103,7 @@ vmlinux-objs-$(CONFIG_EFI_MIXED) += $(obj)/efi_thunk_$(BITS).o quiet_cmd_check_data_rel = DATAREL $@ define cmd_check_data_rel for obj in $(filter %.o,$^); do \ - ${CROSS_COMPILE}readelf -S $$obj | grep -qF .rel.local && { \ + $(READELF) -S $$obj | grep -qF .rel.local && { \ echo "error: $$obj has data relocations!" >&2; \ exit 1; \ } || true; \ -- GitLab From e8193650bf38bf531f19de36ae3afdee32627191 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Fri, 6 Dec 2019 22:03:01 +0900 Subject: [PATCH 0635/1437] mkcompile_h: git rid of UTS_TRUNCATE from LINUX_COMPILE_{BY,HOST} UTS_VERSION is set to struct uts_namespace, hence a too long string should be truncated so it fits in 64 characters. On the other hand, LINUX_COMPILE_BY/HOST are not set to uts_namespace. They are just used in the banners, which do not have specific length limitation. I dug into the git history, but I could not find the reason why these two strings must fit in 64 characters. Remove them. Now that UTS_VERSION is the only user of UTS_TRUNCATE, I squashed it. Signed-off-by: Masahiro Yamada --- scripts/mkcompile_h | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/scripts/mkcompile_h b/scripts/mkcompile_h index d1d757c6edf4f..3097fec1756a6 100755 --- a/scripts/mkcompile_h +++ b/scripts/mkcompile_h @@ -55,12 +55,10 @@ CONFIG_FLAGS="" if [ -n "$SMP" ] ; then CONFIG_FLAGS="SMP"; fi if [ -n "$PREEMPT" ] ; then CONFIG_FLAGS="$CONFIG_FLAGS PREEMPT"; fi if [ -n "$PREEMPT_RT" ] ; then CONFIG_FLAGS="$CONFIG_FLAGS PREEMPT_RT"; fi -UTS_VERSION="$UTS_VERSION $CONFIG_FLAGS $TIMESTAMP" # Truncate to maximum length - UTS_LEN=64 -UTS_TRUNCATE="cut -b -$UTS_LEN" +UTS_VERSION="$(echo $UTS_VERSION $CONFIG_FLAGS $TIMESTAMP | cut -b -$UTS_LEN)" # Generate a temporary compile.h @@ -69,10 +67,10 @@ UTS_TRUNCATE="cut -b -$UTS_LEN" echo \#define UTS_MACHINE \"$ARCH\" - echo \#define UTS_VERSION \"`echo $UTS_VERSION | $UTS_TRUNCATE`\" + echo \#define UTS_VERSION \"$UTS_VERSION\" - echo \#define LINUX_COMPILE_BY \"`echo $LINUX_COMPILE_BY | $UTS_TRUNCATE`\" - echo \#define LINUX_COMPILE_HOST \"`echo $LINUX_COMPILE_HOST | $UTS_TRUNCATE`\" + echo \#define LINUX_COMPILE_BY \"$LINUX_COMPILE_BY\" + echo \#define LINUX_COMPILE_HOST \"$LINUX_COMPILE_HOST\" echo \#define LINUX_COMPILER \"`$CC -v 2>&1 | grep ' version ' | sed 's/[[:space:]]*$//'`\" } > .tmpcompile -- GitLab From c8f3dea90e38194dae542c5d56e05d30447e58cb Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Fri, 6 Dec 2019 22:03:02 +0900 Subject: [PATCH 0636/1437] mkcompile_h: use printf for LINUX_COMPILE_BY Commit 858805b336be ("kbuild: add $(BASH) to run scripts with bash-extension") shed light on portability issues. Here is another one. Since commit f07726048d59 ("Fix handling of backlash character in LINUX_COMPILE_BY name"), we must escape a backslash contained in LINUX_COMPILE_BY. This is not working on such distros as Ubuntu. As the POSIX spec [1] says, if any of the operands contain a backslash ( '\' ) character, the results are implementation-defined. The actual shell of /bin/sh could be bash, dash, etc. depending on distros, and the behavior of builtin echo command is different among them. The bash builtin echo, unless -e is given, copies the arguments to stdout without expanding escape sequences (BSD-like behavior). The dash builtin echo, in contrast, adopts System V behavior, which does expand escape sequences without any option given. Even non-builtin /bin/echo behaves differently depending on the system. Due to these variations, echo is considered as a non-portable command. Using printf is the common solution to avoid the portability issue. [1] https://pubs.opengroup.org/onlinepubs/009695399/utilities/echo.html Fixes: 858805b336be ("kbuild: add $(BASH) to run scripts with bash-extension") Reported-by: XXing Wei Signed-off-by: Masahiro Yamada --- scripts/mkcompile_h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/mkcompile_h b/scripts/mkcompile_h index 3097fec1756a6..3a5a4b210c868 100755 --- a/scripts/mkcompile_h +++ b/scripts/mkcompile_h @@ -69,7 +69,7 @@ UTS_VERSION="$(echo $UTS_VERSION $CONFIG_FLAGS $TIMESTAMP | cut -b -$UTS_LEN)" echo \#define UTS_VERSION \"$UTS_VERSION\" - echo \#define LINUX_COMPILE_BY \"$LINUX_COMPILE_BY\" + printf '#define LINUX_COMPILE_BY "%s"\n' "$LINUX_COMPILE_BY" echo \#define LINUX_COMPILE_HOST \"$LINUX_COMPILE_HOST\" echo \#define LINUX_COMPILER \"`$CC -v 2>&1 | grep ' version ' | sed 's/[[:space:]]*$//'`\" -- GitLab From fd2ab2f6610b2bec70e626c38de8a4242fa88e48 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Mon, 9 Dec 2019 12:51:48 +0900 Subject: [PATCH 0637/1437] scripts/kallsyms: fix offset overflow of kallsyms_relative_base Since commit 5e5c4fa78745 ("scripts/kallsyms: shrink table before sorting it"), kallsyms_relative_base can be larger than _text, which causes overflow when building the 32-bit kernel. https://lkml.org/lkml/2019/12/7/156 This is because _text is, unless --all-symbols is specified, now trimmed from the symbol table before record_relative_base() is called. Handle the offset signedness also for kallsyms_relative_base. Introduce a new helper, output_address(), to reduce the code duplication. Fixes: 5e5c4fa78745 ("scripts/kallsyms: shrink table before sorting it") Reported-by: Olof Johansson Signed-off-by: Masahiro Yamada --- scripts/kallsyms.c | 38 ++++++++++++++++++-------------------- 1 file changed, 18 insertions(+), 20 deletions(-) diff --git a/scripts/kallsyms.c b/scripts/kallsyms.c index fb55f262f42d6..94153732ec001 100644 --- a/scripts/kallsyms.c +++ b/scripts/kallsyms.c @@ -310,6 +310,15 @@ static void output_label(const char *label) printf("%s:\n", label); } +/* Provide proper symbols relocatability by their '_text' relativeness. */ +static void output_address(unsigned long long addr) +{ + if (_text <= addr) + printf("\tPTR\t_text + %#llx\n", addr - _text); + else + printf("\tPTR\t_text - %#llx\n", _text - addr); +} + /* uncompress a compressed symbol. When this function is called, the best table * might still be compressed itself, so the function needs to be recursive */ static int expand_symbol(const unsigned char *data, int len, char *result) @@ -360,19 +369,6 @@ static void write_src(void) printf("\t.section .rodata, \"a\"\n"); - /* Provide proper symbols relocatability by their relativeness - * to a fixed anchor point in the runtime image, either '_text' - * for absolute address tables, in which case the linker will - * emit the final addresses at build time. Otherwise, use the - * offset relative to the lowest value encountered of all relative - * symbols, and emit non-relocatable fixed offsets that will be fixed - * up at runtime. - * - * The symbol names cannot be used to construct normal symbol - * references as the list of symbols contains symbols that are - * declared static and are private to their .o files. This prevents - * .tmp_kallsyms.o or any other object from referencing them. - */ if (!base_relative) output_label("kallsyms_addresses"); else @@ -380,6 +376,13 @@ static void write_src(void) for (i = 0; i < table_cnt; i++) { if (base_relative) { + /* + * Use the offset relative to the lowest value + * encountered of all relative symbols, and emit + * non-relocatable fixed offsets that will be fixed + * up at runtime. + */ + long long offset; int overflow; @@ -402,12 +405,7 @@ static void write_src(void) } printf("\t.long\t%#x\n", (int)offset); } else if (!symbol_absolute(&table[i])) { - if (_text <= table[i].addr) - printf("\tPTR\t_text + %#llx\n", - table[i].addr - _text); - else - printf("\tPTR\t_text - %#llx\n", - _text - table[i].addr); + output_address(table[i].addr); } else { printf("\tPTR\t%#llx\n", table[i].addr); } @@ -416,7 +414,7 @@ static void write_src(void) if (base_relative) { output_label("kallsyms_relative_base"); - printf("\tPTR\t_text - %#llx\n", _text - relative_base); + output_address(relative_base); printf("\n"); } -- GitLab From 16981742717b04644a41052570fb502682a315d2 Mon Sep 17 00:00:00 2001 From: Todd Kjos Date: Fri, 13 Dec 2019 12:25:31 -0800 Subject: [PATCH 0638/1437] binder: fix incorrect calculation for num_valid For BINDER_TYPE_PTR and BINDER_TYPE_FDA transactions, the num_valid local was calculated incorrectly causing the range check in binder_validate_ptr() to miss out-of-bounds offsets. Fixes: bde4a19fc04f ("binder: use userspace pointer as base of buffer space") Signed-off-by: Todd Kjos Cc: stable Link: https://lore.kernel.org/r/20191213202531.55010-1-tkjos@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/android/binder.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/android/binder.c b/drivers/android/binder.c index e9bc9fcc7ea5c..b2dad43dbf829 100644 --- a/drivers/android/binder.c +++ b/drivers/android/binder.c @@ -3310,7 +3310,7 @@ static void binder_transaction(struct binder_proc *proc, binder_size_t parent_offset; struct binder_fd_array_object *fda = to_binder_fd_array_object(hdr); - size_t num_valid = (buffer_offset - off_start_offset) * + size_t num_valid = (buffer_offset - off_start_offset) / sizeof(binder_size_t); struct binder_buffer_object *parent = binder_validate_ptr(target_proc, t->buffer, @@ -3384,7 +3384,7 @@ static void binder_transaction(struct binder_proc *proc, t->buffer->user_data + sg_buf_offset; sg_buf_offset += ALIGN(bp->length, sizeof(u64)); - num_valid = (buffer_offset - off_start_offset) * + num_valid = (buffer_offset - off_start_offset) / sizeof(binder_size_t); ret = binder_fixup_parent(t, thread, bp, off_start_offset, -- GitLab From 018e0e3594f7dcd029d258e368c485e742fa9cdb Mon Sep 17 00:00:00 2001 From: Oded Gabbay Date: Tue, 3 Dec 2019 10:12:10 +0200 Subject: [PATCH 0639/1437] habanalabs: rate limit error msg on waiting for CS In case a user submits a CS, and the submission fails, and the user doesn't check the return value and instead use the error return value as a valid sequence number of a CS and ask to wait on it, the driver will print an error and return an error code for that wait. The real problem happens if now the user ignores the error of the wait, and try to wait again and again. This can lead to a flood of error messages from the driver and even soft lockup event. Signed-off-by: Oded Gabbay Reviewed-by: Tomer Tayar --- drivers/misc/habanalabs/command_submission.c | 5 +++-- drivers/misc/habanalabs/context.c | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/misc/habanalabs/command_submission.c b/drivers/misc/habanalabs/command_submission.c index 8850f475a4136..0bf08678431b2 100644 --- a/drivers/misc/habanalabs/command_submission.c +++ b/drivers/misc/habanalabs/command_submission.c @@ -824,8 +824,9 @@ int hl_cs_wait_ioctl(struct hl_fpriv *hpriv, void *data) memset(args, 0, sizeof(*args)); if (rc < 0) { - dev_err(hdev->dev, "Error %ld on waiting for CS handle %llu\n", - rc, seq); + dev_err_ratelimited(hdev->dev, + "Error %ld on waiting for CS handle %llu\n", + rc, seq); if (rc == -ERESTARTSYS) { args->out.status = HL_WAIT_CS_STATUS_INTERRUPTED; rc = -EINTR; diff --git a/drivers/misc/habanalabs/context.c b/drivers/misc/habanalabs/context.c index 17db7b3dfb4c2..2df6fb87e7ff9 100644 --- a/drivers/misc/habanalabs/context.c +++ b/drivers/misc/habanalabs/context.c @@ -176,7 +176,7 @@ struct dma_fence *hl_ctx_get_fence(struct hl_ctx *ctx, u64 seq) spin_lock(&ctx->cs_lock); if (seq >= ctx->cs_sequence) { - dev_notice(hdev->dev, + dev_notice_ratelimited(hdev->dev, "Can't wait on seq %llu because current CS is at seq %llu\n", seq, ctx->cs_sequence); spin_unlock(&ctx->cs_lock); -- GitLab From 68a1fdf2451f38b4ada0607eb6e1303f8a02e0b7 Mon Sep 17 00:00:00 2001 From: Chen Wandun Date: Tue, 10 Dec 2019 19:06:56 +0800 Subject: [PATCH 0640/1437] habanalabs: remove variable 'val' set but not used Fixes gcc '-Wunused-but-set-variable' warning: drivers/misc/habanalabs/goya/goya.c: In function goya_pldm_init_cpu: drivers/misc/habanalabs/goya/goya.c:2195:6: warning: variable val set but not used [-Wunused-but-set-variable] drivers/misc/habanalabs/goya/goya.c: In function goya_hw_init: drivers/misc/habanalabs/goya/goya.c:2505:6: warning: variable val set but not used [-Wunused-but-set-variable] Fixes: 9494a8dd8d22 ("habanalabs: add h/w queues module") Signed-off-by: Chen Wandun Reviewed-by: Oded Gabbay Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/goya/goya.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/drivers/misc/habanalabs/goya/goya.c b/drivers/misc/habanalabs/goya/goya.c index c8d16aa4382c5..7344e8a222ae5 100644 --- a/drivers/misc/habanalabs/goya/goya.c +++ b/drivers/misc/habanalabs/goya/goya.c @@ -2192,7 +2192,7 @@ static int goya_push_linux_to_device(struct hl_device *hdev) static int goya_pldm_init_cpu(struct hl_device *hdev) { - u32 val, unit_rst_val; + u32 unit_rst_val; int rc; /* Must initialize SRAM scrambler before pushing u-boot to SRAM */ @@ -2200,14 +2200,14 @@ static int goya_pldm_init_cpu(struct hl_device *hdev) /* Put ARM cores into reset */ WREG32(mmCPU_CA53_CFG_ARM_RST_CONTROL, CPU_RESET_ASSERT); - val = RREG32(mmCPU_CA53_CFG_ARM_RST_CONTROL); + RREG32(mmCPU_CA53_CFG_ARM_RST_CONTROL); /* Reset the CA53 MACRO */ unit_rst_val = RREG32(mmPSOC_GLOBAL_CONF_UNIT_RST_N); WREG32(mmPSOC_GLOBAL_CONF_UNIT_RST_N, CA53_RESET); - val = RREG32(mmPSOC_GLOBAL_CONF_UNIT_RST_N); + RREG32(mmPSOC_GLOBAL_CONF_UNIT_RST_N); WREG32(mmPSOC_GLOBAL_CONF_UNIT_RST_N, unit_rst_val); - val = RREG32(mmPSOC_GLOBAL_CONF_UNIT_RST_N); + RREG32(mmPSOC_GLOBAL_CONF_UNIT_RST_N); rc = goya_push_uboot_to_device(hdev); if (rc) @@ -2228,7 +2228,7 @@ static int goya_pldm_init_cpu(struct hl_device *hdev) /* Release ARM core 0 from reset */ WREG32(mmCPU_CA53_CFG_ARM_RST_CONTROL, CPU_RESET_CORE0_DEASSERT); - val = RREG32(mmCPU_CA53_CFG_ARM_RST_CONTROL); + RREG32(mmCPU_CA53_CFG_ARM_RST_CONTROL); return 0; } @@ -2502,13 +2502,12 @@ err: static int goya_hw_init(struct hl_device *hdev) { struct asic_fixed_properties *prop = &hdev->asic_prop; - u32 val; int rc; dev_info(hdev->dev, "Starting initialization of H/W\n"); /* Perform read from the device to make sure device is up */ - val = RREG32(mmPCIE_DBI_DEVICE_ID_VENDOR_ID_REG); + RREG32(mmPCIE_DBI_DEVICE_ID_VENDOR_ID_REG); /* * Let's mark in the H/W that we have reached this point. We check @@ -2560,7 +2559,7 @@ static int goya_hw_init(struct hl_device *hdev) goto disable_queues; /* Perform read from the device to flush all MSI-X configuration */ - val = RREG32(mmPCIE_DBI_DEVICE_ID_VENDOR_ID_REG); + RREG32(mmPCIE_DBI_DEVICE_ID_VENDOR_ID_REG); return 0; -- GitLab From add9d56d7b3781532208afbff5509d7382fb6efe Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 11 Dec 2019 16:57:42 +0100 Subject: [PATCH 0641/1437] ALSA: pcm: Avoid possible info leaks from PCM stream buffers The current PCM code doesn't initialize explicitly the buffers allocated for PCM streams, hence it might leak some uninitialized kernel data or previous stream contents by mmapping or reading the buffer before actually starting the stream. Since this is a common problem, this patch simply adds the clearance of the buffer data at hw_params callback. Although this does only zero-clear no matter which format is used, which doesn't mean the silence for some formats, but it should be OK because the intention is just to clear the previous data on the buffer. Reported-by: Lionel Koenig Cc: Link: https://lore.kernel.org/r/20191211155742.3213-1-tiwai@suse.de Signed-off-by: Takashi Iwai --- sound/core/pcm_native.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index 1fe581167b7b9..d083225344a06 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -739,6 +739,10 @@ static int snd_pcm_hw_params(struct snd_pcm_substream *substream, while (runtime->boundary * 2 <= LONG_MAX - runtime->buffer_size) runtime->boundary *= 2; + /* clear the buffer for avoiding possible kernel info leaks */ + if (runtime->dma_area && !substream->ops->copy_user) + memset(runtime->dma_area, 0, runtime->dma_bytes); + snd_pcm_timer_resolution_change(substream); snd_pcm_set_state(substream, SNDRV_PCM_STATE_SETUP); -- GitLab From 377bc0cfabce0244632dada19060839ced4e6949 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 13 Dec 2019 09:51:09 +0100 Subject: [PATCH 0642/1437] ALSA: hda/ca0132 - Keep power on during processing DSP response We need to keep power on while processing the DSP response via unsol event. Each snd_hda_codec_read() call does the power management, so it should work normally, but still it's safer to keep the power up for the whole function. Fixes: a73d511c4867 ("ALSA: hda/ca0132: Add unsol handler for DSP and jack detection") Cc: Link: https://lore.kernel.org/r/20191213085111.22855-2-tiwai@suse.de Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_ca0132.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c index b7a1abb3e2318..c3d34ff3d9ec5 100644 --- a/sound/pci/hda/patch_ca0132.c +++ b/sound/pci/hda/patch_ca0132.c @@ -7588,12 +7588,14 @@ static void ca0132_process_dsp_response(struct hda_codec *codec, struct ca0132_spec *spec = codec->spec; codec_dbg(codec, "ca0132_process_dsp_response\n"); + snd_hda_power_up_pm(codec); if (spec->wait_scp) { if (dspio_get_response_data(codec) >= 0) spec->wait_scp = 0; } dspio_clear_response_queue(codec); + snd_hda_power_down_pm(codec); } static void hp_callback(struct hda_codec *codec, struct hda_jack_callback *cb) -- GitLab From cb04fc3b6b076f67d228a0b7d096c69ad486c09c Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 13 Dec 2019 09:51:10 +0100 Subject: [PATCH 0643/1437] ALSA: hda/ca0132 - Avoid endless loop Introduce a timeout to dspio_clear_response_queue() so that it won't be caught in an endless loop even if the hardware doesn't respond properly. Fixes: a73d511c4867 ("ALSA: hda/ca0132: Add unsol handler for DSP and jack detection") Cc: Link: https://lore.kernel.org/r/20191213085111.22855-3-tiwai@suse.de Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_ca0132.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c index c3d34ff3d9ec5..8d0209fff8f53 100644 --- a/sound/pci/hda/patch_ca0132.c +++ b/sound/pci/hda/patch_ca0132.c @@ -1809,13 +1809,14 @@ struct scp_msg { static void dspio_clear_response_queue(struct hda_codec *codec) { + unsigned long timeout = jiffies + msecs_to_jiffies(1000); unsigned int dummy = 0; - int status = -1; + int status; /* clear all from the response queue */ do { status = dspio_read(codec, &dummy); - } while (status == 0); + } while (status == 0 && time_before(jiffies, timeout)); } static int dspio_get_response_data(struct hda_codec *codec) -- GitLab From 42fb6b1d41eb5905d77c06cad2e87b70289bdb76 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 13 Dec 2019 09:51:11 +0100 Subject: [PATCH 0644/1437] ALSA: hda/ca0132 - Fix work handling in delayed HP detection CA0132 has the delayed HP jack detection code that is invoked from the unsol handler, but it does a few weird things: it contains the cancel of a work inside the work handler, and yet it misses the cancel-sync call at (runtime-)suspend. This patch addresses those issues. Fixes: 15c2b3cc09a3 ("ALSA: hda/ca0132 - Fix possible workqueue stall") Cc: Link: https://lore.kernel.org/r/20191213085111.22855-4-tiwai@suse.de Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_ca0132.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c index 8d0209fff8f53..32ed46464af7a 100644 --- a/sound/pci/hda/patch_ca0132.c +++ b/sound/pci/hda/patch_ca0132.c @@ -7607,11 +7607,10 @@ static void hp_callback(struct hda_codec *codec, struct hda_jack_callback *cb) /* Delay enabling the HP amp, to let the mic-detection * state machine run. */ - cancel_delayed_work(&spec->unsol_hp_work); - schedule_delayed_work(&spec->unsol_hp_work, msecs_to_jiffies(500)); tbl = snd_hda_jack_tbl_get(codec, cb->nid); if (tbl) tbl->block_report = 1; + schedule_delayed_work(&spec->unsol_hp_work, msecs_to_jiffies(500)); } static void amic_callback(struct hda_codec *codec, struct hda_jack_callback *cb) @@ -8457,12 +8456,25 @@ static void ca0132_reboot_notify(struct hda_codec *codec) codec->patch_ops.free(codec); } +#ifdef CONFIG_PM +static int ca0132_suspend(struct hda_codec *codec) +{ + struct ca0132_spec *spec = codec->spec; + + cancel_delayed_work_sync(&spec->unsol_hp_work); + return 0; +} +#endif + static const struct hda_codec_ops ca0132_patch_ops = { .build_controls = ca0132_build_controls, .build_pcms = ca0132_build_pcms, .init = ca0132_init, .free = ca0132_free, .unsol_event = snd_hda_jack_unsol_event, +#ifdef CONFIG_PM + .suspend = ca0132_suspend, +#endif .reboot_notify = ca0132_reboot_notify, }; -- GitLab From 6fd739c04ffd877641b01371f9fde67901e7f9cb Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Sat, 14 Dec 2019 18:52:17 +0100 Subject: [PATCH 0645/1437] ALSA: hda: Fix regression by strip mask fix The commit e38e486d66e2 ("ALSA: hda: Modify stream stripe mask only when needed") tried to address the regression by the unconditional application of the stripe mask, but this caused yet another regression for the previously working devices. Namely, the patch clears the azx_dev->stripe flag at snd_hdac_stream_clear(), but this may be called multiple times before restarting the stream, so this ended up with clearance of the flag for the whole time. This patch fixes the regression by moving the azx_dev->stripe flag clearance at the counter-part, the close callback of HDMI codec driver instead. Fixes: e38e486d66e2 ("ALSA: hda: Modify stream stripe mask only when needed") BugLink: https://bugzilla.kernel.org/show_bug.cgi?id=205855 BugLink: https://bugzilla.kernel.org/show_bug.cgi?id=204477 Cc: Link: https://lore.kernel.org/r/20191214175217.31852-1-tiwai@suse.de Signed-off-by: Takashi Iwai --- sound/hda/hdac_stream.c | 4 +--- sound/pci/hda/patch_hdmi.c | 2 ++ 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/sound/hda/hdac_stream.c b/sound/hda/hdac_stream.c index f9707fb05efe6..682ed39f79b01 100644 --- a/sound/hda/hdac_stream.c +++ b/sound/hda/hdac_stream.c @@ -120,10 +120,8 @@ void snd_hdac_stream_clear(struct hdac_stream *azx_dev) snd_hdac_stream_updateb(azx_dev, SD_CTL, SD_CTL_DMA_START | SD_INT_MASK, 0); snd_hdac_stream_writeb(azx_dev, SD_STS, SD_INT_MASK); /* to be sure */ - if (azx_dev->stripe) { + if (azx_dev->stripe) snd_hdac_stream_updateb(azx_dev, SD_CTL_3B, SD_CTL_STRIPE_MASK, 0); - azx_dev->stripe = 0; - } azx_dev->running = false; } EXPORT_SYMBOL_GPL(snd_hdac_stream_clear); diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index 78647ee02339f..630b1f5c276d4 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -2021,6 +2021,8 @@ static int hdmi_pcm_close(struct hda_pcm_stream *hinfo, per_cvt->assigned = 0; hinfo->nid = 0; + azx_stream(get_azx_dev(substream))->stripe = 0; + mutex_lock(&spec->pcm_lock); snd_hda_spdif_ctls_unassign(codec, pcm_idx); clear_bit(pcm_idx, &spec->pcm_in_use); -- GitLab From f3f2364ea14d1cf6bf966542f31eadcf178f1577 Mon Sep 17 00:00:00 2001 From: Russell King Date: Tue, 10 Dec 2019 22:33:05 +0000 Subject: [PATCH 0646/1437] net: marvell: mvpp2: phylink requires the link interrupt phylink requires the MAC to report when its link status changes when operating in inband modes. Failure to report link status changes means that phylink has no idea when the link events happen, which results in either the network interface's carrier remaining up or remaining permanently down. For example, with a fiber module, if the interface is brought up and link is initially established, taking the link down at the far end will cut the optical power. The SFP module's LOS asserts, we deactivate the link, and the network interface reports no carrier. When the far end is brought back up, the SFP module's LOS deasserts, but the MAC may be slower to establish link. If this happens (which in my tests is a certainty) then phylink never hears that the MAC has established link with the far end, and the network interface is stuck reporting no carrier. This means the interface is non-functional. Avoiding the link interrupt when we have phylink is basically not an option, so remove the !port->phylink from the test. Fixes: 4bb043262878 ("net: mvpp2: phylink support") Tested-by: Sven Auhagen Tested-by: Antoine Tenart Signed-off-by: Russell King Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c b/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c index 62dc2f362a169..14e372cda7f41 100644 --- a/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c +++ b/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c @@ -3680,7 +3680,7 @@ static int mvpp2_open(struct net_device *dev) valid = true; } - if (priv->hw_version == MVPP22 && port->link_irq && !port->phylink) { + if (priv->hw_version == MVPP22 && port->link_irq) { err = request_irq(port->link_irq, mvpp2_link_status_isr, 0, dev->name, port); if (err) { -- GitLab From c39ea5cba5a2e97fc01b78c85208bf31383b399c Mon Sep 17 00:00:00 2001 From: Haiyang Zhang Date: Wed, 11 Dec 2019 14:26:27 -0800 Subject: [PATCH 0647/1437] hv_netvsc: Fix tx_table init in rndis_set_subchannel() Host can provide send indirection table messages anytime after RSS is enabled by calling rndis_filter_set_rss_param(). So the host provided table values may be overwritten by the initialization in rndis_set_subchannel(). To prevent this problem, move the tx_table initialization before calling rndis_filter_set_rss_param(). Fixes: a6fb6aa3cfa9 ("hv_netvsc: Set tx_table to equal weight after subchannels open") Signed-off-by: Haiyang Zhang Signed-off-by: Jakub Kicinski --- drivers/net/hyperv/rndis_filter.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/net/hyperv/rndis_filter.c b/drivers/net/hyperv/rndis_filter.c index 206b4e77eaf03..05bc5ec8bb1f0 100644 --- a/drivers/net/hyperv/rndis_filter.c +++ b/drivers/net/hyperv/rndis_filter.c @@ -1171,6 +1171,9 @@ int rndis_set_subchannel(struct net_device *ndev, wait_event(nvdev->subchan_open, atomic_read(&nvdev->open_chn) == nvdev->num_chn); + for (i = 0; i < VRSS_SEND_TAB_SIZE; i++) + ndev_ctx->tx_table[i] = i % nvdev->num_chn; + /* ignore failures from setting rss parameters, still have channels */ if (dev_info) rndis_filter_set_rss_param(rdev, dev_info->rss_key); @@ -1180,9 +1183,6 @@ int rndis_set_subchannel(struct net_device *ndev, netif_set_real_num_tx_queues(ndev, nvdev->num_chn); netif_set_real_num_rx_queues(ndev, nvdev->num_chn); - for (i = 0; i < VRSS_SEND_TAB_SIZE; i++) - ndev_ctx->tx_table[i] = i % nvdev->num_chn; - return 0; } -- GitLab From 99e9fe2248f9bd86dd10e773c90c9b54055b1d92 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Wed, 11 Dec 2019 13:56:09 +0100 Subject: [PATCH 0648/1437] net: ethernet: ti: select PAGE_POOL for switchdev driver The new driver misses a dependency: drivers/net/ethernet/ti/cpsw_new.o: In function `cpsw_rx_handler': cpsw_new.c:(.text+0x259c): undefined reference to `__page_pool_put_page' cpsw_new.c:(.text+0x25d0): undefined reference to `page_pool_alloc_pages' drivers/net/ethernet/ti/cpsw_priv.o: In function `cpsw_fill_rx_channels': cpsw_priv.c:(.text+0x22d8): undefined reference to `page_pool_alloc_pages' cpsw_priv.c:(.text+0x2420): undefined reference to `__page_pool_put_page' drivers/net/ethernet/ti/cpsw_priv.o: In function `cpsw_create_xdp_rxqs': cpsw_priv.c:(.text+0x2624): undefined reference to `page_pool_create' drivers/net/ethernet/ti/cpsw_priv.o: In function `cpsw_run_xdp': cpsw_priv.c:(.text+0x2dc8): undefined reference to `__page_pool_put_page' Other drivers use 'select' for PAGE_POOL, so do the same here. Fixes: ed3525eda4c4 ("net: ethernet: ti: introduce cpsw switchdev based driver part 1 - dual-emac") Signed-off-by: Arnd Bergmann Acked-by: Ilias Apalodimas Acked-by: Jesper Dangaard Brouer Reviewed-by: Grygorii Strashko Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/ti/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/ethernet/ti/Kconfig b/drivers/net/ethernet/ti/Kconfig index a46f4189fde37..bf98e0fa7d8be 100644 --- a/drivers/net/ethernet/ti/Kconfig +++ b/drivers/net/ethernet/ti/Kconfig @@ -63,6 +63,7 @@ config TI_CPSW_SWITCHDEV tristate "TI CPSW Switch Support with switchdev" depends on ARCH_DAVINCI || ARCH_OMAP2PLUS || COMPILE_TEST depends on NET_SWITCHDEV + select PAGE_POOL select TI_DAVINCI_MDIO select MFD_SYSCON select REGMAP -- GitLab From ed56dd8f60cf3d56ca1416288b09eec27bdd5413 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Wed, 11 Dec 2019 13:56:10 +0100 Subject: [PATCH 0649/1437] net: ethernet: ti: build cpsw-common for switchdev Without the common part of the driver, the new file fails to link: drivers/net/ethernet/ti/cpsw_new.o: In function `cpsw_probe': cpsw_new.c:(.text+0x312c): undefined reference to `ti_cm_get_macid' Use the same Makefile hack as before, and build cpsw-common.o for any driver that needs it. Fixes: ed3525eda4c4 ("net: ethernet: ti: introduce cpsw switchdev based driver part 1 - dual-emac") Signed-off-by: Arnd Bergmann Reviewed-by: Grygorii Strashko Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/ti/Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/ethernet/ti/Makefile b/drivers/net/ethernet/ti/Makefile index d34df8e5cf941..ecf776ad86895 100644 --- a/drivers/net/ethernet/ti/Makefile +++ b/drivers/net/ethernet/ti/Makefile @@ -5,6 +5,7 @@ obj-$(CONFIG_TI_CPSW) += cpsw-common.o obj-$(CONFIG_TI_DAVINCI_EMAC) += cpsw-common.o +obj-$(CONFIG_TI_CPSW_SWITCHDEV) += cpsw-common.o obj-$(CONFIG_TLAN) += tlan.o obj-$(CONFIG_CPMAC) += cpmac.o -- GitLab From 7113f796bbbced2470cd6d7379d50d7a7a78bf34 Mon Sep 17 00:00:00 2001 From: Manish Chopra Date: Wed, 11 Dec 2019 09:59:55 -0800 Subject: [PATCH 0650/1437] bnx2x: Do not handle requests from VFs after parity Parity error from the hardware will cause PF to lose the state of their VFs due to PF's internal reload and hardware reset following the parity error. Restrict any configuration request from the VFs after the parity as it could cause unexpected hardware behavior, only way for VFs to recover would be to trigger FLR on VFs and reload them. Signed-off-by: Manish Chopra Signed-off-by: Ariel Elior Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c | 12 ++++++++++-- drivers/net/ethernet/broadcom/bnx2x/bnx2x_sriov.h | 1 + drivers/net/ethernet/broadcom/bnx2x/bnx2x_vfpf.c | 12 ++++++++++++ 3 files changed, 23 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c index 192ff8d5da324..cff64e43bdd80 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c @@ -9976,10 +9976,18 @@ static void bnx2x_recovery_failed(struct bnx2x *bp) */ static void bnx2x_parity_recover(struct bnx2x *bp) { - bool global = false; u32 error_recovered, error_unrecovered; - bool is_parity; + bool is_parity, global = false; +#ifdef CONFIG_BNX2X_SRIOV + int vf_idx; + + for (vf_idx = 0; vf_idx < bp->requested_nr_virtfn; vf_idx++) { + struct bnx2x_virtf *vf = BP_VF(bp, vf_idx); + if (vf) + vf->state = VF_LOST; + } +#endif DP(NETIF_MSG_HW, "Handling parity\n"); while (1) { switch (bp->recovery_state) { diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sriov.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sriov.h index b6ebd92ec565f..3a716c0154159 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sriov.h +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sriov.h @@ -139,6 +139,7 @@ struct bnx2x_virtf { #define VF_ACQUIRED 1 /* VF acquired, but not initialized */ #define VF_ENABLED 2 /* VF Enabled */ #define VF_RESET 3 /* VF FLR'd, pending cleanup */ +#define VF_LOST 4 /* Recovery while VFs are loaded */ bool flr_clnup_stage; /* true during flr cleanup */ bool malicious; /* true if FW indicated so, until FLR */ diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_vfpf.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_vfpf.c index 0752b7fa4d9c0..ea0e9394f8986 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_vfpf.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_vfpf.c @@ -2107,6 +2107,18 @@ static void bnx2x_vf_mbx_request(struct bnx2x *bp, struct bnx2x_virtf *vf, { int i; + if (vf->state == VF_LOST) { + /* Just ack the FW and return if VFs are lost + * in case of parity error. VFs are supposed to be timedout + * on waiting for PF response. + */ + DP(BNX2X_MSG_IOV, + "VF 0x%x lost, not handling the request\n", vf->abs_vfid); + + storm_memset_vf_mbx_ack(bp, vf->abs_vfid); + return; + } + /* check if tlv type is known */ if (bnx2x_tlv_supported(mbx->first_tlv.tl.type)) { /* Lock the per vf op mutex and note the locker's identity. -- GitLab From ee699f89bdbaa19c399804504241b5c531b48888 Mon Sep 17 00:00:00 2001 From: Manish Chopra Date: Wed, 11 Dec 2019 09:59:56 -0800 Subject: [PATCH 0651/1437] bnx2x: Fix logic to get total no. of PFs per engine Driver doesn't calculate total number of PFs configured on a given engine correctly which messed up resources in the PFs loaded on that engine, leading driver to exceed configuration of resources (like vlan filters etc.) beyond the limit per engine, which ended up with asserts from the firmware. Signed-off-by: Manish Chopra Signed-off-by: Ariel Elior Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.h index 8b08cb18e3638..3f63ffd7561bf 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.h +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.h @@ -1109,7 +1109,7 @@ static inline u8 bnx2x_get_path_func_num(struct bnx2x *bp) for (i = 0; i < E1H_FUNC_MAX / 2; i++) { u32 func_config = MF_CFG_RD(bp, - func_mf_config[BP_PORT(bp) + 2 * i]. + func_mf_config[BP_PATH(bp) + 2 * i]. config); func_num += ((func_config & FUNC_MF_CFG_FUNC_HIDE) ? 0 : 1); -- GitLab From 826f66b30c2e3d4df533e4224cb8c734afa0a17c Mon Sep 17 00:00:00 2001 From: Andy Roulin Date: Wed, 11 Dec 2019 14:30:58 -0800 Subject: [PATCH 0652/1437] bonding: move 802.3ad port state flags to uapi The bond slave actor/partner operating state is exported as bitfield to userspace, which lacks a way to interpret it, e.g., iproute2 only prints the state as a number: ad_actor_oper_port_state 15 For userspace to interpret the bitfield, the bitfield definitions should be part of the uapi. The bitfield itself is defined in the 802.3ad standard. This commit moves the 802.3ad bitfield definitions to uapi. Related iproute2 patches, soon to be posted upstream, use the new uapi headers to pretty-print bond slave state, e.g., with ip -d link show ad_actor_oper_port_state_str Signed-off-by: Andy Roulin Acked-by: Roopa Prabhu Acked-by: Jay Vosburgh Signed-off-by: Jakub Kicinski --- drivers/net/bonding/bond_3ad.c | 10 ---------- include/uapi/linux/if_bonding.h | 10 ++++++++++ 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/drivers/net/bonding/bond_3ad.c b/drivers/net/bonding/bond_3ad.c index e3b25f3109367..34bfe99641a3c 100644 --- a/drivers/net/bonding/bond_3ad.c +++ b/drivers/net/bonding/bond_3ad.c @@ -31,16 +31,6 @@ #define AD_CHURN_DETECTION_TIME 60 #define AD_AGGREGATE_WAIT_TIME 2 -/* Port state definitions (43.4.2.2 in the 802.3ad standard) */ -#define AD_STATE_LACP_ACTIVITY 0x1 -#define AD_STATE_LACP_TIMEOUT 0x2 -#define AD_STATE_AGGREGATION 0x4 -#define AD_STATE_SYNCHRONIZATION 0x8 -#define AD_STATE_COLLECTING 0x10 -#define AD_STATE_DISTRIBUTING 0x20 -#define AD_STATE_DEFAULTED 0x40 -#define AD_STATE_EXPIRED 0x80 - /* Port Variables definitions used by the State Machines (43.4.7 in the * 802.3ad standard) */ diff --git a/include/uapi/linux/if_bonding.h b/include/uapi/linux/if_bonding.h index 790585f0e61b5..6829213a54c5c 100644 --- a/include/uapi/linux/if_bonding.h +++ b/include/uapi/linux/if_bonding.h @@ -95,6 +95,16 @@ #define BOND_XMIT_POLICY_ENCAP23 3 /* encapsulated layer 2+3 */ #define BOND_XMIT_POLICY_ENCAP34 4 /* encapsulated layer 3+4 */ +/* 802.3ad port state definitions (43.4.2.2 in the 802.3ad standard) */ +#define AD_STATE_LACP_ACTIVITY 0x1 +#define AD_STATE_LACP_TIMEOUT 0x2 +#define AD_STATE_AGGREGATION 0x4 +#define AD_STATE_SYNCHRONIZATION 0x8 +#define AD_STATE_COLLECTING 0x10 +#define AD_STATE_DISTRIBUTING 0x20 +#define AD_STATE_DEFAULTED 0x40 +#define AD_STATE_EXPIRED 0x80 + typedef struct ifbond { __s32 bond_mode; __s32 num_slaves; -- GitLab From 64d4ce892383b2ad6d782e080d25502f91bf2a38 Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Mon, 2 Dec 2019 18:02:12 +0100 Subject: [PATCH 0653/1437] ext4: fix ext4_empty_dir() for directories with holes Function ext4_empty_dir() doesn't correctly handle directories with holes and crashes on bh->b_data dereference when bh is NULL. Reorganize the loop to use 'offset' variable all the times instead of comparing pointers to current direntry with bh->b_data pointer. Also add more strict checking of '.' and '..' directory entries to avoid entering loop in possibly invalid state on corrupted filesystems. References: CVE-2019-19037 CC: stable@vger.kernel.org Fixes: 4e19d6b65fb4 ("ext4: allow directory holes") Signed-off-by: Jan Kara Link: https://lore.kernel.org/r/20191202170213.4761-2-jack@suse.cz Signed-off-by: Theodore Ts'o --- fs/ext4/namei.c | 32 ++++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c index a856997d87b54..9e6d14748b5a5 100644 --- a/fs/ext4/namei.c +++ b/fs/ext4/namei.c @@ -2822,7 +2822,7 @@ bool ext4_empty_dir(struct inode *inode) { unsigned int offset; struct buffer_head *bh; - struct ext4_dir_entry_2 *de, *de1; + struct ext4_dir_entry_2 *de; struct super_block *sb; if (ext4_has_inline_data(inode)) { @@ -2847,19 +2847,25 @@ bool ext4_empty_dir(struct inode *inode) return true; de = (struct ext4_dir_entry_2 *) bh->b_data; - de1 = ext4_next_entry(de, sb->s_blocksize); - if (le32_to_cpu(de->inode) != inode->i_ino || - le32_to_cpu(de1->inode) == 0 || - strcmp(".", de->name) || strcmp("..", de1->name)) { - ext4_warning_inode(inode, "directory missing '.' and/or '..'"); + if (ext4_check_dir_entry(inode, NULL, de, bh, bh->b_data, bh->b_size, + 0) || + le32_to_cpu(de->inode) != inode->i_ino || strcmp(".", de->name)) { + ext4_warning_inode(inode, "directory missing '.'"); + brelse(bh); + return true; + } + offset = ext4_rec_len_from_disk(de->rec_len, sb->s_blocksize); + de = ext4_next_entry(de, sb->s_blocksize); + if (ext4_check_dir_entry(inode, NULL, de, bh, bh->b_data, bh->b_size, + offset) || + le32_to_cpu(de->inode) == 0 || strcmp("..", de->name)) { + ext4_warning_inode(inode, "directory missing '..'"); brelse(bh); return true; } - offset = ext4_rec_len_from_disk(de->rec_len, sb->s_blocksize) + - ext4_rec_len_from_disk(de1->rec_len, sb->s_blocksize); - de = ext4_next_entry(de1, sb->s_blocksize); + offset += ext4_rec_len_from_disk(de->rec_len, sb->s_blocksize); while (offset < inode->i_size) { - if ((void *) de >= (void *) (bh->b_data+sb->s_blocksize)) { + if (!(offset & (sb->s_blocksize - 1))) { unsigned int lblock; brelse(bh); lblock = offset >> EXT4_BLOCK_SIZE_BITS(sb); @@ -2870,12 +2876,11 @@ bool ext4_empty_dir(struct inode *inode) } if (IS_ERR(bh)) return true; - de = (struct ext4_dir_entry_2 *) bh->b_data; } + de = (struct ext4_dir_entry_2 *) (bh->b_data + + (offset & (sb->s_blocksize - 1))); if (ext4_check_dir_entry(inode, NULL, de, bh, bh->b_data, bh->b_size, offset)) { - de = (struct ext4_dir_entry_2 *)(bh->b_data + - sb->s_blocksize); offset = (offset | (sb->s_blocksize - 1)) + 1; continue; } @@ -2884,7 +2889,6 @@ bool ext4_empty_dir(struct inode *inode) return false; } offset += ext4_rec_len_from_disk(de->rec_len, sb->s_blocksize); - de = ext4_next_entry(de, sb->s_blocksize); } brelse(bh); return true; -- GitLab From 109ba779d6cca2d519c5dd624a3276d03e21948e Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Mon, 2 Dec 2019 18:02:13 +0100 Subject: [PATCH 0654/1437] ext4: check for directory entries too close to block end ext4_check_dir_entry() currently does not catch a case when a directory entry ends so close to the block end that the header of the next directory entry would not fit in the remaining space. This can lead to directory iteration code trying to access address beyond end of current buffer head leading to oops. CC: stable@vger.kernel.org Signed-off-by: Jan Kara Link: https://lore.kernel.org/r/20191202170213.4761-3-jack@suse.cz Signed-off-by: Theodore Ts'o --- fs/ext4/dir.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/fs/ext4/dir.c b/fs/ext4/dir.c index 9fdd2b269d617..6305d5ec25af9 100644 --- a/fs/ext4/dir.c +++ b/fs/ext4/dir.c @@ -81,6 +81,11 @@ int __ext4_check_dir_entry(const char *function, unsigned int line, error_msg = "rec_len is too small for name_len"; else if (unlikely(((char *) de - buf) + rlen > size)) error_msg = "directory entry overrun"; + else if (unlikely(((char *) de - buf) + rlen > + size - EXT4_DIR_REC_LEN(1) && + ((char *) de - buf) + rlen != size)) { + error_msg = "directory entry too close to block end"; + } else if (unlikely(le32_to_cpu(de->inode) > le32_to_cpu(EXT4_SB(dir->i_sb)->s_es->s_inodes_count))) error_msg = "inode out of bounds"; -- GitLab From 707d1a2f601bea6110a5633054253c0cb71b44c1 Mon Sep 17 00:00:00 2001 From: Theodore Ts'o Date: Sun, 8 Dec 2019 19:43:46 -0500 Subject: [PATCH 0655/1437] ext4: optimize __ext4_check_dir_entry() Make __ext4_check_dir_entry() a bit easier to understand, and reduce the object size of the function by over 11%. Signed-off-by: Theodore Ts'o Link: https://lore.kernel.org/r/20191209004346.38526-1-tytso@mit.edu Signed-off-by: Theodore Ts'o --- fs/ext4/dir.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/fs/ext4/dir.c b/fs/ext4/dir.c index 6305d5ec25af9..9f00fc0bf21d2 100644 --- a/fs/ext4/dir.c +++ b/fs/ext4/dir.c @@ -72,6 +72,7 @@ int __ext4_check_dir_entry(const char *function, unsigned int line, const char *error_msg = NULL; const int rlen = ext4_rec_len_from_disk(de->rec_len, dir->i_sb->s_blocksize); + const int next_offset = ((char *) de - buf) + rlen; if (unlikely(rlen < EXT4_DIR_REC_LEN(1))) error_msg = "rec_len is smaller than minimal"; @@ -79,13 +80,11 @@ int __ext4_check_dir_entry(const char *function, unsigned int line, error_msg = "rec_len % 4 != 0"; else if (unlikely(rlen < EXT4_DIR_REC_LEN(de->name_len))) error_msg = "rec_len is too small for name_len"; - else if (unlikely(((char *) de - buf) + rlen > size)) + else if (unlikely(next_offset > size)) error_msg = "directory entry overrun"; - else if (unlikely(((char *) de - buf) + rlen > - size - EXT4_DIR_REC_LEN(1) && - ((char *) de - buf) + rlen != size)) { + else if (unlikely(next_offset > size - EXT4_DIR_REC_LEN(1) && + next_offset != size)) error_msg = "directory entry too close to block end"; - } else if (unlikely(le32_to_cpu(de->inode) > le32_to_cpu(EXT4_SB(dir->i_sb)->s_es->s_inodes_count))) error_msg = "inode out of bounds"; -- GitLab From 7f420d64a08c1dcd65b27be82a27cf2bdb2e7847 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Fri, 13 Dec 2019 21:50:11 +0300 Subject: [PATCH 0656/1437] ext4: unlock on error in ext4_expand_extra_isize() We need to unlock the xattr before returning on this error path. Cc: stable@kernel.org # 4.13 Fixes: c03b45b853f5 ("ext4, project: expand inode extra size if possible") Signed-off-by: Dan Carpenter Link: https://lore.kernel.org/r/20191213185010.6k7yl2tck3wlsdkt@kili.mountain Signed-off-by: Theodore Ts'o --- fs/ext4/inode.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index 310e4abd9acae..16e72621e9952 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -5689,7 +5689,7 @@ int ext4_expand_extra_isize(struct inode *inode, error = ext4_journal_get_write_access(handle, iloc->bh); if (error) { brelse(iloc->bh); - goto out_stop; + goto out_unlock; } error = __ext4_expand_extra_isize(inode, new_extra_isize, iloc, @@ -5699,8 +5699,8 @@ int ext4_expand_extra_isize(struct inode *inode, if (!error) error = rc; +out_unlock: ext4_write_unlock_xattr(inode, &no_expand); -out_stop: ext4_journal_stop(handle); return error; } -- GitLab From a70fd5ac2ea787cafe07b69dadd16b3648ad64ac Mon Sep 17 00:00:00 2001 From: yangerkun Date: Fri, 13 Dec 2019 09:49:00 +0800 Subject: [PATCH 0657/1437] ext4: reserve revoke credits in __ext4_new_inode It's possible that __ext4_new_inode will release the xattr block, so it will trigger a warning since there is revoke credits will be 0 if the handle == NULL. The below scripts can reproduce it easily. ------------[ cut here ]------------ WARNING: CPU: 0 PID: 3861 at fs/jbd2/revoke.c:374 jbd2_journal_revoke+0x30e/0x540 fs/jbd2/revoke.c:374 ... __ext4_forget+0x1d7/0x800 fs/ext4/ext4_jbd2.c:248 ext4_free_blocks+0x213/0x1d60 fs/ext4/mballoc.c:4743 ext4_xattr_release_block+0x55b/0x780 fs/ext4/xattr.c:1254 ext4_xattr_block_set+0x1c2c/0x2c40 fs/ext4/xattr.c:2112 ext4_xattr_set_handle+0xa7e/0x1090 fs/ext4/xattr.c:2384 __ext4_set_acl+0x54d/0x6c0 fs/ext4/acl.c:214 ext4_init_acl+0x218/0x2e0 fs/ext4/acl.c:293 __ext4_new_inode+0x352a/0x42b0 fs/ext4/ialloc.c:1151 ext4_mkdir+0x2e9/0xbd0 fs/ext4/namei.c:2774 vfs_mkdir+0x386/0x5f0 fs/namei.c:3811 do_mkdirat+0x11c/0x210 fs/namei.c:3834 do_syscall_64+0xa1/0x530 arch/x86/entry/common.c:294 ... ------------------------------------- scripts: mkfs.ext4 /dev/vdb mount /dev/vdb /mnt cd /mnt && mkdir dir && for i in {1..8}; do setfacl -dm "u:user_"$i":rx" dir; done mkdir dir/dir1 && mv dir/dir1 ./ sh repro.sh && add some user [root@localhost ~]# cat repro.sh while [ 1 -eq 1 ]; do rm -rf dir rm -rf dir1/dir1 mkdir dir for i in {1..8}; do setfacl -dm "u:test"$i":rx" dir; done setfacl -m "u:user_9:rx" dir & mkdir dir1/dir1 & done Before exec repro.sh, dir1 has inherit the default acl from dir, and xattr block of dir1 dir is not the same, so the h_refcount of these two dir's xattr block will be 1. Then repro.sh can trigger the warning with the situation show as below. The last h_refcount can be clear with mkdir, and __ext4_new_inode has not reserved revoke credits, so the warning will happened, fix it by reserve revoke credits in __ext4_new_inode. Thread 1 Thread 2 mkdir dir set default acl(will create a xattr block blk1 and the refcount of ext4_xattr_header will be 1) ... mkdir dir1/dir1 ->....->ext4_init_acl ->__ext4_set_acl(set default acl, will reuse blk1, and h_refcount will be 2) setfacl->ext4_set_acl->... ->ext4_xattr_block_set(will create new block blk2 to store xattr) ->__ext4_set_acl(set access acl, since h_refcount of blk1 is 2, will create blk3 to store xattr) ->ext4_xattr_release_block(dec h_refcount of blk1 to 1) ->ext4_xattr_release_block(dec h_refcount and since it is 0, will release the block and trigger the warning) Link: https://lore.kernel.org/r/20191213014900.47228-1-yangerkun@huawei.com Reported-by: Hulk Robot Reviewed-by: Jan Kara Signed-off-by: yangerkun Signed-off-by: Theodore Ts'o --- fs/ext4/ialloc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/ext4/ialloc.c b/fs/ext4/ialloc.c index dc333e8e51e89..8ca4a23129aaf 100644 --- a/fs/ext4/ialloc.c +++ b/fs/ext4/ialloc.c @@ -921,8 +921,8 @@ repeat_in_this_group: if (!handle) { BUG_ON(nblocks <= 0); handle = __ext4_journal_start_sb(dir->i_sb, line_no, - handle_type, nblocks, - 0, 0); + handle_type, nblocks, 0, + ext4_trans_default_revoke_credits(sb)); if (IS_ERR(handle)) { err = PTR_ERR(handle); ext4_std_error(sb, err); -- GitLab From 3a5f494d84187db5a86de6aeceec4ae6b58380cd Mon Sep 17 00:00:00 2001 From: zhengbin Date: Sat, 14 Dec 2019 18:17:24 +0800 Subject: [PATCH 0658/1437] net: phy: dp83869: Remove unneeded semicolon Fixes coccicheck warning: drivers/net/phy/dp83869.c:337:2-3: Unneeded semicolon Reported-by: Hulk Robot Signed-off-by: zhengbin Reviewed-by: Andrew Lunn Signed-off-by: Jakub Kicinski --- drivers/net/phy/dp83869.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/phy/dp83869.c b/drivers/net/phy/dp83869.c index 93021904c5e42..7996a4aea8d28 100644 --- a/drivers/net/phy/dp83869.c +++ b/drivers/net/phy/dp83869.c @@ -334,7 +334,7 @@ static int dp83869_configure_mode(struct phy_device *phydev, break; default: return -EINVAL; - }; + } return ret; } -- GitLab From 5d485ed88d48f8101a2067348e267c0aaf4ed486 Mon Sep 17 00:00:00 2001 From: Mahesh Bandewar Date: Fri, 6 Dec 2019 15:44:55 -0800 Subject: [PATCH 0659/1437] bonding: fix active-backup transition after link failure After the recent fix in commit 1899bb325149 ("bonding: fix state transition issue in link monitoring"), the active-backup mode with miimon initially come-up fine but after a link-failure, both members transition into backup state. Following steps to reproduce the scenario (eth1 and eth2 are the slaves of the bond): ip link set eth1 up ip link set eth2 down sleep 1 ip link set eth2 up ip link set eth1 down cat /sys/class/net/eth1/bonding_slave/state cat /sys/class/net/eth2/bonding_slave/state Fixes: 1899bb325149 ("bonding: fix state transition issue in link monitoring") CC: Jay Vosburgh Signed-off-by: Mahesh Bandewar Acked-by: Jay Vosburgh Signed-off-by: Jakub Kicinski --- drivers/net/bonding/bond_main.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index 041aa9649dfc7..48d5ec770b942 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -2272,9 +2272,6 @@ static void bond_miimon_commit(struct bonding *bond) } else if (BOND_MODE(bond) != BOND_MODE_ACTIVEBACKUP) { /* make it immediately active */ bond_set_active_slave(slave); - } else if (slave != primary) { - /* prevent it from being the active one */ - bond_set_backup_slave(slave); } slave_info(bond->dev, slave->dev, "link status definitely up, %u Mbps %s duplex\n", -- GitLab From 6b01b1d9b2d38dc84ac398bfe9f00baff06a31e5 Mon Sep 17 00:00:00 2001 From: Taehee Yoo Date: Wed, 11 Dec 2019 08:23:00 +0000 Subject: [PATCH 0660/1437] gtp: do not allow adding duplicate tid and ms_addr pdp context GTP RX packet path lookups pdp context with TID. If duplicate TID pdp contexts are existing in the list, it couldn't select correct pdp context. So, TID value should be unique. GTP TX packet path lookups pdp context with ms_addr. If duplicate ms_addr pdp contexts are existing in the list, it couldn't select correct pdp context. So, ms_addr value should be unique. Fixes: 459aa660eb1d ("gtp: add initial driver for datapath of GPRS Tunneling Protocol (GTP-U)") Signed-off-by: Taehee Yoo Signed-off-by: Jakub Kicinski --- drivers/net/gtp.c | 32 ++++++++++++++++++++++---------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/drivers/net/gtp.c b/drivers/net/gtp.c index ecfe26215935d..8b742edf793dd 100644 --- a/drivers/net/gtp.c +++ b/drivers/net/gtp.c @@ -926,24 +926,31 @@ static void ipv4_pdp_fill(struct pdp_ctx *pctx, struct genl_info *info) } } -static int ipv4_pdp_add(struct gtp_dev *gtp, struct sock *sk, - struct genl_info *info) +static int gtp_pdp_add(struct gtp_dev *gtp, struct sock *sk, + struct genl_info *info) { + struct pdp_ctx *pctx, *pctx_tid = NULL; struct net_device *dev = gtp->dev; u32 hash_ms, hash_tid = 0; - struct pdp_ctx *pctx; + unsigned int version; bool found = false; __be32 ms_addr; ms_addr = nla_get_be32(info->attrs[GTPA_MS_ADDRESS]); hash_ms = ipv4_hashfn(ms_addr) % gtp->hash_size; + version = nla_get_u32(info->attrs[GTPA_VERSION]); - hlist_for_each_entry_rcu(pctx, >p->addr_hash[hash_ms], hlist_addr) { - if (pctx->ms_addr_ip4.s_addr == ms_addr) { - found = true; - break; - } - } + pctx = ipv4_pdp_find(gtp, ms_addr); + if (pctx) + found = true; + if (version == GTP_V0) + pctx_tid = gtp0_pdp_find(gtp, + nla_get_u64(info->attrs[GTPA_TID])); + else if (version == GTP_V1) + pctx_tid = gtp1_pdp_find(gtp, + nla_get_u32(info->attrs[GTPA_I_TEI])); + if (pctx_tid) + found = true; if (found) { if (info->nlhdr->nlmsg_flags & NLM_F_EXCL) @@ -951,6 +958,11 @@ static int ipv4_pdp_add(struct gtp_dev *gtp, struct sock *sk, if (info->nlhdr->nlmsg_flags & NLM_F_REPLACE) return -EOPNOTSUPP; + if (pctx && pctx_tid) + return -EEXIST; + if (!pctx) + pctx = pctx_tid; + ipv4_pdp_fill(pctx, info); if (pctx->gtp_version == GTP_V0) @@ -1074,7 +1086,7 @@ static int gtp_genl_new_pdp(struct sk_buff *skb, struct genl_info *info) goto out_unlock; } - err = ipv4_pdp_add(gtp, sk, info); + err = gtp_pdp_add(gtp, sk, info); out_unlock: rcu_read_unlock(); -- GitLab From 94a6d9fb88df43f92d943c32b84ce398d50bf49f Mon Sep 17 00:00:00 2001 From: Taehee Yoo Date: Wed, 11 Dec 2019 08:23:17 +0000 Subject: [PATCH 0661/1437] gtp: fix wrong condition in gtp_genl_dump_pdp() gtp_genl_dump_pdp() is ->dumpit() callback of GTP module and it is used to dump pdp contexts. it would be re-executed because of dump packet size. If dump packet size is too big, it saves current dump pointer (gtp interface pointer, bucket, TID value) then it restarts dump from last pointer. Current GTP code allows adding zero TID pdp context but dump code ignores zero TID value. So, last dump pointer will not be found. In addition, this patch adds missing rcu_read_lock() in gtp_genl_dump_pdp(). Fixes: 459aa660eb1d ("gtp: add initial driver for datapath of GPRS Tunneling Protocol (GTP-U)") Signed-off-by: Taehee Yoo Signed-off-by: Jakub Kicinski --- drivers/net/gtp.c | 36 +++++++++++++++++++----------------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/drivers/net/gtp.c b/drivers/net/gtp.c index 8b742edf793dd..a010e0a11c338 100644 --- a/drivers/net/gtp.c +++ b/drivers/net/gtp.c @@ -38,7 +38,6 @@ struct pdp_ctx { struct hlist_node hlist_addr; union { - u64 tid; struct { u64 tid; u16 flow; @@ -1244,43 +1243,46 @@ static int gtp_genl_dump_pdp(struct sk_buff *skb, struct netlink_callback *cb) { struct gtp_dev *last_gtp = (struct gtp_dev *)cb->args[2], *gtp; + int i, j, bucket = cb->args[0], skip = cb->args[1]; struct net *net = sock_net(skb->sk); - struct gtp_net *gn = net_generic(net, gtp_net_id); - unsigned long tid = cb->args[1]; - int i, k = cb->args[0], ret; struct pdp_ctx *pctx; + struct gtp_net *gn; + + gn = net_generic(net, gtp_net_id); if (cb->args[4]) return 0; + rcu_read_lock(); list_for_each_entry_rcu(gtp, &gn->gtp_dev_list, list) { if (last_gtp && last_gtp != gtp) continue; else last_gtp = NULL; - for (i = k; i < gtp->hash_size; i++) { - hlist_for_each_entry_rcu(pctx, >p->tid_hash[i], hlist_tid) { - if (tid && tid != pctx->u.tid) - continue; - else - tid = 0; - - ret = gtp_genl_fill_info(skb, - NETLINK_CB(cb->skb).portid, - cb->nlh->nlmsg_seq, - cb->nlh->nlmsg_type, pctx); - if (ret < 0) { + for (i = bucket; i < gtp->hash_size; i++) { + j = 0; + hlist_for_each_entry_rcu(pctx, >p->tid_hash[i], + hlist_tid) { + if (j >= skip && + gtp_genl_fill_info(skb, + NETLINK_CB(cb->skb).portid, + cb->nlh->nlmsg_seq, + cb->nlh->nlmsg_type, pctx)) { cb->args[0] = i; - cb->args[1] = pctx->u.tid; + cb->args[1] = j; cb->args[2] = (unsigned long)gtp; goto out; } + j++; } + skip = 0; } + bucket = 0; } cb->args[4] = 1; out: + rcu_read_unlock(); return skb->len; } -- GitLab From 94dc550a5062030569d4aa76e10e50c8fc001930 Mon Sep 17 00:00:00 2001 From: Taehee Yoo Date: Wed, 11 Dec 2019 08:23:34 +0000 Subject: [PATCH 0662/1437] gtp: fix an use-after-free in ipv4_pdp_find() ipv4_pdp_find() is called in TX packet path of GTP. ipv4_pdp_find() internally uses gtp->tid_hash to lookup pdp context. In the current code, gtp->tid_hash and gtp->addr_hash are freed by ->dellink(), which is gtp_dellink(). But gtp_dellink() would be called while packets are processing. So, gtp_dellink() should not free gtp->tid_hash and gtp->addr_hash. Instead, dev->priv_destructor() would be used because this callback is called after all packet processing safely. Test commands: ip link add veth1 type veth peer name veth2 ip a a 172.0.0.1/24 dev veth1 ip link set veth1 up ip a a 172.99.0.1/32 dev lo gtp-link add gtp1 & gtp-tunnel add gtp1 v1 200 100 172.99.0.2 172.0.0.2 ip r a 172.99.0.2/32 dev gtp1 ip link set gtp1 mtu 1500 ip netns add ns2 ip link set veth2 netns ns2 ip netns exec ns2 ip a a 172.0.0.2/24 dev veth2 ip netns exec ns2 ip link set veth2 up ip netns exec ns2 ip a a 172.99.0.2/32 dev lo ip netns exec ns2 ip link set lo up ip netns exec ns2 gtp-link add gtp2 & ip netns exec ns2 gtp-tunnel add gtp2 v1 100 200 172.99.0.1 172.0.0.1 ip netns exec ns2 ip r a 172.99.0.1/32 dev gtp2 ip netns exec ns2 ip link set gtp2 mtu 1500 hping3 172.99.0.2 -2 --flood & ip link del gtp1 Splat looks like: [ 72.568081][ T1195] BUG: KASAN: use-after-free in ipv4_pdp_find.isra.12+0x130/0x170 [gtp] [ 72.568916][ T1195] Read of size 8 at addr ffff8880b9a35d28 by task hping3/1195 [ 72.569631][ T1195] [ 72.569861][ T1195] CPU: 2 PID: 1195 Comm: hping3 Not tainted 5.5.0-rc1 #199 [ 72.570547][ T1195] Hardware name: innotek GmbH VirtualBox/VirtualBox, BIOS VirtualBox 12/01/2006 [ 72.571438][ T1195] Call Trace: [ 72.571764][ T1195] dump_stack+0x96/0xdb [ 72.572171][ T1195] ? ipv4_pdp_find.isra.12+0x130/0x170 [gtp] [ 72.572761][ T1195] print_address_description.constprop.5+0x1be/0x360 [ 72.573400][ T1195] ? ipv4_pdp_find.isra.12+0x130/0x170 [gtp] [ 72.573971][ T1195] ? ipv4_pdp_find.isra.12+0x130/0x170 [gtp] [ 72.574544][ T1195] __kasan_report+0x12a/0x16f [ 72.575014][ T1195] ? ipv4_pdp_find.isra.12+0x130/0x170 [gtp] [ 72.575593][ T1195] kasan_report+0xe/0x20 [ 72.576004][ T1195] ipv4_pdp_find.isra.12+0x130/0x170 [gtp] [ 72.576577][ T1195] gtp_build_skb_ip4+0x199/0x1420 [gtp] [ ... ] [ 72.647671][ T1195] BUG: unable to handle page fault for address: ffff8880b9a35d28 [ 72.648512][ T1195] #PF: supervisor read access in kernel mode [ 72.649158][ T1195] #PF: error_code(0x0000) - not-present page [ 72.649849][ T1195] PGD a6c01067 P4D a6c01067 PUD 11fb07067 PMD 11f939067 PTE 800fffff465ca060 [ 72.652958][ T1195] Oops: 0000 [#1] SMP DEBUG_PAGEALLOC KASAN PTI [ 72.653834][ T1195] CPU: 2 PID: 1195 Comm: hping3 Tainted: G B 5.5.0-rc1 #199 [ 72.668062][ T1195] RIP: 0010:ipv4_pdp_find.isra.12+0x86/0x170 [gtp] [ ... ] [ 72.679168][ T1195] Call Trace: [ 72.679603][ T1195] gtp_build_skb_ip4+0x199/0x1420 [gtp] [ 72.681915][ T1195] ? ipv4_pdp_find.isra.12+0x170/0x170 [gtp] [ 72.682513][ T1195] ? lock_acquire+0x164/0x3b0 [ 72.682966][ T1195] ? gtp_dev_xmit+0x35e/0x890 [gtp] [ 72.683481][ T1195] gtp_dev_xmit+0x3c2/0x890 [gtp] [ ... ] Fixes: 459aa660eb1d ("gtp: add initial driver for datapath of GPRS Tunneling Protocol (GTP-U)") Signed-off-by: Taehee Yoo Signed-off-by: Jakub Kicinski --- drivers/net/gtp.c | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/drivers/net/gtp.c b/drivers/net/gtp.c index a010e0a11c338..5450b1099c6df 100644 --- a/drivers/net/gtp.c +++ b/drivers/net/gtp.c @@ -640,9 +640,16 @@ static void gtp_link_setup(struct net_device *dev) } static int gtp_hashtable_new(struct gtp_dev *gtp, int hsize); -static void gtp_hashtable_free(struct gtp_dev *gtp); static int gtp_encap_enable(struct gtp_dev *gtp, struct nlattr *data[]); +static void gtp_destructor(struct net_device *dev) +{ + struct gtp_dev *gtp = netdev_priv(dev); + + kfree(gtp->addr_hash); + kfree(gtp->tid_hash); +} + static int gtp_newlink(struct net *src_net, struct net_device *dev, struct nlattr *tb[], struct nlattr *data[], struct netlink_ext_ack *extack) @@ -677,13 +684,15 @@ static int gtp_newlink(struct net *src_net, struct net_device *dev, gn = net_generic(dev_net(dev), gtp_net_id); list_add_rcu(>p->list, &gn->gtp_dev_list); + dev->priv_destructor = gtp_destructor; netdev_dbg(dev, "registered new GTP interface\n"); return 0; out_hashtable: - gtp_hashtable_free(gtp); + kfree(gtp->addr_hash); + kfree(gtp->tid_hash); out_encap: gtp_encap_disable(gtp); return err; @@ -692,8 +701,13 @@ out_encap: static void gtp_dellink(struct net_device *dev, struct list_head *head) { struct gtp_dev *gtp = netdev_priv(dev); + struct pdp_ctx *pctx; + int i; + + for (i = 0; i < gtp->hash_size; i++) + hlist_for_each_entry_rcu(pctx, >p->tid_hash[i], hlist_tid) + pdp_context_delete(pctx); - gtp_hashtable_free(gtp); list_del_rcu(>p->list); unregister_netdevice_queue(dev, head); } @@ -771,20 +785,6 @@ err1: return -ENOMEM; } -static void gtp_hashtable_free(struct gtp_dev *gtp) -{ - struct pdp_ctx *pctx; - int i; - - for (i = 0; i < gtp->hash_size; i++) - hlist_for_each_entry_rcu(pctx, >p->tid_hash[i], hlist_tid) - pdp_context_delete(pctx); - - synchronize_rcu(); - kfree(gtp->addr_hash); - kfree(gtp->tid_hash); -} - static struct sock *gtp_encap_enable_socket(int fd, int type, struct gtp_dev *gtp) { -- GitLab From 6a902c0f31993ab02e1b6ea7085002b9c9083b6a Mon Sep 17 00:00:00 2001 From: Taehee Yoo Date: Wed, 11 Dec 2019 08:23:48 +0000 Subject: [PATCH 0663/1437] gtp: avoid zero size hashtable GTP default hashtable size is 1024 and userspace could set specific hashtable size with IFLA_GTP_PDP_HASHSIZE. If hashtable size is set to 0 from userspace, hashtable will not work and panic will occur. Fixes: 459aa660eb1d ("gtp: add initial driver for datapath of GPRS Tunneling Protocol (GTP-U)") Signed-off-by: Taehee Yoo Signed-off-by: Jakub Kicinski --- drivers/net/gtp.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/net/gtp.c b/drivers/net/gtp.c index 5450b1099c6df..e5b7d6d2286e0 100644 --- a/drivers/net/gtp.c +++ b/drivers/net/gtp.c @@ -667,10 +667,13 @@ static int gtp_newlink(struct net *src_net, struct net_device *dev, if (err < 0) return err; - if (!data[IFLA_GTP_PDP_HASHSIZE]) + if (!data[IFLA_GTP_PDP_HASHSIZE]) { hashsize = 1024; - else + } else { hashsize = nla_get_u32(data[IFLA_GTP_PDP_HASHSIZE]); + if (!hashsize) + hashsize = 1024; + } err = gtp_hashtable_new(gtp, hashsize); if (err < 0) -- GitLab From ea6a547669b37453f2b1a5d85188d75b3613dfaa Mon Sep 17 00:00:00 2001 From: Willem de Bruijn Date: Thu, 12 Dec 2019 11:36:46 -0500 Subject: [PATCH 0664/1437] selftests/net: make so_txtime more robust to timer variance The SO_TXTIME test depends on accurate timers. In some virtualized environments the test has been reported to be flaky. This is easily reproduced by disabling kvm acceleration in Qemu. Allow greater variance in a run and retry to further reduce flakiness. Observed errors are one of two kinds: either the packet arrives too early or late at recv(), or it was dropped in the qdisc itself and the recv() call times out. In the latter case, the qdisc queues a notification to the error queue of the send socket. Also explicitly report this cause. Link: https://lore.kernel.org/netdev/CA+FuTSdYOnJCsGuj43xwV1jxvYsaoa_LzHQF9qMyhrkLrivxKw@mail.gmail.com Reported-by: Naresh Kamboju Signed-off-by: Willem de Bruijn Signed-off-by: Jakub Kicinski --- tools/testing/selftests/net/so_txtime.c | 84 +++++++++++++++++++++++- tools/testing/selftests/net/so_txtime.sh | 9 ++- 2 files changed, 88 insertions(+), 5 deletions(-) diff --git a/tools/testing/selftests/net/so_txtime.c b/tools/testing/selftests/net/so_txtime.c index 34df4c8882afb..383bac05ac324 100644 --- a/tools/testing/selftests/net/so_txtime.c +++ b/tools/testing/selftests/net/so_txtime.c @@ -12,7 +12,11 @@ #include #include #include +#include #include +#include +#include +#include #include #include #include @@ -28,7 +32,7 @@ static int cfg_clockid = CLOCK_TAI; static bool cfg_do_ipv4; static bool cfg_do_ipv6; static uint16_t cfg_port = 8000; -static int cfg_variance_us = 2000; +static int cfg_variance_us = 4000; static uint64_t glob_tstart; @@ -43,6 +47,9 @@ static struct timed_send cfg_in[MAX_NUM_PKT]; static struct timed_send cfg_out[MAX_NUM_PKT]; static int cfg_num_pkt; +static int cfg_errq_level; +static int cfg_errq_type; + static uint64_t gettime_ns(void) { struct timespec ts; @@ -90,13 +97,15 @@ static void do_send_one(int fdt, struct timed_send *ts) } -static void do_recv_one(int fdr, struct timed_send *ts) +static bool do_recv_one(int fdr, struct timed_send *ts) { int64_t tstop, texpect; char rbuf[2]; int ret; ret = recv(fdr, rbuf, sizeof(rbuf), 0); + if (ret == -1 && errno == EAGAIN) + return true; if (ret == -1) error(1, errno, "read"); if (ret != 1) @@ -113,6 +122,8 @@ static void do_recv_one(int fdr, struct timed_send *ts) if (labs(tstop - texpect) > cfg_variance_us) error(1, 0, "exceeds variance (%d us)", cfg_variance_us); + + return false; } static void do_recv_verify_empty(int fdr) @@ -125,12 +136,70 @@ static void do_recv_verify_empty(int fdr) error(1, 0, "recv: not empty as expected (%d, %d)", ret, errno); } +static void do_recv_errqueue_timeout(int fdt) +{ + char control[CMSG_SPACE(sizeof(struct sock_extended_err)) + + CMSG_SPACE(sizeof(struct sockaddr_in6))] = {0}; + char data[sizeof(struct ipv6hdr) + + sizeof(struct tcphdr) + 1]; + struct sock_extended_err *err; + struct msghdr msg = {0}; + struct iovec iov = {0}; + struct cmsghdr *cm; + int64_t tstamp = 0; + int ret; + + iov.iov_base = data; + iov.iov_len = sizeof(data); + + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + + msg.msg_control = control; + msg.msg_controllen = sizeof(control); + + while (1) { + ret = recvmsg(fdt, &msg, MSG_ERRQUEUE); + if (ret == -1 && errno == EAGAIN) + break; + if (ret == -1) + error(1, errno, "errqueue"); + if (msg.msg_flags != MSG_ERRQUEUE) + error(1, 0, "errqueue: flags 0x%x\n", msg.msg_flags); + + cm = CMSG_FIRSTHDR(&msg); + if (cm->cmsg_level != cfg_errq_level || + cm->cmsg_type != cfg_errq_type) + error(1, 0, "errqueue: type 0x%x.0x%x\n", + cm->cmsg_level, cm->cmsg_type); + + err = (struct sock_extended_err *)CMSG_DATA(cm); + if (err->ee_origin != SO_EE_ORIGIN_TXTIME) + error(1, 0, "errqueue: origin 0x%x\n", err->ee_origin); + if (err->ee_code != ECANCELED) + error(1, 0, "errqueue: code 0x%x\n", err->ee_code); + + tstamp = ((int64_t) err->ee_data) << 32 | err->ee_info; + tstamp -= (int64_t) glob_tstart; + tstamp /= 1000 * 1000; + fprintf(stderr, "send: pkt %c at %" PRId64 "ms dropped\n", + data[ret - 1], tstamp); + + msg.msg_flags = 0; + msg.msg_controllen = sizeof(control); + } + + error(1, 0, "recv: timeout"); +} + static void setsockopt_txtime(int fd) { struct sock_txtime so_txtime_val = { .clockid = cfg_clockid }; struct sock_txtime so_txtime_val_read = { 0 }; socklen_t vallen = sizeof(so_txtime_val); + so_txtime_val.flags = SOF_TXTIME_REPORT_ERRORS; + if (setsockopt(fd, SOL_SOCKET, SO_TXTIME, &so_txtime_val, sizeof(so_txtime_val))) error(1, errno, "setsockopt txtime"); @@ -194,7 +263,8 @@ static void do_test(struct sockaddr *addr, socklen_t alen) for (i = 0; i < cfg_num_pkt; i++) do_send_one(fdt, &cfg_in[i]); for (i = 0; i < cfg_num_pkt; i++) - do_recv_one(fdr, &cfg_out[i]); + if (do_recv_one(fdr, &cfg_out[i])) + do_recv_errqueue_timeout(fdt); do_recv_verify_empty(fdr); @@ -280,6 +350,10 @@ int main(int argc, char **argv) addr6.sin6_family = AF_INET6; addr6.sin6_port = htons(cfg_port); addr6.sin6_addr = in6addr_loopback; + + cfg_errq_level = SOL_IPV6; + cfg_errq_type = IPV6_RECVERR; + do_test((void *)&addr6, sizeof(addr6)); } @@ -289,6 +363,10 @@ int main(int argc, char **argv) addr4.sin_family = AF_INET; addr4.sin_port = htons(cfg_port); addr4.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + + cfg_errq_level = SOL_IP; + cfg_errq_type = IP_RECVERR; + do_test((void *)&addr4, sizeof(addr4)); } diff --git a/tools/testing/selftests/net/so_txtime.sh b/tools/testing/selftests/net/so_txtime.sh index 5aa519328a5b5..3f7800eaecb1e 100755 --- a/tools/testing/selftests/net/so_txtime.sh +++ b/tools/testing/selftests/net/so_txtime.sh @@ -5,7 +5,12 @@ # Run in network namespace if [[ $# -eq 0 ]]; then - ./in_netns.sh $0 __subprocess + if ! ./in_netns.sh $0 __subprocess; then + # test is time sensitive, can be flaky + echo "test failed: retry once" + ./in_netns.sh $0 __subprocess + fi + exit $? fi @@ -18,7 +23,7 @@ tc qdisc add dev lo root fq ./so_txtime -4 -6 -c mono a,10,b,20 a,10,b,20 ./so_txtime -4 -6 -c mono a,20,b,10 b,20,a,20 -if tc qdisc replace dev lo root etf clockid CLOCK_TAI delta 200000; then +if tc qdisc replace dev lo root etf clockid CLOCK_TAI delta 400000; then ! ./so_txtime -4 -6 -c tai a,-1 a,-1 ! ./so_txtime -4 -6 -c tai a,0 a,0 ./so_txtime -4 -6 -c tai a,10 a,10 -- GitLab From 6dd504b0fd1039c6e5d391e97cf5c4ee592aefcb Mon Sep 17 00:00:00 2001 From: Thadeu Lima de Souza Cascardo Date: Fri, 13 Dec 2019 07:39:02 -0300 Subject: [PATCH 0665/1437] selftests: net: tls: remove recv_rcvbuf test This test only works when [1] is applied, which was rejected. Basically, the errors are reported and cleared. In this particular case of tls sockets, following reads will block. The test case was originally submitted with the rejected patch, but, then, was included as part of a different patchset, possibly by mistake. [1] https://lore.kernel.org/netdev/20191007035323.4360-2-jakub.kicinski@netronome.com/#t Thanks Paolo Pisati for pointing out the original patchset where this appeared. Fixes: 65190f77424d (selftests/tls: add a test for fragmented messages) Reported-by: Paolo Pisati Signed-off-by: Thadeu Lima de Souza Cascardo Signed-off-by: Jakub Kicinski --- tools/testing/selftests/net/tls.c | 28 ---------------------------- 1 file changed, 28 deletions(-) diff --git a/tools/testing/selftests/net/tls.c b/tools/testing/selftests/net/tls.c index 13e5ef615026f..0ea44d975b6c1 100644 --- a/tools/testing/selftests/net/tls.c +++ b/tools/testing/selftests/net/tls.c @@ -722,34 +722,6 @@ TEST_F(tls, recv_lowat) EXPECT_EQ(memcmp(send_mem, recv_mem + 10, 5), 0); } -TEST_F(tls, recv_rcvbuf) -{ - char send_mem[4096]; - char recv_mem[4096]; - int rcv_buf = 1024; - - memset(send_mem, 0x1c, sizeof(send_mem)); - - EXPECT_EQ(setsockopt(self->cfd, SOL_SOCKET, SO_RCVBUF, - &rcv_buf, sizeof(rcv_buf)), 0); - - EXPECT_EQ(send(self->fd, send_mem, 512, 0), 512); - memset(recv_mem, 0, sizeof(recv_mem)); - EXPECT_EQ(recv(self->cfd, recv_mem, sizeof(recv_mem), 0), 512); - EXPECT_EQ(memcmp(send_mem, recv_mem, 512), 0); - - if (self->notls) - return; - - EXPECT_EQ(send(self->fd, send_mem, 4096, 0), 4096); - memset(recv_mem, 0, sizeof(recv_mem)); - EXPECT_EQ(recv(self->cfd, recv_mem, sizeof(recv_mem), 0), -1); - EXPECT_EQ(errno, EMSGSIZE); - - EXPECT_EQ(recv(self->cfd, recv_mem, sizeof(recv_mem), 0), -1); - EXPECT_EQ(errno, EMSGSIZE); -} - TEST_F(tls, bidir) { char const *test_str = "test_read"; -- GitLab From de1799667b002331609e8ee6b924ccfd51968d99 Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Wed, 11 Dec 2019 20:07:10 -0500 Subject: [PATCH 0666/1437] net: bridge: add STP xstats This adds rx_bpdu, tx_bpdu, rx_tcn, tx_tcn, transition_blk, transition_fwd xstats counters to the bridge ports copied over via netlink, providing useful information for STP. Signed-off-by: Vivien Didelot Acked-by: Nikolay Aleksandrov Signed-off-by: Jakub Kicinski --- include/uapi/linux/if_bridge.h | 10 ++++++++++ net/bridge/br_netlink.c | 13 +++++++++++++ net/bridge/br_private.h | 2 ++ net/bridge/br_stp.c | 15 +++++++++++++++ net/bridge/br_stp_bpdu.c | 4 ++++ 5 files changed, 44 insertions(+) diff --git a/include/uapi/linux/if_bridge.h b/include/uapi/linux/if_bridge.h index 1b3c2b643a02e..4a58e3d7de46f 100644 --- a/include/uapi/linux/if_bridge.h +++ b/include/uapi/linux/if_bridge.h @@ -156,6 +156,15 @@ struct bridge_vlan_xstats { __u32 pad2; }; +struct bridge_stp_xstats { + __u64 transition_blk; + __u64 transition_fwd; + __u64 rx_bpdu; + __u64 tx_bpdu; + __u64 rx_tcn; + __u64 tx_tcn; +}; + /* Bridge multicast database attributes * [MDBA_MDB] = { * [MDBA_MDB_ENTRY] = { @@ -262,6 +271,7 @@ enum { BRIDGE_XSTATS_VLAN, BRIDGE_XSTATS_MCAST, BRIDGE_XSTATS_PAD, + BRIDGE_XSTATS_STP, __BRIDGE_XSTATS_MAX }; #define BRIDGE_XSTATS_MAX (__BRIDGE_XSTATS_MAX - 1) diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c index a0a54482aabc4..60136575aea45 100644 --- a/net/bridge/br_netlink.c +++ b/net/bridge/br_netlink.c @@ -1607,6 +1607,19 @@ static int br_fill_linkxstats(struct sk_buff *skb, br_multicast_get_stats(br, p, nla_data(nla)); } #endif + + if (p) { + nla = nla_reserve_64bit(skb, BRIDGE_XSTATS_STP, + sizeof(p->stp_xstats), + BRIDGE_XSTATS_PAD); + if (!nla) + goto nla_put_failure; + + spin_lock_bh(&br->lock); + memcpy(nla_data(nla), &p->stp_xstats, sizeof(p->stp_xstats)); + spin_unlock_bh(&br->lock); + } + nla_nest_end(skb, nest); *prividx = 0; diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h index 36b0367ca1e0b..f540f3bdf2943 100644 --- a/net/bridge/br_private.h +++ b/net/bridge/br_private.h @@ -283,6 +283,8 @@ struct net_bridge_port { #endif u16 group_fwd_mask; u16 backup_redirected_cnt; + + struct bridge_stp_xstats stp_xstats; }; #define kobj_to_brport(obj) container_of(obj, struct net_bridge_port, kobj) diff --git a/net/bridge/br_stp.c b/net/bridge/br_stp.c index 1f1410f8d312a..6856a6d9282b8 100644 --- a/net/bridge/br_stp.c +++ b/net/bridge/br_stp.c @@ -45,6 +45,17 @@ void br_set_state(struct net_bridge_port *p, unsigned int state) br_info(p->br, "port %u(%s) entered %s state\n", (unsigned int) p->port_no, p->dev->name, br_port_state_names[p->state]); + + if (p->br->stp_enabled == BR_KERNEL_STP) { + switch (p->state) { + case BR_STATE_BLOCKING: + p->stp_xstats.transition_blk++; + break; + case BR_STATE_FORWARDING: + p->stp_xstats.transition_fwd++; + break; + } + } } /* called under bridge lock */ @@ -484,6 +495,8 @@ void br_received_config_bpdu(struct net_bridge_port *p, struct net_bridge *br; int was_root; + p->stp_xstats.rx_bpdu++; + br = p->br; was_root = br_is_root_bridge(br); @@ -517,6 +530,8 @@ void br_received_config_bpdu(struct net_bridge_port *p, /* called under bridge lock */ void br_received_tcn_bpdu(struct net_bridge_port *p) { + p->stp_xstats.rx_tcn++; + if (br_is_designated_port(p)) { br_info(p->br, "port %u(%s) received tcn bpdu\n", (unsigned int) p->port_no, p->dev->name); diff --git a/net/bridge/br_stp_bpdu.c b/net/bridge/br_stp_bpdu.c index 7796dd9d42d7a..0e4572f313307 100644 --- a/net/bridge/br_stp_bpdu.c +++ b/net/bridge/br_stp_bpdu.c @@ -118,6 +118,8 @@ void br_send_config_bpdu(struct net_bridge_port *p, struct br_config_bpdu *bpdu) br_set_ticks(buf+33, bpdu->forward_delay); br_send_bpdu(p, buf, 35); + + p->stp_xstats.tx_bpdu++; } /* called under bridge lock */ @@ -133,6 +135,8 @@ void br_send_tcn_bpdu(struct net_bridge_port *p) buf[2] = 0; buf[3] = BPDU_TYPE_TCN; br_send_bpdu(p, buf, 4); + + p->stp_xstats.tx_tcn++; } /* -- GitLab From f5058a27dac700952d5551bd7c5546f52b0468e4 Mon Sep 17 00:00:00 2001 From: Russell King Date: Thu, 12 Dec 2019 17:16:12 +0000 Subject: [PATCH 0667/1437] net: phylink: propagate phy_attach_direct() return code of_phy_attach() hides the return value of phy_attach_direct(), forcing us to return a "generic" ENODEV error code that is indistinguishable from the lack-of-phy-property case. Switch to using of_phy_find_device() to find the PHY device, and then propagating any phy_attach_direct() error back to the caller. Link: https://lore.kernel.org/lkml/20191210113829.GT25745@shell.armlinux.org.uk Signed-off-by: Russell King Signed-off-by: Jakub Kicinski --- drivers/net/phy/phylink.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c index 47614f9753448..1e0e32c466ee4 100644 --- a/drivers/net/phy/phylink.c +++ b/drivers/net/phy/phylink.c @@ -860,14 +860,17 @@ int phylink_of_phy_connect(struct phylink *pl, struct device_node *dn, return 0; } - phy_dev = of_phy_attach(pl->netdev, phy_node, flags, - pl->link_interface); + phy_dev = of_phy_find_device(phy_node); /* We're done with the phy_node handle */ of_node_put(phy_node); - if (!phy_dev) return -ENODEV; + ret = phy_attach_direct(pl->netdev, phy_dev, flags, + pl->link_interface); + if (ret) + return ret; + ret = phylink_bringup_phy(pl, phy_dev, pl->link_config.interface); if (ret) phy_detach(phy_dev); -- GitLab From 9b2079c046a9d6c9c73a4ec33816678565ee01f3 Mon Sep 17 00:00:00 2001 From: Russell King Date: Fri, 13 Dec 2019 10:06:30 +0000 Subject: [PATCH 0668/1437] net: phylink: fix interface passed to mac_link_up A mismerge between the following two commits: c678726305b9 ("net: phylink: ensure consistent phy interface mode") 27755ff88c0e ("net: phylink: Add phylink_mac_link_{up, down} wrapper functions") resulted in the wrong interface being passed to the mac_link_up() function. Fix this up. Fixes: b4b12b0d2f02 ("Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net") Signed-off-by: Russell King Signed-off-by: Jakub Kicinski --- drivers/net/phy/phylink.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c index 9a616d6bc4eb2..1585eebb73feb 100644 --- a/drivers/net/phy/phylink.c +++ b/drivers/net/phy/phylink.c @@ -442,8 +442,7 @@ static void phylink_mac_link_up(struct phylink *pl, pl->cur_interface = link_state.interface; pl->ops->mac_link_up(pl->config, pl->link_an_mode, - pl->phy_state.interface, - pl->phydev); + pl->cur_interface, pl->phydev); if (ndev) netif_carrier_on(ndev); -- GitLab From ec2025095cf6acda3233a4301809596938b47da5 Mon Sep 17 00:00:00 2001 From: Paul Chaignon Date: Fri, 13 Dec 2019 20:10:04 +0100 Subject: [PATCH 0669/1437] bpftool: Match several programs with same tag When several BPF programs have the same tag, bpftool matches only the first (in ID order). This patch changes that behavior such that dump and show commands return all matched programs. Commands that require a single program (e.g., pin and attach) will error out if given a tag that matches several. bpftool prog dump will also error out if file or visual are given and several programs have the given tag. In the case of the dump command, a program header is added before each dump only if the tag matches several programs; this patch doesn't change the output if a single program matches. The output when several programs match thus looks as follows. $ ./bpftool prog dump xlated tag 6deef7357e7b4530 3: cgroup_skb tag 6deef7357e7b4530 gpl 0: (bf) r6 = r1 [...] 7: (95) exit 4: cgroup_skb tag 6deef7357e7b4530 gpl 0: (bf) r6 = r1 [...] 7: (95) exit Signed-off-by: Paul Chaignon Signed-off-by: Alexei Starovoitov Link: https://lore.kernel.org/bpf/fb1fe943202659a69cd21dd5b907c205af1e1e22.1576263640.git.paul.chaignon@gmail.com --- .../bpftool/Documentation/bpftool-prog.rst | 16 +- tools/bpf/bpftool/prog.c | 372 ++++++++++++------ 2 files changed, 268 insertions(+), 120 deletions(-) diff --git a/tools/bpf/bpftool/Documentation/bpftool-prog.rst b/tools/bpf/bpftool/Documentation/bpftool-prog.rst index 7a374b3c851d0..d377d0cb79231 100644 --- a/tools/bpf/bpftool/Documentation/bpftool-prog.rst +++ b/tools/bpf/bpftool/Documentation/bpftool-prog.rst @@ -53,8 +53,10 @@ DESCRIPTION =========== **bpftool prog { show | list }** [*PROG*] Show information about loaded programs. If *PROG* is - specified show information only about given program, otherwise - list all programs currently loaded on the system. + specified show information only about given programs, + otherwise list all programs currently loaded on the system. + In case of **tag**, *PROG* may match several programs which + will all be shown. Output will start with program ID followed by program type and zero or more named attributes (depending on kernel version). @@ -68,11 +70,15 @@ DESCRIPTION performed via the **kernel.bpf_stats_enabled** sysctl knob. **bpftool prog dump xlated** *PROG* [{ **file** *FILE* | **opcodes** | **visual** | **linum** }] - Dump eBPF instructions of the program from the kernel. By + Dump eBPF instructions of the programs from the kernel. By default, eBPF will be disassembled and printed to standard output in human-readable format. In this case, **opcodes** controls if raw opcodes should be printed as well. + In case of **tag**, *PROG* may match several programs which + will all be dumped. However, if **file** or **visual** is + specified, *PROG* must match a single program. + If **file** is specified, the binary image will instead be written to *FILE*. @@ -80,15 +86,17 @@ DESCRIPTION built instead, and eBPF instructions will be presented with CFG in DOT format, on standard output. - If the prog has line_info available, the source line will + If the programs have line_info available, the source line will be displayed by default. If **linum** is specified, the filename, line number and line column will also be displayed on top of the source line. **bpftool prog dump jited** *PROG* [{ **file** *FILE* | **opcodes** | **linum** }] Dump jited image (host machine code) of the program. + If *FILE* is specified image will be written to a file, otherwise it will be disassembled and printed to stdout. + *PROG* must match a single program when **file** is specified. **opcodes** controls if raw opcodes will be printed. diff --git a/tools/bpf/bpftool/prog.c b/tools/bpf/bpftool/prog.c index 4535c863d2cd9..37948c47aabda 100644 --- a/tools/bpf/bpftool/prog.c +++ b/tools/bpf/bpftool/prog.c @@ -25,6 +25,11 @@ #include "main.h" #include "xlated_dumper.h" +enum dump_mode { + DUMP_JITED, + DUMP_XLATED, +}; + static const char * const attach_type_strings[] = { [BPF_SK_SKB_STREAM_PARSER] = "stream_parser", [BPF_SK_SKB_STREAM_VERDICT] = "stream_verdict", @@ -77,11 +82,12 @@ static void print_boot_time(__u64 nsecs, char *buf, unsigned int size) strftime(buf, size, "%FT%T%z", &load_tm); } -static int prog_fd_by_tag(unsigned char *tag) +static int prog_fd_by_tag(unsigned char *tag, int **fds) { unsigned int id = 0; + int fd, nb_fds = 0; + void *tmp; int err; - int fd; while (true) { struct bpf_prog_info info = {}; @@ -89,36 +95,53 @@ static int prog_fd_by_tag(unsigned char *tag) err = bpf_prog_get_next_id(id, &id); if (err) { - p_err("%s", strerror(errno)); - return -1; + if (errno != ENOENT) { + p_err("%s", strerror(errno)); + goto err_close_fds; + } + return nb_fds; } fd = bpf_prog_get_fd_by_id(id); if (fd < 0) { p_err("can't get prog by id (%u): %s", id, strerror(errno)); - return -1; + goto err_close_fds; } err = bpf_obj_get_info_by_fd(fd, &info, &len); if (err) { p_err("can't get prog info (%u): %s", id, strerror(errno)); - close(fd); - return -1; + goto err_close_fd; } - if (!memcmp(tag, info.tag, BPF_TAG_SIZE)) - return fd; + if (memcmp(tag, info.tag, BPF_TAG_SIZE)) { + close(fd); + continue; + } - close(fd); + if (nb_fds > 0) { + tmp = realloc(*fds, (nb_fds + 1) * sizeof(int)); + if (!tmp) { + p_err("failed to realloc"); + goto err_close_fd; + } + *fds = tmp; + } + (*fds)[nb_fds++] = fd; } + +err_close_fd: + close(fd); +err_close_fds: + while (--nb_fds >= 0) + close((*fds)[nb_fds]); + return -1; } -int prog_parse_fd(int *argc, char ***argv) +static int prog_parse_fds(int *argc, char ***argv, int **fds) { - int fd; - if (is_prefix(**argv, "id")) { unsigned int id; char *endptr; @@ -132,10 +155,12 @@ int prog_parse_fd(int *argc, char ***argv) } NEXT_ARGP(); - fd = bpf_prog_get_fd_by_id(id); - if (fd < 0) + (*fds)[0] = bpf_prog_get_fd_by_id(id); + if ((*fds)[0] < 0) { p_err("get by id (%u): %s", id, strerror(errno)); - return fd; + return -1; + } + return 1; } else if (is_prefix(**argv, "tag")) { unsigned char tag[BPF_TAG_SIZE]; @@ -149,7 +174,7 @@ int prog_parse_fd(int *argc, char ***argv) } NEXT_ARGP(); - return prog_fd_by_tag(tag); + return prog_fd_by_tag(tag, fds); } else if (is_prefix(**argv, "pinned")) { char *path; @@ -158,13 +183,43 @@ int prog_parse_fd(int *argc, char ***argv) path = **argv; NEXT_ARGP(); - return open_obj_pinned_any(path, BPF_OBJ_PROG); + (*fds)[0] = open_obj_pinned_any(path, BPF_OBJ_PROG); + if ((*fds)[0] < 0) + return -1; + return 1; } p_err("expected 'id', 'tag' or 'pinned', got: '%s'?", **argv); return -1; } +int prog_parse_fd(int *argc, char ***argv) +{ + int *fds = NULL; + int nb_fds, fd; + + fds = malloc(sizeof(int)); + if (!fds) { + p_err("mem alloc failed"); + return -1; + } + nb_fds = prog_parse_fds(argc, argv, &fds); + if (nb_fds != 1) { + if (nb_fds > 1) { + p_err("several programs match this handle"); + while (nb_fds--) + close(fds[nb_fds]); + } + fd = -1; + goto exit_free; + } + + fd = fds[0]; +exit_free: + free(fds); + return fd; +} + static void show_prog_maps(int fd, u32 num_maps) { struct bpf_prog_info info = {}; @@ -194,11 +249,8 @@ static void show_prog_maps(int fd, u32 num_maps) } } -static void print_prog_json(struct bpf_prog_info *info, int fd) +static void print_prog_header_json(struct bpf_prog_info *info) { - char *memlock; - - jsonw_start_object(json_wtr); jsonw_uint_field(json_wtr, "id", info->id); if (info->type < ARRAY_SIZE(prog_type_name)) jsonw_string_field(json_wtr, "type", @@ -219,7 +271,14 @@ static void print_prog_json(struct bpf_prog_info *info, int fd) jsonw_uint_field(json_wtr, "run_time_ns", info->run_time_ns); jsonw_uint_field(json_wtr, "run_cnt", info->run_cnt); } +} + +static void print_prog_json(struct bpf_prog_info *info, int fd) +{ + char *memlock; + jsonw_start_object(json_wtr); + print_prog_header_json(info); print_dev_json(info->ifindex, info->netns_dev, info->netns_ino); if (info->load_time) { @@ -268,10 +327,8 @@ static void print_prog_json(struct bpf_prog_info *info, int fd) jsonw_end_object(json_wtr); } -static void print_prog_plain(struct bpf_prog_info *info, int fd) +static void print_prog_header_plain(struct bpf_prog_info *info) { - char *memlock; - printf("%u: ", info->id); if (info->type < ARRAY_SIZE(prog_type_name)) printf("%s ", prog_type_name[info->type]); @@ -289,6 +346,13 @@ static void print_prog_plain(struct bpf_prog_info *info, int fd) printf(" run_time_ns %lld run_cnt %lld", info->run_time_ns, info->run_cnt); printf("\n"); +} + +static void print_prog_plain(struct bpf_prog_info *info, int fd) +{ + char *memlock; + + print_prog_header_plain(info); if (info->load_time) { char buf[32]; @@ -349,6 +413,40 @@ static int show_prog(int fd) return 0; } +static int do_show_subset(int argc, char **argv) +{ + int *fds = NULL; + int nb_fds, i; + int err = -1; + + fds = malloc(sizeof(int)); + if (!fds) { + p_err("mem alloc failed"); + return -1; + } + nb_fds = prog_parse_fds(&argc, &argv, &fds); + if (nb_fds < 1) + goto exit_free; + + if (json_output && nb_fds > 1) + jsonw_start_array(json_wtr); /* root array */ + for (i = 0; i < nb_fds; i++) { + err = show_prog(fds[i]); + if (err) { + for (; i < nb_fds; i++) + close(fds[i]); + break; + } + close(fds[i]); + } + if (json_output && nb_fds > 1) + jsonw_end_array(json_wtr); /* root array */ + +exit_free: + free(fds); + return err; +} + static int do_show(int argc, char **argv) { __u32 id = 0; @@ -358,15 +456,8 @@ static int do_show(int argc, char **argv) if (show_pinned) build_pinned_obj_table(&prog_table, BPF_OBJ_PROG); - if (argc == 2) { - fd = prog_parse_fd(&argc, &argv); - if (fd < 0) - return -1; - - err = show_prog(fd); - close(fd); - return err; - } + if (argc == 2) + return do_show_subset(argc, argv); if (argc) return BAD_ARG(); @@ -408,101 +499,32 @@ static int do_show(int argc, char **argv) return err; } -static int do_dump(int argc, char **argv) +static int +prog_dump(struct bpf_prog_info *info, enum dump_mode mode, + char *filepath, bool opcodes, bool visual, bool linum) { - struct bpf_prog_info_linear *info_linear; struct bpf_prog_linfo *prog_linfo = NULL; - enum {DUMP_JITED, DUMP_XLATED} mode; const char *disasm_opt = NULL; - struct bpf_prog_info *info; struct dump_data dd = {}; void *func_info = NULL; struct btf *btf = NULL; - char *filepath = NULL; - bool opcodes = false; - bool visual = false; char func_sig[1024]; unsigned char *buf; - bool linum = false; __u32 member_len; - __u64 arrays; ssize_t n; int fd; - if (is_prefix(*argv, "jited")) { - if (disasm_init()) - return -1; - mode = DUMP_JITED; - } else if (is_prefix(*argv, "xlated")) { - mode = DUMP_XLATED; - } else { - p_err("expected 'xlated' or 'jited', got: %s", *argv); - return -1; - } - NEXT_ARG(); - - if (argc < 2) - usage(); - - fd = prog_parse_fd(&argc, &argv); - if (fd < 0) - return -1; - - if (is_prefix(*argv, "file")) { - NEXT_ARG(); - if (!argc) { - p_err("expected file path"); - return -1; - } - - filepath = *argv; - NEXT_ARG(); - } else if (is_prefix(*argv, "opcodes")) { - opcodes = true; - NEXT_ARG(); - } else if (is_prefix(*argv, "visual")) { - visual = true; - NEXT_ARG(); - } else if (is_prefix(*argv, "linum")) { - linum = true; - NEXT_ARG(); - } - - if (argc) { - usage(); - return -1; - } - - if (mode == DUMP_JITED) - arrays = 1UL << BPF_PROG_INFO_JITED_INSNS; - else - arrays = 1UL << BPF_PROG_INFO_XLATED_INSNS; - - arrays |= 1UL << BPF_PROG_INFO_JITED_KSYMS; - arrays |= 1UL << BPF_PROG_INFO_JITED_FUNC_LENS; - arrays |= 1UL << BPF_PROG_INFO_FUNC_INFO; - arrays |= 1UL << BPF_PROG_INFO_LINE_INFO; - arrays |= 1UL << BPF_PROG_INFO_JITED_LINE_INFO; - - info_linear = bpf_program__get_prog_info_linear(fd, arrays); - close(fd); - if (IS_ERR_OR_NULL(info_linear)) { - p_err("can't get prog info: %s", strerror(errno)); - return -1; - } - - info = &info_linear->info; if (mode == DUMP_JITED) { if (info->jited_prog_len == 0) { p_info("no instructions returned"); - goto err_free; + return -1; } buf = (unsigned char *)(info->jited_prog_insns); member_len = info->jited_prog_len; } else { /* DUMP_XLATED */ if (info->xlated_prog_len == 0) { p_err("error retrieving insn dump: kernel.kptr_restrict set?"); - goto err_free; + return -1; } buf = (unsigned char *)info->xlated_prog_insns; member_len = info->xlated_prog_len; @@ -510,7 +532,7 @@ static int do_dump(int argc, char **argv) if (info->btf_id && btf__get_from_id(info->btf_id, &btf)) { p_err("failed to get btf"); - goto err_free; + return -1; } func_info = (void *)info->func_info; @@ -526,7 +548,7 @@ static int do_dump(int argc, char **argv) if (fd < 0) { p_err("can't open file %s: %s", filepath, strerror(errno)); - goto err_free; + return -1; } n = write(fd, buf, member_len); @@ -534,7 +556,7 @@ static int do_dump(int argc, char **argv) if (n != member_len) { p_err("error writing output file: %s", n < 0 ? strerror(errno) : "short write"); - goto err_free; + return -1; } if (json_output) @@ -548,7 +570,7 @@ static int do_dump(int argc, char **argv) info->netns_ino, &disasm_opt); if (!name) - goto err_free; + return -1; } if (info->nr_jited_func_lens && info->jited_func_lens) { @@ -643,12 +665,130 @@ static int do_dump(int argc, char **argv) kernel_syms_destroy(&dd); } - free(info_linear); return 0; +} -err_free: - free(info_linear); - return -1; +static int do_dump(int argc, char **argv) +{ + struct bpf_prog_info_linear *info_linear; + char *filepath = NULL; + bool opcodes = false; + bool visual = false; + enum dump_mode mode; + bool linum = false; + int *fds = NULL; + int nb_fds, i = 0; + int err = -1; + __u64 arrays; + + if (is_prefix(*argv, "jited")) { + if (disasm_init()) + return -1; + mode = DUMP_JITED; + } else if (is_prefix(*argv, "xlated")) { + mode = DUMP_XLATED; + } else { + p_err("expected 'xlated' or 'jited', got: %s", *argv); + return -1; + } + NEXT_ARG(); + + if (argc < 2) + usage(); + + fds = malloc(sizeof(int)); + if (!fds) { + p_err("mem alloc failed"); + return -1; + } + nb_fds = prog_parse_fds(&argc, &argv, &fds); + if (nb_fds < 1) + goto exit_free; + + if (is_prefix(*argv, "file")) { + NEXT_ARG(); + if (!argc) { + p_err("expected file path"); + goto exit_close; + } + if (nb_fds > 1) { + p_err("several programs matched"); + goto exit_close; + } + + filepath = *argv; + NEXT_ARG(); + } else if (is_prefix(*argv, "opcodes")) { + opcodes = true; + NEXT_ARG(); + } else if (is_prefix(*argv, "visual")) { + if (nb_fds > 1) { + p_err("several programs matched"); + goto exit_close; + } + + visual = true; + NEXT_ARG(); + } else if (is_prefix(*argv, "linum")) { + linum = true; + NEXT_ARG(); + } + + if (argc) { + usage(); + goto exit_close; + } + + if (mode == DUMP_JITED) + arrays = 1UL << BPF_PROG_INFO_JITED_INSNS; + else + arrays = 1UL << BPF_PROG_INFO_XLATED_INSNS; + + arrays |= 1UL << BPF_PROG_INFO_JITED_KSYMS; + arrays |= 1UL << BPF_PROG_INFO_JITED_FUNC_LENS; + arrays |= 1UL << BPF_PROG_INFO_FUNC_INFO; + arrays |= 1UL << BPF_PROG_INFO_LINE_INFO; + arrays |= 1UL << BPF_PROG_INFO_JITED_LINE_INFO; + + if (json_output && nb_fds > 1) + jsonw_start_array(json_wtr); /* root array */ + for (i = 0; i < nb_fds; i++) { + info_linear = bpf_program__get_prog_info_linear(fds[i], arrays); + if (IS_ERR_OR_NULL(info_linear)) { + p_err("can't get prog info: %s", strerror(errno)); + break; + } + + if (json_output && nb_fds > 1) { + jsonw_start_object(json_wtr); /* prog object */ + print_prog_header_json(&info_linear->info); + jsonw_name(json_wtr, "insns"); + } else if (nb_fds > 1) { + print_prog_header_plain(&info_linear->info); + } + + err = prog_dump(&info_linear->info, mode, filepath, opcodes, + visual, linum); + + if (json_output && nb_fds > 1) + jsonw_end_object(json_wtr); /* prog object */ + else if (i != nb_fds - 1 && nb_fds > 1) + printf("\n"); + + free(info_linear); + if (err) + break; + close(fds[i]); + } + if (json_output && nb_fds > 1) + jsonw_end_array(json_wtr); /* root array */ + +exit_close: + for (; i < nb_fds; i++) + close(fds[i]); +exit_free: + free(fds); + return err; } static int do_pin(int argc, char **argv) -- GitLab From a7d22ca2a483d6c69c0791954447464297315ffa Mon Sep 17 00:00:00 2001 From: Paul Chaignon Date: Fri, 13 Dec 2019 20:10:17 +0100 Subject: [PATCH 0670/1437] bpftool: Match programs by name When working with frequently modified BPF programs, both the ID and the tag may change. bpftool currently doesn't provide a "stable" way to match such programs. This patch implements lookup by name for programs. The show and dump commands will return all programs with the given name, whereas other commands will error out if several programs have the same name. Signed-off-by: Paul Chaignon Signed-off-by: Alexei Starovoitov Reviewed-by: Quentin Monnet Link: https://lore.kernel.org/bpf/b5fc1a5dcfaeb5f16fc80295cdaa606dd2d91534.1576263640.git.paul.chaignon@gmail.com --- .../bpf/bpftool/Documentation/bpftool-map.rst | 2 +- .../bpftool/Documentation/bpftool-prog.rst | 12 +++++----- tools/bpf/bpftool/bash-completion/bpftool | 22 ++++++++++++++----- tools/bpf/bpftool/main.h | 2 +- tools/bpf/bpftool/prog.c | 22 +++++++++++++++---- 5 files changed, 42 insertions(+), 18 deletions(-) diff --git a/tools/bpf/bpftool/Documentation/bpftool-map.rst b/tools/bpf/bpftool/Documentation/bpftool-map.rst index 1c0f7146aab0a..90d848b5b7d35 100644 --- a/tools/bpf/bpftool/Documentation/bpftool-map.rst +++ b/tools/bpf/bpftool/Documentation/bpftool-map.rst @@ -41,7 +41,7 @@ MAP COMMANDS | | *MAP* := { **id** *MAP_ID* | **pinned** *FILE* } | *DATA* := { [**hex**] *BYTES* } -| *PROG* := { **id** *PROG_ID* | **pinned** *FILE* | **tag** *PROG_TAG* } +| *PROG* := { **id** *PROG_ID* | **pinned** *FILE* | **tag** *PROG_TAG* | **name** *PROG_NAME* } | *VALUE* := { *DATA* | *MAP* | *PROG* } | *UPDATE_FLAGS* := { **any** | **exist** | **noexist** } | *TYPE* := { **hash** | **array** | **prog_array** | **perf_event_array** | **percpu_hash** diff --git a/tools/bpf/bpftool/Documentation/bpftool-prog.rst b/tools/bpf/bpftool/Documentation/bpftool-prog.rst index d377d0cb79231..64ddf8a4c518b 100644 --- a/tools/bpf/bpftool/Documentation/bpftool-prog.rst +++ b/tools/bpf/bpftool/Documentation/bpftool-prog.rst @@ -33,7 +33,7 @@ PROG COMMANDS | **bpftool** **prog help** | | *MAP* := { **id** *MAP_ID* | **pinned** *FILE* } -| *PROG* := { **id** *PROG_ID* | **pinned** *FILE* | **tag** *PROG_TAG* } +| *PROG* := { **id** *PROG_ID* | **pinned** *FILE* | **tag** *PROG_TAG* | **name** *PROG_NAME* } | *TYPE* := { | **socket** | **kprobe** | **kretprobe** | **classifier** | **action** | | **tracepoint** | **raw_tracepoint** | **xdp** | **perf_event** | **cgroup/skb** | @@ -55,8 +55,8 @@ DESCRIPTION Show information about loaded programs. If *PROG* is specified show information only about given programs, otherwise list all programs currently loaded on the system. - In case of **tag**, *PROG* may match several programs which - will all be shown. + In case of **tag** or **name**, *PROG* may match several + programs which will all be shown. Output will start with program ID followed by program type and zero or more named attributes (depending on kernel version). @@ -75,9 +75,9 @@ DESCRIPTION output in human-readable format. In this case, **opcodes** controls if raw opcodes should be printed as well. - In case of **tag**, *PROG* may match several programs which - will all be dumped. However, if **file** or **visual** is - specified, *PROG* must match a single program. + In case of **tag** or **name**, *PROG* may match several + programs which will all be dumped. However, if **file** or + **visual** is specified, *PROG* must match a single program. If **file** is specified, the binary image will instead be written to *FILE*. diff --git a/tools/bpf/bpftool/bash-completion/bpftool b/tools/bpf/bpftool/bash-completion/bpftool index 70493a6da2067..05b5be4a6ef96 100644 --- a/tools/bpf/bpftool/bash-completion/bpftool +++ b/tools/bpf/bpftool/bash-completion/bpftool @@ -71,6 +71,12 @@ _bpftool_get_prog_tags() command sed -n 's/.*"tag": "\(.*\)",$/\1/p' )" -- "$cur" ) ) } +_bpftool_get_prog_names() +{ + COMPREPLY+=( $( compgen -W "$( bpftool -jp prog 2>&1 | \ + command sed -n 's/.*"name": "\(.*\)",$/\1/p' )" -- "$cur" ) ) +} + _bpftool_get_btf_ids() { COMPREPLY+=( $( compgen -W "$( bpftool -jp btf 2>&1 | \ @@ -201,6 +207,10 @@ _bpftool() _bpftool_get_prog_tags return 0 ;; + name) + _bpftool_get_prog_names + return 0 + ;; dev) _sysfs_get_netdevs return 0 @@ -263,7 +273,7 @@ _bpftool() ;; esac - local PROG_TYPE='id pinned tag' + local PROG_TYPE='id pinned tag name' local MAP_TYPE='id pinned' case $command in show|list) @@ -559,7 +569,7 @@ _bpftool() return 0 ;; prog_array) - local PROG_TYPE='id pinned tag' + local PROG_TYPE='id pinned tag name' COMPREPLY+=( $( compgen -W "$PROG_TYPE" \ -- "$cur" ) ) return 0 @@ -644,7 +654,7 @@ _bpftool() esac ;; btf) - local PROG_TYPE='id pinned tag' + local PROG_TYPE='id pinned tag name' local MAP_TYPE='id pinned' case $command in dump) @@ -735,7 +745,7 @@ _bpftool() connect6 sendmsg4 sendmsg6 recvmsg4 recvmsg6 sysctl \ getsockopt setsockopt' local ATTACH_FLAGS='multi override' - local PROG_TYPE='id pinned tag' + local PROG_TYPE='id pinned tag name' case $prev in $command) _filedir @@ -760,7 +770,7 @@ _bpftool() elif [[ "$command" == "attach" ]]; then # We have an attach type on the command line, # but it is not the previous word, or - # "id|pinned|tag" (we already checked for + # "id|pinned|tag|name" (we already checked for # that). This should only leave the case when # we need attach flags for "attach" commamnd. _bpftool_one_of_list "$ATTACH_FLAGS" @@ -786,7 +796,7 @@ _bpftool() esac ;; net) - local PROG_TYPE='id pinned tag' + local PROG_TYPE='id pinned tag name' local ATTACH_TYPES='xdp xdpgeneric xdpdrv xdpoffload' case $command in show|list) diff --git a/tools/bpf/bpftool/main.h b/tools/bpf/bpftool/main.h index 2899095f8254f..a7ead7bb94470 100644 --- a/tools/bpf/bpftool/main.h +++ b/tools/bpf/bpftool/main.h @@ -42,7 +42,7 @@ #define BPF_TAG_FMT "%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx" #define HELP_SPEC_PROGRAM \ - "PROG := { id PROG_ID | pinned FILE | tag PROG_TAG }" + "PROG := { id PROG_ID | pinned FILE | tag PROG_TAG | name PROG_NAME }" #define HELP_SPEC_OPTIONS \ "OPTIONS := { {-j|--json} [{-p|--pretty}] | {-f|--bpffs} |\n" \ "\t {-m|--mapcompat} | {-n|--nomount} }" diff --git a/tools/bpf/bpftool/prog.c b/tools/bpf/bpftool/prog.c index 37948c47aabda..2221bae037f16 100644 --- a/tools/bpf/bpftool/prog.c +++ b/tools/bpf/bpftool/prog.c @@ -82,7 +82,7 @@ static void print_boot_time(__u64 nsecs, char *buf, unsigned int size) strftime(buf, size, "%FT%T%z", &load_tm); } -static int prog_fd_by_tag(unsigned char *tag, int **fds) +static int prog_fd_by_nametag(void *nametag, int **fds, bool tag) { unsigned int id = 0; int fd, nb_fds = 0; @@ -116,7 +116,8 @@ static int prog_fd_by_tag(unsigned char *tag, int **fds) goto err_close_fd; } - if (memcmp(tag, info.tag, BPF_TAG_SIZE)) { + if ((tag && memcmp(nametag, info.tag, BPF_TAG_SIZE)) || + (!tag && strncmp(nametag, info.name, BPF_OBJ_NAME_LEN))) { close(fd); continue; } @@ -174,7 +175,20 @@ static int prog_parse_fds(int *argc, char ***argv, int **fds) } NEXT_ARGP(); - return prog_fd_by_tag(tag, fds); + return prog_fd_by_nametag(tag, fds, true); + } else if (is_prefix(**argv, "name")) { + char *name; + + NEXT_ARGP(); + + name = **argv; + if (strlen(name) > BPF_OBJ_NAME_LEN - 1) { + p_err("can't parse name"); + return -1; + } + NEXT_ARGP(); + + return prog_fd_by_nametag(name, fds, false); } else if (is_prefix(**argv, "pinned")) { char *path; @@ -189,7 +203,7 @@ static int prog_parse_fds(int *argc, char ***argv, int **fds) return 1; } - p_err("expected 'id', 'tag' or 'pinned', got: '%s'?", **argv); + p_err("expected 'id', 'tag', 'name' or 'pinned', got: '%s'?", **argv); return -1; } -- GitLab From 99f9863a0c45f4e87cb99593015090fdc9f44398 Mon Sep 17 00:00:00 2001 From: Paul Chaignon Date: Fri, 13 Dec 2019 20:10:37 +0100 Subject: [PATCH 0671/1437] bpftool: Match maps by name This patch implements lookup by name for maps and changes the behavior of lookups by tag to be consistent with prog subcommands. Similarly to program subcommands, the show and dump commands will return all maps with the given name (or tag), whereas other commands will error out if several maps have the same name (resp. tag). When a map has BTF info, it is dumped in JSON with available BTF info. This patch requires that all matched maps have BTF info before switching the output format to JSON. Signed-off-by: Paul Chaignon Signed-off-by: Alexei Starovoitov Link: https://lore.kernel.org/bpf/8de1c9f273860b3ea1680502928f4da2336b853e.1576263640.git.paul.chaignon@gmail.com --- .../bpf/bpftool/Documentation/bpftool-map.rst | 10 +- tools/bpf/bpftool/bash-completion/bpftool | 131 +++++- tools/bpf/bpftool/main.h | 2 +- tools/bpf/bpftool/map.c | 384 ++++++++++++++---- 4 files changed, 439 insertions(+), 88 deletions(-) diff --git a/tools/bpf/bpftool/Documentation/bpftool-map.rst b/tools/bpf/bpftool/Documentation/bpftool-map.rst index 90d848b5b7d35..cdeae8ae90ba5 100644 --- a/tools/bpf/bpftool/Documentation/bpftool-map.rst +++ b/tools/bpf/bpftool/Documentation/bpftool-map.rst @@ -39,7 +39,7 @@ MAP COMMANDS | **bpftool** **map freeze** *MAP* | **bpftool** **map help** | -| *MAP* := { **id** *MAP_ID* | **pinned** *FILE* } +| *MAP* := { **id** *MAP_ID* | **pinned** *FILE* | **name** *MAP_NAME* } | *DATA* := { [**hex**] *BYTES* } | *PROG* := { **id** *PROG_ID* | **pinned** *FILE* | **tag** *PROG_TAG* | **name** *PROG_NAME* } | *VALUE* := { *DATA* | *MAP* | *PROG* } @@ -55,8 +55,9 @@ DESCRIPTION =========== **bpftool map { show | list }** [*MAP*] Show information about loaded maps. If *MAP* is specified - show information only about given map, otherwise list all - maps currently loaded on the system. + show information only about given maps, otherwise list all + maps currently loaded on the system. In case of **name**, + *MAP* may match several maps which will all be shown. Output will start with map ID followed by map type and zero or more named attributes (depending on kernel version). @@ -66,7 +67,8 @@ DESCRIPTION as *FILE*. **bpftool map dump** *MAP* - Dump all entries in a given *MAP*. + Dump all entries in a given *MAP*. In case of **name**, + *MAP* may match several maps which will all be dumped. **bpftool map update** *MAP* [**key** *DATA*] [**value** *VALUE*] [*UPDATE_FLAGS*] Update map entry for a given *KEY*. diff --git a/tools/bpf/bpftool/bash-completion/bpftool b/tools/bpf/bpftool/bash-completion/bpftool index 05b5be4a6ef96..21c676a1eeb1a 100644 --- a/tools/bpf/bpftool/bash-completion/bpftool +++ b/tools/bpf/bpftool/bash-completion/bpftool @@ -59,6 +59,21 @@ _bpftool_get_map_ids_for_type() command sed -n 's/.*"id": \(.*\),$/\1/p' )" -- "$cur" ) ) } +_bpftool_get_map_names() +{ + COMPREPLY+=( $( compgen -W "$( bpftool -jp map 2>&1 | \ + command sed -n 's/.*"name": \(.*\),$/\1/p' )" -- "$cur" ) ) +} + +# Takes map type and adds matching map names to the list of suggestions. +_bpftool_get_map_names_for_type() +{ + local type="$1" + COMPREPLY+=( $( compgen -W "$( bpftool -jp map 2>&1 | \ + command grep -C2 "$type" | \ + command sed -n 's/.*"name": \(.*\),$/\1/p' )" -- "$cur" ) ) +} + _bpftool_get_prog_ids() { COMPREPLY+=( $( compgen -W "$( bpftool -jp prog 2>&1 | \ @@ -186,6 +201,52 @@ _bpftool_map_update_get_id() esac } +_bpftool_map_update_get_name() +{ + local command="$1" + + # Is it the map to update, or a map to insert into the map to update? + # Search for "value" keyword. + local idx value + for (( idx=7; idx < ${#words[@]}-1; idx++ )); do + if [[ ${words[idx]} == "value" ]]; then + value=1 + break + fi + done + if [[ $value -eq 0 ]]; then + case "$command" in + push) + _bpftool_get_map_names_for_type stack + ;; + enqueue) + _bpftool_get_map_names_for_type queue + ;; + *) + _bpftool_get_map_names + ;; + esac + return 0 + fi + + # Name to complete is for a value. It can be either prog name or map name. This + # depends on the type of the map to update. + local type=$(_bpftool_map_guess_map_type) + case $type in + array_of_maps|hash_of_maps) + _bpftool_get_map_names + return 0 + ;; + prog_array) + _bpftool_get_prog_names + return 0 + ;; + *) + return 0 + ;; + esac +} + _bpftool() { local cur prev words objword @@ -207,10 +268,6 @@ _bpftool() _bpftool_get_prog_tags return 0 ;; - name) - _bpftool_get_prog_names - return 0 - ;; dev) _sysfs_get_netdevs return 0 @@ -261,7 +318,8 @@ _bpftool() # Completion depends on object and command in use case $object in prog) - # Complete id, only for subcommands that use prog (but no map) ids + # Complete id and name, only for subcommands that use prog (but no + # map) ids/names. case $command in show|list|dump|pin) case $prev in @@ -269,12 +327,16 @@ _bpftool() _bpftool_get_prog_ids return 0 ;; + name) + _bpftool_get_prog_names + return 0 + ;; esac ;; esac local PROG_TYPE='id pinned tag name' - local MAP_TYPE='id pinned' + local MAP_TYPE='id pinned name' case $command in show|list) [[ $prev != "$command" ]] && return 0 @@ -325,6 +387,9 @@ _bpftool() id) _bpftool_get_prog_ids ;; + name) + _bpftool_get_map_names + ;; pinned) _filedir ;; @@ -345,6 +410,9 @@ _bpftool() id) _bpftool_get_map_ids ;; + name) + _bpftool_get_map_names + ;; pinned) _filedir ;; @@ -409,6 +477,10 @@ _bpftool() _bpftool_get_map_ids return 0 ;; + name) + _bpftool_get_map_names + return 0 + ;; pinned|pinmaps) _filedir return 0 @@ -457,7 +529,7 @@ _bpftool() esac ;; map) - local MAP_TYPE='id pinned' + local MAP_TYPE='id pinned name' case $command in show|list|dump|peek|pop|dequeue|freeze) case $prev in @@ -483,6 +555,24 @@ _bpftool() esac return 0 ;; + name) + case "$command" in + peek) + _bpftool_get_map_names_for_type stack + _bpftool_get_map_names_for_type queue + ;; + pop) + _bpftool_get_map_names_for_type stack + ;; + dequeue) + _bpftool_get_map_names_for_type queue + ;; + *) + _bpftool_get_map_names + ;; + esac + return 0 + ;; *) return 0 ;; @@ -530,6 +620,10 @@ _bpftool() _bpftool_get_map_ids return 0 ;; + name) + _bpftool_get_map_names + return 0 + ;; key) COMPREPLY+=( $( compgen -W 'hex' -- "$cur" ) ) ;; @@ -555,6 +649,10 @@ _bpftool() _bpftool_map_update_get_id $command return 0 ;; + name) + _bpftool_map_update_get_name $command + return 0 + ;; key) COMPREPLY+=( $( compgen -W 'hex' -- "$cur" ) ) ;; @@ -563,7 +661,7 @@ _bpftool() # map, depending on the type of the map to update. case "$(_bpftool_map_guess_map_type)" in array_of_maps|hash_of_maps) - local MAP_TYPE='id pinned' + local MAP_TYPE='id pinned name' COMPREPLY+=( $( compgen -W "$MAP_TYPE" \ -- "$cur" ) ) return 0 @@ -631,6 +729,10 @@ _bpftool() _bpftool_get_map_ids_for_type perf_event_array return 0 ;; + name) + _bpftool_get_map_names_for_type perf_event_array + return 0 + ;; cpu) return 0 ;; @@ -655,7 +757,7 @@ _bpftool() ;; btf) local PROG_TYPE='id pinned tag name' - local MAP_TYPE='id pinned' + local MAP_TYPE='id pinned name' case $command in dump) case $prev in @@ -686,6 +788,17 @@ _bpftool() esac return 0 ;; + name) + case $pprev in + prog) + _bpftool_get_prog_names + ;; + map) + _bpftool_get_map_names + ;; + esac + return 0 + ;; format) COMPREPLY=( $( compgen -W "c raw" -- "$cur" ) ) ;; diff --git a/tools/bpf/bpftool/main.h b/tools/bpf/bpftool/main.h index a7ead7bb94470..81890f4c8ccad 100644 --- a/tools/bpf/bpftool/main.h +++ b/tools/bpf/bpftool/main.h @@ -47,7 +47,7 @@ "OPTIONS := { {-j|--json} [{-p|--pretty}] | {-f|--bpffs} |\n" \ "\t {-m|--mapcompat} | {-n|--nomount} }" #define HELP_SPEC_MAP \ - "MAP := { id MAP_ID | pinned FILE }" + "MAP := { id MAP_ID | pinned FILE | name MAP_NAME }" static const char * const prog_type_name[] = { [BPF_PROG_TYPE_UNSPEC] = "unspec", diff --git a/tools/bpf/bpftool/map.c b/tools/bpf/bpftool/map.c index de61d73b9030b..c01f76fa68761 100644 --- a/tools/bpf/bpftool/map.c +++ b/tools/bpf/bpftool/map.c @@ -91,10 +91,66 @@ static void *alloc_value(struct bpf_map_info *info) return malloc(info->value_size); } -int map_parse_fd(int *argc, char ***argv) +static int map_fd_by_name(char *name, int **fds) { - int fd; + unsigned int id = 0; + int fd, nb_fds = 0; + void *tmp; + int err; + + while (true) { + struct bpf_map_info info = {}; + __u32 len = sizeof(info); + + err = bpf_map_get_next_id(id, &id); + if (err) { + if (errno != ENOENT) { + p_err("%s", strerror(errno)); + goto err_close_fds; + } + return nb_fds; + } + + fd = bpf_map_get_fd_by_id(id); + if (fd < 0) { + p_err("can't get map by id (%u): %s", + id, strerror(errno)); + goto err_close_fds; + } + + err = bpf_obj_get_info_by_fd(fd, &info, &len); + if (err) { + p_err("can't get map info (%u): %s", + id, strerror(errno)); + goto err_close_fd; + } + + if (strncmp(name, info.name, BPF_OBJ_NAME_LEN)) { + close(fd); + continue; + } + + if (nb_fds > 0) { + tmp = realloc(*fds, (nb_fds + 1) * sizeof(int)); + if (!tmp) { + p_err("failed to realloc"); + goto err_close_fd; + } + *fds = tmp; + } + (*fds)[nb_fds++] = fd; + } + +err_close_fd: + close(fd); +err_close_fds: + while (--nb_fds >= 0) + close((*fds)[nb_fds]); + return -1; +} +static int map_parse_fds(int *argc, char ***argv, int **fds) +{ if (is_prefix(**argv, "id")) { unsigned int id; char *endptr; @@ -108,10 +164,25 @@ int map_parse_fd(int *argc, char ***argv) } NEXT_ARGP(); - fd = bpf_map_get_fd_by_id(id); - if (fd < 0) + (*fds)[0] = bpf_map_get_fd_by_id(id); + if ((*fds)[0] < 0) { p_err("get map by id (%u): %s", id, strerror(errno)); - return fd; + return -1; + } + return 1; + } else if (is_prefix(**argv, "name")) { + char *name; + + NEXT_ARGP(); + + name = **argv; + if (strlen(name) > BPF_OBJ_NAME_LEN - 1) { + p_err("can't parse name"); + return -1; + } + NEXT_ARGP(); + + return map_fd_by_name(name, fds); } else if (is_prefix(**argv, "pinned")) { char *path; @@ -120,13 +191,43 @@ int map_parse_fd(int *argc, char ***argv) path = **argv; NEXT_ARGP(); - return open_obj_pinned_any(path, BPF_OBJ_MAP); + (*fds)[0] = open_obj_pinned_any(path, BPF_OBJ_MAP); + if ((*fds)[0] < 0) + return -1; + return 1; } - p_err("expected 'id' or 'pinned', got: '%s'?", **argv); + p_err("expected 'id', 'name' or 'pinned', got: '%s'?", **argv); return -1; } +int map_parse_fd(int *argc, char ***argv) +{ + int *fds = NULL; + int nb_fds, fd; + + fds = malloc(sizeof(int)); + if (!fds) { + p_err("mem alloc failed"); + return -1; + } + nb_fds = map_parse_fds(argc, argv, &fds); + if (nb_fds != 1) { + if (nb_fds > 1) { + p_err("several maps match this handle"); + while (nb_fds--) + close(fds[nb_fds]); + } + fd = -1; + goto exit_free; + } + + fd = fds[0]; +exit_free: + free(fds); + return fd; +} + int map_parse_fd_and_info(int *argc, char ***argv, void *info, __u32 *info_len) { int err; @@ -479,6 +580,21 @@ static int parse_elem(char **argv, struct bpf_map_info *info, return -1; } +static void show_map_header_json(struct bpf_map_info *info, json_writer_t *wtr) +{ + jsonw_uint_field(wtr, "id", info->id); + if (info->type < ARRAY_SIZE(map_type_name)) + jsonw_string_field(wtr, "type", map_type_name[info->type]); + else + jsonw_uint_field(wtr, "type", info->type); + + if (*info->name) + jsonw_string_field(wtr, "name", info->name); + + jsonw_name(wtr, "flags"); + jsonw_printf(wtr, "%d", info->map_flags); +} + static int show_map_close_json(int fd, struct bpf_map_info *info) { char *memlock, *frozen_str; @@ -489,18 +605,7 @@ static int show_map_close_json(int fd, struct bpf_map_info *info) jsonw_start_object(json_wtr); - jsonw_uint_field(json_wtr, "id", info->id); - if (info->type < ARRAY_SIZE(map_type_name)) - jsonw_string_field(json_wtr, "type", - map_type_name[info->type]); - else - jsonw_uint_field(json_wtr, "type", info->type); - - if (*info->name) - jsonw_string_field(json_wtr, "name", info->name); - - jsonw_name(json_wtr, "flags"); - jsonw_printf(json_wtr, "%d", info->map_flags); + show_map_header_json(info, json_wtr); print_dev_json(info->ifindex, info->netns_dev, info->netns_ino); @@ -561,14 +666,8 @@ static int show_map_close_json(int fd, struct bpf_map_info *info) return 0; } -static int show_map_close_plain(int fd, struct bpf_map_info *info) +static void show_map_header_plain(struct bpf_map_info *info) { - char *memlock, *frozen_str; - int frozen = 0; - - memlock = get_fdinfo(fd, "memlock"); - frozen_str = get_fdinfo(fd, "frozen"); - printf("%u: ", info->id); if (info->type < ARRAY_SIZE(map_type_name)) printf("%s ", map_type_name[info->type]); @@ -581,6 +680,17 @@ static int show_map_close_plain(int fd, struct bpf_map_info *info) printf("flags 0x%x", info->map_flags); print_dev_plain(info->ifindex, info->netns_dev, info->netns_ino); printf("\n"); +} + +static int show_map_close_plain(int fd, struct bpf_map_info *info) +{ + char *memlock, *frozen_str; + int frozen = 0; + + memlock = get_fdinfo(fd, "memlock"); + frozen_str = get_fdinfo(fd, "frozen"); + + show_map_header_plain(info); printf("\tkey %uB value %uB max_entries %u", info->key_size, info->value_size, info->max_entries); @@ -642,6 +752,50 @@ static int show_map_close_plain(int fd, struct bpf_map_info *info) return 0; } +static int do_show_subset(int argc, char **argv) +{ + struct bpf_map_info info = {}; + __u32 len = sizeof(info); + int *fds = NULL; + int nb_fds, i; + int err = -1; + + fds = malloc(sizeof(int)); + if (!fds) { + p_err("mem alloc failed"); + return -1; + } + nb_fds = map_parse_fds(&argc, &argv, &fds); + if (nb_fds < 1) + goto exit_free; + + if (json_output && nb_fds > 1) + jsonw_start_array(json_wtr); /* root array */ + for (i = 0; i < nb_fds; i++) { + err = bpf_obj_get_info_by_fd(fds[i], &info, &len); + if (err) { + p_err("can't get map info: %s", + strerror(errno)); + for (; i < nb_fds; i++) + close(fds[i]); + break; + } + + if (json_output) + show_map_close_json(fds[i], &info); + else + show_map_close_plain(fds[i], &info); + + close(fds[i]); + } + if (json_output && nb_fds > 1) + jsonw_end_array(json_wtr); /* root array */ + +exit_free: + free(fds); + return err; +} + static int do_show(int argc, char **argv) { struct bpf_map_info info = {}; @@ -653,16 +807,8 @@ static int do_show(int argc, char **argv) if (show_pinned) build_pinned_obj_table(&map_table, BPF_OBJ_MAP); - if (argc == 2) { - fd = map_parse_fd_and_info(&argc, &argv, &info, &len); - if (fd < 0) - return -1; - - if (json_output) - return show_map_close_json(fd, &info); - else - return show_map_close_plain(fd, &info); - } + if (argc == 2) + return do_show_subset(argc, argv); if (argc) return BAD_ARG(); @@ -765,26 +911,49 @@ static int dump_map_elem(int fd, void *key, void *value, return 0; } -static int do_dump(int argc, char **argv) +static int maps_have_btf(int *fds, int nb_fds) { struct bpf_map_info info = {}; - void *key, *value, *prev_key; - unsigned int num_elems = 0; __u32 len = sizeof(info); - json_writer_t *btf_wtr; struct btf *btf = NULL; - int err; - int fd; + int err, i; - if (argc != 2) - usage(); + for (i = 0; i < nb_fds; i++) { + err = bpf_obj_get_info_by_fd(fds[i], &info, &len); + if (err) { + p_err("can't get map info: %s", strerror(errno)); + goto err_close; + } - fd = map_parse_fd_and_info(&argc, &argv, &info, &len); - if (fd < 0) - return -1; + err = btf__get_from_id(info.btf_id, &btf); + if (err) { + p_err("failed to get btf"); + goto err_close; + } - key = malloc(info.key_size); - value = alloc_value(&info); + if (!btf) + return 0; + } + + return 1; + +err_close: + for (; i < nb_fds; i++) + close(fds[i]); + return -1; +} + +static int +map_dump(int fd, struct bpf_map_info *info, json_writer_t *wtr, + bool enable_btf, bool show_header) +{ + void *key, *value, *prev_key; + unsigned int num_elems = 0; + struct btf *btf = NULL; + int err; + + key = malloc(info->key_size); + value = alloc_value(info); if (!key || !value) { p_err("mem alloc failed"); err = -1; @@ -793,30 +962,32 @@ static int do_dump(int argc, char **argv) prev_key = NULL; - err = btf__get_from_id(info.btf_id, &btf); - if (err) { - p_err("failed to get btf"); - goto exit_free; + if (enable_btf) { + err = btf__get_from_id(info->btf_id, &btf); + if (err || !btf) { + /* enable_btf is true only if we've already checked + * that all maps have BTF information. + */ + p_err("failed to get btf"); + goto exit_free; + } } - if (json_output) - jsonw_start_array(json_wtr); - else - if (btf) { - btf_wtr = get_btf_writer(); - if (!btf_wtr) { - p_info("failed to create json writer for btf. falling back to plain output"); - btf__free(btf); - btf = NULL; - } else { - jsonw_start_array(btf_wtr); - } + if (wtr) { + if (show_header) { + jsonw_start_object(wtr); /* map object */ + show_map_header_json(info, wtr); + jsonw_name(wtr, "elements"); } + jsonw_start_array(wtr); /* elements */ + } else if (show_header) { + show_map_header_plain(info); + } - if (info.type == BPF_MAP_TYPE_REUSEPORT_SOCKARRAY && - info.value_size != 8) + if (info->type == BPF_MAP_TYPE_REUSEPORT_SOCKARRAY && + info->value_size != 8) p_info("Warning: cannot read values from %s map with value_size != 8", - map_type_name[info.type]); + map_type_name[info->type]); while (true) { err = bpf_map_get_next_key(fd, prev_key, key); if (err) { @@ -824,15 +995,14 @@ static int do_dump(int argc, char **argv) err = 0; break; } - num_elems += dump_map_elem(fd, key, value, &info, btf, btf_wtr); + num_elems += dump_map_elem(fd, key, value, info, btf, wtr); prev_key = key; } - if (json_output) - jsonw_end_array(json_wtr); - else if (btf) { - jsonw_end_array(btf_wtr); - jsonw_destroy(&btf_wtr); + if (wtr) { + jsonw_end_array(wtr); /* elements */ + if (show_header) + jsonw_end_object(wtr); /* map object */ } else { printf("Found %u element%s\n", num_elems, num_elems != 1 ? "s" : ""); @@ -847,6 +1017,72 @@ exit_free: return err; } +static int do_dump(int argc, char **argv) +{ + json_writer_t *wtr = NULL, *btf_wtr = NULL; + struct bpf_map_info info = {}; + int nb_fds, i = 0, btf = 0; + __u32 len = sizeof(info); + int *fds = NULL; + int err = -1; + + if (argc != 2) + usage(); + + fds = malloc(sizeof(int)); + if (!fds) { + p_err("mem alloc failed"); + return -1; + } + nb_fds = map_parse_fds(&argc, &argv, &fds); + if (nb_fds < 1) + goto exit_free; + + if (json_output) { + wtr = json_wtr; + } else { + btf = maps_have_btf(fds, nb_fds); + if (btf < 0) + goto exit_close; + if (btf) { + btf_wtr = get_btf_writer(); + if (btf_wtr) { + wtr = btf_wtr; + } else { + p_info("failed to create json writer for btf. falling back to plain output"); + btf = 0; + } + } + } + + if (wtr && nb_fds > 1) + jsonw_start_array(wtr); /* root array */ + for (i = 0; i < nb_fds; i++) { + if (bpf_obj_get_info_by_fd(fds[i], &info, &len)) { + p_err("can't get map info: %s", strerror(errno)); + break; + } + err = map_dump(fds[i], &info, wtr, btf, nb_fds > 1); + if (!wtr && i != nb_fds - 1) + printf("\n"); + + if (err) + break; + close(fds[i]); + } + if (wtr && nb_fds > 1) + jsonw_end_array(wtr); /* root array */ + + if (btf) + jsonw_destroy(&btf_wtr); +exit_close: + for (; i < nb_fds; i++) + close(fds[i]); +exit_free: + free(fds); + return err; +} + static int alloc_key_value(struct bpf_map_info *info, void **key, void **value) { *key = NULL; -- GitLab From aa915931ac3e53ccf371308e6750da510e3591dd Mon Sep 17 00:00:00 2001 From: Thadeu Lima de Souza Cascardo Date: Fri, 13 Dec 2019 07:11:14 -0300 Subject: [PATCH 0672/1437] libbpf: Fix readelf output parsing for Fedora Fedora binutils has been patched to show "other info" for a symbol at the end of the line. This was done in order to support unmaintained scripts that would break with the extra info. [1] [1] https://src.fedoraproject.org/rpms/binutils/c/b8265c46f7ddae23a792ee8306fbaaeacba83bf8 This in turn has been done to fix the build of ruby, because of checksec. [2] Thanks Michael Ellerman for the pointer. [2] https://bugzilla.redhat.com/show_bug.cgi?id=1479302 As libbpf Makefile is not unmaintained, we can simply deal with either output format, by just removing the "other info" field, as it always comes inside brackets. Fixes: 3464afdf11f9 (libbpf: Fix readelf output parsing on powerpc with recent binutils) Reported-by: Justin Forbes Signed-off-by: Thadeu Lima de Souza Cascardo Signed-off-by: Alexei Starovoitov Acked-by: Andrii Nakryiko Cc: Aurelien Jarno Link: https://lore.kernel.org/bpf/20191213101114.GA3986@calabresa --- tools/lib/bpf/Makefile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tools/lib/bpf/Makefile b/tools/lib/bpf/Makefile index defae23a0169d..23ae06c43d088 100644 --- a/tools/lib/bpf/Makefile +++ b/tools/lib/bpf/Makefile @@ -147,6 +147,7 @@ TAGS_PROG := $(if $(shell which etags 2>/dev/null),etags,ctags) GLOBAL_SYM_COUNT = $(shell readelf -s --wide $(BPF_IN_SHARED) | \ cut -d "@" -f1 | sed 's/_v[0-9]_[0-9]_[0-9].*//' | \ + sed 's/\[.*\]//' | \ awk '/GLOBAL/ && /DEFAULT/ && !/UND/ {print $$NF}' | \ sort -u | wc -l) VERSIONED_SYM_COUNT = $(shell readelf -s --wide $(OUTPUT)libbpf.so | \ @@ -213,6 +214,7 @@ check_abi: $(OUTPUT)libbpf.so "versioned in $(VERSION_SCRIPT)." >&2; \ readelf -s --wide $(BPF_IN_SHARED) | \ cut -d "@" -f1 | sed 's/_v[0-9]_[0-9]_[0-9].*//' | \ + sed 's/\[.*\]//' | \ awk '/GLOBAL/ && /DEFAULT/ && !/UND/ {print $$NF}'| \ sort -u > $(OUTPUT)libbpf_global_syms.tmp; \ readelf -s --wide $(OUTPUT)libbpf.so | \ -- GitLab From 29115cef85781057e78f0c967979edd40b16c209 Mon Sep 17 00:00:00 2001 From: Ursula Braun Date: Thu, 12 Dec 2019 22:35:41 +0100 Subject: [PATCH 0673/1437] net/smc: shorten lgr_cnt initialization Save a line of code by making use of ATOMIC_INIT() for lgr_cnt. Suggested-by: David S. Miller Signed-off-by: Ursula Braun Signed-off-by: Karsten Graul Signed-off-by: Jakub Kicinski --- net/smc/smc_core.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/net/smc/smc_core.c b/net/smc/smc_core.c index bb92c7c6214c3..c16b8d3e200b6 100644 --- a/net/smc/smc_core.c +++ b/net/smc/smc_core.c @@ -41,7 +41,7 @@ static struct smc_lgr_list smc_lgr_list = { /* established link groups */ .num = 0, }; -static atomic_t lgr_cnt; /* number of existing link groups */ +static atomic_t lgr_cnt = ATOMIC_INIT(0); /* number of existing link groups */ static DECLARE_WAIT_QUEUE_HEAD(lgrs_deleted); static void smc_buf_free(struct smc_link_group *lgr, bool is_rmb, @@ -1297,7 +1297,6 @@ static struct notifier_block smc_reboot_notifier = { int __init smc_core_init(void) { - atomic_set(&lgr_cnt, 0); return register_reboot_notifier(&smc_reboot_notifier); } -- GitLab From 86434744fedf0cfe07a9eee3f4632c0e25c1d136 Mon Sep 17 00:00:00 2001 From: Ursula Braun Date: Thu, 12 Dec 2019 22:35:58 +0100 Subject: [PATCH 0674/1437] net/smc: add fallback check to connect() FASTOPEN setsockopt() or sendmsg() may switch the SMC socket to fallback mode. Once fallback mode is active, the native TCP socket functions are called. Nevertheless there is a small race window, when FASTOPEN setsockopt/sendmsg runs in parallel to a connect(), and switch the socket into fallback mode before connect() takes the sock lock. Make sure the SMC-specific connect setup is omitted in this case. This way a syzbot-reported refcount problem is fixed, triggered by different threads running non-blocking connect() and FASTOPEN_KEY setsockopt. Reported-by: syzbot+96d3f9ff6a86d37e44c8@syzkaller.appspotmail.com Fixes: 6d6dd528d5af ("net/smc: fix refcount non-blocking connect() -part 2") Signed-off-by: Ursula Braun Signed-off-by: Karsten Graul Signed-off-by: Jakub Kicinski --- net/smc/af_smc.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/net/smc/af_smc.c b/net/smc/af_smc.c index b997072c72e50..cee5bf4a9bb95 100644 --- a/net/smc/af_smc.c +++ b/net/smc/af_smc.c @@ -857,6 +857,8 @@ static int smc_connect(struct socket *sock, struct sockaddr *addr, goto out; sock_hold(&smc->sk); /* sock put in passive closing */ + if (smc->use_fallback) + goto out; if (flags & O_NONBLOCK) { if (schedule_work(&smc->connect_work)) smc->connect_nonblock = 1; @@ -1721,8 +1723,6 @@ static int smc_setsockopt(struct socket *sock, int level, int optname, sk->sk_err = smc->clcsock->sk->sk_err; sk->sk_error_report(sk); } - if (rc) - return rc; if (optlen < sizeof(int)) return -EINVAL; @@ -1730,6 +1730,8 @@ static int smc_setsockopt(struct socket *sock, int level, int optname, return -EFAULT; lock_sock(sk); + if (rc || smc->use_fallback) + goto out; switch (optname) { case TCP_ULP: case TCP_FASTOPEN: @@ -1741,15 +1743,14 @@ static int smc_setsockopt(struct socket *sock, int level, int optname, smc_switch_to_fallback(smc); smc->fallback_rsn = SMC_CLC_DECL_OPTUNSUPP; } else { - if (!smc->use_fallback) - rc = -EINVAL; + rc = -EINVAL; } break; case TCP_NODELAY: if (sk->sk_state != SMC_INIT && sk->sk_state != SMC_LISTEN && sk->sk_state != SMC_CLOSED) { - if (val && !smc->use_fallback) + if (val) mod_delayed_work(system_wq, &smc->conn.tx_work, 0); } @@ -1758,7 +1759,7 @@ static int smc_setsockopt(struct socket *sock, int level, int optname, if (sk->sk_state != SMC_INIT && sk->sk_state != SMC_LISTEN && sk->sk_state != SMC_CLOSED) { - if (!val && !smc->use_fallback) + if (!val) mod_delayed_work(system_wq, &smc->conn.tx_work, 0); } @@ -1769,6 +1770,7 @@ static int smc_setsockopt(struct socket *sock, int level, int optname, default: break; } +out: release_sock(sk); return rc; -- GitLab From 479a0d1376f6d97c60871442911f1394d4446a25 Mon Sep 17 00:00:00 2001 From: Vishal Kulkarni Date: Fri, 13 Dec 2019 06:39:39 +0530 Subject: [PATCH 0675/1437] cxgb4: Fix kernel panic while accessing sge_info The sge_info debugfs collects offload queue info even when offload capability is disabled and leads to panic. [ 144.139871] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 [ 144.139874] CR2: 0000000000000000 CR3: 000000082d456005 CR4: 00000000001606e0 [ 144.139876] Call Trace: [ 144.139887] sge_queue_start+0x12/0x30 [cxgb4] [ 144.139897] seq_read+0x1d4/0x3d0 [ 144.139906] full_proxy_read+0x50/0x70 [ 144.139913] vfs_read+0x89/0x140 [ 144.139916] ksys_read+0x55/0xd0 [ 144.139924] do_syscall_64+0x5b/0x1d0 [ 144.139933] entry_SYSCALL_64_after_hwframe+0x44/0xa9 [ 144.139936] RIP: 0033:0x7f4b01493990 Fix this crash by skipping the offload queue access in sge_qinfo when offload capability is disabled Signed-off-by: Herat Ramani Signed-off-by: Vishal Kulkarni Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c index 93868dca186af..aca9f7a20a2a6 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c @@ -3048,6 +3048,9 @@ static int sge_queue_entries(const struct adapter *adap) int tot_uld_entries = 0; int i; + if (!is_uld(adap)) + goto lld_only; + mutex_lock(&uld_mutex); for (i = 0; i < CXGB4_TX_MAX; i++) tot_uld_entries += sge_qinfo_uld_txq_entries(adap, i); @@ -3058,6 +3061,7 @@ static int sge_queue_entries(const struct adapter *adap) } mutex_unlock(&uld_mutex); +lld_only: return DIV_ROUND_UP(adap->sge.ethqsets, 4) + (adap->sge.eohw_txq ? DIV_ROUND_UP(adap->sge.eoqsets, 4) : 0) + tot_uld_entries + -- GitLab From 858ce8ca62ea1530f2779d0e3f934b0176e663c3 Mon Sep 17 00:00:00 2001 From: Cristian Birsan Date: Fri, 13 Dec 2019 18:33:11 +0200 Subject: [PATCH 0676/1437] net: usb: lan78xx: Fix error message format specifier Display the return code as decimal integer. Fixes: 55d7de9de6c3 ("Microchip's LAN7800 family USB 2/3 to 10/100/1000 Ethernet device driver") Signed-off-by: Cristian Birsan Signed-off-by: Jakub Kicinski --- drivers/net/usb/lan78xx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/usb/lan78xx.c b/drivers/net/usb/lan78xx.c index d7bf1918ca625..f940dc6485e56 100644 --- a/drivers/net/usb/lan78xx.c +++ b/drivers/net/usb/lan78xx.c @@ -511,7 +511,7 @@ static int lan78xx_read_stats(struct lan78xx_net *dev, } } else { netdev_warn(dev->net, - "Failed to read stat ret = 0x%x", ret); + "Failed to read stat ret = %d", ret); } kfree(stats); -- GitLab From fd42bfd1bb31f9a521be6b4f1bd89b85abc08d20 Mon Sep 17 00:00:00 2001 From: Paul Durrant Date: Fri, 13 Dec 2019 13:20:40 +0000 Subject: [PATCH 0677/1437] xen-netback: avoid race that can lead to NULL pointer dereference In function xenvif_disconnect_queue(), the value of queue->rx_irq is zeroed *before* queue->task is stopped. Unfortunately that task may call notify_remote_via_irq(queue->rx_irq) and calling that function with a zero value results in a NULL pointer dereference in evtchn_from_irq(). This patch simply re-orders things, stopping all tasks before zero-ing the irq values, thereby avoiding the possibility of the race. Fixes: 2ac061ce97f4 ("xen/netback: cleanup init and deinit code") Signed-off-by: Paul Durrant Acked-by: Wei Liu Signed-off-by: Jakub Kicinski --- drivers/net/xen-netback/interface.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/drivers/net/xen-netback/interface.c b/drivers/net/xen-netback/interface.c index 68dd7bb07ca66..f15ba3de6195a 100644 --- a/drivers/net/xen-netback/interface.c +++ b/drivers/net/xen-netback/interface.c @@ -628,18 +628,6 @@ err: static void xenvif_disconnect_queue(struct xenvif_queue *queue) { - if (queue->tx_irq) { - unbind_from_irqhandler(queue->tx_irq, queue); - if (queue->tx_irq == queue->rx_irq) - queue->rx_irq = 0; - queue->tx_irq = 0; - } - - if (queue->rx_irq) { - unbind_from_irqhandler(queue->rx_irq, queue); - queue->rx_irq = 0; - } - if (queue->task) { kthread_stop(queue->task); queue->task = NULL; @@ -655,6 +643,18 @@ static void xenvif_disconnect_queue(struct xenvif_queue *queue) queue->napi.poll = NULL; } + if (queue->tx_irq) { + unbind_from_irqhandler(queue->tx_irq, queue); + if (queue->tx_irq == queue->rx_irq) + queue->rx_irq = 0; + queue->tx_irq = 0; + } + + if (queue->rx_irq) { + unbind_from_irqhandler(queue->rx_irq, queue); + queue->rx_irq = 0; + } + xenvif_unmap_frontend_data_rings(queue); } -- GitLab From e16260c21f87b16a33ae8ecac9e8c79f3a8b89bd Mon Sep 17 00:00:00 2001 From: Sven Schnelle Date: Sun, 15 Dec 2019 20:10:39 +0100 Subject: [PATCH 0678/1437] parisc: fix compilation when KEXEC=n and KEXEC_FILE=y Fix compilation when the CONFIG_KEXEC_FILE=y and CONFIG_KEXEC=n. Reported-by: kbuild test robot Signed-off-by: Sven Schnelle Signed-off-by: Helge Deller --- arch/parisc/include/asm/kexec.h | 4 ---- arch/parisc/kernel/Makefile | 2 +- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/arch/parisc/include/asm/kexec.h b/arch/parisc/include/asm/kexec.h index a99ea747d7ede..87e1740069955 100644 --- a/arch/parisc/include/asm/kexec.h +++ b/arch/parisc/include/asm/kexec.h @@ -2,8 +2,6 @@ #ifndef _ASM_PARISC_KEXEC_H #define _ASM_PARISC_KEXEC_H -#ifdef CONFIG_KEXEC - /* Maximum physical address we can use pages from */ #define KEXEC_SOURCE_MEMORY_LIMIT (-1UL) /* Maximum address we can reach in physical address mode */ @@ -32,6 +30,4 @@ static inline void crash_setup_regs(struct pt_regs *newregs, #endif /* __ASSEMBLY__ */ -#endif /* CONFIG_KEXEC */ - #endif /* _ASM_PARISC_KEXEC_H */ diff --git a/arch/parisc/kernel/Makefile b/arch/parisc/kernel/Makefile index 2663c8f8be115..068d90950d937 100644 --- a/arch/parisc/kernel/Makefile +++ b/arch/parisc/kernel/Makefile @@ -37,5 +37,5 @@ obj-$(CONFIG_FUNCTION_GRAPH_TRACER) += ftrace.o obj-$(CONFIG_JUMP_LABEL) += jump_label.o obj-$(CONFIG_KGDB) += kgdb.o obj-$(CONFIG_KPROBES) += kprobes.o -obj-$(CONFIG_KEXEC) += kexec.o relocate_kernel.o +obj-$(CONFIG_KEXEC_CORE) += kexec.o relocate_kernel.o obj-$(CONFIG_KEXEC_FILE) += kexec_file.o -- GitLab From aeea5eae4fd54e94d820ed17ea3b238160be723e Mon Sep 17 00:00:00 2001 From: Sven Schnelle Date: Sun, 15 Dec 2019 20:09:27 +0100 Subject: [PATCH 0679/1437] parisc: add missing __init annotation compilation failed with: MODPOST vmlinux.o WARNING: vmlinux.o(.text.unlikely+0xa0c): Section mismatch in reference from the function walk_lower_bus() to the function .init.text:walk_native_bus() The function walk_lower_bus() references the function __init walk_native_bus(). This is often because walk_lower_bus lacks a __init annotation or the annotation of walk_native_bus is wrong. FATAL: modpost: Section mismatches detected. Set CONFIG_SECTION_MISMATCH_WARN_ONLY=y to allow them. make[2]: *** [/home/svens/linux/parisc-linux/src/scripts/Makefile.modpost:64: __modpost] Error 1 make[1]: *** [/home/svens/linux/parisc-linux/src/Makefile:1077: vmlinux] Error 2 make[1]: Leaving directory '/home/svens/linux/parisc-linux/build' make: *** [Makefile:179: sub-make] Error 2 Signed-off-by: Sven Schnelle Signed-off-by: Helge Deller --- arch/parisc/kernel/drivers.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/parisc/kernel/drivers.c b/arch/parisc/kernel/drivers.c index 3b330e58a4f03..a6c9f49c66128 100644 --- a/arch/parisc/kernel/drivers.c +++ b/arch/parisc/kernel/drivers.c @@ -810,7 +810,7 @@ EXPORT_SYMBOL(device_to_hwpath); static void walk_native_bus(unsigned long io_io_low, unsigned long io_io_high, struct device *parent); -static void walk_lower_bus(struct parisc_device *dev) +static void __init walk_lower_bus(struct parisc_device *dev) { unsigned long io_io_low, io_io_high; -- GitLab From 0e627190563e8be6fc9c2cc2a8a3c14bd961754c Mon Sep 17 00:00:00 2001 From: Arjun Roy Date: Sun, 15 Dec 2019 11:54:51 -0800 Subject: [PATCH 0680/1437] tcp: Set rcv zerocopy hint correctly if skb last frag is < PAGE_SIZE At present, if the last frag of paged data in a skb has < PAGE_SIZE data, we compute the recv_skip_hint as being equal to the size of that frag and the entire next skb. Instead, just return the runt frag size as the hint. recv_skip_hint is used by the application to skip over bytes that can not be mmaped, so returning a too big chunk is pessimistic and forces more bytes to be copied. Signed-off-by: Arjun Roy Acked-by: Soheil Hassas Yeganeh Signed-off-by: Eric Dumazet Signed-off-by: Jakub Kicinski --- net/ipv4/tcp.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index 09e2cae929565..93fe77c5b02d6 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -1778,6 +1778,8 @@ static int tcp_zerocopy_receive(struct sock *sk, while (length + PAGE_SIZE <= zc->length) { if (zc->recv_skip_hint < PAGE_SIZE) { if (skb) { + if (zc->recv_skip_hint > 0) + break; skb = skb->next; offset = seq - TCP_SKB_CB(skb)->seq; } else { -- GitLab From d195a66e367b3d24fdd3c3565f37ab7c6882b9d2 Mon Sep 17 00:00:00 2001 From: Brian Gianforcaro Date: Fri, 13 Dec 2019 03:09:50 -0800 Subject: [PATCH 0681/1437] io_uring: fix stale comment and a few typos - Fix a few typos found while reading the code. - Fix stale io_get_sqring comment referencing s->sqe, the 's' parameter was renamed to 'req', but the comment still holds. Signed-off-by: Brian Gianforcaro Signed-off-by: Jens Axboe --- fs/io-wq.c | 2 +- fs/io_uring.c | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/fs/io-wq.c b/fs/io-wq.c index 90c4978781fb5..11e80b7252a82 100644 --- a/fs/io-wq.c +++ b/fs/io-wq.c @@ -948,7 +948,7 @@ static enum io_wq_cancel io_wqe_cancel_work(struct io_wqe *wqe, /* * Now check if a free (going busy) or busy worker has the work * currently running. If we find it there, we'll return CANCEL_RUNNING - * as an indication that we attempte to signal cancellation. The + * as an indication that we attempt to signal cancellation. The * completion will run normally in this case. */ rcu_read_lock(); diff --git a/fs/io_uring.c b/fs/io_uring.c index 9b1833fedc5cc..04cff3870b3bf 100644 --- a/fs/io_uring.c +++ b/fs/io_uring.c @@ -1178,7 +1178,7 @@ static int io_do_iopoll(struct io_ring_ctx *ctx, unsigned int *nr_events, } /* - * Poll for a mininum of 'min' events. Note that if min == 0 we consider that a + * Poll for a minimum of 'min' events. Note that if min == 0 we consider that a * non-spinning poll check - we'll still enter the driver poll loop, but only * as a non-spinning completion check. */ @@ -2573,7 +2573,7 @@ static enum hrtimer_restart io_timeout_fn(struct hrtimer *timer) /* * Adjust the reqs sequence before the current one because it - * will consume a slot in the cq_ring and the the cq_tail + * will consume a slot in the cq_ring and the cq_tail * pointer will be increased, otherwise other timeout reqs may * return in advance without waiting for enough wait_nr. */ @@ -3430,7 +3430,7 @@ static void io_commit_sqring(struct io_ring_ctx *ctx) } /* - * Fetch an sqe, if one is available. Note that s->sqe will point to memory + * Fetch an sqe, if one is available. Note that req->sqe will point to memory * that is mapped by userspace. This means that care needs to be taken to * ensure that reads are stable, as we cannot rely on userspace always * being a good citizen. If members of the sqe are validated and then later @@ -3694,7 +3694,7 @@ static inline bool io_should_wake(struct io_wait_queue *iowq, bool noflush) struct io_ring_ctx *ctx = iowq->ctx; /* - * Wake up if we have enough events, or if a timeout occured since we + * Wake up if we have enough events, or if a timeout occurred since we * started waiting. For timeouts, we always want to return to userspace, * regardless of event count. */ -- GitLab From 9803387c55f7d2ce69aa64340c5fdc6b3027dbc8 Mon Sep 17 00:00:00 2001 From: Theodore Ts'o Date: Sun, 15 Dec 2019 01:09:03 -0500 Subject: [PATCH 0682/1437] ext4: validate the debug_want_extra_isize mount option at parse time Instead of setting s_want_extra_size and then making sure that it is a valid value afterwards, validate the field before we set it. This avoids races and other problems when remounting the file system. Link: https://lore.kernel.org/r/20191215063020.GA11512@mit.edu Cc: stable@kernel.org Signed-off-by: Theodore Ts'o Reported-and-tested-by: syzbot+4a39a025912b265cacef@syzkaller.appspotmail.com --- fs/ext4/super.c | 143 +++++++++++++++++++++++------------------------- 1 file changed, 69 insertions(+), 74 deletions(-) diff --git a/fs/ext4/super.c b/fs/ext4/super.c index b205112ca0511..46b6d5b150ac9 100644 --- a/fs/ext4/super.c +++ b/fs/ext4/super.c @@ -1886,6 +1886,13 @@ static int handle_mount_opt(struct super_block *sb, char *opt, int token, } sbi->s_commit_interval = HZ * arg; } else if (token == Opt_debug_want_extra_isize) { + if ((arg & 1) || + (arg < 4) || + (arg > (sbi->s_inode_size - EXT4_GOOD_OLD_INODE_SIZE))) { + ext4_msg(sb, KERN_ERR, + "Invalid want_extra_isize %d", arg); + return -1; + } sbi->s_want_extra_isize = arg; } else if (token == Opt_max_batch_time) { sbi->s_max_batch_time = arg; @@ -3540,40 +3547,6 @@ int ext4_calculate_overhead(struct super_block *sb) return 0; } -static void ext4_clamp_want_extra_isize(struct super_block *sb) -{ - struct ext4_sb_info *sbi = EXT4_SB(sb); - struct ext4_super_block *es = sbi->s_es; - unsigned def_extra_isize = sizeof(struct ext4_inode) - - EXT4_GOOD_OLD_INODE_SIZE; - - if (sbi->s_inode_size == EXT4_GOOD_OLD_INODE_SIZE) { - sbi->s_want_extra_isize = 0; - return; - } - if (sbi->s_want_extra_isize < 4) { - sbi->s_want_extra_isize = def_extra_isize; - if (ext4_has_feature_extra_isize(sb)) { - if (sbi->s_want_extra_isize < - le16_to_cpu(es->s_want_extra_isize)) - sbi->s_want_extra_isize = - le16_to_cpu(es->s_want_extra_isize); - if (sbi->s_want_extra_isize < - le16_to_cpu(es->s_min_extra_isize)) - sbi->s_want_extra_isize = - le16_to_cpu(es->s_min_extra_isize); - } - } - /* Check if enough inode space is available */ - if ((sbi->s_want_extra_isize > sbi->s_inode_size) || - (EXT4_GOOD_OLD_INODE_SIZE + sbi->s_want_extra_isize > - sbi->s_inode_size)) { - sbi->s_want_extra_isize = def_extra_isize; - ext4_msg(sb, KERN_INFO, - "required extra inode space not available"); - } -} - static void ext4_set_resv_clusters(struct super_block *sb) { ext4_fsblk_t resv_clusters; @@ -3781,6 +3754,68 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent) */ sbi->s_li_wait_mult = EXT4_DEF_LI_WAIT_MULT; + if (le32_to_cpu(es->s_rev_level) == EXT4_GOOD_OLD_REV) { + sbi->s_inode_size = EXT4_GOOD_OLD_INODE_SIZE; + sbi->s_first_ino = EXT4_GOOD_OLD_FIRST_INO; + } else { + sbi->s_inode_size = le16_to_cpu(es->s_inode_size); + sbi->s_first_ino = le32_to_cpu(es->s_first_ino); + if (sbi->s_first_ino < EXT4_GOOD_OLD_FIRST_INO) { + ext4_msg(sb, KERN_ERR, "invalid first ino: %u", + sbi->s_first_ino); + goto failed_mount; + } + if ((sbi->s_inode_size < EXT4_GOOD_OLD_INODE_SIZE) || + (!is_power_of_2(sbi->s_inode_size)) || + (sbi->s_inode_size > blocksize)) { + ext4_msg(sb, KERN_ERR, + "unsupported inode size: %d", + sbi->s_inode_size); + goto failed_mount; + } + /* + * i_atime_extra is the last extra field available for + * [acm]times in struct ext4_inode. Checking for that + * field should suffice to ensure we have extra space + * for all three. + */ + if (sbi->s_inode_size >= offsetof(struct ext4_inode, i_atime_extra) + + sizeof(((struct ext4_inode *)0)->i_atime_extra)) { + sb->s_time_gran = 1; + sb->s_time_max = EXT4_EXTRA_TIMESTAMP_MAX; + } else { + sb->s_time_gran = NSEC_PER_SEC; + sb->s_time_max = EXT4_NON_EXTRA_TIMESTAMP_MAX; + } + sb->s_time_min = EXT4_TIMESTAMP_MIN; + } + if (sbi->s_inode_size > EXT4_GOOD_OLD_INODE_SIZE) { + sbi->s_want_extra_isize = sizeof(struct ext4_inode) - + EXT4_GOOD_OLD_INODE_SIZE; + if (ext4_has_feature_extra_isize(sb)) { + unsigned v, max = (sbi->s_inode_size - + EXT4_GOOD_OLD_INODE_SIZE); + + v = le16_to_cpu(es->s_want_extra_isize); + if (v > max) { + ext4_msg(sb, KERN_ERR, + "bad s_want_extra_isize: %d", v); + goto failed_mount; + } + if (sbi->s_want_extra_isize < v) + sbi->s_want_extra_isize = v; + + v = le16_to_cpu(es->s_min_extra_isize); + if (v > max) { + ext4_msg(sb, KERN_ERR, + "bad s_min_extra_isize: %d", v); + goto failed_mount; + } + if (sbi->s_want_extra_isize < v) + sbi->s_want_extra_isize = v; + } + } + if (sbi->s_es->s_mount_opts[0]) { char *s_mount_opts = kstrndup(sbi->s_es->s_mount_opts, sizeof(sbi->s_es->s_mount_opts), @@ -4019,42 +4054,6 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent) has_huge_files); sb->s_maxbytes = ext4_max_size(sb->s_blocksize_bits, has_huge_files); - if (le32_to_cpu(es->s_rev_level) == EXT4_GOOD_OLD_REV) { - sbi->s_inode_size = EXT4_GOOD_OLD_INODE_SIZE; - sbi->s_first_ino = EXT4_GOOD_OLD_FIRST_INO; - } else { - sbi->s_inode_size = le16_to_cpu(es->s_inode_size); - sbi->s_first_ino = le32_to_cpu(es->s_first_ino); - if (sbi->s_first_ino < EXT4_GOOD_OLD_FIRST_INO) { - ext4_msg(sb, KERN_ERR, "invalid first ino: %u", - sbi->s_first_ino); - goto failed_mount; - } - if ((sbi->s_inode_size < EXT4_GOOD_OLD_INODE_SIZE) || - (!is_power_of_2(sbi->s_inode_size)) || - (sbi->s_inode_size > blocksize)) { - ext4_msg(sb, KERN_ERR, - "unsupported inode size: %d", - sbi->s_inode_size); - goto failed_mount; - } - /* - * i_atime_extra is the last extra field available for [acm]times in - * struct ext4_inode. Checking for that field should suffice to ensure - * we have extra space for all three. - */ - if (sbi->s_inode_size >= offsetof(struct ext4_inode, i_atime_extra) + - sizeof(((struct ext4_inode *)0)->i_atime_extra)) { - sb->s_time_gran = 1; - sb->s_time_max = EXT4_EXTRA_TIMESTAMP_MAX; - } else { - sb->s_time_gran = NSEC_PER_SEC; - sb->s_time_max = EXT4_NON_EXTRA_TIMESTAMP_MAX; - } - - sb->s_time_min = EXT4_TIMESTAMP_MIN; - } - sbi->s_desc_size = le16_to_cpu(es->s_desc_size); if (ext4_has_feature_64bit(sb)) { if (sbi->s_desc_size < EXT4_MIN_DESC_SIZE_64BIT || @@ -4503,8 +4502,6 @@ no_journal: } else if (ret) goto failed_mount4a; - ext4_clamp_want_extra_isize(sb); - ext4_set_resv_clusters(sb); err = ext4_setup_system_zone(sb); @@ -5292,8 +5289,6 @@ static int ext4_remount(struct super_block *sb, int *flags, char *data) goto restore_opts; } - ext4_clamp_want_extra_isize(sb); - if ((old_opts.s_mount_opt & EXT4_MOUNT_JOURNAL_CHECKSUM) ^ test_opt(sb, JOURNAL_CHECKSUM)) { ext4_msg(sb, KERN_ERR, "changing journal_checksum " -- GitLab From d1eef1c619749b2a57e514a3fa67d9a516ffa919 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Sun, 15 Dec 2019 15:16:08 -0800 Subject: [PATCH 0683/1437] Linux 5.5-rc2 --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 73e3c28029277..f900c23b82914 100644 --- a/Makefile +++ b/Makefile @@ -2,7 +2,7 @@ VERSION = 5 PATCHLEVEL = 5 SUBLEVEL = 0 -EXTRAVERSION = -rc1 +EXTRAVERSION = -rc2 NAME = Kleptomaniac Octopus # *DOCUMENTATION* -- GitLab From 0d13bfce023ac7cef4d0a50b83750254ce31c479 Mon Sep 17 00:00:00 2001 From: Andrii Nakryiko Date: Fri, 13 Dec 2019 17:43:25 -0800 Subject: [PATCH 0684/1437] libbpf: Don't require root for bpf_object__open() Reorganize bpf_object__open and bpf_object__load steps such that bpf_object__open doesn't need root access. This was previously done for feature probing and BTF sanitization. This doesn't have to happen on open, though, so move all those steps into the load phase. This is important, because it makes it possible for tools like bpftool, to just open BPF object file and inspect their contents: programs, maps, BTF, etc. For such operations it is prohibitive to require root access. On the other hand, there is a lot of custom libbpf logic in those steps, so its best avoided for tools to reimplement all that on their own. Signed-off-by: Andrii Nakryiko Signed-off-by: Alexei Starovoitov Acked-by: Martin KaFai Lau Link: https://lore.kernel.org/bpf/20191214014341.3442258-2-andriin@fb.com --- tools/lib/bpf/libbpf.c | 83 +++++++++++++++++++++--------------------- 1 file changed, 41 insertions(+), 42 deletions(-) diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c index 27d5f7ecba32c..dc993112b40ba 100644 --- a/tools/lib/bpf/libbpf.c +++ b/tools/lib/bpf/libbpf.c @@ -101,13 +101,6 @@ void libbpf_print(enum libbpf_print_level level, const char *format, ...) #define STRERR_BUFSIZE 128 -#define CHECK_ERR(action, err, out) do { \ - err = action; \ - if (err) \ - goto out; \ -} while (0) - - /* Copied from tools/perf/util/util.h */ #ifndef zfree # define zfree(ptr) ({ free(*ptr); *ptr = NULL; }) @@ -864,8 +857,7 @@ bpf_object__init_internal_map(struct bpf_object *obj, enum libbpf_map_type type, def->value_size = data->d_size; def->max_entries = 1; def->map_flags = type == LIBBPF_MAP_RODATA ? BPF_F_RDONLY_PROG : 0; - if (obj->caps.array_mmap) - def->map_flags |= BPF_F_MMAPABLE; + def->map_flags |= BPF_F_MMAPABLE; pr_debug("map '%s' (global data): at sec_idx %d, offset %zu, flags %x.\n", map_name, map->sec_idx, map->sec_offset, def->map_flags); @@ -888,8 +880,6 @@ static int bpf_object__init_global_data_maps(struct bpf_object *obj) { int err; - if (!obj->caps.global_data) - return 0; /* * Populate obj->maps with libbpf internal maps. */ @@ -1393,10 +1383,11 @@ static int bpf_object__init_user_btf_maps(struct bpf_object *obj, bool strict, return 0; } -static int bpf_object__init_maps(struct bpf_object *obj, bool relaxed_maps, - const char *pin_root_path) +static int bpf_object__init_maps(struct bpf_object *obj, + struct bpf_object_open_opts *opts) { - bool strict = !relaxed_maps; + const char *pin_root_path = OPTS_GET(opts, pin_root_path, NULL); + bool strict = !OPTS_GET(opts, relaxed_maps, false); int err; err = bpf_object__init_user_maps(obj, strict); @@ -1592,8 +1583,7 @@ static int bpf_object__sanitize_and_load_btf(struct bpf_object *obj) return 0; } -static int bpf_object__elf_collect(struct bpf_object *obj, bool relaxed_maps, - const char *pin_root_path) +static int bpf_object__elf_collect(struct bpf_object *obj) { Elf *elf = obj->efile.elf; GElf_Ehdr *ep = &obj->efile.ehdr; @@ -1728,14 +1718,7 @@ static int bpf_object__elf_collect(struct bpf_object *obj, bool relaxed_maps, pr_warn("Corrupted ELF file: index of strtab invalid\n"); return -LIBBPF_ERRNO__FORMAT; } - err = bpf_object__init_btf(obj, btf_data, btf_ext_data); - if (!err) - err = bpf_object__init_maps(obj, relaxed_maps, pin_root_path); - if (!err) - err = bpf_object__sanitize_and_load_btf(obj); - if (!err) - err = bpf_object__init_prog_names(obj); - return err; + return bpf_object__init_btf(obj, btf_data, btf_ext_data); } static struct bpf_program * @@ -1876,11 +1859,6 @@ static int bpf_program__record_reloc(struct bpf_program *prog, pr_warn("bad data relo against section %u\n", shdr_idx); return -LIBBPF_ERRNO__RELOC; } - if (!obj->caps.global_data) { - pr_warn("relocation: kernel does not support global \'%s\' variable access in insns[%d]\n", - name, insn_idx); - return -LIBBPF_ERRNO__RELOC; - } for (map_idx = 0; map_idx < nr_maps; map_idx++) { map = &obj->maps[map_idx]; if (map->libbpf_type != type) @@ -3918,12 +3896,10 @@ static struct bpf_object * __bpf_object__open(const char *path, const void *obj_buf, size_t obj_buf_sz, struct bpf_object_open_opts *opts) { - const char *pin_root_path; struct bpf_program *prog; struct bpf_object *obj; const char *obj_name; char tmp_name[64]; - bool relaxed_maps; __u32 attach_prog_fd; int err; @@ -3953,16 +3929,16 @@ __bpf_object__open(const char *path, const void *obj_buf, size_t obj_buf_sz, return obj; obj->relaxed_core_relocs = OPTS_GET(opts, relaxed_core_relocs, false); - relaxed_maps = OPTS_GET(opts, relaxed_maps, false); - pin_root_path = OPTS_GET(opts, pin_root_path, NULL); attach_prog_fd = OPTS_GET(opts, attach_prog_fd, 0); - CHECK_ERR(bpf_object__elf_init(obj), err, out); - CHECK_ERR(bpf_object__check_endianness(obj), err, out); - CHECK_ERR(bpf_object__probe_caps(obj), err, out); - CHECK_ERR(bpf_object__elf_collect(obj, relaxed_maps, pin_root_path), - err, out); - CHECK_ERR(bpf_object__collect_reloc(obj), err, out); + err = bpf_object__elf_init(obj); + err = err ? : bpf_object__check_endianness(obj); + err = err ? : bpf_object__elf_collect(obj); + err = err ? : bpf_object__init_maps(obj, opts); + err = err ? : bpf_object__init_prog_names(obj); + err = err ? : bpf_object__collect_reloc(obj); + if (err) + goto out; bpf_object__elf_finish(obj); bpf_object__for_each_program(prog, obj) { @@ -4080,6 +4056,24 @@ int bpf_object__unload(struct bpf_object *obj) return 0; } +static int bpf_object__sanitize_maps(struct bpf_object *obj) +{ + struct bpf_map *m; + + bpf_object__for_each_map(m, obj) { + if (!bpf_map__is_internal(m)) + continue; + if (!obj->caps.global_data) { + pr_warn("kernel doesn't support global data\n"); + return -ENOTSUP; + } + if (!obj->caps.array_mmap) + m->def.map_flags ^= BPF_F_MMAPABLE; + } + + return 0; +} + int bpf_object__load_xattr(struct bpf_object_load_attr *attr) { struct bpf_object *obj; @@ -4098,9 +4092,14 @@ int bpf_object__load_xattr(struct bpf_object_load_attr *attr) obj->loaded = true; - CHECK_ERR(bpf_object__create_maps(obj), err, out); - CHECK_ERR(bpf_object__relocate(obj, attr->target_btf_path), err, out); - CHECK_ERR(bpf_object__load_progs(obj, attr->log_level), err, out); + err = bpf_object__probe_caps(obj); + err = err ? : bpf_object__sanitize_and_load_btf(obj); + err = err ? : bpf_object__sanitize_maps(obj); + err = err ? : bpf_object__create_maps(obj); + err = err ? : bpf_object__relocate(obj, attr->target_btf_path); + err = err ? : bpf_object__load_progs(obj, attr->log_level); + if (err) + goto out; return 0; out: -- GitLab From d7a18ea7e8b612669acd0131fd075e5c735c1ce5 Mon Sep 17 00:00:00 2001 From: Andrii Nakryiko Date: Fri, 13 Dec 2019 17:43:26 -0800 Subject: [PATCH 0685/1437] libbpf: Add generic bpf_program__attach() Generalize BPF program attaching and allow libbpf to auto-detect type (and extra parameters, where applicable) and attach supported BPF program types based on program sections. Currently this is supported for: - kprobe/kretprobe; - tracepoint; - raw tracepoint; - tracing programs (typed raw TP/fentry/fexit). More types support can be trivially added within this framework. Signed-off-by: Andrii Nakryiko Signed-off-by: Alexei Starovoitov Acked-by: Martin KaFai Lau Link: https://lore.kernel.org/bpf/20191214014341.3442258-3-andriin@fb.com --- tools/lib/bpf/libbpf.c | 181 ++++++++++++++---- tools/lib/bpf/libbpf.h | 2 + tools/lib/bpf/libbpf.map | 2 + .../selftests/bpf/prog_tests/probe_user.c | 6 +- 4 files changed, 153 insertions(+), 38 deletions(-) diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c index dc993112b40ba..61b8cdf783325 100644 --- a/tools/lib/bpf/libbpf.c +++ b/tools/lib/bpf/libbpf.c @@ -4972,7 +4972,28 @@ void bpf_program__set_expected_attach_type(struct bpf_program *prog, */ #define BPF_APROG_COMPAT(string, ptype) BPF_PROG_SEC(string, ptype) -static const struct { +#define SEC_DEF(sec_pfx, ptype, ...) { \ + .sec = sec_pfx, \ + .len = sizeof(sec_pfx) - 1, \ + .prog_type = BPF_PROG_TYPE_##ptype, \ + __VA_ARGS__ \ +} + +struct bpf_sec_def; + +typedef struct bpf_link *(*attach_fn_t)(const struct bpf_sec_def *sec, + struct bpf_program *prog); + +static struct bpf_link *attach_kprobe(const struct bpf_sec_def *sec, + struct bpf_program *prog); +static struct bpf_link *attach_tp(const struct bpf_sec_def *sec, + struct bpf_program *prog); +static struct bpf_link *attach_raw_tp(const struct bpf_sec_def *sec, + struct bpf_program *prog); +static struct bpf_link *attach_trace(const struct bpf_sec_def *sec, + struct bpf_program *prog); + +struct bpf_sec_def { const char *sec; size_t len; enum bpf_prog_type prog_type; @@ -4980,25 +5001,40 @@ static const struct { bool is_attachable; bool is_attach_btf; enum bpf_attach_type attach_type; -} section_names[] = { + attach_fn_t attach_fn; +}; + +static const struct bpf_sec_def section_defs[] = { BPF_PROG_SEC("socket", BPF_PROG_TYPE_SOCKET_FILTER), BPF_PROG_SEC("sk_reuseport", BPF_PROG_TYPE_SK_REUSEPORT), - BPF_PROG_SEC("kprobe/", BPF_PROG_TYPE_KPROBE), + SEC_DEF("kprobe/", KPROBE, + .attach_fn = attach_kprobe), BPF_PROG_SEC("uprobe/", BPF_PROG_TYPE_KPROBE), - BPF_PROG_SEC("kretprobe/", BPF_PROG_TYPE_KPROBE), + SEC_DEF("kretprobe/", KPROBE, + .attach_fn = attach_kprobe), BPF_PROG_SEC("uretprobe/", BPF_PROG_TYPE_KPROBE), BPF_PROG_SEC("classifier", BPF_PROG_TYPE_SCHED_CLS), BPF_PROG_SEC("action", BPF_PROG_TYPE_SCHED_ACT), - BPF_PROG_SEC("tracepoint/", BPF_PROG_TYPE_TRACEPOINT), - BPF_PROG_SEC("tp/", BPF_PROG_TYPE_TRACEPOINT), - BPF_PROG_SEC("raw_tracepoint/", BPF_PROG_TYPE_RAW_TRACEPOINT), - BPF_PROG_SEC("raw_tp/", BPF_PROG_TYPE_RAW_TRACEPOINT), - BPF_PROG_BTF("tp_btf/", BPF_PROG_TYPE_TRACING, - BPF_TRACE_RAW_TP), - BPF_PROG_BTF("fentry/", BPF_PROG_TYPE_TRACING, - BPF_TRACE_FENTRY), - BPF_PROG_BTF("fexit/", BPF_PROG_TYPE_TRACING, - BPF_TRACE_FEXIT), + SEC_DEF("tracepoint/", TRACEPOINT, + .attach_fn = attach_tp), + SEC_DEF("tp/", TRACEPOINT, + .attach_fn = attach_tp), + SEC_DEF("raw_tracepoint/", RAW_TRACEPOINT, + .attach_fn = attach_raw_tp), + SEC_DEF("raw_tp/", RAW_TRACEPOINT, + .attach_fn = attach_raw_tp), + SEC_DEF("tp_btf/", TRACING, + .expected_attach_type = BPF_TRACE_RAW_TP, + .is_attach_btf = true, + .attach_fn = attach_trace), + SEC_DEF("fentry/", TRACING, + .expected_attach_type = BPF_TRACE_FENTRY, + .is_attach_btf = true, + .attach_fn = attach_trace), + SEC_DEF("fexit/", TRACING, + .expected_attach_type = BPF_TRACE_FEXIT, + .is_attach_btf = true, + .attach_fn = attach_trace), BPF_PROG_SEC("xdp", BPF_PROG_TYPE_XDP), BPF_PROG_SEC("perf_event", BPF_PROG_TYPE_PERF_EVENT), BPF_PROG_SEC("lwt_in", BPF_PROG_TYPE_LWT_IN), @@ -5060,12 +5096,26 @@ static const struct { #undef BPF_APROG_SEC #undef BPF_EAPROG_SEC #undef BPF_APROG_COMPAT +#undef SEC_DEF #define MAX_TYPE_NAME_SIZE 32 +static const struct bpf_sec_def *find_sec_def(const char *sec_name) +{ + int i, n = ARRAY_SIZE(section_defs); + + for (i = 0; i < n; i++) { + if (strncmp(sec_name, + section_defs[i].sec, section_defs[i].len)) + continue; + return §ion_defs[i]; + } + return NULL; +} + static char *libbpf_get_type_names(bool attach_type) { - int i, len = ARRAY_SIZE(section_names) * MAX_TYPE_NAME_SIZE; + int i, len = ARRAY_SIZE(section_defs) * MAX_TYPE_NAME_SIZE; char *buf; buf = malloc(len); @@ -5074,16 +5124,16 @@ static char *libbpf_get_type_names(bool attach_type) buf[0] = '\0'; /* Forge string buf with all available names */ - for (i = 0; i < ARRAY_SIZE(section_names); i++) { - if (attach_type && !section_names[i].is_attachable) + for (i = 0; i < ARRAY_SIZE(section_defs); i++) { + if (attach_type && !section_defs[i].is_attachable) continue; - if (strlen(buf) + strlen(section_names[i].sec) + 2 > len) { + if (strlen(buf) + strlen(section_defs[i].sec) + 2 > len) { free(buf); return NULL; } strcat(buf, " "); - strcat(buf, section_names[i].sec); + strcat(buf, section_defs[i].sec); } return buf; @@ -5092,19 +5142,19 @@ static char *libbpf_get_type_names(bool attach_type) int libbpf_prog_type_by_name(const char *name, enum bpf_prog_type *prog_type, enum bpf_attach_type *expected_attach_type) { + const struct bpf_sec_def *sec_def; char *type_names; - int i; if (!name) return -EINVAL; - for (i = 0; i < ARRAY_SIZE(section_names); i++) { - if (strncmp(name, section_names[i].sec, section_names[i].len)) - continue; - *prog_type = section_names[i].prog_type; - *expected_attach_type = section_names[i].expected_attach_type; + sec_def = find_sec_def(name); + if (sec_def) { + *prog_type = sec_def->prog_type; + *expected_attach_type = sec_def->expected_attach_type; return 0; } + pr_warn("failed to guess program type from ELF section '%s'\n", name); type_names = libbpf_get_type_names(false); if (type_names != NULL) { @@ -5187,16 +5237,16 @@ static int libbpf_find_attach_btf_id(const char *name, if (!name) return -EINVAL; - for (i = 0; i < ARRAY_SIZE(section_names); i++) { - if (!section_names[i].is_attach_btf) + for (i = 0; i < ARRAY_SIZE(section_defs); i++) { + if (!section_defs[i].is_attach_btf) continue; - if (strncmp(name, section_names[i].sec, section_names[i].len)) + if (strncmp(name, section_defs[i].sec, section_defs[i].len)) continue; if (attach_prog_fd) - err = libbpf_find_prog_btf_id(name + section_names[i].len, + err = libbpf_find_prog_btf_id(name + section_defs[i].len, attach_prog_fd); else - err = libbpf_find_vmlinux_btf_id(name + section_names[i].len, + err = libbpf_find_vmlinux_btf_id(name + section_defs[i].len, attach_type); if (err <= 0) pr_warn("%s is not found in vmlinux BTF\n", name); @@ -5215,12 +5265,12 @@ int libbpf_attach_type_by_name(const char *name, if (!name) return -EINVAL; - for (i = 0; i < ARRAY_SIZE(section_names); i++) { - if (strncmp(name, section_names[i].sec, section_names[i].len)) + for (i = 0; i < ARRAY_SIZE(section_defs); i++) { + if (strncmp(name, section_defs[i].sec, section_defs[i].len)) continue; - if (!section_names[i].is_attachable) + if (!section_defs[i].is_attachable) return -EINVAL; - *attach_type = section_names[i].attach_type; + *attach_type = section_defs[i].attach_type; return 0; } pr_warn("failed to guess attach type based on ELF section name '%s'\n", name); @@ -5680,6 +5730,18 @@ struct bpf_link *bpf_program__attach_kprobe(struct bpf_program *prog, return link; } +static struct bpf_link *attach_kprobe(const struct bpf_sec_def *sec, + struct bpf_program *prog) +{ + const char *func_name; + bool retprobe; + + func_name = bpf_program__title(prog, false) + sec->len; + retprobe = strcmp(sec->sec, "kretprobe/") == 0; + + return bpf_program__attach_kprobe(prog, retprobe, func_name); +} + struct bpf_link *bpf_program__attach_uprobe(struct bpf_program *prog, bool retprobe, pid_t pid, const char *binary_path, @@ -5792,6 +5854,32 @@ struct bpf_link *bpf_program__attach_tracepoint(struct bpf_program *prog, return link; } +static struct bpf_link *attach_tp(const struct bpf_sec_def *sec, + struct bpf_program *prog) +{ + char *sec_name, *tp_cat, *tp_name; + struct bpf_link *link; + + sec_name = strdup(bpf_program__title(prog, false)); + if (!sec_name) + return ERR_PTR(-ENOMEM); + + /* extract "tp//" */ + tp_cat = sec_name + sec->len; + tp_name = strchr(tp_cat, '/'); + if (!tp_name) { + link = ERR_PTR(-EINVAL); + goto out; + } + *tp_name = '\0'; + tp_name++; + + link = bpf_program__attach_tracepoint(prog, tp_cat, tp_name); +out: + free(sec_name); + return link; +} + static int bpf_link__destroy_fd(struct bpf_link *link) { struct bpf_link_fd *l = (void *)link; @@ -5831,6 +5919,14 @@ struct bpf_link *bpf_program__attach_raw_tracepoint(struct bpf_program *prog, return (struct bpf_link *)link; } +static struct bpf_link *attach_raw_tp(const struct bpf_sec_def *sec, + struct bpf_program *prog) +{ + const char *tp_name = bpf_program__title(prog, false) + sec->len; + + return bpf_program__attach_raw_tracepoint(prog, tp_name); +} + struct bpf_link *bpf_program__attach_trace(struct bpf_program *prog) { char errmsg[STRERR_BUFSIZE]; @@ -5862,6 +5958,23 @@ struct bpf_link *bpf_program__attach_trace(struct bpf_program *prog) return (struct bpf_link *)link; } +static struct bpf_link *attach_trace(const struct bpf_sec_def *sec, + struct bpf_program *prog) +{ + return bpf_program__attach_trace(prog); +} + +struct bpf_link *bpf_program__attach(struct bpf_program *prog) +{ + const struct bpf_sec_def *sec_def; + + sec_def = find_sec_def(bpf_program__title(prog, false)); + if (!sec_def || !sec_def->attach_fn) + return ERR_PTR(-ESRCH); + + return sec_def->attach_fn(sec_def, prog); +} + enum bpf_perf_event_ret bpf_perf_event_read_simple(void *mmap_mem, size_t mmap_size, size_t page_size, void **copy_mem, size_t *copy_size, diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h index 0dbf4bfba0c4d..804f445c99574 100644 --- a/tools/lib/bpf/libbpf.h +++ b/tools/lib/bpf/libbpf.h @@ -237,6 +237,8 @@ struct bpf_link; LIBBPF_API int bpf_link__destroy(struct bpf_link *link); +LIBBPF_API struct bpf_link * +bpf_program__attach(struct bpf_program *prog); LIBBPF_API struct bpf_link * bpf_program__attach_perf_event(struct bpf_program *prog, int pfd); LIBBPF_API struct bpf_link * diff --git a/tools/lib/bpf/libbpf.map b/tools/lib/bpf/libbpf.map index 495df575f87f8..757a88f64b5a4 100644 --- a/tools/lib/bpf/libbpf.map +++ b/tools/lib/bpf/libbpf.map @@ -210,4 +210,6 @@ LIBBPF_0.0.6 { } LIBBPF_0.0.5; LIBBPF_0.0.7 { + global: + bpf_program__attach; } LIBBPF_0.0.6; diff --git a/tools/testing/selftests/bpf/prog_tests/probe_user.c b/tools/testing/selftests/bpf/prog_tests/probe_user.c index 8a3187dec0485..7aecfd9e87d15 100644 --- a/tools/testing/selftests/bpf/prog_tests/probe_user.c +++ b/tools/testing/selftests/bpf/prog_tests/probe_user.c @@ -3,8 +3,7 @@ void test_probe_user(void) { -#define kprobe_name "__sys_connect" - const char *prog_name = "kprobe/" kprobe_name; + const char *prog_name = "kprobe/__sys_connect"; const char *obj_file = "./test_probe_user.o"; DECLARE_LIBBPF_OPTS(bpf_object_open_opts, opts, ); int err, results_map_fd, sock_fd, duration = 0; @@ -33,8 +32,7 @@ void test_probe_user(void) "err %d\n", results_map_fd)) goto cleanup; - kprobe_link = bpf_program__attach_kprobe(kprobe_prog, false, - kprobe_name); + kprobe_link = bpf_program__attach(kprobe_prog); if (CHECK(IS_ERR(kprobe_link), "attach_kprobe", "err %ld\n", PTR_ERR(kprobe_link))) { kprobe_link = NULL; -- GitLab From 612d05be250aa8804d3baba7a12445a267a580d3 Mon Sep 17 00:00:00 2001 From: Andrii Nakryiko Date: Fri, 13 Dec 2019 17:43:27 -0800 Subject: [PATCH 0686/1437] libbpf: Move non-public APIs from libbpf.h to libbpf_internal.h Few libbpf APIs are not public but currently exposed through libbpf.h to be used by bpftool. Move them to libbpf_internal.h, where intent of being non-stable and non-public is much more obvious. Signed-off-by: Andrii Nakryiko Signed-off-by: Alexei Starovoitov Acked-by: Martin KaFai Lau Link: https://lore.kernel.org/bpf/20191214014341.3442258-4-andriin@fb.com --- tools/bpf/bpftool/net.c | 1 + tools/lib/bpf/libbpf.h | 17 ----------------- tools/lib/bpf/libbpf_internal.h | 17 +++++++++++++++++ 3 files changed, 18 insertions(+), 17 deletions(-) diff --git a/tools/bpf/bpftool/net.c b/tools/bpf/bpftool/net.c index 4f52d31516166..d93bee298e54c 100644 --- a/tools/bpf/bpftool/net.c +++ b/tools/bpf/bpftool/net.c @@ -18,6 +18,7 @@ #include #include +#include "libbpf_internal.h" #include "main.h" #include "netlink_dumper.h" diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h index 804f445c99574..2698fbcb0c798 100644 --- a/tools/lib/bpf/libbpf.h +++ b/tools/lib/bpf/libbpf.h @@ -126,11 +126,6 @@ bpf_object__open_buffer(const void *obj_buf, size_t obj_buf_sz, LIBBPF_API struct bpf_object * bpf_object__open_xattr(struct bpf_object_open_attr *attr); -int bpf_object__section_size(const struct bpf_object *obj, const char *name, - __u32 *size); -int bpf_object__variable_offset(const struct bpf_object *obj, const char *name, - __u32 *off); - enum libbpf_pin_type { LIBBPF_PIN_NONE, /* PIN_BY_NAME: pin maps by name (in /sys/fs/bpf by default) */ @@ -514,18 +509,6 @@ bpf_perf_event_read_simple(void *mmap_mem, size_t mmap_size, size_t page_size, void **copy_mem, size_t *copy_size, bpf_perf_event_print_t fn, void *private_data); -struct nlattr; -typedef int (*libbpf_dump_nlmsg_t)(void *cookie, void *msg, struct nlattr **tb); -int libbpf_netlink_open(unsigned int *nl_pid); -int libbpf_nl_get_link(int sock, unsigned int nl_pid, - libbpf_dump_nlmsg_t dump_link_nlmsg, void *cookie); -int libbpf_nl_get_class(int sock, unsigned int nl_pid, int ifindex, - libbpf_dump_nlmsg_t dump_class_nlmsg, void *cookie); -int libbpf_nl_get_qdisc(int sock, unsigned int nl_pid, int ifindex, - libbpf_dump_nlmsg_t dump_qdisc_nlmsg, void *cookie); -int libbpf_nl_get_filter(int sock, unsigned int nl_pid, int ifindex, int handle, - libbpf_dump_nlmsg_t dump_filter_nlmsg, void *cookie); - struct bpf_prog_linfo; struct bpf_prog_info; diff --git a/tools/lib/bpf/libbpf_internal.h b/tools/lib/bpf/libbpf_internal.h index f4f10715db406..7d71621bb7e83 100644 --- a/tools/lib/bpf/libbpf_internal.h +++ b/tools/lib/bpf/libbpf_internal.h @@ -100,6 +100,23 @@ int parse_cpu_mask_file(const char *fcpu, bool **mask, int *mask_sz); int libbpf__load_raw_btf(const char *raw_types, size_t types_len, const char *str_sec, size_t str_len); +int bpf_object__section_size(const struct bpf_object *obj, const char *name, + __u32 *size); +int bpf_object__variable_offset(const struct bpf_object *obj, const char *name, + __u32 *off); + +struct nlattr; +typedef int (*libbpf_dump_nlmsg_t)(void *cookie, void *msg, struct nlattr **tb); +int libbpf_netlink_open(unsigned int *nl_pid); +int libbpf_nl_get_link(int sock, unsigned int nl_pid, + libbpf_dump_nlmsg_t dump_link_nlmsg, void *cookie); +int libbpf_nl_get_class(int sock, unsigned int nl_pid, int ifindex, + libbpf_dump_nlmsg_t dump_class_nlmsg, void *cookie); +int libbpf_nl_get_qdisc(int sock, unsigned int nl_pid, int ifindex, + libbpf_dump_nlmsg_t dump_qdisc_nlmsg, void *cookie); +int libbpf_nl_get_filter(int sock, unsigned int nl_pid, int ifindex, int handle, + libbpf_dump_nlmsg_t dump_filter_nlmsg, void *cookie); + struct btf_ext_info { /* * info points to the individual info section (e.g. func_info and -- GitLab From 917f6b7b07a46e53fa73e112d23c97d1b201a877 Mon Sep 17 00:00:00 2001 From: Andrii Nakryiko Date: Fri, 13 Dec 2019 17:43:28 -0800 Subject: [PATCH 0687/1437] libbpf: Add BPF_EMBED_OBJ macro for embedding BPF .o files Add a convenience macro BPF_EMBED_OBJ, which allows to embed other files (typically used to embed BPF .o files) into a hosting userspace programs. To C program it is exposed as struct bpf_embed_data, containing a pointer to raw data and its size in bytes. Signed-off-by: Andrii Nakryiko Signed-off-by: Alexei Starovoitov Acked-by: Martin KaFai Lau Link: https://lore.kernel.org/bpf/20191214014341.3442258-5-andriin@fb.com --- tools/lib/bpf/libbpf.h | 35 +++++++++++++++++++ .../selftests/bpf/prog_tests/attach_probe.c | 23 ++---------- 2 files changed, 38 insertions(+), 20 deletions(-) diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h index 2698fbcb0c798..fa803dde1f460 100644 --- a/tools/lib/bpf/libbpf.h +++ b/tools/lib/bpf/libbpf.h @@ -615,6 +615,41 @@ bpf_program__bpil_offs_to_addr(struct bpf_prog_info_linear *info_linear); */ LIBBPF_API int libbpf_num_possible_cpus(void); +struct bpf_embed_data { + void *data; + size_t size; +}; + +#define BPF_EMBED_OBJ_DECLARE(NAME) \ +extern struct bpf_embed_data NAME##_embed; \ +extern char NAME##_data[]; \ +extern char NAME##_data_end[]; + +#define __BPF_EMBED_OBJ(NAME, PATH, SZ, ASM_TYPE) \ +asm ( \ +" .pushsection \".rodata\", \"a\", @progbits \n" \ +" .global "#NAME"_data \n" \ +#NAME"_data: \n" \ +" .incbin \"" PATH "\" \n" \ +" .global "#NAME"_data_end \n" \ +#NAME"_data_end: \n" \ +" .global "#NAME"_embed \n" \ +" .type "#NAME"_embed, @object \n" \ +" .size "#NAME"_size, "#SZ" \n" \ +" .align 8, \n" \ +#NAME"_embed: \n" \ +" "ASM_TYPE" "#NAME"_data \n" \ +" "ASM_TYPE" "#NAME"_data_end - "#NAME"_data \n" \ +" .popsection \n" \ +); \ +BPF_EMBED_OBJ_DECLARE(NAME) + +#if __SIZEOF_POINTER__ == 4 +#define BPF_EMBED_OBJ(NAME, PATH) __BPF_EMBED_OBJ(NAME, PATH, 8, ".long") +#else +#define BPF_EMBED_OBJ(NAME, PATH) __BPF_EMBED_OBJ(NAME, PATH, 16, ".quad") +#endif + #ifdef __cplusplus } /* extern "C" */ #endif diff --git a/tools/testing/selftests/bpf/prog_tests/attach_probe.c b/tools/testing/selftests/bpf/prog_tests/attach_probe.c index a83111a32d4a5..b2e7c1424b07f 100644 --- a/tools/testing/selftests/bpf/prog_tests/attach_probe.c +++ b/tools/testing/selftests/bpf/prog_tests/attach_probe.c @@ -1,24 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 #include -#define EMBED_FILE(NAME, PATH) \ -asm ( \ -" .pushsection \".rodata\", \"a\", @progbits \n" \ -" .global "#NAME"_data \n" \ -#NAME"_data: \n" \ -" .incbin \"" PATH "\" \n" \ -#NAME"_data_end: \n" \ -" .global "#NAME"_size \n" \ -" .type "#NAME"_size, @object \n" \ -" .size "#NAME"_size, 4 \n" \ -" .align 4, \n" \ -#NAME"_size: \n" \ -" .int "#NAME"_data_end - "#NAME"_data \n" \ -" .popsection \n" \ -); \ -extern char NAME##_data[]; \ -extern int NAME##_size; - ssize_t get_base_addr() { size_t start; char buf[256]; @@ -39,7 +21,7 @@ ssize_t get_base_addr() { return -EINVAL; } -EMBED_FILE(probe, "test_attach_probe.o"); +BPF_EMBED_OBJ(probe, "test_attach_probe.o"); void test_attach_probe(void) { @@ -73,7 +55,8 @@ void test_attach_probe(void) uprobe_offset = (size_t)&get_base_addr - base_addr; /* open object */ - obj = bpf_object__open_mem(probe_data, probe_size, &open_opts); + obj = bpf_object__open_mem(probe_embed.data, probe_embed.size, + &open_opts); if (CHECK(IS_ERR(obj), "obj_open_mem", "err %ld\n", PTR_ERR(obj))) return; -- GitLab From 544402d4b49332a4a9b2b8fff20f9d9f5ef86559 Mon Sep 17 00:00:00 2001 From: Andrii Nakryiko Date: Fri, 13 Dec 2019 17:43:29 -0800 Subject: [PATCH 0688/1437] libbpf: Extract common user-facing helpers LIBBPF_API and DECLARE_LIBBPF_OPTS are needed in many public libbpf API headers. Extract them into libbpf_common.h to avoid unnecessary interdependency between btf.h, libbpf.h, and bpf.h or code duplication. Signed-off-by: Andrii Nakryiko Signed-off-by: Alexei Starovoitov Link: https://lore.kernel.org/bpf/20191214014341.3442258-6-andriin@fb.com --- tools/lib/bpf/bpf.h | 6 ++---- tools/lib/bpf/btf.h | 6 ++---- tools/lib/bpf/libbpf.h | 28 ++------------------------ tools/lib/bpf/libbpf_common.h | 38 +++++++++++++++++++++++++++++++++++ 4 files changed, 44 insertions(+), 34 deletions(-) create mode 100644 tools/lib/bpf/libbpf_common.h diff --git a/tools/lib/bpf/bpf.h b/tools/lib/bpf/bpf.h index 3c791fa8e68e8..269807ce9ef52 100644 --- a/tools/lib/bpf/bpf.h +++ b/tools/lib/bpf/bpf.h @@ -28,14 +28,12 @@ #include #include +#include "libbpf_common.h" + #ifdef __cplusplus extern "C" { #endif -#ifndef LIBBPF_API -#define LIBBPF_API __attribute__((visibility("default"))) -#endif - struct bpf_create_map_attr { const char *name; enum bpf_map_type map_type; diff --git a/tools/lib/bpf/btf.h b/tools/lib/bpf/btf.h index d9ac73a02cde7..5fc23b988deb9 100644 --- a/tools/lib/bpf/btf.h +++ b/tools/lib/bpf/btf.h @@ -8,14 +8,12 @@ #include #include +#include "libbpf_common.h" + #ifdef __cplusplus extern "C" { #endif -#ifndef LIBBPF_API -#define LIBBPF_API __attribute__((visibility("default"))) -#endif - #define BTF_ELF_SEC ".BTF" #define BTF_EXT_ELF_SEC ".BTF.ext" #define MAPS_ELF_SEC ".maps" diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h index fa803dde1f460..49e6fa01024b7 100644 --- a/tools/lib/bpf/libbpf.h +++ b/tools/lib/bpf/libbpf.h @@ -17,14 +17,12 @@ #include // for size_t #include +#include "libbpf_common.h" + #ifdef __cplusplus extern "C" { #endif -#ifndef LIBBPF_API -#define LIBBPF_API __attribute__((visibility("default"))) -#endif - enum libbpf_errno { __LIBBPF_ERRNO__START = 4000, @@ -67,28 +65,6 @@ struct bpf_object_open_attr { enum bpf_prog_type prog_type; }; -/* Helper macro to declare and initialize libbpf options struct - * - * This dance with uninitialized declaration, followed by memset to zero, - * followed by assignment using compound literal syntax is done to preserve - * ability to use a nice struct field initialization syntax and **hopefully** - * have all the padding bytes initialized to zero. It's not guaranteed though, - * when copying literal, that compiler won't copy garbage in literal's padding - * bytes, but that's the best way I've found and it seems to work in practice. - * - * Macro declares opts struct of given type and name, zero-initializes, - * including any extra padding, it with memset() and then assigns initial - * values provided by users in struct initializer-syntax as varargs. - */ -#define DECLARE_LIBBPF_OPTS(TYPE, NAME, ...) \ - struct TYPE NAME = ({ \ - memset(&NAME, 0, sizeof(struct TYPE)); \ - (struct TYPE) { \ - .sz = sizeof(struct TYPE), \ - __VA_ARGS__ \ - }; \ - }) - struct bpf_object_open_opts { /* size of this struct, for forward/backward compatiblity */ size_t sz; diff --git a/tools/lib/bpf/libbpf_common.h b/tools/lib/bpf/libbpf_common.h new file mode 100644 index 0000000000000..4fb833840961f --- /dev/null +++ b/tools/lib/bpf/libbpf_common.h @@ -0,0 +1,38 @@ +/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */ + +/* + * Common user-facing libbpf helpers. + * + * Copyright (c) 2019 Facebook + */ + +#ifndef __LIBBPF_LIBBPF_COMMON_H +#define __LIBBPF_LIBBPF_COMMON_H + +#ifndef LIBBPF_API +#define LIBBPF_API __attribute__((visibility("default"))) +#endif + +/* Helper macro to declare and initialize libbpf options struct + * + * This dance with uninitialized declaration, followed by memset to zero, + * followed by assignment using compound literal syntax is done to preserve + * ability to use a nice struct field initialization syntax and **hopefully** + * have all the padding bytes initialized to zero. It's not guaranteed though, + * when copying literal, that compiler won't copy garbage in literal's padding + * bytes, but that's the best way I've found and it seems to work in practice. + * + * Macro declares opts struct of given type and name, zero-initializes, + * including any extra padding, it with memset() and then assigns initial + * values provided by users in struct initializer-syntax as varargs. + */ +#define DECLARE_LIBBPF_OPTS(TYPE, NAME, ...) \ + struct TYPE NAME = ({ \ + memset(&NAME, 0, sizeof(struct TYPE)); \ + (struct TYPE) { \ + .sz = sizeof(struct TYPE), \ + __VA_ARGS__ \ + }; \ + }) + +#endif /* __LIBBPF_LIBBPF_COMMON_H */ -- GitLab From 3d208f4ca111a614903f49d5a77b93ddc6de294e Mon Sep 17 00:00:00 2001 From: Andrii Nakryiko Date: Fri, 13 Dec 2019 17:43:30 -0800 Subject: [PATCH 0689/1437] libbpf: Expose btf__align_of() API Expose BTF API that calculates type alignment requirements. Signed-off-by: Andrii Nakryiko Signed-off-by: Alexei Starovoitov Link: https://lore.kernel.org/bpf/20191214014341.3442258-7-andriin@fb.com --- tools/lib/bpf/btf.c | 39 +++++++++++++++++++++++++++++++++ tools/lib/bpf/btf.h | 1 + tools/lib/bpf/btf_dump.c | 47 +++++----------------------------------- tools/lib/bpf/libbpf.map | 1 + 4 files changed, 47 insertions(+), 41 deletions(-) diff --git a/tools/lib/bpf/btf.c b/tools/lib/bpf/btf.c index 88efa2bb71374..84fe82f27bef6 100644 --- a/tools/lib/bpf/btf.c +++ b/tools/lib/bpf/btf.c @@ -278,6 +278,45 @@ done: return nelems * size; } +int btf__align_of(const struct btf *btf, __u32 id) +{ + const struct btf_type *t = btf__type_by_id(btf, id); + __u16 kind = btf_kind(t); + + switch (kind) { + case BTF_KIND_INT: + case BTF_KIND_ENUM: + return min(sizeof(void *), t->size); + case BTF_KIND_PTR: + return sizeof(void *); + case BTF_KIND_TYPEDEF: + case BTF_KIND_VOLATILE: + case BTF_KIND_CONST: + case BTF_KIND_RESTRICT: + return btf__align_of(btf, t->type); + case BTF_KIND_ARRAY: + return btf__align_of(btf, btf_array(t)->type); + case BTF_KIND_STRUCT: + case BTF_KIND_UNION: { + const struct btf_member *m = btf_members(t); + __u16 vlen = btf_vlen(t); + int i, align = 1, t; + + for (i = 0; i < vlen; i++, m++) { + t = btf__align_of(btf, m->type); + if (t <= 0) + return t; + align = max(align, t); + } + + return align; + } + default: + pr_warn("unsupported BTF_KIND:%u\n", btf_kind(t)); + return 0; + } +} + int btf__resolve_type(const struct btf *btf, __u32 type_id) { const struct btf_type *t; diff --git a/tools/lib/bpf/btf.h b/tools/lib/bpf/btf.h index 5fc23b988deb9..a114c8ef4f082 100644 --- a/tools/lib/bpf/btf.h +++ b/tools/lib/bpf/btf.h @@ -77,6 +77,7 @@ LIBBPF_API const struct btf_type *btf__type_by_id(const struct btf *btf, __u32 id); LIBBPF_API __s64 btf__resolve_size(const struct btf *btf, __u32 type_id); LIBBPF_API int btf__resolve_type(const struct btf *btf, __u32 type_id); +LIBBPF_API int btf__align_of(const struct btf *btf, __u32 id); LIBBPF_API int btf__fd(const struct btf *btf); LIBBPF_API const void *btf__get_raw_data(const struct btf *btf, __u32 *size); LIBBPF_API const char *btf__name_by_offset(const struct btf *btf, __u32 offset); diff --git a/tools/lib/bpf/btf_dump.c b/tools/lib/bpf/btf_dump.c index cb126d8fcf758..53393026d085b 100644 --- a/tools/lib/bpf/btf_dump.c +++ b/tools/lib/bpf/btf_dump.c @@ -752,41 +752,6 @@ static void btf_dump_emit_type(struct btf_dump *d, __u32 id, __u32 cont_id) } } -static int btf_align_of(const struct btf *btf, __u32 id) -{ - const struct btf_type *t = btf__type_by_id(btf, id); - __u16 kind = btf_kind(t); - - switch (kind) { - case BTF_KIND_INT: - case BTF_KIND_ENUM: - return min(sizeof(void *), t->size); - case BTF_KIND_PTR: - return sizeof(void *); - case BTF_KIND_TYPEDEF: - case BTF_KIND_VOLATILE: - case BTF_KIND_CONST: - case BTF_KIND_RESTRICT: - return btf_align_of(btf, t->type); - case BTF_KIND_ARRAY: - return btf_align_of(btf, btf_array(t)->type); - case BTF_KIND_STRUCT: - case BTF_KIND_UNION: { - const struct btf_member *m = btf_members(t); - __u16 vlen = btf_vlen(t); - int i, align = 1; - - for (i = 0; i < vlen; i++, m++) - align = max(align, btf_align_of(btf, m->type)); - - return align; - } - default: - pr_warn("unsupported BTF_KIND:%u\n", btf_kind(t)); - return 1; - } -} - static bool btf_is_struct_packed(const struct btf *btf, __u32 id, const struct btf_type *t) { @@ -794,18 +759,18 @@ static bool btf_is_struct_packed(const struct btf *btf, __u32 id, int align, i, bit_sz; __u16 vlen; - align = btf_align_of(btf, id); + align = btf__align_of(btf, id); /* size of a non-packed struct has to be a multiple of its alignment*/ - if (t->size % align) + if (align && t->size % align) return true; m = btf_members(t); vlen = btf_vlen(t); /* all non-bitfield fields have to be naturally aligned */ for (i = 0; i < vlen; i++, m++) { - align = btf_align_of(btf, m->type); + align = btf__align_of(btf, m->type); bit_sz = btf_member_bitfield_size(t, i); - if (bit_sz == 0 && m->offset % (8 * align) != 0) + if (align && bit_sz == 0 && m->offset % (8 * align) != 0) return true; } @@ -889,7 +854,7 @@ static void btf_dump_emit_struct_def(struct btf_dump *d, fname = btf_name_of(d, m->name_off); m_sz = btf_member_bitfield_size(t, i); m_off = btf_member_bit_offset(t, i); - align = packed ? 1 : btf_align_of(d->btf, m->type); + align = packed ? 1 : btf__align_of(d->btf, m->type); btf_dump_emit_bit_padding(d, off, m_off, m_sz, align, lvl + 1); btf_dump_printf(d, "\n%s", pfx(lvl + 1)); @@ -907,7 +872,7 @@ static void btf_dump_emit_struct_def(struct btf_dump *d, /* pad at the end, if necessary */ if (is_struct) { - align = packed ? 1 : btf_align_of(d->btf, id); + align = packed ? 1 : btf__align_of(d->btf, id); btf_dump_emit_bit_padding(d, off, t->size * 8, 0, align, lvl + 1); } diff --git a/tools/lib/bpf/libbpf.map b/tools/lib/bpf/libbpf.map index 757a88f64b5a4..e7fcca36f1864 100644 --- a/tools/lib/bpf/libbpf.map +++ b/tools/lib/bpf/libbpf.map @@ -212,4 +212,5 @@ LIBBPF_0.0.6 { LIBBPF_0.0.7 { global: bpf_program__attach; + btf__align_of; } LIBBPF_0.0.6; -- GitLab From 9f81654eebe8de7e0db15534816d8f6c84b2e1e5 Mon Sep 17 00:00:00 2001 From: Andrii Nakryiko Date: Fri, 13 Dec 2019 17:43:31 -0800 Subject: [PATCH 0690/1437] libbpf: Expose BTF-to-C type declaration emitting API Expose API that allows to emit type declaration and field/variable definition (if optional field name is specified) in valid C syntax for any provided BTF type. This is going to be used by bpftool when emitting data section layout as a struct. As part of making this API useful in a stand-alone fashion, move initialization of some of the internal btf_dump state to earlier phase. Signed-off-by: Andrii Nakryiko Signed-off-by: Alexei Starovoitov Acked-by: Martin KaFai Lau Link: https://lore.kernel.org/bpf/20191214014341.3442258-8-andriin@fb.com --- tools/lib/bpf/btf.h | 22 +++++++++++++ tools/lib/bpf/btf_dump.c | 68 +++++++++++++++++++++++++--------------- tools/lib/bpf/libbpf.map | 1 + 3 files changed, 65 insertions(+), 26 deletions(-) diff --git a/tools/lib/bpf/btf.h b/tools/lib/bpf/btf.h index a114c8ef4f082..8d73f7f5551ff 100644 --- a/tools/lib/bpf/btf.h +++ b/tools/lib/bpf/btf.h @@ -126,6 +126,28 @@ LIBBPF_API void btf_dump__free(struct btf_dump *d); LIBBPF_API int btf_dump__dump_type(struct btf_dump *d, __u32 id); +struct btf_dump_emit_type_decl_opts { + /* size of this struct, for forward/backward compatiblity */ + size_t sz; + /* optional field name for type declaration, e.g.: + * - struct my_struct + * - void (*)(int) + * - char (*)[123] + */ + const char *field_name; + /* extra indentation level (in number of tabs) to emit for multi-line + * type declarations (e.g., anonymous struct); applies for lines + * starting from the second one (first line is assumed to have + * necessary indentation already + */ + int indent_level; +}; +#define btf_dump_emit_type_decl_opts__last_field indent_level + +LIBBPF_API int +btf_dump__emit_type_decl(struct btf_dump *d, __u32 id, + const struct btf_dump_emit_type_decl_opts *opts); + /* * A set of helpers for easier BTF types handling */ diff --git a/tools/lib/bpf/btf_dump.c b/tools/lib/bpf/btf_dump.c index 53393026d085b..e95f7710f2101 100644 --- a/tools/lib/bpf/btf_dump.c +++ b/tools/lib/bpf/btf_dump.c @@ -116,6 +116,8 @@ static void btf_dump_printf(const struct btf_dump *d, const char *fmt, ...) va_end(args); } +static int btf_dump_mark_referenced(struct btf_dump *d); + struct btf_dump *btf_dump__new(const struct btf *btf, const struct btf_ext *btf_ext, const struct btf_dump_opts *opts, @@ -137,18 +139,39 @@ struct btf_dump *btf_dump__new(const struct btf *btf, if (IS_ERR(d->type_names)) { err = PTR_ERR(d->type_names); d->type_names = NULL; - btf_dump__free(d); - return ERR_PTR(err); } d->ident_names = hashmap__new(str_hash_fn, str_equal_fn, NULL); if (IS_ERR(d->ident_names)) { err = PTR_ERR(d->ident_names); d->ident_names = NULL; - btf_dump__free(d); - return ERR_PTR(err); + goto err; + } + d->type_states = calloc(1 + btf__get_nr_types(d->btf), + sizeof(d->type_states[0])); + if (!d->type_states) { + err = -ENOMEM; + goto err; + } + d->cached_names = calloc(1 + btf__get_nr_types(d->btf), + sizeof(d->cached_names[0])); + if (!d->cached_names) { + err = -ENOMEM; + goto err; } + /* VOID is special */ + d->type_states[0].order_state = ORDERED; + d->type_states[0].emit_state = EMITTED; + + /* eagerly determine referenced types for anon enums */ + err = btf_dump_mark_referenced(d); + if (err) + goto err; + return d; +err: + btf_dump__free(d); + return ERR_PTR(err); } void btf_dump__free(struct btf_dump *d) @@ -175,7 +198,6 @@ void btf_dump__free(struct btf_dump *d) free(d); } -static int btf_dump_mark_referenced(struct btf_dump *d); static int btf_dump_order_type(struct btf_dump *d, __u32 id, bool through_ptr); static void btf_dump_emit_type(struct btf_dump *d, __u32 id, __u32 cont_id); @@ -202,27 +224,6 @@ int btf_dump__dump_type(struct btf_dump *d, __u32 id) if (id > btf__get_nr_types(d->btf)) return -EINVAL; - /* type states are lazily allocated, as they might not be needed */ - if (!d->type_states) { - d->type_states = calloc(1 + btf__get_nr_types(d->btf), - sizeof(d->type_states[0])); - if (!d->type_states) - return -ENOMEM; - d->cached_names = calloc(1 + btf__get_nr_types(d->btf), - sizeof(d->cached_names[0])); - if (!d->cached_names) - return -ENOMEM; - - /* VOID is special */ - d->type_states[0].order_state = ORDERED; - d->type_states[0].emit_state = EMITTED; - - /* eagerly determine referenced types for anon enums */ - err = btf_dump_mark_referenced(d); - if (err) - return err; - } - d->emit_queue_cnt = 0; err = btf_dump_order_type(d, id, false); if (err < 0) @@ -1016,6 +1017,21 @@ static int btf_dump_push_decl_stack_id(struct btf_dump *d, __u32 id) * of a stack frame. Some care is required to "pop" stack frames after * processing type declaration chain. */ +int btf_dump__emit_type_decl(struct btf_dump *d, __u32 id, + const struct btf_dump_emit_type_decl_opts *opts) +{ + const char *fname; + int lvl; + + if (!OPTS_VALID(opts, btf_dump_emit_type_decl_opts)) + return -EINVAL; + + fname = OPTS_GET(opts, field_name, NULL); + lvl = OPTS_GET(opts, indent_level, 0); + btf_dump_emit_type_decl(d, id, fname, lvl); + return 0; +} + static void btf_dump_emit_type_decl(struct btf_dump *d, __u32 id, const char *fname, int lvl) { diff --git a/tools/lib/bpf/libbpf.map b/tools/lib/bpf/libbpf.map index e7fcca36f1864..990c7c0e2d9f5 100644 --- a/tools/lib/bpf/libbpf.map +++ b/tools/lib/bpf/libbpf.map @@ -211,6 +211,7 @@ LIBBPF_0.0.6 { LIBBPF_0.0.7 { global: + btf_dump__emit_type_decl; bpf_program__attach; btf__align_of; } LIBBPF_0.0.6; -- GitLab From 01af3bf06755dc5cda7050fe5d898998e5775e63 Mon Sep 17 00:00:00 2001 From: Andrii Nakryiko Date: Fri, 13 Dec 2019 17:43:32 -0800 Subject: [PATCH 0691/1437] libbpf: Expose BPF program's function name Add APIs to get BPF program function name, as opposed to bpf_program__title(), which returns BPF program function's section name. Function name has a benefit of being a valid C identifier and uniquely identifies a specific BPF program, while section name can be duplicated across multiple independent BPF programs. Add also bpf_object__find_program_by_name(), similar to bpf_object__find_program_by_title(), to facilitate looking up BPF programs by their C function names. Convert one of selftests to new API for look up. Signed-off-by: Andrii Nakryiko Signed-off-by: Alexei Starovoitov Acked-by: Martin KaFai Lau Link: https://lore.kernel.org/bpf/20191214014341.3442258-9-andriin@fb.com --- tools/lib/bpf/libbpf.c | 28 +++++++++++++++---- tools/lib/bpf/libbpf.h | 9 ++++-- tools/lib/bpf/libbpf.map | 2 ++ .../selftests/bpf/prog_tests/rdonly_maps.c | 11 +++----- 4 files changed, 36 insertions(+), 14 deletions(-) diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c index 61b8cdf783325..e7a6b57d849c9 100644 --- a/tools/lib/bpf/libbpf.c +++ b/tools/lib/bpf/libbpf.c @@ -209,8 +209,8 @@ static const char * const libbpf_type_to_btf_name[] = { }; struct bpf_map { - int fd; char *name; + int fd; int sec_idx; size_t sec_offset; int map_ifindex; @@ -1384,7 +1384,7 @@ static int bpf_object__init_user_btf_maps(struct bpf_object *obj, bool strict, } static int bpf_object__init_maps(struct bpf_object *obj, - struct bpf_object_open_opts *opts) + const struct bpf_object_open_opts *opts) { const char *pin_root_path = OPTS_GET(opts, pin_root_path, NULL); bool strict = !OPTS_GET(opts, relaxed_maps, false); @@ -1748,6 +1748,19 @@ bpf_object__find_program_by_title(const struct bpf_object *obj, return NULL; } +struct bpf_program * +bpf_object__find_program_by_name(const struct bpf_object *obj, + const char *name) +{ + struct bpf_program *prog; + + bpf_object__for_each_program(prog, obj) { + if (!strcmp(prog->name, name)) + return prog; + } + return NULL; +} + static bool bpf_object__shndx_is_data(const struct bpf_object *obj, int shndx) { @@ -3894,7 +3907,7 @@ static int libbpf_find_attach_btf_id(const char *name, __u32 attach_prog_fd); static struct bpf_object * __bpf_object__open(const char *path, const void *obj_buf, size_t obj_buf_sz, - struct bpf_object_open_opts *opts) + const struct bpf_object_open_opts *opts) { struct bpf_program *prog; struct bpf_object *obj; @@ -4003,7 +4016,7 @@ struct bpf_object *bpf_object__open(const char *path) } struct bpf_object * -bpf_object__open_file(const char *path, struct bpf_object_open_opts *opts) +bpf_object__open_file(const char *path, const struct bpf_object_open_opts *opts) { if (!path) return ERR_PTR(-EINVAL); @@ -4015,7 +4028,7 @@ bpf_object__open_file(const char *path, struct bpf_object_open_opts *opts) struct bpf_object * bpf_object__open_mem(const void *obj_buf, size_t obj_buf_sz, - struct bpf_object_open_opts *opts) + const struct bpf_object_open_opts *opts) { if (!obj_buf || obj_buf_sz == 0) return ERR_PTR(-EINVAL); @@ -4820,6 +4833,11 @@ void bpf_program__set_ifindex(struct bpf_program *prog, __u32 ifindex) prog->prog_ifindex = ifindex; } +const char *bpf_program__name(const struct bpf_program *prog) +{ + return prog->name; +} + const char *bpf_program__title(const struct bpf_program *prog, bool needs_copy) { const char *title; diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h index 49e6fa01024b7..f37bd4a3e14b2 100644 --- a/tools/lib/bpf/libbpf.h +++ b/tools/lib/bpf/libbpf.h @@ -90,10 +90,10 @@ struct bpf_object_open_opts { LIBBPF_API struct bpf_object *bpf_object__open(const char *path); LIBBPF_API struct bpf_object * -bpf_object__open_file(const char *path, struct bpf_object_open_opts *opts); +bpf_object__open_file(const char *path, const struct bpf_object_open_opts *opts); LIBBPF_API struct bpf_object * bpf_object__open_mem(const void *obj_buf, size_t obj_buf_sz, - struct bpf_object_open_opts *opts); + const struct bpf_object_open_opts *opts); /* deprecated bpf_object__open variants */ LIBBPF_API struct bpf_object * @@ -132,6 +132,7 @@ struct bpf_object_load_attr { LIBBPF_API int bpf_object__load(struct bpf_object *obj); LIBBPF_API int bpf_object__load_xattr(struct bpf_object_load_attr *attr); LIBBPF_API int bpf_object__unload(struct bpf_object *obj); + LIBBPF_API const char *bpf_object__name(const struct bpf_object *obj); LIBBPF_API unsigned int bpf_object__kversion(const struct bpf_object *obj); @@ -142,6 +143,9 @@ LIBBPF_API int bpf_object__btf_fd(const struct bpf_object *obj); LIBBPF_API struct bpf_program * bpf_object__find_program_by_title(const struct bpf_object *obj, const char *title); +LIBBPF_API struct bpf_program * +bpf_object__find_program_by_name(const struct bpf_object *obj, + const char *name); LIBBPF_API struct bpf_object *bpf_object__next(struct bpf_object *prev); #define bpf_object__for_each_safe(pos, tmp) \ @@ -185,6 +189,7 @@ LIBBPF_API void *bpf_program__priv(const struct bpf_program *prog); LIBBPF_API void bpf_program__set_ifindex(struct bpf_program *prog, __u32 ifindex); +LIBBPF_API const char *bpf_program__name(const struct bpf_program *prog); LIBBPF_API const char *bpf_program__title(const struct bpf_program *prog, bool needs_copy); diff --git a/tools/lib/bpf/libbpf.map b/tools/lib/bpf/libbpf.map index 990c7c0e2d9f5..5a7630748eebd 100644 --- a/tools/lib/bpf/libbpf.map +++ b/tools/lib/bpf/libbpf.map @@ -212,6 +212,8 @@ LIBBPF_0.0.6 { LIBBPF_0.0.7 { global: btf_dump__emit_type_decl; + bpf_object__find_program_by_name; bpf_program__attach; + bpf_program__name; btf__align_of; } LIBBPF_0.0.6; diff --git a/tools/testing/selftests/bpf/prog_tests/rdonly_maps.c b/tools/testing/selftests/bpf/prog_tests/rdonly_maps.c index d90acc13d1ecf..563e12120e77a 100644 --- a/tools/testing/selftests/bpf/prog_tests/rdonly_maps.c +++ b/tools/testing/selftests/bpf/prog_tests/rdonly_maps.c @@ -16,14 +16,11 @@ struct rdonly_map_subtest { void test_rdonly_maps(void) { - const char *prog_name_skip_loop = "raw_tracepoint/sys_enter:skip_loop"; - const char *prog_name_part_loop = "raw_tracepoint/sys_enter:part_loop"; - const char *prog_name_full_loop = "raw_tracepoint/sys_enter:full_loop"; const char *file = "test_rdonly_maps.o"; struct rdonly_map_subtest subtests[] = { - { "skip loop", prog_name_skip_loop, 0, 0 }, - { "part loop", prog_name_part_loop, 3, 2 + 3 + 4 }, - { "full loop", prog_name_full_loop, 4, 2 + 3 + 4 + 5 }, + { "skip loop", "skip_loop", 0, 0 }, + { "part loop", "part_loop", 3, 2 + 3 + 4 }, + { "full loop", "full_loop", 4, 2 + 3 + 4 + 5 }, }; int i, err, zero = 0, duration = 0; struct bpf_link *link = NULL; @@ -50,7 +47,7 @@ void test_rdonly_maps(void) if (!test__start_subtest(t->subtest_name)) continue; - prog = bpf_object__find_program_by_title(obj, t->prog_name); + prog = bpf_object__find_program_by_name(obj, t->prog_name); if (CHECK(!prog, "find_prog", "prog '%s' not found\n", t->prog_name)) goto cleanup; -- GitLab From eba9c5f498a11a8558e099d14692c24f2fea27e3 Mon Sep 17 00:00:00 2001 From: Andrii Nakryiko Date: Fri, 13 Dec 2019 17:43:33 -0800 Subject: [PATCH 0692/1437] libbpf: Refactor global data map initialization Refactor global data map initialization to use anonymous mmap()-ed memory instead of malloc()-ed one. This allows to do a transparent re-mmap()-ing of already existing memory address to point to BPF map's memory after bpf_object__load() step (done in follow up patch). This choreographed setup allows to have a nice and unsurprising way to pre-initialize read-only (and r/w as well) maps by user and after BPF map creation keep working with mmap()-ed contents of this map. All in a way that doesn't require user code to update any pointers: the illusion of working with memory contents is preserved before and after actual BPF map instantiation. Selftests and runqslower example demonstrate this feature in follow up patches. Signed-off-by: Andrii Nakryiko Signed-off-by: Alexei Starovoitov Acked-by: Martin KaFai Lau Link: https://lore.kernel.org/bpf/20191214014341.3442258-10-andriin@fb.com --- tools/lib/bpf/libbpf.c | 97 +++++++++++++++++++++++++----------------- 1 file changed, 59 insertions(+), 38 deletions(-) diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c index e7a6b57d849c9..06dfa36ed0bbc 100644 --- a/tools/lib/bpf/libbpf.c +++ b/tools/lib/bpf/libbpf.c @@ -221,16 +221,12 @@ struct bpf_map { void *priv; bpf_map_clear_priv_t clear_priv; enum libbpf_map_type libbpf_type; + void *mmaped; char *pin_path; bool pinned; bool reused; }; -struct bpf_secdata { - void *rodata; - void *data; -}; - static LIST_HEAD(bpf_objects_list); struct bpf_object { @@ -243,7 +239,6 @@ struct bpf_object { struct bpf_map *maps; size_t nr_maps; size_t maps_cap; - struct bpf_secdata sections; bool loaded; bool has_pseudo_calls; @@ -828,13 +823,24 @@ static struct bpf_map *bpf_object__add_map(struct bpf_object *obj) return &obj->maps[obj->nr_maps++]; } +static size_t bpf_map_mmap_sz(const struct bpf_map *map) +{ + long page_sz = sysconf(_SC_PAGE_SIZE); + size_t map_sz; + + map_sz = roundup(map->def.value_size, 8) * map->def.max_entries; + map_sz = roundup(map_sz, page_sz); + return map_sz; +} + static int bpf_object__init_internal_map(struct bpf_object *obj, enum libbpf_map_type type, - int sec_idx, Elf_Data *data, void **data_buff) + int sec_idx, void *data, size_t data_sz) { char map_name[BPF_OBJ_NAME_LEN]; struct bpf_map_def *def; struct bpf_map *map; + int err; map = bpf_object__add_map(obj); if (IS_ERR(map)) @@ -854,7 +860,7 @@ bpf_object__init_internal_map(struct bpf_object *obj, enum libbpf_map_type type, def = &map->def; def->type = BPF_MAP_TYPE_ARRAY; def->key_size = sizeof(int); - def->value_size = data->d_size; + def->value_size = data_sz; def->max_entries = 1; def->map_flags = type == LIBBPF_MAP_RODATA ? BPF_F_RDONLY_PROG : 0; def->map_flags |= BPF_F_MMAPABLE; @@ -862,16 +868,20 @@ bpf_object__init_internal_map(struct bpf_object *obj, enum libbpf_map_type type, pr_debug("map '%s' (global data): at sec_idx %d, offset %zu, flags %x.\n", map_name, map->sec_idx, map->sec_offset, def->map_flags); - if (data_buff) { - *data_buff = malloc(data->d_size); - if (!*data_buff) { - zfree(&map->name); - pr_warn("failed to alloc map content buffer\n"); - return -ENOMEM; - } - memcpy(*data_buff, data->d_buf, data->d_size); + map->mmaped = mmap(NULL, bpf_map_mmap_sz(map), PROT_READ | PROT_WRITE, + MAP_SHARED | MAP_ANONYMOUS, -1, 0); + if (map->mmaped == MAP_FAILED) { + err = -errno; + map->mmaped = NULL; + pr_warn("failed to alloc map '%s' content buffer: %d\n", + map->name, err); + zfree(&map->name); + return err; } + if (type != LIBBPF_MAP_BSS) + memcpy(map->mmaped, data, data_sz); + pr_debug("map %td is \"%s\"\n", map - obj->maps, map->name); return 0; } @@ -886,23 +896,24 @@ static int bpf_object__init_global_data_maps(struct bpf_object *obj) if (obj->efile.data_shndx >= 0) { err = bpf_object__init_internal_map(obj, LIBBPF_MAP_DATA, obj->efile.data_shndx, - obj->efile.data, - &obj->sections.data); + obj->efile.data->d_buf, + obj->efile.data->d_size); if (err) return err; } if (obj->efile.rodata_shndx >= 0) { err = bpf_object__init_internal_map(obj, LIBBPF_MAP_RODATA, obj->efile.rodata_shndx, - obj->efile.rodata, - &obj->sections.rodata); + obj->efile.rodata->d_buf, + obj->efile.rodata->d_size); if (err) return err; } if (obj->efile.bss_shndx >= 0) { err = bpf_object__init_internal_map(obj, LIBBPF_MAP_BSS, obj->efile.bss_shndx, - obj->efile.bss, NULL); + NULL, + obj->efile.bss->d_size); if (err) return err; } @@ -2292,27 +2303,32 @@ bpf_object__populate_internal_map(struct bpf_object *obj, struct bpf_map *map) { char *cp, errmsg[STRERR_BUFSIZE]; int err, zero = 0; - __u8 *data; /* Nothing to do here since kernel already zero-initializes .bss map. */ if (map->libbpf_type == LIBBPF_MAP_BSS) return 0; - data = map->libbpf_type == LIBBPF_MAP_DATA ? - obj->sections.data : obj->sections.rodata; + err = bpf_map_update_elem(map->fd, &zero, map->mmaped, 0); + if (err) { + err = -errno; + cp = libbpf_strerror_r(err, errmsg, sizeof(errmsg)); + pr_warn("Error setting initial map(%s) contents: %s\n", + map->name, cp); + return err; + } - err = bpf_map_update_elem(map->fd, &zero, data, 0); /* Freeze .rodata map as read-only from syscall side. */ - if (!err && map->libbpf_type == LIBBPF_MAP_RODATA) { + if (map->libbpf_type == LIBBPF_MAP_RODATA) { err = bpf_map_freeze(map->fd); if (err) { - cp = libbpf_strerror_r(errno, errmsg, sizeof(errmsg)); + err = -errno; + cp = libbpf_strerror_r(err, errmsg, sizeof(errmsg)); pr_warn("Error freezing map(%s) as read-only: %s\n", map->name, cp); - err = 0; + return err; } } - return err; + return 0; } static int @@ -4683,17 +4699,22 @@ void bpf_object__close(struct bpf_object *obj) btf_ext__free(obj->btf_ext); for (i = 0; i < obj->nr_maps; i++) { - zfree(&obj->maps[i].name); - zfree(&obj->maps[i].pin_path); - if (obj->maps[i].clear_priv) - obj->maps[i].clear_priv(&obj->maps[i], - obj->maps[i].priv); - obj->maps[i].priv = NULL; - obj->maps[i].clear_priv = NULL; + struct bpf_map *map = &obj->maps[i]; + + if (map->clear_priv) + map->clear_priv(map, map->priv); + map->priv = NULL; + map->clear_priv = NULL; + + if (map->mmaped) { + munmap(map->mmaped, bpf_map_mmap_sz(map)); + map->mmaped = NULL; + } + + zfree(&map->name); + zfree(&map->pin_path); } - zfree(&obj->sections.rodata); - zfree(&obj->sections.data); zfree(&obj->maps); obj->nr_maps = 0; -- GitLab From 13acb508ae203075134327551a6705e6e8f23d48 Mon Sep 17 00:00:00 2001 From: Andrii Nakryiko Date: Fri, 13 Dec 2019 17:43:34 -0800 Subject: [PATCH 0693/1437] libbpf: Postpone BTF ID finding for TRACING programs to load phase Move BTF ID determination for BPF_PROG_TYPE_TRACING programs to a load phase. Performing it at open step is inconvenient, because it prevents BPF skeleton generation on older host kernel, which doesn't contain BTF_KIND_FUNCs information in vmlinux BTF. This is a common set up, though, when, e.g., selftests are compiled on older host kernel, but the test program itself is executed in qemu VM with bleeding edge kernel. Having this BTF searching performed at load time allows to successfully use bpf_object__open() for codegen and inspection of BPF object file. Signed-off-by: Andrii Nakryiko Signed-off-by: Alexei Starovoitov Acked-by: Martin KaFai Lau Link: https://lore.kernel.org/bpf/20191214014341.3442258-11-andriin@fb.com --- tools/lib/bpf/libbpf.c | 37 ++++++++++++++++++------------------- 1 file changed, 18 insertions(+), 19 deletions(-) diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c index 06dfa36ed0bbc..3488ac4f70158 100644 --- a/tools/lib/bpf/libbpf.c +++ b/tools/lib/bpf/libbpf.c @@ -3815,11 +3815,22 @@ out: return ret; } -int -bpf_program__load(struct bpf_program *prog, - char *license, __u32 kern_version) +static int libbpf_find_attach_btf_id(const char *name, + enum bpf_attach_type attach_type, + __u32 attach_prog_fd); + +int bpf_program__load(struct bpf_program *prog, char *license, __u32 kern_ver) { - int err = 0, fd, i; + int err = 0, fd, i, btf_id; + + if (prog->type == BPF_PROG_TYPE_TRACING) { + btf_id = libbpf_find_attach_btf_id(prog->section_name, + prog->expected_attach_type, + prog->attach_prog_fd); + if (btf_id <= 0) + return btf_id; + prog->attach_btf_id = btf_id; + } if (prog->instances.nr < 0 || !prog->instances.fds) { if (prog->preprocessor) { @@ -3843,7 +3854,7 @@ bpf_program__load(struct bpf_program *prog, prog->section_name, prog->instances.nr); } err = load_program(prog, prog->insns, prog->insns_cnt, - license, kern_version, &fd); + license, kern_ver, &fd); if (!err) prog->instances.fds[0] = fd; goto out; @@ -3872,9 +3883,7 @@ bpf_program__load(struct bpf_program *prog, } err = load_program(prog, result.new_insn_ptr, - result.new_insn_cnt, - license, kern_version, &fd); - + result.new_insn_cnt, license, kern_ver, &fd); if (err) { pr_warn("Loading the %dth instance of program '%s' failed\n", i, prog->section_name); @@ -3918,9 +3927,6 @@ bpf_object__load_progs(struct bpf_object *obj, int log_level) return 0; } -static int libbpf_find_attach_btf_id(const char *name, - enum bpf_attach_type attach_type, - __u32 attach_prog_fd); static struct bpf_object * __bpf_object__open(const char *path, const void *obj_buf, size_t obj_buf_sz, const struct bpf_object_open_opts *opts) @@ -3984,15 +3990,8 @@ __bpf_object__open(const char *path, const void *obj_buf, size_t obj_buf_sz, bpf_program__set_type(prog, prog_type); bpf_program__set_expected_attach_type(prog, attach_type); - if (prog_type == BPF_PROG_TYPE_TRACING) { - err = libbpf_find_attach_btf_id(prog->section_name, - attach_type, - attach_prog_fd); - if (err <= 0) - goto out; - prog->attach_btf_id = err; + if (prog_type == BPF_PROG_TYPE_TRACING) prog->attach_prog_fd = attach_prog_fd; - } } return obj; -- GitLab From 3f51935314b8c0e0f45f28bed2e7a023b2c7627c Mon Sep 17 00:00:00 2001 From: Andrii Nakryiko Date: Fri, 13 Dec 2019 17:43:35 -0800 Subject: [PATCH 0694/1437] libbpf: Reduce log level of supported section names dump It's quite spammy. And now that bpf_object__open() is trying to determine program type from its section name, we are getting these verbose messages all the time. Reduce their log level to DEBUG. Signed-off-by: Andrii Nakryiko Signed-off-by: Alexei Starovoitov Acked-by: Martin KaFai Lau Link: https://lore.kernel.org/bpf/20191214014341.3442258-12-andriin@fb.com --- tools/lib/bpf/libbpf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c index 3488ac4f70158..b6bd6c47c9194 100644 --- a/tools/lib/bpf/libbpf.c +++ b/tools/lib/bpf/libbpf.c @@ -5196,7 +5196,7 @@ int libbpf_prog_type_by_name(const char *name, enum bpf_prog_type *prog_type, pr_warn("failed to guess program type from ELF section '%s'\n", name); type_names = libbpf_get_type_names(false); if (type_names != NULL) { - pr_info("supported section(type) names are:%s\n", type_names); + pr_debug("supported section(type) names are:%s\n", type_names); free(type_names); } -- GitLab From d66562fba1ce66975bd61b0786fb8b1810f33caa Mon Sep 17 00:00:00 2001 From: Andrii Nakryiko Date: Fri, 13 Dec 2019 17:43:36 -0800 Subject: [PATCH 0695/1437] libbpf: Add BPF object skeleton support Add new set of APIs, allowing to open/load/attach BPF object through BPF object skeleton, generated by bpftool for a specific BPF object file. All the xxx_skeleton() APIs wrap up corresponding bpf_object_xxx() APIs, but additionally also automate map/program lookups by name, global data initialization and mmap()-ing, etc. All this greatly improves and simplifies userspace usability of working with BPF programs. See follow up patches for examples. Signed-off-by: Andrii Nakryiko Signed-off-by: Alexei Starovoitov Acked-by: Martin KaFai Lau Link: https://lore.kernel.org/bpf/20191214014341.3442258-13-andriin@fb.com --- tools/lib/bpf/libbpf.c | 162 +++++++++++++++++++++++++++++++++++++++ tools/lib/bpf/libbpf.h | 38 +++++++++ tools/lib/bpf/libbpf.map | 5 ++ 3 files changed, 205 insertions(+) diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c index b6bd6c47c9194..a1a902fa6e0c5 100644 --- a/tools/lib/bpf/libbpf.c +++ b/tools/lib/bpf/libbpf.c @@ -6793,3 +6793,165 @@ int libbpf_num_possible_cpus(void) WRITE_ONCE(cpus, tmp_cpus); return tmp_cpus; } + +int bpf_object__open_skeleton(struct bpf_object_skeleton *s, + const struct bpf_object_open_opts *opts) +{ + DECLARE_LIBBPF_OPTS(bpf_object_open_opts, skel_opts, + .object_name = s->name, + ); + struct bpf_object *obj; + int i; + + /* Attempt to preserve opts->object_name, unless overriden by user + * explicitly. Overwriting object name for skeletons is discouraged, + * as it breaks global data maps, because they contain object name + * prefix as their own map name prefix. When skeleton is generated, + * bpftool is making an assumption that this name will stay the same. + */ + if (opts) { + memcpy(&skel_opts, opts, sizeof(*opts)); + if (!opts->object_name) + skel_opts.object_name = s->name; + } + + obj = bpf_object__open_mem(s->data, s->data_sz, &skel_opts); + if (IS_ERR(obj)) { + pr_warn("failed to initialize skeleton BPF object '%s': %ld\n", + s->name, PTR_ERR(obj)); + return PTR_ERR(obj); + } + + *s->obj = obj; + + for (i = 0; i < s->map_cnt; i++) { + struct bpf_map **map = s->maps[i].map; + const char *name = s->maps[i].name; + void **mmaped = s->maps[i].mmaped; + + *map = bpf_object__find_map_by_name(obj, name); + if (!*map) { + pr_warn("failed to find skeleton map '%s'\n", name); + return -ESRCH; + } + + if (mmaped) + *mmaped = (*map)->mmaped; + } + + for (i = 0; i < s->prog_cnt; i++) { + struct bpf_program **prog = s->progs[i].prog; + const char *name = s->progs[i].name; + + *prog = bpf_object__find_program_by_name(obj, name); + if (!*prog) { + pr_warn("failed to find skeleton program '%s'\n", name); + return -ESRCH; + } + } + + return 0; +} + +int bpf_object__load_skeleton(struct bpf_object_skeleton *s) +{ + int i, err; + + err = bpf_object__load(*s->obj); + if (err) { + pr_warn("failed to load BPF skeleton '%s': %d\n", s->name, err); + return err; + } + + for (i = 0; i < s->map_cnt; i++) { + struct bpf_map *map = *s->maps[i].map; + size_t mmap_sz = bpf_map_mmap_sz(map); + int prot, map_fd = bpf_map__fd(map); + void **mmaped = s->maps[i].mmaped; + void *remapped; + + if (!mmaped) + continue; + + if (!(map->def.map_flags & BPF_F_MMAPABLE)) { + *mmaped = NULL; + continue; + } + + if (map->def.map_flags & BPF_F_RDONLY_PROG) + prot = PROT_READ; + else + prot = PROT_READ | PROT_WRITE; + + /* Remap anonymous mmap()-ed "map initialization image" as + * a BPF map-backed mmap()-ed memory, but preserving the same + * memory address. This will cause kernel to change process' + * page table to point to a different piece of kernel memory, + * but from userspace point of view memory address (and its + * contents, being identical at this point) will stay the + * same. This mapping will be released by bpf_object__close() + * as per normal clean up procedure, so we don't need to worry + * about it from skeleton's clean up perspective. + */ + remapped = mmap(*mmaped, mmap_sz, prot, MAP_SHARED | MAP_FIXED, + map_fd, 0); + if (remapped == MAP_FAILED) { + err = -errno; + *mmaped = NULL; + pr_warn("failed to re-mmap() map '%s': %d\n", + bpf_map__name(map), err); + return err; + } + } + + return 0; +} + +int bpf_object__attach_skeleton(struct bpf_object_skeleton *s) +{ + int i; + + for (i = 0; i < s->prog_cnt; i++) { + struct bpf_program *prog = *s->progs[i].prog; + struct bpf_link **link = s->progs[i].link; + const struct bpf_sec_def *sec_def; + const char *sec_name = bpf_program__title(prog, false); + + sec_def = find_sec_def(sec_name); + if (!sec_def || !sec_def->attach_fn) + continue; + + *link = sec_def->attach_fn(sec_def, prog); + if (IS_ERR(*link)) { + pr_warn("failed to auto-attach program '%s': %ld\n", + bpf_program__name(prog), PTR_ERR(*link)); + return PTR_ERR(*link); + } + } + + return 0; +} + +void bpf_object__detach_skeleton(struct bpf_object_skeleton *s) +{ + int i; + + for (i = 0; i < s->prog_cnt; i++) { + struct bpf_link **link = s->progs[i].link; + + if (!IS_ERR_OR_NULL(*link)) + bpf_link__destroy(*link); + *link = NULL; + } +} + +void bpf_object__destroy_skeleton(struct bpf_object_skeleton *s) +{ + if (s->progs) + bpf_object__detach_skeleton(s); + if (s->obj) + bpf_object__close(*s->obj); + free(s->maps); + free(s->progs); + free(s); +} diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h index f37bd4a3e14b2..623191e714154 100644 --- a/tools/lib/bpf/libbpf.h +++ b/tools/lib/bpf/libbpf.h @@ -631,6 +631,44 @@ BPF_EMBED_OBJ_DECLARE(NAME) #define BPF_EMBED_OBJ(NAME, PATH) __BPF_EMBED_OBJ(NAME, PATH, 16, ".quad") #endif +struct bpf_map_skeleton { + const char *name; + struct bpf_map **map; + void **mmaped; +}; + +struct bpf_prog_skeleton { + const char *name; + struct bpf_program **prog; + struct bpf_link **link; +}; + +struct bpf_object_skeleton { + size_t sz; /* size of this struct, for forward/backward compatibility */ + + const char *name; + void *data; + size_t data_sz; + + struct bpf_object **obj; + + int map_cnt; + int map_skel_sz; /* sizeof(struct bpf_skeleton_map) */ + struct bpf_map_skeleton *maps; + + int prog_cnt; + int prog_skel_sz; /* sizeof(struct bpf_skeleton_prog) */ + struct bpf_prog_skeleton *progs; +}; + +LIBBPF_API int +bpf_object__open_skeleton(struct bpf_object_skeleton *s, + const struct bpf_object_open_opts *opts); +LIBBPF_API int bpf_object__load_skeleton(struct bpf_object_skeleton *s); +LIBBPF_API int bpf_object__attach_skeleton(struct bpf_object_skeleton *s); +LIBBPF_API void bpf_object__detach_skeleton(struct bpf_object_skeleton *s); +LIBBPF_API void bpf_object__destroy_skeleton(struct bpf_object_skeleton *s); + #ifdef __cplusplus } /* extern "C" */ #endif diff --git a/tools/lib/bpf/libbpf.map b/tools/lib/bpf/libbpf.map index 5a7630748eebd..1376d992a703a 100644 --- a/tools/lib/bpf/libbpf.map +++ b/tools/lib/bpf/libbpf.map @@ -213,6 +213,11 @@ LIBBPF_0.0.7 { global: btf_dump__emit_type_decl; bpf_object__find_program_by_name; + bpf_object__attach_skeleton; + bpf_object__destroy_skeleton; + bpf_object__detach_skeleton; + bpf_object__load_skeleton; + bpf_object__open_skeleton; bpf_program__attach; bpf_program__name; btf__align_of; -- GitLab From 985ead416df39d6fe8e89580cc1db6aa273e0175 Mon Sep 17 00:00:00 2001 From: Andrii Nakryiko Date: Fri, 13 Dec 2019 17:43:37 -0800 Subject: [PATCH 0696/1437] bpftool: Add skeleton codegen command Add `bpftool gen skeleton` command, which takes in compiled BPF .o object file and dumps a BPF skeleton struct and related code to work with that skeleton. Skeleton itself is tailored to a specific structure of provided BPF object file, containing accessors (just plain struct fields) for every map and program, as well as dedicated space for bpf_links. If BPF program is using global variables, corresponding structure definitions of compatible memory layout are emitted as well, making it possible to initialize and subsequently read/update global variables values using simple and clear C syntax for accessing fields. This skeleton majorly improves usability of opening/loading/attaching of BPF object, as well as interacting with it throughout the lifetime of loaded BPF object. Generated skeleton struct has the following structure: struct { /* used by libbpf's skeleton API */ struct bpf_object_skeleton *skeleton; /* bpf_object for libbpf APIs */ struct bpf_object *obj; struct { /* for every defined map in BPF object: */ struct bpf_map *; } maps; struct { /* for every program in BPF object: */ struct bpf_program *; } progs; struct { /* for every program in BPF object: */ struct bpf_link *; } links; /* for every present global data section: */ struct __ { /* memory layout of corresponding data section, * with every defined variable represented as a struct field * with exactly the same type, but without const/volatile * modifiers, e.g.: */ int *my_var_1; ... } *; }; This provides great usability improvements: - no need to look up maps and programs by name, instead just my_obj->maps.my_map or my_obj->progs.my_prog would give necessary bpf_map/bpf_program pointers, which user can pass to existing libbpf APIs; - pre-defined places for bpf_links, which will be automatically populated for program types that libbpf knows how to attach automatically (currently tracepoints, kprobe/kretprobe, raw tracepoint and tracing programs). On tearing down skeleton, all active bpf_links will be destroyed (meaning BPF programs will be detached, if they are attached). For cases in which libbpf doesn't know how to auto-attach BPF program, user can manually create link after loading skeleton and they will be auto-detached on skeleton destruction: my_obj->links.my_fancy_prog = bpf_program__attach_cgroup_whatever( my_obj->progs.my_fancy_prog, rodata->my_var = 123; my_obj__load(skel); /* 123 will be initialization value for my_var */ After load, if kernel supports mmap() for BPF arrays, user can still read (and write for .bss and .data) variables values, but at that point it will be directly mmap()-ed to BPF array, backing global variables. This allows to seamlessly exchange data with BPF side. From userspace program's POV, all the pointers and memory contents stay the same, but mapped kernel memory changes to point to created map. If kernel doesn't yet support mmap() for BPF arrays, it's still possible to use those data section structs to pre-initialize .bss, .data, and .rodata, but after load their pointers will be reset to NULL, allowing user code to gracefully handle this condition, if necessary. Signed-off-by: Andrii Nakryiko Signed-off-by: Alexei Starovoitov Acked-by: Martin KaFai Lau Link: https://lore.kernel.org/bpf/20191214014341.3442258-14-andriin@fb.com --- tools/bpf/bpftool/gen.c | 551 +++++++++++++++++++++++++++++++++++++++ tools/bpf/bpftool/main.c | 3 +- tools/bpf/bpftool/main.h | 1 + 3 files changed, 554 insertions(+), 1 deletion(-) create mode 100644 tools/bpf/bpftool/gen.c diff --git a/tools/bpf/bpftool/gen.c b/tools/bpf/bpftool/gen.c new file mode 100644 index 0000000000000..7379dae35dcaf --- /dev/null +++ b/tools/bpf/bpftool/gen.c @@ -0,0 +1,551 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +/* Copyright (C) 2019 Facebook */ + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "btf.h" +#include "libbpf_internal.h" +#include "json_writer.h" +#include "main.h" + + +#define MAX_OBJ_NAME_LEN 64 + +static void sanitize_identifier(char *name) +{ + int i; + + for (i = 0; name[i]; i++) + if (!isalnum(name[i]) && name[i] != '_') + name[i] = '_'; +} + +static bool str_has_suffix(const char *str, const char *suffix) +{ + size_t i, n1 = strlen(str), n2 = strlen(suffix); + + if (n1 < n2) + return false; + + for (i = 0; i < n2; i++) { + if (str[n1 - i - 1] != suffix[n2 - i - 1]) + return false; + } + + return true; +} + +static void get_obj_name(char *name, const char *file) +{ + /* Using basename() GNU version which doesn't modify arg. */ + strncpy(name, basename(file), MAX_OBJ_NAME_LEN - 1); + name[MAX_OBJ_NAME_LEN - 1] = '\0'; + if (str_has_suffix(name, ".o")) + name[strlen(name) - 2] = '\0'; + sanitize_identifier(name); +} + +static void get_header_guard(char *guard, const char *obj_name) +{ + int i; + + sprintf(guard, "__%s_SKEL_H__", obj_name); + for (i = 0; guard[i]; i++) + guard[i] = toupper(guard[i]); +} + +static const char *get_map_ident(const struct bpf_map *map) +{ + const char *name = bpf_map__name(map); + + if (!bpf_map__is_internal(map)) + return name; + + if (str_has_suffix(name, ".data")) + return "data"; + else if (str_has_suffix(name, ".rodata")) + return "rodata"; + else if (str_has_suffix(name, ".bss")) + return "bss"; + else + return NULL; +} + +static void codegen_btf_dump_printf(void *ct, const char *fmt, va_list args) +{ + vprintf(fmt, args); +} + +static int codegen_datasec_def(struct bpf_object *obj, + struct btf *btf, + struct btf_dump *d, + const struct btf_type *sec, + const char *obj_name) +{ + const char *sec_name = btf__name_by_offset(btf, sec->name_off); + const struct btf_var_secinfo *sec_var = btf_var_secinfos(sec); + int i, err, off = 0, pad_cnt = 0, vlen = btf_vlen(sec); + const char *sec_ident; + char var_ident[256]; + + if (strcmp(sec_name, ".data") == 0) + sec_ident = "data"; + else if (strcmp(sec_name, ".bss") == 0) + sec_ident = "bss"; + else if (strcmp(sec_name, ".rodata") == 0) + sec_ident = "rodata"; + else + return 0; + + printf(" struct %s__%s {\n", obj_name, sec_ident); + for (i = 0; i < vlen; i++, sec_var++) { + const struct btf_type *var = btf__type_by_id(btf, sec_var->type); + const char *var_name = btf__name_by_offset(btf, var->name_off); + DECLARE_LIBBPF_OPTS(btf_dump_emit_type_decl_opts, opts, + .field_name = var_ident, + .indent_level = 2, + ); + int need_off = sec_var->offset, align_off, align; + __u32 var_type_id = var->type; + const struct btf_type *t; + + t = btf__type_by_id(btf, var_type_id); + while (btf_is_mod(t)) { + var_type_id = t->type; + t = btf__type_by_id(btf, var_type_id); + } + + if (off > need_off) { + p_err("Something is wrong for %s's variable #%d: need offset %d, already at %d.\n", + sec_name, i, need_off, off); + return -EINVAL; + } + + align = btf__align_of(btf, var->type); + if (align <= 0) { + p_err("Failed to determine alignment of variable '%s': %d", + var_name, align); + return -EINVAL; + } + + align_off = (off + align - 1) / align * align; + if (align_off != need_off) { + printf("\t\tchar __pad%d[%d];\n", + pad_cnt, need_off - off); + pad_cnt++; + } + + /* sanitize variable name, e.g., for static vars inside + * a function, it's name is '.', + * which we'll turn into a '_' + */ + var_ident[0] = '\0'; + strncat(var_ident, var_name, sizeof(var_ident) - 1); + sanitize_identifier(var_ident); + + printf("\t\t"); + err = btf_dump__emit_type_decl(d, var_type_id, &opts); + if (err) + return err; + printf(";\n"); + + off = sec_var->offset + sec_var->size; + } + printf(" } *%s;\n", sec_ident); + return 0; +} + +static int codegen_datasecs(struct bpf_object *obj, const char *obj_name) +{ + struct btf *btf = bpf_object__btf(obj); + int n = btf__get_nr_types(btf); + struct btf_dump *d; + int i, err = 0; + + d = btf_dump__new(btf, NULL, NULL, codegen_btf_dump_printf); + if (IS_ERR(d)) + return PTR_ERR(d); + + for (i = 1; i <= n; i++) { + const struct btf_type *t = btf__type_by_id(btf, i); + + if (!btf_is_datasec(t)) + continue; + + err = codegen_datasec_def(obj, btf, d, t, obj_name); + if (err) + goto out; + } +out: + btf_dump__free(d); + return err; +} + +static int codegen(const char *template, ...) +{ + const char *src, *end; + int skip_tabs = 0, n; + char *s, *dst; + va_list args; + char c; + + n = strlen(template); + s = malloc(n + 1); + if (!s) + return -ENOMEM; + src = template; + dst = s; + + /* find out "baseline" indentation to skip */ + while ((c = *src++)) { + if (c == '\t') { + skip_tabs++; + } else if (c == '\n') { + break; + } else { + p_err("unrecognized character at pos %td in template '%s'", + src - template - 1, template); + return -EINVAL; + } + } + + while (*src) { + /* skip baseline indentation tabs */ + for (n = skip_tabs; n > 0; n--, src++) { + if (*src != '\t') { + p_err("not enough tabs at pos %td in template '%s'", + src - template - 1, template); + return -EINVAL; + } + } + /* trim trailing whitespace */ + end = strchrnul(src, '\n'); + for (n = end - src; n > 0 && isspace(src[n - 1]); n--) + ; + memcpy(dst, src, n); + dst += n; + if (*end) + *dst++ = '\n'; + src = *end ? end + 1 : end; + } + *dst++ = '\0'; + + /* print out using adjusted template */ + va_start(args, template); + n = vprintf(s, args); + va_end(args); + + free(s); + return n; +} + +static int do_skeleton(int argc, char **argv) +{ + char header_guard[MAX_OBJ_NAME_LEN + sizeof("__SKEL_H__")]; + size_t i, map_cnt = 0, prog_cnt = 0; + char obj_name[MAX_OBJ_NAME_LEN]; + const char *file, *ident; + struct bpf_program *prog; + struct bpf_object *obj; + struct bpf_map *map; + struct btf *btf; + int err = -1; + + if (!REQ_ARGS(1)) { + usage(); + return -1; + } + file = GET_ARG(); + + if (argc) { + p_err("extra unknown arguments"); + return -1; + } + + obj = bpf_object__open_file(file, NULL); + if (IS_ERR(obj)) { + p_err("failed to open BPF object file: %ld", PTR_ERR(obj)); + return -1; + } + + get_obj_name(obj_name, file); + get_header_guard(header_guard, obj_name); + + bpf_object__for_each_map(map, obj) { + ident = get_map_ident(map); + if (!ident) { + p_err("ignoring unrecognized internal map '%s'...", + bpf_map__name(map)); + continue; + } + map_cnt++; + } + bpf_object__for_each_program(prog, obj) { + prog_cnt++; + } + + codegen("\ + \n\ + /* THIS FILE IS AUTOGENERATED! */ \n\ + #ifndef %2$s \n\ + #define %2$s \n\ + \n\ + #include \n\ + #include \n\ + \n\ + struct %1$s { \n\ + struct bpf_object_skeleton *skeleton; \n\ + struct bpf_object *obj; \n\ + ", + obj_name, header_guard + ); + + if (map_cnt) { + printf("\tstruct {\n"); + bpf_object__for_each_map(map, obj) { + ident = get_map_ident(map); + if (!ident) + continue; + printf("\t\tstruct bpf_map *%s;\n", ident); + } + printf("\t} maps;\n"); + } + + if (prog_cnt) { + printf("\tstruct {\n"); + bpf_object__for_each_program(prog, obj) { + printf("\t\tstruct bpf_program *%s;\n", + bpf_program__name(prog)); + } + printf("\t} progs;\n"); + printf("\tstruct {\n"); + bpf_object__for_each_program(prog, obj) { + printf("\t\tstruct bpf_link *%s;\n", + bpf_program__name(prog)); + } + printf("\t} links;\n"); + } + + btf = bpf_object__btf(obj); + if (btf) { + err = codegen_datasecs(obj, obj_name); + if (err) + goto out; + } + + codegen("\ + \n\ + }; \n\ + \n\ + static inline struct bpf_object_skeleton * \n\ + %1$s__create_skeleton(struct %1$s *obj, struct bpf_embed_data *embed)\n\ + { \n\ + struct bpf_object_skeleton *s; \n\ + \n\ + s = calloc(1, sizeof(*s)); \n\ + if (!s) \n\ + return NULL; \n\ + \n\ + s->sz = sizeof(*s); \n\ + s->name = \"%1$s\"; \n\ + s->data = embed->data; \n\ + s->data_sz = embed->size; \n\ + s->obj = &obj->obj; \n\ + ", + obj_name + ); + if (map_cnt) { + codegen("\ + \n\ + \n\ + /* maps */ \n\ + s->map_cnt = %zu; \n\ + s->map_skel_sz = sizeof(*s->maps); \n\ + s->maps = calloc(s->map_cnt, s->map_skel_sz);\n\ + if (!s->maps) \n\ + goto err; \n\ + ", + map_cnt + ); + i = 0; + bpf_object__for_each_map(map, obj) { + const char *ident = get_map_ident(map); + + if (!ident) + continue; + + codegen("\ + \n\ + \n\ + s->maps[%zu].name = \"%s\"; \n\ + s->maps[%zu].map = &obj->maps.%s; \n\ + ", + i, bpf_map__name(map), i, ident); + /* memory-mapped internal maps */ + if (bpf_map__is_internal(map) && + (bpf_map__def(map)->map_flags & BPF_F_MMAPABLE)) { + printf("\ts->maps[%zu].mmaped = (void **)&obj->%s;\n", + i, ident); + } + i++; + } + } + if (prog_cnt) { + codegen("\ + \n\ + \n\ + /* programs */ \n\ + s->prog_cnt = %zu; \n\ + s->prog_skel_sz = sizeof(*s->progs); \n\ + s->progs = calloc(s->prog_cnt, s->prog_skel_sz);\n\ + if (!s->progs) \n\ + goto err; \n\ + ", + prog_cnt + ); + i = 0; + bpf_object__for_each_program(prog, obj) { + codegen("\ + \n\ + \n\ + s->progs[%1$zu].name = \"%2$s\"; \n\ + s->progs[%1$zu].prog = &obj->progs.%2$s;\n\ + s->progs[%1$zu].link = &obj->links.%2$s;\n\ + ", + i, bpf_program__name(prog)); + i++; + } + } + codegen("\ + \n\ + \n\ + return s; \n\ + err: \n\ + bpf_object__destroy_skeleton(s); \n\ + return NULL; \n\ + } \n\ + \n\ + static void \n\ + %1$s__destroy(struct %1$s *obj) \n\ + { \n\ + if (!obj) \n\ + return; \n\ + if (obj->skeleton) \n\ + bpf_object__destroy_skeleton(obj->skeleton);\n\ + free(obj); \n\ + } \n\ + \n\ + static inline struct %1$s * \n\ + %1$s__open_opts(struct bpf_embed_data *embed, const struct bpf_object_open_opts *opts)\n\ + { \n\ + struct %1$s *obj; \n\ + \n\ + obj = calloc(1, sizeof(*obj)); \n\ + if (!obj) \n\ + return NULL; \n\ + \n\ + obj->skeleton = %1$s__create_skeleton(obj, embed); \n\ + if (!obj->skeleton) \n\ + goto err; \n\ + \n\ + if (bpf_object__open_skeleton(obj->skeleton, opts)) \n\ + goto err; \n\ + \n\ + return obj; \n\ + err: \n\ + %1$s__destroy(obj); \n\ + return NULL; \n\ + } \n\ + \n\ + static inline struct %1$s * \n\ + %1$s__open(struct bpf_embed_data *embed) \n\ + { \n\ + return %1$s__open_opts(embed, NULL); \n\ + } \n\ + \n\ + static inline int \n\ + %1$s__load(struct %1$s *obj) \n\ + { \n\ + return bpf_object__load_skeleton(obj->skeleton); \n\ + } \n\ + \n\ + static inline struct %1$s * \n\ + %1$s__open_and_load(struct bpf_embed_data *embed) \n\ + { \n\ + struct %1$s *obj; \n\ + \n\ + obj = %1$s__open(embed); \n\ + if (!obj) \n\ + return NULL; \n\ + if (%1$s__load(obj)) { \n\ + %1$s__destroy(obj); \n\ + return NULL; \n\ + } \n\ + return obj; \n\ + } \n\ + \n\ + static inline int \n\ + %1$s__attach(struct %1$s *obj) \n\ + { \n\ + return bpf_object__attach_skeleton(obj->skeleton); \n\ + } \n\ + \n\ + static inline void \n\ + %1$s__detach(struct %1$s *obj) \n\ + { \n\ + return bpf_object__detach_skeleton(obj->skeleton); \n\ + } \n\ + \n\ + #endif /* %2$s */ \n\ + ", + obj_name, header_guard + ); + err = 0; +out: + bpf_object__close(obj); + return err; +} + +static int do_help(int argc, char **argv) +{ + if (json_output) { + jsonw_null(json_wtr); + return 0; + } + + fprintf(stderr, + "Usage: %1$s gen skeleton FILE\n" + " %1$s gen help\n" + "\n" + " " HELP_SPEC_OPTIONS "\n" + "", + bin_name); + + return 0; +} + +static const struct cmd cmds[] = { + { "skeleton", do_skeleton }, + { "help", do_help }, + { 0 } +}; + +int do_gen(int argc, char **argv) +{ + return cmd_select(cmds, argc, argv, do_help); +} diff --git a/tools/bpf/bpftool/main.c b/tools/bpf/bpftool/main.c index 4764581ff9eaf..1fe91c5585083 100644 --- a/tools/bpf/bpftool/main.c +++ b/tools/bpf/bpftool/main.c @@ -58,7 +58,7 @@ static int do_help(int argc, char **argv) " %s batch file FILE\n" " %s version\n" "\n" - " OBJECT := { prog | map | cgroup | perf | net | feature | btf }\n" + " OBJECT := { prog | map | cgroup | perf | net | feature | btf | gen }\n" " " HELP_SPEC_OPTIONS "\n" "", bin_name, bin_name, bin_name); @@ -227,6 +227,7 @@ static const struct cmd cmds[] = { { "net", do_net }, { "feature", do_feature }, { "btf", do_btf }, + { "gen", do_gen }, { "version", do_version }, { 0 } }; diff --git a/tools/bpf/bpftool/main.h b/tools/bpf/bpftool/main.h index 81890f4c8ccad..4e75b58d39898 100644 --- a/tools/bpf/bpftool/main.h +++ b/tools/bpf/bpftool/main.h @@ -155,6 +155,7 @@ int do_net(int argc, char **arg); int do_tracelog(int argc, char **arg); int do_feature(int argc, char **argv); int do_btf(int argc, char **argv); +int do_gen(int argc, char **argv); int parse_u32_arg(int *argc, char ***argv, __u32 *val, const char *what); int prog_parse_fd(int *argc, char ***argv); -- GitLab From f3c926a4df2cddf6230c3f56b1f43e439552cdad Mon Sep 17 00:00:00 2001 From: Andrii Nakryiko Date: Fri, 13 Dec 2019 17:43:38 -0800 Subject: [PATCH 0697/1437] selftests/bpf: Add BPF skeletons selftests and convert attach_probe.c Add BPF skeleton generation to selftest/bpf's Makefile. Convert attach_probe.c to use skeleton. Signed-off-by: Andrii Nakryiko Signed-off-by: Alexei Starovoitov Acked-by: Martin KaFai Lau Link: https://lore.kernel.org/bpf/20191214014341.3442258-15-andriin@fb.com --- tools/testing/selftests/bpf/.gitignore | 2 + tools/testing/selftests/bpf/Makefile | 36 +++-- .../selftests/bpf/prog_tests/attach_probe.c | 135 +++++------------- .../selftests/bpf/progs/test_attach_probe.c | 34 ++--- 4 files changed, 74 insertions(+), 133 deletions(-) diff --git a/tools/testing/selftests/bpf/.gitignore b/tools/testing/selftests/bpf/.gitignore index 419652458da4a..ce5af95ede422 100644 --- a/tools/testing/selftests/bpf/.gitignore +++ b/tools/testing/selftests/bpf/.gitignore @@ -38,5 +38,7 @@ test_hashmap test_btf_dump xdping test_cpp +*.skel.h /no_alu32 /bpf_gcc +/tools diff --git a/tools/testing/selftests/bpf/Makefile b/tools/testing/selftests/bpf/Makefile index 90de7d75b5c64..f70c8e735120a 100644 --- a/tools/testing/selftests/bpf/Makefile +++ b/tools/testing/selftests/bpf/Makefile @@ -3,10 +3,12 @@ include ../../../../scripts/Kbuild.include include ../../../scripts/Makefile.arch CURDIR := $(abspath .) -LIBDIR := $(abspath ../../../lib) +TOOLSDIR := $(abspath ../../..) +LIBDIR := $(TOOLSDIR)/lib BPFDIR := $(LIBDIR)/bpf -TOOLSDIR := $(abspath ../../../include) -APIDIR := $(TOOLSDIR)/uapi +TOOLSINCDIR := $(TOOLSDIR)/include +BPFTOOLDIR := $(TOOLSDIR)/bpf/bpftool +APIDIR := $(TOOLSINCDIR)/uapi GENDIR := $(abspath ../../../../include/generated) GENHDR := $(GENDIR)/autoconf.h @@ -19,7 +21,7 @@ LLC ?= llc LLVM_OBJCOPY ?= llvm-objcopy BPF_GCC ?= $(shell command -v bpf-gcc;) CFLAGS += -g -Wall -O2 $(GENFLAGS) -I$(APIDIR) -I$(LIBDIR) -I$(BPFDIR) \ - -I$(GENDIR) -I$(TOOLSDIR) -I$(CURDIR) \ + -I$(GENDIR) -I$(TOOLSINCDIR) -I$(CURDIR) \ -Dbpf_prog_load=bpf_prog_test_load \ -Dbpf_load_program=bpf_test_load_program LDLIBS += -lcap -lelf -lrt -lpthread @@ -117,6 +119,12 @@ $(OUTPUT)/test_cgroup_attach: cgroup_helpers.c # force a rebuild of BPFOBJ when its dependencies are updated force: +DEFAULT_BPFTOOL := $(OUTPUT)/tools/usr/local/sbin/bpftool +BPFTOOL ?= $(DEFAULT_BPFTOOL) + +$(DEFAULT_BPFTOOL): force + $(MAKE) -C $(BPFTOOLDIR) DESTDIR=$(OUTPUT)/tools install + $(BPFOBJ): force $(MAKE) -C $(BPFDIR) OUTPUT=$(OUTPUT)/ @@ -180,6 +188,8 @@ define GCC_BPF_BUILD_RULE $(BPF_GCC) $3 $4 -O2 -c $1 -o $2 endef +SKEL_BLACKLIST := btf__% test_pinning_invalid.c + # Set up extra TRUNNER_XXX "temporary" variables in the environment (relies on # $eval()) and pass control to DEFINE_TEST_RUNNER_RULES. # Parameters: @@ -195,8 +205,11 @@ TRUNNER_EXTRA_OBJS := $$(patsubst %.c,$$(TRUNNER_OUTPUT)/%.o, \ $$(filter %.c,$(TRUNNER_EXTRA_SOURCES))) TRUNNER_EXTRA_HDRS := $$(filter %.h,$(TRUNNER_EXTRA_SOURCES)) TRUNNER_TESTS_HDR := $(TRUNNER_TESTS_DIR)/tests.h -TRUNNER_BPF_OBJS := $$(patsubst %.c,$$(TRUNNER_OUTPUT)/%.o, \ - $$(notdir $$(wildcard $(TRUNNER_BPF_PROGS_DIR)/*.c))) +TRUNNER_BPF_SRCS := $$(notdir $$(wildcard $(TRUNNER_BPF_PROGS_DIR)/*.c)) +TRUNNER_BPF_OBJS := $$(patsubst %.c,$$(TRUNNER_OUTPUT)/%.o, $$(TRUNNER_BPF_SRCS)) +TRUNNER_BPF_SKELS := $$(patsubst %.c,$$(TRUNNER_OUTPUT)/%.skel.h, \ + $$(filter-out $(SKEL_BLACKLIST), \ + $$(TRUNNER_BPF_SRCS))) # Evaluate rules now with extra TRUNNER_XXX variables above already defined $$(eval $$(call DEFINE_TEST_RUNNER_RULES,$1,$2)) @@ -226,6 +239,11 @@ $(TRUNNER_BPF_OBJS): $(TRUNNER_OUTPUT)/%.o: \ $$(call $(TRUNNER_BPF_BUILD_RULE),$$<,$$@, \ $(TRUNNER_BPF_CFLAGS), \ $(TRUNNER_BPF_LDFLAGS)) + +$(TRUNNER_BPF_SKELS): $(TRUNNER_OUTPUT)/%.skel.h: \ + $(TRUNNER_OUTPUT)/%.o \ + | $(BPFTOOL) $(TRUNNER_OUTPUT) + $$(BPFTOOL) gen skeleton $$< > $$@ endif # ensure we set up tests.h header generation rule just once @@ -245,6 +263,7 @@ $(TRUNNER_TEST_OBJS): $(TRUNNER_OUTPUT)/%.test.o: \ $(TRUNNER_TESTS_DIR)/%.c \ $(TRUNNER_EXTRA_HDRS) \ $(TRUNNER_BPF_OBJS) \ + $(TRUNNER_BPF_SKELS) \ $$(BPFOBJ) | $(TRUNNER_OUTPUT) cd $$(@D) && $$(CC) $$(CFLAGS) -c $(CURDIR)/$$< $$(LDLIBS) -o $$(@F) @@ -255,9 +274,9 @@ $(TRUNNER_EXTRA_OBJS): $(TRUNNER_OUTPUT)/%.o: \ $$(BPFOBJ) | $(TRUNNER_OUTPUT) $$(CC) $$(CFLAGS) -c $$< $$(LDLIBS) -o $$@ +# only copy extra resources if in flavored build $(TRUNNER_BINARY)-extras: $(TRUNNER_EXTRA_FILES) | $(TRUNNER_OUTPUT) ifneq ($2,) - # only copy extra resources if in flavored build cp -a $$^ $(TRUNNER_OUTPUT)/ endif @@ -323,4 +342,5 @@ $(OUTPUT)/test_cpp: test_cpp.cpp $(BPFOBJ) EXTRA_CLEAN := $(TEST_CUSTOM_PROGS) \ prog_tests/tests.h map_tests/tests.h verifier/tests.h \ - feature $(OUTPUT)/*.o $(OUTPUT)/no_alu32 $(OUTPUT)/bpf_gcc + feature $(OUTPUT)/*.o $(OUTPUT)/no_alu32 $(OUTPUT)/bpf_gcc \ + tools *.skel.h diff --git a/tools/testing/selftests/bpf/prog_tests/attach_probe.c b/tools/testing/selftests/bpf/prog_tests/attach_probe.c index b2e7c1424b07f..60da1d08daa0b 100644 --- a/tools/testing/selftests/bpf/prog_tests/attach_probe.c +++ b/tools/testing/selftests/bpf/prog_tests/attach_probe.c @@ -1,5 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 #include +#include "test_attach_probe.skel.h" ssize_t get_base_addr() { size_t start; @@ -25,26 +26,10 @@ BPF_EMBED_OBJ(probe, "test_attach_probe.o"); void test_attach_probe(void) { - const char *kprobe_name = "kprobe/sys_nanosleep"; - const char *kretprobe_name = "kretprobe/sys_nanosleep"; - const char *uprobe_name = "uprobe/trigger_func"; - const char *uretprobe_name = "uretprobe/trigger_func"; - const int kprobe_idx = 0, kretprobe_idx = 1; - const int uprobe_idx = 2, uretprobe_idx = 3; - const char *obj_name = "attach_probe"; - DECLARE_LIBBPF_OPTS(bpf_object_open_opts, open_opts, - .object_name = obj_name, - .relaxed_maps = true, - ); - struct bpf_program *kprobe_prog, *kretprobe_prog; - struct bpf_program *uprobe_prog, *uretprobe_prog; - struct bpf_object *obj; - int err, duration = 0, res; - struct bpf_link *kprobe_link = NULL; - struct bpf_link *kretprobe_link = NULL; - struct bpf_link *uprobe_link = NULL; - struct bpf_link *uretprobe_link = NULL; - int results_map_fd; + int duration = 0; + struct bpf_link *kprobe_link, *kretprobe_link; + struct bpf_link *uprobe_link, *uretprobe_link; + struct test_attach_probe* skel; size_t uprobe_offset; ssize_t base_addr; @@ -54,124 +39,68 @@ void test_attach_probe(void) return; uprobe_offset = (size_t)&get_base_addr - base_addr; - /* open object */ - obj = bpf_object__open_mem(probe_embed.data, probe_embed.size, - &open_opts); - if (CHECK(IS_ERR(obj), "obj_open_mem", "err %ld\n", PTR_ERR(obj))) + skel = test_attach_probe__open_and_load(&probe_embed); + if (CHECK(!skel, "skel_open", "failed to open skeleton\n")) return; - - if (CHECK(strcmp(bpf_object__name(obj), obj_name), "obj_name", - "wrong obj name '%s', expected '%s'\n", - bpf_object__name(obj), obj_name)) - goto cleanup; - - kprobe_prog = bpf_object__find_program_by_title(obj, kprobe_name); - if (CHECK(!kprobe_prog, "find_probe", - "prog '%s' not found\n", kprobe_name)) - goto cleanup; - kretprobe_prog = bpf_object__find_program_by_title(obj, kretprobe_name); - if (CHECK(!kretprobe_prog, "find_probe", - "prog '%s' not found\n", kretprobe_name)) - goto cleanup; - uprobe_prog = bpf_object__find_program_by_title(obj, uprobe_name); - if (CHECK(!uprobe_prog, "find_probe", - "prog '%s' not found\n", uprobe_name)) - goto cleanup; - uretprobe_prog = bpf_object__find_program_by_title(obj, uretprobe_name); - if (CHECK(!uretprobe_prog, "find_probe", - "prog '%s' not found\n", uretprobe_name)) + if (CHECK(!skel->bss, "check_bss", ".bss wasn't mmap()-ed\n")) goto cleanup; - /* create maps && load programs */ - err = bpf_object__load(obj); - if (CHECK(err, "obj_load", "err %d\n", err)) - goto cleanup; - - /* load maps */ - results_map_fd = bpf_find_map(__func__, obj, "results_map"); - if (CHECK(results_map_fd < 0, "find_results_map", - "err %d\n", results_map_fd)) - goto cleanup; - - kprobe_link = bpf_program__attach_kprobe(kprobe_prog, + kprobe_link = bpf_program__attach_kprobe(skel->progs.handle_kprobe, false /* retprobe */, SYS_NANOSLEEP_KPROBE_NAME); if (CHECK(IS_ERR(kprobe_link), "attach_kprobe", - "err %ld\n", PTR_ERR(kprobe_link))) { - kprobe_link = NULL; + "err %ld\n", PTR_ERR(kprobe_link))) goto cleanup; - } - kretprobe_link = bpf_program__attach_kprobe(kretprobe_prog, + skel->links.handle_kprobe = kprobe_link; + + kretprobe_link = bpf_program__attach_kprobe(skel->progs.handle_kretprobe, true /* retprobe */, SYS_NANOSLEEP_KPROBE_NAME); if (CHECK(IS_ERR(kretprobe_link), "attach_kretprobe", - "err %ld\n", PTR_ERR(kretprobe_link))) { - kretprobe_link = NULL; + "err %ld\n", PTR_ERR(kretprobe_link))) goto cleanup; - } - uprobe_link = bpf_program__attach_uprobe(uprobe_prog, + skel->links.handle_kretprobe = kretprobe_link; + + uprobe_link = bpf_program__attach_uprobe(skel->progs.handle_uprobe, false /* retprobe */, 0 /* self pid */, "/proc/self/exe", uprobe_offset); if (CHECK(IS_ERR(uprobe_link), "attach_uprobe", - "err %ld\n", PTR_ERR(uprobe_link))) { - uprobe_link = NULL; + "err %ld\n", PTR_ERR(uprobe_link))) goto cleanup; - } - uretprobe_link = bpf_program__attach_uprobe(uretprobe_prog, + skel->links.handle_uprobe = uprobe_link; + + uretprobe_link = bpf_program__attach_uprobe(skel->progs.handle_uretprobe, true /* retprobe */, -1 /* any pid */, "/proc/self/exe", uprobe_offset); if (CHECK(IS_ERR(uretprobe_link), "attach_uretprobe", - "err %ld\n", PTR_ERR(uretprobe_link))) { - uretprobe_link = NULL; + "err %ld\n", PTR_ERR(uretprobe_link))) goto cleanup; - } + skel->links.handle_uretprobe = uretprobe_link; /* trigger & validate kprobe && kretprobe */ usleep(1); - err = bpf_map_lookup_elem(results_map_fd, &kprobe_idx, &res); - if (CHECK(err, "get_kprobe_res", - "failed to get kprobe res: %d\n", err)) + if (CHECK(skel->bss->kprobe_res != 1, "check_kprobe_res", + "wrong kprobe res: %d\n", skel->bss->kprobe_res)) goto cleanup; - if (CHECK(res != kprobe_idx + 1, "check_kprobe_res", - "wrong kprobe res: %d\n", res)) - goto cleanup; - - err = bpf_map_lookup_elem(results_map_fd, &kretprobe_idx, &res); - if (CHECK(err, "get_kretprobe_res", - "failed to get kretprobe res: %d\n", err)) - goto cleanup; - if (CHECK(res != kretprobe_idx + 1, "check_kretprobe_res", - "wrong kretprobe res: %d\n", res)) + if (CHECK(skel->bss->kretprobe_res != 2, "check_kretprobe_res", + "wrong kretprobe res: %d\n", skel->bss->kretprobe_res)) goto cleanup; /* trigger & validate uprobe & uretprobe */ get_base_addr(); - err = bpf_map_lookup_elem(results_map_fd, &uprobe_idx, &res); - if (CHECK(err, "get_uprobe_res", - "failed to get uprobe res: %d\n", err)) - goto cleanup; - if (CHECK(res != uprobe_idx + 1, "check_uprobe_res", - "wrong uprobe res: %d\n", res)) - goto cleanup; - - err = bpf_map_lookup_elem(results_map_fd, &uretprobe_idx, &res); - if (CHECK(err, "get_uretprobe_res", - "failed to get uretprobe res: %d\n", err)) + if (CHECK(skel->bss->uprobe_res != 3, "check_uprobe_res", + "wrong uprobe res: %d\n", skel->bss->uprobe_res)) goto cleanup; - if (CHECK(res != uretprobe_idx + 1, "check_uretprobe_res", - "wrong uretprobe res: %d\n", res)) + if (CHECK(skel->bss->uretprobe_res != 4, "check_uretprobe_res", + "wrong uretprobe res: %d\n", skel->bss->uretprobe_res)) goto cleanup; cleanup: - bpf_link__destroy(kprobe_link); - bpf_link__destroy(kretprobe_link); - bpf_link__destroy(uprobe_link); - bpf_link__destroy(uretprobe_link); - bpf_object__close(obj); + test_attach_probe__destroy(skel); } diff --git a/tools/testing/selftests/bpf/progs/test_attach_probe.c b/tools/testing/selftests/bpf/progs/test_attach_probe.c index 534621e389069..221b69700625d 100644 --- a/tools/testing/selftests/bpf/progs/test_attach_probe.c +++ b/tools/testing/selftests/bpf/progs/test_attach_probe.c @@ -5,46 +5,36 @@ #include #include "bpf_helpers.h" -struct { - __uint(type, BPF_MAP_TYPE_ARRAY); - __uint(max_entries, 4); - __type(key, int); - __type(value, int); -} results_map SEC(".maps"); +int kprobe_res = 0; +int kretprobe_res = 0; +int uprobe_res = 0; +int uretprobe_res = 0; SEC("kprobe/sys_nanosleep") -int handle_sys_nanosleep_entry(struct pt_regs *ctx) +int handle_kprobe(struct pt_regs *ctx) { - const int key = 0, value = 1; - - bpf_map_update_elem(&results_map, &key, &value, 0); + kprobe_res = 1; return 0; } SEC("kretprobe/sys_nanosleep") -int handle_sys_getpid_return(struct pt_regs *ctx) +int handle_kretprobe(struct pt_regs *ctx) { - const int key = 1, value = 2; - - bpf_map_update_elem(&results_map, &key, &value, 0); + kretprobe_res = 2; return 0; } SEC("uprobe/trigger_func") -int handle_uprobe_entry(struct pt_regs *ctx) +int handle_uprobe(struct pt_regs *ctx) { - const int key = 2, value = 3; - - bpf_map_update_elem(&results_map, &key, &value, 0); + uprobe_res = 3; return 0; } SEC("uretprobe/trigger_func") -int handle_uprobe_return(struct pt_regs *ctx) +int handle_uretprobe(struct pt_regs *ctx) { - const int key = 3, value = 4; - - bpf_map_update_elem(&results_map, &key, &value, 0); + uretprobe_res = 4; return 0; } -- GitLab From dde53c1b763b5038545efa5d812758ce589654e1 Mon Sep 17 00:00:00 2001 From: Andrii Nakryiko Date: Fri, 13 Dec 2019 17:43:39 -0800 Subject: [PATCH 0698/1437] selftests/bpf: Convert few more selftest to skeletons Convert few more selftests to use generated BPF skeletons as a demonstration on how to use it. Signed-off-by: Andrii Nakryiko Signed-off-by: Alexei Starovoitov Acked-by: Martin KaFai Lau Link: https://lore.kernel.org/bpf/20191214014341.3442258-16-andriin@fb.com --- .../selftests/bpf/prog_tests/fentry_fexit.c | 105 ++++++------------ .../selftests/bpf/prog_tests/fentry_test.c | 72 +++++------- tools/testing/selftests/bpf/prog_tests/mmap.c | 58 ++++------ .../bpf/prog_tests/stacktrace_build_id.c | 79 +++++-------- .../bpf/prog_tests/stacktrace_build_id_nmi.c | 84 ++++++-------- 5 files changed, 149 insertions(+), 249 deletions(-) diff --git a/tools/testing/selftests/bpf/prog_tests/fentry_fexit.c b/tools/testing/selftests/bpf/prog_tests/fentry_fexit.c index 40bcff2cc2743..110fcf053fd05 100644 --- a/tools/testing/selftests/bpf/prog_tests/fentry_fexit.c +++ b/tools/testing/selftests/bpf/prog_tests/fentry_fexit.c @@ -1,90 +1,59 @@ // SPDX-License-Identifier: GPL-2.0 /* Copyright (c) 2019 Facebook */ #include +#include "test_pkt_access.skel.h" +#include "fentry_test.skel.h" +#include "fexit_test.skel.h" + +BPF_EMBED_OBJ(pkt_access, "test_pkt_access.o"); +BPF_EMBED_OBJ(fentry, "fentry_test.o"); +BPF_EMBED_OBJ(fexit, "fexit_test.o"); void test_fentry_fexit(void) { - struct bpf_prog_load_attr attr_fentry = { - .file = "./fentry_test.o", - }; - struct bpf_prog_load_attr attr_fexit = { - .file = "./fexit_test.o", - }; - - struct bpf_object *obj_fentry = NULL, *obj_fexit = NULL, *pkt_obj; - struct bpf_map *data_map_fentry, *data_map_fexit; - char fentry_name[] = "fentry/bpf_fentry_testX"; - char fexit_name[] = "fexit/bpf_fentry_testX"; - int err, pkt_fd, kfree_skb_fd, i; - struct bpf_link *link[12] = {}; - struct bpf_program *prog[12]; - __u32 duration, retval; - const int zero = 0; - u64 result[12]; - - err = bpf_prog_load("./test_pkt_access.o", BPF_PROG_TYPE_SCHED_CLS, - &pkt_obj, &pkt_fd); - if (CHECK(err, "prog_load sched cls", "err %d errno %d\n", err, errno)) + struct test_pkt_access *pkt_skel = NULL; + struct fentry_test *fentry_skel = NULL; + struct fexit_test *fexit_skel = NULL; + __u64 *fentry_res, *fexit_res; + __u32 duration = 0, retval; + int err, pkt_fd, i; + + pkt_skel = test_pkt_access__open_and_load(&pkt_access_embed); + if (CHECK(!pkt_skel, "pkt_skel_load", "pkt_access skeleton failed\n")) return; - err = bpf_prog_load_xattr(&attr_fentry, &obj_fentry, &kfree_skb_fd); - if (CHECK(err, "prog_load fail", "err %d errno %d\n", err, errno)) + fentry_skel = fentry_test__open_and_load(&fentry_embed); + if (CHECK(!fentry_skel, "fentry_skel_load", "fentry skeleton failed\n")) goto close_prog; - err = bpf_prog_load_xattr(&attr_fexit, &obj_fexit, &kfree_skb_fd); - if (CHECK(err, "prog_load fail", "err %d errno %d\n", err, errno)) + fexit_skel = fexit_test__open_and_load(&fexit_embed); + if (CHECK(!fexit_skel, "fexit_skel_load", "fexit skeleton failed\n")) goto close_prog; - for (i = 0; i < 6; i++) { - fentry_name[sizeof(fentry_name) - 2] = '1' + i; - prog[i] = bpf_object__find_program_by_title(obj_fentry, fentry_name); - if (CHECK(!prog[i], "find_prog", "prog %s not found\n", fentry_name)) - goto close_prog; - link[i] = bpf_program__attach_trace(prog[i]); - if (CHECK(IS_ERR(link[i]), "attach_trace", "failed to link\n")) - goto close_prog; - } - data_map_fentry = bpf_object__find_map_by_name(obj_fentry, "fentry_t.bss"); - if (CHECK(!data_map_fentry, "find_data_map", "data map not found\n")) + err = fentry_test__attach(fentry_skel); + if (CHECK(err, "fentry_attach", "fentry attach failed: %d\n", err)) goto close_prog; - - for (i = 6; i < 12; i++) { - fexit_name[sizeof(fexit_name) - 2] = '1' + i - 6; - prog[i] = bpf_object__find_program_by_title(obj_fexit, fexit_name); - if (CHECK(!prog[i], "find_prog", "prog %s not found\n", fexit_name)) - goto close_prog; - link[i] = bpf_program__attach_trace(prog[i]); - if (CHECK(IS_ERR(link[i]), "attach_trace", "failed to link\n")) - goto close_prog; - } - data_map_fexit = bpf_object__find_map_by_name(obj_fexit, "fexit_te.bss"); - if (CHECK(!data_map_fexit, "find_data_map", "data map not found\n")) + err = fexit_test__attach(fexit_skel); + if (CHECK(err, "fexit_attach", "fexit attach failed: %d\n", err)) goto close_prog; + pkt_fd = bpf_program__fd(pkt_skel->progs.test_pkt_access); err = bpf_prog_test_run(pkt_fd, 1, &pkt_v6, sizeof(pkt_v6), NULL, NULL, &retval, &duration); CHECK(err || retval, "ipv6", "err %d errno %d retval %d duration %d\n", err, errno, retval, duration); - err = bpf_map_lookup_elem(bpf_map__fd(data_map_fentry), &zero, &result); - if (CHECK(err, "get_result", - "failed to get output data: %d\n", err)) - goto close_prog; - - err = bpf_map_lookup_elem(bpf_map__fd(data_map_fexit), &zero, result + 6); - if (CHECK(err, "get_result", - "failed to get output data: %d\n", err)) - goto close_prog; - - for (i = 0; i < 12; i++) - if (CHECK(result[i] != 1, "result", "bpf_fentry_test%d failed err %ld\n", - i % 6 + 1, result[i])) - goto close_prog; + fentry_res = (__u64 *)fentry_skel->bss; + fexit_res = (__u64 *)fexit_skel->bss; + printf("%lld\n", fentry_skel->bss->test1_result); + for (i = 0; i < 6; i++) { + CHECK(fentry_res[i] != 1, "result", + "fentry_test%d failed err %lld\n", i + 1, fentry_res[i]); + CHECK(fexit_res[i] != 1, "result", + "fexit_test%d failed err %lld\n", i + 1, fexit_res[i]); + } close_prog: - for (i = 0; i < 12; i++) - if (!IS_ERR_OR_NULL(link[i])) - bpf_link__destroy(link[i]); - bpf_object__close(obj_fentry); - bpf_object__close(obj_fexit); - bpf_object__close(pkt_obj); + test_pkt_access__destroy(pkt_skel); + fentry_test__destroy(fentry_skel); + fexit_test__destroy(fexit_skel); } diff --git a/tools/testing/selftests/bpf/prog_tests/fentry_test.c b/tools/testing/selftests/bpf/prog_tests/fentry_test.c index 9fb103193878f..46a4afdf507af 100644 --- a/tools/testing/selftests/bpf/prog_tests/fentry_test.c +++ b/tools/testing/selftests/bpf/prog_tests/fentry_test.c @@ -1,64 +1,46 @@ // SPDX-License-Identifier: GPL-2.0 /* Copyright (c) 2019 Facebook */ #include +#include "test_pkt_access.skel.h" +#include "fentry_test.skel.h" + +BPF_EMBED_OBJ_DECLARE(pkt_access); +BPF_EMBED_OBJ_DECLARE(fentry); void test_fentry_test(void) { - struct bpf_prog_load_attr attr = { - .file = "./fentry_test.o", - }; - - char prog_name[] = "fentry/bpf_fentry_testX"; - struct bpf_object *obj = NULL, *pkt_obj; - int err, pkt_fd, kfree_skb_fd, i; - struct bpf_link *link[6] = {}; - struct bpf_program *prog[6]; + struct test_pkt_access *pkt_skel = NULL; + struct fentry_test *fentry_skel = NULL; + int err, pkt_fd, i; __u32 duration, retval; - struct bpf_map *data_map; - const int zero = 0; - u64 result[6]; + __u64 *result; - err = bpf_prog_load("./test_pkt_access.o", BPF_PROG_TYPE_SCHED_CLS, - &pkt_obj, &pkt_fd); - if (CHECK(err, "prog_load sched cls", "err %d errno %d\n", err, errno)) + pkt_skel = test_pkt_access__open_and_load(&pkt_access_embed); + if (CHECK(!pkt_skel, "pkt_skel_load", "pkt_access skeleton failed\n")) return; - err = bpf_prog_load_xattr(&attr, &obj, &kfree_skb_fd); - if (CHECK(err, "prog_load fail", "err %d errno %d\n", err, errno)) - goto close_prog; + fentry_skel = fentry_test__open_and_load(&fentry_embed); + if (CHECK(!fentry_skel, "fentry_skel_load", "fentry skeleton failed\n")) + goto cleanup; - for (i = 0; i < 6; i++) { - prog_name[sizeof(prog_name) - 2] = '1' + i; - prog[i] = bpf_object__find_program_by_title(obj, prog_name); - if (CHECK(!prog[i], "find_prog", "prog %s not found\n", prog_name)) - goto close_prog; - link[i] = bpf_program__attach_trace(prog[i]); - if (CHECK(IS_ERR(link[i]), "attach_trace", "failed to link\n")) - goto close_prog; - } - data_map = bpf_object__find_map_by_name(obj, "fentry_t.bss"); - if (CHECK(!data_map, "find_data_map", "data map not found\n")) - goto close_prog; + err = fentry_test__attach(fentry_skel); + if (CHECK(err, "fentry_attach", "fentry attach failed: %d\n", err)) + goto cleanup; + pkt_fd = bpf_program__fd(pkt_skel->progs.test_pkt_access); err = bpf_prog_test_run(pkt_fd, 1, &pkt_v6, sizeof(pkt_v6), NULL, NULL, &retval, &duration); CHECK(err || retval, "ipv6", "err %d errno %d retval %d duration %d\n", err, errno, retval, duration); - err = bpf_map_lookup_elem(bpf_map__fd(data_map), &zero, &result); - if (CHECK(err, "get_result", - "failed to get output data: %d\n", err)) - goto close_prog; - - for (i = 0; i < 6; i++) - if (CHECK(result[i] != 1, "result", "bpf_fentry_test%d failed err %ld\n", - i + 1, result[i])) - goto close_prog; + result = (__u64 *)fentry_skel->bss; + for (i = 0; i < 6; i++) { + if (CHECK(result[i] != 1, "result", + "fentry_test%d failed err %lld\n", i + 1, result[i])) + goto cleanup; + } -close_prog: - for (i = 0; i < 6; i++) - if (!IS_ERR_OR_NULL(link[i])) - bpf_link__destroy(link[i]); - bpf_object__close(obj); - bpf_object__close(pkt_obj); +cleanup: + fentry_test__destroy(fentry_skel); + test_pkt_access__destroy(pkt_skel); } diff --git a/tools/testing/selftests/bpf/prog_tests/mmap.c b/tools/testing/selftests/bpf/prog_tests/mmap.c index 051a6d48762c2..95a44d37ccea5 100644 --- a/tools/testing/selftests/bpf/prog_tests/mmap.c +++ b/tools/testing/selftests/bpf/prog_tests/mmap.c @@ -1,59 +1,41 @@ // SPDX-License-Identifier: GPL-2.0 #include #include +#include "test_mmap.skel.h" struct map_data { __u64 val[512 * 4]; }; -struct bss_data { - __u64 in_val; - __u64 out_val; -}; - static size_t roundup_page(size_t sz) { long page_size = sysconf(_SC_PAGE_SIZE); return (sz + page_size - 1) / page_size * page_size; } +BPF_EMBED_OBJ(test_mmap, "test_mmap.o"); + void test_mmap(void) { - const char *file = "test_mmap.o"; - const char *probe_name = "raw_tracepoint/sys_enter"; - const char *tp_name = "sys_enter"; - const size_t bss_sz = roundup_page(sizeof(struct bss_data)); + const size_t bss_sz = roundup_page(sizeof(struct test_mmap__bss)); const size_t map_sz = roundup_page(sizeof(struct map_data)); const int zero = 0, one = 1, two = 2, far = 1500; const long page_size = sysconf(_SC_PAGE_SIZE); int err, duration = 0, i, data_map_fd; - struct bpf_program *prog; - struct bpf_object *obj; - struct bpf_link *link = NULL; struct bpf_map *data_map, *bss_map; void *bss_mmaped = NULL, *map_mmaped = NULL, *tmp1, *tmp2; - volatile struct bss_data *bss_data; - volatile struct map_data *map_data; + struct test_mmap__bss *bss_data; + struct map_data *map_data; + struct test_mmap *skel; __u64 val = 0; - obj = bpf_object__open_file("test_mmap.o", NULL); - if (CHECK(IS_ERR(obj), "obj_open", "failed to open '%s': %ld\n", - file, PTR_ERR(obj))) + + skel = test_mmap__open_and_load(&test_mmap_embed); + if (CHECK(!skel, "skel_open_and_load", "skeleton open/load failed\n")) return; - prog = bpf_object__find_program_by_title(obj, probe_name); - if (CHECK(!prog, "find_probe", "prog '%s' not found\n", probe_name)) - goto cleanup; - err = bpf_object__load(obj); - if (CHECK(err, "obj_load", "failed to load prog '%s': %d\n", - probe_name, err)) - goto cleanup; - bss_map = bpf_object__find_map_by_name(obj, "test_mma.bss"); - if (CHECK(!bss_map, "find_bss_map", ".bss map not found\n")) - goto cleanup; - data_map = bpf_object__find_map_by_name(obj, "data_map"); - if (CHECK(!data_map, "find_data_map", "data_map map not found\n")) - goto cleanup; + bss_map = skel->maps.bss; + data_map = skel->maps.data_map; data_map_fd = bpf_map__fd(data_map); bss_mmaped = mmap(NULL, bss_sz, PROT_READ | PROT_WRITE, MAP_SHARED, @@ -77,13 +59,15 @@ void test_mmap(void) CHECK_FAIL(bss_data->in_val); CHECK_FAIL(bss_data->out_val); + CHECK_FAIL(skel->bss->in_val); + CHECK_FAIL(skel->bss->out_val); CHECK_FAIL(map_data->val[0]); CHECK_FAIL(map_data->val[1]); CHECK_FAIL(map_data->val[2]); CHECK_FAIL(map_data->val[far]); - link = bpf_program__attach_raw_tracepoint(prog, tp_name); - if (CHECK(IS_ERR(link), "attach_raw_tp", "err %ld\n", PTR_ERR(link))) + err = test_mmap__attach(skel); + if (CHECK(err, "attach_raw_tp", "err %d\n", err)) goto cleanup; bss_data->in_val = 123; @@ -94,6 +78,8 @@ void test_mmap(void) CHECK_FAIL(bss_data->in_val != 123); CHECK_FAIL(bss_data->out_val != 123); + CHECK_FAIL(skel->bss->in_val != 123); + CHECK_FAIL(skel->bss->out_val != 123); CHECK_FAIL(map_data->val[0] != 111); CHECK_FAIL(map_data->val[1] != 222); CHECK_FAIL(map_data->val[2] != 123); @@ -160,6 +146,8 @@ void test_mmap(void) usleep(1); CHECK_FAIL(bss_data->in_val != 321); CHECK_FAIL(bss_data->out_val != 321); + CHECK_FAIL(skel->bss->in_val != 321); + CHECK_FAIL(skel->bss->out_val != 321); CHECK_FAIL(map_data->val[0] != 111); CHECK_FAIL(map_data->val[1] != 222); CHECK_FAIL(map_data->val[2] != 321); @@ -203,6 +191,8 @@ void test_mmap(void) map_data = tmp2; CHECK_FAIL(bss_data->in_val != 321); CHECK_FAIL(bss_data->out_val != 321); + CHECK_FAIL(skel->bss->in_val != 321); + CHECK_FAIL(skel->bss->out_val != 321); CHECK_FAIL(map_data->val[0] != 111); CHECK_FAIL(map_data->val[1] != 222); CHECK_FAIL(map_data->val[2] != 321); @@ -214,7 +204,5 @@ cleanup: CHECK_FAIL(munmap(bss_mmaped, bss_sz)); if (map_mmaped) CHECK_FAIL(munmap(map_mmaped, map_sz)); - if (!IS_ERR_OR_NULL(link)) - bpf_link__destroy(link); - bpf_object__close(obj); + test_mmap__destroy(skel); } diff --git a/tools/testing/selftests/bpf/prog_tests/stacktrace_build_id.c b/tools/testing/selftests/bpf/prog_tests/stacktrace_build_id.c index d841dced971ff..4af8b8253f25a 100644 --- a/tools/testing/selftests/bpf/prog_tests/stacktrace_build_id.c +++ b/tools/testing/selftests/bpf/prog_tests/stacktrace_build_id.c @@ -1,16 +1,16 @@ // SPDX-License-Identifier: GPL-2.0 #include +#include "test_stacktrace_build_id.skel.h" + +BPF_EMBED_OBJ(stacktrace_build_id, "test_stacktrace_build_id.o"); void test_stacktrace_build_id(void) { + int control_map_fd, stackid_hmap_fd, stackmap_fd, stack_amap_fd; - const char *prog_name = "tracepoint/random/urandom_read"; - const char *file = "./test_stacktrace_build_id.o"; - int err, prog_fd, stack_trace_len; + struct test_stacktrace_build_id *skel; + int err, stack_trace_len; __u32 key, previous_key, val, duration = 0; - struct bpf_program *prog; - struct bpf_object *obj; - struct bpf_link *link = NULL; char buf[256]; int i, j; struct bpf_stack_build_id id_offs[PERF_MAX_STACK_DEPTH]; @@ -18,43 +18,24 @@ void test_stacktrace_build_id(void) int retry = 1; retry: - err = bpf_prog_load(file, BPF_PROG_TYPE_TRACEPOINT, &obj, &prog_fd); - if (CHECK(err, "prog_load", "err %d errno %d\n", err, errno)) + skel = test_stacktrace_build_id__open_and_load(&stacktrace_build_id_embed); + if (CHECK(!skel, "skel_open_and_load", "skeleton open/load failed\n")) return; - prog = bpf_object__find_program_by_title(obj, prog_name); - if (CHECK(!prog, "find_prog", "prog '%s' not found\n", prog_name)) - goto close_prog; - - link = bpf_program__attach_tracepoint(prog, "random", "urandom_read"); - if (CHECK(IS_ERR(link), "attach_tp", "err %ld\n", PTR_ERR(link))) - goto close_prog; + err = test_stacktrace_build_id__attach(skel); + if (CHECK(err, "attach_tp", "err %d\n", err)) + goto cleanup; /* find map fds */ - control_map_fd = bpf_find_map(__func__, obj, "control_map"); - if (CHECK(control_map_fd < 0, "bpf_find_map control_map", - "err %d errno %d\n", err, errno)) - goto disable_pmu; - - stackid_hmap_fd = bpf_find_map(__func__, obj, "stackid_hmap"); - if (CHECK(stackid_hmap_fd < 0, "bpf_find_map stackid_hmap", - "err %d errno %d\n", err, errno)) - goto disable_pmu; - - stackmap_fd = bpf_find_map(__func__, obj, "stackmap"); - if (CHECK(stackmap_fd < 0, "bpf_find_map stackmap", "err %d errno %d\n", - err, errno)) - goto disable_pmu; - - stack_amap_fd = bpf_find_map(__func__, obj, "stack_amap"); - if (CHECK(stack_amap_fd < 0, "bpf_find_map stack_amap", - "err %d errno %d\n", err, errno)) - goto disable_pmu; + control_map_fd = bpf_map__fd(skel->maps.control_map); + stackid_hmap_fd = bpf_map__fd(skel->maps.stackid_hmap); + stackmap_fd = bpf_map__fd(skel->maps.stackmap); + stack_amap_fd = bpf_map__fd(skel->maps.stack_amap); if (CHECK_FAIL(system("dd if=/dev/urandom of=/dev/zero count=4 2> /dev/null"))) - goto disable_pmu; + goto cleanup; if (CHECK_FAIL(system("./urandom_read"))) - goto disable_pmu; + goto cleanup; /* disable stack trace collection */ key = 0; val = 1; @@ -66,23 +47,23 @@ retry: err = compare_map_keys(stackid_hmap_fd, stackmap_fd); if (CHECK(err, "compare_map_keys stackid_hmap vs. stackmap", "err %d errno %d\n", err, errno)) - goto disable_pmu; + goto cleanup; err = compare_map_keys(stackmap_fd, stackid_hmap_fd); if (CHECK(err, "compare_map_keys stackmap vs. stackid_hmap", "err %d errno %d\n", err, errno)) - goto disable_pmu; + goto cleanup; err = extract_build_id(buf, 256); if (CHECK(err, "get build_id with readelf", "err %d errno %d\n", err, errno)) - goto disable_pmu; + goto cleanup; err = bpf_map_get_next_key(stackmap_fd, NULL, &key); if (CHECK(err, "get_next_key from stackmap", "err %d, errno %d\n", err, errno)) - goto disable_pmu; + goto cleanup; do { char build_id[64]; @@ -90,7 +71,7 @@ retry: err = bpf_map_lookup_elem(stackmap_fd, &key, id_offs); if (CHECK(err, "lookup_elem from stackmap", "err %d, errno %d\n", err, errno)) - goto disable_pmu; + goto cleanup; for (i = 0; i < PERF_MAX_STACK_DEPTH; ++i) if (id_offs[i].status == BPF_STACK_BUILD_ID_VALID && id_offs[i].offset != 0) { @@ -108,8 +89,7 @@ retry: * try it one more time. */ if (build_id_matches < 1 && retry--) { - bpf_link__destroy(link); - bpf_object__close(obj); + test_stacktrace_build_id__destroy(skel); printf("%s:WARN:Didn't find expected build ID from the map, retrying\n", __func__); goto retry; @@ -117,17 +97,14 @@ retry: if (CHECK(build_id_matches < 1, "build id match", "Didn't find expected build ID from the map\n")) - goto disable_pmu; + goto cleanup; - stack_trace_len = PERF_MAX_STACK_DEPTH - * sizeof(struct bpf_stack_build_id); + stack_trace_len = PERF_MAX_STACK_DEPTH * + sizeof(struct bpf_stack_build_id); err = compare_stack_ips(stackmap_fd, stack_amap_fd, stack_trace_len); CHECK(err, "compare_stack_ips stackmap vs. stack_amap", "err %d errno %d\n", err, errno); -disable_pmu: - bpf_link__destroy(link); - -close_prog: - bpf_object__close(obj); +cleanup: + test_stacktrace_build_id__destroy(skel); } diff --git a/tools/testing/selftests/bpf/prog_tests/stacktrace_build_id_nmi.c b/tools/testing/selftests/bpf/prog_tests/stacktrace_build_id_nmi.c index f62aa0eb959bb..32fb03881a7b3 100644 --- a/tools/testing/selftests/bpf/prog_tests/stacktrace_build_id_nmi.c +++ b/tools/testing/selftests/bpf/prog_tests/stacktrace_build_id_nmi.c @@ -1,5 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 #include +#include "test_stacktrace_build_id.skel.h" static __u64 read_perf_max_sample_freq(void) { @@ -14,21 +15,19 @@ static __u64 read_perf_max_sample_freq(void) return sample_freq; } +BPF_EMBED_OBJ_DECLARE(stacktrace_build_id); + void test_stacktrace_build_id_nmi(void) { - int control_map_fd, stackid_hmap_fd, stackmap_fd, stack_amap_fd; - const char *prog_name = "tracepoint/random/urandom_read"; - const char *file = "./test_stacktrace_build_id.o"; - int err, pmu_fd, prog_fd; + int control_map_fd, stackid_hmap_fd, stackmap_fd; + struct test_stacktrace_build_id *skel; + int err, pmu_fd; struct perf_event_attr attr = { .freq = 1, .type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_CPU_CYCLES, }; __u32 key, previous_key, val, duration = 0; - struct bpf_program *prog; - struct bpf_object *obj; - struct bpf_link *link; char buf[256]; int i, j; struct bpf_stack_build_id id_offs[PERF_MAX_STACK_DEPTH]; @@ -38,13 +37,16 @@ void test_stacktrace_build_id_nmi(void) attr.sample_freq = read_perf_max_sample_freq(); retry: - err = bpf_prog_load(file, BPF_PROG_TYPE_PERF_EVENT, &obj, &prog_fd); - if (CHECK(err, "prog_load", "err %d errno %d\n", err, errno)) + skel = test_stacktrace_build_id__open(&stacktrace_build_id_embed); + if (CHECK(!skel, "skel_open", "skeleton open failed\n")) return; - prog = bpf_object__find_program_by_title(obj, prog_name); - if (CHECK(!prog, "find_prog", "prog '%s' not found\n", prog_name)) - goto close_prog; + /* override program type */ + bpf_program__set_perf_event(skel->progs.oncpu); + + err = test_stacktrace_build_id__load(skel); + if (CHECK(err, "skel_load", "skeleton load failed: %d\n", err)) + goto cleanup; pmu_fd = syscall(__NR_perf_event_open, &attr, -1 /* pid */, 0 /* cpu 0 */, -1 /* group id */, @@ -52,40 +54,25 @@ retry: if (CHECK(pmu_fd < 0, "perf_event_open", "err %d errno %d. Does the test host support PERF_COUNT_HW_CPU_CYCLES?\n", pmu_fd, errno)) - goto close_prog; + goto cleanup; - link = bpf_program__attach_perf_event(prog, pmu_fd); - if (CHECK(IS_ERR(link), "attach_perf_event", - "err %ld\n", PTR_ERR(link))) { + skel->links.oncpu = bpf_program__attach_perf_event(skel->progs.oncpu, + pmu_fd); + if (CHECK(IS_ERR(skel->links.oncpu), "attach_perf_event", + "err %ld\n", PTR_ERR(skel->links.oncpu))) { close(pmu_fd); - goto close_prog; + goto cleanup; } /* find map fds */ - control_map_fd = bpf_find_map(__func__, obj, "control_map"); - if (CHECK(control_map_fd < 0, "bpf_find_map control_map", - "err %d errno %d\n", err, errno)) - goto disable_pmu; - - stackid_hmap_fd = bpf_find_map(__func__, obj, "stackid_hmap"); - if (CHECK(stackid_hmap_fd < 0, "bpf_find_map stackid_hmap", - "err %d errno %d\n", err, errno)) - goto disable_pmu; - - stackmap_fd = bpf_find_map(__func__, obj, "stackmap"); - if (CHECK(stackmap_fd < 0, "bpf_find_map stackmap", "err %d errno %d\n", - err, errno)) - goto disable_pmu; - - stack_amap_fd = bpf_find_map(__func__, obj, "stack_amap"); - if (CHECK(stack_amap_fd < 0, "bpf_find_map stack_amap", - "err %d errno %d\n", err, errno)) - goto disable_pmu; + control_map_fd = bpf_map__fd(skel->maps.control_map); + stackid_hmap_fd = bpf_map__fd(skel->maps.stackid_hmap); + stackmap_fd = bpf_map__fd(skel->maps.stackmap); if (CHECK_FAIL(system("dd if=/dev/urandom of=/dev/zero count=4 2> /dev/null"))) - goto disable_pmu; + goto cleanup; if (CHECK_FAIL(system("taskset 0x1 ./urandom_read 100000"))) - goto disable_pmu; + goto cleanup; /* disable stack trace collection */ key = 0; val = 1; @@ -97,23 +84,23 @@ retry: err = compare_map_keys(stackid_hmap_fd, stackmap_fd); if (CHECK(err, "compare_map_keys stackid_hmap vs. stackmap", "err %d errno %d\n", err, errno)) - goto disable_pmu; + goto cleanup; err = compare_map_keys(stackmap_fd, stackid_hmap_fd); if (CHECK(err, "compare_map_keys stackmap vs. stackid_hmap", "err %d errno %d\n", err, errno)) - goto disable_pmu; + goto cleanup; err = extract_build_id(buf, 256); if (CHECK(err, "get build_id with readelf", "err %d errno %d\n", err, errno)) - goto disable_pmu; + goto cleanup; err = bpf_map_get_next_key(stackmap_fd, NULL, &key); if (CHECK(err, "get_next_key from stackmap", "err %d, errno %d\n", err, errno)) - goto disable_pmu; + goto cleanup; do { char build_id[64]; @@ -121,7 +108,7 @@ retry: err = bpf_map_lookup_elem(stackmap_fd, &key, id_offs); if (CHECK(err, "lookup_elem from stackmap", "err %d, errno %d\n", err, errno)) - goto disable_pmu; + goto cleanup; for (i = 0; i < PERF_MAX_STACK_DEPTH; ++i) if (id_offs[i].status == BPF_STACK_BUILD_ID_VALID && id_offs[i].offset != 0) { @@ -139,8 +126,7 @@ retry: * try it one more time. */ if (build_id_matches < 1 && retry--) { - bpf_link__destroy(link); - bpf_object__close(obj); + test_stacktrace_build_id__destroy(skel); printf("%s:WARN:Didn't find expected build ID from the map, retrying\n", __func__); goto retry; @@ -148,7 +134,7 @@ retry: if (CHECK(build_id_matches < 1, "build id match", "Didn't find expected build ID from the map\n")) - goto disable_pmu; + goto cleanup; /* * We intentionally skip compare_stack_ips(). This is because we @@ -157,8 +143,6 @@ retry: * BPF_STACK_BUILD_ID_IP; */ -disable_pmu: - bpf_link__destroy(link); -close_prog: - bpf_object__close(obj); +cleanup: + test_stacktrace_build_id__destroy(skel); } -- GitLab From 197448eaac1ab330fb485769bccb62346ba1f458 Mon Sep 17 00:00:00 2001 From: Andrii Nakryiko Date: Fri, 13 Dec 2019 17:43:40 -0800 Subject: [PATCH 0699/1437] selftests/bpf: Add test validating data section to struct convertion layout Add a simple selftests validating datasection-to-struct layour dumping. Global variables are constructed in such a way as to cause both natural and artificial padding (through custom alignment requirement). Signed-off-by: Andrii Nakryiko Signed-off-by: Alexei Starovoitov Acked-by: Martin KaFai Lau Link: https://lore.kernel.org/bpf/20191214014341.3442258-17-andriin@fb.com --- .../selftests/bpf/prog_tests/skeleton.c | 51 +++++++++++++++++++ .../selftests/bpf/progs/test_skeleton.c | 37 ++++++++++++++ 2 files changed, 88 insertions(+) create mode 100644 tools/testing/selftests/bpf/prog_tests/skeleton.c create mode 100644 tools/testing/selftests/bpf/progs/test_skeleton.c diff --git a/tools/testing/selftests/bpf/prog_tests/skeleton.c b/tools/testing/selftests/bpf/prog_tests/skeleton.c new file mode 100644 index 0000000000000..79f8d13e67403 --- /dev/null +++ b/tools/testing/selftests/bpf/prog_tests/skeleton.c @@ -0,0 +1,51 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2019 Facebook */ + +#include + +struct s { + int a; + long long b; +} __attribute__((packed)); + +#include "test_skeleton.skel.h" + +BPF_EMBED_OBJ(skeleton, "test_skeleton.o"); + +void test_skeleton(void) +{ + int duration = 0, err; + struct test_skeleton* skel; + struct test_skeleton__bss *bss; + + skel = test_skeleton__open_and_load(&skeleton_embed); + if (CHECK(!skel, "skel_open", "failed to open skeleton\n")) + return; + + bss = skel->bss; + bss->in1 = 1; + bss->in2 = 2; + bss->in3 = 3; + bss->in4 = 4; + bss->in5.a = 5; + bss->in5.b = 6; + + err = test_skeleton__attach(skel); + if (CHECK(err, "skel_attach", "skeleton attach failed: %d\n", err)) + goto cleanup; + + /* trigger tracepoint */ + usleep(1); + + CHECK(bss->out1 != 1, "res1", "got %d != exp %d\n", bss->out1, 1); + CHECK(bss->out2 != 2, "res2", "got %lld != exp %d\n", bss->out2, 2); + CHECK(bss->out3 != 3, "res3", "got %d != exp %d\n", (int)bss->out3, 3); + CHECK(bss->out4 != 4, "res4", "got %lld != exp %d\n", bss->out4, 4); + CHECK(bss->handler_out5.a != 5, "res5", "got %d != exp %d\n", + bss->handler_out5.a, 5); + CHECK(bss->handler_out5.b != 6, "res6", "got %lld != exp %d\n", + bss->handler_out5.b, 6); + +cleanup: + test_skeleton__destroy(skel); +} diff --git a/tools/testing/selftests/bpf/progs/test_skeleton.c b/tools/testing/selftests/bpf/progs/test_skeleton.c new file mode 100644 index 0000000000000..c0013d2e16f27 --- /dev/null +++ b/tools/testing/selftests/bpf/progs/test_skeleton.c @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2019 Facebook */ + +#include +#include "bpf_helpers.h" + +struct s { + int a; + long long b; +} __attribute__((packed)); + +int in1 = 0; +long long in2 = 0; +char in3 = '\0'; +long long in4 __attribute__((aligned(64))) = 0; +struct s in5 = {}; + +long long out2 = 0; +char out3 = 0; +long long out4 = 0; +int out1 = 0; + + +SEC("raw_tp/sys_enter") +int handler(const void *ctx) +{ + static volatile struct s out5; + + out1 = in1; + out2 = in2; + out3 = in3; + out4 = in4; + out5 = in5; + return 0; +} + +char _license[] SEC("license") = "GPL"; -- GitLab From d9c00c3b1639a3c8f46663cc042a3768d222021f Mon Sep 17 00:00:00 2001 From: Andrii Nakryiko Date: Fri, 13 Dec 2019 17:43:41 -0800 Subject: [PATCH 0700/1437] bpftool: Add `gen skeleton` BASH completions Add BASH completions for gen sub-command. Signed-off-by: Andrii Nakryiko Signed-off-by: Alexei Starovoitov Acked-by: Martin KaFai Lau Cc: Quentin Monnet Link: https://lore.kernel.org/bpf/20191214014341.3442258-18-andriin@fb.com --- tools/bpf/bpftool/bash-completion/bpftool | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/tools/bpf/bpftool/bash-completion/bpftool b/tools/bpf/bpftool/bash-completion/bpftool index 21c676a1eeb1a..754d8395e4511 100644 --- a/tools/bpf/bpftool/bash-completion/bpftool +++ b/tools/bpf/bpftool/bash-completion/bpftool @@ -839,6 +839,17 @@ _bpftool() ;; esac ;; + gen) + case $command in + skeleton) + _filedir + ;; + *) + [[ $prev == $object ]] && \ + COMPREPLY=( $( compgen -W 'skeleton help' -- "$cur" ) ) + ;; + esac + ;; cgroup) case $command in show|list|tree) -- GitLab From ac9d1389631a4bee0df47e50d6bee8b94230759d Mon Sep 17 00:00:00 2001 From: Andrii Nakryiko Date: Fri, 13 Dec 2019 17:47:07 -0800 Subject: [PATCH 0701/1437] libbpf: Extract internal map names into constants Instead of duplicating string literals, keep them in one place and consistent. Signed-off-by: Andrii Nakryiko Signed-off-by: Alexei Starovoitov Link: https://lore.kernel.org/bpf/20191214014710.3449601-2-andriin@fb.com --- tools/lib/bpf/libbpf.c | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c index a1a902fa6e0c5..f6c1358697015 100644 --- a/tools/lib/bpf/libbpf.c +++ b/tools/lib/bpf/libbpf.c @@ -195,6 +195,10 @@ struct bpf_program { __u32 prog_flags; }; +#define DATA_SEC ".data" +#define BSS_SEC ".bss" +#define RODATA_SEC ".rodata" + enum libbpf_map_type { LIBBPF_MAP_UNSPEC, LIBBPF_MAP_DATA, @@ -203,9 +207,9 @@ enum libbpf_map_type { }; static const char * const libbpf_type_to_btf_name[] = { - [LIBBPF_MAP_DATA] = ".data", - [LIBBPF_MAP_BSS] = ".bss", - [LIBBPF_MAP_RODATA] = ".rodata", + [LIBBPF_MAP_DATA] = DATA_SEC, + [LIBBPF_MAP_BSS] = BSS_SEC, + [LIBBPF_MAP_RODATA] = RODATA_SEC, }; struct bpf_map { @@ -736,13 +740,13 @@ int bpf_object__section_size(const struct bpf_object *obj, const char *name, *size = 0; if (!name) { return -EINVAL; - } else if (!strcmp(name, ".data")) { + } else if (!strcmp(name, DATA_SEC)) { if (obj->efile.data) *size = obj->efile.data->d_size; - } else if (!strcmp(name, ".bss")) { + } else if (!strcmp(name, BSS_SEC)) { if (obj->efile.bss) *size = obj->efile.bss->d_size; - } else if (!strcmp(name, ".rodata")) { + } else if (!strcmp(name, RODATA_SEC)) { if (obj->efile.rodata) *size = obj->efile.rodata->d_size; } else { @@ -1684,10 +1688,10 @@ static int bpf_object__elf_collect(struct bpf_object *obj) name, obj->path, cp); return err; } - } else if (strcmp(name, ".data") == 0) { + } else if (strcmp(name, DATA_SEC) == 0) { obj->efile.data = data; obj->efile.data_shndx = idx; - } else if (strcmp(name, ".rodata") == 0) { + } else if (strcmp(name, RODATA_SEC) == 0) { obj->efile.rodata = data; obj->efile.rodata_shndx = idx; } else { @@ -1717,7 +1721,8 @@ static int bpf_object__elf_collect(struct bpf_object *obj) obj->efile.reloc_sects[nr_sects].shdr = sh; obj->efile.reloc_sects[nr_sects].data = data; - } else if (sh.sh_type == SHT_NOBITS && strcmp(name, ".bss") == 0) { + } else if (sh.sh_type == SHT_NOBITS && + strcmp(name, BSS_SEC) == 0) { obj->efile.bss = data; obj->efile.bss_shndx = idx; } else { -- GitLab From 166750bc1dd256b2184b22588fb9fe6d3fbb93ae Mon Sep 17 00:00:00 2001 From: Andrii Nakryiko Date: Fri, 13 Dec 2019 17:47:08 -0800 Subject: [PATCH 0702/1437] libbpf: Support libbpf-provided extern variables Add support for extern variables, provided to BPF program by libbpf. Currently the following extern variables are supported: - LINUX_KERNEL_VERSION; version of a kernel in which BPF program is executing, follows KERNEL_VERSION() macro convention, can be 4- and 8-byte long; - CONFIG_xxx values; a set of values of actual kernel config. Tristate, boolean, strings, and integer values are supported. Set of possible values is determined by declared type of extern variable. Supported types of variables are: - Tristate values. Are represented as `enum libbpf_tristate`. Accepted values are **strictly** 'y', 'n', or 'm', which are represented as TRI_YES, TRI_NO, or TRI_MODULE, respectively. - Boolean values. Are represented as bool (_Bool) types. Accepted values are 'y' and 'n' only, turning into true/false values, respectively. - Single-character values. Can be used both as a substritute for bool/tristate, or as a small-range integer: - 'y'/'n'/'m' are represented as is, as characters 'y', 'n', or 'm'; - integers in a range [-128, 127] or [0, 255] (depending on signedness of char in target architecture) are recognized and represented with respective values of char type. - Strings. String values are declared as fixed-length char arrays. String of up to that length will be accepted and put in first N bytes of char array, with the rest of bytes zeroed out. If config string value is longer than space alloted, it will be truncated and warning message emitted. Char array is always zero terminated. String literals in config have to be enclosed in double quotes, just like C-style string literals. - Integers. 8-, 16-, 32-, and 64-bit integers are supported, both signed and unsigned variants. Libbpf enforces parsed config value to be in the supported range of corresponding integer type. Integers values in config can be: - decimal integers, with optional + and - signs; - hexadecimal integers, prefixed with 0x or 0X; - octal integers, starting with 0. Config file itself is searched in /boot/config-$(uname -r) location with fallback to /proc/config.gz, unless config path is specified explicitly through bpf_object_open_opts' kernel_config_path option. Both gzipped and plain text formats are supported. Libbpf adds explicit dependency on zlib because of this, but this shouldn't be a problem, given libelf already depends on zlib. All detected extern variables, are put into a separate .extern internal map. It, similarly to .rodata map, is marked as read-only from BPF program side, as well as is frozen on load. This allows BPF verifier to track extern values as constants and perform enhanced branch prediction and dead code elimination. This can be relied upon for doing kernel version/feature detection and using potentially unsupported field relocations or BPF helpers in a CO-RE-based BPF program, while still having a single version of BPF program running on old and new kernels. Selftests are validating this explicitly for unexisting BPF helper. Signed-off-by: Andrii Nakryiko Signed-off-by: Alexei Starovoitov Link: https://lore.kernel.org/bpf/20191214014710.3449601-3-andriin@fb.com --- include/uapi/linux/btf.h | 3 +- tools/include/uapi/linux/btf.h | 7 +- tools/lib/bpf/Makefile | 15 +- tools/lib/bpf/bpf_helpers.h | 9 + tools/lib/bpf/btf.c | 9 +- tools/lib/bpf/libbpf.c | 738 +++++++++++++++++++++++++-- tools/lib/bpf/libbpf.h | 12 +- tools/testing/selftests/bpf/Makefile | 2 +- 8 files changed, 729 insertions(+), 66 deletions(-) diff --git a/include/uapi/linux/btf.h b/include/uapi/linux/btf.h index c02dec97e1cee..1a2898c482eef 100644 --- a/include/uapi/linux/btf.h +++ b/include/uapi/linux/btf.h @@ -142,7 +142,8 @@ struct btf_param { enum { BTF_VAR_STATIC = 0, - BTF_VAR_GLOBAL_ALLOCATED, + BTF_VAR_GLOBAL_ALLOCATED = 1, + BTF_VAR_GLOBAL_EXTERN = 2, }; /* BTF_KIND_VAR is followed by a single "struct btf_var" to describe diff --git a/tools/include/uapi/linux/btf.h b/tools/include/uapi/linux/btf.h index 63ae4a39e58b4..1a2898c482eef 100644 --- a/tools/include/uapi/linux/btf.h +++ b/tools/include/uapi/linux/btf.h @@ -22,9 +22,9 @@ struct btf_header { }; /* Max # of type identifier */ -#define BTF_MAX_TYPE 0x0000ffff +#define BTF_MAX_TYPE 0x000fffff /* Max offset into the string section */ -#define BTF_MAX_NAME_OFFSET 0x0000ffff +#define BTF_MAX_NAME_OFFSET 0x00ffffff /* Max # of struct/union/enum members or func args */ #define BTF_MAX_VLEN 0xffff @@ -142,7 +142,8 @@ struct btf_param { enum { BTF_VAR_STATIC = 0, - BTF_VAR_GLOBAL_ALLOCATED, + BTF_VAR_GLOBAL_ALLOCATED = 1, + BTF_VAR_GLOBAL_EXTERN = 2, }; /* BTF_KIND_VAR is followed by a single "struct btf_var" to describe diff --git a/tools/lib/bpf/Makefile b/tools/lib/bpf/Makefile index 23ae06c43d088..a3718cb275f2e 100644 --- a/tools/lib/bpf/Makefile +++ b/tools/lib/bpf/Makefile @@ -56,8 +56,8 @@ ifndef VERBOSE endif FEATURE_USER = .libbpf -FEATURE_TESTS = libelf libelf-mmap bpf reallocarray -FEATURE_DISPLAY = libelf bpf +FEATURE_TESTS = libelf libelf-mmap zlib bpf reallocarray +FEATURE_DISPLAY = libelf zlib bpf INCLUDES = -I. -I$(srctree)/tools/include -I$(srctree)/tools/arch/$(ARCH)/include/uapi -I$(srctree)/tools/include/uapi FEATURE_CHECK_CFLAGS-bpf = $(INCLUDES) @@ -160,7 +160,7 @@ all: fixdep all_cmd: $(CMD_TARGETS) check -$(BPF_IN_SHARED): force elfdep bpfdep bpf_helper_defs.h +$(BPF_IN_SHARED): force elfdep zdep bpfdep bpf_helper_defs.h @(test -f ../../include/uapi/linux/bpf.h -a -f ../../../include/uapi/linux/bpf.h && ( \ (diff -B ../../include/uapi/linux/bpf.h ../../../include/uapi/linux/bpf.h >/dev/null) || \ echo "Warning: Kernel ABI header at 'tools/include/uapi/linux/bpf.h' differs from latest version at 'include/uapi/linux/bpf.h'" >&2 )) || true @@ -178,7 +178,7 @@ $(BPF_IN_SHARED): force elfdep bpfdep bpf_helper_defs.h echo "Warning: Kernel ABI header at 'tools/include/uapi/linux/if_xdp.h' differs from latest version at 'include/uapi/linux/if_xdp.h'" >&2 )) || true $(Q)$(MAKE) $(build)=libbpf OUTPUT=$(SHARED_OBJDIR) CFLAGS="$(CFLAGS) $(SHLIB_FLAGS)" -$(BPF_IN_STATIC): force elfdep bpfdep bpf_helper_defs.h +$(BPF_IN_STATIC): force elfdep zdep bpfdep bpf_helper_defs.h $(Q)$(MAKE) $(build)=libbpf OUTPUT=$(STATIC_OBJDIR) bpf_helper_defs.h: $(srctree)/tools/include/uapi/linux/bpf.h @@ -190,7 +190,7 @@ $(OUTPUT)libbpf.so: $(OUTPUT)libbpf.so.$(LIBBPF_VERSION) $(OUTPUT)libbpf.so.$(LIBBPF_VERSION): $(BPF_IN_SHARED) $(QUIET_LINK)$(CC) $(LDFLAGS) \ --shared -Wl,-soname,libbpf.so.$(LIBBPF_MAJOR_VERSION) \ - -Wl,--version-script=$(VERSION_SCRIPT) $^ -lelf -o $@ + -Wl,--version-script=$(VERSION_SCRIPT) $^ -lelf -lz -o $@ @ln -sf $(@F) $(OUTPUT)libbpf.so @ln -sf $(@F) $(OUTPUT)libbpf.so.$(LIBBPF_MAJOR_VERSION) @@ -279,12 +279,15 @@ clean: -PHONY += force elfdep bpfdep cscope tags +PHONY += force elfdep zdep bpfdep cscope tags force: elfdep: @if [ "$(feature-libelf)" != "1" ]; then echo "No libelf found"; exit 1 ; fi +zdep: + @if [ "$(feature-zlib)" != "1" ]; then echo "No zlib found"; exit 1 ; fi + bpfdep: @if [ "$(feature-bpf)" != "1" ]; then echo "BPF API too old"; exit 1 ; fi diff --git a/tools/lib/bpf/bpf_helpers.h b/tools/lib/bpf/bpf_helpers.h index 0c7d28292898c..aa46700075e16 100644 --- a/tools/lib/bpf/bpf_helpers.h +++ b/tools/lib/bpf/bpf_helpers.h @@ -25,6 +25,9 @@ #ifndef __always_inline #define __always_inline __attribute__((always_inline)) #endif +#ifndef __weak +#define __weak __attribute__((weak)) +#endif /* * Helper structure used by eBPF C program @@ -44,4 +47,10 @@ enum libbpf_pin_type { LIBBPF_PIN_BY_NAME, }; +enum libbpf_tristate { + TRI_NO = 0, + TRI_YES = 1, + TRI_MODULE = 2, +}; + #endif diff --git a/tools/lib/bpf/btf.c b/tools/lib/bpf/btf.c index 84fe82f27bef6..520021939d81a 100644 --- a/tools/lib/bpf/btf.c +++ b/tools/lib/bpf/btf.c @@ -578,6 +578,12 @@ static int btf_fixup_datasec(struct bpf_object *obj, struct btf *btf, return -ENOENT; } + /* .extern datasec size and var offsets were set correctly during + * extern collection step, so just skip straight to sorting variables + */ + if (t->size) + goto sort_vars; + ret = bpf_object__section_size(obj, name, &size); if (ret || !size || (t->size && t->size != size)) { pr_debug("Invalid size for section %s: %u bytes\n", name, size); @@ -614,7 +620,8 @@ static int btf_fixup_datasec(struct bpf_object *obj, struct btf *btf, vsi->offset = off; } - qsort(t + 1, vars, sizeof(*vsi), compare_vsi_off); +sort_vars: + qsort(btf_var_secinfos(t), vars, sizeof(*vsi), compare_vsi_off); return 0; } diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c index f6c1358697015..26144706ba0bb 100644 --- a/tools/lib/bpf/libbpf.c +++ b/tools/lib/bpf/libbpf.c @@ -44,6 +44,7 @@ #include #include #include +#include #include "libbpf.h" #include "bpf.h" @@ -139,6 +140,20 @@ struct bpf_capabilities { __u32 array_mmap:1; }; +enum reloc_type { + RELO_LD64, + RELO_CALL, + RELO_DATA, + RELO_EXTERN, +}; + +struct reloc_desc { + enum reloc_type type; + int insn_idx; + int map_idx; + int sym_off; +}; + /* * bpf_prog should be a better name but it has been used in * linux/filter.h. @@ -157,16 +172,7 @@ struct bpf_program { size_t insns_cnt, main_prog_cnt; enum bpf_prog_type type; - struct reloc_desc { - enum { - RELO_LD64, - RELO_CALL, - RELO_DATA, - } type; - int insn_idx; - int map_idx; - int sym_off; - } *reloc_desc; + struct reloc_desc *reloc_desc; int nr_reloc; int log_level; @@ -198,18 +204,21 @@ struct bpf_program { #define DATA_SEC ".data" #define BSS_SEC ".bss" #define RODATA_SEC ".rodata" +#define EXTERN_SEC ".extern" enum libbpf_map_type { LIBBPF_MAP_UNSPEC, LIBBPF_MAP_DATA, LIBBPF_MAP_BSS, LIBBPF_MAP_RODATA, + LIBBPF_MAP_EXTERN, }; static const char * const libbpf_type_to_btf_name[] = { [LIBBPF_MAP_DATA] = DATA_SEC, [LIBBPF_MAP_BSS] = BSS_SEC, [LIBBPF_MAP_RODATA] = RODATA_SEC, + [LIBBPF_MAP_EXTERN] = EXTERN_SEC, }; struct bpf_map { @@ -231,6 +240,28 @@ struct bpf_map { bool reused; }; +enum extern_type { + EXT_UNKNOWN, + EXT_CHAR, + EXT_BOOL, + EXT_INT, + EXT_TRISTATE, + EXT_CHAR_ARR, +}; + +struct extern_desc { + const char *name; + int sym_idx; + int btf_id; + enum extern_type type; + int sz; + int align; + int data_off; + bool is_signed; + bool is_weak; + bool is_set; +}; + static LIST_HEAD(bpf_objects_list); struct bpf_object { @@ -244,6 +275,11 @@ struct bpf_object { size_t nr_maps; size_t maps_cap; + char *kconfig_path; + struct extern_desc *externs; + int nr_extern; + int extern_map_idx; + bool loaded; bool has_pseudo_calls; bool relaxed_core_relocs; @@ -271,6 +307,7 @@ struct bpf_object { int maps_shndx; int btf_maps_shndx; int text_shndx; + int symbols_shndx; int data_shndx; int rodata_shndx; int bss_shndx; @@ -542,6 +579,7 @@ static struct bpf_object *bpf_object__new(const char *path, obj->efile.data_shndx = -1; obj->efile.rodata_shndx = -1; obj->efile.bss_shndx = -1; + obj->extern_map_idx = -1; obj->kern_version = get_kernel_version(); obj->loaded = false; @@ -866,7 +904,8 @@ bpf_object__init_internal_map(struct bpf_object *obj, enum libbpf_map_type type, def->key_size = sizeof(int); def->value_size = data_sz; def->max_entries = 1; - def->map_flags = type == LIBBPF_MAP_RODATA ? BPF_F_RDONLY_PROG : 0; + def->map_flags = type == LIBBPF_MAP_RODATA || type == LIBBPF_MAP_EXTERN + ? BPF_F_RDONLY_PROG : 0; def->map_flags |= BPF_F_MMAPABLE; pr_debug("map '%s' (global data): at sec_idx %d, offset %zu, flags %x.\n", @@ -883,7 +922,7 @@ bpf_object__init_internal_map(struct bpf_object *obj, enum libbpf_map_type type, return err; } - if (type != LIBBPF_MAP_BSS) + if (data) memcpy(map->mmaped, data, data_sz); pr_debug("map %td is \"%s\"\n", map - obj->maps, map->name); @@ -924,6 +963,271 @@ static int bpf_object__init_global_data_maps(struct bpf_object *obj) return 0; } + +static struct extern_desc *find_extern_by_name(const struct bpf_object *obj, + const void *name) +{ + int i; + + for (i = 0; i < obj->nr_extern; i++) { + if (strcmp(obj->externs[i].name, name) == 0) + return &obj->externs[i]; + } + return NULL; +} + +static int set_ext_value_tri(struct extern_desc *ext, void *ext_val, + char value) +{ + switch (ext->type) { + case EXT_BOOL: + if (value == 'm') { + pr_warn("extern %s=%c should be tristate or char\n", + ext->name, value); + return -EINVAL; + } + *(bool *)ext_val = value == 'y' ? true : false; + break; + case EXT_TRISTATE: + if (value == 'y') + *(enum libbpf_tristate *)ext_val = TRI_YES; + else if (value == 'm') + *(enum libbpf_tristate *)ext_val = TRI_MODULE; + else /* value == 'n' */ + *(enum libbpf_tristate *)ext_val = TRI_NO; + break; + case EXT_CHAR: + *(char *)ext_val = value; + break; + case EXT_UNKNOWN: + case EXT_INT: + case EXT_CHAR_ARR: + default: + pr_warn("extern %s=%c should be bool, tristate, or char\n", + ext->name, value); + return -EINVAL; + } + ext->is_set = true; + return 0; +} + +static int set_ext_value_str(struct extern_desc *ext, char *ext_val, + const char *value) +{ + size_t len; + + if (ext->type != EXT_CHAR_ARR) { + pr_warn("extern %s=%s should char array\n", ext->name, value); + return -EINVAL; + } + + len = strlen(value); + if (value[len - 1] != '"') { + pr_warn("extern '%s': invalid string config '%s'\n", + ext->name, value); + return -EINVAL; + } + + /* strip quotes */ + len -= 2; + if (len >= ext->sz) { + pr_warn("extern '%s': long string config %s of (%zu bytes) truncated to %d bytes\n", + ext->name, value, len, ext->sz - 1); + len = ext->sz - 1; + } + memcpy(ext_val, value + 1, len); + ext_val[len] = '\0'; + ext->is_set = true; + return 0; +} + +static int parse_u64(const char *value, __u64 *res) +{ + char *value_end; + int err; + + errno = 0; + *res = strtoull(value, &value_end, 0); + if (errno) { + err = -errno; + pr_warn("failed to parse '%s' as integer: %d\n", value, err); + return err; + } + if (*value_end) { + pr_warn("failed to parse '%s' as integer completely\n", value); + return -EINVAL; + } + return 0; +} + +static bool is_ext_value_in_range(const struct extern_desc *ext, __u64 v) +{ + int bit_sz = ext->sz * 8; + + if (ext->sz == 8) + return true; + + /* Validate that value stored in u64 fits in integer of `ext->sz` + * bytes size without any loss of information. If the target integer + * is signed, we rely on the following limits of integer type of + * Y bits and subsequent transformation: + * + * -2^(Y-1) <= X <= 2^(Y-1) - 1 + * 0 <= X + 2^(Y-1) <= 2^Y - 1 + * 0 <= X + 2^(Y-1) < 2^Y + * + * For unsigned target integer, check that all the (64 - Y) bits are + * zero. + */ + if (ext->is_signed) + return v + (1ULL << (bit_sz - 1)) < (1ULL << bit_sz); + else + return (v >> bit_sz) == 0; +} + +static int set_ext_value_num(struct extern_desc *ext, void *ext_val, + __u64 value) +{ + if (ext->type != EXT_INT && ext->type != EXT_CHAR) { + pr_warn("extern %s=%llu should be integer\n", + ext->name, value); + return -EINVAL; + } + if (!is_ext_value_in_range(ext, value)) { + pr_warn("extern %s=%llu value doesn't fit in %d bytes\n", + ext->name, value, ext->sz); + return -ERANGE; + } + switch (ext->sz) { + case 1: *(__u8 *)ext_val = value; break; + case 2: *(__u16 *)ext_val = value; break; + case 4: *(__u32 *)ext_val = value; break; + case 8: *(__u64 *)ext_val = value; break; + default: + return -EINVAL; + } + ext->is_set = true; + return 0; +} + +static int bpf_object__read_kernel_config(struct bpf_object *obj, + const char *config_path, + void *data) +{ + char buf[PATH_MAX], *sep, *value; + struct extern_desc *ext; + int len, err = 0; + void *ext_val; + __u64 num; + gzFile file; + + if (config_path) { + file = gzopen(config_path, "r"); + } else { + struct utsname uts; + + uname(&uts); + len = snprintf(buf, PATH_MAX, "/boot/config-%s", uts.release); + if (len < 0) + return -EINVAL; + else if (len >= PATH_MAX) + return -ENAMETOOLONG; + /* gzopen also accepts uncompressed files. */ + file = gzopen(buf, "r"); + if (!file) + file = gzopen("/proc/config.gz", "r"); + } + if (!file) { + pr_warn("failed to read kernel config at '%s'\n", config_path); + return -ENOENT; + } + + while (gzgets(file, buf, sizeof(buf))) { + if (strncmp(buf, "CONFIG_", 7)) + continue; + + sep = strchr(buf, '='); + if (!sep) { + err = -EINVAL; + pr_warn("failed to parse '%s': no separator\n", buf); + goto out; + } + /* Trim ending '\n' */ + len = strlen(buf); + if (buf[len - 1] == '\n') + buf[len - 1] = '\0'; + /* Split on '=' and ensure that a value is present. */ + *sep = '\0'; + if (!sep[1]) { + err = -EINVAL; + *sep = '='; + pr_warn("failed to parse '%s': no value\n", buf); + goto out; + } + + ext = find_extern_by_name(obj, buf); + if (!ext) + continue; + if (ext->is_set) { + err = -EINVAL; + pr_warn("re-defining extern '%s' not allowed\n", buf); + goto out; + } + + ext_val = data + ext->data_off; + value = sep + 1; + + switch (*value) { + case 'y': case 'n': case 'm': + err = set_ext_value_tri(ext, ext_val, *value); + break; + case '"': + err = set_ext_value_str(ext, ext_val, value); + break; + default: + /* assume integer */ + err = parse_u64(value, &num); + if (err) { + pr_warn("extern %s=%s should be integer\n", + ext->name, value); + goto out; + } + err = set_ext_value_num(ext, ext_val, num); + break; + } + if (err) + goto out; + pr_debug("extern %s=%s\n", ext->name, value); + } + +out: + gzclose(file); + return err; +} + +static int bpf_object__init_extern_map(struct bpf_object *obj) +{ + struct extern_desc *last_ext; + size_t map_sz; + int err; + + if (obj->nr_extern == 0) + return 0; + + last_ext = &obj->externs[obj->nr_extern - 1]; + map_sz = last_ext->data_off + last_ext->sz; + + err = bpf_object__init_internal_map(obj, LIBBPF_MAP_EXTERN, + obj->efile.symbols_shndx, + NULL, map_sz); + if (err) + return err; + + obj->extern_map_idx = obj->nr_maps - 1; + + return 0; +} + static int bpf_object__init_user_maps(struct bpf_object *obj, bool strict) { Elf_Data *symbols = obj->efile.symbols; @@ -1401,19 +1705,17 @@ static int bpf_object__init_user_btf_maps(struct bpf_object *obj, bool strict, static int bpf_object__init_maps(struct bpf_object *obj, const struct bpf_object_open_opts *opts) { - const char *pin_root_path = OPTS_GET(opts, pin_root_path, NULL); - bool strict = !OPTS_GET(opts, relaxed_maps, false); + const char *pin_root_path; + bool strict; int err; - err = bpf_object__init_user_maps(obj, strict); - if (err) - return err; + strict = !OPTS_GET(opts, relaxed_maps, false); + pin_root_path = OPTS_GET(opts, pin_root_path, NULL); - err = bpf_object__init_user_btf_maps(obj, strict, pin_root_path); - if (err) - return err; - - err = bpf_object__init_global_data_maps(obj); + err = bpf_object__init_user_maps(obj, strict); + err = err ?: bpf_object__init_user_btf_maps(obj, strict, pin_root_path); + err = err ?: bpf_object__init_global_data_maps(obj); + err = err ?: bpf_object__init_extern_map(obj); if (err) return err; @@ -1532,11 +1834,6 @@ static int bpf_object__init_btf(struct bpf_object *obj, BTF_ELF_SEC, err); goto out; } - err = btf__finalize_data(obj, obj->btf); - if (err) { - pr_warn("Error finalizing %s: %d.\n", BTF_ELF_SEC, err); - goto out; - } } if (btf_ext_data) { if (!obj->btf) { @@ -1570,6 +1867,30 @@ out: return 0; } +static int bpf_object__finalize_btf(struct bpf_object *obj) +{ + int err; + + if (!obj->btf) + return 0; + + err = btf__finalize_data(obj, obj->btf); + if (!err) + return 0; + + pr_warn("Error finalizing %s: %d.\n", BTF_ELF_SEC, err); + btf__free(obj->btf); + obj->btf = NULL; + btf_ext__free(obj->btf_ext); + obj->btf_ext = NULL; + + if (bpf_object__is_btf_mandatory(obj)) { + pr_warn("BTF is required, but is missing or corrupted.\n"); + return -ENOENT; + } + return 0; +} + static int bpf_object__sanitize_and_load_btf(struct bpf_object *obj) { int err = 0; @@ -1670,6 +1991,7 @@ static int bpf_object__elf_collect(struct bpf_object *obj) return -LIBBPF_ERRNO__FORMAT; } obj->efile.symbols = data; + obj->efile.symbols_shndx = idx; obj->efile.strtabidx = sh.sh_link; } else if (sh.sh_type == SHT_PROGBITS && data->d_size > 0) { if (sh.sh_flags & SHF_EXECINSTR) { @@ -1737,6 +2059,216 @@ static int bpf_object__elf_collect(struct bpf_object *obj) return bpf_object__init_btf(obj, btf_data, btf_ext_data); } +static bool sym_is_extern(const GElf_Sym *sym) +{ + int bind = GELF_ST_BIND(sym->st_info); + /* externs are symbols w/ type=NOTYPE, bind=GLOBAL|WEAK, section=UND */ + return sym->st_shndx == SHN_UNDEF && + (bind == STB_GLOBAL || bind == STB_WEAK) && + GELF_ST_TYPE(sym->st_info) == STT_NOTYPE; +} + +static int find_extern_btf_id(const struct btf *btf, const char *ext_name) +{ + const struct btf_type *t; + const char *var_name; + int i, n; + + if (!btf) + return -ESRCH; + + n = btf__get_nr_types(btf); + for (i = 1; i <= n; i++) { + t = btf__type_by_id(btf, i); + + if (!btf_is_var(t)) + continue; + + var_name = btf__name_by_offset(btf, t->name_off); + if (strcmp(var_name, ext_name)) + continue; + + if (btf_var(t)->linkage != BTF_VAR_GLOBAL_EXTERN) + return -EINVAL; + + return i; + } + + return -ENOENT; +} + +static enum extern_type find_extern_type(const struct btf *btf, int id, + bool *is_signed) +{ + const struct btf_type *t; + const char *name; + + t = skip_mods_and_typedefs(btf, id, NULL); + name = btf__name_by_offset(btf, t->name_off); + + if (is_signed) + *is_signed = false; + switch (btf_kind(t)) { + case BTF_KIND_INT: { + int enc = btf_int_encoding(t); + + if (enc & BTF_INT_BOOL) + return t->size == 1 ? EXT_BOOL : EXT_UNKNOWN; + if (is_signed) + *is_signed = enc & BTF_INT_SIGNED; + if (t->size == 1) + return EXT_CHAR; + if (t->size < 1 || t->size > 8 || (t->size & (t->size - 1))) + return EXT_UNKNOWN; + return EXT_INT; + } + case BTF_KIND_ENUM: + if (t->size != 4) + return EXT_UNKNOWN; + if (strcmp(name, "libbpf_tristate")) + return EXT_UNKNOWN; + return EXT_TRISTATE; + case BTF_KIND_ARRAY: + if (btf_array(t)->nelems == 0) + return EXT_UNKNOWN; + if (find_extern_type(btf, btf_array(t)->type, NULL) != EXT_CHAR) + return EXT_UNKNOWN; + return EXT_CHAR_ARR; + default: + return EXT_UNKNOWN; + } +} + +static int cmp_externs(const void *_a, const void *_b) +{ + const struct extern_desc *a = _a; + const struct extern_desc *b = _b; + + /* descending order by alignment requirements */ + if (a->align != b->align) + return a->align > b->align ? -1 : 1; + /* ascending order by size, within same alignment class */ + if (a->sz != b->sz) + return a->sz < b->sz ? -1 : 1; + /* resolve ties by name */ + return strcmp(a->name, b->name); +} + +static int bpf_object__collect_externs(struct bpf_object *obj) +{ + const struct btf_type *t; + struct extern_desc *ext; + int i, n, off, btf_id; + struct btf_type *sec; + const char *ext_name; + Elf_Scn *scn; + GElf_Shdr sh; + + if (!obj->efile.symbols) + return 0; + + scn = elf_getscn(obj->efile.elf, obj->efile.symbols_shndx); + if (!scn) + return -LIBBPF_ERRNO__FORMAT; + if (gelf_getshdr(scn, &sh) != &sh) + return -LIBBPF_ERRNO__FORMAT; + n = sh.sh_size / sh.sh_entsize; + + pr_debug("looking for externs among %d symbols...\n", n); + for (i = 0; i < n; i++) { + GElf_Sym sym; + + if (!gelf_getsym(obj->efile.symbols, i, &sym)) + return -LIBBPF_ERRNO__FORMAT; + if (!sym_is_extern(&sym)) + continue; + ext_name = elf_strptr(obj->efile.elf, obj->efile.strtabidx, + sym.st_name); + if (!ext_name || !ext_name[0]) + continue; + + ext = obj->externs; + ext = reallocarray(ext, obj->nr_extern + 1, sizeof(*ext)); + if (!ext) + return -ENOMEM; + obj->externs = ext; + ext = &ext[obj->nr_extern]; + memset(ext, 0, sizeof(*ext)); + obj->nr_extern++; + + ext->btf_id = find_extern_btf_id(obj->btf, ext_name); + if (ext->btf_id <= 0) { + pr_warn("failed to find BTF for extern '%s': %d\n", + ext_name, ext->btf_id); + return ext->btf_id; + } + t = btf__type_by_id(obj->btf, ext->btf_id); + ext->name = btf__name_by_offset(obj->btf, t->name_off); + ext->sym_idx = i; + ext->is_weak = GELF_ST_BIND(sym.st_info) == STB_WEAK; + ext->sz = btf__resolve_size(obj->btf, t->type); + if (ext->sz <= 0) { + pr_warn("failed to resolve size of extern '%s': %d\n", + ext_name, ext->sz); + return ext->sz; + } + ext->align = btf__align_of(obj->btf, t->type); + if (ext->align <= 0) { + pr_warn("failed to determine alignment of extern '%s': %d\n", + ext_name, ext->align); + return -EINVAL; + } + ext->type = find_extern_type(obj->btf, t->type, + &ext->is_signed); + if (ext->type == EXT_UNKNOWN) { + pr_warn("extern '%s' type is unsupported\n", ext_name); + return -ENOTSUP; + } + } + pr_debug("collected %d externs total\n", obj->nr_extern); + + if (!obj->nr_extern) + return 0; + + /* sort externs by (alignment, size, name) and calculate their offsets + * within a map */ + qsort(obj->externs, obj->nr_extern, sizeof(*ext), cmp_externs); + off = 0; + for (i = 0; i < obj->nr_extern; i++) { + ext = &obj->externs[i]; + ext->data_off = roundup(off, ext->align); + off = ext->data_off + ext->sz; + pr_debug("extern #%d: symbol %d, off %u, name %s\n", + i, ext->sym_idx, ext->data_off, ext->name); + } + + btf_id = btf__find_by_name(obj->btf, EXTERN_SEC); + if (btf_id <= 0) { + pr_warn("no BTF info found for '%s' datasec\n", EXTERN_SEC); + return -ESRCH; + } + + sec = (struct btf_type *)btf__type_by_id(obj->btf, btf_id); + sec->size = off; + n = btf_vlen(sec); + for (i = 0; i < n; i++) { + struct btf_var_secinfo *vs = btf_var_secinfos(sec) + i; + + t = btf__type_by_id(obj->btf, vs->type); + ext_name = btf__name_by_offset(obj->btf, t->name_off); + ext = find_extern_by_name(obj, ext_name); + if (!ext) { + pr_warn("failed to find extern definition for BTF var '%s'\n", + ext_name); + return -ESRCH; + } + vs->offset = ext->data_off; + btf_var(t)->linkage = BTF_VAR_GLOBAL_ALLOCATED; + } + + return 0; +} + static struct bpf_program * bpf_object__find_prog_by_idx(struct bpf_object *obj, int idx) { @@ -1801,6 +2333,8 @@ bpf_object__section_to_libbpf_map_type(const struct bpf_object *obj, int shndx) return LIBBPF_MAP_BSS; else if (shndx == obj->efile.rodata_shndx) return LIBBPF_MAP_RODATA; + else if (shndx == obj->efile.symbols_shndx) + return LIBBPF_MAP_EXTERN; else return LIBBPF_MAP_UNSPEC; } @@ -1845,6 +2379,30 @@ static int bpf_program__record_reloc(struct bpf_program *prog, insn_idx, insn->code); return -LIBBPF_ERRNO__RELOC; } + + if (sym_is_extern(sym)) { + int sym_idx = GELF_R_SYM(rel->r_info); + int i, n = obj->nr_extern; + struct extern_desc *ext; + + for (i = 0; i < n; i++) { + ext = &obj->externs[i]; + if (ext->sym_idx == sym_idx) + break; + } + if (i >= n) { + pr_warn("extern relo failed to find extern for sym %d\n", + sym_idx); + return -LIBBPF_ERRNO__RELOC; + } + pr_debug("found extern #%d '%s' (sym %d, off %u) for insn %u\n", + i, ext->name, ext->sym_idx, ext->data_off, insn_idx); + reloc_desc->type = RELO_EXTERN; + reloc_desc->insn_idx = insn_idx; + reloc_desc->sym_off = ext->data_off; + return 0; + } + if (!shdr_idx || shdr_idx >= SHN_LORESERVE) { pr_warn("invalid relo for \'%s\' in special section 0x%x; forgot to initialize global var?..\n", name, shdr_idx); @@ -2306,11 +2864,12 @@ bpf_object__reuse_map(struct bpf_map *map) static int bpf_object__populate_internal_map(struct bpf_object *obj, struct bpf_map *map) { + enum libbpf_map_type map_type = map->libbpf_type; char *cp, errmsg[STRERR_BUFSIZE]; int err, zero = 0; - /* Nothing to do here since kernel already zero-initializes .bss map. */ - if (map->libbpf_type == LIBBPF_MAP_BSS) + /* kernel already zero-initializes .bss map. */ + if (map_type == LIBBPF_MAP_BSS) return 0; err = bpf_map_update_elem(map->fd, &zero, map->mmaped, 0); @@ -2322,8 +2881,8 @@ bpf_object__populate_internal_map(struct bpf_object *obj, struct bpf_map *map) return err; } - /* Freeze .rodata map as read-only from syscall side. */ - if (map->libbpf_type == LIBBPF_MAP_RODATA) { + /* Freeze .rodata and .extern map as read-only from syscall side. */ + if (map_type == LIBBPF_MAP_RODATA || map_type == LIBBPF_MAP_EXTERN) { err = bpf_map_freeze(map->fd); if (err) { err = -errno; @@ -3572,9 +4131,6 @@ bpf_program__reloc_text(struct bpf_program *prog, struct bpf_object *obj, size_t new_cnt; int err; - if (relo->type != RELO_CALL) - return -LIBBPF_ERRNO__RELOC; - if (prog->idx == obj->efile.text_shndx) { pr_warn("relo in .text insn %d into off %d (insn #%d)\n", relo->insn_idx, relo->sym_off, relo->sym_off / 8); @@ -3636,27 +4192,37 @@ bpf_program__relocate(struct bpf_program *prog, struct bpf_object *obj) for (i = 0; i < prog->nr_reloc; i++) { struct reloc_desc *relo = &prog->reloc_desc[i]; + struct bpf_insn *insn = &prog->insns[relo->insn_idx]; - if (relo->type == RELO_LD64 || relo->type == RELO_DATA) { - struct bpf_insn *insn = &prog->insns[relo->insn_idx]; - - if (relo->insn_idx + 1 >= (int)prog->insns_cnt) { - pr_warn("relocation out of range: '%s'\n", - prog->section_name); - return -LIBBPF_ERRNO__RELOC; - } + if (relo->insn_idx + 1 >= (int)prog->insns_cnt) { + pr_warn("relocation out of range: '%s'\n", + prog->section_name); + return -LIBBPF_ERRNO__RELOC; + } - if (relo->type != RELO_DATA) { - insn[0].src_reg = BPF_PSEUDO_MAP_FD; - } else { - insn[0].src_reg = BPF_PSEUDO_MAP_VALUE; - insn[1].imm = insn[0].imm + relo->sym_off; - } + switch (relo->type) { + case RELO_LD64: + insn[0].src_reg = BPF_PSEUDO_MAP_FD; + insn[0].imm = obj->maps[relo->map_idx].fd; + break; + case RELO_DATA: + insn[0].src_reg = BPF_PSEUDO_MAP_VALUE; + insn[1].imm = insn[0].imm + relo->sym_off; insn[0].imm = obj->maps[relo->map_idx].fd; - } else if (relo->type == RELO_CALL) { + break; + case RELO_EXTERN: + insn[0].src_reg = BPF_PSEUDO_MAP_VALUE; + insn[0].imm = obj->maps[obj->extern_map_idx].fd; + insn[1].imm = relo->sym_off; + break; + case RELO_CALL: err = bpf_program__reloc_text(prog, obj, relo); if (err) return err; + break; + default: + pr_warn("relo #%d: bad relo type %d\n", i, relo->type); + return -EINVAL; } } @@ -3936,11 +4502,10 @@ static struct bpf_object * __bpf_object__open(const char *path, const void *obj_buf, size_t obj_buf_sz, const struct bpf_object_open_opts *opts) { + const char *obj_name, *kconfig_path; struct bpf_program *prog; struct bpf_object *obj; - const char *obj_name; char tmp_name[64]; - __u32 attach_prog_fd; int err; if (elf_version(EV_CURRENT) == EV_NONE) { @@ -3969,11 +4534,18 @@ __bpf_object__open(const char *path, const void *obj_buf, size_t obj_buf_sz, return obj; obj->relaxed_core_relocs = OPTS_GET(opts, relaxed_core_relocs, false); - attach_prog_fd = OPTS_GET(opts, attach_prog_fd, 0); + kconfig_path = OPTS_GET(opts, kconfig_path, NULL); + if (kconfig_path) { + obj->kconfig_path = strdup(kconfig_path); + if (!obj->kconfig_path) + return ERR_PTR(-ENOMEM); + } err = bpf_object__elf_init(obj); err = err ? : bpf_object__check_endianness(obj); err = err ? : bpf_object__elf_collect(obj); + err = err ? : bpf_object__collect_externs(obj); + err = err ? : bpf_object__finalize_btf(obj); err = err ? : bpf_object__init_maps(obj, opts); err = err ? : bpf_object__init_prog_names(obj); err = err ? : bpf_object__collect_reloc(obj); @@ -3996,7 +4568,7 @@ __bpf_object__open(const char *path, const void *obj_buf, size_t obj_buf_sz, bpf_program__set_type(prog, prog_type); bpf_program__set_expected_attach_type(prog, attach_type); if (prog_type == BPF_PROG_TYPE_TRACING) - prog->attach_prog_fd = attach_prog_fd; + prog->attach_prog_fd = OPTS_GET(opts, attach_prog_fd, 0); } return obj; @@ -4107,6 +4679,61 @@ static int bpf_object__sanitize_maps(struct bpf_object *obj) return 0; } +static int bpf_object__resolve_externs(struct bpf_object *obj, + const char *config_path) +{ + bool need_config = false; + struct extern_desc *ext; + int err, i; + void *data; + + if (obj->nr_extern == 0) + return 0; + + data = obj->maps[obj->extern_map_idx].mmaped; + + for (i = 0; i < obj->nr_extern; i++) { + ext = &obj->externs[i]; + + if (strcmp(ext->name, "LINUX_KERNEL_VERSION") == 0) { + void *ext_val = data + ext->data_off; + __u32 kver = get_kernel_version(); + + if (!kver) { + pr_warn("failed to get kernel version\n"); + return -EINVAL; + } + err = set_ext_value_num(ext, ext_val, kver); + if (err) + return err; + pr_debug("extern %s=0x%x\n", ext->name, kver); + } else if (strncmp(ext->name, "CONFIG_", 7) == 0) { + need_config = true; + } else { + pr_warn("unrecognized extern '%s'\n", ext->name); + return -EINVAL; + } + } + if (need_config) { + err = bpf_object__read_kernel_config(obj, config_path, data); + if (err) + return -EINVAL; + } + for (i = 0; i < obj->nr_extern; i++) { + ext = &obj->externs[i]; + + if (!ext->is_set && !ext->is_weak) { + pr_warn("extern %s (strong) not resolved\n", ext->name); + return -ESRCH; + } else if (!ext->is_set) { + pr_debug("extern %s (weak) not resolved, defaulting to zero\n", + ext->name); + } + } + + return 0; +} + int bpf_object__load_xattr(struct bpf_object_load_attr *attr) { struct bpf_object *obj; @@ -4126,6 +4753,7 @@ int bpf_object__load_xattr(struct bpf_object_load_attr *attr) obj->loaded = true; err = bpf_object__probe_caps(obj); + err = err ? : bpf_object__resolve_externs(obj, obj->kconfig_path); err = err ? : bpf_object__sanitize_and_load_btf(obj); err = err ? : bpf_object__sanitize_maps(obj); err = err ? : bpf_object__create_maps(obj); @@ -4719,6 +5347,10 @@ void bpf_object__close(struct bpf_object *obj) zfree(&map->pin_path); } + zfree(&obj->kconfig_path); + zfree(&obj->externs); + obj->nr_extern = 0; + zfree(&obj->maps); obj->nr_maps = 0; diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h index 623191e714154..6340823871e20 100644 --- a/tools/lib/bpf/libbpf.h +++ b/tools/lib/bpf/libbpf.h @@ -85,8 +85,12 @@ struct bpf_object_open_opts { */ const char *pin_root_path; __u32 attach_prog_fd; + /* kernel config file path override (for CONFIG_ externs); can point + * to either uncompressed text file or .gz file + */ + const char *kconfig_path; }; -#define bpf_object_open_opts__last_field attach_prog_fd +#define bpf_object_open_opts__last_field kconfig_path LIBBPF_API struct bpf_object *bpf_object__open(const char *path); LIBBPF_API struct bpf_object * @@ -669,6 +673,12 @@ LIBBPF_API int bpf_object__attach_skeleton(struct bpf_object_skeleton *s); LIBBPF_API void bpf_object__detach_skeleton(struct bpf_object_skeleton *s); LIBBPF_API void bpf_object__destroy_skeleton(struct bpf_object_skeleton *s); +enum libbpf_tristate { + TRI_NO = 0, + TRI_YES = 1, + TRI_MODULE = 2, +}; + #ifdef __cplusplus } /* extern "C" */ #endif diff --git a/tools/testing/selftests/bpf/Makefile b/tools/testing/selftests/bpf/Makefile index f70c8e735120a..ba90621617a8d 100644 --- a/tools/testing/selftests/bpf/Makefile +++ b/tools/testing/selftests/bpf/Makefile @@ -24,7 +24,7 @@ CFLAGS += -g -Wall -O2 $(GENFLAGS) -I$(APIDIR) -I$(LIBDIR) -I$(BPFDIR) \ -I$(GENDIR) -I$(TOOLSINCDIR) -I$(CURDIR) \ -Dbpf_prog_load=bpf_prog_test_load \ -Dbpf_load_program=bpf_test_load_program -LDLIBS += -lcap -lelf -lrt -lpthread +LDLIBS += -lcap -lelf -lz -lrt -lpthread # Order correspond to 'make run_tests' order TEST_GEN_PROGS = test_verifier test_tag test_maps test_lru_map test_lpm_map test_progs \ -- GitLab From 2ad97d473db57ab866f0756806bb94515f7f2551 Mon Sep 17 00:00:00 2001 From: Andrii Nakryiko Date: Fri, 13 Dec 2019 17:47:09 -0800 Subject: [PATCH 0703/1437] bpftool: Generate externs datasec in BPF skeleton Add support for generation of mmap()-ed read-only view of libbpf-provided extern variables. As externs are not supposed to be provided by user code (that's what .data, .bss, and .rodata is for), don't mmap() it initially. Only after skeleton load is performed, map .extern contents as read-only memory. Signed-off-by: Andrii Nakryiko Signed-off-by: Alexei Starovoitov Link: https://lore.kernel.org/bpf/20191214014710.3449601-4-andriin@fb.com --- tools/bpf/bpftool/gen.c | 4 ++++ tools/lib/bpf/libbpf.c | 10 +++++----- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/tools/bpf/bpftool/gen.c b/tools/bpf/bpftool/gen.c index 7379dae35dcaf..a07c80429c7a3 100644 --- a/tools/bpf/bpftool/gen.c +++ b/tools/bpf/bpftool/gen.c @@ -82,6 +82,8 @@ static const char *get_map_ident(const struct bpf_map *map) return "rodata"; else if (str_has_suffix(name, ".bss")) return "bss"; + else if (str_has_suffix(name, ".extern")) + return "externs"; /* extern is a C keyword */ else return NULL; } @@ -109,6 +111,8 @@ static int codegen_datasec_def(struct bpf_object *obj, sec_ident = "bss"; else if (strcmp(sec_name, ".rodata") == 0) sec_ident = "rodata"; + else if (strcmp(sec_name, ".extern") == 0) + sec_ident = "externs"; /* extern is a C keyword */ else return 0; diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c index 26144706ba0bb..c9f90bfd2f0d8 100644 --- a/tools/lib/bpf/libbpf.c +++ b/tools/lib/bpf/libbpf.c @@ -7472,7 +7472,8 @@ int bpf_object__open_skeleton(struct bpf_object_skeleton *s, return -ESRCH; } - if (mmaped) + /* externs shouldn't be pre-setup from user code */ + if (mmaped && (*map)->libbpf_type != LIBBPF_MAP_EXTERN) *mmaped = (*map)->mmaped; } @@ -7505,7 +7506,6 @@ int bpf_object__load_skeleton(struct bpf_object_skeleton *s) size_t mmap_sz = bpf_map_mmap_sz(map); int prot, map_fd = bpf_map__fd(map); void **mmaped = s->maps[i].mmaped; - void *remapped; if (!mmaped) continue; @@ -7530,9 +7530,9 @@ int bpf_object__load_skeleton(struct bpf_object_skeleton *s) * as per normal clean up procedure, so we don't need to worry * about it from skeleton's clean up perspective. */ - remapped = mmap(*mmaped, mmap_sz, prot, MAP_SHARED | MAP_FIXED, - map_fd, 0); - if (remapped == MAP_FAILED) { + *mmaped = mmap(map->mmaped, mmap_sz, prot, + MAP_SHARED | MAP_FIXED, map_fd, 0); + if (*mmaped == MAP_FAILED) { err = -errno; *mmaped = NULL; pr_warn("failed to re-mmap() map '%s': %d\n", -- GitLab From 330a73a7b6ca93a415de1b7da68d7a0698fe4937 Mon Sep 17 00:00:00 2001 From: Andrii Nakryiko Date: Fri, 13 Dec 2019 17:47:10 -0800 Subject: [PATCH 0704/1437] selftests/bpf: Add tests for libbpf-provided externs Add a set of tests validating libbpf-provided extern variables. One crucial feature that's tested is dead code elimination together with using invalid BPF helper. CONFIG_MISSING is not supposed to exist and should always be specified by libbpf as zero, which allows BPF verifier to correctly do branch pruning and not fail validation, when invalid BPF helper is called from dead if branch. Signed-off-by: Andrii Nakryiko Signed-off-by: Alexei Starovoitov Link: https://lore.kernel.org/bpf/20191214014710.3449601-5-andriin@fb.com --- .../selftests/bpf/prog_tests/core_extern.c | 195 ++++++++++++++++++ .../selftests/bpf/prog_tests/skeleton.c | 18 +- .../selftests/bpf/progs/test_core_extern.c | 62 ++++++ .../selftests/bpf/progs/test_skeleton.c | 9 + 4 files changed, 283 insertions(+), 1 deletion(-) create mode 100644 tools/testing/selftests/bpf/prog_tests/core_extern.c create mode 100644 tools/testing/selftests/bpf/progs/test_core_extern.c diff --git a/tools/testing/selftests/bpf/prog_tests/core_extern.c b/tools/testing/selftests/bpf/prog_tests/core_extern.c new file mode 100644 index 0000000000000..30a7972e90120 --- /dev/null +++ b/tools/testing/selftests/bpf/prog_tests/core_extern.c @@ -0,0 +1,195 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2019 Facebook */ + +#include +#include +#include +#include +#include "test_core_extern.skel.h" + +static uint32_t get_kernel_version(void) +{ + uint32_t major, minor, patch; + struct utsname info; + + uname(&info); + if (sscanf(info.release, "%u.%u.%u", &major, &minor, &patch) != 3) + return 0; + return KERNEL_VERSION(major, minor, patch); +} + +#define CFG "CONFIG_BPF_SYSCALL=n\n" + +static struct test_case { + const char *name; + const char *cfg; + const char *cfg_path; + bool fails; + struct test_core_extern__data data; +} test_cases[] = { + { .name = "default search path", .cfg_path = NULL, + .data = { .bpf_syscall = true } }, + { .name = "/proc/config.gz", .cfg_path = "/proc/config.gz", + .data = { .bpf_syscall = true } }, + { .name = "missing config", .fails = true, + .cfg_path = "/proc/invalid-config.gz" }, + { + .name = "custom values", + .cfg = "CONFIG_BPF_SYSCALL=y\n" + "CONFIG_TRISTATE=m\n" + "CONFIG_BOOL=y\n" + "CONFIG_CHAR=100\n" + "CONFIG_USHORT=30000\n" + "CONFIG_INT=123456\n" + "CONFIG_ULONG=0xDEADBEEFC0DE\n" + "CONFIG_STR=\"abracad\"\n" + "CONFIG_MISSING=0", + .data = { + .bpf_syscall = true, + .tristate_val = TRI_MODULE, + .bool_val = true, + .char_val = 100, + .ushort_val = 30000, + .int_val = 123456, + .ulong_val = 0xDEADBEEFC0DE, + .str_val = "abracad", + }, + }, + /* TRISTATE */ + { .name = "tristate (y)", .cfg = CFG"CONFIG_TRISTATE=y\n", + .data = { .tristate_val = TRI_YES } }, + { .name = "tristate (n)", .cfg = CFG"CONFIG_TRISTATE=n\n", + .data = { .tristate_val = TRI_NO } }, + { .name = "tristate (m)", .cfg = CFG"CONFIG_TRISTATE=m\n", + .data = { .tristate_val = TRI_MODULE } }, + { .name = "tristate (int)", .fails = 1, .cfg = CFG"CONFIG_TRISTATE=1" }, + { .name = "tristate (bad)", .fails = 1, .cfg = CFG"CONFIG_TRISTATE=M" }, + /* BOOL */ + { .name = "bool (y)", .cfg = CFG"CONFIG_BOOL=y\n", + .data = { .bool_val = true } }, + { .name = "bool (n)", .cfg = CFG"CONFIG_BOOL=n\n", + .data = { .bool_val = false } }, + { .name = "bool (tristate)", .fails = 1, .cfg = CFG"CONFIG_BOOL=m" }, + { .name = "bool (int)", .fails = 1, .cfg = CFG"CONFIG_BOOL=1" }, + /* CHAR */ + { .name = "char (tristate)", .cfg = CFG"CONFIG_CHAR=m\n", + .data = { .char_val = 'm' } }, + { .name = "char (bad)", .fails = 1, .cfg = CFG"CONFIG_CHAR=q\n" }, + { .name = "char (empty)", .fails = 1, .cfg = CFG"CONFIG_CHAR=\n" }, + { .name = "char (str)", .fails = 1, .cfg = CFG"CONFIG_CHAR=\"y\"\n" }, + /* STRING */ + { .name = "str (empty)", .cfg = CFG"CONFIG_STR=\"\"\n", + .data = { .str_val = "\0\0\0\0\0\0\0" } }, + { .name = "str (padded)", .cfg = CFG"CONFIG_STR=\"abra\"\n", + .data = { .str_val = "abra\0\0\0" } }, + { .name = "str (too long)", .cfg = CFG"CONFIG_STR=\"abracada\"\n", + .data = { .str_val = "abracad" } }, + { .name = "str (no value)", .fails = 1, .cfg = CFG"CONFIG_STR=\n" }, + { .name = "str (bad value)", .fails = 1, .cfg = CFG"CONFIG_STR=bla\n" }, + /* INTEGERS */ + { + .name = "integer forms", + .cfg = CFG + "CONFIG_CHAR=0xA\n" + "CONFIG_USHORT=0462\n" + "CONFIG_INT=-100\n" + "CONFIG_ULONG=+1000000000000", + .data = { + .char_val = 0xA, + .ushort_val = 0462, + .int_val = -100, + .ulong_val = 1000000000000, + }, + }, + { .name = "int (bad)", .fails = 1, .cfg = CFG"CONFIG_INT=abc" }, + { .name = "int (str)", .fails = 1, .cfg = CFG"CONFIG_INT=\"abc\"" }, + { .name = "int (empty)", .fails = 1, .cfg = CFG"CONFIG_INT=" }, + { .name = "int (mixed)", .fails = 1, .cfg = CFG"CONFIG_INT=123abc" }, + { .name = "int (max)", .cfg = CFG"CONFIG_INT=2147483647", + .data = { .int_val = 2147483647 } }, + { .name = "int (min)", .cfg = CFG"CONFIG_INT=-2147483648", + .data = { .int_val = -2147483648 } }, + { .name = "int (max+1)", .fails = 1, .cfg = CFG"CONFIG_INT=2147483648" }, + { .name = "int (min-1)", .fails = 1, .cfg = CFG"CONFIG_INT=-2147483649" }, + { .name = "ushort (max)", .cfg = CFG"CONFIG_USHORT=65535", + .data = { .ushort_val = 65535 } }, + { .name = "ushort (min)", .cfg = CFG"CONFIG_USHORT=0", + .data = { .ushort_val = 0 } }, + { .name = "ushort (max+1)", .fails = 1, .cfg = CFG"CONFIG_USHORT=65536" }, + { .name = "ushort (min-1)", .fails = 1, .cfg = CFG"CONFIG_USHORT=-1" }, + { .name = "u64 (max)", .cfg = CFG"CONFIG_ULONG=0xffffffffffffffff", + .data = { .ulong_val = 0xffffffffffffffff } }, + { .name = "u64 (min)", .cfg = CFG"CONFIG_ULONG=0", + .data = { .ulong_val = 0 } }, + { .name = "u64 (max+1)", .fails = 1, .cfg = CFG"CONFIG_ULONG=0x10000000000000000" }, +}; + +BPF_EMBED_OBJ(core_extern, "test_core_extern.o"); + +void test_core_extern(void) +{ + const uint32_t kern_ver = get_kernel_version(); + int err, duration = 0, i, j; + struct test_core_extern *skel = NULL; + uint64_t *got, *exp; + int n = sizeof(*skel->data) / sizeof(uint64_t); + + for (i = 0; i < ARRAY_SIZE(test_cases); i++) { + char tmp_cfg_path[] = "/tmp/test_core_extern_cfg.XXXXXX"; + struct test_case *t = &test_cases[i]; + DECLARE_LIBBPF_OPTS(bpf_object_open_opts, opts, + .kconfig_path = t->cfg_path, + ); + + if (!test__start_subtest(t->name)) + continue; + + if (t->cfg) { + size_t n = strlen(t->cfg) + 1; + int fd = mkstemp(tmp_cfg_path); + int written; + + if (CHECK(fd < 0, "mkstemp", "errno: %d\n", errno)) + continue; + printf("using '%s' as config file\n", tmp_cfg_path); + written = write(fd, t->cfg, n); + close(fd); + if (CHECK_FAIL(written != n)) + goto cleanup; + opts.kconfig_path = tmp_cfg_path; + } + + skel = test_core_extern__open_opts(&core_extern_embed, &opts); + if (CHECK(!skel, "skel_open", "skeleton open failed\n")) + goto cleanup; + err = test_core_extern__load(skel); + if (t->fails) { + CHECK(!err, "skel_load", + "shouldn't succeed open/load of skeleton\n"); + goto cleanup; + } else if (CHECK(err, "skel_load", + "failed to open/load skeleton\n")) { + goto cleanup; + } + err = test_core_extern__attach(skel); + if (CHECK(err, "attach_raw_tp", "failed attach: %d\n", err)) + goto cleanup; + + usleep(1); + + t->data.kern_ver = kern_ver; + t->data.missing_val = 0xDEADC0DE; + got = (uint64_t *)skel->data; + exp = (uint64_t *)&t->data; + for (j = 0; j < n; j++) { + CHECK(got[j] != exp[j], "check_res", + "result #%d: expected %lx, but got %lx\n", + j, exp[j], got[j]); + } +cleanup: + if (t->cfg) + unlink(tmp_cfg_path); + test_core_extern__destroy(skel); + skel = NULL; + } +} diff --git a/tools/testing/selftests/bpf/prog_tests/skeleton.c b/tools/testing/selftests/bpf/prog_tests/skeleton.c index 79f8d13e67403..151cdad3ad0d0 100644 --- a/tools/testing/selftests/bpf/prog_tests/skeleton.c +++ b/tools/testing/selftests/bpf/prog_tests/skeleton.c @@ -17,11 +17,21 @@ void test_skeleton(void) int duration = 0, err; struct test_skeleton* skel; struct test_skeleton__bss *bss; + struct test_skeleton__externs *exts; - skel = test_skeleton__open_and_load(&skeleton_embed); + skel = test_skeleton__open(&skeleton_embed); if (CHECK(!skel, "skel_open", "failed to open skeleton\n")) return; + printf("EXTERNS BEFORE: %p\n", skel->externs); + if (CHECK(skel->externs, "skel_externs", "externs are mmaped()!\n")) + goto cleanup; + + err = test_skeleton__load(skel); + if (CHECK(err, "skel_load", "failed to load skeleton: %d\n", err)) + goto cleanup; + printf("EXTERNS AFTER: %p\n", skel->externs); + bss = skel->bss; bss->in1 = 1; bss->in2 = 2; @@ -29,6 +39,7 @@ void test_skeleton(void) bss->in4 = 4; bss->in5.a = 5; bss->in5.b = 6; + exts = skel->externs; err = test_skeleton__attach(skel); if (CHECK(err, "skel_attach", "skeleton attach failed: %d\n", err)) @@ -46,6 +57,11 @@ void test_skeleton(void) CHECK(bss->handler_out5.b != 6, "res6", "got %lld != exp %d\n", bss->handler_out5.b, 6); + CHECK(bss->bpf_syscall != exts->CONFIG_BPF_SYSCALL, "ext1", + "got %d != exp %d\n", bss->bpf_syscall, exts->CONFIG_BPF_SYSCALL); + CHECK(bss->kern_ver != exts->LINUX_KERNEL_VERSION, "ext2", + "got %d != exp %d\n", bss->kern_ver, exts->LINUX_KERNEL_VERSION); + cleanup: test_skeleton__destroy(skel); } diff --git a/tools/testing/selftests/bpf/progs/test_core_extern.c b/tools/testing/selftests/bpf/progs/test_core_extern.c new file mode 100644 index 0000000000000..e12f09f9e881a --- /dev/null +++ b/tools/testing/selftests/bpf/progs/test_core_extern.c @@ -0,0 +1,62 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2019 Facebook */ + +#include +#include +#include +#include +#include "bpf_helpers.h" + +/* non-existing BPF helper, to test dead code elimination */ +static int (*bpf_missing_helper)(const void *arg1, int arg2) = (void *) 999; + +extern int LINUX_KERNEL_VERSION; +extern bool CONFIG_BPF_SYSCALL; /* strong */ +extern enum libbpf_tristate CONFIG_TRISTATE __weak; +extern bool CONFIG_BOOL __weak; +extern char CONFIG_CHAR __weak; +extern uint16_t CONFIG_USHORT __weak; +extern int CONFIG_INT __weak; +extern uint64_t CONFIG_ULONG __weak; +extern const char CONFIG_STR[8] __weak; +extern uint64_t CONFIG_MISSING __weak; + +uint64_t kern_ver = -1; +uint64_t bpf_syscall = -1; +uint64_t tristate_val = -1; +uint64_t bool_val = -1; +uint64_t char_val = -1; +uint64_t ushort_val = -1; +uint64_t int_val = -1; +uint64_t ulong_val = -1; +char str_val[8] = {-1, -1, -1, -1, -1, -1, -1, -1}; +uint64_t missing_val = -1; + +SEC("raw_tp/sys_enter") +int handle_sys_enter(struct pt_regs *ctx) +{ + int i; + + kern_ver = LINUX_KERNEL_VERSION; + bpf_syscall = CONFIG_BPF_SYSCALL; + tristate_val = CONFIG_TRISTATE; + bool_val = CONFIG_BOOL; + char_val = CONFIG_CHAR; + ushort_val = CONFIG_USHORT; + int_val = CONFIG_INT; + ulong_val = CONFIG_ULONG; + + for (i = 0; i < sizeof(CONFIG_STR); i++) { + str_val[i] = CONFIG_STR[i]; + } + + if (CONFIG_MISSING) + /* invalid, but dead code - never executed */ + missing_val = bpf_missing_helper(ctx, 123); + else + missing_val = 0xDEADC0DE; + + return 0; +} + +char _license[] SEC("license") = "GPL"; diff --git a/tools/testing/selftests/bpf/progs/test_skeleton.c b/tools/testing/selftests/bpf/progs/test_skeleton.c index c0013d2e16f27..9caa44758ea26 100644 --- a/tools/testing/selftests/bpf/progs/test_skeleton.c +++ b/tools/testing/selftests/bpf/progs/test_skeleton.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 /* Copyright (c) 2019 Facebook */ +#include #include #include "bpf_helpers.h" @@ -20,6 +21,10 @@ char out3 = 0; long long out4 = 0; int out1 = 0; +extern bool CONFIG_BPF_SYSCALL; +extern int LINUX_KERNEL_VERSION; +bool bpf_syscall = 0; +int kern_ver = 0; SEC("raw_tp/sys_enter") int handler(const void *ctx) @@ -31,6 +36,10 @@ int handler(const void *ctx) out3 = in3; out4 = in4; out5 = in5; + + bpf_syscall = CONFIG_BPF_SYSCALL; + kern_ver = LINUX_KERNEL_VERSION; + return 0; } -- GitLab From 1b484b301cec404e8a329f05edf848fc0a5875c0 Mon Sep 17 00:00:00 2001 From: Andrii Nakryiko Date: Sat, 14 Dec 2019 23:08:43 -0800 Subject: [PATCH 0705/1437] libbpf: Support flexible arrays in CO-RE Some data stuctures in kernel are defined with either zero-sized array or flexible (dimensionless) array at the end of a struct. Actual data of such array follows in memory immediately after the end of that struct, forming its variable-sized "body" of elements. Support such access pattern in CO-RE relocation handling. Signed-off-by: Andrii Nakryiko Signed-off-by: Alexei Starovoitov Link: https://lore.kernel.org/bpf/20191215070844.1014385-2-andriin@fb.com --- tools/lib/bpf/libbpf.c | 34 +++++++++++++++++++++++++++++----- 1 file changed, 29 insertions(+), 5 deletions(-) diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c index c9f90bfd2f0d8..a2cc7313763a8 100644 --- a/tools/lib/bpf/libbpf.c +++ b/tools/lib/bpf/libbpf.c @@ -3108,6 +3108,21 @@ static bool str_is_empty(const char *s) return !s || !s[0]; } +static bool is_flex_arr(const struct btf *btf, + const struct bpf_core_accessor *acc, + const struct btf_array *arr) +{ + const struct btf_type *t; + + /* not a flexible array, if not inside a struct or has non-zero size */ + if (!acc->name || arr->nelems > 0) + return false; + + /* has to be the last member of enclosing struct */ + t = btf__type_by_id(btf, acc->type_id); + return acc->idx == btf_vlen(t) - 1; +} + /* * Turn bpf_field_reloc into a low- and high-level spec representation, * validating correctness along the way, as well as calculating resulting @@ -3145,6 +3160,7 @@ static int bpf_core_spec_parse(const struct btf *btf, struct bpf_core_spec *spec) { int access_idx, parsed_len, i; + struct bpf_core_accessor *acc; const struct btf_type *t; const char *name; __u32 id; @@ -3192,6 +3208,7 @@ static int bpf_core_spec_parse(const struct btf *btf, return -EINVAL; access_idx = spec->raw_spec[i]; + acc = &spec->spec[spec->len]; if (btf_is_composite(t)) { const struct btf_member *m; @@ -3209,18 +3226,23 @@ static int bpf_core_spec_parse(const struct btf *btf, if (str_is_empty(name)) return -EINVAL; - spec->spec[spec->len].type_id = id; - spec->spec[spec->len].idx = access_idx; - spec->spec[spec->len].name = name; + acc->type_id = id; + acc->idx = access_idx; + acc->name = name; spec->len++; } id = m->type; } else if (btf_is_array(t)) { const struct btf_array *a = btf_array(t); + bool flex; t = skip_mods_and_typedefs(btf, a->type, &id); - if (!t || access_idx >= a->nelems) + if (!t) + return -EINVAL; + + flex = is_flex_arr(btf, acc - 1, a); + if (!flex && access_idx >= a->nelems) return -EINVAL; spec->spec[spec->len].type_id = id; @@ -3525,12 +3547,14 @@ static int bpf_core_spec_match(struct bpf_core_spec *local_spec, */ if (i > 0) { const struct btf_array *a; + bool flex; if (!btf_is_array(targ_type)) return 0; a = btf_array(targ_type); - if (local_acc->idx >= a->nelems) + flex = is_flex_arr(targ_btf, targ_acc - 1, a); + if (!flex && local_acc->idx >= a->nelems) return 0; if (!skip_mods_and_typedefs(targ_btf, a->type, &targ_id)) -- GitLab From 5f2eeceffb92a0d799b141df7af3d1ac77337dc4 Mon Sep 17 00:00:00 2001 From: Andrii Nakryiko Date: Sat, 14 Dec 2019 23:08:44 -0800 Subject: [PATCH 0706/1437] selftests/bpf: Add flexible array relocation tests Add few tests validation CO-RE relocation handling of flexible array accesses. Signed-off-by: Andrii Nakryiko Signed-off-by: Alexei Starovoitov Link: https://lore.kernel.org/bpf/20191215070844.1014385-3-andriin@fb.com --- .../selftests/bpf/prog_tests/core_reloc.c | 4 ++ ...f__core_reloc_arrays___equiv_zero_sz_arr.c | 3 ++ ..._core_reloc_arrays___err_bad_zero_sz_arr.c | 3 ++ .../btf__core_reloc_arrays___fixed_arr.c | 3 ++ .../selftests/bpf/progs/core_reloc_types.h | 39 +++++++++++++++++++ .../bpf/progs/test_core_reloc_arrays.c | 8 ++-- 6 files changed, 56 insertions(+), 4 deletions(-) create mode 100644 tools/testing/selftests/bpf/progs/btf__core_reloc_arrays___equiv_zero_sz_arr.c create mode 100644 tools/testing/selftests/bpf/progs/btf__core_reloc_arrays___err_bad_zero_sz_arr.c create mode 100644 tools/testing/selftests/bpf/progs/btf__core_reloc_arrays___fixed_arr.c diff --git a/tools/testing/selftests/bpf/prog_tests/core_reloc.c b/tools/testing/selftests/bpf/prog_tests/core_reloc.c index 05fe85281ff76..31e177adbdf15 100644 --- a/tools/testing/selftests/bpf/prog_tests/core_reloc.c +++ b/tools/testing/selftests/bpf/prog_tests/core_reloc.c @@ -74,6 +74,7 @@ .b123 = 2, \ .c1c = 3, \ .d00d = 4, \ + .f10c = 0, \ }, \ .output_len = sizeof(struct core_reloc_arrays_output) \ } @@ -308,12 +309,15 @@ static struct core_reloc_test_case test_cases[] = { ARRAYS_CASE(arrays), ARRAYS_CASE(arrays___diff_arr_dim), ARRAYS_CASE(arrays___diff_arr_val_sz), + ARRAYS_CASE(arrays___equiv_zero_sz_arr), + ARRAYS_CASE(arrays___fixed_arr), ARRAYS_ERR_CASE(arrays___err_too_small), ARRAYS_ERR_CASE(arrays___err_too_shallow), ARRAYS_ERR_CASE(arrays___err_non_array), ARRAYS_ERR_CASE(arrays___err_wrong_val_type1), ARRAYS_ERR_CASE(arrays___err_wrong_val_type2), + ARRAYS_ERR_CASE(arrays___err_bad_zero_sz_arr), /* enum/ptr/int handling scenarios */ PRIMITIVES_CASE(primitives), diff --git a/tools/testing/selftests/bpf/progs/btf__core_reloc_arrays___equiv_zero_sz_arr.c b/tools/testing/selftests/bpf/progs/btf__core_reloc_arrays___equiv_zero_sz_arr.c new file mode 100644 index 0000000000000..65eac371b061c --- /dev/null +++ b/tools/testing/selftests/bpf/progs/btf__core_reloc_arrays___equiv_zero_sz_arr.c @@ -0,0 +1,3 @@ +#include "core_reloc_types.h" + +void f(struct core_reloc_arrays___equiv_zero_sz_arr x) {} diff --git a/tools/testing/selftests/bpf/progs/btf__core_reloc_arrays___err_bad_zero_sz_arr.c b/tools/testing/selftests/bpf/progs/btf__core_reloc_arrays___err_bad_zero_sz_arr.c new file mode 100644 index 0000000000000..ecda2b545ac25 --- /dev/null +++ b/tools/testing/selftests/bpf/progs/btf__core_reloc_arrays___err_bad_zero_sz_arr.c @@ -0,0 +1,3 @@ +#include "core_reloc_types.h" + +void f(struct core_reloc_arrays___err_bad_zero_sz_arr x) {} diff --git a/tools/testing/selftests/bpf/progs/btf__core_reloc_arrays___fixed_arr.c b/tools/testing/selftests/bpf/progs/btf__core_reloc_arrays___fixed_arr.c new file mode 100644 index 0000000000000..fe1d01232c225 --- /dev/null +++ b/tools/testing/selftests/bpf/progs/btf__core_reloc_arrays___fixed_arr.c @@ -0,0 +1,3 @@ +#include "core_reloc_types.h" + +void f(struct core_reloc_arrays___fixed_arr x) {} diff --git a/tools/testing/selftests/bpf/progs/core_reloc_types.h b/tools/testing/selftests/bpf/progs/core_reloc_types.h index 9311489e14b2b..6d598cfbdb3e2 100644 --- a/tools/testing/selftests/bpf/progs/core_reloc_types.h +++ b/tools/testing/selftests/bpf/progs/core_reloc_types.h @@ -327,6 +327,7 @@ struct core_reloc_arrays_output { char b123; int c1c; int d00d; + int f10c; }; struct core_reloc_arrays_substruct { @@ -339,6 +340,7 @@ struct core_reloc_arrays { char b[2][3][4]; struct core_reloc_arrays_substruct c[3]; struct core_reloc_arrays_substruct d[1][2]; + struct core_reloc_arrays_substruct f[][2]; }; /* bigger array dimensions */ @@ -347,6 +349,7 @@ struct core_reloc_arrays___diff_arr_dim { char b[3][4][5]; struct core_reloc_arrays_substruct c[4]; struct core_reloc_arrays_substruct d[2][3]; + struct core_reloc_arrays_substruct f[1][3]; }; /* different size of array's value (struct) */ @@ -363,6 +366,29 @@ struct core_reloc_arrays___diff_arr_val_sz { int d; int __padding2; } d[1][2]; + struct { + int __padding1; + int c; + int __padding2; + } f[][2]; +}; + +struct core_reloc_arrays___equiv_zero_sz_arr { + int a[5]; + char b[2][3][4]; + struct core_reloc_arrays_substruct c[3]; + struct core_reloc_arrays_substruct d[1][2]; + /* equivalent to flexible array */ + struct core_reloc_arrays_substruct f[0][2]; +}; + +struct core_reloc_arrays___fixed_arr { + int a[5]; + char b[2][3][4]; + struct core_reloc_arrays_substruct c[3]; + struct core_reloc_arrays_substruct d[1][2]; + /* not a flexible array anymore, but within access bounds */ + struct core_reloc_arrays_substruct f[1][2]; }; struct core_reloc_arrays___err_too_small { @@ -370,6 +396,7 @@ struct core_reloc_arrays___err_too_small { char b[2][3][4]; struct core_reloc_arrays_substruct c[3]; struct core_reloc_arrays_substruct d[1][2]; + struct core_reloc_arrays_substruct f[][2]; }; struct core_reloc_arrays___err_too_shallow { @@ -377,6 +404,7 @@ struct core_reloc_arrays___err_too_shallow { char b[2][3]; /* this one lacks one dimension */ struct core_reloc_arrays_substruct c[3]; struct core_reloc_arrays_substruct d[1][2]; + struct core_reloc_arrays_substruct f[][2]; }; struct core_reloc_arrays___err_non_array { @@ -384,6 +412,7 @@ struct core_reloc_arrays___err_non_array { char b[2][3][4]; struct core_reloc_arrays_substruct c[3]; struct core_reloc_arrays_substruct d[1][2]; + struct core_reloc_arrays_substruct f[][2]; }; struct core_reloc_arrays___err_wrong_val_type { @@ -391,6 +420,16 @@ struct core_reloc_arrays___err_wrong_val_type { char b[2][3][4]; int c[3]; /* value is not a struct */ struct core_reloc_arrays_substruct d[1][2]; + struct core_reloc_arrays_substruct f[][2]; +}; + +struct core_reloc_arrays___err_bad_zero_sz_arr { + /* zero-sized array, but not at the end */ + struct core_reloc_arrays_substruct f[0][2]; + int a[5]; + char b[2][3][4]; + struct core_reloc_arrays_substruct c[3]; + struct core_reloc_arrays_substruct d[1][2]; }; /* diff --git a/tools/testing/selftests/bpf/progs/test_core_reloc_arrays.c b/tools/testing/selftests/bpf/progs/test_core_reloc_arrays.c index 89951b684282d..053b86f6b53f4 100644 --- a/tools/testing/selftests/bpf/progs/test_core_reloc_arrays.c +++ b/tools/testing/selftests/bpf/progs/test_core_reloc_arrays.c @@ -18,6 +18,7 @@ struct core_reloc_arrays_output { char b123; int c1c; int d00d; + int f01c; }; struct core_reloc_arrays_substruct { @@ -30,6 +31,7 @@ struct core_reloc_arrays { char b[2][3][4]; struct core_reloc_arrays_substruct c[3]; struct core_reloc_arrays_substruct d[1][2]; + struct core_reloc_arrays_substruct f[][2]; }; #define CORE_READ(dst, src) bpf_core_read(dst, sizeof(*(dst)), src) @@ -40,18 +42,16 @@ int test_core_arrays(void *ctx) struct core_reloc_arrays *in = (void *)&data.in; struct core_reloc_arrays_output *out = (void *)&data.out; - /* in->a[2] */ if (CORE_READ(&out->a2, &in->a[2])) return 1; - /* in->b[1][2][3] */ if (CORE_READ(&out->b123, &in->b[1][2][3])) return 1; - /* in->c[1].c */ if (CORE_READ(&out->c1c, &in->c[1].c)) return 1; - /* in->d[0][0].d */ if (CORE_READ(&out->d00d, &in->d[0][0].d)) return 1; + if (CORE_READ(&out->f01c, &in->f[0][1].c)) + return 1; return 0; } -- GitLab From 69000d82eee1b41d88afc75861153b7e514bb777 Mon Sep 17 00:00:00 2001 From: Phong Tran Date: Fri, 13 Dec 2019 22:33:07 +0700 Subject: [PATCH 0707/1437] ext4: use RCU API in debug_print_tree struct ext4_sb_info.system_blks was marked __rcu. But access the pointer without using RCU lock and dereference. Sparse warning with __rcu notation: block_validity.c:139:29: warning: incorrect type in argument 1 (different address spaces) block_validity.c:139:29: expected struct rb_root const * block_validity.c:139:29: got struct rb_root [noderef] * Link: https://lore.kernel.org/r/20191213153306.30744-1-tranmanphong@gmail.com Reviewed-by: Jan Kara Signed-off-by: Phong Tran Signed-off-by: Theodore Ts'o --- fs/ext4/block_validity.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/fs/ext4/block_validity.c b/fs/ext4/block_validity.c index d4d4fdfac1a65..1ee04e76bbe04 100644 --- a/fs/ext4/block_validity.c +++ b/fs/ext4/block_validity.c @@ -133,10 +133,13 @@ static void debug_print_tree(struct ext4_sb_info *sbi) { struct rb_node *node; struct ext4_system_zone *entry; + struct ext4_system_blocks *system_blks; int first = 1; printk(KERN_INFO "System zones: "); - node = rb_first(&sbi->system_blks->root); + rcu_read_lock(); + system_blks = rcu_dereference(sbi->system_blks); + node = rb_first(&system_blks->root); while (node) { entry = rb_entry(node, struct ext4_system_zone, node); printk(KERN_CONT "%s%llu-%llu", first ? "" : ", ", @@ -144,6 +147,7 @@ static void debug_print_tree(struct ext4_sb_info *sbi) first = 0; node = rb_next(node); } + rcu_read_unlock(); printk(KERN_CONT "\n"); } -- GitLab From d82d47d5d8a6d40bb622cef31b6c6fa09cbd01ea Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Sun, 8 Dec 2019 20:31:32 -0800 Subject: [PATCH 0708/1437] jbd2: fix kernel-doc notation warning Fix kernel-doc warning by inserting a beginning '*' character for the kernel-doc line. ../include/linux/jbd2.h:461: warning: bad line: journal. These are dirty buffers and revoke descriptor blocks. Link: https://lore.kernel.org/r/53e3ce27-ceae-560d-0fd4-f95728a33e12@infradead.org Cc: stable@kernel.org Signed-off-by: Randy Dunlap Signed-off-by: Theodore Ts'o --- include/linux/jbd2.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/linux/jbd2.h b/include/linux/jbd2.h index 842b62606025a..89bf48a817985 100644 --- a/include/linux/jbd2.h +++ b/include/linux/jbd2.h @@ -457,7 +457,7 @@ struct jbd2_revoke_table_s; * @h_journal: Which journal handle belongs to - used iff h_reserved set. * @h_rsv_handle: Handle reserved for finishing the logical operation. * @h_total_credits: Number of remaining buffers we are allowed to add to - journal. These are dirty buffers and revoke descriptor blocks. + * journal. These are dirty buffers and revoke descriptor blocks. * @h_revoke_credits: Number of remaining revoke records available for handle * @h_ref: Reference count on this handle. * @h_err: Field for caller's use to track errors through large fs operations. -- GitLab From 59034b9d6af8ccc1de0a265ca90b3aa967bae19f Mon Sep 17 00:00:00 2001 From: "H. Nikolaus Schaller" Date: Sun, 15 Dec 2019 08:49:02 +0100 Subject: [PATCH 0709/1437] ARM: bcm: Add missing sentinel to bcm2711_compat[] commit 781fa0a95424 ("ARM: bcm: Add support for BCM2711 SoC") breaks boot of many other platforms (e.g. OMAP or i.MX6) if CONFIG_ARCH_BCM2835 is enabled in addition to some multiplatform config (e.g. omap2plus_defconfig). The symptom is that the OMAP based board does not show any activity beyond "Starting Kernel ..." even with earlycon. Reverting the mentioned commit makes it work again. The real fix is to add the missing NULL sentinel to the bcm2711_compat[] variable-length array. Fixes: 781fa0a95424 ("ARM: bcm: Add support for BCM2711 SoC") Acked-by: Stefan Wahren Signed-off-by: H. Nikolaus Schaller Signed-off-by: Florian Fainelli --- arch/arm/mach-bcm/bcm2711.c | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm/mach-bcm/bcm2711.c b/arch/arm/mach-bcm/bcm2711.c index dbe296798647f..fa0300d8c79da 100644 --- a/arch/arm/mach-bcm/bcm2711.c +++ b/arch/arm/mach-bcm/bcm2711.c @@ -13,6 +13,7 @@ static const char * const bcm2711_compat[] = { #ifdef CONFIG_ARCH_MULTI_V7 "brcm,bcm2711", #endif + NULL }; DT_MACHINE_START(BCM2711, "BCM2711") -- GitLab From 0b416c3e1345fd696db4c422643468d844410877 Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Sun, 15 Dec 2019 10:57:46 -0700 Subject: [PATCH 0710/1437] io_uring: fix sporadic -EFAULT from IORING_OP_RECVMSG MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If we have to punt the recvmsg to async context, we copy all the context. But since the iovec used can be either on-stack (if small) or dynamically allocated, if it's on-stack, then we need to ensure we reset the iov pointer. If we don't, then we're reusing old stack data, and that can lead to -EFAULTs if things get overwritten. Ensure we retain the right pointers for the iov, and free it as well if we end up having to go beyond UIO_FASTIOV number of vectors. Fixes: 03b1230ca12a ("io_uring: ensure async punted sendmsg/recvmsg requests copy data") Reported-by: 李通洲 Signed-off-by: Jens Axboe --- fs/io_uring.c | 40 ++++++++++++++++++++++++++-------------- 1 file changed, 26 insertions(+), 14 deletions(-) diff --git a/fs/io_uring.c b/fs/io_uring.c index 04cff3870b3bf..0e01cdc8a120a 100644 --- a/fs/io_uring.c +++ b/fs/io_uring.c @@ -2041,6 +2041,7 @@ static int io_sendmsg(struct io_kiocb *req, const struct io_uring_sqe *sqe, struct io_kiocb **nxt, bool force_nonblock) { #if defined(CONFIG_NET) + struct io_async_msghdr *kmsg = NULL; struct socket *sock; int ret; @@ -2051,7 +2052,6 @@ static int io_sendmsg(struct io_kiocb *req, const struct io_uring_sqe *sqe, if (sock) { struct io_async_ctx io, *copy; struct sockaddr_storage addr; - struct msghdr *kmsg; unsigned flags; flags = READ_ONCE(sqe->msg_flags); @@ -2061,17 +2061,21 @@ static int io_sendmsg(struct io_kiocb *req, const struct io_uring_sqe *sqe, flags |= MSG_DONTWAIT; if (req->io) { - kmsg = &req->io->msg.msg; - kmsg->msg_name = &addr; + kmsg = &req->io->msg; + kmsg->msg.msg_name = &addr; + /* if iov is set, it's allocated already */ + if (!kmsg->iov) + kmsg->iov = kmsg->fast_iov; + kmsg->msg.msg_iter.iov = kmsg->iov; } else { - kmsg = &io.msg.msg; - kmsg->msg_name = &addr; + kmsg = &io.msg; + kmsg->msg.msg_name = &addr; ret = io_sendmsg_prep(req, &io); if (ret) goto out; } - ret = __sys_sendmsg_sock(sock, kmsg, flags); + ret = __sys_sendmsg_sock(sock, &kmsg->msg, flags); if (force_nonblock && ret == -EAGAIN) { copy = kmalloc(sizeof(*copy), GFP_KERNEL); if (!copy) { @@ -2082,13 +2086,15 @@ static int io_sendmsg(struct io_kiocb *req, const struct io_uring_sqe *sqe, req->io = copy; memcpy(&req->io->sqe, req->sqe, sizeof(*req->sqe)); req->sqe = &req->io->sqe; - return ret; + return -EAGAIN; } if (ret == -ERESTARTSYS) ret = -EINTR; } out: + if (kmsg && kmsg->iov != kmsg->fast_iov) + kfree(kmsg->iov); io_cqring_add_event(req, ret); if (ret < 0) req_set_fail_links(req); @@ -2120,6 +2126,7 @@ static int io_recvmsg(struct io_kiocb *req, const struct io_uring_sqe *sqe, struct io_kiocb **nxt, bool force_nonblock) { #if defined(CONFIG_NET) + struct io_async_msghdr *kmsg = NULL; struct socket *sock; int ret; @@ -2131,7 +2138,6 @@ static int io_recvmsg(struct io_kiocb *req, const struct io_uring_sqe *sqe, struct user_msghdr __user *msg; struct io_async_ctx io, *copy; struct sockaddr_storage addr; - struct msghdr *kmsg; unsigned flags; flags = READ_ONCE(sqe->msg_flags); @@ -2143,17 +2149,21 @@ static int io_recvmsg(struct io_kiocb *req, const struct io_uring_sqe *sqe, msg = (struct user_msghdr __user *) (unsigned long) READ_ONCE(sqe->addr); if (req->io) { - kmsg = &req->io->msg.msg; - kmsg->msg_name = &addr; + kmsg = &req->io->msg; + kmsg->msg.msg_name = &addr; + /* if iov is set, it's allocated already */ + if (!kmsg->iov) + kmsg->iov = kmsg->fast_iov; + kmsg->msg.msg_iter.iov = kmsg->iov; } else { - kmsg = &io.msg.msg; - kmsg->msg_name = &addr; + kmsg = &io.msg; + kmsg->msg.msg_name = &addr; ret = io_recvmsg_prep(req, &io); if (ret) goto out; } - ret = __sys_recvmsg_sock(sock, kmsg, msg, io.msg.uaddr, flags); + ret = __sys_recvmsg_sock(sock, &kmsg->msg, msg, kmsg->uaddr, flags); if (force_nonblock && ret == -EAGAIN) { copy = kmalloc(sizeof(*copy), GFP_KERNEL); if (!copy) { @@ -2164,13 +2174,15 @@ static int io_recvmsg(struct io_kiocb *req, const struct io_uring_sqe *sqe, req->io = copy; memcpy(&req->io->sqe, req->sqe, sizeof(*req->sqe)); req->sqe = &req->io->sqe; - return ret; + return -EAGAIN; } if (ret == -ERESTARTSYS) ret = -EINTR; } out: + if (kmsg && kmsg->iov != kmsg->fast_iov) + kfree(kmsg->iov); io_cqring_add_event(req, ret); if (ret < 0) req_set_fail_links(req); -- GitLab From 6fc232db9e8cd50b9b83534de9cd91ace711b2d7 Mon Sep 17 00:00:00 2001 From: Aditya Pakki Date: Sun, 15 Dec 2019 09:34:08 -0600 Subject: [PATCH 0711/1437] rfkill: Fix incorrect check to avoid NULL pointer dereference In rfkill_register, the struct rfkill pointer is first derefernced and then checked for NULL. This patch removes the BUG_ON and returns an error to the caller in case rfkill is NULL. Signed-off-by: Aditya Pakki Link: https://lore.kernel.org/r/20191215153409.21696-1-pakki001@umn.edu Signed-off-by: Johannes Berg --- net/rfkill/core.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/net/rfkill/core.c b/net/rfkill/core.c index 461d75274fb3c..971c73c7d34cb 100644 --- a/net/rfkill/core.c +++ b/net/rfkill/core.c @@ -1002,10 +1002,13 @@ static void rfkill_sync_work(struct work_struct *work) int __must_check rfkill_register(struct rfkill *rfkill) { static unsigned long rfkill_no; - struct device *dev = &rfkill->dev; + struct device *dev; int error; - BUG_ON(!rfkill); + if (!rfkill) + return -EINVAL; + + dev = &rfkill->dev; mutex_lock(&rfkill_global_mutex); -- GitLab From fbc44a0b34a10e6cbe86b41617aaff25292d9a4c Mon Sep 17 00:00:00 2001 From: Animesh Manna Date: Thu, 5 Dec 2019 18:05:13 +0530 Subject: [PATCH 0712/1437] drm/i915/dsb: Fix in mmio offset calculation of DSB instance As the current usage is restricted to first DSB instance per pipe, so existing code could not catch the issue to calculate the mmio offset of different DSB instance per pipe. Corrected the offset calculation. Fixes: a6e58d9a2e04 ("drm/i915/dsb: Check DSB engine status.") Signed-off-by: Animesh Manna Reviewed-by: Anshuman Gupta Signed-off-by: Jani Nikula Link: https://patchwork.freedesktop.org/patch/msgid/20191205123513.22603-1-animesh.manna@intel.com (cherry picked from commit d04a661a2c7169b48782aa5e9d85d4b4383d562e) Signed-off-by: Joonas Lahtinen --- drivers/gpu/drm/i915/i915_reg.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index 73079b503724d..9599b9955aa90 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -11994,7 +11994,7 @@ enum skl_power_gate { /* This register controls the Display State Buffer (DSB) engines. */ #define _DSBSL_INSTANCE_BASE 0x70B00 #define DSBSL_INSTANCE(pipe, id) (_DSBSL_INSTANCE_BASE + \ - (pipe) * 0x1000 + (id) * 100) + (pipe) * 0x1000 + (id) * 0x100) #define DSB_HEAD(pipe, id) _MMIO(DSBSL_INSTANCE(pipe, id) + 0x0) #define DSB_TAIL(pipe, id) _MMIO(DSBSL_INSTANCE(pipe, id) + 0x4) #define DSB_CTRL(pipe, id) _MMIO(DSBSL_INSTANCE(pipe, id) + 0x8) -- GitLab From 99de95360faa769d970059c5b16abf85c53d7b31 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Tue, 10 Dec 2019 15:13:32 +0000 Subject: [PATCH 0713/1437] drm/i915: Copy across scheduler behaviour flags across submit fences We want the bonded request to have the same scheduler properties as its master so that it is placed at the same depth in the queue. For example, consider we have requests A, B and B', where B & B' are a bonded pair to run in parallel on two engines. A -> B \- B' B will run after A and so may be scheduled on an idle engine and wait on A using a semaphore. B' sees B being executed and so enters the queue on the same engine as A. As B' did not inherit the semaphore-chain from B, it may have higher precedence than A and so preempts execution. However, B' then sits on a semaphore waiting for B, who is waiting for A, who is blocked by B. Ergo B' needs to inherit the scheduler properties from B (i.e. the semaphore chain) so that it is scheduled with the same priority as B and will not be executed ahead of Bs dependencies. Furthermore, to prevent the priorities changing via the expose fence on B', we need to couple in the dependencies for PI. This requires us to relax our sanity-checks that dependencies are strictly in order. v2: Synchronise (B, B') execution on all platforms, regardless of using a scheduler, any no-op syncs should be elided. Fixes: ee1136908e9b ("drm/i915/execlists: Virtual engine bonding") Closes: https://gitlab.freedesktop.org/drm/intel/issues/464 Testcase: igt/gem_exec_balancer/bonded-chain Testcase: igt/gem_exec_balancer/bonded-semaphore Signed-off-by: Chris Wilson Cc: Tvrtko Ursulin Reviewed-by: Tvrtko Ursulin Link: https://patchwork.freedesktop.org/patch/msgid/20191210151332.3902215-1-chris@chris-wilson.co.uk (cherry picked from commit c81471f5e95c79c55687282ff6800f112b5d560b) Signed-off-by: Joonas Lahtinen --- drivers/gpu/drm/i915/i915_request.c | 114 ++++++++++++++++++++------ drivers/gpu/drm/i915/i915_scheduler.c | 1 - 2 files changed, 89 insertions(+), 26 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_request.c b/drivers/gpu/drm/i915/i915_request.c index bbd71af00a913..765bec89fc0de 100644 --- a/drivers/gpu/drm/i915/i915_request.c +++ b/drivers/gpu/drm/i915/i915_request.c @@ -300,11 +300,11 @@ void i915_request_retire_upto(struct i915_request *rq) } static int -__i915_request_await_execution(struct i915_request *rq, - struct i915_request *signal, - void (*hook)(struct i915_request *rq, - struct dma_fence *signal), - gfp_t gfp) +__await_execution(struct i915_request *rq, + struct i915_request *signal, + void (*hook)(struct i915_request *rq, + struct dma_fence *signal), + gfp_t gfp) { struct execute_cb *cb; @@ -341,6 +341,8 @@ __i915_request_await_execution(struct i915_request *rq, } spin_unlock_irq(&signal->lock); + /* Copy across semaphore status as we need the same behaviour */ + rq->sched.flags |= signal->sched.flags; return 0; } @@ -811,31 +813,21 @@ already_busywaiting(struct i915_request *rq) } static int -emit_semaphore_wait(struct i915_request *to, - struct i915_request *from, - gfp_t gfp) +__emit_semaphore_wait(struct i915_request *to, + struct i915_request *from, + u32 seqno) { const int has_token = INTEL_GEN(to->i915) >= 12; u32 hwsp_offset; - int len; + int len, err; u32 *cs; GEM_BUG_ON(INTEL_GEN(to->i915) < 8); - /* Just emit the first semaphore we see as request space is limited. */ - if (already_busywaiting(to) & from->engine->mask) - goto await_fence; - - if (i915_request_await_start(to, from) < 0) - goto await_fence; - - /* Only submit our spinner after the signaler is running! */ - if (__i915_request_await_execution(to, from, NULL, gfp)) - goto await_fence; - /* We need to pin the signaler's HWSP until we are finished reading. */ - if (intel_timeline_read_hwsp(from, to, &hwsp_offset)) - goto await_fence; + err = intel_timeline_read_hwsp(from, to, &hwsp_offset); + if (err) + return err; len = 4; if (has_token) @@ -858,7 +850,7 @@ emit_semaphore_wait(struct i915_request *to, MI_SEMAPHORE_POLL | MI_SEMAPHORE_SAD_GTE_SDD) + has_token; - *cs++ = from->fence.seqno; + *cs++ = seqno; *cs++ = hwsp_offset; *cs++ = 0; if (has_token) { @@ -867,6 +859,28 @@ emit_semaphore_wait(struct i915_request *to, } intel_ring_advance(to, cs); + return 0; +} + +static int +emit_semaphore_wait(struct i915_request *to, + struct i915_request *from, + gfp_t gfp) +{ + /* Just emit the first semaphore we see as request space is limited. */ + if (already_busywaiting(to) & from->engine->mask) + goto await_fence; + + if (i915_request_await_start(to, from) < 0) + goto await_fence; + + /* Only submit our spinner after the signaler is running! */ + if (__await_execution(to, from, NULL, gfp)) + goto await_fence; + + if (__emit_semaphore_wait(to, from, from->fence.seqno)) + goto await_fence; + to->sched.semaphores |= from->engine->mask; to->sched.flags |= I915_SCHED_HAS_SEMAPHORE_CHAIN; return 0; @@ -980,6 +994,57 @@ i915_request_await_dma_fence(struct i915_request *rq, struct dma_fence *fence) return 0; } +static bool intel_timeline_sync_has_start(struct intel_timeline *tl, + struct dma_fence *fence) +{ + return __intel_timeline_sync_is_later(tl, + fence->context, + fence->seqno - 1); +} + +static int intel_timeline_sync_set_start(struct intel_timeline *tl, + const struct dma_fence *fence) +{ + return __intel_timeline_sync_set(tl, fence->context, fence->seqno - 1); +} + +static int +__i915_request_await_execution(struct i915_request *to, + struct i915_request *from, + void (*hook)(struct i915_request *rq, + struct dma_fence *signal)) +{ + int err; + + /* Submit both requests at the same time */ + err = __await_execution(to, from, hook, I915_FENCE_GFP); + if (err) + return err; + + /* Squash repeated depenendices to the same timelines */ + if (intel_timeline_sync_has_start(i915_request_timeline(to), + &from->fence)) + return 0; + + /* Ensure both start together [after all semaphores in signal] */ + if (intel_engine_has_semaphores(to->engine)) + err = __emit_semaphore_wait(to, from, from->fence.seqno - 1); + else + err = i915_request_await_start(to, from); + if (err < 0) + return err; + + /* Couple the dependency tree for PI on this exposed to->fence */ + if (to->engine->schedule) { + err = i915_sched_node_add_dependency(&to->sched, &from->sched); + if (err < 0) + return err; + } + + return intel_timeline_sync_set_start(i915_request_timeline(to), + &from->fence); +} + int i915_request_await_execution(struct i915_request *rq, struct dma_fence *fence, @@ -1013,8 +1078,7 @@ i915_request_await_execution(struct i915_request *rq, if (dma_fence_is_i915(fence)) ret = __i915_request_await_execution(rq, to_request(fence), - hook, - I915_FENCE_GFP); + hook); else ret = i915_sw_fence_await_dma_fence(&rq->submit, fence, I915_FENCE_TIMEOUT, diff --git a/drivers/gpu/drm/i915/i915_scheduler.c b/drivers/gpu/drm/i915/i915_scheduler.c index 010d67f48ad94..247a9671bca53 100644 --- a/drivers/gpu/drm/i915/i915_scheduler.c +++ b/drivers/gpu/drm/i915/i915_scheduler.c @@ -474,7 +474,6 @@ void i915_sched_node_fini(struct i915_sched_node *node) * so we may be called out-of-order. */ list_for_each_entry_safe(dep, tmp, &node->signalers_list, signal_link) { - GEM_BUG_ON(!node_signaled(dep->signaler)); GEM_BUG_ON(!list_empty(&dep->dfs_link)); list_del(&dep->wait_link); -- GitLab From 242bff7fc515d8e5275e5b8cd8c9c85a8d037dbf Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Thu, 12 Dec 2019 15:42:24 +0000 Subject: [PATCH 0714/1437] drm/i915: Set fence_work.ops before dma_fence_init Since dma_fence_init may call ops (because of a meaningless trace_dma_fence), we need to set the worker ops prior to that call. Reported-by: Vince Weaver Fixes: 8e458fe2ee05 ("drm/i915: Generalise the clflush dma-worker") Signed-off-by: Chris Wilson Cc: Matthew Auld Cc: Vince Weaver Reviewed-by: Matthew Auld Link: https://patchwork.freedesktop.org/patch/msgid/20191212154224.1631531-1-chris@chris-wilson.co.uk (cherry picked from commit 1bc9865d8df3e1c6857a27c4b2b8a9ce8f027349) Signed-off-by: Joonas Lahtinen --- drivers/gpu/drm/i915/i915_sw_fence_work.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_sw_fence_work.c b/drivers/gpu/drm/i915/i915_sw_fence_work.c index 07552cd544f21..8538ee7a521de 100644 --- a/drivers/gpu/drm/i915/i915_sw_fence_work.c +++ b/drivers/gpu/drm/i915/i915_sw_fence_work.c @@ -78,12 +78,11 @@ static const struct dma_fence_ops fence_ops = { void dma_fence_work_init(struct dma_fence_work *f, const struct dma_fence_work_ops *ops) { + f->ops = ops; spin_lock_init(&f->lock); dma_fence_init(&f->dma, &fence_ops, &f->lock, 0, 0); i915_sw_fence_init(&f->chain, fence_notify); INIT_WORK(&f->work, fence_work); - - f->ops = ops; } int dma_fence_work_chain(struct dma_fence_work *f, struct dma_fence *signal) -- GitLab From 2eeab8eb1e3a04a644cbcb8c568c26c66ea12f02 Mon Sep 17 00:00:00 2001 From: Matt Roper Date: Thu, 12 Dec 2019 16:15:09 -0800 Subject: [PATCH 0715/1437] drm/i915/ehl: Define EHL powerwells independently of ICL Outputs C and D on EHL are combo PHY outputs and thus should not be using the same TC AUX power well handlers as ICL. And even though icl_combo_phy_aux_power_well_ops works okay for EHL/JSL combo PHYs none of its special handling is actually necessary for this platform: * EHL/JSL don't actually need to program PORT_CL_DW12 * Display WA #1178 does not apply to EHL/JSL Thus we can simply drop back to using our standard "hsw-style" power well ops for EHL AUX power wells. Bspec: 4301 Fixes: f722b8c1e2a2 ("drm/i915/ehl: All EHL ports are combo phys") Cc: Jose Souza Cc: Bob Paauwe Cc: Vivek Kasireddy Cc: Lucas De Marchi Signed-off-by: Matt Roper Link: https://patchwork.freedesktop.org/patch/msgid/20191213001511.678070-2-matthew.d.roper@intel.com Reviewed-by: Lucas De Marchi (cherry picked from commit e8ab8d669d046a8e9b07707d2f00b9ba3e25d0ae) Signed-off-by: Joonas Lahtinen --- .../drm/i915/display/intel_display_power.c | 147 ++++++++++++++++++ 1 file changed, 147 insertions(+) diff --git a/drivers/gpu/drm/i915/display/intel_display_power.c b/drivers/gpu/drm/i915/display/intel_display_power.c index ce1b64f4dd44a..fc1683bb2e528 100644 --- a/drivers/gpu/drm/i915/display/intel_display_power.c +++ b/drivers/gpu/drm/i915/display/intel_display_power.c @@ -3688,6 +3688,151 @@ static const struct i915_power_well_desc icl_power_wells[] = { }, }; +static const struct i915_power_well_desc ehl_power_wells[] = { + { + .name = "always-on", + .always_on = true, + .domains = POWER_DOMAIN_MASK, + .ops = &i9xx_always_on_power_well_ops, + .id = DISP_PW_ID_NONE, + }, + { + .name = "power well 1", + /* Handled by the DMC firmware */ + .always_on = true, + .domains = 0, + .ops = &hsw_power_well_ops, + .id = SKL_DISP_PW_1, + { + .hsw.regs = &hsw_power_well_regs, + .hsw.idx = ICL_PW_CTL_IDX_PW_1, + .hsw.has_fuses = true, + }, + }, + { + .name = "DC off", + .domains = ICL_DISPLAY_DC_OFF_POWER_DOMAINS, + .ops = &gen9_dc_off_power_well_ops, + .id = SKL_DISP_DC_OFF, + }, + { + .name = "power well 2", + .domains = ICL_PW_2_POWER_DOMAINS, + .ops = &hsw_power_well_ops, + .id = SKL_DISP_PW_2, + { + .hsw.regs = &hsw_power_well_regs, + .hsw.idx = ICL_PW_CTL_IDX_PW_2, + .hsw.has_fuses = true, + }, + }, + { + .name = "power well 3", + .domains = ICL_PW_3_POWER_DOMAINS, + .ops = &hsw_power_well_ops, + .id = DISP_PW_ID_NONE, + { + .hsw.regs = &hsw_power_well_regs, + .hsw.idx = ICL_PW_CTL_IDX_PW_3, + .hsw.irq_pipe_mask = BIT(PIPE_B), + .hsw.has_vga = true, + .hsw.has_fuses = true, + }, + }, + { + .name = "DDI A IO", + .domains = ICL_DDI_IO_A_POWER_DOMAINS, + .ops = &hsw_power_well_ops, + .id = DISP_PW_ID_NONE, + { + .hsw.regs = &icl_ddi_power_well_regs, + .hsw.idx = ICL_PW_CTL_IDX_DDI_A, + }, + }, + { + .name = "DDI B IO", + .domains = ICL_DDI_IO_B_POWER_DOMAINS, + .ops = &hsw_power_well_ops, + .id = DISP_PW_ID_NONE, + { + .hsw.regs = &icl_ddi_power_well_regs, + .hsw.idx = ICL_PW_CTL_IDX_DDI_B, + }, + }, + { + .name = "DDI C IO", + .domains = ICL_DDI_IO_C_POWER_DOMAINS, + .ops = &hsw_power_well_ops, + .id = DISP_PW_ID_NONE, + { + .hsw.regs = &icl_ddi_power_well_regs, + .hsw.idx = ICL_PW_CTL_IDX_DDI_C, + }, + }, + { + .name = "DDI D IO", + .domains = ICL_DDI_IO_D_POWER_DOMAINS, + .ops = &hsw_power_well_ops, + .id = DISP_PW_ID_NONE, + { + .hsw.regs = &icl_ddi_power_well_regs, + .hsw.idx = ICL_PW_CTL_IDX_DDI_D, + }, + }, + { + .name = "AUX A", + .domains = ICL_AUX_A_IO_POWER_DOMAINS, + .ops = &hsw_power_well_ops, + .id = DISP_PW_ID_NONE, + { + .hsw.regs = &icl_aux_power_well_regs, + .hsw.idx = ICL_PW_CTL_IDX_AUX_A, + }, + }, + { + .name = "AUX B", + .domains = ICL_AUX_B_IO_POWER_DOMAINS, + .ops = &hsw_power_well_ops, + .id = DISP_PW_ID_NONE, + { + .hsw.regs = &icl_aux_power_well_regs, + .hsw.idx = ICL_PW_CTL_IDX_AUX_B, + }, + }, + { + .name = "AUX C", + .domains = ICL_AUX_C_TC1_IO_POWER_DOMAINS, + .ops = &hsw_power_well_ops, + .id = DISP_PW_ID_NONE, + { + .hsw.regs = &icl_aux_power_well_regs, + .hsw.idx = ICL_PW_CTL_IDX_AUX_C, + }, + }, + { + .name = "AUX D", + .domains = ICL_AUX_D_TC2_IO_POWER_DOMAINS, + .ops = &hsw_power_well_ops, + .id = DISP_PW_ID_NONE, + { + .hsw.regs = &icl_aux_power_well_regs, + .hsw.idx = ICL_PW_CTL_IDX_AUX_D, + }, + }, + { + .name = "power well 4", + .domains = ICL_PW_4_POWER_DOMAINS, + .ops = &hsw_power_well_ops, + .id = DISP_PW_ID_NONE, + { + .hsw.regs = &hsw_power_well_regs, + .hsw.idx = ICL_PW_CTL_IDX_PW_4, + .hsw.has_fuses = true, + .hsw.irq_pipe_mask = BIT(PIPE_C), + }, + }, +}; + static const struct i915_power_well_desc tgl_power_wells[] = { { .name = "always-on", @@ -4162,6 +4307,8 @@ int intel_power_domains_init(struct drm_i915_private *dev_priv) */ if (IS_GEN(dev_priv, 12)) { err = set_power_wells(power_domains, tgl_power_wells); + } else if (IS_ELKHARTLAKE(dev_priv)) { + err = set_power_wells(power_domains, ehl_power_wells); } else if (IS_GEN(dev_priv, 11)) { err = set_power_wells(power_domains, icl_power_wells); } else if (IS_CANNONLAKE(dev_priv)) { -- GitLab From 5665a1febfe7bbb8c166fcf16856ef2730f947c0 Mon Sep 17 00:00:00 2001 From: Matt Roper Date: Thu, 12 Dec 2019 16:15:10 -0800 Subject: [PATCH 0716/1437] drm/i915/tgl: Drop Wa#1178 The TGL workaround database no longer shows Wa #1178 (or anything similar under different workaround names/numbers) so we should be able to drop it. In fact Swati just discovered that applying this workaround is the root cause of some power well enable failures we've been seeing in CI (gitlab issue 498). Once we stop applying this WA, TGL no longer utilizes any of the special handling provided by icl_combo_phy_aux_power_well_ops so we can just drop back to using the standard hsw-style power well ops instead. v3: Drop now-unused _TGL_AUX_ANAOVRD1_C definition too. (Lucas) Closes: https://gitlab.freedesktop.org/drm/intel/issues/498 Fixes: deea06b47574 ("drm/i915/tgl: apply Display WA #1178 to fix type C dongles") Cc: Lucas De Marchi Cc: Swati Sharma Cc: Imre Deak Signed-off-by: Matt Roper Reviewed-by: Lucas De Marchi Link: https://patchwork.freedesktop.org/patch/msgid/20191213001511.678070-3-matthew.d.roper@intel.com (cherry picked from commit ab3402581d0fa58e63e36875995f43ab02b4d0a0) Signed-off-by: Joonas Lahtinen --- drivers/gpu/drm/i915/display/intel_display_power.c | 6 +++--- drivers/gpu/drm/i915/i915_reg.h | 4 +--- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_display_power.c b/drivers/gpu/drm/i915/display/intel_display_power.c index fc1683bb2e528..12ba74788ccec 100644 --- a/drivers/gpu/drm/i915/display/intel_display_power.c +++ b/drivers/gpu/drm/i915/display/intel_display_power.c @@ -3977,7 +3977,7 @@ static const struct i915_power_well_desc tgl_power_wells[] = { { .name = "AUX A", .domains = TGL_AUX_A_IO_POWER_DOMAINS, - .ops = &icl_combo_phy_aux_power_well_ops, + .ops = &hsw_power_well_ops, .id = DISP_PW_ID_NONE, { .hsw.regs = &icl_aux_power_well_regs, @@ -3987,7 +3987,7 @@ static const struct i915_power_well_desc tgl_power_wells[] = { { .name = "AUX B", .domains = TGL_AUX_B_IO_POWER_DOMAINS, - .ops = &icl_combo_phy_aux_power_well_ops, + .ops = &hsw_power_well_ops, .id = DISP_PW_ID_NONE, { .hsw.regs = &icl_aux_power_well_regs, @@ -3997,7 +3997,7 @@ static const struct i915_power_well_desc tgl_power_wells[] = { { .name = "AUX C", .domains = TGL_AUX_C_IO_POWER_DOMAINS, - .ops = &icl_combo_phy_aux_power_well_ops, + .ops = &hsw_power_well_ops, .id = DISP_PW_ID_NONE, { .hsw.regs = &icl_aux_power_well_regs, diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index 9599b9955aa90..4fd3d76db346c 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -9405,11 +9405,9 @@ enum skl_power_gate { #define _ICL_AUX_REG_IDX(pw_idx) ((pw_idx) - ICL_PW_CTL_IDX_AUX_A) #define _ICL_AUX_ANAOVRD1_A 0x162398 #define _ICL_AUX_ANAOVRD1_B 0x6C398 -#define _TGL_AUX_ANAOVRD1_C 0x160398 #define ICL_AUX_ANAOVRD1(pw_idx) _MMIO(_PICK(_ICL_AUX_REG_IDX(pw_idx), \ _ICL_AUX_ANAOVRD1_A, \ - _ICL_AUX_ANAOVRD1_B, \ - _TGL_AUX_ANAOVRD1_C)) + _ICL_AUX_ANAOVRD1_B)) #define ICL_AUX_ANAOVRD1_LDO_BYPASS (1 << 7) #define ICL_AUX_ANAOVRD1_ENABLE (1 << 0) -- GitLab From 286e7beaa4cc7734894ee214569de4669ed9891e Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Fri, 13 Dec 2019 14:00:34 +0100 Subject: [PATCH 0717/1437] gpio: tegra186: Allow building on Tegra194-only configurations The driver is compatible with both Tegra186 and Tegra194, but currently it cannot be selected if only Tegra194 support is enabled. Allow builds with only Tegra194 support enabled to select this driver. While at it, select this driver by default on Tegra194 builds because it is an essential part of the system. Signed-off-by: Thierry Reding Link: https://lore.kernel.org/r/20191213130034.219227-1-thierry.reding@gmail.com Signed-off-by: Linus Walleij --- drivers/gpio/Kconfig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 8adffd42f8cb0..6ab25fe1c4237 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -553,8 +553,8 @@ config GPIO_TEGRA config GPIO_TEGRA186 tristate "NVIDIA Tegra186 GPIO support" - default ARCH_TEGRA_186_SOC - depends on ARCH_TEGRA_186_SOC || COMPILE_TEST + default ARCH_TEGRA_186_SOC || ARCH_TEGRA_194_SOC + depends on ARCH_TEGRA_186_SOC || ARCH_TEGRA_194_SOC || COMPILE_TEST depends on OF_GPIO select GPIOLIB_IRQCHIP select IRQ_DOMAIN_HIERARCHY -- GitLab From 9e65527ac3bab5480529d1ad07d4d228cc0295cd Mon Sep 17 00:00:00 2001 From: Paul Cercueil Date: Tue, 10 Dec 2019 17:44:46 +0100 Subject: [PATCH 0718/1437] pinctrl: ingenic: Fixup PIN_CONFIG_OUTPUT config JZ4760 support was added in parallel of the previous patch so this one slipped through. The first SoC to use the new register is the JZ4760 and not the JZ4770, fix it here. Fixes: 7009d046a601 ("pinctrl: ingenic: Handle PIN_CONFIG_OUTPUT config") Signed-off-by: Paul Cercueil Link: https://lore.kernel.org/r/20191210164446.53912-1-paul@crapouillou.net [Folded into OF dependency] Signed-off-by: Linus Walleij --- drivers/pinctrl/Kconfig | 2 +- drivers/pinctrl/pinctrl-ingenic.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig index ba0cad4bd0720..df0ef69dd4748 100644 --- a/drivers/pinctrl/Kconfig +++ b/drivers/pinctrl/Kconfig @@ -422,7 +422,7 @@ config PINCTRL_TB10X config PINCTRL_EQUILIBRIUM tristate "Generic pinctrl and GPIO driver for Intel Lightning Mountain SoC" - depends on OF + depends on OF && HAS_IOMEM select PINMUX select PINCONF select GPIOLIB diff --git a/drivers/pinctrl/pinctrl-ingenic.c b/drivers/pinctrl/pinctrl-ingenic.c index 24e0e2ef47a4d..369e04350e3de 100644 --- a/drivers/pinctrl/pinctrl-ingenic.c +++ b/drivers/pinctrl/pinctrl-ingenic.c @@ -1809,7 +1809,7 @@ static void ingenic_set_bias(struct ingenic_pinctrl *jzpc, static void ingenic_set_output_level(struct ingenic_pinctrl *jzpc, unsigned int pin, bool high) { - if (jzpc->version >= ID_JZ4770) + if (jzpc->version >= ID_JZ4760) ingenic_config_pin(jzpc, pin, JZ4760_GPIO_PAT0, high); else ingenic_config_pin(jzpc, pin, JZ4740_GPIO_DATA, high); -- GitLab From 07bcc411567cb96f9d1fc84fff8d387118a2920d Mon Sep 17 00:00:00 2001 From: Faiz Abbas Date: Thu, 28 Nov 2019 16:34:22 +0530 Subject: [PATCH 0719/1437] Revert "mmc: sdhci: Fix incorrect switch to HS mode" This reverts commit c894e33ddc1910e14d6f2a2016f60ab613fd8b37. This commit aims to treat SD High speed and SDR25 as the same while setting UHS Timings in HOST_CONTROL2 which leads to failures with some SD cards in AM65x. Revert this commit. The issue this commit was trying to fix can be implemented in a platform specific callback instead of common sdhci code. Cc: Signed-off-by: Faiz Abbas Acked-by: Adrian Hunter Link: https://lore.kernel.org/r/20191128110422.25917-1-faiz_abbas@ti.com Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 3140fe2e5dba8..296d955ede595 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -1882,9 +1882,7 @@ void sdhci_set_uhs_signaling(struct sdhci_host *host, unsigned timing) ctrl_2 |= SDHCI_CTRL_UHS_SDR104; else if (timing == MMC_TIMING_UHS_SDR12) ctrl_2 |= SDHCI_CTRL_UHS_SDR12; - else if (timing == MMC_TIMING_SD_HS || - timing == MMC_TIMING_MMC_HS || - timing == MMC_TIMING_UHS_SDR25) + else if (timing == MMC_TIMING_UHS_SDR25) ctrl_2 |= SDHCI_CTRL_UHS_SDR25; else if (timing == MMC_TIMING_UHS_SDR50) ctrl_2 |= SDHCI_CTRL_UHS_SDR50; -- GitLab From fa56ac9792265354b565f28def7164e7d7db2b1e Mon Sep 17 00:00:00 2001 From: Veerabhadrarao Badiganti Date: Tue, 26 Nov 2019 10:19:16 +0000 Subject: [PATCH 0720/1437] mmc: sdhci-msm: Correct the offset and value for DDR_CONFIG register The DDR_CONFIG register offset got updated after a specific minor version of sdcc V4. This offset change has not been properly taken care of while updating register changes for sdcc V5. Correcting proper offset for this register. Also updating this register value to reflect the recommended RCLK delay. Signed-off-by: Veerabhadrarao Badiganti Link: https://lore.kernel.org/r/0101016ea738ec72-fa0f852d-20f8-474a-80b2-4b0ef63b132c-000000@us-west-2.amazonses.com Fixes: f15358885dda ("mmc: sdhci-msm: Define new Register address map") Cc: stable@vger.kernel.org Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-msm.c | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c index b75c82d8d6c17..3d0bb5e2e09b2 100644 --- a/drivers/mmc/host/sdhci-msm.c +++ b/drivers/mmc/host/sdhci-msm.c @@ -99,7 +99,7 @@ #define CORE_PWRSAVE_DLL BIT(3) -#define DDR_CONFIG_POR_VAL 0x80040853 +#define DDR_CONFIG_POR_VAL 0x80040873 #define INVALID_TUNING_PHASE -1 @@ -148,8 +148,9 @@ struct sdhci_msm_offset { u32 core_ddr_200_cfg; u32 core_vendor_spec3; u32 core_dll_config_2; + u32 core_dll_config_3; + u32 core_ddr_config_old; /* Applicable to sdcc minor ver < 0x49 */ u32 core_ddr_config; - u32 core_ddr_config_2; }; static const struct sdhci_msm_offset sdhci_msm_v5_offset = { @@ -177,8 +178,8 @@ static const struct sdhci_msm_offset sdhci_msm_v5_offset = { .core_ddr_200_cfg = 0x224, .core_vendor_spec3 = 0x250, .core_dll_config_2 = 0x254, - .core_ddr_config = 0x258, - .core_ddr_config_2 = 0x25c, + .core_dll_config_3 = 0x258, + .core_ddr_config = 0x25c, }; static const struct sdhci_msm_offset sdhci_msm_mci_offset = { @@ -207,8 +208,8 @@ static const struct sdhci_msm_offset sdhci_msm_mci_offset = { .core_ddr_200_cfg = 0x184, .core_vendor_spec3 = 0x1b0, .core_dll_config_2 = 0x1b4, - .core_ddr_config = 0x1b8, - .core_ddr_config_2 = 0x1bc, + .core_ddr_config_old = 0x1b8, + .core_ddr_config = 0x1bc, }; struct sdhci_msm_variant_ops { @@ -253,6 +254,7 @@ struct sdhci_msm_host { const struct sdhci_msm_offset *offset; bool use_cdr; u32 transfer_mode; + bool updated_ddr_cfg; }; static const struct sdhci_msm_offset *sdhci_priv_msm_offset(struct sdhci_host *host) @@ -924,8 +926,10 @@ out: static int sdhci_msm_cm_dll_sdc4_calibration(struct sdhci_host *host) { struct mmc_host *mmc = host->mmc; - u32 dll_status, config; + u32 dll_status, config, ddr_cfg_offset; int ret; + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host); const struct sdhci_msm_offset *msm_offset = sdhci_priv_msm_offset(host); @@ -938,8 +942,11 @@ static int sdhci_msm_cm_dll_sdc4_calibration(struct sdhci_host *host) * bootloaders. In the future, if this changes, then the desired * values will need to be programmed appropriately. */ - writel_relaxed(DDR_CONFIG_POR_VAL, host->ioaddr + - msm_offset->core_ddr_config); + if (msm_host->updated_ddr_cfg) + ddr_cfg_offset = msm_offset->core_ddr_config; + else + ddr_cfg_offset = msm_offset->core_ddr_config_old; + writel_relaxed(DDR_CONFIG_POR_VAL, host->ioaddr + ddr_cfg_offset); if (mmc->ios.enhanced_strobe) { config = readl_relaxed(host->ioaddr + @@ -1899,6 +1906,9 @@ static int sdhci_msm_probe(struct platform_device *pdev) msm_offset->core_vendor_spec_capabilities0); } + if (core_major == 1 && core_minor >= 0x49) + msm_host->updated_ddr_cfg = true; + /* * Power on reset state may trigger power irq if previous status of * PWRCTL was either BUS_ON or IO_HIGH_V. So before enabling pwr irq -- GitLab From 8f34e5bd7024d1ffebddd82d7318b1be17be9e9a Mon Sep 17 00:00:00 2001 From: Chaotian Jing Date: Wed, 4 Dec 2019 15:19:58 +0800 Subject: [PATCH 0721/1437] mmc: mediatek: fix CMD_TA to 2 for MT8173 HS200/HS400 mode there is a chance that always get response CRC error after HS200 tuning, the reason is that need set CMD_TA to 2. this modification is only for MT8173. Signed-off-by: Chaotian Jing Tested-by: Hsin-Yi Wang Cc: stable@vger.kernel.org Fixes: 1ede5cb88a29 ("mmc: mediatek: Use data tune for CMD line tune") Link: https://lore.kernel.org/r/20191204071958.18553-1-chaotian.jing@mediatek.com Signed-off-by: Ulf Hansson --- drivers/mmc/host/mtk-sd.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/mmc/host/mtk-sd.c b/drivers/mmc/host/mtk-sd.c index 189e42674d859..010fe29a48883 100644 --- a/drivers/mmc/host/mtk-sd.c +++ b/drivers/mmc/host/mtk-sd.c @@ -228,6 +228,7 @@ #define MSDC_PATCH_BIT_SPCPUSH (0x1 << 29) /* RW */ #define MSDC_PATCH_BIT_DECRCTMO (0x1 << 30) /* RW */ +#define MSDC_PATCH_BIT1_CMDTA (0x7 << 3) /* RW */ #define MSDC_PATCH_BIT1_STOP_DLY (0xf << 8) /* RW */ #define MSDC_PATCH_BIT2_CFGRESP (0x1 << 15) /* RW */ @@ -1881,6 +1882,7 @@ static int hs400_tune_response(struct mmc_host *mmc, u32 opcode) /* select EMMC50 PAD CMD tune */ sdr_set_bits(host->base + PAD_CMD_TUNE, BIT(0)); + sdr_set_field(host->base + MSDC_PATCH_BIT1, MSDC_PATCH_BIT1_CMDTA, 2); if (mmc->ios.timing == MMC_TIMING_MMC_HS200 || mmc->ios.timing == MMC_TIMING_UHS_SDR104) -- GitLab From 8b6dc6b2d60221e90703babbc141f063b8a07e72 Mon Sep 17 00:00:00 2001 From: Rasmus Villemoes Date: Wed, 4 Dec 2019 09:54:46 +0100 Subject: [PATCH 0722/1437] mmc: sdhci-of-esdhc: Revert "mmc: sdhci-of-esdhc: add erratum A-009204 support" This reverts commit 5dd195522562542bc6ebe6e7bd47890d8b7ca93c. First, the fix seems to be plain wrong, since the erratum suggests waiting 5ms before setting setting SYSCTL[RSTD], but this msleep() happens after the call of sdhci_reset() which is where that bit gets set (if SDHCI_RESET_DATA is in mask). Second, walking the whole device tree to figure out if some node has a "fsl,p2020-esdhc" compatible string is hugely expensive - about 70 to 100 us on our mpc8309 board. Walking the device tree is done under a raw_spin_lock, so this is obviously really bad on an -rt system, and a waste of time on all. In fact, since esdhc_reset() seems to get called around 100 times per second, that mpc8309 now spends 0.8% of its time determining that it is not a p2020. Whether those 100 calls/s are normal or due to some other bug or misconfiguration, regularly hitting a 100 us non-preemptible window is unacceptable. Signed-off-by: Rasmus Villemoes Cc: stable@vger.kernel.org Link: https://lore.kernel.org/r/20191204085447.27491-1-linux@rasmusvillemoes.dk Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-of-esdhc.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/mmc/host/sdhci-of-esdhc.c b/drivers/mmc/host/sdhci-of-esdhc.c index 5cca3fa4610bc..7f87a90bf56a0 100644 --- a/drivers/mmc/host/sdhci-of-esdhc.c +++ b/drivers/mmc/host/sdhci-of-esdhc.c @@ -764,9 +764,6 @@ static void esdhc_reset(struct sdhci_host *host, u8 mask) sdhci_writel(host, host->ier, SDHCI_INT_ENABLE); sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE); - if (of_find_compatible_node(NULL, NULL, "fsl,p2020-esdhc")) - mdelay(5); - if (mask & SDHCI_RESET_ALL) { val = sdhci_readl(host, ESDHC_TBCTL); val &= ~ESDHC_TB_EN; -- GitLab From 2c92dd20304f505b6ef43d206fff21bda8f1f0ae Mon Sep 17 00:00:00 2001 From: Faiz Abbas Date: Fri, 6 Dec 2019 17:13:26 +0530 Subject: [PATCH 0723/1437] mmc: sdhci: Update the tuning failed messages to pr_debug level Tuning support in DDR50 speed mode was added in SD Specifications Part1 Physical Layer Specification v3.01. Its not possible to distinguish between v3.00 and v3.01 from the SCR and that is why since commit 4324f6de6d2e ("mmc: core: enable CMD19 tuning for DDR50 mode") tuning failures are ignored in DDR50 speed mode. Cards compatible with v3.00 don't respond to CMD19 in DDR50 and this error gets printed during enumeration and also if retune is triggered at any time during operation. Update the printk level to pr_debug so that these errors don't lead to false error reports. Signed-off-by: Faiz Abbas Cc: stable@vger.kernel.org # v4.4+ Link: https://lore.kernel.org/r/20191206114326.15856-1-faiz_abbas@ti.com Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 296d955ede595..42a9c8179da7c 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -2417,8 +2417,8 @@ static int __sdhci_execute_tuning(struct sdhci_host *host, u32 opcode) sdhci_send_tuning(host, opcode); if (!host->tuning_done) { - pr_info("%s: Tuning timeout, falling back to fixed sampling clock\n", - mmc_hostname(host->mmc)); + pr_debug("%s: Tuning timeout, falling back to fixed sampling clock\n", + mmc_hostname(host->mmc)); sdhci_abort_tuning(host, opcode); return -ETIMEDOUT; } -- GitLab From 556672d75ff486e0b6786056da624131679e0576 Mon Sep 17 00:00:00 2001 From: Shengjiu Wang Date: Wed, 11 Dec 2019 19:57:22 +0800 Subject: [PATCH 0724/1437] ASoC: wm8962: fix lambda value According to user manual, it is required that FLL_LAMBDA > 0 in all cases (Integer and Franctional modes). Fixes: 9a76f1ff6e29 ("ASoC: Add initial WM8962 CODEC driver") Signed-off-by: Shengjiu Wang Acked-by: Charles Keepax Link: https://lore.kernel.org/r/1576065442-19763-1-git-send-email-shengjiu.wang@nxp.com Signed-off-by: Mark Brown --- sound/soc/codecs/wm8962.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/codecs/wm8962.c b/sound/soc/codecs/wm8962.c index 3e5c69fbc33ad..d9d59f45833fd 100644 --- a/sound/soc/codecs/wm8962.c +++ b/sound/soc/codecs/wm8962.c @@ -2788,7 +2788,7 @@ static int fll_factors(struct _fll_div *fll_div, unsigned int Fref, if (target % Fref == 0) { fll_div->theta = 0; - fll_div->lambda = 0; + fll_div->lambda = 1; } else { gcd_fll = gcd(target, fratio * Fref); @@ -2858,7 +2858,7 @@ static int wm8962_set_fll(struct snd_soc_component *component, int fll_id, int s return -EINVAL; } - if (fll_div.theta || fll_div.lambda) + if (fll_div.theta) fll1 |= WM8962_FLL_FRAC; /* Stop the FLL while we reconfigure */ -- GitLab From 62a1923cc8fe095912e6213ed5de27abbf1de77e Mon Sep 17 00:00:00 2001 From: Andreas Kemnade Date: Wed, 11 Dec 2019 23:16:00 +0100 Subject: [PATCH 0725/1437] regulator: rn5t618: fix module aliases platform device aliases were missing, preventing autoloading of module. Fixes: 811b700630ff ("regulator: rn5t618: add driver for Ricoh RN5T618 regulators") Signed-off-by: Andreas Kemnade Link: https://lore.kernel.org/r/20191211221600.29438-1-andreas@kemnade.info Signed-off-by: Mark Brown --- drivers/regulator/rn5t618-regulator.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/regulator/rn5t618-regulator.c b/drivers/regulator/rn5t618-regulator.c index eb807a059479a..aa6e7c5341ceb 100644 --- a/drivers/regulator/rn5t618-regulator.c +++ b/drivers/regulator/rn5t618-regulator.c @@ -148,6 +148,7 @@ static struct platform_driver rn5t618_regulator_driver = { module_platform_driver(rn5t618_regulator_driver); +MODULE_ALIAS("platform:rn5t618-regulator"); MODULE_AUTHOR("Beniamino Galvani "); MODULE_DESCRIPTION("RN5T618 regulator driver"); MODULE_LICENSE("GPL v2"); -- GitLab From 007773e16a6f3f49d1439554078c3ba8af131998 Mon Sep 17 00:00:00 2001 From: Michael Walle Date: Wed, 11 Dec 2019 20:57:30 +0100 Subject: [PATCH 0726/1437] spi: nxp-fspi: Ensure width is respected in spi-mem operations Make use of a core helper to ensure the desired width is respected when calling spi-mem operators. Otherwise only the SPI controller will be matched with the flash chip, which might lead to wrong widths. Also consider the width specified by the user in the device tree. Fixes: a5356aef6a90 ("spi: spi-mem: Add driver for NXP FlexSPI controller") Signed-off-by: Michael Walle Link: https://lore.kernel.org/r/20191211195730.26794-1-michael@walle.cc Signed-off-by: Mark Brown --- drivers/spi/spi-nxp-fspi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/spi/spi-nxp-fspi.c b/drivers/spi/spi-nxp-fspi.c index c36bb1bb464e6..8c5084a3a617b 100644 --- a/drivers/spi/spi-nxp-fspi.c +++ b/drivers/spi/spi-nxp-fspi.c @@ -439,7 +439,7 @@ static bool nxp_fspi_supports_op(struct spi_mem *mem, op->data.nbytes > f->devtype_data->txfifo) return false; - return true; + return spi_mem_default_supports_op(mem, op); } /* Instead of busy looping invoke readl_poll_timeout functionality. */ -- GitLab From 63aa6a692595d47a0785297b481072086b9272d2 Mon Sep 17 00:00:00 2001 From: Christophe Leroy Date: Thu, 12 Dec 2019 17:47:24 +0000 Subject: [PATCH 0727/1437] spi: fsl: use platform_get_irq() instead of of_irq_to_resource() Unlike irq_of_parse_and_map() which has a dummy definition on SPARC, of_irq_to_resource() hasn't. But as platform_get_irq() can be used instead and is generic, use it. Reported-by: kbuild test robot Suggested-by: Mark Brown Fixes: 3194d2533eff ("spi: fsl: don't map irq during probe") Cc: stable@vger.kernel.org Signed-off-by: Christophe Leroy Link: https://lore.kernel.org/r/091a277fd0b3356dca1e29858c1c96983fc9cb25.1576172743.git.christophe.leroy@c-s.fr Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-spi.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/spi/spi-fsl-spi.c b/drivers/spi/spi-fsl-spi.c index d0ad9709f4a6c..fb4159ad6bf6a 100644 --- a/drivers/spi/spi-fsl-spi.c +++ b/drivers/spi/spi-fsl-spi.c @@ -746,9 +746,9 @@ static int of_fsl_spi_probe(struct platform_device *ofdev) if (ret) goto err; - irq = of_irq_to_resource(np, 0, NULL); - if (irq <= 0) { - ret = -EINVAL; + irq = platform_get_irq(ofdev, 0); + if (irq < 0) { + ret = irq; goto err; } -- GitLab From 0601546f23fb70d84b807e73cfe8e789d054c98d Mon Sep 17 00:00:00 2001 From: Christophe Leroy Date: Sat, 14 Dec 2019 08:10:29 +0000 Subject: [PATCH 0728/1437] powerpc/8xx: fix bogus __init on mmu_mapin_ram_chunk() Remove __init qualifier for mmu_mapin_ram_chunk() as it is called by mmu_mark_initmem_nx() and mmu_mark_rodata_ro() which are not __init functions. At the same time, mark it static as it is only used in this file. Reported-by: kbuild test robot Fixes: a2227a277743 ("powerpc/32: Don't populate page tables for block mapped pages except on the 8xx") Signed-off-by: Christophe Leroy Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/56648921986a6b3e7315b1fbbf4684f21bd2dea8.1576310997.git.christophe.leroy@c-s.fr --- arch/powerpc/mm/nohash/8xx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/powerpc/mm/nohash/8xx.c b/arch/powerpc/mm/nohash/8xx.c index 090af2d2d3e4c..96eb8e43f39b5 100644 --- a/arch/powerpc/mm/nohash/8xx.c +++ b/arch/powerpc/mm/nohash/8xx.c @@ -103,7 +103,7 @@ static void mmu_patch_addis(s32 *site, long simm) patch_instruction_site(site, instr); } -void __init mmu_mapin_ram_chunk(unsigned long offset, unsigned long top, pgprot_t prot) +static void mmu_mapin_ram_chunk(unsigned long offset, unsigned long top, pgprot_t prot) { unsigned long s = offset; unsigned long v = PAGE_OFFSET + s; -- GitLab From e352f576d345e5bf1fb62c8559851448a6c1d9cd Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Mon, 16 Dec 2019 11:30:58 +0100 Subject: [PATCH 0729/1437] powerpc/pseries/cmm: fix managed page counts when migrating pages between zones Commit 63341ab03706 (virtio-balloon: fix managed page counts when migrating pages between zones) fixed a long existing BUG in the virtio-balloon driver when pages would get migrated between zones. I did not try to reproduce on powerpc, but looking at the code, the same should apply to powerpc/cmm ever since it started using the balloon compaction infrastructure (luckily just recently). In case we have to migrate a ballon page to a newpage of another zone, the managed page count of both zones is wrong. Paired with memory offlining (which will adjust the managed page count), we can trigger kernel crashes and all kinds of different symptoms. Fix it by properly adjusting the managed page count when migrating if the zone changed. We'll temporarily modify the totalram page count. If this ever becomes a problem, we can fine tune by providing helpers that don't touch the totalram pages (e.g., adjust_zone_managed_page_count()). Fixes: fe030c9b85e6 ("powerpc/pseries/cmm: Implement balloon compaction") Signed-off-by: David Hildenbrand Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20191216103058.4958-1-david@redhat.com --- arch/powerpc/platforms/pseries/cmm.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/arch/powerpc/platforms/pseries/cmm.c b/arch/powerpc/platforms/pseries/cmm.c index 91571841df8a7..9dba7e8808853 100644 --- a/arch/powerpc/platforms/pseries/cmm.c +++ b/arch/powerpc/platforms/pseries/cmm.c @@ -539,6 +539,16 @@ static int cmm_migratepage(struct balloon_dev_info *b_dev_info, /* balloon page list reference */ get_page(newpage); + /* + * When we migrate a page to a different zone, we have to fixup the + * count of both involved zones as we adjusted the managed page count + * when inflating. + */ + if (page_zone(page) != page_zone(newpage)) { + adjust_managed_page_count(page, 1); + adjust_managed_page_count(newpage, -1); + } + spin_lock_irqsave(&b_dev_info->pages_lock, flags); balloon_page_insert(b_dev_info, newpage); balloon_page_delete(page); -- GitLab From 61e3acd8c693a14fc69b824cb5b08d02cb90a6e7 Mon Sep 17 00:00:00 2001 From: Andrew Donnellan Date: Tue, 10 Dec 2019 00:22:21 +1100 Subject: [PATCH 0730/1437] powerpc: Fix __clear_user() with KUAP enabled The KUAP implementation adds calls in clear_user() to enable and disable access to userspace memory. However, it doesn't add these to __clear_user(), which is used in the ptrace regset code. As there's only one direct user of __clear_user() (the regset code), and the time taken to set the AMR for KUAP purposes is going to dominate the cost of a quick access_ok(), there's not much point having a separate path. Rename __clear_user() to __arch_clear_user(), and make __clear_user() just call clear_user(). Reported-by: syzbot+f25ecf4b2982d8c7a640@syzkaller-ppc64.appspotmail.com Reported-by: Daniel Axtens Suggested-by: Michael Ellerman Fixes: de78a9c42a79 ("powerpc: Add a framework for Kernel Userspace Access Protection") Signed-off-by: Andrew Donnellan [mpe: Use __arch_clear_user() for the asm version like arm64 & nds32] Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20191209132221.15328-1-ajd@linux.ibm.com --- arch/powerpc/include/asm/uaccess.h | 9 +++++++-- arch/powerpc/lib/string_32.S | 4 ++-- arch/powerpc/lib/string_64.S | 6 +++--- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/arch/powerpc/include/asm/uaccess.h b/arch/powerpc/include/asm/uaccess.h index 15002b51ff18d..c92fe7fe9692c 100644 --- a/arch/powerpc/include/asm/uaccess.h +++ b/arch/powerpc/include/asm/uaccess.h @@ -401,7 +401,7 @@ copy_to_user_mcsafe(void __user *to, const void *from, unsigned long n) return n; } -extern unsigned long __clear_user(void __user *addr, unsigned long size); +unsigned long __arch_clear_user(void __user *addr, unsigned long size); static inline unsigned long clear_user(void __user *addr, unsigned long size) { @@ -409,12 +409,17 @@ static inline unsigned long clear_user(void __user *addr, unsigned long size) might_fault(); if (likely(access_ok(addr, size))) { allow_write_to_user(addr, size); - ret = __clear_user(addr, size); + ret = __arch_clear_user(addr, size); prevent_write_to_user(addr, size); } return ret; } +static inline unsigned long __clear_user(void __user *addr, unsigned long size) +{ + return clear_user(addr, size); +} + extern long strncpy_from_user(char *dst, const char __user *src, long count); extern __must_check long strnlen_user(const char __user *str, long n); diff --git a/arch/powerpc/lib/string_32.S b/arch/powerpc/lib/string_32.S index f69a6aab7bfbb..1ddb26394e8ac 100644 --- a/arch/powerpc/lib/string_32.S +++ b/arch/powerpc/lib/string_32.S @@ -17,7 +17,7 @@ CACHELINE_BYTES = L1_CACHE_BYTES LG_CACHELINE_BYTES = L1_CACHE_SHIFT CACHELINE_MASK = (L1_CACHE_BYTES-1) -_GLOBAL(__clear_user) +_GLOBAL(__arch_clear_user) /* * Use dcbz on the complete cache lines in the destination * to set them to zero. This requires that the destination @@ -87,4 +87,4 @@ _GLOBAL(__clear_user) EX_TABLE(8b, 91b) EX_TABLE(9b, 91b) -EXPORT_SYMBOL(__clear_user) +EXPORT_SYMBOL(__arch_clear_user) diff --git a/arch/powerpc/lib/string_64.S b/arch/powerpc/lib/string_64.S index 507b18b1660e6..169872bc08928 100644 --- a/arch/powerpc/lib/string_64.S +++ b/arch/powerpc/lib/string_64.S @@ -17,7 +17,7 @@ PPC64_CACHES: .section ".text" /** - * __clear_user: - Zero a block of memory in user space, with less checking. + * __arch_clear_user: - Zero a block of memory in user space, with less checking. * @to: Destination address, in user space. * @n: Number of bytes to zero. * @@ -58,7 +58,7 @@ err3; stb r0,0(r3) mr r3,r4 blr -_GLOBAL_TOC(__clear_user) +_GLOBAL_TOC(__arch_clear_user) cmpdi r4,32 neg r6,r3 li r0,0 @@ -181,4 +181,4 @@ err1; dcbz 0,r3 cmpdi r4,32 blt .Lshort_clear b .Lmedium_clear -EXPORT_SYMBOL(__clear_user) +EXPORT_SYMBOL(__arch_clear_user) -- GitLab From a79ac2d1036a824abba982c33e938b717d1b659f Mon Sep 17 00:00:00 2001 From: Prashant Bhole Date: Mon, 16 Dec 2019 17:27:38 +0900 Subject: [PATCH 0731/1437] libbpf: Fix build by renaming variables MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In btf__align_of() variable name 't' is shadowed by inner block declaration of another variable with same name. Patch renames variables in order to fix it. CC sharedobjs/btf.o btf.c: In function ‘btf__align_of’: btf.c:303:21: error: declaration of ‘t’ shadows a previous local [-Werror=shadow] 303 | int i, align = 1, t; | ^ btf.c:283:25: note: shadowed declaration is here 283 | const struct btf_type *t = btf__type_by_id(btf, id); | Fixes: 3d208f4ca111 ("libbpf: Expose btf__align_of() API") Signed-off-by: Prashant Bhole Signed-off-by: Daniel Borkmann Tested-by: Toke Høiland-Jørgensen Link: https://lore.kernel.org/bpf/20191216082738.28421-1-prashantbhole.linux@gmail.com --- tools/lib/bpf/btf.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tools/lib/bpf/btf.c b/tools/lib/bpf/btf.c index 520021939d81a..5f04f56e1eb65 100644 --- a/tools/lib/bpf/btf.c +++ b/tools/lib/bpf/btf.c @@ -300,16 +300,16 @@ int btf__align_of(const struct btf *btf, __u32 id) case BTF_KIND_UNION: { const struct btf_member *m = btf_members(t); __u16 vlen = btf_vlen(t); - int i, align = 1, t; + int i, max_align = 1, align; for (i = 0; i < vlen; i++, m++) { - t = btf__align_of(btf, m->type); - if (t <= 0) - return t; - align = max(align, t); + align = btf__align_of(btf, m->type); + if (align <= 0) + return align; + max_align = max(max_align, align); } - return align; + return max_align; } default: pr_warn("unsupported BTF_KIND:%u\n", btf_kind(t)); -- GitLab From 159ecc002b5f55f9c58de533551a2ee552861185 Mon Sep 17 00:00:00 2001 From: Paul Chaignon Date: Mon, 16 Dec 2019 12:27:33 +0100 Subject: [PATCH 0732/1437] bpftool: Fix compilation warning on shadowed variable The ident variable has already been declared at the top of the function and doesn't need to be re-declared. Fixes: 985ead416df39 ("bpftool: Add skeleton codegen command") Signed-off-by: Paul Chaignon Signed-off-by: Daniel Borkmann Link: https://lore.kernel.org/bpf/20191216112733.GA28366@Omicron --- tools/bpf/bpftool/gen.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/bpf/bpftool/gen.c b/tools/bpf/bpftool/gen.c index a07c80429c7a3..f70088b4c19b9 100644 --- a/tools/bpf/bpftool/gen.c +++ b/tools/bpf/bpftool/gen.c @@ -388,7 +388,7 @@ static int do_skeleton(int argc, char **argv) ); i = 0; bpf_object__for_each_map(map, obj) { - const char *ident = get_map_ident(map); + ident = get_map_ident(map); if (!ident) continue; -- GitLab From 6bd5ce6089b561f5392460bfb654dea89356ab1b Mon Sep 17 00:00:00 2001 From: Tetsuo Handa Date: Mon, 16 Dec 2019 19:16:48 +0900 Subject: [PATCH 0733/1437] tomoyo: Suppress RCU warning at list_for_each_entry_rcu(). John Garry has reported that allmodconfig kernel on arm64 causes flood of "RCU-list traversed in non-reader section!!" warning. I don't know what change caused this warning, but this warning is safe because TOMOYO uses SRCU lock instead. Let's suppress this warning by explicitly telling that the caller is holding SRCU lock. Reported-and-tested-by: John Garry Signed-off-by: Tetsuo Handa --- security/tomoyo/common.c | 9 ++++++--- security/tomoyo/domain.c | 15 ++++++++++----- security/tomoyo/group.c | 9 ++++++--- security/tomoyo/util.c | 6 ++++-- 4 files changed, 26 insertions(+), 13 deletions(-) diff --git a/security/tomoyo/common.c b/security/tomoyo/common.c index dd3d5942e6692..c36bafbcd77ee 100644 --- a/security/tomoyo/common.c +++ b/security/tomoyo/common.c @@ -951,7 +951,8 @@ static bool tomoyo_manager(void) exe = tomoyo_get_exe(); if (!exe) return false; - list_for_each_entry_rcu(ptr, &tomoyo_kernel_namespace.policy_list[TOMOYO_ID_MANAGER], head.list) { + list_for_each_entry_rcu(ptr, &tomoyo_kernel_namespace.policy_list[TOMOYO_ID_MANAGER], head.list, + srcu_read_lock_held(&tomoyo_ss)) { if (!ptr->head.is_deleted && (!tomoyo_pathcmp(domainname, ptr->manager) || !strcmp(exe, ptr->manager->name))) { @@ -1095,7 +1096,8 @@ static int tomoyo_delete_domain(char *domainname) if (mutex_lock_interruptible(&tomoyo_policy_lock)) return -EINTR; /* Is there an active domain? */ - list_for_each_entry_rcu(domain, &tomoyo_domain_list, list) { + list_for_each_entry_rcu(domain, &tomoyo_domain_list, list, + srcu_read_lock_held(&tomoyo_ss)) { /* Never delete tomoyo_kernel_domain */ if (domain == &tomoyo_kernel_domain) continue; @@ -2778,7 +2780,8 @@ void tomoyo_check_profile(void) tomoyo_policy_loaded = true; pr_info("TOMOYO: 2.6.0\n"); - list_for_each_entry_rcu(domain, &tomoyo_domain_list, list) { + list_for_each_entry_rcu(domain, &tomoyo_domain_list, list, + srcu_read_lock_held(&tomoyo_ss)) { const u8 profile = domain->profile; struct tomoyo_policy_namespace *ns = domain->ns; diff --git a/security/tomoyo/domain.c b/security/tomoyo/domain.c index 8526a0a740238..7869d6a9980bf 100644 --- a/security/tomoyo/domain.c +++ b/security/tomoyo/domain.c @@ -41,7 +41,8 @@ int tomoyo_update_policy(struct tomoyo_acl_head *new_entry, const int size, if (mutex_lock_interruptible(&tomoyo_policy_lock)) return -ENOMEM; - list_for_each_entry_rcu(entry, list, list) { + list_for_each_entry_rcu(entry, list, list, + srcu_read_lock_held(&tomoyo_ss)) { if (entry->is_deleted == TOMOYO_GC_IN_PROGRESS) continue; if (!check_duplicate(entry, new_entry)) @@ -119,7 +120,8 @@ int tomoyo_update_domain(struct tomoyo_acl_info *new_entry, const int size, } if (mutex_lock_interruptible(&tomoyo_policy_lock)) goto out; - list_for_each_entry_rcu(entry, list, list) { + list_for_each_entry_rcu(entry, list, list, + srcu_read_lock_held(&tomoyo_ss)) { if (entry->is_deleted == TOMOYO_GC_IN_PROGRESS) continue; if (!tomoyo_same_acl_head(entry, new_entry) || @@ -166,7 +168,8 @@ void tomoyo_check_acl(struct tomoyo_request_info *r, u16 i = 0; retry: - list_for_each_entry_rcu(ptr, list, list) { + list_for_each_entry_rcu(ptr, list, list, + srcu_read_lock_held(&tomoyo_ss)) { if (ptr->is_deleted || ptr->type != r->param_type) continue; if (!check_entry(r, ptr)) @@ -298,7 +301,8 @@ static inline bool tomoyo_scan_transition { const struct tomoyo_transition_control *ptr; - list_for_each_entry_rcu(ptr, list, head.list) { + list_for_each_entry_rcu(ptr, list, head.list, + srcu_read_lock_held(&tomoyo_ss)) { if (ptr->head.is_deleted || ptr->type != type) continue; if (ptr->domainname) { @@ -735,7 +739,8 @@ retry: /* Check 'aggregator' directive. */ candidate = &exename; - list_for_each_entry_rcu(ptr, list, head.list) { + list_for_each_entry_rcu(ptr, list, head.list, + srcu_read_lock_held(&tomoyo_ss)) { if (ptr->head.is_deleted || !tomoyo_path_matches_pattern(&exename, ptr->original_name)) diff --git a/security/tomoyo/group.c b/security/tomoyo/group.c index a37c7dc66e444..1cecdd7975971 100644 --- a/security/tomoyo/group.c +++ b/security/tomoyo/group.c @@ -133,7 +133,8 @@ tomoyo_path_matches_group(const struct tomoyo_path_info *pathname, { struct tomoyo_path_group *member; - list_for_each_entry_rcu(member, &group->member_list, head.list) { + list_for_each_entry_rcu(member, &group->member_list, head.list, + srcu_read_lock_held(&tomoyo_ss)) { if (member->head.is_deleted) continue; if (!tomoyo_path_matches_pattern(pathname, member->member_name)) @@ -161,7 +162,8 @@ bool tomoyo_number_matches_group(const unsigned long min, struct tomoyo_number_group *member; bool matched = false; - list_for_each_entry_rcu(member, &group->member_list, head.list) { + list_for_each_entry_rcu(member, &group->member_list, head.list, + srcu_read_lock_held(&tomoyo_ss)) { if (member->head.is_deleted) continue; if (min > member->number.values[1] || @@ -191,7 +193,8 @@ bool tomoyo_address_matches_group(const bool is_ipv6, const __be32 *address, bool matched = false; const u8 size = is_ipv6 ? 16 : 4; - list_for_each_entry_rcu(member, &group->member_list, head.list) { + list_for_each_entry_rcu(member, &group->member_list, head.list, + srcu_read_lock_held(&tomoyo_ss)) { if (member->head.is_deleted) continue; if (member->address.is_ipv6 != is_ipv6) diff --git a/security/tomoyo/util.c b/security/tomoyo/util.c index 52752e1a84ed8..eba0b3395851e 100644 --- a/security/tomoyo/util.c +++ b/security/tomoyo/util.c @@ -594,7 +594,8 @@ struct tomoyo_domain_info *tomoyo_find_domain(const char *domainname) name.name = domainname; tomoyo_fill_path_info(&name); - list_for_each_entry_rcu(domain, &tomoyo_domain_list, list) { + list_for_each_entry_rcu(domain, &tomoyo_domain_list, list, + srcu_read_lock_held(&tomoyo_ss)) { if (!domain->is_deleted && !tomoyo_pathcmp(&name, domain->domainname)) return domain; @@ -1028,7 +1029,8 @@ bool tomoyo_domain_quota_is_ok(struct tomoyo_request_info *r) return false; if (!domain) return true; - list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) { + list_for_each_entry_rcu(ptr, &domain->acl_info_list, list, + srcu_read_lock_held(&tomoyo_ss)) { u16 perm; u8 i; -- GitLab From 5984dc6cb5aa6cce342a44f01f984fde09ed05b1 Mon Sep 17 00:00:00 2001 From: Prashant Bhole Date: Mon, 16 Dec 2019 16:16:19 +0900 Subject: [PATCH 0734/1437] samples/bpf: Reintroduce missed build targets Add xdp_redirect and per_socket_stats_example in build targets. They got removed from build targets in Makefile reorganization. Fixes: 1d97c6c2511f ("samples/bpf: Base target programs rules on Makefile.target") Signed-off-by: Prashant Bhole Signed-off-by: Alexei Starovoitov Link: https://lore.kernel.org/bpf/20191216071619.25479-1-prashantbhole.linux@gmail.com --- samples/bpf/Makefile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/samples/bpf/Makefile b/samples/bpf/Makefile index 1fc42ad8ff49b..8003d2823fa88 100644 --- a/samples/bpf/Makefile +++ b/samples/bpf/Makefile @@ -38,6 +38,8 @@ tprogs-y += tc_l2_redirect tprogs-y += lwt_len_hist tprogs-y += xdp_tx_iptunnel tprogs-y += test_map_in_map +tprogs-y += per_socket_stats_example +tprogs-y += xdp_redirect tprogs-y += xdp_redirect_map tprogs-y += xdp_redirect_cpu tprogs-y += xdp_monitor -- GitLab From 5615ed472dc04e38a1affca69059f17c1178f770 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Toke=20H=C3=B8iland-J=C3=B8rgensen?= Date: Mon, 16 Dec 2019 11:24:05 +0100 Subject: [PATCH 0735/1437] samples/bpf: Add missing -lz to TPROGS_LDLIBS MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since libbpf now links against zlib, this needs to be included in the linker invocation for the userspace programs in samples/bpf that link statically against libbpf. Fixes: 166750bc1dd2 ("libbpf: Support libbpf-provided extern variables") Signed-off-by: Toke Høiland-Jørgensen Signed-off-by: Alexei Starovoitov Tested-by: Björn Töpel Link: https://lore.kernel.org/bpf/20191216102405.353834-1-toke@redhat.com --- samples/bpf/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/bpf/Makefile b/samples/bpf/Makefile index 8003d2823fa88..227077ae66ed0 100644 --- a/samples/bpf/Makefile +++ b/samples/bpf/Makefile @@ -198,7 +198,7 @@ endif TPROGCFLAGS_bpf_load.o += -Wno-unused-variable -TPROGS_LDLIBS += $(LIBBPF) -lelf +TPROGS_LDLIBS += $(LIBBPF) -lelf -lz TPROGLDLIBS_tracex4 += -lrt TPROGLDLIBS_trace_output += -lrt TPROGLDLIBS_map_perf_test += -lrt -- GitLab From 450278977acbf494a20367c22fbb38729772d1fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Toke=20H=C3=B8iland-J=C3=B8rgensen?= Date: Mon, 16 Dec 2019 11:38:19 +0100 Subject: [PATCH 0736/1437] samples/bpf: Set -fno-stack-protector when building BPF programs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It seems Clang can in some cases turn on stack protection by default, which doesn't work with BPF. This was reported once before[0], but it seems the flag to explicitly turn off the stack protector wasn't added to the Makefile, so do that now. The symptom of this is compile errors like the following: error: :0:0: in function bpf_prog1 i32 (%struct.__sk_buff*): A call to built-in function '__stack_chk_fail' is not supported. [0] https://www.spinics.net/lists/netdev/msg556400.html Signed-off-by: Toke Høiland-Jørgensen Signed-off-by: Alexei Starovoitov Link: https://lore.kernel.org/bpf/20191216103819.359535-1-toke@redhat.com --- samples/bpf/Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/samples/bpf/Makefile b/samples/bpf/Makefile index 227077ae66ed0..5b89c0370f332 100644 --- a/samples/bpf/Makefile +++ b/samples/bpf/Makefile @@ -236,6 +236,7 @@ BTF_LLVM_PROBE := $(shell echo "int main() { return 0; }" | \ readelf -S ./llvm_btf_verify.o | grep BTF; \ /bin/rm -f ./llvm_btf_verify.o) +BPF_EXTRA_CFLAGS += -fno-stack-protector ifneq ($(BTF_LLVM_PROBE),) BPF_EXTRA_CFLAGS += -g else -- GitLab From d50ecc46d18fa19ccf06e0c4d2ee8a050c665e3d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Toke=20H=C3=B8iland-J=C3=B8rgensen?= Date: Mon, 16 Dec 2019 12:07:42 +0100 Subject: [PATCH 0737/1437] samples/bpf: Attach XDP programs in driver mode by default MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When attaching XDP programs, userspace can set flags to request the attach mode (generic/SKB mode, driver mode or hw offloaded mode). If no such flags are requested, the kernel will attempt to attach in driver mode, and then silently fall back to SKB mode if this fails. The silent fallback is a major source of user confusion, as users will try to load a program on a device without XDP support, and instead of an error they will get the silent fallback behaviour, not notice, and then wonder why performance is not what they were expecting. In an attempt to combat this, let's switch all the samples to default to explicitly requesting driver-mode attach. As part of this, ensure that all the userspace utilities have a switch to enable SKB mode. For those that have a switch to request driver mode, keep it but turn it into a no-op. Signed-off-by: Toke Høiland-Jørgensen Signed-off-by: Alexei Starovoitov Acked-by: Jesper Dangaard Brouer Acked-by: David Ahern Link: https://lore.kernel.org/bpf/20191216110742.364456-1-toke@redhat.com --- samples/bpf/xdp1_user.c | 5 ++++- samples/bpf/xdp_adjust_tail_user.c | 5 ++++- samples/bpf/xdp_fwd_user.c | 17 ++++++++++++++--- samples/bpf/xdp_redirect_cpu_user.c | 4 ++++ samples/bpf/xdp_redirect_map_user.c | 5 ++++- samples/bpf/xdp_redirect_user.c | 5 ++++- samples/bpf/xdp_router_ipv4_user.c | 3 +++ samples/bpf/xdp_rxq_info_user.c | 4 ++++ samples/bpf/xdp_sample_pkts_user.c | 12 +++++++++--- samples/bpf/xdp_tx_iptunnel_user.c | 5 ++++- samples/bpf/xdpsock_user.c | 5 ++++- 11 files changed, 58 insertions(+), 12 deletions(-) diff --git a/samples/bpf/xdp1_user.c b/samples/bpf/xdp1_user.c index 3e553eed95a77..38a8852cb57f5 100644 --- a/samples/bpf/xdp1_user.c +++ b/samples/bpf/xdp1_user.c @@ -98,7 +98,7 @@ int main(int argc, char **argv) xdp_flags |= XDP_FLAGS_SKB_MODE; break; case 'N': - xdp_flags |= XDP_FLAGS_DRV_MODE; + /* default, set below */ break; case 'F': xdp_flags &= ~XDP_FLAGS_UPDATE_IF_NOEXIST; @@ -109,6 +109,9 @@ int main(int argc, char **argv) } } + if (!(xdp_flags & XDP_FLAGS_SKB_MODE)) + xdp_flags |= XDP_FLAGS_DRV_MODE; + if (optind == argc) { usage(basename(argv[0])); return 1; diff --git a/samples/bpf/xdp_adjust_tail_user.c b/samples/bpf/xdp_adjust_tail_user.c index d86e9ad0356bd..008789eb6adad 100644 --- a/samples/bpf/xdp_adjust_tail_user.c +++ b/samples/bpf/xdp_adjust_tail_user.c @@ -120,7 +120,7 @@ int main(int argc, char **argv) xdp_flags |= XDP_FLAGS_SKB_MODE; break; case 'N': - xdp_flags |= XDP_FLAGS_DRV_MODE; + /* default, set below */ break; case 'F': xdp_flags &= ~XDP_FLAGS_UPDATE_IF_NOEXIST; @@ -132,6 +132,9 @@ int main(int argc, char **argv) opt_flags[opt] = 0; } + if (!(xdp_flags & XDP_FLAGS_SKB_MODE)) + xdp_flags |= XDP_FLAGS_DRV_MODE; + for (i = 0; i < strlen(optstr); i++) { if (opt_flags[(unsigned int)optstr[i]]) { fprintf(stderr, "Missing argument -%c\n", optstr[i]); diff --git a/samples/bpf/xdp_fwd_user.c b/samples/bpf/xdp_fwd_user.c index 97ff1dad76697..c30f9acfdb84d 100644 --- a/samples/bpf/xdp_fwd_user.c +++ b/samples/bpf/xdp_fwd_user.c @@ -27,11 +27,13 @@ #include "libbpf.h" #include +static __u32 xdp_flags = XDP_FLAGS_UPDATE_IF_NOEXIST; + static int do_attach(int idx, int prog_fd, int map_fd, const char *name) { int err; - err = bpf_set_link_xdp_fd(idx, prog_fd, 0); + err = bpf_set_link_xdp_fd(idx, prog_fd, xdp_flags); if (err < 0) { printf("ERROR: failed to attach program to %s\n", name); return err; @@ -49,7 +51,7 @@ static int do_detach(int idx, const char *name) { int err; - err = bpf_set_link_xdp_fd(idx, -1, 0); + err = bpf_set_link_xdp_fd(idx, -1, xdp_flags); if (err < 0) printf("ERROR: failed to detach program from %s\n", name); @@ -83,11 +85,17 @@ int main(int argc, char **argv) int attach = 1; int ret = 0; - while ((opt = getopt(argc, argv, ":dD")) != -1) { + while ((opt = getopt(argc, argv, ":dDSF")) != -1) { switch (opt) { case 'd': attach = 0; break; + case 'S': + xdp_flags |= XDP_FLAGS_SKB_MODE; + break; + case 'F': + xdp_flags &= ~XDP_FLAGS_UPDATE_IF_NOEXIST; + break; case 'D': prog_name = "xdp_fwd_direct"; break; @@ -97,6 +105,9 @@ int main(int argc, char **argv) } } + if (!(xdp_flags & XDP_FLAGS_SKB_MODE)) + xdp_flags |= XDP_FLAGS_DRV_MODE; + if (optind == argc) { usage(basename(argv[0])); return 1; diff --git a/samples/bpf/xdp_redirect_cpu_user.c b/samples/bpf/xdp_redirect_cpu_user.c index 0da6e9e7132ec..72af628529b51 100644 --- a/samples/bpf/xdp_redirect_cpu_user.c +++ b/samples/bpf/xdp_redirect_cpu_user.c @@ -728,6 +728,10 @@ int main(int argc, char **argv) return EXIT_FAIL_OPTION; } } + + if (!(xdp_flags & XDP_FLAGS_SKB_MODE)) + xdp_flags |= XDP_FLAGS_DRV_MODE; + /* Required option */ if (ifindex == -1) { fprintf(stderr, "ERR: required option --dev missing\n"); diff --git a/samples/bpf/xdp_redirect_map_user.c b/samples/bpf/xdp_redirect_map_user.c index f70ee33907fde..cc840661faabc 100644 --- a/samples/bpf/xdp_redirect_map_user.c +++ b/samples/bpf/xdp_redirect_map_user.c @@ -116,7 +116,7 @@ int main(int argc, char **argv) xdp_flags |= XDP_FLAGS_SKB_MODE; break; case 'N': - xdp_flags |= XDP_FLAGS_DRV_MODE; + /* default, set below */ break; case 'F': xdp_flags &= ~XDP_FLAGS_UPDATE_IF_NOEXIST; @@ -127,6 +127,9 @@ int main(int argc, char **argv) } } + if (!(xdp_flags & XDP_FLAGS_SKB_MODE)) + xdp_flags |= XDP_FLAGS_DRV_MODE; + if (optind == argc) { printf("usage: %s _IN _OUT\n", argv[0]); return 1; diff --git a/samples/bpf/xdp_redirect_user.c b/samples/bpf/xdp_redirect_user.c index 5440cd620607f..71dff8e3382a8 100644 --- a/samples/bpf/xdp_redirect_user.c +++ b/samples/bpf/xdp_redirect_user.c @@ -117,7 +117,7 @@ int main(int argc, char **argv) xdp_flags |= XDP_FLAGS_SKB_MODE; break; case 'N': - xdp_flags |= XDP_FLAGS_DRV_MODE; + /* default, set below */ break; case 'F': xdp_flags &= ~XDP_FLAGS_UPDATE_IF_NOEXIST; @@ -128,6 +128,9 @@ int main(int argc, char **argv) } } + if (!(xdp_flags & XDP_FLAGS_SKB_MODE)) + xdp_flags |= XDP_FLAGS_DRV_MODE; + if (optind == argc) { printf("usage: %s _IN _OUT\n", argv[0]); return 1; diff --git a/samples/bpf/xdp_router_ipv4_user.c b/samples/bpf/xdp_router_ipv4_user.c index 1469b66ebad1e..fef286c5add2d 100644 --- a/samples/bpf/xdp_router_ipv4_user.c +++ b/samples/bpf/xdp_router_ipv4_user.c @@ -662,6 +662,9 @@ int main(int ac, char **argv) } } + if (!(flags & XDP_FLAGS_SKB_MODE)) + flags |= XDP_FLAGS_DRV_MODE; + if (optind == ac) { usage(basename(argv[0])); return 1; diff --git a/samples/bpf/xdp_rxq_info_user.c b/samples/bpf/xdp_rxq_info_user.c index 8fc3ad01de72c..fc4983fd69590 100644 --- a/samples/bpf/xdp_rxq_info_user.c +++ b/samples/bpf/xdp_rxq_info_user.c @@ -551,6 +551,10 @@ int main(int argc, char **argv) return EXIT_FAIL_OPTION; } } + + if (!(xdp_flags & XDP_FLAGS_SKB_MODE)) + xdp_flags |= XDP_FLAGS_DRV_MODE; + /* Required option */ if (ifindex == -1) { fprintf(stderr, "ERR: required option --dev missing\n"); diff --git a/samples/bpf/xdp_sample_pkts_user.c b/samples/bpf/xdp_sample_pkts_user.c index a5760e8bf2c44..8c1af1b7372d4 100644 --- a/samples/bpf/xdp_sample_pkts_user.c +++ b/samples/bpf/xdp_sample_pkts_user.c @@ -52,13 +52,13 @@ static int do_detach(int idx, const char *name) __u32 curr_prog_id = 0; int err = 0; - err = bpf_get_link_xdp_id(idx, &curr_prog_id, 0); + err = bpf_get_link_xdp_id(idx, &curr_prog_id, xdp_flags); if (err) { printf("bpf_get_link_xdp_id failed\n"); return err; } if (prog_id == curr_prog_id) { - err = bpf_set_link_xdp_fd(idx, -1, 0); + err = bpf_set_link_xdp_fd(idx, -1, xdp_flags); if (err < 0) printf("ERROR: failed to detach prog from %s\n", name); } else if (!curr_prog_id) { @@ -115,7 +115,7 @@ int main(int argc, char **argv) .prog_type = BPF_PROG_TYPE_XDP, }; struct perf_buffer_opts pb_opts = {}; - const char *optstr = "F"; + const char *optstr = "FS"; int prog_fd, map_fd, opt; struct bpf_object *obj; struct bpf_map *map; @@ -127,12 +127,18 @@ int main(int argc, char **argv) case 'F': xdp_flags &= ~XDP_FLAGS_UPDATE_IF_NOEXIST; break; + case 'S': + xdp_flags |= XDP_FLAGS_SKB_MODE; + break; default: usage(basename(argv[0])); return 1; } } + if (!(xdp_flags & XDP_FLAGS_SKB_MODE)) + xdp_flags |= XDP_FLAGS_DRV_MODE; + if (optind == argc) { usage(basename(argv[0])); return 1; diff --git a/samples/bpf/xdp_tx_iptunnel_user.c b/samples/bpf/xdp_tx_iptunnel_user.c index 2fe4c7f5ffe5e..5f33b55300323 100644 --- a/samples/bpf/xdp_tx_iptunnel_user.c +++ b/samples/bpf/xdp_tx_iptunnel_user.c @@ -231,7 +231,7 @@ int main(int argc, char **argv) xdp_flags |= XDP_FLAGS_SKB_MODE; break; case 'N': - xdp_flags |= XDP_FLAGS_DRV_MODE; + /* default, set below */ break; case 'F': xdp_flags &= ~XDP_FLAGS_UPDATE_IF_NOEXIST; @@ -243,6 +243,9 @@ int main(int argc, char **argv) opt_flags[opt] = 0; } + if (!(xdp_flags & XDP_FLAGS_SKB_MODE)) + xdp_flags |= XDP_FLAGS_DRV_MODE; + for (i = 0; i < strlen(optstr); i++) { if (opt_flags[(unsigned int)optstr[i]]) { fprintf(stderr, "Missing argument -%c\n", optstr[i]); diff --git a/samples/bpf/xdpsock_user.c b/samples/bpf/xdpsock_user.c index a15480010828f..e7829e5baaffd 100644 --- a/samples/bpf/xdpsock_user.c +++ b/samples/bpf/xdpsock_user.c @@ -440,7 +440,7 @@ static void parse_command_line(int argc, char **argv) opt_xdp_bind_flags |= XDP_COPY; break; case 'N': - opt_xdp_flags |= XDP_FLAGS_DRV_MODE; + /* default, set below */ break; case 'n': opt_interval = atoi(optarg); @@ -474,6 +474,9 @@ static void parse_command_line(int argc, char **argv) } } + if (!(opt_xdp_flags & XDP_FLAGS_SKB_MODE)) + opt_xdp_flags |= XDP_FLAGS_DRV_MODE; + opt_ifindex = if_nametoindex(opt_if); if (!opt_ifindex) { fprintf(stderr, "ERROR: interface \"%s\" does not exist\n", -- GitLab From 475feec0c41ad71cb7d02f0310e56256606b57c5 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 16 Dec 2019 16:12:24 +0100 Subject: [PATCH 0738/1437] ALSA: hda - Downgrade error message for single-cmd fallback We made the error message for the CORB/RIRB communication clearer by upgrading to dev_WARN() so that user can notice better. But this struck us like a boomerang: now it caught syzbot and reported back as a fatal issue although it's not really any too serious bug that worth for stopping the whole system. OK, OK, let's be softy, downgrade it to the standard dev_err() again. Fixes: dd65f7e19c69 ("ALSA: hda - Show the fatal CORB/RIRB error more clearly") Reported-by: syzbot+b3028ac3933f5c466389@syzkaller.appspotmail.com Link: https://lore.kernel.org/r/20191216151224.30013-1-tiwai@suse.de Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_controller.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/pci/hda/hda_controller.c b/sound/pci/hda/hda_controller.c index 2f3b7a35f2d9c..ba56b59b3e178 100644 --- a/sound/pci/hda/hda_controller.c +++ b/sound/pci/hda/hda_controller.c @@ -883,7 +883,7 @@ static int azx_rirb_get_response(struct hdac_bus *bus, unsigned int addr, return -EAGAIN; /* give a chance to retry */ } - dev_WARN(chip->card->dev, + dev_err(chip->card->dev, "azx_get_response timeout, switching to single_cmd mode: last cmd=0x%08x\n", bus->last_cmd[addr]); chip->single_cmd = 1; -- GitLab From c09982f8e2bae80a66232630ec4ba50afacea486 Mon Sep 17 00:00:00 2001 From: Sudip Mukherjee Date: Sat, 7 Dec 2019 11:14:40 +0000 Subject: [PATCH 0739/1437] libtraceevent: Allow custom libdir path When I use prefix=/usr and try to install libtraceevent in my laptop it tries to install in /usr/lib64. I am not having any folder as /usr/lib64 and also the debian policy doesnot allow installing in /usr/lib64. It should be in /usr/lib/x86_64-linux-gnu/. Quote: No package for a 64 bit architecture may install files in /usr/lib64/ or in a subdirectory of it. ref: https://www.debian.org/doc/debian-policy/ch-opersys.html Make it more flexible by allowing to mention libdir_relative while installing so that distros can mention the path according to their policy or use the default one. Signed-off-by: Sudipm Mukherjee Reviewed-by: Steven Rostedt (VMware) Cc: Sudipm Mukherjee Cc: linux-trace-devel@vger.kernel.org Link: http://lore.kernel.org/lkml/20191207111440.6574-1-sudipm.mukherjee@gmail.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/lib/traceevent/Makefile | 5 +++-- tools/lib/traceevent/plugins/Makefile | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/tools/lib/traceevent/Makefile b/tools/lib/traceevent/Makefile index c5a03356a9995..c874c017c6369 100644 --- a/tools/lib/traceevent/Makefile +++ b/tools/lib/traceevent/Makefile @@ -39,11 +39,12 @@ DESTDIR_SQ = '$(subst ','\'',$(DESTDIR))' LP64 := $(shell echo __LP64__ | ${CC} ${CFLAGS} -E -x c - | tail -n 1) ifeq ($(LP64), 1) - libdir_relative = lib64 + libdir_relative_temp = lib64 else - libdir_relative = lib + libdir_relative_temp = lib endif +libdir_relative ?= $(libdir_relative_temp) prefix ?= /usr/local libdir = $(prefix)/$(libdir_relative) man_dir = $(prefix)/share/man diff --git a/tools/lib/traceevent/plugins/Makefile b/tools/lib/traceevent/plugins/Makefile index f440989fa55e4..349bb81482abb 100644 --- a/tools/lib/traceevent/plugins/Makefile +++ b/tools/lib/traceevent/plugins/Makefile @@ -32,11 +32,12 @@ DESTDIR_SQ = '$(subst ','\'',$(DESTDIR))' LP64 := $(shell echo __LP64__ | ${CC} ${CFLAGS} -E -x c - | tail -n 1) ifeq ($(LP64), 1) - libdir_relative = lib64 + libdir_relative_tmp = lib64 else - libdir_relative = lib + libdir_relative_tmp = lib endif +libdir_relative ?= $(libdir_relative_tmp) prefix ?= /usr/local libdir = $(prefix)/$(libdir_relative) -- GitLab From 28396b7df09b9565f404591c9945eac43526cb3f Mon Sep 17 00:00:00 2001 From: Ed Maste Date: Thu, 12 Dec 2019 14:34:46 +0000 Subject: [PATCH 0740/1437] perf vendor events s390: Fix counter long description for DTLB1_GPAGE_WRITES The cf_z13 counter DTLB1_GPAGE_WRITES included a prefix 'Counter:132\tName:'. This is incorrect; remove the prefix as with 7fcfa9a2d9 for cf_z14. Signed-off-by: Ed Maste Cc: Alexander Shishkin Cc: Greentime Hu Cc: Jiri Olsa Cc: Mark Rutland Cc: Namhyung Kim Cc: Nick Hu Cc: Peter Zijlstra Cc: Thomas Richter Cc: Vincent Chen Link: http://lore.kernel.org/lkml/20191212143446.88582-1-emaste@freefall.freebsd.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/pmu-events/arch/s390/cf_z13/extended.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/perf/pmu-events/arch/s390/cf_z13/extended.json b/tools/perf/pmu-events/arch/s390/cf_z13/extended.json index 436ce33f1182e..5da8296b667e7 100644 --- a/tools/perf/pmu-events/arch/s390/cf_z13/extended.json +++ b/tools/perf/pmu-events/arch/s390/cf_z13/extended.json @@ -32,7 +32,7 @@ "EventCode": "132", "EventName": "DTLB1_GPAGE_WRITES", "BriefDescription": "DTLB1 Two-Gigabyte Page Writes", - "PublicDescription": "Counter:132 Name:DTLB1_GPAGE_WRITES A translation entry has been written to the Level-1 Data Translation Lookaside Buffer for a two-gigabyte page." + "PublicDescription": "A translation entry has been written to the Level-1 Data Translation Lookaside Buffer for a two-gigabyte page." }, { "Unit": "CPU-M-CF", -- GitLab From 58b3bafff8257c6946df5d6aeb215b8ac839ed2a Mon Sep 17 00:00:00 2001 From: Ed Maste Date: Thu, 12 Dec 2019 14:53:46 +0000 Subject: [PATCH 0741/1437] perf vendor events s390: Remove name from L1D_RO_EXCL_WRITES description In 7fcfa9a2d9 an unintended prefix "Counter:18 Name:" was removed from the description for L1D_RO_EXCL_WRITES, but the extra name remained in the description. Remove it too. Fixes: 7fcfa9a2d9a7 ("perf list: Fix s390 counter long description for L1D_RO_EXCL_WRITES") Signed-off-by: Ed Maste Cc: Alexander Shishkin Cc: Greentime Hu Cc: Jiri Olsa Cc: Mark Rutland Cc: Namhyung Kim Cc: Nick Hu Cc: Peter Zijlstra Cc: Thomas Richter Cc: Vincent Chen Link: http://lore.kernel.org/lkml/20191212145346.5026-1-emaste@freefall.freebsd.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/pmu-events/arch/s390/cf_z14/extended.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/perf/pmu-events/arch/s390/cf_z14/extended.json b/tools/perf/pmu-events/arch/s390/cf_z14/extended.json index 68618152ea2c6..89e070727e1bd 100644 --- a/tools/perf/pmu-events/arch/s390/cf_z14/extended.json +++ b/tools/perf/pmu-events/arch/s390/cf_z14/extended.json @@ -4,7 +4,7 @@ "EventCode": "128", "EventName": "L1D_RO_EXCL_WRITES", "BriefDescription": "L1D Read-only Exclusive Writes", - "PublicDescription": "L1D_RO_EXCL_WRITES A directory write to the Level-1 Data cache where the line was originally in a Read-Only state in the cache but has been updated to be in the Exclusive state that allows stores to the cache line" + "PublicDescription": "A directory write to the Level-1 Data cache where the line was originally in a Read-Only state in the cache but has been updated to be in the Exclusive state that allows stores to the cache line" }, { "Unit": "CPU-M-CF", -- GitLab From 7de7de7ca0ae0fc70515ee3154af33af75edae2c Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Sun, 15 Dec 2019 19:50:23 -0800 Subject: [PATCH 0742/1437] Fix root mounting with no mount options MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The "trivial conversion" in commit cccaa5e33525 ("init: use do_mount() instead of ksys_mount()") was totally broken, since it didn't handle the case of a NULL mount data pointer. And while I had "tested" it (and presumably Dominik had too) that bug was hidden by me having options. Cc: Dominik Brodowski Cc: Arnd Bergmann Reported-by: Ondřej Jirman Reported-by: Guenter Roeck Reported-by: Naresh Kamboju Reported-and-tested-by: Borislav Petkov Tested-by: Chris Clayton Tested-by: Eric Biggers Tested-by: Geert Uytterhoeven Tested-by: Guido Günther Signed-off-by: Linus Torvalds --- init/do_mounts.c | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/init/do_mounts.c b/init/do_mounts.c index f55cbd9cb8183..0ae9cc22f2ae2 100644 --- a/init/do_mounts.c +++ b/init/do_mounts.c @@ -391,17 +391,19 @@ static int __init do_mount_root(const char *name, const char *fs, const int flags, const void *data) { struct super_block *s; - char *data_page; - struct page *p; + struct page *p = NULL; + char *data_page = NULL; int ret; - /* do_mount() requires a full page as fifth argument */ - p = alloc_page(GFP_KERNEL); - if (!p) - return -ENOMEM; - - data_page = page_address(p); - strncpy(data_page, data, PAGE_SIZE - 1); + if (data) { + /* do_mount() requires a full page as fifth argument */ + p = alloc_page(GFP_KERNEL); + if (!p) + return -ENOMEM; + data_page = page_address(p); + /* zero-pad. do_mount() will make sure it's terminated */ + strncpy(data_page, data, PAGE_SIZE); + } ret = do_mount(name, "/root", fs, flags, data_page); if (ret) @@ -417,7 +419,8 @@ static int __init do_mount_root(const char *name, const char *fs, MAJOR(ROOT_DEV), MINOR(ROOT_DEV)); out: - put_page(p); + if (p) + put_page(p); return ret; } -- GitLab From 4c80ba392bf603d468ea827d902f8e7b2505fbf4 Mon Sep 17 00:00:00 2001 From: Eric Auger Date: Tue, 26 Nov 2019 18:54:13 +0100 Subject: [PATCH 0743/1437] iommu: fix KASAN use-after-free in iommu_insert_resv_region In case the new region gets merged into another one, the nr list node is freed. Checking its type while completing the merge algorithm leads to a use-after-free. Use new->type instead. Fixes: 4dbd258ff63e ("iommu: Revisit iommu_insert_resv_region() implementation") Signed-off-by: Eric Auger Reported-by: Qian Cai Reviewed-by: Jerry Snitselaar Cc: Stable #v5.3+ Signed-off-by: Linus Torvalds --- drivers/iommu/iommu.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c index db7bfd4f2d20e..1c3f2a3035c16 100644 --- a/drivers/iommu/iommu.c +++ b/drivers/iommu/iommu.c @@ -312,8 +312,8 @@ int iommu_insert_resv_region(struct iommu_resv_region *new, list_for_each_entry_safe(iter, tmp, regions, list) { phys_addr_t top_end, iter_end = iter->start + iter->length - 1; - /* no merge needed on elements of different types than @nr */ - if (iter->type != nr->type) { + /* no merge needed on elements of different types than @new */ + if (iter->type != new->type) { list_move_tail(&iter->list, &stack); continue; } -- GitLab From e0ff126ee7ad405c1ef531f9f3db92929de4f20f Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Mon, 16 Dec 2019 11:54:32 +0100 Subject: [PATCH 0744/1437] pipe: Fix bogus dereference in iov_iter_alignment() We cannot look at 'i->pipe' unless we know the iter is a pipe. Move the ring_size load to a branch in iov_iter_alignment() where we've already checked the iter is a pipe to avoid bogus dereference. Reported-by: syzbot+bea68382bae9490e7dd6@syzkaller.appspotmail.com Fixes: 8cefc107ca54 ("pipe: Use head and tail pointers for the ring, not cursor and length") Signed-off-by: Jan Kara Signed-off-by: Al Viro --- lib/iov_iter.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/iov_iter.c b/lib/iov_iter.c index fb29c02c6a3c3..51595bf3af850 100644 --- a/lib/iov_iter.c +++ b/lib/iov_iter.c @@ -1222,11 +1222,12 @@ EXPORT_SYMBOL(iov_iter_discard); unsigned long iov_iter_alignment(const struct iov_iter *i) { - unsigned int p_mask = i->pipe->ring_size - 1; unsigned long res = 0; size_t size = i->count; if (unlikely(iov_iter_is_pipe(i))) { + unsigned int p_mask = i->pipe->ring_size - 1; + if (size && i->iov_offset && allocated(&i->pipe->bufs[i->head & p_mask])) return size | i->iov_offset; return size; -- GitLab From 6956eb33abb5deab2cd916b4c31226b57736bc3c Mon Sep 17 00:00:00 2001 From: Alexandre Belloni Date: Thu, 28 Nov 2019 11:25:31 +0100 Subject: [PATCH 0745/1437] clk: at91: fix possible deadlock MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Lockdep warns about a possible circular locking dependency because using syscon_node_to_regmap() will make the created regmap get and enable the first clock it can parse from the device tree. This clock is not needed to access the registers and should not be enabled at that time. Use the recently introduced device_node_to_regmap to solve that as it looks up the regmap in the same list but doesn't care about the clocks. Reported-by: Michał Mirosław Signed-off-by: Alexandre Belloni Link: https://lkml.kernel.org/r/20191128102531.817549-1-alexandre.belloni@bootlin.com Tested-by: Michał Mirosław Signed-off-by: Stephen Boyd --- drivers/clk/at91/at91sam9260.c | 2 +- drivers/clk/at91/at91sam9rl.c | 2 +- drivers/clk/at91/at91sam9x5.c | 2 +- drivers/clk/at91/pmc.c | 2 +- drivers/clk/at91/sama5d2.c | 2 +- drivers/clk/at91/sama5d4.c | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/clk/at91/at91sam9260.c b/drivers/clk/at91/at91sam9260.c index 0aabe49aed094..a9d4234758d7d 100644 --- a/drivers/clk/at91/at91sam9260.c +++ b/drivers/clk/at91/at91sam9260.c @@ -348,7 +348,7 @@ static void __init at91sam926x_pmc_setup(struct device_node *np, return; mainxtal_name = of_clk_get_parent_name(np, i); - regmap = syscon_node_to_regmap(np); + regmap = device_node_to_regmap(np); if (IS_ERR(regmap)) return; diff --git a/drivers/clk/at91/at91sam9rl.c b/drivers/clk/at91/at91sam9rl.c index 0ac34cdaa1069..77fe83a73bf48 100644 --- a/drivers/clk/at91/at91sam9rl.c +++ b/drivers/clk/at91/at91sam9rl.c @@ -83,7 +83,7 @@ static void __init at91sam9rl_pmc_setup(struct device_node *np) return; mainxtal_name = of_clk_get_parent_name(np, i); - regmap = syscon_node_to_regmap(np); + regmap = device_node_to_regmap(np); if (IS_ERR(regmap)) return; diff --git a/drivers/clk/at91/at91sam9x5.c b/drivers/clk/at91/at91sam9x5.c index 0855f3a80cc79..086cf0b4955c2 100644 --- a/drivers/clk/at91/at91sam9x5.c +++ b/drivers/clk/at91/at91sam9x5.c @@ -146,7 +146,7 @@ static void __init at91sam9x5_pmc_setup(struct device_node *np, return; mainxtal_name = of_clk_get_parent_name(np, i); - regmap = syscon_node_to_regmap(np); + regmap = device_node_to_regmap(np); if (IS_ERR(regmap)) return; diff --git a/drivers/clk/at91/pmc.c b/drivers/clk/at91/pmc.c index 0b03cfae3a9dd..b71515acdec1f 100644 --- a/drivers/clk/at91/pmc.c +++ b/drivers/clk/at91/pmc.c @@ -275,7 +275,7 @@ static int __init pmc_register_ops(void) np = of_find_matching_node(NULL, sama5d2_pmc_dt_ids); - pmcreg = syscon_node_to_regmap(np); + pmcreg = device_node_to_regmap(np); if (IS_ERR(pmcreg)) return PTR_ERR(pmcreg); diff --git a/drivers/clk/at91/sama5d2.c b/drivers/clk/at91/sama5d2.c index 0de1108737db9..ff7e3f727082e 100644 --- a/drivers/clk/at91/sama5d2.c +++ b/drivers/clk/at91/sama5d2.c @@ -162,7 +162,7 @@ static void __init sama5d2_pmc_setup(struct device_node *np) return; mainxtal_name = of_clk_get_parent_name(np, i); - regmap = syscon_node_to_regmap(np); + regmap = device_node_to_regmap(np); if (IS_ERR(regmap)) return; diff --git a/drivers/clk/at91/sama5d4.c b/drivers/clk/at91/sama5d4.c index 25b156d4e645f..a6dee4a3b6e48 100644 --- a/drivers/clk/at91/sama5d4.c +++ b/drivers/clk/at91/sama5d4.c @@ -136,7 +136,7 @@ static void __init sama5d4_pmc_setup(struct device_node *np) return; mainxtal_name = of_clk_get_parent_name(np, i); - regmap = syscon_node_to_regmap(np); + regmap = device_node_to_regmap(np); if (IS_ERR(regmap)) return; -- GitLab From a2ea07465c8d7984cc6b8b1f0b3324f9b138094a Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Mon, 16 Dec 2019 17:49:00 +0100 Subject: [PATCH 0746/1437] bpf: Fix missing prog untrack in release_maps Commit da765a2f5993 ("bpf: Add poke dependency tracking for prog array maps") wrongly assumed that in case of prog load errors, we're cleaning up all program tracking via bpf_free_used_maps(). However, it can happen that we're still at the point where we didn't copy map pointers into the prog's aux section such that env->prog->aux->used_maps is still zero, running into a UAF. In such case, the verifier has similar release_maps() helper that drops references to used maps from its env. Consolidate the release code into __bpf_free_used_maps() and call it from all sides to fix it. Fixes: da765a2f5993 ("bpf: Add poke dependency tracking for prog array maps") Signed-off-by: Daniel Borkmann Signed-off-by: Alexei Starovoitov Acked-by: Yonghong Song Link: https://lore.kernel.org/bpf/1c2909484ca524ae9f55109b06f22b6213e76376.1576514756.git.daniel@iogearbox.net --- include/linux/bpf.h | 2 ++ kernel/bpf/core.c | 14 ++++++++++---- kernel/bpf/verifier.c | 14 ++------------ 3 files changed, 14 insertions(+), 16 deletions(-) diff --git a/include/linux/bpf.h b/include/linux/bpf.h index ac7de52915093..085a59afba857 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -818,6 +818,8 @@ struct bpf_prog * __must_check bpf_prog_inc_not_zero(struct bpf_prog *prog); void bpf_prog_put(struct bpf_prog *prog); int __bpf_prog_charge(struct user_struct *user, u32 pages); void __bpf_prog_uncharge(struct user_struct *user, u32 pages); +void __bpf_free_used_maps(struct bpf_prog_aux *aux, + struct bpf_map **used_maps, u32 len); void bpf_prog_free_id(struct bpf_prog *prog, bool do_idr_lock); void bpf_map_free_id(struct bpf_map *map, bool do_idr_lock); diff --git a/kernel/bpf/core.c b/kernel/bpf/core.c index 49e32acad7d80..6231858df7239 100644 --- a/kernel/bpf/core.c +++ b/kernel/bpf/core.c @@ -2048,18 +2048,24 @@ static void bpf_free_cgroup_storage(struct bpf_prog_aux *aux) } } -static void bpf_free_used_maps(struct bpf_prog_aux *aux) +void __bpf_free_used_maps(struct bpf_prog_aux *aux, + struct bpf_map **used_maps, u32 len) { struct bpf_map *map; - int i; + u32 i; bpf_free_cgroup_storage(aux); - for (i = 0; i < aux->used_map_cnt; i++) { - map = aux->used_maps[i]; + for (i = 0; i < len; i++) { + map = used_maps[i]; if (map->ops->map_poke_untrack) map->ops->map_poke_untrack(map, aux); bpf_map_put(map); } +} + +static void bpf_free_used_maps(struct bpf_prog_aux *aux) +{ + __bpf_free_used_maps(aux, aux->used_maps, aux->used_map_cnt); kfree(aux->used_maps); } diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 034ef81f935b0..a1acdce770709 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -8298,18 +8298,8 @@ next_insn: /* drop refcnt of maps used by the rejected program */ static void release_maps(struct bpf_verifier_env *env) { - enum bpf_cgroup_storage_type stype; - int i; - - for_each_cgroup_storage_type(stype) { - if (!env->prog->aux->cgroup_storage[stype]) - continue; - bpf_cgroup_storage_release(env->prog, - env->prog->aux->cgroup_storage[stype]); - } - - for (i = 0; i < env->used_map_cnt; i++) - bpf_map_put(env->used_maps[i]); + __bpf_free_used_maps(env->prog->aux, env->used_maps, + env->used_map_cnt); } /* convert pseudo BPF_LD_IMM64 into generic BPF_LD_IMM64 */ -- GitLab From dc3a2d254782dea8e23a55f46129105ecfe787fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Toke=20H=C3=B8iland-J=C3=B8rgensen?= Date: Mon, 16 Dec 2019 19:12:04 +0100 Subject: [PATCH 0747/1437] libbpf: Print hint about ulimit when getting permission denied error MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Probably the single most common error newcomers to XDP are stumped by is the 'permission denied' error they get when trying to load their program and 'ulimit -l' is set too low. For examples, see [0], [1]. Since the error code is UAPI, we can't change that. Instead, this patch adds a few heuristics in libbpf and outputs an additional hint if they are met: If an EPERM is returned on map create or program load, and geteuid() shows we are root, and the current RLIMIT_MEMLOCK is not infinity, we output a hint about raising 'ulimit -l' as an additional log line. [0] https://marc.info/?l=xdp-newbies&m=157043612505624&w=2 [1] https://github.com/xdp-project/xdp-tutorial/issues/86 Signed-off-by: Toke Høiland-Jørgensen Signed-off-by: Alexei Starovoitov Acked-by: Jesper Dangaard Brouer Acked-by: Yonghong Song Link: https://lore.kernel.org/bpf/20191216181204.724953-1-toke@redhat.com --- tools/lib/bpf/libbpf.c | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c index a2cc7313763a8..3fe42d6b0c2ff 100644 --- a/tools/lib/bpf/libbpf.c +++ b/tools/lib/bpf/libbpf.c @@ -41,6 +41,7 @@ #include #include #include +#include #include #include #include @@ -100,6 +101,32 @@ void libbpf_print(enum libbpf_print_level level, const char *format, ...) va_end(args); } +static void pr_perm_msg(int err) +{ + struct rlimit limit; + char buf[100]; + + if (err != -EPERM || geteuid() != 0) + return; + + err = getrlimit(RLIMIT_MEMLOCK, &limit); + if (err) + return; + + if (limit.rlim_cur == RLIM_INFINITY) + return; + + if (limit.rlim_cur < 1024) + snprintf(buf, sizeof(buf), "%lu bytes", limit.rlim_cur); + else if (limit.rlim_cur < 1024*1024) + snprintf(buf, sizeof(buf), "%.1f KiB", (double)limit.rlim_cur / 1024); + else + snprintf(buf, sizeof(buf), "%.1f MiB", (double)limit.rlim_cur / (1024*1024)); + + pr_warn("permission error while running as root; try raising 'ulimit -l'? current value: %s\n", + buf); +} + #define STRERR_BUFSIZE 128 /* Copied from tools/perf/util/util.h */ @@ -2983,6 +3010,7 @@ err_out: cp = libbpf_strerror_r(err, errmsg, sizeof(errmsg)); pr_warn("failed to create map (name: '%s'): %s(%d)\n", map->name, cp, err); + pr_perm_msg(err); for (j = 0; j < i; j++) zclose(obj->maps[j].fd); return err; @@ -4381,6 +4409,7 @@ retry_load: ret = -errno; cp = libbpf_strerror_r(errno, errmsg, sizeof(errmsg)); pr_warn("load bpf program failed: %s\n", cp); + pr_perm_msg(ret); if (log_buf && log_buf[0] != '\0') { ret = -LIBBPF_ERRNO__VERIFY; -- GitLab From dbd8f6bae6f4a98aa517aeda6e5276ed4a20f571 Mon Sep 17 00:00:00 2001 From: Andrii Nakryiko Date: Mon, 16 Dec 2019 10:38:29 -0800 Subject: [PATCH 0748/1437] libbpf: Add zlib as a dependency in pkg-config template List zlib as another dependency of libbpf in pkg-config template. Verified it is correctly resolved to proper -lz flag: $ make DESTDIR=/tmp/libbpf-install install $ pkg-config --libs /tmp/libbpf-install/usr/local/lib64/pkgconfig/libbpf.pc -L/usr/local/lib64 -lbpf $ pkg-config --libs --static /tmp/libbpf-install/usr/local/lib64/pkgconfig/libbpf.pc -L/usr/local/lib64 -lbpf -lelf -lz Fixes: 166750bc1dd2 ("libbpf: Support libbpf-provided extern variables") Signed-off-by: Andrii Nakryiko Signed-off-by: Alexei Starovoitov Acked-by: Yonghong Song Cc: Luca Boccassi Link: https://lore.kernel.org/bpf/20191216183830.3972964-1-andriin@fb.com --- tools/lib/bpf/libbpf.pc.template | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/lib/bpf/libbpf.pc.template b/tools/lib/bpf/libbpf.pc.template index ac17fcef2108e..b45ed534bdfb5 100644 --- a/tools/lib/bpf/libbpf.pc.template +++ b/tools/lib/bpf/libbpf.pc.template @@ -8,5 +8,5 @@ Name: libbpf Description: BPF library Version: @VERSION@ Libs: -L${libdir} -lbpf -Requires.private: libelf +Requires.private: libelf zlib Cflags: -I${includedir} -- GitLab From d7bd15a138aef3be227818aad9c501e43c89c8c5 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Mon, 16 Dec 2019 13:34:00 -0800 Subject: [PATCH 0749/1437] iocost: over-budget forced IOs should schedule async delay When over-budget IOs are force-issued through root cgroup, iocg_kick_delay() adjusts the async delay accordingly but doesn't actually schedule async throttle for the issuing task. This bug is pretty well masked because sooner or later the offending threads are gonna get directly throttled on regular IOs or have async delay scheduled by mem_cgroup_throttle_swaprate(). However, it can affect control quality on filesystem metadata heavy operations. Let's fix it by invoking blkcg_schedule_throttle() when iocg_kick_delay() says async delay is needed. Signed-off-by: Tejun Heo Fixes: 7caa47151ab2 ("blkcg: implement blk-iocost") Cc: stable@vger.kernel.org Reported-by: Josef Bacik Signed-off-by: Jens Axboe --- block/blk-iocost.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/block/blk-iocost.c b/block/blk-iocost.c index e01267f991839..27ca68621137a 100644 --- a/block/blk-iocost.c +++ b/block/blk-iocost.c @@ -1212,7 +1212,7 @@ static enum hrtimer_restart iocg_waitq_timer_fn(struct hrtimer *timer) return HRTIMER_NORESTART; } -static void iocg_kick_delay(struct ioc_gq *iocg, struct ioc_now *now, u64 cost) +static bool iocg_kick_delay(struct ioc_gq *iocg, struct ioc_now *now, u64 cost) { struct ioc *ioc = iocg->ioc; struct blkcg_gq *blkg = iocg_to_blkg(iocg); @@ -1229,11 +1229,11 @@ static void iocg_kick_delay(struct ioc_gq *iocg, struct ioc_now *now, u64 cost) /* clear or maintain depending on the overage */ if (time_before_eq64(vtime, now->vnow)) { blkcg_clear_delay(blkg); - return; + return false; } if (!atomic_read(&blkg->use_delay) && time_before_eq64(vtime, now->vnow + vmargin)) - return; + return false; /* use delay */ if (cost) { @@ -1250,10 +1250,11 @@ static void iocg_kick_delay(struct ioc_gq *iocg, struct ioc_now *now, u64 cost) oexpires = ktime_to_ns(hrtimer_get_softexpires(&iocg->delay_timer)); if (hrtimer_is_queued(&iocg->delay_timer) && abs(oexpires - expires) <= margin_ns / 4) - return; + return true; hrtimer_start_range_ns(&iocg->delay_timer, ns_to_ktime(expires), margin_ns / 4, HRTIMER_MODE_ABS); + return true; } static enum hrtimer_restart iocg_delay_timer_fn(struct hrtimer *timer) @@ -1739,7 +1740,9 @@ static void ioc_rqos_throttle(struct rq_qos *rqos, struct bio *bio) */ if (bio_issue_as_root_blkg(bio) || fatal_signal_pending(current)) { atomic64_add(abs_cost, &iocg->abs_vdebt); - iocg_kick_delay(iocg, &now, cost); + if (iocg_kick_delay(iocg, &now, cost)) + blkcg_schedule_throttle(rqos->q, + (bio->bi_opf & REQ_SWAP) == REQ_SWAP); return; } -- GitLab From 1c05839aa973cfae8c3db964a21f9c0eef8fcc21 Mon Sep 17 00:00:00 2001 From: Mike Christie Date: Sun, 8 Dec 2019 16:51:50 -0600 Subject: [PATCH 0750/1437] nbd: fix shutdown and recv work deadlock v2 This fixes a regression added with: commit e9e006f5fcf2bab59149cb38a48a4817c1b538b4 Author: Mike Christie Date: Sun Aug 4 14:10:06 2019 -0500 nbd: fix max number of supported devs where we can deadlock during device shutdown. The problem occurs if the recv_work's nbd_config_put occurs after nbd_start_device_ioctl has returned and the userspace app has droppped its reference via closing the device and running nbd_release. The recv_work nbd_config_put call would then drop the refcount to zero and try to destroy the config which would try to do destroy_workqueue from the recv work. This patch just has nbd_start_device_ioctl do a flush_workqueue when it wakes so we know after the ioctl returns running works have exited. This also fixes a possible race where we could try to reuse the device while old recv_works are still running. Cc: stable@vger.kernel.org Fixes: e9e006f5fcf2 ("nbd: fix max number of supported devs") Signed-off-by: Mike Christie Signed-off-by: Jens Axboe --- drivers/block/nbd.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/block/nbd.c b/drivers/block/nbd.c index 57532465fb83a..b4607dd961852 100644 --- a/drivers/block/nbd.c +++ b/drivers/block/nbd.c @@ -1296,10 +1296,10 @@ static int nbd_start_device_ioctl(struct nbd_device *nbd, struct block_device *b mutex_unlock(&nbd->config_lock); ret = wait_event_interruptible(config->recv_wq, atomic_read(&config->recv_threads) == 0); - if (ret) { + if (ret) sock_shutdown(nbd); - flush_workqueue(nbd->recv_workq); - } + flush_workqueue(nbd->recv_workq); + mutex_lock(&nbd->config_lock); nbd_bdev_reset(bdev); /* user requested, ignore socket errors */ -- GitLab From 10bc877c762339b8501a5b397137cf2f47cde575 Mon Sep 17 00:00:00 2001 From: Yangbo Lu Date: Thu, 12 Dec 2019 18:08:04 +0800 Subject: [PATCH 0751/1437] ptp_qoriq: check valid status before reading extts fifo For PTP timer supporting external trigger timestamp FIFO, there is a valid bit in TMR_STAT register indicating the timestamp is available. For PTP timer without FIFO, there is not TMR_STAT register. This patch is to check the valid bit for the FIFO before reading timestamp, and to avoid operating TMR_STAT register for PTP timer without the FIFO. Signed-off-by: Yangbo Lu Signed-off-by: David S. Miller --- drivers/ptp/ptp_qoriq.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/drivers/ptp/ptp_qoriq.c b/drivers/ptp/ptp_qoriq.c index a577218d1ab71..a3062cd4d174a 100644 --- a/drivers/ptp/ptp_qoriq.c +++ b/drivers/ptp/ptp_qoriq.c @@ -81,7 +81,7 @@ static int extts_clean_up(struct ptp_qoriq *ptp_qoriq, int index, struct ptp_clock_event event; void __iomem *reg_etts_l; void __iomem *reg_etts_h; - u32 valid, stat, lo, hi; + u32 valid, lo, hi; switch (index) { case 0: @@ -101,6 +101,10 @@ static int extts_clean_up(struct ptp_qoriq *ptp_qoriq, int index, event.type = PTP_CLOCK_EXTTS; event.index = index; + if (ptp_qoriq->extts_fifo_support) + if (!(ptp_qoriq->read(®s->ctrl_regs->tmr_stat) & valid)) + return 0; + do { lo = ptp_qoriq->read(reg_etts_l); hi = ptp_qoriq->read(reg_etts_h); @@ -111,8 +115,9 @@ static int extts_clean_up(struct ptp_qoriq *ptp_qoriq, int index, ptp_clock_event(ptp_qoriq->clock, &event); } - stat = ptp_qoriq->read(®s->ctrl_regs->tmr_stat); - } while (ptp_qoriq->extts_fifo_support && (stat & valid)); + if (!ptp_qoriq->extts_fifo_support) + break; + } while (ptp_qoriq->read(®s->ctrl_regs->tmr_stat) & valid); return 0; } -- GitLab From 9429439f59cd3b82a3e2732ead5363578de97a84 Mon Sep 17 00:00:00 2001 From: Yangbo Lu Date: Thu, 12 Dec 2019 18:08:05 +0800 Subject: [PATCH 0752/1437] ptp_qoriq: export extts_clean_up() function Export extts_clean_up() function so that dpaa2-ptp driver is able to reuse it. Signed-off-by: Yangbo Lu Signed-off-by: David S. Miller --- drivers/ptp/ptp_qoriq.c | 4 ++-- include/linux/fsl/ptp_qoriq.h | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/ptp/ptp_qoriq.c b/drivers/ptp/ptp_qoriq.c index a3062cd4d174a..b27c46ebfc8f2 100644 --- a/drivers/ptp/ptp_qoriq.c +++ b/drivers/ptp/ptp_qoriq.c @@ -74,8 +74,7 @@ static void set_fipers(struct ptp_qoriq *ptp_qoriq) ptp_qoriq->write(®s->fiper_regs->tmr_fiper2, ptp_qoriq->tmr_fiper2); } -static int extts_clean_up(struct ptp_qoriq *ptp_qoriq, int index, - bool update_event) +int extts_clean_up(struct ptp_qoriq *ptp_qoriq, int index, bool update_event) { struct ptp_qoriq_registers *regs = &ptp_qoriq->regs; struct ptp_clock_event event; @@ -121,6 +120,7 @@ static int extts_clean_up(struct ptp_qoriq *ptp_qoriq, int index, return 0; } +EXPORT_SYMBOL_GPL(extts_clean_up); /* * Interrupt service routine diff --git a/include/linux/fsl/ptp_qoriq.h b/include/linux/fsl/ptp_qoriq.h index 992bf9fa17298..b0b743563f432 100644 --- a/include/linux/fsl/ptp_qoriq.h +++ b/include/linux/fsl/ptp_qoriq.h @@ -192,6 +192,7 @@ int ptp_qoriq_settime(struct ptp_clock_info *ptp, const struct timespec64 *ts); int ptp_qoriq_enable(struct ptp_clock_info *ptp, struct ptp_clock_request *rq, int on); +int extts_clean_up(struct ptp_qoriq *ptp_qoriq, int index, bool update_event); #ifdef CONFIG_DEBUG_FS void ptp_qoriq_create_debugfs(struct ptp_qoriq *ptp_qoriq); void ptp_qoriq_remove_debugfs(struct ptp_qoriq *ptp_qoriq); -- GitLab From 17568c62cd2c9a8d75eb5da44b7df093819b6ebe Mon Sep 17 00:00:00 2001 From: Yangbo Lu Date: Thu, 12 Dec 2019 18:08:06 +0800 Subject: [PATCH 0753/1437] dpaa2-ptp: add external trigger event support This patch is to add external trigger event support for dpaa2-ptp driver with v2 dprtc_set_irq_mask() API. Signed-off-by: Yangbo Lu Signed-off-by: David S. Miller --- .../net/ethernet/freescale/dpaa2/dpaa2-ptp.c | 20 +++++++++++++++++++ .../net/ethernet/freescale/dpaa2/dprtc-cmd.h | 4 +++- drivers/net/ethernet/freescale/dpaa2/dprtc.h | 2 ++ 3 files changed, 25 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-ptp.c b/drivers/net/ethernet/freescale/dpaa2/dpaa2-ptp.c index a9503aea527f5..a0061e9299088 100644 --- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-ptp.c +++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-ptp.c @@ -27,6 +27,20 @@ static int dpaa2_ptp_enable(struct ptp_clock_info *ptp, mc_dev = to_fsl_mc_device(dev); switch (rq->type) { + case PTP_CLK_REQ_EXTTS: + switch (rq->extts.index) { + case 0: + bit = DPRTC_EVENT_ETS1; + break; + case 1: + bit = DPRTC_EVENT_ETS2; + break; + default: + return -EINVAL; + } + if (on) + extts_clean_up(ptp_qoriq, rq->extts.index, false); + break; case PTP_CLK_REQ_PPS: bit = DPRTC_EVENT_PPS; break; @@ -96,6 +110,12 @@ static irqreturn_t dpaa2_ptp_irq_handler_thread(int irq, void *priv) ptp_clock_event(ptp_qoriq->clock, &event); } + if (status & DPRTC_EVENT_ETS1) + extts_clean_up(ptp_qoriq, 0, true); + + if (status & DPRTC_EVENT_ETS2) + extts_clean_up(ptp_qoriq, 1, true); + err = dprtc_clear_irq_status(mc_dev->mc_io, 0, mc_dev->mc_handle, DPRTC_IRQ_INDEX, status); if (unlikely(err)) { diff --git a/drivers/net/ethernet/freescale/dpaa2/dprtc-cmd.h b/drivers/net/ethernet/freescale/dpaa2/dprtc-cmd.h index 4ac05bfef3380..96ffeb948f088 100644 --- a/drivers/net/ethernet/freescale/dpaa2/dprtc-cmd.h +++ b/drivers/net/ethernet/freescale/dpaa2/dprtc-cmd.h @@ -9,9 +9,11 @@ /* Command versioning */ #define DPRTC_CMD_BASE_VERSION 1 +#define DPRTC_CMD_VERSION_2 2 #define DPRTC_CMD_ID_OFFSET 4 #define DPRTC_CMD(id) (((id) << DPRTC_CMD_ID_OFFSET) | DPRTC_CMD_BASE_VERSION) +#define DPRTC_CMD_V2(id) (((id) << DPRTC_CMD_ID_OFFSET) | DPRTC_CMD_VERSION_2) /* Command IDs */ #define DPRTC_CMDID_CLOSE DPRTC_CMD(0x800) @@ -19,7 +21,7 @@ #define DPRTC_CMDID_SET_IRQ_ENABLE DPRTC_CMD(0x012) #define DPRTC_CMDID_GET_IRQ_ENABLE DPRTC_CMD(0x013) -#define DPRTC_CMDID_SET_IRQ_MASK DPRTC_CMD(0x014) +#define DPRTC_CMDID_SET_IRQ_MASK DPRTC_CMD_V2(0x014) #define DPRTC_CMDID_GET_IRQ_MASK DPRTC_CMD(0x015) #define DPRTC_CMDID_GET_IRQ_STATUS DPRTC_CMD(0x016) #define DPRTC_CMDID_CLEAR_IRQ_STATUS DPRTC_CMD(0x017) diff --git a/drivers/net/ethernet/freescale/dpaa2/dprtc.h b/drivers/net/ethernet/freescale/dpaa2/dprtc.h index 311c184e1aef6..05c413719e555 100644 --- a/drivers/net/ethernet/freescale/dpaa2/dprtc.h +++ b/drivers/net/ethernet/freescale/dpaa2/dprtc.h @@ -20,6 +20,8 @@ struct fsl_mc_io; #define DPRTC_IRQ_INDEX 0 #define DPRTC_EVENT_PPS 0x08000000 +#define DPRTC_EVENT_ETS1 0x00800000 +#define DPRTC_EVENT_ETS2 0x00400000 int dprtc_open(struct fsl_mc_io *mc_io, u32 cmd_flags, -- GitLab From f325937735498afb054a0195291bbf68d0b60be5 Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Fri, 13 Dec 2019 16:53:37 +0100 Subject: [PATCH 0754/1437] r8169: check that Realtek PHY driver module is loaded Some users complained about problems with r8169 and it turned out that the generic PHY driver was used instead instead of the dedicated one. In all cases reason was that r8169.ko was in initramfs, but realtek.ko not. Manually adding realtek.ko to initramfs fixed the issues. Root cause seems to be that tools like dracut and genkernel don't consider softdeps. Add a check for loaded Realtek PHY driver module and provide the user with a hint if it's not loaded. Signed-off-by: Heiner Kallweit Signed-off-by: David S. Miller --- drivers/net/ethernet/realtek/r8169_main.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/net/ethernet/realtek/r8169_main.c b/drivers/net/ethernet/realtek/r8169_main.c index 2cfaa22703192..a887b685d0704 100644 --- a/drivers/net/ethernet/realtek/r8169_main.c +++ b/drivers/net/ethernet/realtek/r8169_main.c @@ -6825,6 +6825,15 @@ static int rtl_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) int chipset, region; int jumbo_max, rc; + /* Some tools for creating an initramfs don't consider softdeps, then + * r8169.ko may be in initramfs, but realtek.ko not. Then the generic + * PHY driver is used that doesn't work with most chip versions. + */ + if (!driver_find("RTL8201CP Ethernet", &mdio_bus_type)) { + dev_err(&pdev->dev, "realtek.ko not loaded, maybe it needs to be added to initramfs?\n"); + return -ENOENT; + } + dev = devm_alloc_etherdev(&pdev->dev, sizeof (*tp)); if (!dev) return -ENOMEM; -- GitLab From df18fa14629ae3ae10f51577642113ccc8ce87d1 Mon Sep 17 00:00:00 2001 From: Stefano Garzarella Date: Fri, 13 Dec 2019 19:48:00 +0100 Subject: [PATCH 0755/1437] vsock/virtio: fix null-pointer dereference in virtio_transport_recv_listen() With multi-transport support, listener sockets are not bound to any transport. So, calling virtio_transport_reset(), when an error occurs, on a listener socket produces the following null-pointer dereference: BUG: kernel NULL pointer dereference, address: 00000000000000e8 #PF: supervisor read access in kernel mode #PF: error_code(0x0000) - not-present page PGD 0 P4D 0 Oops: 0000 [#1] SMP PTI CPU: 0 PID: 20 Comm: kworker/0:1 Not tainted 5.5.0-rc1-ste-00003-gb4be21f316ac-dirty #56 Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS ?-20190727_073836-buildvm-ppc64le-16.ppc.fedoraproject.org-3.fc31 04/01/2014 Workqueue: virtio_vsock virtio_transport_rx_work [vmw_vsock_virtio_transport] RIP: 0010:virtio_transport_send_pkt_info+0x20/0x130 [vmw_vsock_virtio_transport_common] Code: 1f 84 00 00 00 00 00 0f 1f 00 55 48 89 e5 41 57 41 56 41 55 49 89 f5 41 54 49 89 fc 53 48 83 ec 10 44 8b 76 20 e8 c0 ba fe ff <48> 8b 80 e8 00 00 00 e8 64 e3 7d c1 45 8b 45 00 41 8b 8c 24 d4 02 RSP: 0018:ffffc900000b7d08 EFLAGS: 00010282 RAX: 0000000000000000 RBX: ffff88807bf12728 RCX: 0000000000000000 RDX: ffff88807bf12700 RSI: ffffc900000b7d50 RDI: ffff888035c84000 RBP: ffffc900000b7d40 R08: ffff888035c84000 R09: ffffc900000b7d08 R10: ffff8880781de800 R11: 0000000000000018 R12: ffff888035c84000 R13: ffffc900000b7d50 R14: 0000000000000000 R15: ffff88807bf12724 FS: 0000000000000000(0000) GS:ffff88807dc00000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 00000000000000e8 CR3: 00000000790f4004 CR4: 0000000000160ef0 DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400 Call Trace: virtio_transport_reset+0x59/0x70 [vmw_vsock_virtio_transport_common] virtio_transport_recv_pkt+0x5bb/0xe50 [vmw_vsock_virtio_transport_common] ? detach_buf_split+0xf1/0x130 virtio_transport_rx_work+0xba/0x130 [vmw_vsock_virtio_transport] process_one_work+0x1c0/0x300 worker_thread+0x45/0x3c0 kthread+0xfc/0x130 ? current_work+0x40/0x40 ? kthread_park+0x90/0x90 ret_from_fork+0x35/0x40 Modules linked in: sunrpc kvm_intel kvm vmw_vsock_virtio_transport vmw_vsock_virtio_transport_common irqbypass vsock virtio_rng rng_core CR2: 00000000000000e8 ---[ end trace e75400e2ea2fa824 ]--- This happens because virtio_transport_reset() calls virtio_transport_send_pkt_info() that can be used only on connecting/connected sockets. This patch fixes the issue, using virtio_transport_reset_no_sock() instead of virtio_transport_reset() when we are handling a listener socket. Fixes: c0cfa2d8a788 ("vsock: add multi-transports support") Signed-off-by: Stefano Garzarella Signed-off-by: David S. Miller --- net/vmw_vsock/virtio_transport_common.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/net/vmw_vsock/virtio_transport_common.c b/net/vmw_vsock/virtio_transport_common.c index e5ea29c6bca7e..f5991006190ee 100644 --- a/net/vmw_vsock/virtio_transport_common.c +++ b/net/vmw_vsock/virtio_transport_common.c @@ -1021,18 +1021,18 @@ virtio_transport_recv_listen(struct sock *sk, struct virtio_vsock_pkt *pkt, int ret; if (le16_to_cpu(pkt->hdr.op) != VIRTIO_VSOCK_OP_REQUEST) { - virtio_transport_reset(vsk, pkt); + virtio_transport_reset_no_sock(t, pkt); return -EINVAL; } if (sk_acceptq_is_full(sk)) { - virtio_transport_reset(vsk, pkt); + virtio_transport_reset_no_sock(t, pkt); return -ENOMEM; } child = vsock_create_connected(sk); if (!child) { - virtio_transport_reset(vsk, pkt); + virtio_transport_reset_no_sock(t, pkt); return -ENOMEM; } @@ -1054,7 +1054,7 @@ virtio_transport_recv_listen(struct sock *sk, struct virtio_vsock_pkt *pkt, */ if (ret || vchild->transport != &t->transport) { release_sock(child); - virtio_transport_reset(vsk, pkt); + virtio_transport_reset_no_sock(t, pkt); sock_put(child); return ret; } -- GitLab From 4aaf596148f4b518244cf2b02fafc53ad586dd6b Mon Sep 17 00:00:00 2001 From: Stefano Garzarella Date: Fri, 13 Dec 2019 19:48:01 +0100 Subject: [PATCH 0756/1437] vsock/virtio: add WARN_ON check on virtio_transport_get_ops() virtio_transport_get_ops() and virtio_transport_send_pkt_info() can only be used on connecting/connected sockets, since a socket assigned to a transport is required. This patch adds a WARN_ON() on virtio_transport_get_ops() to check this requirement, a comment and a returned error on virtio_transport_send_pkt_info(), Signed-off-by: Stefano Garzarella Signed-off-by: David S. Miller --- net/vmw_vsock/virtio_transport_common.c | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/net/vmw_vsock/virtio_transport_common.c b/net/vmw_vsock/virtio_transport_common.c index f5991006190ee..6abec3fc81d1c 100644 --- a/net/vmw_vsock/virtio_transport_common.c +++ b/net/vmw_vsock/virtio_transport_common.c @@ -34,6 +34,9 @@ virtio_transport_get_ops(struct vsock_sock *vsk) { const struct vsock_transport *t = vsock_core_get_transport(vsk); + if (WARN_ON(!t)) + return NULL; + return container_of(t, struct virtio_transport, transport); } @@ -161,15 +164,25 @@ void virtio_transport_deliver_tap_pkt(struct virtio_vsock_pkt *pkt) } EXPORT_SYMBOL_GPL(virtio_transport_deliver_tap_pkt); +/* This function can only be used on connecting/connected sockets, + * since a socket assigned to a transport is required. + * + * Do not use on listener sockets! + */ static int virtio_transport_send_pkt_info(struct vsock_sock *vsk, struct virtio_vsock_pkt_info *info) { u32 src_cid, src_port, dst_cid, dst_port; + const struct virtio_transport *t_ops; struct virtio_vsock_sock *vvs; struct virtio_vsock_pkt *pkt; u32 pkt_len = info->pkt_len; - src_cid = virtio_transport_get_ops(vsk)->transport.get_local_cid(); + t_ops = virtio_transport_get_ops(vsk); + if (unlikely(!t_ops)) + return -EFAULT; + + src_cid = t_ops->transport.get_local_cid(); src_port = vsk->local_addr.svm_port; if (!info->remote_cid) { dst_cid = vsk->remote_addr.svm_cid; @@ -202,7 +215,7 @@ static int virtio_transport_send_pkt_info(struct vsock_sock *vsk, virtio_transport_inc_tx_pkt(vvs, pkt); - return virtio_transport_get_ops(vsk)->send_pkt(pkt); + return t_ops->send_pkt(pkt); } static bool virtio_transport_inc_rx_pkt(struct virtio_vsock_sock *vvs, -- GitLab From 63cc54a6f0736a432b04308a74677ab0ba8a58ee Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Fri, 13 Dec 2019 12:00:27 -0800 Subject: [PATCH 0757/1437] net: dsa: b53: Fix egress flooding settings There were several issues with 53568438e381 ("net: dsa: b53: Add support for port_egress_floods callback") that resulted in breaking connectivity for standalone ports: - both user and CPU ports must allow unicast and multicast forwarding by default otherwise this just flat out breaks connectivity for standalone DSA ports - IP multicast is treated similarly as multicast, but has separate control registers - the UC, MC and IPMC lookup failure register offsets were wrong, and instead used bit values that are meaningful for the B53_IP_MULTICAST_CTRL register Fixes: 53568438e381 ("net: dsa: b53: Add support for port_egress_floods callback") Signed-off-by: Florian Fainelli Reviewed-by: Vivien Didelot Signed-off-by: David S. Miller --- drivers/net/dsa/b53/b53_common.c | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/drivers/net/dsa/b53/b53_common.c b/drivers/net/dsa/b53/b53_common.c index 36828f2100307..edacacfc9365e 100644 --- a/drivers/net/dsa/b53/b53_common.c +++ b/drivers/net/dsa/b53/b53_common.c @@ -347,7 +347,7 @@ static void b53_set_forwarding(struct b53_device *dev, int enable) * frames should be flooded or not. */ b53_read8(dev, B53_CTRL_PAGE, B53_IP_MULTICAST_CTRL, &mgmt); - mgmt |= B53_UC_FWD_EN | B53_MC_FWD_EN; + mgmt |= B53_UC_FWD_EN | B53_MC_FWD_EN | B53_IPMC_FWD_EN; b53_write8(dev, B53_CTRL_PAGE, B53_IP_MULTICAST_CTRL, mgmt); } @@ -526,6 +526,8 @@ int b53_enable_port(struct dsa_switch *ds, int port, struct phy_device *phy) cpu_port = dsa_to_port(ds, port)->cpu_dp->index; + b53_br_egress_floods(ds, port, true, true); + if (dev->ops->irq_enable) ret = dev->ops->irq_enable(dev, port); if (ret) @@ -641,6 +643,8 @@ static void b53_enable_cpu_port(struct b53_device *dev, int port) b53_write8(dev, B53_CTRL_PAGE, B53_PORT_CTRL(port), port_ctrl); b53_brcm_hdr_setup(dev->ds, port); + + b53_br_egress_floods(dev->ds, port, true, true); } static void b53_enable_mib(struct b53_device *dev) @@ -1821,19 +1825,26 @@ int b53_br_egress_floods(struct dsa_switch *ds, int port, struct b53_device *dev = ds->priv; u16 uc, mc; - b53_read16(dev, B53_CTRL_PAGE, B53_UC_FWD_EN, &uc); + b53_read16(dev, B53_CTRL_PAGE, B53_UC_FLOOD_MASK, &uc); if (unicast) uc |= BIT(port); else uc &= ~BIT(port); - b53_write16(dev, B53_CTRL_PAGE, B53_UC_FWD_EN, uc); + b53_write16(dev, B53_CTRL_PAGE, B53_UC_FLOOD_MASK, uc); + + b53_read16(dev, B53_CTRL_PAGE, B53_MC_FLOOD_MASK, &mc); + if (multicast) + mc |= BIT(port); + else + mc &= ~BIT(port); + b53_write16(dev, B53_CTRL_PAGE, B53_MC_FLOOD_MASK, mc); - b53_read16(dev, B53_CTRL_PAGE, B53_MC_FWD_EN, &mc); + b53_read16(dev, B53_CTRL_PAGE, B53_IPMC_FLOOD_MASK, &mc); if (multicast) mc |= BIT(port); else mc &= ~BIT(port); - b53_write16(dev, B53_CTRL_PAGE, B53_MC_FWD_EN, mc); + b53_write16(dev, B53_CTRL_PAGE, B53_IPMC_FLOOD_MASK, mc); return 0; -- GitLab From 1f1c1d7c89ee538f3e36b43098e95973f8fa37db Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Fri, 13 Dec 2019 21:24:27 +0100 Subject: [PATCH 0758/1437] ipv6: Annotate bitwise IPv6 dsfield pointer cast The sparse commit 6002ded74587 ("add a flag to warn on casts to/from bitwise pointers") introduced a check for non-direct casts from/to restricted datatypes (when -Wbitwise-pointer is enabled). This triggered a warning in ipv6_get_dsfield() because sparse doesn't know that the buffer already points to some data in the correct bitwise integer format. This was already fixed in ipv6_change_dsfield() by the __force attribute and can be fixed here the same way. Signed-off-by: Sven Eckelmann Signed-off-by: David S. Miller --- include/net/dsfield.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/net/dsfield.h b/include/net/dsfield.h index 1a245ee10c951..a59a57ffc5468 100644 --- a/include/net/dsfield.h +++ b/include/net/dsfield.h @@ -21,7 +21,7 @@ static inline __u8 ipv4_get_dsfield(const struct iphdr *iph) static inline __u8 ipv6_get_dsfield(const struct ipv6hdr *ipv6h) { - return ntohs(*(const __be16 *)ipv6h) >> 4; + return ntohs(*(__force const __be16 *)ipv6h) >> 4; } -- GitLab From 54e1f08bddbe63a3c0ae44f65df2c8b895003ef4 Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Fri, 13 Dec 2019 21:24:28 +0100 Subject: [PATCH 0759/1437] ipv6: Annotate ipv6_addr_is_* bitwise pointer casts The sparse commit 6002ded74587 ("add a flag to warn on casts to/from bitwise pointers") introduced a check for non-direct casts from/to restricted datatypes (when -Wbitwise-pointer is enabled). This triggered a warning in the 64 bit optimized ipv6_addr_is_*() functions because sparse doesn't know that the buffer already points to some data in the correct bitwise integer format. But these were correct and can therefore be marked with __force to signalize sparse an intended cast to a specific bitwise type. Signed-off-by: Sven Eckelmann Signed-off-by: David S. Miller --- include/net/addrconf.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/include/net/addrconf.h b/include/net/addrconf.h index 1bab88184d3c3..a088349dd94f2 100644 --- a/include/net/addrconf.h +++ b/include/net/addrconf.h @@ -437,7 +437,7 @@ static inline void addrconf_addr_solict_mult(const struct in6_addr *addr, static inline bool ipv6_addr_is_ll_all_nodes(const struct in6_addr *addr) { #if defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS) && BITS_PER_LONG == 64 - __be64 *p = (__be64 *)addr; + __be64 *p = (__force __be64 *)addr; return ((p[0] ^ cpu_to_be64(0xff02000000000000UL)) | (p[1] ^ cpu_to_be64(1))) == 0UL; #else return ((addr->s6_addr32[0] ^ htonl(0xff020000)) | @@ -449,7 +449,7 @@ static inline bool ipv6_addr_is_ll_all_nodes(const struct in6_addr *addr) static inline bool ipv6_addr_is_ll_all_routers(const struct in6_addr *addr) { #if defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS) && BITS_PER_LONG == 64 - __be64 *p = (__be64 *)addr; + __be64 *p = (__force __be64 *)addr; return ((p[0] ^ cpu_to_be64(0xff02000000000000UL)) | (p[1] ^ cpu_to_be64(2))) == 0UL; #else return ((addr->s6_addr32[0] ^ htonl(0xff020000)) | @@ -466,7 +466,7 @@ static inline bool ipv6_addr_is_isatap(const struct in6_addr *addr) static inline bool ipv6_addr_is_solict_mult(const struct in6_addr *addr) { #if defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS) && BITS_PER_LONG == 64 - __be64 *p = (__be64 *)addr; + __be64 *p = (__force __be64 *)addr; return ((p[0] ^ cpu_to_be64(0xff02000000000000UL)) | ((p[1] ^ cpu_to_be64(0x00000001ff000000UL)) & cpu_to_be64(0xffffffffff000000UL))) == 0UL; @@ -481,7 +481,7 @@ static inline bool ipv6_addr_is_solict_mult(const struct in6_addr *addr) static inline bool ipv6_addr_is_all_snoopers(const struct in6_addr *addr) { #if defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS) && BITS_PER_LONG == 64 - __be64 *p = (__be64 *)addr; + __be64 *p = (__force __be64 *)addr; return ((p[0] ^ cpu_to_be64(0xff02000000000000UL)) | (p[1] ^ cpu_to_be64(0x6a))) == 0UL; -- GitLab From b3c3fe8e9961c3c76f8c6c6d5e50336cb6b16b48 Mon Sep 17 00:00:00 2001 From: Yunsheng Lin Date: Sat, 14 Dec 2019 10:06:37 +0800 Subject: [PATCH 0760/1437] net: hns3: schedule hclgevf_service by using delayed workqueue Currently, a timer is defined to schedule hclgevf_service per second. To simplify the code, this patch uses the delayed work instead of timer to schedule hclgevf_serive. Signed-off-by: Yunsheng Lin Signed-off-by: Huazhong Tan Signed-off-by: David S. Miller --- .../hisilicon/hns3/hns3vf/hclgevf_main.c | 39 +++++++------------ .../hisilicon/hns3/hns3vf/hclgevf_main.h | 3 +- 2 files changed, 15 insertions(+), 27 deletions(-) diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.c b/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.c index 25d78a5aaa343..e48f8fe585bd8 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.c @@ -1786,8 +1786,11 @@ void hclgevf_mbx_task_schedule(struct hclgevf_dev *hdev) static void hclgevf_task_schedule(struct hclgevf_dev *hdev) { if (!test_bit(HCLGEVF_STATE_DOWN, &hdev->state) && - !test_and_set_bit(HCLGEVF_STATE_SERVICE_SCHED, &hdev->state)) - schedule_work(&hdev->service_task); + !test_and_set_bit(HCLGEVF_STATE_SERVICE_SCHED, &hdev->state)) { + mod_delayed_work(system_wq, &hdev->service_task, + round_jiffies_relative(HZ)); + hdev->stats_timer++; + } } static void hclgevf_deferred_task_schedule(struct hclgevf_dev *hdev) @@ -1800,17 +1803,6 @@ static void hclgevf_deferred_task_schedule(struct hclgevf_dev *hdev) hclgevf_reset_task_schedule(hdev); } -static void hclgevf_service_timer(struct timer_list *t) -{ - struct hclgevf_dev *hdev = from_timer(hdev, t, service_timer); - - mod_timer(&hdev->service_timer, jiffies + - HCLGEVF_GENERAL_TASK_INTERVAL * HZ); - - hdev->stats_timer++; - hclgevf_task_schedule(hdev); -} - static void hclgevf_reset_service_task(struct work_struct *work) { #define HCLGEVF_MAX_RESET_ATTEMPTS_CNT 3 @@ -1933,7 +1925,7 @@ static void hclgevf_service_task(struct work_struct *work) struct hnae3_handle *handle; struct hclgevf_dev *hdev; - hdev = container_of(work, struct hclgevf_dev, service_task); + hdev = container_of(work, struct hclgevf_dev, service_task.work); handle = &hdev->nic; if (hdev->stats_timer >= HCLGEVF_STATS_TIMER_INTERVAL) { @@ -1953,6 +1945,8 @@ static void hclgevf_service_task(struct work_struct *work) hclgevf_deferred_task_schedule(hdev); clear_bit(HCLGEVF_STATE_SERVICE_SCHED, &hdev->state); + + hclgevf_task_schedule(hdev); } static void hclgevf_clear_event_cause(struct hclgevf_dev *hdev, u32 regclr) @@ -2194,10 +2188,10 @@ static void hclgevf_set_timer_task(struct hnae3_handle *handle, bool enable) struct hclgevf_dev *hdev = hclgevf_ae_get_hdev(handle); if (enable) { - mod_timer(&hdev->service_timer, jiffies + HZ); + hclgevf_task_schedule(hdev); } else { - del_timer_sync(&hdev->service_timer); - cancel_work_sync(&hdev->service_task); + set_bit(HCLGEVF_STATE_DOWN, &hdev->state); + cancel_delayed_work_sync(&hdev->service_task); clear_bit(HCLGEVF_STATE_SERVICE_SCHED, &hdev->state); } } @@ -2279,10 +2273,7 @@ static void hclgevf_state_init(struct hclgevf_dev *hdev) clear_bit(HCLGEVF_STATE_MBX_SERVICE_SCHED, &hdev->state); clear_bit(HCLGEVF_STATE_MBX_HANDLING, &hdev->state); - /* setup tasks for service timer */ - timer_setup(&hdev->service_timer, hclgevf_service_timer, 0); - - INIT_WORK(&hdev->service_task, hclgevf_service_task); + INIT_DELAYED_WORK(&hdev->service_task, hclgevf_service_task); clear_bit(HCLGEVF_STATE_SERVICE_SCHED, &hdev->state); INIT_WORK(&hdev->rst_service_task, hclgevf_reset_service_task); @@ -2302,10 +2293,8 @@ static void hclgevf_state_uninit(struct hclgevf_dev *hdev) del_timer_sync(&hdev->keep_alive_timer); if (hdev->keep_alive_task.func) cancel_work_sync(&hdev->keep_alive_task); - if (hdev->service_timer.function) - del_timer_sync(&hdev->service_timer); - if (hdev->service_task.func) - cancel_work_sync(&hdev->service_task); + if (hdev->service_task.work.func) + cancel_delayed_work_sync(&hdev->service_task); if (hdev->mbx_service_task.func) cancel_work_sync(&hdev->mbx_service_task); if (hdev->rst_service_task.func) diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.h b/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.h index 2f4c81bf4169b..991a924da5ed3 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.h +++ b/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.h @@ -283,9 +283,8 @@ struct hclgevf_dev { struct hclgevf_mbx_resp_status mbx_resp; /* mailbox response */ struct hclgevf_mbx_arq_ring arq; /* mailbox async rx queue */ - struct timer_list service_timer; struct timer_list keep_alive_timer; - struct work_struct service_task; + struct delayed_work service_task; struct work_struct keep_alive_task; struct work_struct rst_service_task; struct work_struct mbx_service_task; -- GitLab From 1c6dfe6fc6f77f3d124e63c2c2c3f9bb73f8d92c Mon Sep 17 00:00:00 2001 From: Yunsheng Lin Date: Sat, 14 Dec 2019 10:06:38 +0800 Subject: [PATCH 0761/1437] net: hns3: remove mailbox and reset work in hclge_main There are three work (mbx_service_task, service_task, rst_service_task) in the HNS3 driver, mbx_service_task is for handling mailbox work, service_task is for periodic management issue and rst_service_task is for reset related issue, which can be handled in a single work. This patch removes the mbx_service_task and rst_service_task work, and moves the related handling to the service_task work in order to remove concurrency between the three work and to improve efficiency. BTW, since stats_timer in struct hclge_hw_stats is not needed anymore, so removes the definition of struct hclge_hw_stats, and moves mac_stats into struct hclge_dev. Signed-off-by: Yunsheng Lin Signed-off-by: Huazhong Tan Signed-off-by: David S. Miller --- .../hisilicon/hns3/hns3pf/hclge_debugfs.c | 10 ++ .../hisilicon/hns3/hns3pf/hclge_main.c | 163 +++++++++++------- .../hisilicon/hns3/hns3pf/hclge_main.h | 17 +- .../hisilicon/hns3/hns3pf/hclge_mbx.c | 1 - 4 files changed, 118 insertions(+), 73 deletions(-) diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_debugfs.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_debugfs.c index 112df34b38699..92a99e4fdda56 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_debugfs.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_debugfs.c @@ -976,6 +976,14 @@ void hclge_dbg_dump_rst_info(struct hclge_dev *hdev) dev_info(&hdev->pdev->dev, "hdev state: 0x%lx\n", hdev->state); } +static void hclge_dbg_dump_serv_info(struct hclge_dev *hdev) +{ + dev_info(&hdev->pdev->dev, "last_serv_processed: %lu\n", + hdev->last_serv_processed); + dev_info(&hdev->pdev->dev, "last_serv_cnt: %lu\n", + hdev->serv_processed_cnt); +} + static void hclge_dbg_get_m7_stats_info(struct hclge_dev *hdev) { struct hclge_desc *desc_src, *desc_tmp; @@ -1227,6 +1235,8 @@ int hclge_dbg_run_cmd(struct hnae3_handle *handle, const char *cmd_buf) hclge_dbg_dump_reg_cmd(hdev, &cmd_buf[sizeof(DUMP_REG)]); } else if (strncmp(cmd_buf, "dump reset info", 15) == 0) { hclge_dbg_dump_rst_info(hdev); + } else if (strncmp(cmd_buf, "dump serv info", 14) == 0) { + hclge_dbg_dump_serv_info(hdev); } else if (strncmp(cmd_buf, "dump m7 info", 12) == 0) { hclge_dbg_get_m7_stats_info(hdev); } else if (strncmp(cmd_buf, "dump ncl_config", 15) == 0) { diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c index d862e9ba27e15..a0177e3195509 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c @@ -416,7 +416,7 @@ static int hclge_mac_update_stats_defective(struct hclge_dev *hdev) { #define HCLGE_MAC_CMD_NUM 21 - u64 *data = (u64 *)(&hdev->hw_stats.mac_stats); + u64 *data = (u64 *)(&hdev->mac_stats); struct hclge_desc desc[HCLGE_MAC_CMD_NUM]; __le64 *desc_data; int i, k, n; @@ -453,7 +453,7 @@ static int hclge_mac_update_stats_defective(struct hclge_dev *hdev) static int hclge_mac_update_stats_complete(struct hclge_dev *hdev, u32 desc_num) { - u64 *data = (u64 *)(&hdev->hw_stats.mac_stats); + u64 *data = (u64 *)(&hdev->mac_stats); struct hclge_desc *desc; __le64 *desc_data; u16 i, k, n; @@ -802,7 +802,7 @@ static void hclge_get_stats(struct hnae3_handle *handle, u64 *data) struct hclge_dev *hdev = vport->back; u64 *p; - p = hclge_comm_get_stats(&hdev->hw_stats.mac_stats, g_mac_stats_string, + p = hclge_comm_get_stats(&hdev->mac_stats, g_mac_stats_string, ARRAY_SIZE(g_mac_stats_string), data); p = hclge_tqps_get_stats(handle, p); } @@ -815,8 +815,8 @@ static void hclge_get_mac_stat(struct hnae3_handle *handle, hclge_update_stats(handle, NULL); - mac_stats->tx_pause_cnt = hdev->hw_stats.mac_stats.mac_tx_mac_pause_num; - mac_stats->rx_pause_cnt = hdev->hw_stats.mac_stats.mac_rx_mac_pause_num; + mac_stats->tx_pause_cnt = hdev->mac_stats.mac_tx_mac_pause_num; + mac_stats->rx_pause_cnt = hdev->mac_stats.mac_rx_mac_pause_num; } static int hclge_parse_func_status(struct hclge_dev *hdev, @@ -2665,31 +2665,26 @@ static int hclge_mac_init(struct hclge_dev *hdev) static void hclge_mbx_task_schedule(struct hclge_dev *hdev) { - if (!test_bit(HCLGE_STATE_CMD_DISABLE, &hdev->state) && + if (!test_bit(HCLGE_STATE_REMOVING, &hdev->state) && !test_and_set_bit(HCLGE_STATE_MBX_SERVICE_SCHED, &hdev->state)) - queue_work_on(cpumask_first(&hdev->affinity_mask), system_wq, - &hdev->mbx_service_task); + mod_delayed_work_on(cpumask_first(&hdev->affinity_mask), + system_wq, &hdev->service_task, 0); } static void hclge_reset_task_schedule(struct hclge_dev *hdev) { if (!test_bit(HCLGE_STATE_REMOVING, &hdev->state) && !test_and_set_bit(HCLGE_STATE_RST_SERVICE_SCHED, &hdev->state)) - queue_work_on(cpumask_first(&hdev->affinity_mask), system_wq, - &hdev->rst_service_task); + mod_delayed_work_on(cpumask_first(&hdev->affinity_mask), + system_wq, &hdev->service_task, 0); } void hclge_task_schedule(struct hclge_dev *hdev, unsigned long delay_time) { - if (!test_bit(HCLGE_STATE_DOWN, &hdev->state) && - !test_bit(HCLGE_STATE_REMOVING, &hdev->state) && - !test_and_set_bit(HCLGE_STATE_SERVICE_SCHED, &hdev->state)) { - hdev->hw_stats.stats_timer++; - hdev->fd_arfs_expire_timer++; + if (!test_bit(HCLGE_STATE_REMOVING, &hdev->state)) mod_delayed_work_on(cpumask_first(&hdev->affinity_mask), system_wq, &hdev->service_task, delay_time); - } } static int hclge_get_mac_link_status(struct hclge_dev *hdev) @@ -2748,6 +2743,10 @@ static void hclge_update_link_status(struct hclge_dev *hdev) if (!client) return; + + if (test_and_set_bit(HCLGE_STATE_LINK_UPDATING, &hdev->state)) + return; + state = hclge_get_mac_phy_link(hdev); if (state != hdev->hw.mac.link) { for (i = 0; i < hdev->num_vmdq_vport + 1; i++) { @@ -2761,6 +2760,8 @@ static void hclge_update_link_status(struct hclge_dev *hdev) } hdev->hw.mac.link = state; } + + clear_bit(HCLGE_STATE_LINK_UPDATING, &hdev->state); } static void hclge_update_port_capability(struct hclge_mac *mac) @@ -3352,6 +3353,18 @@ static int hclge_set_all_vf_rst(struct hclge_dev *hdev, bool reset) return 0; } +static void hclge_mailbox_service_task(struct hclge_dev *hdev) +{ + if (!test_and_clear_bit(HCLGE_STATE_MBX_SERVICE_SCHED, &hdev->state) || + test_bit(HCLGE_STATE_CMD_DISABLE, &hdev->state) || + test_and_set_bit(HCLGE_STATE_MBX_HANDLING, &hdev->state)) + return; + + hclge_mbx_handler(hdev); + + clear_bit(HCLGE_STATE_MBX_HANDLING, &hdev->state); +} + static int hclge_func_reset_sync_vf(struct hclge_dev *hdev) { struct hclge_pf_rst_sync_cmd *req; @@ -3363,6 +3376,9 @@ static int hclge_func_reset_sync_vf(struct hclge_dev *hdev) hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_QUERY_VF_RST_RDY, true); do { + /* vf need to down netdev by mbx during PF or FLR reset */ + hclge_mailbox_service_task(hdev); + ret = hclge_cmd_send(&hdev->hw, &desc, 1); /* for compatible with old firmware, wait * 100 ms for VF to stop IO @@ -3939,36 +3955,19 @@ static void hclge_reset_subtask(struct hclge_dev *hdev) hdev->reset_type = HNAE3_NONE_RESET; } -static void hclge_reset_service_task(struct work_struct *work) +static void hclge_reset_service_task(struct hclge_dev *hdev) { - struct hclge_dev *hdev = - container_of(work, struct hclge_dev, rst_service_task); + if (!test_and_clear_bit(HCLGE_STATE_RST_SERVICE_SCHED, &hdev->state)) + return; if (test_and_set_bit(HCLGE_STATE_RST_HANDLING, &hdev->state)) return; - clear_bit(HCLGE_STATE_RST_SERVICE_SCHED, &hdev->state); - hclge_reset_subtask(hdev); clear_bit(HCLGE_STATE_RST_HANDLING, &hdev->state); } -static void hclge_mailbox_service_task(struct work_struct *work) -{ - struct hclge_dev *hdev = - container_of(work, struct hclge_dev, mbx_service_task); - - if (test_and_set_bit(HCLGE_STATE_MBX_HANDLING, &hdev->state)) - return; - - clear_bit(HCLGE_STATE_MBX_SERVICE_SCHED, &hdev->state); - - hclge_mbx_handler(hdev); - - clear_bit(HCLGE_STATE_MBX_HANDLING, &hdev->state); -} - static void hclge_update_vport_alive(struct hclge_dev *hdev) { int i; @@ -3986,29 +3985,62 @@ static void hclge_update_vport_alive(struct hclge_dev *hdev) } } -static void hclge_service_task(struct work_struct *work) +static void hclge_periodic_service_task(struct hclge_dev *hdev) { - struct hclge_dev *hdev = - container_of(work, struct hclge_dev, service_task.work); + unsigned long delta = round_jiffies_relative(HZ); - clear_bit(HCLGE_STATE_SERVICE_SCHED, &hdev->state); + /* Always handle the link updating to make sure link state is + * updated when it is triggered by mbx. + */ + hclge_update_link_status(hdev); - if (hdev->hw_stats.stats_timer >= HCLGE_STATS_TIMER_INTERVAL) { - hclge_update_stats_for_all(hdev); - hdev->hw_stats.stats_timer = 0; + if (time_is_after_jiffies(hdev->last_serv_processed + HZ)) { + delta = jiffies - hdev->last_serv_processed; + + if (delta < round_jiffies_relative(HZ)) { + delta = round_jiffies_relative(HZ) - delta; + goto out; + } } - hclge_update_port_info(hdev); - hclge_update_link_status(hdev); + hdev->serv_processed_cnt++; hclge_update_vport_alive(hdev); + + if (test_bit(HCLGE_STATE_DOWN, &hdev->state)) { + hdev->last_serv_processed = jiffies; + goto out; + } + + if (!(hdev->serv_processed_cnt % HCLGE_STATS_TIMER_INTERVAL)) + hclge_update_stats_for_all(hdev); + + hclge_update_port_info(hdev); hclge_sync_vlan_filter(hdev); - if (hdev->fd_arfs_expire_timer >= HCLGE_FD_ARFS_EXPIRE_TIMER_INTERVAL) { + if (!(hdev->serv_processed_cnt % HCLGE_ARFS_EXPIRE_INTERVAL)) hclge_rfs_filter_expire(hdev); - hdev->fd_arfs_expire_timer = 0; - } - hclge_task_schedule(hdev, round_jiffies_relative(HZ)); + hdev->last_serv_processed = jiffies; + +out: + hclge_task_schedule(hdev, delta); +} + +static void hclge_service_task(struct work_struct *work) +{ + struct hclge_dev *hdev = + container_of(work, struct hclge_dev, service_task.work); + + hclge_reset_service_task(hdev); + hclge_mailbox_service_task(hdev); + hclge_periodic_service_task(hdev); + + /* Handle reset and mbx again in case periodical task delays the + * handling by calling hclge_task_schedule() in + * hclge_periodic_service_task(). + */ + hclge_reset_service_task(hdev); + hclge_mailbox_service_task(hdev); } struct hclge_vport *hclge_get_vport(struct hnae3_handle *handle) @@ -6734,6 +6766,19 @@ static void hclge_reset_tqp_stats(struct hnae3_handle *handle) } } +static void hclge_flush_link_update(struct hclge_dev *hdev) +{ +#define HCLGE_FLUSH_LINK_TIMEOUT 100000 + + unsigned long last = hdev->serv_processed_cnt; + int i = 0; + + while (test_bit(HCLGE_STATE_LINK_UPDATING, &hdev->state) && + i++ < HCLGE_FLUSH_LINK_TIMEOUT && + last == hdev->serv_processed_cnt) + usleep_range(1, 1); +} + static void hclge_set_timer_task(struct hnae3_handle *handle, bool enable) { struct hclge_vport *vport = hclge_get_vport(handle); @@ -6742,12 +6787,12 @@ static void hclge_set_timer_task(struct hnae3_handle *handle, bool enable) if (enable) { hclge_task_schedule(hdev, round_jiffies_relative(HZ)); } else { - /* Set the DOWN flag here to disable the service to be - * scheduled again - */ + /* Set the DOWN flag here to disable link updating */ set_bit(HCLGE_STATE_DOWN, &hdev->state); - cancel_delayed_work_sync(&hdev->service_task); - clear_bit(HCLGE_STATE_SERVICE_SCHED, &hdev->state); + + /* flush memory to make sure DOWN is seen by service task */ + smp_mb__before_atomic(); + hclge_flush_link_update(hdev); } } @@ -9269,10 +9314,6 @@ static void hclge_state_uninit(struct hclge_dev *hdev) del_timer_sync(&hdev->reset_timer); if (hdev->service_task.work.func) cancel_delayed_work_sync(&hdev->service_task); - if (hdev->rst_service_task.func) - cancel_work_sync(&hdev->rst_service_task); - if (hdev->mbx_service_task.func) - cancel_work_sync(&hdev->mbx_service_task); } static void hclge_flr_prepare(struct hnae3_ae_dev *ae_dev) @@ -9477,8 +9518,6 @@ static int hclge_init_ae_dev(struct hnae3_ae_dev *ae_dev) timer_setup(&hdev->reset_timer, hclge_reset_timer, 0); INIT_DELAYED_WORK(&hdev->service_task, hclge_service_task); - INIT_WORK(&hdev->rst_service_task, hclge_reset_service_task); - INIT_WORK(&hdev->mbx_service_task, hclge_mailbox_service_task); /* Setup affinity after service timer setup because add_timer_on * is called in affinity notify. @@ -9512,6 +9551,8 @@ static int hclge_init_ae_dev(struct hnae3_ae_dev *ae_dev) dev_info(&hdev->pdev->dev, "%s driver initialization finished.\n", HCLGE_DRIVER_NAME); + hclge_task_schedule(hdev, round_jiffies_relative(HZ)); + return 0; err_mdiobus_unreg: @@ -9534,7 +9575,7 @@ out: static void hclge_stats_clear(struct hclge_dev *hdev) { - memset(&hdev->hw_stats, 0, sizeof(hdev->hw_stats)); + memset(&hdev->mac_stats, 0, sizeof(hdev->mac_stats)); } static int hclge_set_mac_spoofchk(struct hclge_dev *hdev, int vf, bool enable) diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.h b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.h index ebb4c6e9aed37..ad40cf6be4f1d 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.h +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.h @@ -208,13 +208,13 @@ enum HCLGE_DEV_STATE { HCLGE_STATE_NIC_REGISTERED, HCLGE_STATE_ROCE_REGISTERED, HCLGE_STATE_SERVICE_INITED, - HCLGE_STATE_SERVICE_SCHED, HCLGE_STATE_RST_SERVICE_SCHED, HCLGE_STATE_RST_HANDLING, HCLGE_STATE_MBX_SERVICE_SCHED, HCLGE_STATE_MBX_HANDLING, HCLGE_STATE_STATISTICS_UPDATING, HCLGE_STATE_CMD_DISABLE, + HCLGE_STATE_LINK_UPDATING, HCLGE_STATE_MAX }; @@ -454,11 +454,7 @@ struct hclge_mac_stats { u64 mac_rx_ctrl_pkt_num; }; -#define HCLGE_STATS_TIMER_INTERVAL (60 * 5) -struct hclge_hw_stats { - struct hclge_mac_stats mac_stats; - u32 stats_timer; -}; +#define HCLGE_STATS_TIMER_INTERVAL 300UL struct hclge_vlan_type_cfg { u16 rx_ot_fst_vlan_type; @@ -549,7 +545,7 @@ struct key_info { /* assigned by firmware, the real filter number for each pf may be less */ #define MAX_FD_FILTER_NUM 4096 -#define HCLGE_FD_ARFS_EXPIRE_TIMER_INTERVAL 5 +#define HCLGE_ARFS_EXPIRE_INTERVAL 5UL enum HCLGE_FD_ACTIVE_RULE_TYPE { HCLGE_FD_RULE_NONE, @@ -712,7 +708,7 @@ struct hclge_dev { struct hnae3_ae_dev *ae_dev; struct hclge_hw hw; struct hclge_misc_vector misc_vector; - struct hclge_hw_stats hw_stats; + struct hclge_mac_stats mac_stats; unsigned long state; unsigned long flr_state; unsigned long last_reset_time; @@ -774,8 +770,6 @@ struct hclge_dev { unsigned long service_timer_previous; struct timer_list reset_timer; struct delayed_work service_task; - struct work_struct rst_service_task; - struct work_struct mbx_service_task; bool cur_promisc; int num_alloc_vfs; /* Actual number of VFs allocated */ @@ -811,7 +805,8 @@ struct hclge_dev { struct hlist_head fd_rule_list; spinlock_t fd_rule_lock; /* protect fd_rule_list and fd_bmap */ u16 hclge_fd_rule_num; - u16 fd_arfs_expire_timer; + unsigned long serv_processed_cnt; + unsigned long last_serv_processed; unsigned long fd_bmap[BITS_TO_LONGS(MAX_FD_FILTER_NUM)]; enum HCLGE_FD_ACTIVE_RULE_TYPE fd_active_type; u8 fd_en; diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_mbx.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_mbx.c index 0b433ebe6a2d9..a79d1512119d2 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_mbx.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_mbx.c @@ -635,7 +635,6 @@ static void hclge_handle_link_change_event(struct hclge_dev *hdev, #define LINK_STATUS_OFFSET 1 #define LINK_FAIL_CODE_OFFSET 2 - clear_bit(HCLGE_STATE_SERVICE_SCHED, &hdev->state); hclge_task_schedule(hdev, 0); if (!req->msg[LINK_STATUS_OFFSET]) -- GitLab From ff200099d271cb14e4415449559da7cc6a38f4a1 Mon Sep 17 00:00:00 2001 From: Yunsheng Lin Date: Sat, 14 Dec 2019 10:06:39 +0800 Subject: [PATCH 0762/1437] net: hns3: remove unnecessary work in hclgevf_main There are four work (mbx_service_task, service_task, rst_service_task and keep_alive_task)in the hclgevf module, mbx_service_task is for handling mailbox issue, service_task is for periodic management issue and rst_service_task is for reset related issue, keep_alive_task is used to keepalive between PF and VF, which can be done in a single work. This patch removes the mbx_service_task, rst_service_task and keep_alive_task, and moves the related handling to the service_task work in order to remove concurrency between the four work and to improve efficiency. Signed-off-by: Yunsheng Lin Signed-off-by: Huazhong Tan Signed-off-by: David S. Miller --- .../hisilicon/hns3/hns3vf/hclgevf_main.c | 175 +++++++++--------- .../hisilicon/hns3/hns3vf/hclgevf_main.h | 9 +- 2 files changed, 88 insertions(+), 96 deletions(-) diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.c b/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.c index e48f8fe585bd8..004dbaffee2d0 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.c @@ -440,6 +440,9 @@ void hclgevf_update_link_status(struct hclgevf_dev *hdev, int link_state) struct hnae3_client *rclient; struct hnae3_client *client; + if (test_and_set_bit(HCLGEVF_STATE_LINK_UPDATING, &hdev->state)) + return; + client = handle->client; rclient = hdev->roce_client; @@ -452,6 +455,8 @@ void hclgevf_update_link_status(struct hclgevf_dev *hdev, int link_state) rclient->ops->link_status_change(rhandle, !!link_state); hdev->hw.mac.link = link_state; } + + clear_bit(HCLGEVF_STATE_LINK_UPDATING, &hdev->state); } static void hclgevf_update_link_mode(struct hclgevf_dev *hdev) @@ -1767,54 +1772,38 @@ static void hclgevf_get_misc_vector(struct hclgevf_dev *hdev) void hclgevf_reset_task_schedule(struct hclgevf_dev *hdev) { - if (!test_bit(HCLGEVF_STATE_RST_SERVICE_SCHED, &hdev->state) && - !test_bit(HCLGEVF_STATE_REMOVING, &hdev->state)) { - set_bit(HCLGEVF_STATE_RST_SERVICE_SCHED, &hdev->state); - schedule_work(&hdev->rst_service_task); - } + if (!test_bit(HCLGEVF_STATE_REMOVING, &hdev->state) && + !test_and_set_bit(HCLGEVF_STATE_RST_SERVICE_SCHED, + &hdev->state)) + mod_delayed_work(system_wq, &hdev->service_task, 0); } void hclgevf_mbx_task_schedule(struct hclgevf_dev *hdev) { - if (!test_bit(HCLGEVF_STATE_MBX_SERVICE_SCHED, &hdev->state) && - !test_bit(HCLGEVF_STATE_MBX_HANDLING, &hdev->state)) { - set_bit(HCLGEVF_STATE_MBX_SERVICE_SCHED, &hdev->state); - schedule_work(&hdev->mbx_service_task); - } + if (!test_bit(HCLGEVF_STATE_REMOVING, &hdev->state) && + !test_and_set_bit(HCLGEVF_STATE_MBX_SERVICE_SCHED, + &hdev->state)) + mod_delayed_work(system_wq, &hdev->service_task, 0); } -static void hclgevf_task_schedule(struct hclgevf_dev *hdev) +static void hclgevf_task_schedule(struct hclgevf_dev *hdev, + unsigned long delay) { - if (!test_bit(HCLGEVF_STATE_DOWN, &hdev->state) && - !test_and_set_bit(HCLGEVF_STATE_SERVICE_SCHED, &hdev->state)) { - mod_delayed_work(system_wq, &hdev->service_task, - round_jiffies_relative(HZ)); - hdev->stats_timer++; - } + if (!test_bit(HCLGEVF_STATE_REMOVING, &hdev->state)) + mod_delayed_work(system_wq, &hdev->service_task, delay); } -static void hclgevf_deferred_task_schedule(struct hclgevf_dev *hdev) -{ - /* if we have any pending mailbox event then schedule the mbx task */ - if (hdev->mbx_event_pending) - hclgevf_mbx_task_schedule(hdev); - - if (test_bit(HCLGEVF_RESET_PENDING, &hdev->reset_state)) - hclgevf_reset_task_schedule(hdev); -} - -static void hclgevf_reset_service_task(struct work_struct *work) +static void hclgevf_reset_service_task(struct hclgevf_dev *hdev) { #define HCLGEVF_MAX_RESET_ATTEMPTS_CNT 3 - struct hclgevf_dev *hdev = - container_of(work, struct hclgevf_dev, rst_service_task); int ret; - if (test_and_set_bit(HCLGEVF_STATE_RST_HANDLING, &hdev->state)) + if (!test_and_clear_bit(HCLGEVF_STATE_RST_SERVICE_SCHED, &hdev->state)) return; - clear_bit(HCLGEVF_STATE_RST_SERVICE_SCHED, &hdev->state); + if (test_and_set_bit(HCLGEVF_STATE_RST_HANDLING, &hdev->state)) + return; if (test_and_clear_bit(HCLGEVF_RESET_PENDING, &hdev->reset_state)) { @@ -1877,39 +1866,24 @@ static void hclgevf_reset_service_task(struct work_struct *work) clear_bit(HCLGEVF_STATE_RST_HANDLING, &hdev->state); } -static void hclgevf_mailbox_service_task(struct work_struct *work) +static void hclgevf_mailbox_service_task(struct hclgevf_dev *hdev) { - struct hclgevf_dev *hdev; - - hdev = container_of(work, struct hclgevf_dev, mbx_service_task); + if (!test_and_clear_bit(HCLGEVF_STATE_MBX_SERVICE_SCHED, &hdev->state)) + return; if (test_and_set_bit(HCLGEVF_STATE_MBX_HANDLING, &hdev->state)) return; - clear_bit(HCLGEVF_STATE_MBX_SERVICE_SCHED, &hdev->state); - hclgevf_mbx_async_handler(hdev); clear_bit(HCLGEVF_STATE_MBX_HANDLING, &hdev->state); } -static void hclgevf_keep_alive_timer(struct timer_list *t) -{ - struct hclgevf_dev *hdev = from_timer(hdev, t, keep_alive_timer); - - schedule_work(&hdev->keep_alive_task); - mod_timer(&hdev->keep_alive_timer, jiffies + - HCLGEVF_KEEP_ALIVE_TASK_INTERVAL * HZ); -} - -static void hclgevf_keep_alive_task(struct work_struct *work) +static void hclgevf_keep_alive(struct hclgevf_dev *hdev) { - struct hclgevf_dev *hdev; u8 respmsg; int ret; - hdev = container_of(work, struct hclgevf_dev, keep_alive_task); - if (test_bit(HCLGEVF_STATE_CMD_DISABLE, &hdev->state)) return; @@ -1920,19 +1894,32 @@ static void hclgevf_keep_alive_task(struct work_struct *work) "VF sends keep alive cmd failed(=%d)\n", ret); } -static void hclgevf_service_task(struct work_struct *work) +static void hclgevf_periodic_service_task(struct hclgevf_dev *hdev) { - struct hnae3_handle *handle; - struct hclgevf_dev *hdev; + unsigned long delta = round_jiffies_relative(HZ); + struct hnae3_handle *handle = &hdev->nic; - hdev = container_of(work, struct hclgevf_dev, service_task.work); - handle = &hdev->nic; + if (time_is_after_jiffies(hdev->last_serv_processed + HZ)) { + delta = jiffies - hdev->last_serv_processed; - if (hdev->stats_timer >= HCLGEVF_STATS_TIMER_INTERVAL) { - hclgevf_tqps_update_stats(handle); - hdev->stats_timer = 0; + if (delta < round_jiffies_relative(HZ)) { + delta = round_jiffies_relative(HZ) - delta; + goto out; + } + } + + hdev->serv_processed_cnt++; + if (!(hdev->serv_processed_cnt % HCLGEVF_KEEP_ALIVE_TASK_INTERVAL)) + hclgevf_keep_alive(hdev); + + if (test_bit(HCLGEVF_STATE_DOWN, &hdev->state)) { + hdev->last_serv_processed = jiffies; + goto out; } + if (!(hdev->serv_processed_cnt % HCLGEVF_STATS_TIMER_INTERVAL)) + hclgevf_tqps_update_stats(handle); + /* request the link status from the PF. PF would be able to tell VF * about such updates in future so we might remove this later */ @@ -1942,11 +1929,27 @@ static void hclgevf_service_task(struct work_struct *work) hclgevf_sync_vlan_filter(hdev); - hclgevf_deferred_task_schedule(hdev); + hdev->last_serv_processed = jiffies; - clear_bit(HCLGEVF_STATE_SERVICE_SCHED, &hdev->state); +out: + hclgevf_task_schedule(hdev, delta); +} + +static void hclgevf_service_task(struct work_struct *work) +{ + struct hclgevf_dev *hdev = container_of(work, struct hclgevf_dev, + service_task.work); - hclgevf_task_schedule(hdev); + hclgevf_reset_service_task(hdev); + hclgevf_mailbox_service_task(hdev); + hclgevf_periodic_service_task(hdev); + + /* Handle reset and mbx again in case periodical task delays the + * handling by calling hclgevf_task_schedule() in + * hclgevf_periodic_service_task() + */ + hclgevf_reset_service_task(hdev); + hclgevf_mailbox_service_task(hdev); } static void hclgevf_clear_event_cause(struct hclgevf_dev *hdev, u32 regclr) @@ -2183,16 +2186,31 @@ static int hclgevf_init_vlan_config(struct hclgevf_dev *hdev) false); } +static void hclgevf_flush_link_update(struct hclgevf_dev *hdev) +{ +#define HCLGEVF_FLUSH_LINK_TIMEOUT 100000 + + unsigned long last = hdev->serv_processed_cnt; + int i = 0; + + while (test_bit(HCLGEVF_STATE_LINK_UPDATING, &hdev->state) && + i++ < HCLGEVF_FLUSH_LINK_TIMEOUT && + last == hdev->serv_processed_cnt) + usleep_range(1, 1); +} + static void hclgevf_set_timer_task(struct hnae3_handle *handle, bool enable) { struct hclgevf_dev *hdev = hclgevf_ae_get_hdev(handle); if (enable) { - hclgevf_task_schedule(hdev); + hclgevf_task_schedule(hdev, 0); } else { set_bit(HCLGEVF_STATE_DOWN, &hdev->state); - cancel_delayed_work_sync(&hdev->service_task); - clear_bit(HCLGEVF_STATE_SERVICE_SCHED, &hdev->state); + + /* flush memory to make sure DOWN is seen by service task */ + smp_mb__before_atomic(); + hclgevf_flush_link_update(hdev); } } @@ -2239,16 +2257,12 @@ static int hclgevf_set_alive(struct hnae3_handle *handle, bool alive) static int hclgevf_client_start(struct hnae3_handle *handle) { - struct hclgevf_dev *hdev = hclgevf_ae_get_hdev(handle); int ret; ret = hclgevf_set_alive(handle, true); if (ret) return ret; - mod_timer(&hdev->keep_alive_timer, jiffies + - HCLGEVF_KEEP_ALIVE_TASK_INTERVAL * HZ); - return 0; } @@ -2261,22 +2275,14 @@ static void hclgevf_client_stop(struct hnae3_handle *handle) if (ret) dev_warn(&hdev->pdev->dev, "%s failed %d\n", __func__, ret); - - del_timer_sync(&hdev->keep_alive_timer); - cancel_work_sync(&hdev->keep_alive_task); } static void hclgevf_state_init(struct hclgevf_dev *hdev) { - /* setup tasks for the MBX */ - INIT_WORK(&hdev->mbx_service_task, hclgevf_mailbox_service_task); clear_bit(HCLGEVF_STATE_MBX_SERVICE_SCHED, &hdev->state); clear_bit(HCLGEVF_STATE_MBX_HANDLING, &hdev->state); INIT_DELAYED_WORK(&hdev->service_task, hclgevf_service_task); - clear_bit(HCLGEVF_STATE_SERVICE_SCHED, &hdev->state); - - INIT_WORK(&hdev->rst_service_task, hclgevf_reset_service_task); mutex_init(&hdev->mbx_resp.mbx_mutex); @@ -2289,16 +2295,8 @@ static void hclgevf_state_uninit(struct hclgevf_dev *hdev) set_bit(HCLGEVF_STATE_DOWN, &hdev->state); set_bit(HCLGEVF_STATE_REMOVING, &hdev->state); - if (hdev->keep_alive_timer.function) - del_timer_sync(&hdev->keep_alive_timer); - if (hdev->keep_alive_task.func) - cancel_work_sync(&hdev->keep_alive_task); if (hdev->service_task.work.func) cancel_delayed_work_sync(&hdev->service_task); - if (hdev->mbx_service_task.func) - cancel_work_sync(&hdev->mbx_service_task); - if (hdev->rst_service_task.func) - cancel_work_sync(&hdev->rst_service_task); mutex_destroy(&hdev->mbx_resp.mbx_mutex); } @@ -2796,6 +2794,8 @@ static int hclgevf_init_hdev(struct hclgevf_dev *hdev) dev_info(&hdev->pdev->dev, "finished initializing %s driver\n", HCLGEVF_DRIVER_NAME); + hclgevf_task_schedule(hdev, round_jiffies_relative(HZ)); + return 0; err_config: @@ -2827,7 +2827,6 @@ static void hclgevf_uninit_hdev(struct hclgevf_dev *hdev) static int hclgevf_init_ae_dev(struct hnae3_ae_dev *ae_dev) { struct pci_dev *pdev = ae_dev->pdev; - struct hclgevf_dev *hdev; int ret; ret = hclgevf_alloc_hdev(ae_dev); @@ -2842,10 +2841,6 @@ static int hclgevf_init_ae_dev(struct hnae3_ae_dev *ae_dev) return ret; } - hdev = ae_dev->priv; - timer_setup(&hdev->keep_alive_timer, hclgevf_keep_alive_timer, 0); - INIT_WORK(&hdev->keep_alive_task, hclgevf_keep_alive_task); - return 0; } diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.h b/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.h index 991a924da5ed3..450e5878293f4 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.h +++ b/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.h @@ -142,12 +142,12 @@ enum hclgevf_states { HCLGEVF_STATE_REMOVING, HCLGEVF_STATE_NIC_REGISTERED, /* task states */ - HCLGEVF_STATE_SERVICE_SCHED, HCLGEVF_STATE_RST_SERVICE_SCHED, HCLGEVF_STATE_RST_HANDLING, HCLGEVF_STATE_MBX_SERVICE_SCHED, HCLGEVF_STATE_MBX_HANDLING, HCLGEVF_STATE_CMD_DISABLE, + HCLGEVF_STATE_LINK_UPDATING, }; struct hclgevf_mac { @@ -283,11 +283,7 @@ struct hclgevf_dev { struct hclgevf_mbx_resp_status mbx_resp; /* mailbox response */ struct hclgevf_mbx_arq_ring arq; /* mailbox async rx queue */ - struct timer_list keep_alive_timer; struct delayed_work service_task; - struct work_struct keep_alive_task; - struct work_struct rst_service_task; - struct work_struct mbx_service_task; struct hclgevf_tqp *htqp; @@ -297,7 +293,8 @@ struct hclgevf_dev { struct hnae3_client *nic_client; struct hnae3_client *roce_client; u32 flag; - u32 stats_timer; + unsigned long serv_processed_cnt; + unsigned long last_serv_processed; }; static inline bool hclgevf_is_reset_pending(struct hclgevf_dev *hdev) -- GitLab From 0ea68902256e6cc705c94d1844dd8cf0805fd3e9 Mon Sep 17 00:00:00 2001 From: Yunsheng Lin Date: Sat, 14 Dec 2019 10:06:40 +0800 Subject: [PATCH 0763/1437] net: hns3: allocate WQ with WQ_MEM_RECLAIM flag The hns3 driver may be used in memory reclaim path when it is the low level transport of a network file system, so it needs to guarantee forward progress even under memory pressure. This patch allocates a private WQ with WQ_MEM_RECLAIM set for both hclge_main and hclgevf_main modules. Signed-off-by: Yunsheng Lin Signed-off-by: Huazhong Tan Signed-off-by: David S. Miller --- .../ethernet/hisilicon/hns3/hns3pf/hclge_main.c | 15 ++++++++++++--- .../ethernet/hisilicon/hns3/hns3vf/hclgevf_main.c | 15 ++++++++++++--- 2 files changed, 24 insertions(+), 6 deletions(-) diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c index a0177e3195509..5129b4a55f9bc 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c @@ -72,6 +72,8 @@ static int hclge_set_default_loopback(struct hclge_dev *hdev); static struct hnae3_ae_algo ae_algo; +static struct workqueue_struct *hclge_wq; + static const struct pci_device_id ae_algo_pci_tbl[] = { {PCI_VDEVICE(HUAWEI, HNAE3_DEV_ID_GE), 0}, {PCI_VDEVICE(HUAWEI, HNAE3_DEV_ID_25GE), 0}, @@ -2668,7 +2670,7 @@ static void hclge_mbx_task_schedule(struct hclge_dev *hdev) if (!test_bit(HCLGE_STATE_REMOVING, &hdev->state) && !test_and_set_bit(HCLGE_STATE_MBX_SERVICE_SCHED, &hdev->state)) mod_delayed_work_on(cpumask_first(&hdev->affinity_mask), - system_wq, &hdev->service_task, 0); + hclge_wq, &hdev->service_task, 0); } static void hclge_reset_task_schedule(struct hclge_dev *hdev) @@ -2676,14 +2678,14 @@ static void hclge_reset_task_schedule(struct hclge_dev *hdev) if (!test_bit(HCLGE_STATE_REMOVING, &hdev->state) && !test_and_set_bit(HCLGE_STATE_RST_SERVICE_SCHED, &hdev->state)) mod_delayed_work_on(cpumask_first(&hdev->affinity_mask), - system_wq, &hdev->service_task, 0); + hclge_wq, &hdev->service_task, 0); } void hclge_task_schedule(struct hclge_dev *hdev, unsigned long delay_time) { if (!test_bit(HCLGE_STATE_REMOVING, &hdev->state)) mod_delayed_work_on(cpumask_first(&hdev->affinity_mask), - system_wq, &hdev->service_task, + hclge_wq, &hdev->service_task, delay_time); } @@ -10652,6 +10654,12 @@ static int hclge_init(void) { pr_info("%s is initializing\n", HCLGE_NAME); + hclge_wq = alloc_workqueue("%s", WQ_MEM_RECLAIM, 0, HCLGE_NAME); + if (!hclge_wq) { + pr_err("%s: failed to create workqueue\n", HCLGE_NAME); + return -ENOMEM; + } + hnae3_register_ae_algo(&ae_algo); return 0; @@ -10660,6 +10668,7 @@ static int hclge_init(void) static void hclge_exit(void) { hnae3_unregister_ae_algo(&ae_algo); + destroy_workqueue(hclge_wq); } module_init(hclge_init); module_exit(hclge_exit); diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.c b/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.c index 004dbaffee2d0..b56c19afc3b9a 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.c @@ -16,6 +16,8 @@ static int hclgevf_reset_hdev(struct hclgevf_dev *hdev); static struct hnae3_ae_algo ae_algovf; +static struct workqueue_struct *hclgevf_wq; + static const struct pci_device_id ae_algovf_pci_tbl[] = { {PCI_VDEVICE(HUAWEI, HNAE3_DEV_ID_100G_VF), 0}, {PCI_VDEVICE(HUAWEI, HNAE3_DEV_ID_100G_RDMA_DCB_PFC_VF), 0}, @@ -1775,7 +1777,7 @@ void hclgevf_reset_task_schedule(struct hclgevf_dev *hdev) if (!test_bit(HCLGEVF_STATE_REMOVING, &hdev->state) && !test_and_set_bit(HCLGEVF_STATE_RST_SERVICE_SCHED, &hdev->state)) - mod_delayed_work(system_wq, &hdev->service_task, 0); + mod_delayed_work(hclgevf_wq, &hdev->service_task, 0); } void hclgevf_mbx_task_schedule(struct hclgevf_dev *hdev) @@ -1783,14 +1785,14 @@ void hclgevf_mbx_task_schedule(struct hclgevf_dev *hdev) if (!test_bit(HCLGEVF_STATE_REMOVING, &hdev->state) && !test_and_set_bit(HCLGEVF_STATE_MBX_SERVICE_SCHED, &hdev->state)) - mod_delayed_work(system_wq, &hdev->service_task, 0); + mod_delayed_work(hclgevf_wq, &hdev->service_task, 0); } static void hclgevf_task_schedule(struct hclgevf_dev *hdev, unsigned long delay) { if (!test_bit(HCLGEVF_STATE_REMOVING, &hdev->state)) - mod_delayed_work(system_wq, &hdev->service_task, delay); + mod_delayed_work(hclgevf_wq, &hdev->service_task, delay); } static void hclgevf_reset_service_task(struct hclgevf_dev *hdev) @@ -3197,6 +3199,12 @@ static int hclgevf_init(void) { pr_info("%s is initializing\n", HCLGEVF_NAME); + hclgevf_wq = alloc_workqueue("%s", WQ_MEM_RECLAIM, 0, HCLGEVF_NAME); + if (!hclgevf_wq) { + pr_err("%s: failed to create workqueue\n", HCLGEVF_NAME); + return -ENOMEM; + } + hnae3_register_ae_algo(&ae_algovf); return 0; @@ -3205,6 +3213,7 @@ static int hclgevf_init(void) static void hclgevf_exit(void) { hnae3_unregister_ae_algo(&ae_algovf); + destroy_workqueue(hclgevf_wq); } module_init(hclgevf_init); module_exit(hclgevf_exit); -- GitLab From d5432455b1a869d92d5643de0b4875bbcfbcefed Mon Sep 17 00:00:00 2001 From: Guojia Liao Date: Sat, 14 Dec 2019 10:06:41 +0800 Subject: [PATCH 0764/1437] net: hns3: do not schedule the periodic task when reset fail service_task will be scheduled per second to do some periodic jobs. When reset fails, it means this device is not available now, so the periodic jobs do not need to be handled. This patch adds flag HCLGE_STATE_RST_FAIL/HCLGEVF_STATE_RST_FAIL to indicate that reset fails, and checks this flag before schedule periodic task. Signed-off-by: Guojia Liao Signed-off-by: Huazhong Tan Signed-off-by: David S. Miller --- drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c | 7 ++++++- drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.h | 1 + drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.c | 6 +++++- drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.h | 1 + 4 files changed, 13 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c index 5129b4a55f9bc..4e7a07877e5d7 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c @@ -2683,7 +2683,8 @@ static void hclge_reset_task_schedule(struct hclge_dev *hdev) void hclge_task_schedule(struct hclge_dev *hdev, unsigned long delay_time) { - if (!test_bit(HCLGE_STATE_REMOVING, &hdev->state)) + if (!test_bit(HCLGE_STATE_REMOVING, &hdev->state) && + !test_bit(HCLGE_STATE_RST_FAIL, &hdev->state)) mod_delayed_work_on(cpumask_first(&hdev->affinity_mask), hclge_wq, &hdev->service_task, delay_time); @@ -3690,6 +3691,8 @@ static bool hclge_reset_err_handle(struct hclge_dev *hdev) hclge_dbg_dump_rst_info(hdev); + set_bit(HCLGE_STATE_RST_FAIL, &hdev->state); + return false; } @@ -3843,6 +3846,7 @@ static void hclge_reset(struct hclge_dev *hdev) hdev->rst_stats.reset_fail_cnt = 0; hdev->rst_stats.reset_done_cnt++; ae_dev->reset_type = HNAE3_NONE_RESET; + clear_bit(HCLGE_STATE_RST_FAIL, &hdev->state); /* if default_reset_request has a higher level reset request, * it should be handled as soon as possible. since some errors @@ -9303,6 +9307,7 @@ static void hclge_state_init(struct hclge_dev *hdev) set_bit(HCLGE_STATE_DOWN, &hdev->state); clear_bit(HCLGE_STATE_RST_SERVICE_SCHED, &hdev->state); clear_bit(HCLGE_STATE_RST_HANDLING, &hdev->state); + clear_bit(HCLGE_STATE_RST_FAIL, &hdev->state); clear_bit(HCLGE_STATE_MBX_SERVICE_SCHED, &hdev->state); clear_bit(HCLGE_STATE_MBX_HANDLING, &hdev->state); } diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.h b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.h index ad40cf6be4f1d..3a913976b820d 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.h +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.h @@ -215,6 +215,7 @@ enum HCLGE_DEV_STATE { HCLGE_STATE_STATISTICS_UPDATING, HCLGE_STATE_CMD_DISABLE, HCLGE_STATE_LINK_UPDATING, + HCLGE_STATE_RST_FAIL, HCLGE_STATE_MAX }; diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.c b/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.c index b56c19afc3b9a..c33b8027f8012 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.c @@ -1598,6 +1598,7 @@ static void hclgevf_reset_err_handle(struct hclgevf_dev *hdev) set_bit(HCLGEVF_RESET_PENDING, &hdev->reset_state); hclgevf_reset_task_schedule(hdev); } else { + set_bit(HCLGEVF_STATE_RST_FAIL, &hdev->state); hclgevf_dump_rst_info(hdev); } } @@ -1659,6 +1660,7 @@ static int hclgevf_reset(struct hclgevf_dev *hdev) ae_dev->reset_type = HNAE3_NONE_RESET; hdev->rst_stats.rst_done_cnt++; hdev->rst_stats.rst_fail_cnt = 0; + clear_bit(HCLGEVF_STATE_RST_FAIL, &hdev->state); return ret; err_reset_lock: @@ -1791,7 +1793,8 @@ void hclgevf_mbx_task_schedule(struct hclgevf_dev *hdev) static void hclgevf_task_schedule(struct hclgevf_dev *hdev, unsigned long delay) { - if (!test_bit(HCLGEVF_STATE_REMOVING, &hdev->state)) + if (!test_bit(HCLGEVF_STATE_REMOVING, &hdev->state) && + !test_bit(HCLGEVF_STATE_RST_FAIL, &hdev->state)) mod_delayed_work(hclgevf_wq, &hdev->service_task, delay); } @@ -2283,6 +2286,7 @@ static void hclgevf_state_init(struct hclgevf_dev *hdev) { clear_bit(HCLGEVF_STATE_MBX_SERVICE_SCHED, &hdev->state); clear_bit(HCLGEVF_STATE_MBX_HANDLING, &hdev->state); + clear_bit(HCLGEVF_STATE_RST_FAIL, &hdev->state); INIT_DELAYED_WORK(&hdev->service_task, hclgevf_service_task); diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.h b/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.h index 450e5878293f4..003114f6db6cf 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.h +++ b/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.h @@ -148,6 +148,7 @@ enum hclgevf_states { HCLGEVF_STATE_MBX_HANDLING, HCLGEVF_STATE_CMD_DISABLE, HCLGEVF_STATE_LINK_UPDATING, + HCLGEVF_STATE_RST_FAIL, }; struct hclgevf_mac { -- GitLab From ba8381236fd0fa0ab4e8383fb752c843a31dd019 Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Sat, 14 Dec 2019 17:53:06 +0200 Subject: [PATCH 0765/1437] net: fib_notifier: Add temporary events to the FIB notification chain Subsequent patches are going to simplify the IPv4 route offload API, which will only use two events - replace and delete. Introduce a temporary version of these two events in order to make the conversion easier to review. Signed-off-by: Ido Schimmel Reviewed-by: David Ahern Signed-off-by: David S. Miller --- include/net/fib_notifier.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/net/fib_notifier.h b/include/net/fib_notifier.h index 6d59221ff05ad..b3c54325caec0 100644 --- a/include/net/fib_notifier.h +++ b/include/net/fib_notifier.h @@ -23,6 +23,8 @@ enum fib_event_type { FIB_EVENT_NH_DEL, FIB_EVENT_VIF_ADD, FIB_EVENT_VIF_DEL, + FIB_EVENT_ENTRY_REPLACE_TMP, + FIB_EVENT_ENTRY_DEL_TMP, }; struct fib_notifier_ops { -- GitLab From a6c76c17df021b141b0d306828c9fe4ba2d2717c Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Sat, 14 Dec 2019 17:53:07 +0200 Subject: [PATCH 0766/1437] ipv4: Notify route after insertion to the routing table Currently, a new route is notified in the FIB notification chain before it is inserted to the FIB alias list. Subsequent patches will use the placement of the new route in the ordered FIB alias list in order to determine if the route should be notified or not. As a preparatory step, change the order so that the route is first inserted into the FIB alias list and only then notified. Signed-off-by: Ido Schimmel Reviewed-by: David Ahern Signed-off-by: David S. Miller --- net/ipv4/fib_trie.c | 29 ++++++++++++++--------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/net/ipv4/fib_trie.c b/net/ipv4/fib_trie.c index b9df9c09b84e5..9264d6628e9f7 100644 --- a/net/ipv4/fib_trie.c +++ b/net/ipv4/fib_trie.c @@ -1063,9 +1063,6 @@ noleaf: return -ENOMEM; } -/* fib notifier for ADD is sent before calling fib_insert_alias with - * the expectation that the only possible failure ENOMEM - */ static int fib_insert_alias(struct trie *t, struct key_vector *tp, struct key_vector *l, struct fib_alias *new, struct fib_alias *fa, t_key key) @@ -1118,6 +1115,9 @@ static bool fib_valid_key_len(u32 key, u8 plen, struct netlink_ext_ack *extack) return true; } +static void fib_remove_alias(struct trie *t, struct key_vector *tp, + struct key_vector *l, struct fib_alias *old); + /* Caller must hold RTNL. */ int fib_table_insert(struct net *net, struct fib_table *tb, struct fib_config *cfg, struct netlink_ext_ack *extack) @@ -1269,14 +1269,19 @@ int fib_table_insert(struct net *net, struct fib_table *tb, new_fa->tb_id = tb->tb_id; new_fa->fa_default = -1; - err = call_fib_entry_notifiers(net, event, key, plen, new_fa, extack); + /* Insert new entry to the list. */ + err = fib_insert_alias(t, tp, l, new_fa, fa, key); if (err) goto out_free_new_fa; - /* Insert new entry to the list. */ - err = fib_insert_alias(t, tp, l, new_fa, fa, key); + /* The alias was already inserted, so the node must exist. */ + l = l ? l : fib_find_node(t, &tp, key); + if (WARN_ON_ONCE(!l)) + goto out_free_new_fa; + + err = call_fib_entry_notifiers(net, event, key, plen, new_fa, extack); if (err) - goto out_fib_notif; + goto out_remove_new_fa; if (!plen) tb->tb_num_default++; @@ -1287,14 +1292,8 @@ int fib_table_insert(struct net *net, struct fib_table *tb, succeeded: return 0; -out_fib_notif: - /* notifier was sent that entry would be added to trie, but - * the add failed and need to recover. Only failure for - * fib_insert_alias is ENOMEM. - */ - NL_SET_ERR_MSG(extack, "Failed to insert route into trie"); - call_fib_entry_notifiers(net, FIB_EVENT_ENTRY_DEL, key, - plen, new_fa, NULL); +out_remove_new_fa: + fib_remove_alias(t, tp, l, new_fa); out_free_new_fa: kmem_cache_free(fn_alias_kmem, new_fa); out: -- GitLab From b5fc0430dcbb36294b29a25cddb3bf7e92fe2307 Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Sat, 14 Dec 2019 17:53:08 +0200 Subject: [PATCH 0767/1437] ipv4: Extend FIB alias find function Extend the function with another argument, 'find_first'. When set, the function returns the first FIB alias with the matching {prefix, prefix length, table ID}. The TOS and priority parameters are ignored. Current callers are converted to pass 'false' in order to maintain existing behavior. This will be used by subsequent patches in the series. v2: * New patch Signed-off-by: Ido Schimmel Suggested-by: David Ahern Signed-off-by: David S. Miller --- net/ipv4/fib_trie.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/net/ipv4/fib_trie.c b/net/ipv4/fib_trie.c index 9264d6628e9f7..4c22e768a5b53 100644 --- a/net/ipv4/fib_trie.c +++ b/net/ipv4/fib_trie.c @@ -980,9 +980,12 @@ static struct key_vector *fib_find_node(struct trie *t, /* Return the first fib alias matching TOS with * priority less than or equal to PRIO. + * If 'find_first' is set, return the first matching + * fib alias, regardless of TOS and priority. */ static struct fib_alias *fib_find_alias(struct hlist_head *fah, u8 slen, - u8 tos, u32 prio, u32 tb_id) + u8 tos, u32 prio, u32 tb_id, + bool find_first) { struct fib_alias *fa; @@ -998,6 +1001,8 @@ static struct fib_alias *fib_find_alias(struct hlist_head *fah, u8 slen, continue; if (fa->tb_id != tb_id) break; + if (find_first) + return fa; if (fa->fa_tos > tos) continue; if (fa->fa_info->fib_priority >= prio || fa->fa_tos < tos) @@ -1149,7 +1154,7 @@ int fib_table_insert(struct net *net, struct fib_table *tb, l = fib_find_node(t, &tp, key); fa = l ? fib_find_alias(&l->leaf, slen, tos, fi->fib_priority, - tb->tb_id) : NULL; + tb->tb_id, false) : NULL; /* Now fa, if non-NULL, points to the first fib alias * with the same keys [prefix,tos,priority], if such key already @@ -1565,7 +1570,7 @@ int fib_table_delete(struct net *net, struct fib_table *tb, if (!l) return -ESRCH; - fa = fib_find_alias(&l->leaf, slen, tos, 0, tb->tb_id); + fa = fib_find_alias(&l->leaf, slen, tos, 0, tb->tb_id, false); if (!fa) return -ESRCH; -- GitLab From ee3936d658821d26de9039c64c90750ba240989d Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Sat, 14 Dec 2019 17:53:09 +0200 Subject: [PATCH 0768/1437] ipv4: Notify route if replacing currently offloaded one When replacing a route, its replacement should only be notified in case the replaced route is of any interest to listeners. In other words, if the replaced route is currently used in the data path, which means it is the first route in the FIB alias list with the given {prefix, prefix length, table ID}. v2: * Convert to use fib_find_alias() instead of fib_find_first_alias() Signed-off-by: Ido Schimmel Reviewed-by: David Ahern Signed-off-by: David S. Miller --- net/ipv4/fib_trie.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/net/ipv4/fib_trie.c b/net/ipv4/fib_trie.c index 4c22e768a5b53..4c80ac0344f48 100644 --- a/net/ipv4/fib_trie.c +++ b/net/ipv4/fib_trie.c @@ -1222,6 +1222,17 @@ int fib_table_insert(struct net *net, struct fib_table *tb, new_fa->tb_id = tb->tb_id; new_fa->fa_default = -1; + if (fib_find_alias(&l->leaf, fa->fa_slen, 0, 0, + tb->tb_id, true) == fa) { + enum fib_event_type fib_event; + + fib_event = FIB_EVENT_ENTRY_REPLACE_TMP; + err = call_fib_entry_notifiers(net, fib_event, + key, plen, + new_fa, extack); + if (err) + goto out_free_new_fa; + } err = call_fib_entry_notifiers(net, FIB_EVENT_ENTRY_REPLACE, key, plen, new_fa, -- GitLab From a8674f753e36f566d6c1d992ab85323d784281d9 Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Sat, 14 Dec 2019 17:53:10 +0200 Subject: [PATCH 0769/1437] ipv4: Notify newly added route if should be offloaded When a route is added, it should only be notified in case it is the first route in the FIB alias list with the given {prefix, prefix length, table ID}. Otherwise, it is not used in the data path and should not be considered by switch drivers. v2: * Convert to use fib_find_alias() instead of fib_find_first_alias() Signed-off-by: Ido Schimmel Reviewed-by: David Ahern Signed-off-by: David S. Miller --- net/ipv4/fib_trie.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/net/ipv4/fib_trie.c b/net/ipv4/fib_trie.c index 4c80ac0344f48..f56945d00d7ac 100644 --- a/net/ipv4/fib_trie.c +++ b/net/ipv4/fib_trie.c @@ -1295,6 +1295,16 @@ int fib_table_insert(struct net *net, struct fib_table *tb, if (WARN_ON_ONCE(!l)) goto out_free_new_fa; + if (fib_find_alias(&l->leaf, new_fa->fa_slen, 0, 0, tb->tb_id, true) == + new_fa) { + enum fib_event_type fib_event; + + fib_event = FIB_EVENT_ENTRY_REPLACE_TMP; + err = call_fib_entry_notifiers(net, fib_event, key, plen, + new_fa, extack); + if (err) + goto out_remove_new_fa; + } err = call_fib_entry_notifiers(net, event, key, plen, new_fa, extack); if (err) goto out_remove_new_fa; -- GitLab From f613b6e2ffe13e9acba0d330a058b87aa0e19a2a Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Sat, 14 Dec 2019 17:53:11 +0200 Subject: [PATCH 0770/1437] ipv4: Handle route deletion notification When a route is deleted we potentially need to promote the next route in the FIB alias list (e.g., with an higher metric). In case we find such a route, a replace notification is emitted. Otherwise, a delete notification for the deleted route. v2: * Convert to use fib_find_alias() instead of fib_find_first_alias() Signed-off-by: Ido Schimmel Reviewed-by: David Ahern Signed-off-by: David S. Miller --- net/ipv4/fib_trie.c | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/net/ipv4/fib_trie.c b/net/ipv4/fib_trie.c index f56945d00d7ac..012aca433a913 100644 --- a/net/ipv4/fib_trie.c +++ b/net/ipv4/fib_trie.c @@ -1570,6 +1570,36 @@ static void fib_remove_alias(struct trie *t, struct key_vector *tp, node_pull_suffix(tp, fa->fa_slen); } +static void fib_notify_alias_delete(struct net *net, u32 key, + struct hlist_head *fah, + struct fib_alias *fa_to_delete, + struct netlink_ext_ack *extack) +{ + struct fib_alias *fa_next, *fa_to_notify; + u32 tb_id = fa_to_delete->tb_id; + u8 slen = fa_to_delete->fa_slen; + enum fib_event_type fib_event; + + /* Do not notify if we do not care about the route. */ + if (fib_find_alias(fah, slen, 0, 0, tb_id, true) != fa_to_delete) + return; + + /* Determine if the route should be replaced by the next route in the + * list. + */ + fa_next = hlist_entry_safe(fa_to_delete->fa_list.next, + struct fib_alias, fa_list); + if (fa_next && fa_next->fa_slen == slen && fa_next->tb_id == tb_id) { + fib_event = FIB_EVENT_ENTRY_REPLACE_TMP; + fa_to_notify = fa_next; + } else { + fib_event = FIB_EVENT_ENTRY_DEL_TMP; + fa_to_notify = fa_to_delete; + } + call_fib_entry_notifiers(net, fib_event, key, KEYLENGTH - slen, + fa_to_notify, extack); +} + /* Caller must hold RTNL. */ int fib_table_delete(struct net *net, struct fib_table *tb, struct fib_config *cfg, struct netlink_ext_ack *extack) @@ -1623,6 +1653,7 @@ int fib_table_delete(struct net *net, struct fib_table *tb, if (!fa_to_delete) return -ESRCH; + fib_notify_alias_delete(net, key, &l->leaf, fa_to_delete, extack); call_fib_entry_notifiers(net, FIB_EVENT_ENTRY_DEL, key, plen, fa_to_delete, extack); rtmsg_fib(RTM_DELROUTE, htonl(key), fa_to_delete, plen, tb->tb_id, -- GitLab From 525bc345fcbc6f0d58977452999dd18a15dc0e9b Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Sat, 14 Dec 2019 17:53:12 +0200 Subject: [PATCH 0771/1437] ipv4: Handle route deletion notification during flush In a similar fashion to previous patch, when a route is deleted as part of table flushing, promote the next route in the list, if exists. Otherwise, simply emit a delete notification. Signed-off-by: Ido Schimmel Reviewed-by: David Ahern Signed-off-by: David S. Miller --- net/ipv4/fib_trie.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/net/ipv4/fib_trie.c b/net/ipv4/fib_trie.c index 012aca433a913..c23be49ca51cb 100644 --- a/net/ipv4/fib_trie.c +++ b/net/ipv4/fib_trie.c @@ -1979,6 +1979,8 @@ int fib_table_flush(struct net *net, struct fib_table *tb, bool flush_all) continue; } + fib_notify_alias_delete(net, n->key, &n->leaf, fa, + NULL); call_fib_entry_notifiers(net, FIB_EVENT_ENTRY_DEL, n->key, KEYLENGTH - fa->fa_slen, fa, -- GitLab From 20d1565203e68cd32d5db3e293e7323173ad7a0d Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Sat, 14 Dec 2019 17:53:13 +0200 Subject: [PATCH 0772/1437] ipv4: Only Replay routes of interest to new listeners When a new listener is registered to the FIB notification chain it receives a dump of all the available routes in the system. Instead, make sure to only replay the IPv4 routes that are actually used in the data path and are of any interest to the new listener. Signed-off-by: Ido Schimmel Reviewed-by: David Ahern Signed-off-by: David S. Miller --- net/ipv4/fib_trie.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/net/ipv4/fib_trie.c b/net/ipv4/fib_trie.c index c23be49ca51cb..3f2ff97618ec7 100644 --- a/net/ipv4/fib_trie.c +++ b/net/ipv4/fib_trie.c @@ -2080,6 +2080,7 @@ static int fib_leaf_notify(struct key_vector *l, struct fib_table *tb, struct netlink_ext_ack *extack) { struct fib_alias *fa; + int last_slen = -1; int err; hlist_for_each_entry_rcu(fa, &l->leaf, fa_list) { @@ -2099,6 +2100,16 @@ static int fib_leaf_notify(struct key_vector *l, struct fib_table *tb, fa, extack); if (err) return err; + + if (fa->fa_slen == last_slen) + continue; + + last_slen = fa->fa_slen; + err = call_fib_entry_notifier(nb, FIB_EVENT_ENTRY_REPLACE_TMP, + l->key, KEYLENGTH - fa->fa_slen, + fa, extack); + if (err) + return err; } return 0; } -- GitLab From b6a1d871d37a8a1beffb5432631ca77ca3712c84 Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Sat, 14 Dec 2019 17:53:14 +0200 Subject: [PATCH 0773/1437] mlxsw: spectrum_router: Start using new IPv4 route notifications With the new notifications mlxsw does not need to handle identical routes itself, as this is taken care of by the core IPv4 code. Instead, mlxsw only needs to take care of inserting and removing routes from the device. Convert mlxsw to use the new IPv4 route notifications and simplify the code. Signed-off-by: Ido Schimmel Signed-off-by: David S. Miller --- .../ethernet/mellanox/mlxsw/spectrum_router.c | 141 +++--------------- 1 file changed, 20 insertions(+), 121 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c index 30bfe3880fafc..396b27b9cdb45 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c @@ -3845,7 +3845,7 @@ static void mlxsw_sp_nexthop4_event(struct mlxsw_sp *mlxsw_sp, key.fib_nh = fib_nh; nh = mlxsw_sp_nexthop_lookup(mlxsw_sp, key); - if (WARN_ON_ONCE(!nh)) + if (!nh) return; switch (event) { @@ -4780,95 +4780,6 @@ static void mlxsw_sp_fib_node_put(struct mlxsw_sp *mlxsw_sp, mlxsw_sp_vr_put(mlxsw_sp, vr); } -static struct mlxsw_sp_fib4_entry * -mlxsw_sp_fib4_node_entry_find(const struct mlxsw_sp_fib_node *fib_node, - const struct mlxsw_sp_fib4_entry *new4_entry) -{ - struct mlxsw_sp_fib4_entry *fib4_entry; - - list_for_each_entry(fib4_entry, &fib_node->entry_list, common.list) { - if (fib4_entry->tb_id > new4_entry->tb_id) - continue; - if (fib4_entry->tb_id != new4_entry->tb_id) - break; - if (fib4_entry->tos > new4_entry->tos) - continue; - if (fib4_entry->prio >= new4_entry->prio || - fib4_entry->tos < new4_entry->tos) - return fib4_entry; - } - - return NULL; -} - -static int -mlxsw_sp_fib4_node_list_append(struct mlxsw_sp_fib4_entry *fib4_entry, - struct mlxsw_sp_fib4_entry *new4_entry) -{ - struct mlxsw_sp_fib_node *fib_node; - - if (WARN_ON(!fib4_entry)) - return -EINVAL; - - fib_node = fib4_entry->common.fib_node; - list_for_each_entry_from(fib4_entry, &fib_node->entry_list, - common.list) { - if (fib4_entry->tb_id != new4_entry->tb_id || - fib4_entry->tos != new4_entry->tos || - fib4_entry->prio != new4_entry->prio) - break; - } - - list_add_tail(&new4_entry->common.list, &fib4_entry->common.list); - return 0; -} - -static int -mlxsw_sp_fib4_node_list_insert(struct mlxsw_sp_fib4_entry *new4_entry, - bool replace, bool append) -{ - struct mlxsw_sp_fib_node *fib_node = new4_entry->common.fib_node; - struct mlxsw_sp_fib4_entry *fib4_entry; - - fib4_entry = mlxsw_sp_fib4_node_entry_find(fib_node, new4_entry); - - if (append) - return mlxsw_sp_fib4_node_list_append(fib4_entry, new4_entry); - if (replace && WARN_ON(!fib4_entry)) - return -EINVAL; - - /* Insert new entry before replaced one, so that we can later - * remove the second. - */ - if (fib4_entry) { - list_add_tail(&new4_entry->common.list, - &fib4_entry->common.list); - } else { - struct mlxsw_sp_fib4_entry *last; - - list_for_each_entry(last, &fib_node->entry_list, common.list) { - if (new4_entry->tb_id > last->tb_id) - break; - fib4_entry = last; - } - - if (fib4_entry) - list_add(&new4_entry->common.list, - &fib4_entry->common.list); - else - list_add(&new4_entry->common.list, - &fib_node->entry_list); - } - - return 0; -} - -static void -mlxsw_sp_fib4_node_list_remove(struct mlxsw_sp_fib4_entry *fib4_entry) -{ - list_del(&fib4_entry->common.list); -} - static int mlxsw_sp_fib_node_entry_add(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_fib_entry *fib_entry) { @@ -4912,14 +4823,12 @@ static void mlxsw_sp_fib_node_entry_del(struct mlxsw_sp *mlxsw_sp, } static int mlxsw_sp_fib4_node_entry_link(struct mlxsw_sp *mlxsw_sp, - struct mlxsw_sp_fib4_entry *fib4_entry, - bool replace, bool append) + struct mlxsw_sp_fib4_entry *fib4_entry) { + struct mlxsw_sp_fib_node *fib_node = fib4_entry->common.fib_node; int err; - err = mlxsw_sp_fib4_node_list_insert(fib4_entry, replace, append); - if (err) - return err; + list_add(&fib4_entry->common.list, &fib_node->entry_list); err = mlxsw_sp_fib_node_entry_add(mlxsw_sp, &fib4_entry->common); if (err) @@ -4928,7 +4837,7 @@ static int mlxsw_sp_fib4_node_entry_link(struct mlxsw_sp *mlxsw_sp, return 0; err_fib_node_entry_add: - mlxsw_sp_fib4_node_list_remove(fib4_entry); + list_del(&fib4_entry->common.list); return err; } @@ -4937,20 +4846,19 @@ mlxsw_sp_fib4_node_entry_unlink(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_fib4_entry *fib4_entry) { mlxsw_sp_fib_node_entry_del(mlxsw_sp, &fib4_entry->common); - mlxsw_sp_fib4_node_list_remove(fib4_entry); + list_del(&fib4_entry->common.list); if (fib4_entry->common.type == MLXSW_SP_FIB_ENTRY_TYPE_IPIP_DECAP) mlxsw_sp_fib_entry_decap_fini(mlxsw_sp, &fib4_entry->common); } static void mlxsw_sp_fib4_entry_replace(struct mlxsw_sp *mlxsw_sp, - struct mlxsw_sp_fib4_entry *fib4_entry, - bool replace) + struct mlxsw_sp_fib4_entry *fib4_entry) { struct mlxsw_sp_fib_node *fib_node = fib4_entry->common.fib_node; struct mlxsw_sp_fib4_entry *replaced; - if (!replace) + if (list_is_singular(&fib_node->entry_list)) return; /* We inserted the new entry before replaced one */ @@ -4962,9 +4870,8 @@ static void mlxsw_sp_fib4_entry_replace(struct mlxsw_sp *mlxsw_sp, } static int -mlxsw_sp_router_fib4_add(struct mlxsw_sp *mlxsw_sp, - const struct fib_entry_notifier_info *fen_info, - bool replace, bool append) +mlxsw_sp_router_fib4_replace(struct mlxsw_sp *mlxsw_sp, + const struct fib_entry_notifier_info *fen_info) { struct mlxsw_sp_fib4_entry *fib4_entry; struct mlxsw_sp_fib_node *fib_node; @@ -4989,14 +4896,13 @@ mlxsw_sp_router_fib4_add(struct mlxsw_sp *mlxsw_sp, goto err_fib4_entry_create; } - err = mlxsw_sp_fib4_node_entry_link(mlxsw_sp, fib4_entry, replace, - append); + err = mlxsw_sp_fib4_node_entry_link(mlxsw_sp, fib4_entry); if (err) { dev_warn(mlxsw_sp->bus_info->dev, "Failed to link FIB entry to node\n"); goto err_fib4_node_entry_link; } - mlxsw_sp_fib4_entry_replace(mlxsw_sp, fib4_entry, replace); + mlxsw_sp_fib4_entry_replace(mlxsw_sp, fib4_entry); return 0; @@ -6094,7 +6000,6 @@ static void mlxsw_sp_router_fib4_event_work(struct work_struct *work) struct mlxsw_sp_fib_event_work *fib_work = container_of(work, struct mlxsw_sp_fib_event_work, work); struct mlxsw_sp *mlxsw_sp = fib_work->mlxsw_sp; - bool replace, append; int err; /* Protect internal structures from changes */ @@ -6102,18 +6007,14 @@ static void mlxsw_sp_router_fib4_event_work(struct work_struct *work) mlxsw_sp_span_respin(mlxsw_sp); switch (fib_work->event) { - case FIB_EVENT_ENTRY_REPLACE: /* fall through */ - case FIB_EVENT_ENTRY_APPEND: /* fall through */ - case FIB_EVENT_ENTRY_ADD: - replace = fib_work->event == FIB_EVENT_ENTRY_REPLACE; - append = fib_work->event == FIB_EVENT_ENTRY_APPEND; - err = mlxsw_sp_router_fib4_add(mlxsw_sp, &fib_work->fen_info, - replace, append); + case FIB_EVENT_ENTRY_REPLACE_TMP: + err = mlxsw_sp_router_fib4_replace(mlxsw_sp, + &fib_work->fen_info); if (err) mlxsw_sp_router_fib_abort(mlxsw_sp); fib_info_put(fib_work->fen_info.fi); break; - case FIB_EVENT_ENTRY_DEL: + case FIB_EVENT_ENTRY_DEL_TMP: mlxsw_sp_router_fib4_del(mlxsw_sp, &fib_work->fen_info); fib_info_put(fib_work->fen_info.fi); break; @@ -6210,10 +6111,8 @@ static void mlxsw_sp_router_fib4_event(struct mlxsw_sp_fib_event_work *fib_work, struct fib_nh_notifier_info *fnh_info; switch (fib_work->event) { - case FIB_EVENT_ENTRY_REPLACE: /* fall through */ - case FIB_EVENT_ENTRY_APPEND: /* fall through */ - case FIB_EVENT_ENTRY_ADD: /* fall through */ - case FIB_EVENT_ENTRY_DEL: + case FIB_EVENT_ENTRY_REPLACE_TMP: /* fall through */ + case FIB_EVENT_ENTRY_DEL_TMP: fen_info = container_of(info, struct fib_entry_notifier_info, info); fib_work->fen_info = *fen_info; @@ -6343,9 +6242,9 @@ static int mlxsw_sp_router_fib_event(struct notifier_block *nb, err = mlxsw_sp_router_fib_rule_event(event, info, router->mlxsw_sp); return notifier_from_errno(err); - case FIB_EVENT_ENTRY_ADD: + case FIB_EVENT_ENTRY_ADD: /* fall through */ case FIB_EVENT_ENTRY_REPLACE: /* fall through */ - case FIB_EVENT_ENTRY_APPEND: /* fall through */ + case FIB_EVENT_ENTRY_REPLACE_TMP: if (router->aborted) { NL_SET_ERR_MSG_MOD(info->extack, "FIB offload was aborted. Not configuring route"); return notifier_from_errno(-EINVAL); -- GitLab From 446f739104f4da6207230363848ec2c89dfd858d Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Sat, 14 Dec 2019 17:53:15 +0200 Subject: [PATCH 0774/1437] ipv4: Remove old route notifications and convert listeners Unlike mlxsw, the other listeners to the FIB notification chain do not require any special modifications as they never considered multiple identical routes. This patch removes the old route notifications and converts all the listeners to use the new replace / delete notifications. Signed-off-by: Ido Schimmel Reviewed-by: David Ahern Signed-off-by: David S. Miller --- .../net/ethernet/mellanox/mlx5/core/lag_mp.c | 4 -- .../ethernet/mellanox/mlxsw/spectrum_router.c | 11 +++--- drivers/net/ethernet/rocker/rocker_main.c | 4 +- drivers/net/netdevsim/fib.c | 4 +- include/net/fib_notifier.h | 2 - net/ipv4/fib_trie.c | 38 ++++--------------- 6 files changed, 16 insertions(+), 47 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lag_mp.c b/drivers/net/ethernet/mellanox/mlx5/core/lag_mp.c index b70afa310ad2a..416676c35b1f9 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/lag_mp.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/lag_mp.c @@ -200,8 +200,6 @@ static void mlx5_lag_fib_update(struct work_struct *work) rtnl_lock(); switch (fib_work->event) { case FIB_EVENT_ENTRY_REPLACE: /* fall through */ - case FIB_EVENT_ENTRY_APPEND: /* fall through */ - case FIB_EVENT_ENTRY_ADD: /* fall through */ case FIB_EVENT_ENTRY_DEL: mlx5_lag_fib_route_event(ldev, fib_work->event, fib_work->fen_info.fi); @@ -259,8 +257,6 @@ static int mlx5_lag_fib_event(struct notifier_block *nb, switch (event) { case FIB_EVENT_ENTRY_REPLACE: /* fall through */ - case FIB_EVENT_ENTRY_APPEND: /* fall through */ - case FIB_EVENT_ENTRY_ADD: /* fall through */ case FIB_EVENT_ENTRY_DEL: fen_info = container_of(info, struct fib_entry_notifier_info, info); diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c index 396b27b9cdb45..bba1c8215d06c 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c @@ -6007,14 +6007,14 @@ static void mlxsw_sp_router_fib4_event_work(struct work_struct *work) mlxsw_sp_span_respin(mlxsw_sp); switch (fib_work->event) { - case FIB_EVENT_ENTRY_REPLACE_TMP: + case FIB_EVENT_ENTRY_REPLACE: err = mlxsw_sp_router_fib4_replace(mlxsw_sp, &fib_work->fen_info); if (err) mlxsw_sp_router_fib_abort(mlxsw_sp); fib_info_put(fib_work->fen_info.fi); break; - case FIB_EVENT_ENTRY_DEL_TMP: + case FIB_EVENT_ENTRY_DEL: mlxsw_sp_router_fib4_del(mlxsw_sp, &fib_work->fen_info); fib_info_put(fib_work->fen_info.fi); break; @@ -6111,8 +6111,8 @@ static void mlxsw_sp_router_fib4_event(struct mlxsw_sp_fib_event_work *fib_work, struct fib_nh_notifier_info *fnh_info; switch (fib_work->event) { - case FIB_EVENT_ENTRY_REPLACE_TMP: /* fall through */ - case FIB_EVENT_ENTRY_DEL_TMP: + case FIB_EVENT_ENTRY_REPLACE: /* fall through */ + case FIB_EVENT_ENTRY_DEL: fen_info = container_of(info, struct fib_entry_notifier_info, info); fib_work->fen_info = *fen_info; @@ -6243,8 +6243,7 @@ static int mlxsw_sp_router_fib_event(struct notifier_block *nb, router->mlxsw_sp); return notifier_from_errno(err); case FIB_EVENT_ENTRY_ADD: /* fall through */ - case FIB_EVENT_ENTRY_REPLACE: /* fall through */ - case FIB_EVENT_ENTRY_REPLACE_TMP: + case FIB_EVENT_ENTRY_REPLACE: if (router->aborted) { NL_SET_ERR_MSG_MOD(info->extack, "FIB offload was aborted. Not configuring route"); return notifier_from_errno(-EINVAL); diff --git a/drivers/net/ethernet/rocker/rocker_main.c b/drivers/net/ethernet/rocker/rocker_main.c index bc4f951315da8..7585cd2270ba4 100644 --- a/drivers/net/ethernet/rocker/rocker_main.c +++ b/drivers/net/ethernet/rocker/rocker_main.c @@ -2159,7 +2159,7 @@ static void rocker_router_fib_event_work(struct work_struct *work) /* Protect internal structures from changes */ rtnl_lock(); switch (fib_work->event) { - case FIB_EVENT_ENTRY_ADD: + case FIB_EVENT_ENTRY_REPLACE: err = rocker_world_fib4_add(rocker, &fib_work->fen_info); if (err) rocker_world_fib4_abort(rocker); @@ -2201,7 +2201,7 @@ static int rocker_router_fib_event(struct notifier_block *nb, fib_work->event = event; switch (event) { - case FIB_EVENT_ENTRY_ADD: /* fall through */ + case FIB_EVENT_ENTRY_REPLACE: /* fall through */ case FIB_EVENT_ENTRY_DEL: if (info->family == AF_INET) { struct fib_entry_notifier_info *fen_info = ptr; diff --git a/drivers/net/netdevsim/fib.c b/drivers/net/netdevsim/fib.c index 13540dee73646..4e02a4231fcb9 100644 --- a/drivers/net/netdevsim/fib.c +++ b/drivers/net/netdevsim/fib.c @@ -177,10 +177,10 @@ static int nsim_fib_event_nb(struct notifier_block *nb, unsigned long event, event == FIB_EVENT_RULE_ADD); break; + case FIB_EVENT_ENTRY_REPLACE: /* fall through */ case FIB_EVENT_ENTRY_ADD: /* fall through */ case FIB_EVENT_ENTRY_DEL: - err = nsim_fib_event(data, info, - event == FIB_EVENT_ENTRY_ADD); + err = nsim_fib_event(data, info, event != FIB_EVENT_ENTRY_DEL); break; } diff --git a/include/net/fib_notifier.h b/include/net/fib_notifier.h index b3c54325caec0..6d59221ff05ad 100644 --- a/include/net/fib_notifier.h +++ b/include/net/fib_notifier.h @@ -23,8 +23,6 @@ enum fib_event_type { FIB_EVENT_NH_DEL, FIB_EVENT_VIF_ADD, FIB_EVENT_VIF_DEL, - FIB_EVENT_ENTRY_REPLACE_TMP, - FIB_EVENT_ENTRY_DEL_TMP, }; struct fib_notifier_ops { diff --git a/net/ipv4/fib_trie.c b/net/ipv4/fib_trie.c index 3f2ff97618ec7..b92a42433a7db 100644 --- a/net/ipv4/fib_trie.c +++ b/net/ipv4/fib_trie.c @@ -1127,7 +1127,6 @@ static void fib_remove_alias(struct trie *t, struct key_vector *tp, int fib_table_insert(struct net *net, struct fib_table *tb, struct fib_config *cfg, struct netlink_ext_ack *extack) { - enum fib_event_type event = FIB_EVENT_ENTRY_ADD; struct trie *t = (struct trie *)tb->tb_data; struct fib_alias *fa, *new_fa; struct key_vector *l, *tp; @@ -1226,19 +1225,13 @@ int fib_table_insert(struct net *net, struct fib_table *tb, tb->tb_id, true) == fa) { enum fib_event_type fib_event; - fib_event = FIB_EVENT_ENTRY_REPLACE_TMP; + fib_event = FIB_EVENT_ENTRY_REPLACE; err = call_fib_entry_notifiers(net, fib_event, key, plen, new_fa, extack); if (err) goto out_free_new_fa; } - err = call_fib_entry_notifiers(net, - FIB_EVENT_ENTRY_REPLACE, - key, plen, new_fa, - extack); - if (err) - goto out_free_new_fa; rtmsg_fib(RTM_NEWROUTE, htonl(key), new_fa, plen, tb->tb_id, &cfg->fc_nlinfo, nlflags); @@ -1260,12 +1253,10 @@ int fib_table_insert(struct net *net, struct fib_table *tb, if (fa_match) goto out; - if (cfg->fc_nlflags & NLM_F_APPEND) { - event = FIB_EVENT_ENTRY_APPEND; + if (cfg->fc_nlflags & NLM_F_APPEND) nlflags |= NLM_F_APPEND; - } else { + else fa = fa_first; - } } err = -ENOENT; if (!(cfg->fc_nlflags & NLM_F_CREATE)) @@ -1299,15 +1290,12 @@ int fib_table_insert(struct net *net, struct fib_table *tb, new_fa) { enum fib_event_type fib_event; - fib_event = FIB_EVENT_ENTRY_REPLACE_TMP; + fib_event = FIB_EVENT_ENTRY_REPLACE; err = call_fib_entry_notifiers(net, fib_event, key, plen, new_fa, extack); if (err) goto out_remove_new_fa; } - err = call_fib_entry_notifiers(net, event, key, plen, new_fa, extack); - if (err) - goto out_remove_new_fa; if (!plen) tb->tb_num_default++; @@ -1590,10 +1578,10 @@ static void fib_notify_alias_delete(struct net *net, u32 key, fa_next = hlist_entry_safe(fa_to_delete->fa_list.next, struct fib_alias, fa_list); if (fa_next && fa_next->fa_slen == slen && fa_next->tb_id == tb_id) { - fib_event = FIB_EVENT_ENTRY_REPLACE_TMP; + fib_event = FIB_EVENT_ENTRY_REPLACE; fa_to_notify = fa_next; } else { - fib_event = FIB_EVENT_ENTRY_DEL_TMP; + fib_event = FIB_EVENT_ENTRY_DEL; fa_to_notify = fa_to_delete; } call_fib_entry_notifiers(net, fib_event, key, KEYLENGTH - slen, @@ -1654,8 +1642,6 @@ int fib_table_delete(struct net *net, struct fib_table *tb, return -ESRCH; fib_notify_alias_delete(net, key, &l->leaf, fa_to_delete, extack); - call_fib_entry_notifiers(net, FIB_EVENT_ENTRY_DEL, key, plen, - fa_to_delete, extack); rtmsg_fib(RTM_DELROUTE, htonl(key), fa_to_delete, plen, tb->tb_id, &cfg->fc_nlinfo, 0); @@ -1981,10 +1967,6 @@ int fib_table_flush(struct net *net, struct fib_table *tb, bool flush_all) fib_notify_alias_delete(net, n->key, &n->leaf, fa, NULL); - call_fib_entry_notifiers(net, FIB_EVENT_ENTRY_DEL, - n->key, - KEYLENGTH - fa->fa_slen, fa, - NULL); hlist_del_rcu(&fa->fa_list); fib_release_info(fa->fa_info); alias_free_mem_rcu(fa); @@ -2095,17 +2077,11 @@ static int fib_leaf_notify(struct key_vector *l, struct fib_table *tb, if (tb->tb_id != fa->tb_id) continue; - err = call_fib_entry_notifier(nb, FIB_EVENT_ENTRY_ADD, l->key, - KEYLENGTH - fa->fa_slen, - fa, extack); - if (err) - return err; - if (fa->fa_slen == last_slen) continue; last_slen = fa->fa_slen; - err = call_fib_entry_notifier(nb, FIB_EVENT_ENTRY_REPLACE_TMP, + err = call_fib_entry_notifier(nb, FIB_EVENT_ENTRY_REPLACE, l->key, KEYLENGTH - fa->fa_slen, fa, extack); if (err) -- GitLab From f37f710353677639bc5d37ee785335994adf2529 Mon Sep 17 00:00:00 2001 From: Navid Emamdoost Date: Sat, 14 Dec 2019 19:10:44 -0600 Subject: [PATCH 0775/1437] net: gemini: Fix memory leak in gmac_setup_txqs In the implementation of gmac_setup_txqs() the allocated desc_ring is leaked if TX queue base is not aligned. Release it via dma_free_coherent. Fixes: 4d5ae32f5e1e ("net: ethernet: Add a driver for Gemini gigabit ethernet") Signed-off-by: Navid Emamdoost Reviewed-by: Linus Walleij Signed-off-by: David S. Miller --- drivers/net/ethernet/cortina/gemini.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/ethernet/cortina/gemini.c b/drivers/net/ethernet/cortina/gemini.c index a8f4c69252ff8..2814b96751b4f 100644 --- a/drivers/net/ethernet/cortina/gemini.c +++ b/drivers/net/ethernet/cortina/gemini.c @@ -576,6 +576,8 @@ static int gmac_setup_txqs(struct net_device *netdev) if (port->txq_dma_base & ~DMA_Q_BASE_MASK) { dev_warn(geth->dev, "TX queue base is not aligned\n"); + dma_free_coherent(geth->dev, len * sizeof(*desc_ring), + desc_ring, port->txq_dma_base); kfree(skb_tab); return -ENOMEM; } -- GitLab From bbd20c939c8aa3f27fa30e86691af250bf92973a Mon Sep 17 00:00:00 2001 From: Aditya Pakki Date: Sun, 15 Dec 2019 10:14:51 -0600 Subject: [PATCH 0776/1437] fore200e: Fix incorrect checks of NULL pointer dereference In fore200e_send and fore200e_close, the pointers from the arguments are dereferenced in the variable declaration block and then checked for NULL. The patch fixes these issues by avoiding NULL pointer dereferences. Signed-off-by: Aditya Pakki Signed-off-by: David S. Miller --- drivers/atm/fore200e.c | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/drivers/atm/fore200e.c b/drivers/atm/fore200e.c index f1a5002053132..8fbd36eb89410 100644 --- a/drivers/atm/fore200e.c +++ b/drivers/atm/fore200e.c @@ -1414,12 +1414,14 @@ fore200e_open(struct atm_vcc *vcc) static void fore200e_close(struct atm_vcc* vcc) { - struct fore200e* fore200e = FORE200E_DEV(vcc->dev); struct fore200e_vcc* fore200e_vcc; + struct fore200e* fore200e; struct fore200e_vc_map* vc_map; unsigned long flags; ASSERT(vcc); + fore200e = FORE200E_DEV(vcc->dev); + ASSERT((vcc->vpi >= 0) && (vcc->vpi < 1<vci >= 0) && (vcc->vci < 1<dev); - struct fore200e_vcc* fore200e_vcc = FORE200E_VCC(vcc); + struct fore200e* fore200e; + struct fore200e_vcc* fore200e_vcc; struct fore200e_vc_map* vc_map; - struct host_txq* txq = &fore200e->host_txq; + struct host_txq* txq; struct host_txq_entry* entry; struct tpd* tpd; struct tpd_haddr tpd_haddr; @@ -1480,9 +1482,18 @@ fore200e_send(struct atm_vcc *vcc, struct sk_buff *skb) unsigned char* data; unsigned long flags; - ASSERT(vcc); - ASSERT(fore200e); - ASSERT(fore200e_vcc); + if (!vcc) + return -EINVAL; + + fore200e = FORE200E_DEV(vcc->dev); + fore200e_vcc = FORE200E_VCC(vcc); + + if (!fore200e) + return -EINVAL; + + txq = &fore200e->host_txq; + if (!fore200e_vcc) + return -EINVAL; if (!test_bit(ATM_VF_READY, &vcc->flags)) { DPRINTK(1, "VC %d.%d.%d not ready for tx\n", vcc->itf, vcc->vpi, vcc->vpi); -- GitLab From c5dea815834c7d2e9fc633785455bc428b7a1956 Mon Sep 17 00:00:00 2001 From: Aditya Pakki Date: Sun, 15 Dec 2019 11:51:30 -0600 Subject: [PATCH 0777/1437] net: caif: replace BUG_ON with recovery code In caif_xmit, there is a crash if the ptr dev is NULL. However, by returning the error to the callers, the error can be handled. The patch fixes this issue. Signed-off-by: Aditya Pakki Signed-off-by: David S. Miller --- drivers/net/caif/caif_serial.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/net/caif/caif_serial.c b/drivers/net/caif/caif_serial.c index bd40b114d6cd7..d737ceb612031 100644 --- a/drivers/net/caif/caif_serial.c +++ b/drivers/net/caif/caif_serial.c @@ -270,7 +270,9 @@ static int caif_xmit(struct sk_buff *skb, struct net_device *dev) { struct ser_device *ser; - BUG_ON(dev == NULL); + if (WARN_ON(!dev)) + return -EINVAL; + ser = netdev_priv(dev); /* Send flow off once, on high water mark */ -- GitLab From 21df4a8b6018b842d4db181a8b24166006bad3cd Mon Sep 17 00:00:00 2001 From: Jerry Snitselaar Date: Wed, 11 Dec 2019 16:54:55 -0700 Subject: [PATCH 0778/1437] tpm_tis: reserve chip for duration of tpm_tis_core_init Instead of repeatedly calling tpm_chip_start/tpm_chip_stop when issuing commands to the tpm during initialization, just reserve the chip after wait_startup, and release it when we are ready to call tpm_chip_register. Cc: Christian Bundy Cc: Dan Williams Cc: Peter Huewe Cc: Jarkko Sakkinen Cc: Jason Gunthorpe Cc: Stefan Berger Cc: stable@vger.kernel.org Cc: linux-integrity@vger.kernel.org Fixes: a3fbfae82b4c ("tpm: take TPM chip power gating out of tpm_transmit()") Fixes: 5b359c7c4372 ("tpm_tis_core: Turn on the TPM before probing IRQ's") Suggested-by: Jarkko Sakkinen Reviewed-by: Jarkko Sakkinen Signed-off-by: Jerry Snitselaar Signed-off-by: Jarkko Sakkinen --- drivers/char/tpm/tpm_tis_core.c | 35 +++++++++++++++++---------------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/drivers/char/tpm/tpm_tis_core.c b/drivers/char/tpm/tpm_tis_core.c index 8af2cee1a762c..bb0343ffd2357 100644 --- a/drivers/char/tpm/tpm_tis_core.c +++ b/drivers/char/tpm/tpm_tis_core.c @@ -978,13 +978,13 @@ int tpm_tis_core_init(struct device *dev, struct tpm_tis_data *priv, int irq, if (wait_startup(chip, 0) != 0) { rc = -ENODEV; - goto out_err; + goto err_start; } /* Take control of the TPM's interrupt hardware and shut it off */ rc = tpm_tis_read32(priv, TPM_INT_ENABLE(priv->locality), &intmask); if (rc < 0) - goto out_err; + goto err_start; intmask |= TPM_INTF_CMD_READY_INT | TPM_INTF_LOCALITY_CHANGE_INT | TPM_INTF_DATA_AVAIL_INT | TPM_INTF_STS_VALID_INT; @@ -993,21 +993,21 @@ int tpm_tis_core_init(struct device *dev, struct tpm_tis_data *priv, int irq, rc = tpm_chip_start(chip); if (rc) - goto out_err; + goto err_start; + rc = tpm2_probe(chip); - tpm_chip_stop(chip); if (rc) - goto out_err; + goto err_probe; rc = tpm_tis_read32(priv, TPM_DID_VID(0), &vendor); if (rc < 0) - goto out_err; + goto err_probe; priv->manufacturer_id = vendor; rc = tpm_tis_read8(priv, TPM_RID(0), &rid); if (rc < 0) - goto out_err; + goto err_probe; dev_info(dev, "%s TPM (device-id 0x%X, rev-id %d)\n", (chip->flags & TPM_CHIP_FLAG_TPM2) ? "2.0" : "1.2", @@ -1016,13 +1016,13 @@ int tpm_tis_core_init(struct device *dev, struct tpm_tis_data *priv, int irq, probe = probe_itpm(chip); if (probe < 0) { rc = -ENODEV; - goto out_err; + goto err_probe; } /* Figure out the capabilities */ rc = tpm_tis_read32(priv, TPM_INTF_CAPS(priv->locality), &intfcaps); if (rc < 0) - goto out_err; + goto err_probe; dev_dbg(dev, "TPM interface capabilities (0x%x):\n", intfcaps); @@ -1056,10 +1056,9 @@ int tpm_tis_core_init(struct device *dev, struct tpm_tis_data *priv, int irq, if (tpm_get_timeouts(chip)) { dev_err(dev, "Could not get TPM timeouts and durations\n"); rc = -ENODEV; - goto out_err; + goto err_probe; } - tpm_chip_start(chip); chip->flags |= TPM_CHIP_FLAG_IRQ; if (irq) { tpm_tis_probe_irq_single(chip, intmask, IRQF_SHARED, @@ -1070,18 +1069,20 @@ int tpm_tis_core_init(struct device *dev, struct tpm_tis_data *priv, int irq, } else { tpm_tis_probe_irq(chip, intmask); } - tpm_chip_stop(chip); } + tpm_chip_stop(chip); + rc = tpm_chip_register(chip); if (rc) - goto out_err; - - if (chip->ops->clk_enable != NULL) - chip->ops->clk_enable(chip, false); + goto err_start; return 0; -out_err: + +err_probe: + tpm_chip_stop(chip); + +err_start: if ((chip->ops != NULL) && (chip->ops->clk_enable != NULL)) chip->ops->clk_enable(chip, false); -- GitLab From ee560e7bbab0c10cf3f0e71997fbc354ab2ee5cb Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Tue, 3 Dec 2019 12:36:52 +0300 Subject: [PATCH 0779/1437] scsi: mpt3sas: Fix double free in attach error handling The caller also calls _base_release_memory_pools() on error so it leads to a number of double frees: drivers/scsi/mpt3sas/mpt3sas_base.c:7207 mpt3sas_base_attach() warn: 'ioc->chain_dma_pool' double freed drivers/scsi/mpt3sas/mpt3sas_base.c:7207 mpt3sas_base_attach() warn: 'ioc->hpr_lookup' double freed drivers/scsi/mpt3sas/mpt3sas_base.c:7207 mpt3sas_base_attach() warn: 'ioc->internal_lookup' double freed drivers/scsi/mpt3sas/mpt3sas_base.c:7207 mpt3sas_base_attach() warn: 'ioc->pcie_sgl_dma_pool' double freed drivers/scsi/mpt3sas/mpt3sas_base.c:7207 mpt3sas_base_attach() warn: 'ioc->reply_dma_pool' double freed drivers/scsi/mpt3sas/mpt3sas_base.c:7207 mpt3sas_base_attach() warn: 'ioc->reply_free_dma_pool' double freed drivers/scsi/mpt3sas/mpt3sas_base.c:7207 mpt3sas_base_attach() warn: 'ioc->reply_post_free_array_dma_pool' double freed drivers/scsi/mpt3sas/mpt3sas_base.c:7207 mpt3sas_base_attach() warn: 'ioc->reply_post_free_dma_pool' double freed drivers/scsi/mpt3sas/mpt3sas_base.c:7207 mpt3sas_base_attach() warn: 'ioc->sense_dma_pool' double freed Fixes: 74522a92bbf0 ("scsi: mpt3sas: Optimize I/O memory consumption in driver.") Link: https://lore.kernel.org/r/20191203093652.gyntgvnkw2udatyc@kili.mountain Signed-off-by: Dan Carpenter Acked-by: Sreekanth Reddy Signed-off-by: Martin K. Petersen --- drivers/scsi/mpt3sas/mpt3sas_base.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/scsi/mpt3sas/mpt3sas_base.c b/drivers/scsi/mpt3sas/mpt3sas_base.c index 848fbec7bda6a..45fd8dfb7c405 100644 --- a/drivers/scsi/mpt3sas/mpt3sas_base.c +++ b/drivers/scsi/mpt3sas/mpt3sas_base.c @@ -5248,7 +5248,6 @@ _base_allocate_memory_pools(struct MPT3SAS_ADAPTER *ioc) &ct->chain_buffer_dma); if (!ct->chain_buffer) { ioc_err(ioc, "chain_lookup: pci_pool_alloc failed\n"); - _base_release_memory_pools(ioc); goto out; } } -- GitLab From 201743b99c0548ffa8ddbd2571b1dcf4d79b73a9 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Mon, 16 Dec 2019 14:16:49 +0100 Subject: [PATCH 0780/1437] scsi: lpfc: fix build failure with DEBUGFS disabled A recent change appears to have moved an #endif by accident: drivers/scsi/lpfc/lpfc_debugfs.c:5393:18: error: 'lpfc_debugfs_dumpHBASlim_open' undeclared here (not in a function); did you mean 'lpfc_debugfs_op_dumpHBASlim'? drivers/scsi/lpfc/lpfc_debugfs.c:5394:18: error: 'lpfc_debugfs_lseek' undeclared here (not in a function); did you mean 'lpfc_debugfs_nvme_trc'? drivers/scsi/lpfc/lpfc_debugfs.c:5395:18: error: 'lpfc_debugfs_read' undeclared here (not in a function); did you mean 'lpfc_debug_dump_q'? drivers/scsi/lpfc/lpfc_debugfs.c:5396:18: error: 'lpfc_debugfs_release' undeclared here (not in a function); did you mean 'lpfc_debugfs_terminate'? drivers/scsi/lpfc/lpfc_debugfs.c:5402:18: error: 'lpfc_debugfs_dumpHostSlim_open' undeclared here (not in a function); did you mean 'lpfc_debugfs_op_dumpHostSlim'? Move it back to where it was previously. Fixes: 95bfc6d8ad86 ("scsi: lpfc: Make FW logging dynamically configurable") Link: https://lore.kernel.org/r/20191216131701.3125077-1-arnd@arndb.de Signed-off-by: Arnd Bergmann Reviewed-by: James Smart Signed-off-by: Martin K. Petersen --- drivers/scsi/lpfc/lpfc_debugfs.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/scsi/lpfc/lpfc_debugfs.c b/drivers/scsi/lpfc/lpfc_debugfs.c index 2e6a68d9ea4fe..a5ecbce4eda23 100644 --- a/drivers/scsi/lpfc/lpfc_debugfs.c +++ b/drivers/scsi/lpfc/lpfc_debugfs.c @@ -5385,7 +5385,6 @@ static const struct file_operations lpfc_debugfs_ras_log = { .read = lpfc_debugfs_read, .release = lpfc_debugfs_ras_log_release, }; -#endif #undef lpfc_debugfs_op_dumpHBASlim static const struct file_operations lpfc_debugfs_op_dumpHBASlim = { @@ -5557,7 +5556,7 @@ static const struct file_operations lpfc_idiag_op_extAcc = { .write = lpfc_idiag_extacc_write, .release = lpfc_idiag_cmd_release, }; - +#endif /* lpfc_idiag_mbxacc_dump_bsg_mbox - idiag debugfs dump bsg mailbox command * @phba: Pointer to HBA context object. -- GitLab From 5e822e44cecec1ea48925630aa31dfac827fd202 Mon Sep 17 00:00:00 2001 From: Gao Fred Date: Tue, 17 Dec 2019 11:19:58 +0800 Subject: [PATCH 0781/1437] drm/i915/gvt: Fix guest boot warning Simulate MIA core in reset status once GUC engine is reset. v2: 1. use vgpu_vreg_t() function, 2. clear MIA_IN_RESET after reading. (Zhenyu) v3: add comments. (Zhenyu) Signed-off-by: Gao Fred Reviewed-by: Zhenyu Wang Signed-off-by: Zhenyu Wang Link: http://patchwork.freedesktop.org/patch/msgid/20191216160255.29499-1-fred.gao@intel.com --- drivers/gpu/drm/i915/gvt/handlers.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/drivers/gpu/drm/i915/gvt/handlers.c b/drivers/gpu/drm/i915/gvt/handlers.c index bb9fe6bf5275d..1043e6d564df3 100644 --- a/drivers/gpu/drm/i915/gvt/handlers.c +++ b/drivers/gpu/drm/i915/gvt/handlers.c @@ -341,6 +341,10 @@ static int gdrst_mmio_write(struct intel_vgpu *vgpu, unsigned int offset, gvt_dbg_mmio("vgpu%d: request VCS2 Reset\n", vgpu->id); engine_mask |= BIT(VCS1); } + if (data & GEN9_GRDOM_GUC) { + gvt_dbg_mmio("vgpu%d: request GUC Reset\n", vgpu->id); + vgpu_vreg_t(vgpu, GUC_STATUS) |= GS_MIA_IN_RESET; + } engine_mask &= INTEL_INFO(vgpu->gvt->dev_priv)->engine_mask; } @@ -1636,6 +1640,16 @@ static int edp_psr_imr_iir_write(struct intel_vgpu *vgpu, return 0; } +static int guc_status_read(struct intel_vgpu *vgpu, + unsigned int offset, void *p_data, + unsigned int bytes) +{ + /* keep MIA_IN_RESET before clearing */ + read_vreg(vgpu, offset, p_data, bytes); + vgpu_vreg(vgpu, offset) &= ~GS_MIA_IN_RESET; + return 0; +} + static int mmio_read_from_hw(struct intel_vgpu *vgpu, unsigned int offset, void *p_data, unsigned int bytes) { @@ -2672,6 +2686,8 @@ static int init_generic_mmio_info(struct intel_gvt *gvt) MMIO_DH(EDP_PSR_IMR, D_BDW_PLUS, NULL, edp_psr_imr_iir_write); MMIO_DH(EDP_PSR_IIR, D_BDW_PLUS, NULL, edp_psr_imr_iir_write); + MMIO_DH(GUC_STATUS, D_ALL, guc_status_read, NULL); + return 0; } -- GitLab From 65d88d04114bca7d85faebd5fed61069cb2b632c Mon Sep 17 00:00:00 2001 From: "Jason A. Donenfeld" Date: Sun, 15 Dec 2019 22:08:00 +0100 Subject: [PATCH 0782/1437] wireguard: selftests: import harness makefile for test suite WireGuard has been using this on build.wireguard.com for the last several years with considerable success. It allows for very quick and iterative development cycles, and supports several platforms. To run the test suite on your current platform in QEMU: $ make -C tools/testing/selftests/wireguard/qemu -j$(nproc) To run it with KASAN and such turned on: $ DEBUG_KERNEL=yes make -C tools/testing/selftests/wireguard/qemu -j$(nproc) To run it emulated for another platform in QEMU: $ ARCH=arm make -C tools/testing/selftests/wireguard/qemu -j$(nproc) At the moment, we support aarch64_be, aarch64, arm, armeb, i686, m68k, mips64, mips64el, mips, mipsel, powerpc64le, powerpc, and x86_64. The system supports incremental rebuilding, so it should be very fast to change a single file and then test it out and have immediate feedback. This requires for the right toolchain and qemu to be installed prior. I've had success with those from musl.cc. This is tailored for WireGuard at the moment, though later projects might generalize it for other network testing. Signed-off-by: Jason A. Donenfeld Signed-off-by: David S. Miller --- .../selftests/wireguard/qemu/.gitignore | 2 + .../testing/selftests/wireguard/qemu/Makefile | 385 ++++++++++++++++++ .../wireguard/qemu/arch/aarch64.config | 5 + .../wireguard/qemu/arch/aarch64_be.config | 6 + .../selftests/wireguard/qemu/arch/arm.config | 9 + .../wireguard/qemu/arch/armeb.config | 10 + .../selftests/wireguard/qemu/arch/i686.config | 5 + .../selftests/wireguard/qemu/arch/m68k.config | 9 + .../selftests/wireguard/qemu/arch/mips.config | 11 + .../wireguard/qemu/arch/mips64.config | 14 + .../wireguard/qemu/arch/mips64el.config | 15 + .../wireguard/qemu/arch/mipsel.config | 12 + .../wireguard/qemu/arch/powerpc.config | 10 + .../wireguard/qemu/arch/powerpc64le.config | 12 + .../wireguard/qemu/arch/x86_64.config | 5 + .../selftests/wireguard/qemu/debug.config | 67 +++ tools/testing/selftests/wireguard/qemu/init.c | 284 +++++++++++++ .../selftests/wireguard/qemu/kernel.config | 86 ++++ 18 files changed, 947 insertions(+) create mode 100644 tools/testing/selftests/wireguard/qemu/.gitignore create mode 100644 tools/testing/selftests/wireguard/qemu/Makefile create mode 100644 tools/testing/selftests/wireguard/qemu/arch/aarch64.config create mode 100644 tools/testing/selftests/wireguard/qemu/arch/aarch64_be.config create mode 100644 tools/testing/selftests/wireguard/qemu/arch/arm.config create mode 100644 tools/testing/selftests/wireguard/qemu/arch/armeb.config create mode 100644 tools/testing/selftests/wireguard/qemu/arch/i686.config create mode 100644 tools/testing/selftests/wireguard/qemu/arch/m68k.config create mode 100644 tools/testing/selftests/wireguard/qemu/arch/mips.config create mode 100644 tools/testing/selftests/wireguard/qemu/arch/mips64.config create mode 100644 tools/testing/selftests/wireguard/qemu/arch/mips64el.config create mode 100644 tools/testing/selftests/wireguard/qemu/arch/mipsel.config create mode 100644 tools/testing/selftests/wireguard/qemu/arch/powerpc.config create mode 100644 tools/testing/selftests/wireguard/qemu/arch/powerpc64le.config create mode 100644 tools/testing/selftests/wireguard/qemu/arch/x86_64.config create mode 100644 tools/testing/selftests/wireguard/qemu/debug.config create mode 100644 tools/testing/selftests/wireguard/qemu/init.c create mode 100644 tools/testing/selftests/wireguard/qemu/kernel.config diff --git a/tools/testing/selftests/wireguard/qemu/.gitignore b/tools/testing/selftests/wireguard/qemu/.gitignore new file mode 100644 index 0000000000000..415b542a9d599 --- /dev/null +++ b/tools/testing/selftests/wireguard/qemu/.gitignore @@ -0,0 +1,2 @@ +build/ +distfiles/ diff --git a/tools/testing/selftests/wireguard/qemu/Makefile b/tools/testing/selftests/wireguard/qemu/Makefile new file mode 100644 index 0000000000000..6d51bf78eeff4 --- /dev/null +++ b/tools/testing/selftests/wireguard/qemu/Makefile @@ -0,0 +1,385 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Copyright (C) 2015-2019 Jason A. Donenfeld . All Rights Reserved. + +PWD := $(shell pwd) + +CHOST := $(shell gcc -dumpmachine) +ifneq (,$(ARCH)) +CBUILD := $(subst -gcc,,$(lastword $(subst /, ,$(firstword $(wildcard $(foreach bindir,$(subst :, ,$(PATH)),$(bindir)/$(ARCH)-*-gcc)))))) +ifeq (,$(CBUILD)) +$(error The toolchain for $(ARCH) is not installed) +endif +else +CBUILD := $(CHOST) +ARCH := $(firstword $(subst -, ,$(CBUILD))) +endif + +# Set these from the environment to override +KERNEL_PATH ?= $(PWD)/../../../../.. +BUILD_PATH ?= $(PWD)/build/$(ARCH) +DISTFILES_PATH ?= $(PWD)/distfiles +NR_CPUS ?= 4 + +MIRROR := https://download.wireguard.com/qemu-test/distfiles/ + +default: qemu + +# variable name, tarball project name, version, tarball extension, default URI base +define tar_download = +$(1)_VERSION := $(3) +$(1)_NAME := $(2)-$$($(1)_VERSION) +$(1)_TAR := $(DISTFILES_PATH)/$$($(1)_NAME)$(4) +$(1)_PATH := $(BUILD_PATH)/$$($(1)_NAME) +$(call file_download,$$($(1)_NAME)$(4),$(5),$(6)) +endef + +define file_download = +$(DISTFILES_PATH)/$(1): + mkdir -p $(DISTFILES_PATH) + flock -x $$@.lock -c '[ -f $$@ ] && exit 0; wget -O $$@.tmp $(MIRROR)$(1) || wget -t inf --retry-on-http-error=404 -O $$@.tmp $(2)$(1) || rm -f $$@.tmp' + if echo "$(3) $$@.tmp" | sha256sum -c -; then mv $$@.tmp $$@; else rm -f $$@.tmp; exit 71; fi +endef + +$(eval $(call tar_download,MUSL,musl,1.1.20,.tar.gz,https://www.musl-libc.org/releases/,44be8771d0e6c6b5f82dd15662eb2957c9a3173a19a8b49966ac0542bbd40d61)) +$(eval $(call tar_download,LIBMNL,libmnl,1.0.4,.tar.bz2,https://www.netfilter.org/projects/libmnl/files/,171f89699f286a5854b72b91d06e8f8e3683064c5901fb09d954a9ab6f551f81)) +$(eval $(call tar_download,IPERF,iperf,3.1.7,.tar.gz,http://downloads.es.net/pub/iperf/,a4ef73406fe92250602b8da2ae89ec53211f805df97a1d1d629db5a14043734f)) +$(eval $(call tar_download,BASH,bash,5.0,.tar.gz,https://ftp.gnu.org/gnu/bash/,b4a80f2ac66170b2913efbfb9f2594f1f76c7b1afd11f799e22035d63077fb4d)) +$(eval $(call tar_download,IPROUTE2,iproute2,5.1.0,.tar.gz,https://www.kernel.org/pub/linux/utils/net/iproute2/,9b43707d6075ecdca14803ca8ce0c8553848c49fa1586d12fd508d66577243f2)) +$(eval $(call tar_download,IPTABLES,iptables,1.6.1,.tar.bz2,https://www.netfilter.org/projects/iptables/files/,0fc2d7bd5d7be11311726466789d4c65fb4c8e096c9182b56ce97440864f0cf5)) +$(eval $(call tar_download,NMAP,nmap,7.60,.tar.bz2,https://nmap.org/dist/,a8796ecc4fa6c38aad6139d9515dc8113023a82e9d787e5a5fb5fa1b05516f21)) +$(eval $(call tar_download,IPUTILS,iputils,s20161105,.tar.gz,https://github.com/iputils/iputils/archive/s20161105.tar.gz/#,f813092f03d17294fd23544b129b95cdb87fe19f7970a51908a6b88509acad8a)) +$(eval $(call tar_download,WIREGUARD_TOOLS,WireGuard,0.0.20191212,.tar.xz,https://git.zx2c4.com/WireGuard/snapshot/,b0d718380f7a8822b2f12d75e462fa4eafa3a77871002981f367cd4fe2a1b071)) + +KERNEL_BUILD_PATH := $(BUILD_PATH)/kernel$(if $(findstring yes,$(DEBUG_KERNEL)),-debug) +rwildcard=$(foreach d,$(wildcard $1*),$(call rwildcard,$d/,$2) $(filter $(subst *,%,$2),$d)) +WIREGUARD_SOURCES := $(call rwildcard,$(KERNEL_PATH)/drivers/net/wireguard/,*) + +export CFLAGS ?= -O3 -pipe +export LDFLAGS ?= +export CPPFLAGS := -I$(BUILD_PATH)/include + +ifeq ($(CHOST),$(CBUILD)) +CROSS_COMPILE_FLAG := --host=$(CHOST) +NOPIE_GCC := gcc -fno-PIE +CFLAGS += -march=native +STRIP := strip +else +$(info Cross compilation: building for $(CBUILD) using $(CHOST)) +CROSS_COMPILE_FLAG := --build=$(CBUILD) --host=$(CHOST) +export CROSS_COMPILE=$(CBUILD)- +NOPIE_GCC := $(CBUILD)-gcc -fno-PIE +STRIP := $(CBUILD)-strip +endif +ifeq ($(ARCH),aarch64) +QEMU_ARCH := aarch64 +KERNEL_ARCH := arm64 +KERNEL_BZIMAGE := $(KERNEL_BUILD_PATH)/arch/arm64/boot/Image +ifeq ($(CHOST),$(CBUILD)) +QEMU_MACHINE := -cpu host -machine virt,gic_version=host,accel=kvm +else +QEMU_MACHINE := -cpu cortex-a53 -machine virt +CFLAGS += -march=armv8-a -mtune=cortex-a53 +endif +else ifeq ($(ARCH),aarch64_be) +QEMU_ARCH := aarch64 +KERNEL_ARCH := arm64 +KERNEL_BZIMAGE := $(KERNEL_BUILD_PATH)/arch/arm64/boot/Image +ifeq ($(CHOST),$(CBUILD)) +QEMU_MACHINE := -cpu host -machine virt,gic_version=host,accel=kvm +else +QEMU_MACHINE := -cpu cortex-a53 -machine virt +CFLAGS += -march=armv8-a -mtune=cortex-a53 +endif +else ifeq ($(ARCH),arm) +QEMU_ARCH := arm +KERNEL_ARCH := arm +KERNEL_BZIMAGE := $(KERNEL_BUILD_PATH)/arch/arm/boot/zImage +ifeq ($(CHOST),$(CBUILD)) +QEMU_MACHINE := -cpu host -machine virt,gic_version=host,accel=kvm +else +QEMU_MACHINE := -cpu cortex-a15 -machine virt +CFLAGS += -march=armv7-a -mtune=cortex-a15 -mabi=aapcs-linux +endif +else ifeq ($(ARCH),armeb) +QEMU_ARCH := arm +KERNEL_ARCH := arm +KERNEL_BZIMAGE := $(KERNEL_BUILD_PATH)/arch/arm/boot/zImage +ifeq ($(CHOST),$(CBUILD)) +QEMU_MACHINE := -cpu host -machine virt,gic_version=host,accel=kvm +else +QEMU_MACHINE := -cpu cortex-a15 -machine virt +CFLAGS += -march=armv7-a -mabi=aapcs-linux # We don't pass -mtune=cortex-a15 due to a compiler bug on big endian. +LDFLAGS += -Wl,--be8 +endif +else ifeq ($(ARCH),x86_64) +QEMU_ARCH := x86_64 +KERNEL_ARCH := x86_64 +KERNEL_BZIMAGE := $(KERNEL_BUILD_PATH)/arch/x86/boot/bzImage +ifeq ($(CHOST),$(CBUILD)) +QEMU_MACHINE := -cpu host -machine q35,accel=kvm +else +QEMU_MACHINE := -cpu Skylake-Server -machine q35 +CFLAGS += -march=skylake-avx512 +endif +else ifeq ($(ARCH),i686) +QEMU_ARCH := i386 +KERNEL_ARCH := x86 +KERNEL_BZIMAGE := $(KERNEL_BUILD_PATH)/arch/x86/boot/bzImage +ifeq ($(subst i686,x86_64,$(CBUILD)),$(CHOST)) +QEMU_MACHINE := -cpu host -machine q35,accel=kvm +else +QEMU_MACHINE := -cpu coreduo -machine q35 +CFLAGS += -march=prescott +endif +else ifeq ($(ARCH),mips64) +QEMU_ARCH := mips64 +KERNEL_ARCH := mips +KERNEL_BZIMAGE := $(KERNEL_BUILD_PATH)/vmlinux +ifeq ($(CHOST),$(CBUILD)) +QEMU_MACHINE := -cpu host -machine malta,accel=kvm +CFLAGS += -EB +else +QEMU_MACHINE := -cpu MIPS64R2-generic -machine malta -smp 1 +CFLAGS += -march=mips64r2 -EB +endif +else ifeq ($(ARCH),mips64el) +QEMU_ARCH := mips64el +KERNEL_ARCH := mips +KERNEL_BZIMAGE := $(KERNEL_BUILD_PATH)/vmlinux +ifeq ($(CHOST),$(CBUILD)) +QEMU_MACHINE := -cpu host -machine malta,accel=kvm +CFLAGS += -EL +else +QEMU_MACHINE := -cpu MIPS64R2-generic -machine malta -smp 1 +CFLAGS += -march=mips64r2 -EL +endif +else ifeq ($(ARCH),mips) +QEMU_ARCH := mips +KERNEL_ARCH := mips +KERNEL_BZIMAGE := $(KERNEL_BUILD_PATH)/vmlinux +ifeq ($(CHOST),$(CBUILD)) +QEMU_MACHINE := -cpu host -machine malta,accel=kvm +CFLAGS += -EB +else +QEMU_MACHINE := -cpu 24Kf -machine malta -smp 1 +CFLAGS += -march=mips32r2 -EB +endif +else ifeq ($(ARCH),mipsel) +QEMU_ARCH := mipsel +KERNEL_ARCH := mips +KERNEL_BZIMAGE := $(KERNEL_BUILD_PATH)/vmlinux +ifeq ($(CHOST),$(CBUILD)) +QEMU_MACHINE := -cpu host -machine malta,accel=kvm +CFLAGS += -EL +else +QEMU_MACHINE := -cpu 24Kf -machine malta -smp 1 +CFLAGS += -march=mips32r2 -EL +endif +else ifeq ($(ARCH),powerpc64le) +QEMU_ARCH := ppc64 +KERNEL_ARCH := powerpc +KERNEL_BZIMAGE := $(KERNEL_BUILD_PATH)/vmlinux +ifeq ($(CHOST),$(CBUILD)) +QEMU_MACHINE := -cpu host,accel=kvm -machine pseries +else +QEMU_MACHINE := -machine pseries +endif +CFLAGS += -mcpu=powerpc64le -mlong-double-64 +else ifeq ($(ARCH),powerpc) +QEMU_ARCH := ppc +KERNEL_ARCH := powerpc +KERNEL_BZIMAGE := $(KERNEL_BUILD_PATH)/arch/powerpc/boot/uImage +ifeq ($(CHOST),$(CBUILD)) +QEMU_MACHINE := -cpu host,accel=kvm -machine ppce500 +else +QEMU_MACHINE := -machine ppce500 +endif +CFLAGS += -mcpu=powerpc -mlong-double-64 -msecure-plt +else ifeq ($(ARCH),m68k) +QEMU_ARCH := m68k +KERNEL_ARCH := m68k +KERNEL_BZIMAGE := $(KERNEL_BUILD_PATH)/vmlinux +ifeq ($(CHOST),$(CBUILD)) +QEMU_MACHINE := -cpu host,accel=kvm -machine q800 +else +QEMU_MACHINE := -machine q800 +endif +else +$(error I only build: x86_64, i686, arm, armeb, aarch64, aarch64_be, mips, mipsel, mips64, mips64el, powerpc64le, powerpc, m68k) +endif + +REAL_CC := $(CBUILD)-gcc +MUSL_CC := $(BUILD_PATH)/musl-gcc +export CC := $(MUSL_CC) +USERSPACE_DEPS := $(MUSL_CC) $(BUILD_PATH)/include/.installed $(BUILD_PATH)/include/linux/.installed + +build: $(KERNEL_BZIMAGE) +qemu: $(KERNEL_BZIMAGE) + rm -f $(BUILD_PATH)/result + timeout --foreground 20m qemu-system-$(QEMU_ARCH) \ + -nodefaults \ + -nographic \ + -smp $(NR_CPUS) \ + $(QEMU_MACHINE) \ + -m $$(grep -q CONFIG_DEBUG_KMEMLEAK=y $(KERNEL_BUILD_PATH)/.config && echo 1G || echo 256M) \ + -serial stdio \ + -serial file:$(BUILD_PATH)/result \ + -no-reboot \ + -monitor none \ + -kernel $< + grep -Fq success $(BUILD_PATH)/result + +$(BUILD_PATH)/init-cpio-spec.txt: + mkdir -p $(BUILD_PATH) + echo "file /init $(BUILD_PATH)/init 755 0 0" > $@ + echo "file /init.sh $(PWD)/../netns.sh 755 0 0" >> $@ + echo "dir /dev 755 0 0" >> $@ + echo "nod /dev/console 644 0 0 c 5 1" >> $@ + echo "dir /bin 755 0 0" >> $@ + echo "file /bin/iperf3 $(IPERF_PATH)/src/iperf3 755 0 0" >> $@ + echo "file /bin/wg $(WIREGUARD_TOOLS_PATH)/src/tools/wg 755 0 0" >> $@ + echo "file /bin/bash $(BASH_PATH)/bash 755 0 0" >> $@ + echo "file /bin/ip $(IPROUTE2_PATH)/ip/ip 755 0 0" >> $@ + echo "file /bin/ss $(IPROUTE2_PATH)/misc/ss 755 0 0" >> $@ + echo "file /bin/ping $(IPUTILS_PATH)/ping 755 0 0" >> $@ + echo "file /bin/ncat $(NMAP_PATH)/ncat/ncat 755 0 0" >> $@ + echo "file /bin/xtables-multi $(IPTABLES_PATH)/iptables/xtables-multi 755 0 0" >> $@ + echo "slink /bin/iptables xtables-multi 777 0 0" >> $@ + echo "slink /bin/ping6 ping 777 0 0" >> $@ + echo "dir /lib 755 0 0" >> $@ + echo "file /lib/libc.so $(MUSL_PATH)/lib/libc.so 755 0 0" >> $@ + echo "slink /lib/ld-linux.so.1 libc.so 777 0 0" >> $@ + +$(KERNEL_BUILD_PATH)/.config: kernel.config arch/$(ARCH).config + mkdir -p $(KERNEL_BUILD_PATH) + cp kernel.config $(KERNEL_BUILD_PATH)/minimal.config + printf 'CONFIG_NR_CPUS=$(NR_CPUS)\nCONFIG_INITRAMFS_SOURCE="$(BUILD_PATH)/init-cpio-spec.txt"\n' >> $(KERNEL_BUILD_PATH)/minimal.config + cat arch/$(ARCH).config >> $(KERNEL_BUILD_PATH)/minimal.config + $(MAKE) -C $(KERNEL_PATH) O=$(KERNEL_BUILD_PATH) ARCH=$(KERNEL_ARCH) allnoconfig + cd $(KERNEL_BUILD_PATH) && ARCH=$(KERNEL_ARCH) $(KERNEL_PATH)/scripts/kconfig/merge_config.sh -n $(KERNEL_BUILD_PATH)/.config $(KERNEL_BUILD_PATH)/minimal.config + $(if $(findstring yes,$(DEBUG_KERNEL)),cp debug.config $(KERNEL_BUILD_PATH) && cd $(KERNEL_BUILD_PATH) && ARCH=$(KERNEL_ARCH) $(KERNEL_PATH)/scripts/kconfig/merge_config.sh -n $(KERNEL_BUILD_PATH)/.config debug.config,) + +$(KERNEL_BZIMAGE): $(KERNEL_BUILD_PATH)/.config $(BUILD_PATH)/init-cpio-spec.txt $(MUSL_PATH)/lib/libc.so $(IPERF_PATH)/src/iperf3 $(IPUTILS_PATH)/ping $(BASH_PATH)/bash $(IPROUTE2_PATH)/misc/ss $(IPROUTE2_PATH)/ip/ip $(IPTABLES_PATH)/iptables/xtables-multi $(NMAP_PATH)/ncat/ncat $(WIREGUARD_TOOLS_PATH)/src/tools/wg $(BUILD_PATH)/init ../netns.sh $(WIREGUARD_SOURCES) + $(MAKE) -C $(KERNEL_PATH) O=$(KERNEL_BUILD_PATH) ARCH=$(KERNEL_ARCH) CROSS_COMPILE=$(CROSS_COMPILE) CC="$(NOPIE_GCC)" + +$(BUILD_PATH)/include/linux/.installed: | $(KERNEL_BUILD_PATH)/.config + $(MAKE) -C $(KERNEL_PATH) O=$(KERNEL_BUILD_PATH) INSTALL_HDR_PATH=$(BUILD_PATH) ARCH=$(KERNEL_ARCH) CROSS_COMPILE=$(CROSS_COMPILE) headers_install + touch $@ + +$(MUSL_PATH)/lib/libc.so: $(MUSL_TAR) + mkdir -p $(BUILD_PATH) + flock -s $<.lock tar -C $(BUILD_PATH) -xf $< + cd $(MUSL_PATH) && CC=$(REAL_CC) ./configure --prefix=/ --disable-static --build=$(CBUILD) + $(MAKE) -C $(MUSL_PATH) + $(STRIP) -s $@ + +$(BUILD_PATH)/include/.installed: $(MUSL_PATH)/lib/libc.so + $(MAKE) -C $(MUSL_PATH) DESTDIR=$(BUILD_PATH) install-headers + touch $@ + +$(MUSL_CC): $(MUSL_PATH)/lib/libc.so + sh $(MUSL_PATH)/tools/musl-gcc.specs.sh $(BUILD_PATH)/include $(MUSL_PATH)/lib /lib/ld-linux.so.1 > $(BUILD_PATH)/musl-gcc.specs + printf '#!/bin/sh\nexec "$(REAL_CC)" --specs="$(BUILD_PATH)/musl-gcc.specs" -fno-stack-protector -no-pie "$$@"\n' > $(BUILD_PATH)/musl-gcc + chmod +x $(BUILD_PATH)/musl-gcc + +$(IPERF_PATH)/.installed: $(IPERF_TAR) + mkdir -p $(BUILD_PATH) + flock -s $<.lock tar -C $(BUILD_PATH) -xf $< + sed -i '1s/^/#include /' $(IPERF_PATH)/src/cjson.h $(IPERF_PATH)/src/timer.h + sed -i -r 's/-p?g//g' $(IPERF_PATH)/src/Makefile* + touch $@ + +$(IPERF_PATH)/src/iperf3: | $(IPERF_PATH)/.installed $(USERSPACE_DEPS) + cd $(IPERF_PATH) && CFLAGS="$(CFLAGS) -D_GNU_SOURCE" ./configure --prefix=/ $(CROSS_COMPILE_FLAG) --enable-static --disable-shared + $(MAKE) -C $(IPERF_PATH) + $(STRIP) -s $@ + +$(LIBMNL_PATH)/.installed: $(LIBMNL_TAR) + flock -s $<.lock tar -C $(BUILD_PATH) -xf $< + touch $@ + +$(LIBMNL_PATH)/src/.libs/libmnl.a: | $(LIBMNL_PATH)/.installed $(USERSPACE_DEPS) + cd $(LIBMNL_PATH) && ./configure --prefix=/ $(CROSS_COMPILE_FLAG) --enable-static --disable-shared + $(MAKE) -C $(LIBMNL_PATH) + sed -i 's:prefix=.*:prefix=$(LIBMNL_PATH):' $(LIBMNL_PATH)/libmnl.pc + +$(WIREGUARD_TOOLS_PATH)/.installed: $(WIREGUARD_TOOLS_TAR) + flock -s $<.lock tar -C $(BUILD_PATH) -xf $< + touch $@ + +$(WIREGUARD_TOOLS_PATH)/src/tools/wg: | $(WIREGUARD_TOOLS_PATH)/.installed $(LIBMNL_PATH)/src/.libs/libmnl.a $(USERSPACE_DEPS) + LDFLAGS="$(LDFLAGS) -L$(LIBMNL_PATH)/src/.libs" $(MAKE) -C $(WIREGUARD_TOOLS_PATH)/src/tools LIBMNL_CFLAGS="-I$(LIBMNL_PATH)/include" LIBMNL_LDLIBS="-lmnl" wg + $(STRIP) -s $@ + +$(BUILD_PATH)/init: init.c | $(USERSPACE_DEPS) + mkdir -p $(BUILD_PATH) + $(MUSL_CC) -o $@ $(CFLAGS) $(LDFLAGS) -std=gnu11 $< + $(STRIP) -s $@ + +$(IPUTILS_PATH)/.installed: $(IPUTILS_TAR) + mkdir -p $(BUILD_PATH) + flock -s $<.lock tar -C $(BUILD_PATH) -xf $< + touch $@ + +$(IPUTILS_PATH)/ping: | $(IPUTILS_PATH)/.installed $(USERSPACE_DEPS) + $(MAKE) -C $(IPUTILS_PATH) USE_CAP=no USE_IDN=no USE_NETTLE=no USE_CRYPTO=no ping + $(STRIP) -s $@ + +$(BASH_PATH)/.installed: $(BASH_TAR) + mkdir -p $(BUILD_PATH) + flock -s $<.lock tar -C $(BUILD_PATH) -xf $< + touch $@ + +$(BASH_PATH)/bash: | $(BASH_PATH)/.installed $(USERSPACE_DEPS) + cd $(BASH_PATH) && ./configure --prefix=/ $(CROSS_COMPILE_FLAG) --without-bash-malloc --disable-debugger --disable-help-builtin --disable-history --disable-multibyte --disable-progcomp --disable-readline --disable-mem-scramble + $(MAKE) -C $(BASH_PATH) + $(STRIP) -s $@ + +$(IPROUTE2_PATH)/.installed: $(IPROUTE2_TAR) + mkdir -p $(BUILD_PATH) + flock -s $<.lock tar -C $(BUILD_PATH) -xf $< + printf 'CC:=$(CC)\nPKG_CONFIG:=pkg-config\nTC_CONFIG_XT:=n\nTC_CONFIG_ATM:=n\nTC_CONFIG_IPSET:=n\nIP_CONFIG_SETNS:=y\nHAVE_ELF:=n\nHAVE_MNL:=y\nHAVE_BERKELEY_DB:=n\nHAVE_LATEX:=n\nHAVE_PDFLATEX:=n\nCFLAGS+=-DHAVE_SETNS -DHAVE_LIBMNL -I$(LIBMNL_PATH)/include\nLDLIBS+=-lmnl' > $(IPROUTE2_PATH)/config.mk + printf 'lib: snapshot\n\t$$(MAKE) -C lib\nip/ip: lib\n\t$$(MAKE) -C ip ip\nmisc/ss: lib\n\t$$(MAKE) -C misc ss\n' >> $(IPROUTE2_PATH)/Makefile + touch $@ + +$(IPROUTE2_PATH)/ip/ip: | $(IPROUTE2_PATH)/.installed $(LIBMNL_PATH)/src/.libs/libmnl.a $(USERSPACE_DEPS) + LDFLAGS="$(LDFLAGS) -L$(LIBMNL_PATH)/src/.libs" PKG_CONFIG_LIBDIR="$(LIBMNL_PATH)" $(MAKE) -C $(IPROUTE2_PATH) PREFIX=/ ip/ip + $(STRIP) -s $(IPROUTE2_PATH)/ip/ip + +$(IPROUTE2_PATH)/misc/ss: | $(IPROUTE2_PATH)/.installed $(LIBMNL_PATH)/src/.libs/libmnl.a $(USERSPACE_DEPS) + LDFLAGS="$(LDFLAGS) -L$(LIBMNL_PATH)/src/.libs" PKG_CONFIG_LIBDIR="$(LIBMNL_PATH)" $(MAKE) -C $(IPROUTE2_PATH) PREFIX=/ misc/ss + $(STRIP) -s $(IPROUTE2_PATH)/misc/ss + +$(IPTABLES_PATH)/.installed: $(IPTABLES_TAR) + mkdir -p $(BUILD_PATH) + flock -s $<.lock tar -C $(BUILD_PATH) -xf $< + sed -i -e "/nfnetlink=[01]/s:=[01]:=0:" -e "/nfconntrack=[01]/s:=[01]:=0:" $(IPTABLES_PATH)/configure + touch $@ + +$(IPTABLES_PATH)/iptables/xtables-multi: | $(IPTABLES_PATH)/.installed $(LIBMNL_PATH)/src/.libs/libmnl.a $(USERSPACE_DEPS) + cd $(IPTABLES_PATH) && PKG_CONFIG_LIBDIR="$(LIBMNL_PATH)" ./configure --prefix=/ $(CROSS_COMPILE_FLAG) --enable-static --disable-shared --disable-nftables --disable-bpf-compiler --disable-nfsynproxy --disable-libipq --with-kernel=$(BUILD_PATH)/include + $(MAKE) -C $(IPTABLES_PATH) + $(STRIP) -s $@ + +$(NMAP_PATH)/.installed: $(NMAP_TAR) + mkdir -p $(BUILD_PATH) + flock -s $<.lock tar -C $(BUILD_PATH) -xf $< + touch $@ + +$(NMAP_PATH)/ncat/ncat: | $(NMAP_PATH)/.installed $(USERSPACE_DEPS) + cd $(NMAP_PATH) && ./configure --prefix=/ $(CROSS_COMPILE_FLAG) --enable-static --disable-shared --without-ndiff --without-zenmap --without-nping --with-libpcap=included --with-libpcre=included --with-libdnet=included --without-liblua --with-liblinear=included --without-nmap-update --without-openssl --with-pcap=linux + $(MAKE) -C $(NMAP_PATH) build-ncat + $(STRIP) -s $@ + +clean: + rm -rf $(BUILD_PATH) + +distclean: clean + rm -rf $(DISTFILES_PATH) + +menuconfig: $(KERNEL_BUILD_PATH)/.config + $(MAKE) -C $(KERNEL_PATH) O=$(KERNEL_BUILD_PATH) ARCH=$(KERNEL_ARCH) CROSS_COMPILE=$(CROSS_COMPILE) CC="$(NOPIE_GCC)" menuconfig + +.PHONY: qemu build clean distclean menuconfig +.DELETE_ON_ERROR: diff --git a/tools/testing/selftests/wireguard/qemu/arch/aarch64.config b/tools/testing/selftests/wireguard/qemu/arch/aarch64.config new file mode 100644 index 0000000000000..3d063bb247bbe --- /dev/null +++ b/tools/testing/selftests/wireguard/qemu/arch/aarch64.config @@ -0,0 +1,5 @@ +CONFIG_SERIAL_AMBA_PL011=y +CONFIG_SERIAL_AMBA_PL011_CONSOLE=y +CONFIG_CMDLINE_BOOL=y +CONFIG_CMDLINE="console=ttyAMA0 wg.success=ttyAMA1" +CONFIG_FRAME_WARN=1280 diff --git a/tools/testing/selftests/wireguard/qemu/arch/aarch64_be.config b/tools/testing/selftests/wireguard/qemu/arch/aarch64_be.config new file mode 100644 index 0000000000000..dbdc7e406a7ba --- /dev/null +++ b/tools/testing/selftests/wireguard/qemu/arch/aarch64_be.config @@ -0,0 +1,6 @@ +CONFIG_CPU_BIG_ENDIAN=y +CONFIG_SERIAL_AMBA_PL011=y +CONFIG_SERIAL_AMBA_PL011_CONSOLE=y +CONFIG_CMDLINE_BOOL=y +CONFIG_CMDLINE="console=ttyAMA0 wg.success=ttyAMA1" +CONFIG_FRAME_WARN=1280 diff --git a/tools/testing/selftests/wireguard/qemu/arch/arm.config b/tools/testing/selftests/wireguard/qemu/arch/arm.config new file mode 100644 index 0000000000000..148f499054182 --- /dev/null +++ b/tools/testing/selftests/wireguard/qemu/arch/arm.config @@ -0,0 +1,9 @@ +CONFIG_MMU=y +CONFIG_ARCH_MULTI_V7=y +CONFIG_ARCH_VIRT=y +CONFIG_THUMB2_KERNEL=n +CONFIG_SERIAL_AMBA_PL011=y +CONFIG_SERIAL_AMBA_PL011_CONSOLE=y +CONFIG_CMDLINE_BOOL=y +CONFIG_CMDLINE="console=ttyAMA0 wg.success=ttyAMA1" +CONFIG_FRAME_WARN=1024 diff --git a/tools/testing/selftests/wireguard/qemu/arch/armeb.config b/tools/testing/selftests/wireguard/qemu/arch/armeb.config new file mode 100644 index 0000000000000..bd76b07d00a2c --- /dev/null +++ b/tools/testing/selftests/wireguard/qemu/arch/armeb.config @@ -0,0 +1,10 @@ +CONFIG_MMU=y +CONFIG_ARCH_MULTI_V7=y +CONFIG_ARCH_VIRT=y +CONFIG_THUMB2_KERNEL=n +CONFIG_SERIAL_AMBA_PL011=y +CONFIG_SERIAL_AMBA_PL011_CONSOLE=y +CONFIG_CMDLINE_BOOL=y +CONFIG_CMDLINE="console=ttyAMA0 wg.success=ttyAMA1" +CONFIG_CPU_BIG_ENDIAN=y +CONFIG_FRAME_WARN=1024 diff --git a/tools/testing/selftests/wireguard/qemu/arch/i686.config b/tools/testing/selftests/wireguard/qemu/arch/i686.config new file mode 100644 index 0000000000000..a85025d7206ee --- /dev/null +++ b/tools/testing/selftests/wireguard/qemu/arch/i686.config @@ -0,0 +1,5 @@ +CONFIG_SERIAL_8250=y +CONFIG_SERIAL_8250_CONSOLE=y +CONFIG_CMDLINE_BOOL=y +CONFIG_CMDLINE="console=ttyS0 wg.success=ttyS1" +CONFIG_FRAME_WARN=1024 diff --git a/tools/testing/selftests/wireguard/qemu/arch/m68k.config b/tools/testing/selftests/wireguard/qemu/arch/m68k.config new file mode 100644 index 0000000000000..5381ea10896c3 --- /dev/null +++ b/tools/testing/selftests/wireguard/qemu/arch/m68k.config @@ -0,0 +1,9 @@ +CONFIG_MMU=y +CONFIG_M68040=y +CONFIG_MAC=y +CONFIG_SERIAL_PMACZILOG=y +CONFIG_SERIAL_PMACZILOG_TTYS=y +CONFIG_SERIAL_PMACZILOG_CONSOLE=y +CONFIG_CMDLINE_BOOL=y +CONFIG_CMDLINE="console=ttyS0 wg.success=ttyS1" +CONFIG_FRAME_WARN=1024 diff --git a/tools/testing/selftests/wireguard/qemu/arch/mips.config b/tools/testing/selftests/wireguard/qemu/arch/mips.config new file mode 100644 index 0000000000000..df71d6b95546f --- /dev/null +++ b/tools/testing/selftests/wireguard/qemu/arch/mips.config @@ -0,0 +1,11 @@ +CONFIG_CPU_MIPS32_R2=y +CONFIG_MIPS_MALTA=y +CONFIG_MIPS_CPS=y +CONFIG_MIPS_FP_SUPPORT=y +CONFIG_POWER_RESET=y +CONFIG_POWER_RESET_SYSCON=y +CONFIG_SERIAL_8250=y +CONFIG_SERIAL_8250_CONSOLE=y +CONFIG_CMDLINE_BOOL=y +CONFIG_CMDLINE="console=ttyS0 wg.success=ttyS1" +CONFIG_FRAME_WARN=1024 diff --git a/tools/testing/selftests/wireguard/qemu/arch/mips64.config b/tools/testing/selftests/wireguard/qemu/arch/mips64.config new file mode 100644 index 0000000000000..90c783f725c4d --- /dev/null +++ b/tools/testing/selftests/wireguard/qemu/arch/mips64.config @@ -0,0 +1,14 @@ +CONFIG_64BIT=y +CONFIG_CPU_MIPS64_R2=y +CONFIG_MIPS32_N32=y +CONFIG_CPU_HAS_MSA=y +CONFIG_MIPS_MALTA=y +CONFIG_MIPS_CPS=y +CONFIG_MIPS_FP_SUPPORT=y +CONFIG_POWER_RESET=y +CONFIG_POWER_RESET_SYSCON=y +CONFIG_SERIAL_8250=y +CONFIG_SERIAL_8250_CONSOLE=y +CONFIG_CMDLINE_BOOL=y +CONFIG_CMDLINE="console=ttyS0 wg.success=ttyS1" +CONFIG_FRAME_WARN=1280 diff --git a/tools/testing/selftests/wireguard/qemu/arch/mips64el.config b/tools/testing/selftests/wireguard/qemu/arch/mips64el.config new file mode 100644 index 0000000000000..435b0b43e00cb --- /dev/null +++ b/tools/testing/selftests/wireguard/qemu/arch/mips64el.config @@ -0,0 +1,15 @@ +CONFIG_64BIT=y +CONFIG_CPU_MIPS64_R2=y +CONFIG_MIPS32_N32=y +CONFIG_CPU_HAS_MSA=y +CONFIG_MIPS_MALTA=y +CONFIG_CPU_LITTLE_ENDIAN=y +CONFIG_MIPS_CPS=y +CONFIG_MIPS_FP_SUPPORT=y +CONFIG_POWER_RESET=y +CONFIG_POWER_RESET_SYSCON=y +CONFIG_SERIAL_8250=y +CONFIG_SERIAL_8250_CONSOLE=y +CONFIG_CMDLINE_BOOL=y +CONFIG_CMDLINE="console=ttyS0 wg.success=ttyS1" +CONFIG_FRAME_WARN=1280 diff --git a/tools/testing/selftests/wireguard/qemu/arch/mipsel.config b/tools/testing/selftests/wireguard/qemu/arch/mipsel.config new file mode 100644 index 0000000000000..62bb50c4a85fc --- /dev/null +++ b/tools/testing/selftests/wireguard/qemu/arch/mipsel.config @@ -0,0 +1,12 @@ +CONFIG_CPU_MIPS32_R2=y +CONFIG_MIPS_MALTA=y +CONFIG_CPU_LITTLE_ENDIAN=y +CONFIG_MIPS_CPS=y +CONFIG_MIPS_FP_SUPPORT=y +CONFIG_POWER_RESET=y +CONFIG_POWER_RESET_SYSCON=y +CONFIG_SERIAL_8250=y +CONFIG_SERIAL_8250_CONSOLE=y +CONFIG_CMDLINE_BOOL=y +CONFIG_CMDLINE="console=ttyS0 wg.success=ttyS1" +CONFIG_FRAME_WARN=1024 diff --git a/tools/testing/selftests/wireguard/qemu/arch/powerpc.config b/tools/testing/selftests/wireguard/qemu/arch/powerpc.config new file mode 100644 index 0000000000000..57957093b71b8 --- /dev/null +++ b/tools/testing/selftests/wireguard/qemu/arch/powerpc.config @@ -0,0 +1,10 @@ +CONFIG_PPC_QEMU_E500=y +CONFIG_FSL_SOC_BOOKE=y +CONFIG_PPC_85xx=y +CONFIG_PHYS_64BIT=y +CONFIG_SERIAL_8250=y +CONFIG_SERIAL_8250_CONSOLE=y +CONFIG_MATH_EMULATION=y +CONFIG_CMDLINE_BOOL=y +CONFIG_CMDLINE="console=ttyS0 wg.success=ttyS1" +CONFIG_FRAME_WARN=1024 diff --git a/tools/testing/selftests/wireguard/qemu/arch/powerpc64le.config b/tools/testing/selftests/wireguard/qemu/arch/powerpc64le.config new file mode 100644 index 0000000000000..990c510a9cfa5 --- /dev/null +++ b/tools/testing/selftests/wireguard/qemu/arch/powerpc64le.config @@ -0,0 +1,12 @@ +CONFIG_PPC64=y +CONFIG_PPC_PSERIES=y +CONFIG_ALTIVEC=y +CONFIG_VSX=y +CONFIG_PPC_OF_BOOT_TRAMPOLINE=y +CONFIG_PPC_RADIX_MMU=y +CONFIG_HVC_CONSOLE=y +CONFIG_CPU_LITTLE_ENDIAN=y +CONFIG_CMDLINE_BOOL=y +CONFIG_CMDLINE="console=hvc0 wg.success=hvc1" +CONFIG_SECTION_MISMATCH_WARN_ONLY=y +CONFIG_FRAME_WARN=1280 diff --git a/tools/testing/selftests/wireguard/qemu/arch/x86_64.config b/tools/testing/selftests/wireguard/qemu/arch/x86_64.config new file mode 100644 index 0000000000000..00a1ef4869d58 --- /dev/null +++ b/tools/testing/selftests/wireguard/qemu/arch/x86_64.config @@ -0,0 +1,5 @@ +CONFIG_SERIAL_8250=y +CONFIG_SERIAL_8250_CONSOLE=y +CONFIG_CMDLINE_BOOL=y +CONFIG_CMDLINE="console=ttyS0 wg.success=ttyS1" +CONFIG_FRAME_WARN=1280 diff --git a/tools/testing/selftests/wireguard/qemu/debug.config b/tools/testing/selftests/wireguard/qemu/debug.config new file mode 100644 index 0000000000000..b9c72706fe4db --- /dev/null +++ b/tools/testing/selftests/wireguard/qemu/debug.config @@ -0,0 +1,67 @@ +CONFIG_LOCALVERSION="-debug" +CONFIG_ENABLE_WARN_DEPRECATED=y +CONFIG_ENABLE_MUST_CHECK=y +CONFIG_FRAME_POINTER=y +CONFIG_STACK_VALIDATION=y +CONFIG_DEBUG_KERNEL=y +CONFIG_DEBUG_INFO=y +CONFIG_DEBUG_INFO_DWARF4=y +CONFIG_PAGE_EXTENSION=y +CONFIG_PAGE_POISONING=y +CONFIG_DEBUG_OBJECTS=y +CONFIG_DEBUG_OBJECTS_FREE=y +CONFIG_DEBUG_OBJECTS_TIMERS=y +CONFIG_DEBUG_OBJECTS_WORK=y +CONFIG_DEBUG_OBJECTS_RCU_HEAD=y +CONFIG_DEBUG_OBJECTS_PERCPU_COUNTER=y +CONFIG_DEBUG_OBJECTS_ENABLE_DEFAULT=1 +CONFIG_SLUB_DEBUG_ON=y +CONFIG_DEBUG_VM=y +CONFIG_DEBUG_MEMORY_INIT=y +CONFIG_HAVE_DEBUG_STACKOVERFLOW=y +CONFIG_DEBUG_STACKOVERFLOW=y +CONFIG_HAVE_ARCH_KMEMCHECK=y +CONFIG_HAVE_ARCH_KASAN=y +CONFIG_KASAN=y +CONFIG_KASAN_INLINE=y +CONFIG_UBSAN=y +CONFIG_UBSAN_SANITIZE_ALL=y +CONFIG_UBSAN_NO_ALIGNMENT=y +CONFIG_UBSAN_NULL=y +CONFIG_DEBUG_KMEMLEAK=y +CONFIG_DEBUG_KMEMLEAK_EARLY_LOG_SIZE=8192 +CONFIG_DEBUG_STACK_USAGE=y +CONFIG_DEBUG_SHIRQ=y +CONFIG_WQ_WATCHDOG=y +CONFIG_SCHED_DEBUG=y +CONFIG_SCHED_INFO=y +CONFIG_SCHEDSTATS=y +CONFIG_SCHED_STACK_END_CHECK=y +CONFIG_DEBUG_TIMEKEEPING=y +CONFIG_TIMER_STATS=y +CONFIG_DEBUG_PREEMPT=y +CONFIG_DEBUG_RT_MUTEXES=y +CONFIG_DEBUG_SPINLOCK=y +CONFIG_DEBUG_MUTEXES=y +CONFIG_DEBUG_LOCK_ALLOC=y +CONFIG_PROVE_LOCKING=y +CONFIG_LOCKDEP=y +CONFIG_DEBUG_ATOMIC_SLEEP=y +CONFIG_TRACE_IRQFLAGS=y +CONFIG_DEBUG_BUGVERBOSE=y +CONFIG_DEBUG_LIST=y +CONFIG_DEBUG_PI_LIST=y +CONFIG_PROVE_RCU=y +CONFIG_SPARSE_RCU_POINTER=y +CONFIG_RCU_CPU_STALL_TIMEOUT=21 +CONFIG_RCU_TRACE=y +CONFIG_RCU_EQS_DEBUG=y +CONFIG_USER_STACKTRACE_SUPPORT=y +CONFIG_DEBUG_SG=y +CONFIG_DEBUG_NOTIFIERS=y +CONFIG_DOUBLEFAULT=y +CONFIG_X86_DEBUG_FPU=y +CONFIG_DEBUG_SECTION_MISMATCH=y +CONFIG_DEBUG_PAGEALLOC=y +CONFIG_DEBUG_PAGEALLOC_ENABLE_DEFAULT=y +CONFIG_DEBUG_WW_MUTEX_SLOWPATH=y diff --git a/tools/testing/selftests/wireguard/qemu/init.c b/tools/testing/selftests/wireguard/qemu/init.c new file mode 100644 index 0000000000000..51e5ddedee886 --- /dev/null +++ b/tools/testing/selftests/wireguard/qemu/init.c @@ -0,0 +1,284 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2015-2019 Jason A. Donenfeld . All Rights Reserved. + */ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +__attribute__((noreturn)) static void poweroff(void) +{ + fflush(stdout); + fflush(stderr); + reboot(RB_AUTOBOOT); + sleep(30); + fprintf(stderr, "\x1b[37m\x1b[41m\x1b[1mFailed to power off!!!\x1b[0m\n"); + exit(1); +} + +static void panic(const char *what) +{ + fprintf(stderr, "\n\n\x1b[37m\x1b[41m\x1b[1mSOMETHING WENT HORRIBLY WRONG\x1b[0m\n\n \x1b[31m\x1b[1m%s: %s\x1b[0m\n\n\x1b[37m\x1b[44m\x1b[1mPower off...\x1b[0m\n\n", what, strerror(errno)); + poweroff(); +} + +#define pretty_message(msg) puts("\x1b[32m\x1b[1m" msg "\x1b[0m") + +static void print_banner(void) +{ + struct utsname utsname; + int len; + + if (uname(&utsname) < 0) + panic("uname"); + + len = strlen(" WireGuard Test Suite on ") + strlen(utsname.sysname) + strlen(utsname.release) + strlen(utsname.machine); + printf("\x1b[45m\x1b[33m\x1b[1m%*.s\x1b[0m\n\x1b[45m\x1b[33m\x1b[1m WireGuard Test Suite on %s %s %s \x1b[0m\n\x1b[45m\x1b[33m\x1b[1m%*.s\x1b[0m\n\n", len, "", utsname.sysname, utsname.release, utsname.machine, len, ""); +} + +static void seed_rng(void) +{ + int fd; + struct { + int entropy_count; + int buffer_size; + unsigned char buffer[256]; + } entropy = { + .entropy_count = sizeof(entropy.buffer) * 8, + .buffer_size = sizeof(entropy.buffer), + .buffer = "Adding real entropy is not actually important for these tests. Don't try this at home, kids!" + }; + + if (mknod("/dev/urandom", S_IFCHR | 0644, makedev(1, 9))) + panic("mknod(/dev/urandom)"); + fd = open("/dev/urandom", O_WRONLY); + if (fd < 0) + panic("open(urandom)"); + for (int i = 0; i < 256; ++i) { + if (ioctl(fd, RNDADDENTROPY, &entropy) < 0) + panic("ioctl(urandom)"); + } + close(fd); +} + +static void mount_filesystems(void) +{ + pretty_message("[+] Mounting filesystems..."); + mkdir("/dev", 0755); + mkdir("/proc", 0755); + mkdir("/sys", 0755); + mkdir("/tmp", 0755); + mkdir("/run", 0755); + mkdir("/var", 0755); + if (mount("none", "/dev", "devtmpfs", 0, NULL)) + panic("devtmpfs mount"); + if (mount("none", "/proc", "proc", 0, NULL)) + panic("procfs mount"); + if (mount("none", "/sys", "sysfs", 0, NULL)) + panic("sysfs mount"); + if (mount("none", "/tmp", "tmpfs", 0, NULL)) + panic("tmpfs mount"); + if (mount("none", "/run", "tmpfs", 0, NULL)) + panic("tmpfs mount"); + if (mount("none", "/sys/kernel/debug", "debugfs", 0, NULL)) + ; /* Not a problem if it fails.*/ + if (symlink("/run", "/var/run")) + panic("run symlink"); + if (symlink("/proc/self/fd", "/dev/fd")) + panic("fd symlink"); +} + +static void enable_logging(void) +{ + int fd; + pretty_message("[+] Enabling logging..."); + fd = open("/proc/sys/kernel/printk", O_WRONLY); + if (fd >= 0) { + if (write(fd, "9\n", 2) != 2) + panic("write(printk)"); + close(fd); + } + fd = open("/proc/sys/debug/exception-trace", O_WRONLY); + if (fd >= 0) { + if (write(fd, "1\n", 2) != 2) + panic("write(exception-trace)"); + close(fd); + } + fd = open("/proc/sys/kernel/panic_on_warn", O_WRONLY); + if (fd >= 0) { + if (write(fd, "1\n", 2) != 2) + panic("write(panic_on_warn)"); + close(fd); + } +} + +static void kmod_selftests(void) +{ + FILE *file; + char line[2048], *start, *pass; + bool success = true; + pretty_message("[+] Module self-tests:"); + file = fopen("/proc/kmsg", "r"); + if (!file) + panic("fopen(kmsg)"); + if (fcntl(fileno(file), F_SETFL, O_NONBLOCK) < 0) + panic("fcntl(kmsg, nonblock)"); + while (fgets(line, sizeof(line), file)) { + start = strstr(line, "wireguard: "); + if (!start) + continue; + start += 11; + *strchrnul(start, '\n') = '\0'; + if (strstr(start, "www.wireguard.com")) + break; + pass = strstr(start, ": pass"); + if (!pass || pass[6] != '\0') { + success = false; + printf(" \x1b[31m* %s\x1b[0m\n", start); + } else + printf(" \x1b[32m* %s\x1b[0m\n", start); + } + fclose(file); + if (!success) { + puts("\x1b[31m\x1b[1m[-] Tests failed! \u2639\x1b[0m"); + poweroff(); + } +} + +static void launch_tests(void) +{ + char cmdline[4096], *success_dev; + int status, fd; + pid_t pid; + + pretty_message("[+] Launching tests..."); + pid = fork(); + if (pid == -1) + panic("fork"); + else if (pid == 0) { + execl("/init.sh", "init", NULL); + panic("exec"); + } + if (waitpid(pid, &status, 0) < 0) + panic("waitpid"); + if (WIFEXITED(status) && WEXITSTATUS(status) == 0) { + pretty_message("[+] Tests successful! :-)"); + fd = open("/proc/cmdline", O_RDONLY); + if (fd < 0) + panic("open(/proc/cmdline)"); + if (read(fd, cmdline, sizeof(cmdline) - 1) <= 0) + panic("read(/proc/cmdline)"); + cmdline[sizeof(cmdline) - 1] = '\0'; + for (success_dev = strtok(cmdline, " \n"); success_dev; success_dev = strtok(NULL, " \n")) { + if (strncmp(success_dev, "wg.success=", 11)) + continue; + memcpy(success_dev + 11 - 5, "/dev/", 5); + success_dev += 11 - 5; + break; + } + if (!success_dev || !strlen(success_dev)) + panic("Unable to find success device"); + + fd = open(success_dev, O_WRONLY); + if (fd < 0) + panic("open(success_dev)"); + if (write(fd, "success\n", 8) != 8) + panic("write(success_dev)"); + close(fd); + } else { + const char *why = "unknown cause"; + int what = -1; + + if (WIFEXITED(status)) { + why = "exit code"; + what = WEXITSTATUS(status); + } else if (WIFSIGNALED(status)) { + why = "signal"; + what = WTERMSIG(status); + } + printf("\x1b[31m\x1b[1m[-] Tests failed with %s %d! \u2639\x1b[0m\n", why, what); + } +} + +static void ensure_console(void) +{ + for (unsigned int i = 0; i < 1000; ++i) { + int fd = open("/dev/console", O_RDWR); + if (fd < 0) { + usleep(50000); + continue; + } + dup2(fd, 0); + dup2(fd, 1); + dup2(fd, 2); + close(fd); + if (write(1, "\0\0\0\0\n", 5) == 5) + return; + } + panic("Unable to open console device"); +} + +static void clear_leaks(void) +{ + int fd; + + fd = open("/sys/kernel/debug/kmemleak", O_WRONLY); + if (fd < 0) + return; + pretty_message("[+] Starting memory leak detection..."); + write(fd, "clear\n", 5); + close(fd); +} + +static void check_leaks(void) +{ + int fd; + + fd = open("/sys/kernel/debug/kmemleak", O_WRONLY); + if (fd < 0) + return; + pretty_message("[+] Scanning for memory leaks..."); + sleep(2); /* Wait for any grace periods. */ + write(fd, "scan\n", 5); + close(fd); + + fd = open("/sys/kernel/debug/kmemleak", O_RDONLY); + if (fd < 0) + return; + if (sendfile(1, fd, NULL, 0x7ffff000) > 0) + panic("Memory leaks encountered"); + close(fd); +} + +int main(int argc, char *argv[]) +{ + seed_rng(); + ensure_console(); + print_banner(); + mount_filesystems(); + kmod_selftests(); + enable_logging(); + clear_leaks(); + launch_tests(); + check_leaks(); + poweroff(); + return 1; +} diff --git a/tools/testing/selftests/wireguard/qemu/kernel.config b/tools/testing/selftests/wireguard/qemu/kernel.config new file mode 100644 index 0000000000000..9cca30206014d --- /dev/null +++ b/tools/testing/selftests/wireguard/qemu/kernel.config @@ -0,0 +1,86 @@ +CONFIG_LOCALVERSION="" +CONFIG_NET=y +CONFIG_NETDEVICES=y +CONFIG_NET_CORE=y +CONFIG_NET_IPIP=y +CONFIG_DUMMY=y +CONFIG_VETH=y +CONFIG_MULTIUSER=y +CONFIG_NAMESPACES=y +CONFIG_NET_NS=y +CONFIG_UNIX=y +CONFIG_INET=y +CONFIG_IPV6=y +CONFIG_NETFILTER=y +CONFIG_NETFILTER_ADVANCED=y +CONFIG_NF_CONNTRACK=y +CONFIG_NF_NAT=y +CONFIG_NETFILTER_XTABLES=y +CONFIG_NETFILTER_XT_NAT=y +CONFIG_NETFILTER_XT_MATCH_LENGTH=y +CONFIG_NF_CONNTRACK_IPV4=y +CONFIG_NF_NAT_IPV4=y +CONFIG_IP_NF_IPTABLES=y +CONFIG_IP_NF_FILTER=y +CONFIG_IP_NF_NAT=y +CONFIG_IP_ADVANCED_ROUTER=y +CONFIG_IP_MULTIPLE_TABLES=y +CONFIG_IPV6_MULTIPLE_TABLES=y +CONFIG_TTY=y +CONFIG_BINFMT_ELF=y +CONFIG_BINFMT_SCRIPT=y +CONFIG_VDSO=y +CONFIG_VIRTUALIZATION=y +CONFIG_HYPERVISOR_GUEST=y +CONFIG_PARAVIRT=y +CONFIG_KVM_GUEST=y +CONFIG_PARAVIRT_SPINLOCKS=y +CONFIG_PRINTK=y +CONFIG_KALLSYMS=y +CONFIG_BUG=y +CONFIG_CC_OPTIMIZE_FOR_PERFORMANCE=y +CONFIG_EMBEDDED=n +CONFIG_BASE_FULL=y +CONFIG_FUTEX=y +CONFIG_SHMEM=y +CONFIG_SLUB=y +CONFIG_SPARSEMEM_VMEMMAP=y +CONFIG_SMP=y +CONFIG_SCHED_SMT=y +CONFIG_SCHED_MC=y +CONFIG_NUMA=y +CONFIG_PREEMPT=y +CONFIG_NO_HZ=y +CONFIG_NO_HZ_IDLE=y +CONFIG_NO_HZ_FULL=n +CONFIG_HZ_PERIODIC=n +CONFIG_HIGH_RES_TIMERS=y +CONFIG_ARCH_RANDOM=y +CONFIG_FILE_LOCKING=y +CONFIG_POSIX_TIMERS=y +CONFIG_DEVTMPFS=y +CONFIG_PROC_FS=y +CONFIG_PROC_SYSCTL=y +CONFIG_SYSFS=y +CONFIG_TMPFS=y +CONFIG_CONSOLE_LOGLEVEL_DEFAULT=15 +CONFIG_PRINTK_TIME=y +CONFIG_BLK_DEV_INITRD=y +CONFIG_LEGACY_VSYSCALL_NONE=y +CONFIG_KERNEL_GZIP=y +CONFIG_PANIC_ON_OOPS=y +CONFIG_BUG_ON_DATA_CORRUPTION=y +CONFIG_LOCKUP_DETECTOR=y +CONFIG_SOFTLOCKUP_DETECTOR=y +CONFIG_HARDLOCKUP_DETECTOR=y +CONFIG_WQ_WATCHDOG=y +CONFIG_DETECT_HUNG_TASK=y +CONFIG_BOOTPARAM_HARDLOCKUP_PANIC=y +CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC=y +CONFIG_BOOTPARAM_HUNG_TASK_PANIC=y +CONFIG_PANIC_TIMEOUT=-1 +CONFIG_STACKTRACE=y +CONFIG_EARLY_PRINTK=y +CONFIG_GDB_SCRIPTS=y +CONFIG_WIREGUARD=y +CONFIG_WIREGUARD_DEBUG=y -- GitLab From d7c68a38bb4f9b7c1a2e4a772872c752ee5c44a6 Mon Sep 17 00:00:00 2001 From: "Jason A. Donenfeld" Date: Sun, 15 Dec 2019 22:08:01 +0100 Subject: [PATCH 0783/1437] wireguard: Kconfig: select parent dependency for crypto This fixes the crypto selection submenu depenencies. Otherwise, we'd wind up issuing warnings in which certain dependencies we also select couldn't be satisfied. This condition was triggered by the addition of the test suite autobuilder in the previous commit. Signed-off-by: Jason A. Donenfeld Signed-off-by: David S. Miller --- drivers/net/Kconfig | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index ffe8d4e2b2067..01e2657e4c268 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -85,6 +85,8 @@ config WIREGUARD select CRYPTO_POLY1305_X86_64 if X86 && 64BIT select CRYPTO_BLAKE2S_X86 if X86 && 64BIT select CRYPTO_CURVE25519_X86 if X86 && 64BIT + select ARM_CRYPTO if ARM + select ARM64_CRYPTO if ARM64 select CRYPTO_CHACHA20_NEON if (ARM || ARM64) && KERNEL_MODE_NEON select CRYPTO_POLY1305_NEON if ARM64 && KERNEL_MODE_NEON select CRYPTO_POLY1305_ARM if ARM -- GitLab From a2ec8b5706944d228181c8b91d815f41d6dd8e7b Mon Sep 17 00:00:00 2001 From: Josh Soref Date: Sun, 15 Dec 2019 22:08:02 +0100 Subject: [PATCH 0784/1437] wireguard: global: fix spelling mistakes in comments This fixes two spelling errors in source code comments. Signed-off-by: Josh Soref [Jason: rewrote commit message] Signed-off-by: Jason A. Donenfeld Signed-off-by: David S. Miller --- drivers/net/wireguard/receive.c | 2 +- include/uapi/linux/wireguard.h | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireguard/receive.c b/drivers/net/wireguard/receive.c index 7e675f5414910..9c6bab9c981f4 100644 --- a/drivers/net/wireguard/receive.c +++ b/drivers/net/wireguard/receive.c @@ -380,7 +380,7 @@ static void wg_packet_consume_data_done(struct wg_peer *peer, /* We've already verified the Poly1305 auth tag, which means this packet * was not modified in transit. We can therefore tell the networking * stack that all checksums of every layer of encapsulation have already - * been checked "by the hardware" and therefore is unneccessary to check + * been checked "by the hardware" and therefore is unnecessary to check * again in software. */ skb->ip_summed = CHECKSUM_UNNECESSARY; diff --git a/include/uapi/linux/wireguard.h b/include/uapi/linux/wireguard.h index dd8a47c4ad11f..ae88be14c9478 100644 --- a/include/uapi/linux/wireguard.h +++ b/include/uapi/linux/wireguard.h @@ -18,13 +18,13 @@ * one but not both of: * * WGDEVICE_A_IFINDEX: NLA_U32 - * WGDEVICE_A_IFNAME: NLA_NUL_STRING, maxlen IFNAMESIZ - 1 + * WGDEVICE_A_IFNAME: NLA_NUL_STRING, maxlen IFNAMSIZ - 1 * * The kernel will then return several messages (NLM_F_MULTI) containing the * following tree of nested items: * * WGDEVICE_A_IFINDEX: NLA_U32 - * WGDEVICE_A_IFNAME: NLA_NUL_STRING, maxlen IFNAMESIZ - 1 + * WGDEVICE_A_IFNAME: NLA_NUL_STRING, maxlen IFNAMSIZ - 1 * WGDEVICE_A_PRIVATE_KEY: NLA_EXACT_LEN, len WG_KEY_LEN * WGDEVICE_A_PUBLIC_KEY: NLA_EXACT_LEN, len WG_KEY_LEN * WGDEVICE_A_LISTEN_PORT: NLA_U16 @@ -77,7 +77,7 @@ * WGDEVICE_A_IFINDEX and WGDEVICE_A_IFNAME: * * WGDEVICE_A_IFINDEX: NLA_U32 - * WGDEVICE_A_IFNAME: NLA_NUL_STRING, maxlen IFNAMESIZ - 1 + * WGDEVICE_A_IFNAME: NLA_NUL_STRING, maxlen IFNAMSIZ - 1 * WGDEVICE_A_FLAGS: NLA_U32, 0 or WGDEVICE_F_REPLACE_PEERS if all current * peers should be removed prior to adding the list below. * WGDEVICE_A_PRIVATE_KEY: len WG_KEY_LEN, all zeros to remove @@ -121,7 +121,7 @@ * filling in information not contained in the prior. Note that if * WGDEVICE_F_REPLACE_PEERS is specified in the first message, it probably * should not be specified in fragments that come after, so that the list - * of peers is only cleared the first time but appened after. Likewise for + * of peers is only cleared the first time but appended after. Likewise for * peers, if WGPEER_F_REPLACE_ALLOWEDIPS is specified in the first message * of a peer, it likely should not be specified in subsequent fragments. * -- GitLab From 43967b6ff91e53bcce5ae08c16a0588a475b53a1 Mon Sep 17 00:00:00 2001 From: YueHaibing Date: Sun, 15 Dec 2019 22:08:03 +0100 Subject: [PATCH 0785/1437] wireguard: main: remove unused include Remove from the includes for main.c, which is unused. Signed-off-by: YueHaibing [Jason: reworded commit message] Signed-off-by: Jason A. Donenfeld Signed-off-by: David S. Miller --- drivers/net/wireguard/main.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/net/wireguard/main.c b/drivers/net/wireguard/main.c index 10c0a40f6a9e5..7a7d5f1a80fc7 100644 --- a/drivers/net/wireguard/main.c +++ b/drivers/net/wireguard/main.c @@ -12,7 +12,6 @@ #include -#include #include #include #include -- GitLab From d89ee7d5c73af15c1c6f12b016cdf469742b5726 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Sun, 15 Dec 2019 22:08:04 +0100 Subject: [PATCH 0786/1437] wireguard: allowedips: use kfree_rcu() instead of call_rcu() The callback function of call_rcu() just calls a kfree(), so we can use kfree_rcu() instead of call_rcu() + callback function. Signed-off-by: Wei Yongjun Signed-off-by: Jason A. Donenfeld Signed-off-by: David S. Miller --- drivers/net/wireguard/allowedips.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/drivers/net/wireguard/allowedips.c b/drivers/net/wireguard/allowedips.c index 72667d5399c34..121d9ea0f1358 100644 --- a/drivers/net/wireguard/allowedips.c +++ b/drivers/net/wireguard/allowedips.c @@ -31,11 +31,6 @@ static void copy_and_assign_cidr(struct allowedips_node *node, const u8 *src, #define CHOOSE_NODE(parent, key) \ parent->bit[(key[parent->bit_at_a] >> parent->bit_at_b) & 1] -static void node_free_rcu(struct rcu_head *rcu) -{ - kfree(container_of(rcu, struct allowedips_node, rcu)); -} - static void push_rcu(struct allowedips_node **stack, struct allowedips_node __rcu *p, unsigned int *len) { @@ -112,7 +107,7 @@ static void walk_remove_by_peer(struct allowedips_node __rcu **top, if (!node->bit[0] || !node->bit[1]) { rcu_assign_pointer(*nptr, DEREF( &node->bit[!REF(node->bit[0])])); - call_rcu(&node->rcu, node_free_rcu); + kfree_rcu(node, rcu); node = DEREF(nptr); } } -- GitLab From 95bed1a9fb2b7ee13b58d0d29710282a62082a89 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Mon, 16 Dec 2019 00:12:14 +0200 Subject: [PATCH 0787/1437] net: dsa: ocelot: add NET_VENDOR_MICROSEMI dependency Selecting MSCC_OCELOT_SWITCH is not possible when NET_VENDOR_MICROSEMI is disabled: WARNING: unmet direct dependencies detected for MSCC_OCELOT_SWITCH Depends on [n]: NETDEVICES [=y] && ETHERNET [=n] && NET_VENDOR_MICROSEMI [=n] && NET_SWITCHDEV [=y] && HAS_IOMEM [=y] Selected by [m]: - NET_DSA_MSCC_FELIX [=m] && NETDEVICES [=y] && HAVE_NET_DSA [=y] && NET_DSA [=y] && PCI [=y] Add a Kconfig dependency on NET_VENDOR_MICROSEMI, which also implies CONFIG_NETDEVICES. Depending on a vendor config violates menuconfig locality for the DSA driver, but is the smallest compromise since all other solutions are much more complicated (see [0]). https://www.spinics.net/lists/netdev/msg618808.html Fixes: 56051948773e ("net: dsa: ocelot: add driver for Felix switch family") Signed-off-by: Arnd Bergmann Signed-off-by: Mao Wenan Signed-off-by: Vladimir Oltean Signed-off-by: David S. Miller --- drivers/net/dsa/ocelot/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/dsa/ocelot/Kconfig b/drivers/net/dsa/ocelot/Kconfig index 0031ca8143468..6f9804093150f 100644 --- a/drivers/net/dsa/ocelot/Kconfig +++ b/drivers/net/dsa/ocelot/Kconfig @@ -2,6 +2,7 @@ config NET_DSA_MSCC_FELIX tristate "Ocelot / Felix Ethernet switch support" depends on NET_DSA && PCI + depends on NET_VENDOR_MICROSEMI select MSCC_OCELOT_SWITCH select NET_DSA_TAG_OCELOT help -- GitLab From 228b607d8ea1b7d4561945058d5692709099d432 Mon Sep 17 00:00:00 2001 From: Marcus Comstedt Date: Sun, 15 Dec 2019 10:49:00 +0100 Subject: [PATCH 0788/1437] KVM: PPC: Book3S HV: Fix regression on big endian hosts VCPU_CR is the offset of arch.regs.ccr in kvm_vcpu. arch/powerpc/include/asm/kvm_host.h defines arch.regs as a struct pt_regs, and arch/powerpc/include/asm/ptrace.h defines the ccr field of pt_regs as "unsigned long ccr". Since unsigned long is 64 bits, a 64-bit load needs to be used to load it, unless an endianness specific correction offset is added to access the desired subpart. In this case there is no reason to _not_ use a 64 bit load though. Fixes: 6c85b7bc637b ("powerpc/kvm: Use UV_RETURN ucall to return to ultravisor") Cc: stable@vger.kernel.org # v5.4+ Signed-off-by: Marcus Comstedt Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20191215094900.46740-1-marcus@mc.pp.se --- arch/powerpc/kvm/book3s_hv_rmhandlers.S | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/powerpc/kvm/book3s_hv_rmhandlers.S b/arch/powerpc/kvm/book3s_hv_rmhandlers.S index 0496e66aaa565..c6fbbd29bd871 100644 --- a/arch/powerpc/kvm/book3s_hv_rmhandlers.S +++ b/arch/powerpc/kvm/book3s_hv_rmhandlers.S @@ -1117,7 +1117,7 @@ END_FTR_SECTION_IFSET(CPU_FTR_ARCH_300) ld r7, VCPU_GPR(R7)(r4) bne ret_to_ultra - lwz r0, VCPU_CR(r4) + ld r0, VCPU_CR(r4) mtcr r0 ld r0, VCPU_GPR(R0)(r4) @@ -1137,7 +1137,7 @@ END_FTR_SECTION_IFSET(CPU_FTR_ARCH_300) * R3 = UV_RETURN */ ret_to_ultra: - lwz r0, VCPU_CR(r4) + ld r0, VCPU_CR(r4) mtcr r0 ld r0, VCPU_GPR(R3)(r4) -- GitLab From ab42b48f32d4c766420c3499ee9c0289b7028182 Mon Sep 17 00:00:00 2001 From: Ian Abbott Date: Mon, 16 Dec 2019 11:08:23 +0000 Subject: [PATCH 0789/1437] staging: comedi: gsc_hpdi: check dma_alloc_coherent() return value The "auto-attach" handler function `gsc_hpdi_auto_attach()` calls `dma_alloc_coherent()` in a loop to allocate some DMA data buffers, and also calls it to allocate a buffer for a DMA descriptor chain. However, it does not check the return value of any of these calls. Change `gsc_hpdi_auto_attach()` to return `-ENOMEM` if any of these `dma_alloc_coherent()` calls fail. This will result in the comedi core calling the "detach" handler `gsc_hpdi_detach()` as part of the clean-up, which will call `gsc_hpdi_free_dma()` to free any allocated DMA coherent memory buffers. Cc: #4.6+ Signed-off-by: Ian Abbott Link: https://lore.kernel.org/r/20191216110823.216237-1-abbotti@mev.co.uk Signed-off-by: Greg Kroah-Hartman --- drivers/staging/comedi/drivers/gsc_hpdi.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/staging/comedi/drivers/gsc_hpdi.c b/drivers/staging/comedi/drivers/gsc_hpdi.c index 4bdf44d828799..dc62db1ee1dde 100644 --- a/drivers/staging/comedi/drivers/gsc_hpdi.c +++ b/drivers/staging/comedi/drivers/gsc_hpdi.c @@ -623,6 +623,11 @@ static int gsc_hpdi_auto_attach(struct comedi_device *dev, dma_alloc_coherent(&pcidev->dev, DMA_BUFFER_SIZE, &devpriv->dio_buffer_phys_addr[i], GFP_KERNEL); + if (!devpriv->dio_buffer[i]) { + dev_warn(dev->class_dev, + "failed to allocate DMA buffer\n"); + return -ENOMEM; + } } /* allocate dma descriptors */ devpriv->dma_desc = dma_alloc_coherent(&pcidev->dev, @@ -630,6 +635,11 @@ static int gsc_hpdi_auto_attach(struct comedi_device *dev, NUM_DMA_DESCRIPTORS, &devpriv->dma_desc_phys_addr, GFP_KERNEL); + if (!devpriv->dma_desc) { + dev_warn(dev->class_dev, + "failed to allocate DMA descriptors\n"); + return -ENOMEM; + } if (devpriv->dma_desc_phys_addr & 0xf) { dev_warn(dev->class_dev, " dma descriptors not quad-word aligned (bug)\n"); -- GitLab From d3aa8de6b5d0853c43c616586b4e232aa1fa7de9 Mon Sep 17 00:00:00 2001 From: Brendan Higgins Date: Wed, 11 Dec 2019 11:27:41 -0800 Subject: [PATCH 0790/1437] staging: axis-fifo: add unspecified HAS_IOMEM dependency Currently CONFIG_XIL_AXIS_FIFO=y implicitly depends on CONFIG_HAS_IOMEM=y; consequently, on architectures without IOMEM we get the following build error: ld: drivers/staging/axis-fifo/axis-fifo.o: in function `axis_fifo_probe': drivers/staging/axis-fifo/axis-fifo.c:809: undefined reference to `devm_ioremap_resource' Fix the build error by adding the unspecified dependency. Reported-by: Brendan Higgins Signed-off-by: Brendan Higgins Link: https://lore.kernel.org/r/20191211192742.95699-7-brendanhiggins@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/axis-fifo/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/axis-fifo/Kconfig b/drivers/staging/axis-fifo/Kconfig index 3fffe4d6f327f..f180a8e9f58af 100644 --- a/drivers/staging/axis-fifo/Kconfig +++ b/drivers/staging/axis-fifo/Kconfig @@ -4,7 +4,7 @@ # config XIL_AXIS_FIFO tristate "Xilinx AXI-Stream FIFO IP core driver" - depends on OF + depends on OF && HAS_IOMEM help This adds support for the Xilinx AXI-Stream FIFO IP core driver. The AXI Streaming FIFO allows memory mapped access to a AXI Streaming -- GitLab From 246ff09f89e54fdf740a8d496176c86743db3ec7 Mon Sep 17 00:00:00 2001 From: Konstantin Khlebnikov Date: Thu, 31 Oct 2019 16:04:48 +0300 Subject: [PATCH 0791/1437] x86/MCE/AMD: Do not use rdmsr_safe_on_cpu() in smca_configure() ... because interrupts are disabled that early and sending IPIs can deadlock: BUG: sleeping function called from invalid context at kernel/sched/completion.c:99 in_atomic(): 1, irqs_disabled(): 1, non_block: 0, pid: 0, name: swapper/1 no locks held by swapper/1/0. irq event stamp: 0 hardirqs last enabled at (0): [<0000000000000000>] 0x0 hardirqs last disabled at (0): [] copy_process+0x8b9/0x1ca0 softirqs last enabled at (0): [] copy_process+0x8b9/0x1ca0 softirqs last disabled at (0): [<0000000000000000>] 0x0 Preemption disabled at: [] start_secondary+0x3b/0x190 CPU: 1 PID: 0 Comm: swapper/1 Not tainted 5.5.0-rc2+ #1 Hardware name: GIGABYTE MZ01-CE1-00/MZ01-CE1-00, BIOS F02 08/29/2018 Call Trace: dump_stack ___might_sleep.cold.92 wait_for_completion ? generic_exec_single rdmsr_safe_on_cpu ? wrmsr_on_cpus mce_amd_feature_init mcheck_cpu_init identify_cpu identify_secondary_cpu smp_store_cpu_info start_secondary secondary_startup_64 The function smca_configure() is called only on the current CPU anyway, therefore replace rdmsr_safe_on_cpu() with atomic rdmsr_safe() and avoid the IPI. [ bp: Update commit message. ] Signed-off-by: Konstantin Khlebnikov Signed-off-by: Borislav Petkov Reviewed-by: Yazen Ghannam Cc: "H. Peter Anvin" Cc: Ingo Molnar Cc: linux-edac Cc: Cc: Thomas Gleixner Cc: Tony Luck Cc: x86-ml Link: https://lkml.kernel.org/r/157252708836.3876.4604398213417262402.stgit@buzz --- arch/x86/kernel/cpu/mce/amd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/x86/kernel/cpu/mce/amd.c b/arch/x86/kernel/cpu/mce/amd.c index 5167bd2bb6b14..e41e3b40aa486 100644 --- a/arch/x86/kernel/cpu/mce/amd.c +++ b/arch/x86/kernel/cpu/mce/amd.c @@ -269,7 +269,7 @@ static void smca_configure(unsigned int bank, unsigned int cpu) if (smca_banks[bank].hwid) return; - if (rdmsr_safe_on_cpu(cpu, MSR_AMD64_SMCA_MCx_IPID(bank), &low, &high)) { + if (rdmsr_safe(MSR_AMD64_SMCA_MCx_IPID(bank), &low, &high)) { pr_warn("Failed to read MCA_IPID for bank %d\n", bank); return; } -- GitLab From 966af20929ac24360ba3fac5533eb2ab003747da Mon Sep 17 00:00:00 2001 From: Yazen Ghannam Date: Thu, 21 Nov 2019 08:15:08 -0600 Subject: [PATCH 0792/1437] x86/MCE/AMD: Allow Reserved types to be overwritten in smca_banks[] Each logical CPU in Scalable MCA systems controls a unique set of MCA banks in the system. These banks are not shared between CPUs. The bank types and ordering will be the same across CPUs on currently available systems. However, some CPUs may see a bank as Reserved/Read-as-Zero (RAZ) while other CPUs do not. In this case, the bank seen as Reserved on one CPU is assumed to be the same type as the bank seen as a known type on another CPU. In general, this occurs when the hardware represented by the MCA bank is disabled, e.g. disabled memory controllers on certain models, etc. The MCA bank is disabled in the hardware, so there is no possibility of getting an MCA/MCE from it even if it is assumed to have a known type. For example: Full system: Bank | Type seen on CPU0 | Type seen on CPU1 ------------------------------------------------ 0 | LS | LS 1 | UMC | UMC 2 | CS | CS System with hardware disabled: Bank | Type seen on CPU0 | Type seen on CPU1 ------------------------------------------------ 0 | LS | LS 1 | UMC | RAZ 2 | CS | CS For this reason, there is a single, global struct smca_banks[] that is initialized at boot time. This array is initialized on each CPU as it comes online. However, the array will not be updated if an entry already exists. This works as expected when the first CPU (usually CPU0) has all possible MCA banks enabled. But if the first CPU has a subset, then it will save a "Reserved" type in smca_banks[]. Successive CPUs will then not be able to update smca_banks[] even if they encounter a known bank type. This may result in unexpected behavior. Depending on the system configuration, a user may observe issues enumerating the MCA thresholding sysfs interface. The issues may be as trivial as sysfs entries not being available, or as severe as system hangs. For example: Bank | Type seen on CPU0 | Type seen on CPU1 ------------------------------------------------ 0 | LS | LS 1 | RAZ | UMC 2 | CS | CS Extend the smca_banks[] entry check to return if the entry is a non-reserved type. Otherwise, continue so that CPUs that encounter a known bank type can update smca_banks[]. Fixes: 68627a697c19 ("x86/mce/AMD, EDAC/mce_amd: Enumerate Reserved SMCA bank type") Signed-off-by: Yazen Ghannam Signed-off-by: Borislav Petkov Cc: "H. Peter Anvin" Cc: Ingo Molnar Cc: linux-edac Cc: Cc: Thomas Gleixner Cc: Tony Luck Cc: x86-ml Link: https://lkml.kernel.org/r/20191121141508.141273-1-Yazen.Ghannam@amd.com --- arch/x86/kernel/cpu/mce/amd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/x86/kernel/cpu/mce/amd.c b/arch/x86/kernel/cpu/mce/amd.c index e41e3b40aa486..d6cf5c18a7e04 100644 --- a/arch/x86/kernel/cpu/mce/amd.c +++ b/arch/x86/kernel/cpu/mce/amd.c @@ -266,7 +266,7 @@ static void smca_configure(unsigned int bank, unsigned int cpu) smca_set_misc_banks_map(bank, cpu); /* Return early if this bank was already initialized. */ - if (smca_banks[bank].hwid) + if (smca_banks[bank].hwid && smca_banks[bank].hwid->hwid_mcatype != 0) return; if (rdmsr_safe(MSR_AMD64_SMCA_MCx_IPID(bank), &low, &high)) { -- GitLab From a3a57ddad061acc90bef39635caf2b2330ce8f21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20H=2E=20Sch=C3=B6nherr?= Date: Tue, 10 Dec 2019 01:07:30 +0100 Subject: [PATCH 0793/1437] x86/mce: Fix possibly incorrect severity calculation on AMD MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The function mce_severity_amd_smca() requires m->bank to be initialized for correct operation. Fix the one case, where mce_severity() is called without doing so. Fixes: 6bda529ec42e ("x86/mce: Grade uncorrected errors for SMCA-enabled systems") Fixes: d28af26faa0b ("x86/MCE: Initialize mce.bank in the case of a fatal error in mce_no_way_out()") Signed-off-by: Jan H. Schönherr Signed-off-by: Borislav Petkov Reviewed-by: Tony Luck Cc: "H. Peter Anvin" Cc: Ingo Molnar Cc: linux-edac Cc: Cc: Thomas Gleixner Cc: x86-ml Cc: Yazen Ghannam Link: https://lkml.kernel.org/r/20191210000733.17979-4-jschoenh@amazon.de --- arch/x86/kernel/cpu/mce/core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/x86/kernel/cpu/mce/core.c b/arch/x86/kernel/cpu/mce/core.c index 5f42f25bac8f8..2e2a421c8528b 100644 --- a/arch/x86/kernel/cpu/mce/core.c +++ b/arch/x86/kernel/cpu/mce/core.c @@ -819,8 +819,8 @@ static int mce_no_way_out(struct mce *m, char **msg, unsigned long *validp, if (quirk_no_way_out) quirk_no_way_out(i, m, regs); + m->bank = i; if (mce_severity(m, mca_cfg.tolerant, &tmp, true) >= MCE_PANIC_SEVERITY) { - m->bank = i; mce_read_aux(m, i); *msg = tmp; return 1; -- GitLab From 45477b3fe3d10376b649b1b85fce72b2f9f1da84 Mon Sep 17 00:00:00 2001 From: James Bottomley Date: Thu, 12 Dec 2019 12:58:35 -0500 Subject: [PATCH 0794/1437] security: keys: trusted: fix lost handle flush The original code, before it was moved into security/keys/trusted-keys had a flush after the blob unseal. Without that flush, the volatile handles increase in the TPM until it becomes unusable and the system either has to be rebooted or the TPM volatile area manually flushed. Fix by adding back the lost flush, which we now have to export because of the relocation of the trusted key code may cause the consumer to be modular. Signed-off-by: James Bottomley Fixes: 2e19e10131a0 ("KEYS: trusted: Move TPM2 trusted keys code") Reviewed-by: Jerry Snitselaar Reviewed-by: Jarkko Sakkinen Signed-off-by: Jarkko Sakkinen --- drivers/char/tpm/tpm.h | 1 - drivers/char/tpm/tpm2-cmd.c | 1 + include/linux/tpm.h | 1 + security/keys/trusted-keys/trusted_tpm2.c | 1 + 4 files changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h index b9e1547be6b51..5620747da0cfd 100644 --- a/drivers/char/tpm/tpm.h +++ b/drivers/char/tpm/tpm.h @@ -218,7 +218,6 @@ int tpm2_pcr_read(struct tpm_chip *chip, u32 pcr_idx, int tpm2_pcr_extend(struct tpm_chip *chip, u32 pcr_idx, struct tpm_digest *digests); int tpm2_get_random(struct tpm_chip *chip, u8 *dest, size_t max); -void tpm2_flush_context(struct tpm_chip *chip, u32 handle); ssize_t tpm2_get_tpm_pt(struct tpm_chip *chip, u32 property_id, u32 *value, const char *desc); diff --git a/drivers/char/tpm/tpm2-cmd.c b/drivers/char/tpm/tpm2-cmd.c index fdb457704aa79..13696deceae8e 100644 --- a/drivers/char/tpm/tpm2-cmd.c +++ b/drivers/char/tpm/tpm2-cmd.c @@ -362,6 +362,7 @@ void tpm2_flush_context(struct tpm_chip *chip, u32 handle) tpm_transmit_cmd(chip, &buf, 0, "flushing context"); tpm_buf_destroy(&buf); } +EXPORT_SYMBOL_GPL(tpm2_flush_context); struct tpm2_get_cap_out { u8 more_data; diff --git a/include/linux/tpm.h b/include/linux/tpm.h index 0d6e949ba315d..03e9b184411be 100644 --- a/include/linux/tpm.h +++ b/include/linux/tpm.h @@ -403,6 +403,7 @@ extern int tpm_pcr_extend(struct tpm_chip *chip, u32 pcr_idx, extern int tpm_send(struct tpm_chip *chip, void *cmd, size_t buflen); extern int tpm_get_random(struct tpm_chip *chip, u8 *data, size_t max); extern struct tpm_chip *tpm_default_chip(void); +void tpm2_flush_context(struct tpm_chip *chip, u32 handle); #else static inline int tpm_is_tpm2(struct tpm_chip *chip) { diff --git a/security/keys/trusted-keys/trusted_tpm2.c b/security/keys/trusted-keys/trusted_tpm2.c index a9810ac2776f6..08ec7f48f01d0 100644 --- a/security/keys/trusted-keys/trusted_tpm2.c +++ b/security/keys/trusted-keys/trusted_tpm2.c @@ -309,6 +309,7 @@ int tpm2_unseal_trusted(struct tpm_chip *chip, return rc; rc = tpm2_unseal_cmd(chip, payload, options, blob_handle); + tpm2_flush_context(chip, blob_handle); return rc; } -- GitLab From 472d26df5e8075eda677b6be730e0fbf434ff2a8 Mon Sep 17 00:00:00 2001 From: Xiaotao Yin Date: Tue, 10 Dec 2019 12:27:04 +0800 Subject: [PATCH 0795/1437] iommu/iova: Init the struct iova to fix the possible memleak During ethernet(Marvell octeontx2) set ring buffer test: ethtool -G eth1 rx tx following kmemleak will happen sometimes: unreferenced object 0xffff000b85421340 (size 64): comm "ethtool", pid 867, jiffies 4295323539 (age 550.500s) hex dump (first 64 bytes): 80 13 42 85 0b 00 ff ff ff ff ff ff ff ff ff ff ..B............. 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ ff ff ff ff ff ff ff ff 00 00 00 00 00 00 00 00 ................ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ backtrace: [<000000001b204ddf>] kmem_cache_alloc+0x1b0/0x350 [<00000000d9ef2e50>] alloc_iova+0x3c/0x168 [<00000000ea30f99d>] alloc_iova_fast+0x7c/0x2d8 [<00000000b8bb2f1f>] iommu_dma_alloc_iova.isra.0+0x12c/0x138 [<000000002f1a43b5>] __iommu_dma_map+0x8c/0xf8 [<00000000ecde7899>] iommu_dma_map_page+0x98/0xf8 [<0000000082004e59>] otx2_alloc_rbuf+0xf4/0x158 [<000000002b107f6b>] otx2_rq_aura_pool_init+0x110/0x270 [<00000000c3d563c7>] otx2_open+0x15c/0x734 [<00000000a2f5f3a8>] otx2_dev_open+0x3c/0x68 [<00000000456a98b5>] otx2_set_ringparam+0x1ac/0x1d4 [<00000000f2fbb819>] dev_ethtool+0xb84/0x2028 [<0000000069b67c5a>] dev_ioctl+0x248/0x3a0 [<00000000af38663a>] sock_ioctl+0x280/0x638 [<000000002582384c>] do_vfs_ioctl+0x8b0/0xa80 [<000000004e1a2c02>] ksys_ioctl+0x84/0xb8 The reason: When alloc_iova_mem() without initial with Zero, sometimes fpn_lo will equal to IOVA_ANCHOR by chance, so when return with -ENOMEM(iova32_full) from __alloc_and_insert_iova_range(), the new_iova will not be freed in free_iova_mem(). Fixes: bb68b2fbfbd6 ("iommu/iova: Add rbtree anchor node") Signed-off-by: Xiaotao Yin Reviewed-by: Robin Murphy Signed-off-by: Joerg Roedel --- drivers/iommu/iova.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iommu/iova.c b/drivers/iommu/iova.c index 41c605b0058f9..c7a914b9bbbc4 100644 --- a/drivers/iommu/iova.c +++ b/drivers/iommu/iova.c @@ -233,7 +233,7 @@ static DEFINE_MUTEX(iova_cache_mutex); struct iova *alloc_iova_mem(void) { - return kmem_cache_alloc(iova_cache, GFP_ATOMIC); + return kmem_cache_zalloc(iova_cache, GFP_ATOMIC); } EXPORT_SYMBOL(alloc_iova_mem); -- GitLab From bd036d2fdd374fa252abfc221e1a1280eee42f89 Mon Sep 17 00:00:00 2001 From: Robin Murphy Date: Wed, 11 Dec 2019 18:33:26 +0000 Subject: [PATCH 0796/1437] iommu/dma: Rationalise types for DMA masks Since iommu_dma_alloc_iova() combines incoming masks with the u64 bus limit, it makes more sense to pass them around in their native u64 rather than converting to dma_addr_t early. Do that, and resolve the remaining type discrepancy against the domain geometry with a cheeky cast to keep things simple. Signed-off-by: Robin Murphy Reviewed-by: Christoph Hellwig Tested-by: Nathan Chancellor # build Reviewed-by: Nicolas Saenz Julienne Signed-off-by: Joerg Roedel --- drivers/iommu/dma-iommu.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c index 0cc702a70a962..6e573d1cb8bf0 100644 --- a/drivers/iommu/dma-iommu.c +++ b/drivers/iommu/dma-iommu.c @@ -399,7 +399,7 @@ static int dma_info_to_prot(enum dma_data_direction dir, bool coherent, } static dma_addr_t iommu_dma_alloc_iova(struct iommu_domain *domain, - size_t size, dma_addr_t dma_limit, struct device *dev) + size_t size, u64 dma_limit, struct device *dev) { struct iommu_dma_cookie *cookie = domain->iova_cookie; struct iova_domain *iovad = &cookie->iovad; @@ -424,7 +424,7 @@ static dma_addr_t iommu_dma_alloc_iova(struct iommu_domain *domain, dma_limit = min_not_zero(dma_limit, dev->bus_dma_limit); if (domain->geometry.force_aperture) - dma_limit = min(dma_limit, domain->geometry.aperture_end); + dma_limit = min(dma_limit, (u64)domain->geometry.aperture_end); /* Try to get PCI devices a SAC address */ if (dma_limit > DMA_BIT_MASK(32) && dev_is_pci(dev)) @@ -477,7 +477,7 @@ static void __iommu_dma_unmap(struct device *dev, dma_addr_t dma_addr, } static dma_addr_t __iommu_dma_map(struct device *dev, phys_addr_t phys, - size_t size, int prot, dma_addr_t dma_mask) + size_t size, int prot, u64 dma_mask) { struct iommu_domain *domain = iommu_get_dma_domain(dev); struct iommu_dma_cookie *cookie = domain->iova_cookie; -- GitLab From d23d12484307b40eea549b8a858f5fffad913897 Mon Sep 17 00:00:00 2001 From: Tadeusz Struk Date: Thu, 12 Dec 2019 09:48:47 -0800 Subject: [PATCH 0797/1437] tpm: fix invalid locking in NONBLOCKING mode When an application sends TPM commands in NONBLOCKING mode the driver holds chip->tpm_mutex returning from write(), which triggers: "WARNING: lock held when returning to user space". To fix this issue the driver needs to release the mutex before returning and acquire it again in tpm_dev_async_work() before sending the command. Cc: stable@vger.kernel.org Fixes: 9e1b74a63f776 (tpm: add support for nonblocking operation) Reported-by: Jeffrin Jose T Tested-by: Jeffrin Jose T Signed-off-by: Tadeusz Struk Reviewed-by: Jarkko Sakkinen Signed-off-by: Jarkko Sakkinen --- drivers/char/tpm/tpm-dev-common.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/char/tpm/tpm-dev-common.c b/drivers/char/tpm/tpm-dev-common.c index 2ec47a69a2a6c..b23b0b9992322 100644 --- a/drivers/char/tpm/tpm-dev-common.c +++ b/drivers/char/tpm/tpm-dev-common.c @@ -61,6 +61,12 @@ static void tpm_dev_async_work(struct work_struct *work) mutex_lock(&priv->buffer_mutex); priv->command_enqueued = false; + ret = tpm_try_get_ops(priv->chip); + if (ret) { + priv->response_length = ret; + goto out; + } + ret = tpm_dev_transmit(priv->chip, priv->space, priv->data_buffer, sizeof(priv->data_buffer)); tpm_put_ops(priv->chip); @@ -68,6 +74,7 @@ static void tpm_dev_async_work(struct work_struct *work) priv->response_length = ret; mod_timer(&priv->user_read_timer, jiffies + (120 * HZ)); } +out: mutex_unlock(&priv->buffer_mutex); wake_up_interruptible(&priv->async_wait); } @@ -204,6 +211,7 @@ ssize_t tpm_common_write(struct file *file, const char __user *buf, if (file->f_flags & O_NONBLOCK) { priv->command_enqueued = true; queue_work(tpm_dev_wq, &priv->async_work); + tpm_put_ops(priv->chip); mutex_unlock(&priv->buffer_mutex); return size; } -- GitLab From 8f84bddcfac9117564721ead494db7a604fdf861 Mon Sep 17 00:00:00 2001 From: Tadeusz Struk Date: Thu, 12 Dec 2019 09:48:53 -0800 Subject: [PATCH 0798/1437] tpm: selftest: add test covering async mode Add a test that sends a tpm cmd in an async mode. Currently there is a gap in test coverage with regards to this functionality. Signed-off-by: Tadeusz Struk Reviewed-by: Jarkko Sakkinen Signed-off-by: Jarkko Sakkinen --- tools/testing/selftests/tpm2/test_smoke.sh | 1 + tools/testing/selftests/tpm2/tpm2.py | 19 +++++++++++++++++-- tools/testing/selftests/tpm2/tpm2_tests.py | 13 +++++++++++++ 3 files changed, 31 insertions(+), 2 deletions(-) diff --git a/tools/testing/selftests/tpm2/test_smoke.sh b/tools/testing/selftests/tpm2/test_smoke.sh index 80521d46220cc..cb54ab637ea65 100755 --- a/tools/testing/selftests/tpm2/test_smoke.sh +++ b/tools/testing/selftests/tpm2/test_smoke.sh @@ -2,3 +2,4 @@ # SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) python -m unittest -v tpm2_tests.SmokeTest +python -m unittest -v tpm2_tests.AsyncTest diff --git a/tools/testing/selftests/tpm2/tpm2.py b/tools/testing/selftests/tpm2/tpm2.py index 828c185846248..d0fcb66a88a68 100644 --- a/tools/testing/selftests/tpm2/tpm2.py +++ b/tools/testing/selftests/tpm2/tpm2.py @@ -6,8 +6,8 @@ import socket import struct import sys import unittest -from fcntl import ioctl - +import fcntl +import select TPM2_ST_NO_SESSIONS = 0x8001 TPM2_ST_SESSIONS = 0x8002 @@ -352,6 +352,7 @@ def hex_dump(d): class Client: FLAG_DEBUG = 0x01 FLAG_SPACE = 0x02 + FLAG_NONBLOCK = 0x04 TPM_IOC_NEW_SPACE = 0xa200 def __init__(self, flags = 0): @@ -362,13 +363,27 @@ class Client: else: self.tpm = open('/dev/tpmrm0', 'r+b', buffering=0) + if (self.flags & Client.FLAG_NONBLOCK): + flags = fcntl.fcntl(self.tpm, fcntl.F_GETFL) + flags |= os.O_NONBLOCK + fcntl.fcntl(self.tpm, fcntl.F_SETFL, flags) + self.tpm_poll = select.poll() + def close(self): self.tpm.close() def send_cmd(self, cmd): self.tpm.write(cmd) + + if (self.flags & Client.FLAG_NONBLOCK): + self.tpm_poll.register(self.tpm, select.POLLIN) + self.tpm_poll.poll(10000) + rsp = self.tpm.read() + if (self.flags & Client.FLAG_NONBLOCK): + self.tpm_poll.unregister(self.tpm) + if (self.flags & Client.FLAG_DEBUG) != 0: sys.stderr.write('cmd' + os.linesep) sys.stderr.write(hex_dump(cmd) + os.linesep) diff --git a/tools/testing/selftests/tpm2/tpm2_tests.py b/tools/testing/selftests/tpm2/tpm2_tests.py index d4973be534932..728be7c69b764 100644 --- a/tools/testing/selftests/tpm2/tpm2_tests.py +++ b/tools/testing/selftests/tpm2/tpm2_tests.py @@ -288,3 +288,16 @@ class SpaceTest(unittest.TestCase): self.assertEqual(rc, tpm2.TPM2_RC_COMMAND_CODE | tpm2.TSS2_RESMGR_TPM_RC_LAYER) + +class AsyncTest(unittest.TestCase): + def setUp(self): + logging.basicConfig(filename='AsyncTest.log', level=logging.DEBUG) + + def test_async(self): + log = logging.getLogger(__name__) + log.debug(sys._getframe().f_code.co_name) + + async_client = tpm2.Client(tpm2.Client.FLAG_NONBLOCK) + log.debug("Calling get_cap in a NON_BLOCKING mode") + async_client.get_cap(tpm2.TPM2_CAP_HANDLES, tpm2.HR_LOADED_SESSION) + async_client.close() -- GitLab From a9920d3bad40201ee8ab1da36bee4674f7e50d69 Mon Sep 17 00:00:00 2001 From: Tadeusz Struk Date: Thu, 12 Dec 2019 09:48:59 -0800 Subject: [PATCH 0799/1437] tpm: selftest: cleanup after unseal with wrong auth/policy test Unseal with wrong auth or wrong policy test affects DA lockout and eventually causes the tests to fail with: "ProtocolError: TPM_RC_LOCKOUT: rc=0x00000921" when the tests run multiple times. Send tpm clear command after the test to reset the DA counters. Signed-off-by: Tadeusz Struk Reviewed-by: Jarkko Sakkinen Signed-off-by: Jarkko Sakkinen --- tools/testing/selftests/tpm2/test_smoke.sh | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tools/testing/selftests/tpm2/test_smoke.sh b/tools/testing/selftests/tpm2/test_smoke.sh index cb54ab637ea65..8155c2ea7ccbb 100755 --- a/tools/testing/selftests/tpm2/test_smoke.sh +++ b/tools/testing/selftests/tpm2/test_smoke.sh @@ -3,3 +3,8 @@ python -m unittest -v tpm2_tests.SmokeTest python -m unittest -v tpm2_tests.AsyncTest + +CLEAR_CMD=$(which tpm2_clear) +if [ -n $CLEAR_CMD ]; then + tpm2_clear -T device +fi -- GitLab From d8018a0e9195ba9f0fb9cf0fd3843807c8b952d5 Mon Sep 17 00:00:00 2001 From: Alex Williamson Date: Wed, 11 Dec 2019 13:28:29 -0700 Subject: [PATCH 0800/1437] iommu/vt-d: Set ISA bridge reserved region as relaxable Commit d850c2ee5fe2 ("iommu/vt-d: Expose ISA direct mapping region via iommu_get_resv_regions") created a direct-mapped reserved memory region in order to replace the static identity mapping of the ISA address space, where the latter was then removed in commit df4f3c603aeb ("iommu/vt-d: Remove static identity map code"). According to the history of this code and the Kconfig option surrounding it, this direct mapping exists for the benefit of legacy ISA drivers that are not compatible with the DMA API. In conjuntion with commit 9b77e5c79840 ("vfio/type1: check dma map request is within a valid iova range") this change introduced a regression where the vfio IOMMU backend enforces reserved memory regions per IOMMU group, preventing userspace from creating IOMMU mappings conflicting with prescribed reserved regions. A necessary prerequisite for the vfio change was the introduction of "relaxable" direct mappings introduced by commit adfd37382090 ("iommu: Introduce IOMMU_RESV_DIRECT_RELAXABLE reserved memory regions"). These relaxable direct mappings provide the same identity mapping support in the default domain, but also indicate that the reservation is software imposed and may be relaxed under some conditions, such as device assignment. Convert the ISA bridge direct-mapped reserved region to relaxable to reflect that the restriction is self imposed and need not be enforced by drivers such as vfio. Fixes: 1c5c59fbad20 ("iommu/vt-d: Differentiate relaxable and non relaxable RMRRs") Cc: stable@vger.kernel.org # v5.3+ Link: https://lore.kernel.org/linux-iommu/20191211082304.2d4fab45@x1.home Reported-by: cprt Tested-by: cprt Signed-off-by: Alex Williamson Acked-by: Lu Baolu Reviewed-by: Eric Auger Tested-by: Jerry Snitselaar Reviewed-by: Jerry Snitselaar Signed-off-by: Joerg Roedel --- drivers/iommu/intel-iommu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c index 0c8d81f56a306..6eb0dd7489a11 100644 --- a/drivers/iommu/intel-iommu.c +++ b/drivers/iommu/intel-iommu.c @@ -5737,7 +5737,7 @@ static void intel_iommu_get_resv_regions(struct device *device, if ((pdev->class >> 8) == PCI_CLASS_BRIDGE_ISA) { reg = iommu_alloc_resv_region(0, 1UL << 24, 0, - IOMMU_RESV_DIRECT); + IOMMU_RESV_DIRECT_RELAXABLE); if (reg) list_add_tail(®->list, head); } -- GitLab From 272a72103012862e3a24ea06635253ead0b6e808 Mon Sep 17 00:00:00 2001 From: Thomas Hebb Date: Mon, 9 Dec 2019 00:19:17 -0800 Subject: [PATCH 0801/1437] kconfig: don't crash on NULL expressions in expr_eq() NULL expressions are taken to always be true, as implemented by the expr_is_yes() macro and by several other functions in expr.c. As such, they ought to be valid inputs to expr_eq(), which compares two expressions. Signed-off-by: Thomas Hebb Signed-off-by: Masahiro Yamada --- scripts/kconfig/expr.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/scripts/kconfig/expr.c b/scripts/kconfig/expr.c index 77ffff3a053cc..9f1de58e9f0c9 100644 --- a/scripts/kconfig/expr.c +++ b/scripts/kconfig/expr.c @@ -254,6 +254,13 @@ static int expr_eq(struct expr *e1, struct expr *e2) { int res, old_count; + /* + * A NULL expr is taken to be yes, but there's also a different way to + * represent yes. expr_is_yes() checks for either representation. + */ + if (!e1 || !e2) + return expr_is_yes(e1) && expr_is_yes(e2); + if (e1->type != e2->type) return 0; switch (e1->type) { -- GitLab From a11391b6f50689adb22c65df783e09143fafb794 Mon Sep 17 00:00:00 2001 From: "Enrico Weigelt, metux IT consult" Date: Mon, 16 Dec 2019 21:07:19 +0100 Subject: [PATCH 0802/1437] scripts: package: mkdebian: add missing rsync dependency We've missed the dependency to rsync, so build fails on minimal containers. Fixes: 59b2bd05f5f4 ("kbuild: add 'headers' target to build up uapi headers in usr/include") Signed-off-by: Enrico Weigelt, metux IT consult Signed-off-by: Masahiro Yamada --- scripts/package/mkdebian | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/package/mkdebian b/scripts/package/mkdebian index e0750b70453f2..7c230016b08d1 100755 --- a/scripts/package/mkdebian +++ b/scripts/package/mkdebian @@ -174,7 +174,7 @@ Source: $sourcename Section: kernel Priority: optional Maintainer: $maintainer -Build-Depends: bc, kmod, cpio, bison, flex | flex:native $extra_build_depends +Build-Depends: bc, rsync, kmod, cpio, bison, flex | flex:native $extra_build_depends Homepage: http://www.kernel.org/ Package: $packagename -- GitLab From d55157f6aaca04df4223c2c7a96995ceda016a15 Mon Sep 17 00:00:00 2001 From: Vandita Kulkarni Date: Mon, 16 Dec 2019 13:36:19 +0530 Subject: [PATCH 0803/1437] drm/i915: Fix WARN_ON condition for cursor plane ddb allocation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In some cases like latency[level]==0, wm[level].res_lines>31, min_ddb_alloc can be U16_MAX, exclude it from the WARN_ON. v2: Specify the cases in which we hit U16_MAX, indentation (Ville) Fixes: 10a7e07b68b9 ("drm/i915: Make sure cursor has enough ddb for the selected wm level") Suggested-by: Ville Syrjälä Signed-off-by: Vandita Kulkarni Reviewed-by: Ville Syrjälä Signed-off-by: Ville Syrjälä Link: https://patchwork.freedesktop.org/patch/msgid/20191216080619.10945-1-vandita.kulkarni@intel.com (cherry picked from commit 4ba487019d1a196051feefab57f4a393815733b4) Signed-off-by: Joonas Lahtinen --- drivers/gpu/drm/i915/intel_pm.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index 809bff955b5ac..75ae6f4951616 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -4291,8 +4291,8 @@ skl_allocate_pipe_ddb(struct intel_crtc_state *crtc_state, &crtc_state->wm.skl.optimal.planes[plane_id]; if (plane_id == PLANE_CURSOR) { - if (WARN_ON(wm->wm[level].min_ddb_alloc > - total[PLANE_CURSOR])) { + if (wm->wm[level].min_ddb_alloc > total[PLANE_CURSOR]) { + WARN_ON(wm->wm[level].min_ddb_alloc != U16_MAX); blocks = U32_MAX; break; } -- GitLab From 75d18385394f56db76845d91a192532aba421875 Mon Sep 17 00:00:00 2001 From: Lu Baolu Date: Wed, 11 Dec 2019 09:40:15 +0800 Subject: [PATCH 0804/1437] iommu/vt-d: Fix dmar pte read access not set error If the default DMA domain of a group doesn't fit a device, it will still sit in the group but use a private identity domain. When map/unmap/iova_to_phys come through iommu API, the driver should still serve them, otherwise, other devices in the same group will be impacted. Since identity domain has been mapped with the whole available memory space and RMRRs, we don't need to worry about the impact on it. Link: https://www.spinics.net/lists/iommu/msg40416.html Cc: Jerry Snitselaar Reported-by: Jerry Snitselaar Fixes: 942067f1b6b97 ("iommu/vt-d: Identify default domains replaced with private") Cc: stable@vger.kernel.org # v5.3+ Signed-off-by: Lu Baolu Reviewed-by: Jerry Snitselaar Tested-by: Jerry Snitselaar Signed-off-by: Joerg Roedel --- drivers/iommu/intel-iommu.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c index 6eb0dd7489a11..c8ced7712a8e1 100644 --- a/drivers/iommu/intel-iommu.c +++ b/drivers/iommu/intel-iommu.c @@ -5478,9 +5478,6 @@ static int intel_iommu_map(struct iommu_domain *domain, int prot = 0; int ret; - if (dmar_domain->flags & DOMAIN_FLAG_LOSE_CHILDREN) - return -EINVAL; - if (iommu_prot & IOMMU_READ) prot |= DMA_PTE_READ; if (iommu_prot & IOMMU_WRITE) @@ -5523,8 +5520,6 @@ static size_t intel_iommu_unmap(struct iommu_domain *domain, /* Cope with horrid API which requires us to unmap more than the size argument if it happens to be a large-page mapping. */ BUG_ON(!pfn_to_dma_pte(dmar_domain, iova >> VTD_PAGE_SHIFT, &level)); - if (dmar_domain->flags & DOMAIN_FLAG_LOSE_CHILDREN) - return 0; if (size < VTD_PAGE_SIZE << level_to_offset_bits(level)) size = VTD_PAGE_SIZE << level_to_offset_bits(level); @@ -5556,9 +5551,6 @@ static phys_addr_t intel_iommu_iova_to_phys(struct iommu_domain *domain, int level = 0; u64 phys = 0; - if (dmar_domain->flags & DOMAIN_FLAG_LOSE_CHILDREN) - return 0; - pte = pfn_to_dma_pte(dmar_domain, iova >> VTD_PAGE_SHIFT, &level); if (pte) phys = dma_pte_addr(pte); -- GitLab From d360211524bece6db9920f32c91808235290b51c Mon Sep 17 00:00:00 2001 From: Jerry Snitselaar Date: Tue, 10 Dec 2019 11:56:06 -0700 Subject: [PATCH 0805/1437] iommu: set group default domain before creating direct mappings iommu_group_create_direct_mappings uses group->default_domain, but right after it is called, request_default_domain_for_dev calls iommu_domain_free for the default domain, and sets the group default domain to a different domain. Move the iommu_group_create_direct_mappings call to after the group default domain is set, so the direct mappings get associated with that domain. Cc: Joerg Roedel Cc: Lu Baolu Cc: iommu@lists.linux-foundation.org Cc: stable@vger.kernel.org Fixes: 7423e01741dd ("iommu: Add API to request DMA domain for device") Signed-off-by: Jerry Snitselaar Reviewed-by: Lu Baolu Signed-off-by: Joerg Roedel --- drivers/iommu/iommu.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c index db7bfd4f2d20e..fa908179b80b9 100644 --- a/drivers/iommu/iommu.c +++ b/drivers/iommu/iommu.c @@ -2282,13 +2282,13 @@ request_default_domain_for_dev(struct device *dev, unsigned long type) goto out; } - iommu_group_create_direct_mappings(group, dev); - /* Make the domain the default for this group */ if (group->default_domain) iommu_domain_free(group->default_domain); group->default_domain = domain; + iommu_group_create_direct_mappings(group, dev); + dev_info(dev, "Using iommu %s mapping\n", type == IOMMU_DOMAIN_DMA ? "dma" : "direct"); -- GitLab From cde9319e884eb6267a0df446f3c131fe1108defb Mon Sep 17 00:00:00 2001 From: Jerry Snitselaar Date: Thu, 12 Dec 2019 22:36:42 -0700 Subject: [PATCH 0806/1437] iommu/vt-d: Allocate reserved region for ISA with correct permission Currently the reserved region for ISA is allocated with no permissions. If a dma domain is being used, mapping this region will fail. Set the permissions to DMA_PTE_READ|DMA_PTE_WRITE. Cc: Joerg Roedel Cc: Lu Baolu Cc: iommu@lists.linux-foundation.org Cc: stable@vger.kernel.org # v5.3+ Fixes: d850c2ee5fe2 ("iommu/vt-d: Expose ISA direct mapping region via iommu_get_resv_regions") Signed-off-by: Jerry Snitselaar Acked-by: Lu Baolu Signed-off-by: Joerg Roedel --- drivers/iommu/intel-iommu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c index c8ced7712a8e1..42966611a1926 100644 --- a/drivers/iommu/intel-iommu.c +++ b/drivers/iommu/intel-iommu.c @@ -5728,7 +5728,7 @@ static void intel_iommu_get_resv_regions(struct device *device, struct pci_dev *pdev = to_pci_dev(device); if ((pdev->class >> 8) == PCI_CLASS_BRIDGE_ISA) { - reg = iommu_alloc_resv_region(0, 1UL << 24, 0, + reg = iommu_alloc_resv_region(0, 1UL << 24, prot, IOMMU_RESV_DIRECT_RELAXABLE); if (reg) list_add_tail(®->list, head); -- GitLab From 1e69a0efc0bd0e02b8327e7186fbb4a81878ea0b Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Fri, 6 Dec 2019 12:50:16 +0100 Subject: [PATCH 0807/1437] perf/x86: Fix potential out-of-bounds access UBSAN reported out-of-bound accesses for x86_pmu.event_map(), it's arguments should be < x86_pmu.max_events. Make sure all users observe this constraint. Reported-by: Meelis Roos Signed-off-by: Peter Zijlstra (Intel) Tested-by: Meelis Roos --- arch/x86/events/core.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/arch/x86/events/core.c b/arch/x86/events/core.c index 9a89d98c55bd8..84fe1becbe26b 100644 --- a/arch/x86/events/core.c +++ b/arch/x86/events/core.c @@ -1642,9 +1642,12 @@ static struct attribute_group x86_pmu_format_group __ro_after_init = { ssize_t events_sysfs_show(struct device *dev, struct device_attribute *attr, char *page) { - struct perf_pmu_events_attr *pmu_attr = \ + struct perf_pmu_events_attr *pmu_attr = container_of(attr, struct perf_pmu_events_attr, attr); - u64 config = x86_pmu.event_map(pmu_attr->id); + u64 config = 0; + + if (pmu_attr->id < x86_pmu.max_events) + config = x86_pmu.event_map(pmu_attr->id); /* string trumps id */ if (pmu_attr->event_str) @@ -1713,6 +1716,9 @@ is_visible(struct kobject *kobj, struct attribute *attr, int idx) { struct perf_pmu_events_attr *pmu_attr; + if (idx >= x86_pmu.max_events) + return 0; + pmu_attr = container_of(attr, struct perf_pmu_events_attr, attr.attr); /* str trumps id */ return pmu_attr->event_str || x86_pmu.event_map(idx) ? attr->mode : 0; -- GitLab From ff61541cc6c1962957758ba433c574b76f588d23 Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Thu, 5 Dec 2019 17:28:52 +0300 Subject: [PATCH 0808/1437] perf/x86/intel/bts: Fix the use of page_private() Commit 8062382c8dbe2 ("perf/x86/intel/bts: Add BTS PMU driver") brought in a warning with the BTS buffer initialization that is easily tripped with (assuming KPTI is disabled): instantly throwing: > ------------[ cut here ]------------ > WARNING: CPU: 2 PID: 326 at arch/x86/events/intel/bts.c:86 bts_buffer_setup_aux+0x117/0x3d0 > Modules linked in: > CPU: 2 PID: 326 Comm: perf Not tainted 5.4.0-rc8-00291-gceb9e77324fa #904 > RIP: 0010:bts_buffer_setup_aux+0x117/0x3d0 > Call Trace: > rb_alloc_aux+0x339/0x550 > perf_mmap+0x607/0xc70 > mmap_region+0x76b/0xbd0 ... It appears to assume (for lost raisins) that PagePrivate() is set, while later it actually tests for PagePrivate() before using page_private(). Make it consistent and always check PagePrivate() before using page_private(). Fixes: 8062382c8dbe2 ("perf/x86/intel/bts: Add BTS PMU driver") Signed-off-by: Alexander Shishkin Signed-off-by: Peter Zijlstra (Intel) Cc: Jiri Olsa Cc: Vince Weaver Cc: Ingo Molnar Cc: Arnaldo Carvalho de Melo Link: https://lkml.kernel.org/r/20191205142853.28894-2-alexander.shishkin@linux.intel.com --- arch/x86/events/intel/bts.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/arch/x86/events/intel/bts.c b/arch/x86/events/intel/bts.c index 38de4a7f6752b..6a3b599ee0fe7 100644 --- a/arch/x86/events/intel/bts.c +++ b/arch/x86/events/intel/bts.c @@ -63,9 +63,17 @@ struct bts_buffer { static struct pmu bts_pmu; +static int buf_nr_pages(struct page *page) +{ + if (!PagePrivate(page)) + return 1; + + return 1 << page_private(page); +} + static size_t buf_size(struct page *page) { - return 1 << (PAGE_SHIFT + page_private(page)); + return buf_nr_pages(page) * PAGE_SIZE; } static void * @@ -83,9 +91,7 @@ bts_buffer_setup_aux(struct perf_event *event, void **pages, /* count all the high order buffers */ for (pg = 0, nbuf = 0; pg < nr_pages;) { page = virt_to_page(pages[pg]); - if (WARN_ON_ONCE(!PagePrivate(page) && nr_pages > 1)) - return NULL; - pg += 1 << page_private(page); + pg += buf_nr_pages(page); nbuf++; } @@ -109,7 +115,7 @@ bts_buffer_setup_aux(struct perf_event *event, void **pages, unsigned int __nr_pages; page = virt_to_page(pages[pg]); - __nr_pages = PagePrivate(page) ? 1 << page_private(page) : 1; + __nr_pages = buf_nr_pages(page); buf->buf[nbuf].page = page; buf->buf[nbuf].offset = offset; buf->buf[nbuf].displacement = (pad ? BTS_RECORD_SIZE - pad : 0); -- GitLab From 92ca7da4bdc24d63bb0bcd241c11441ddb63b80a Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Tue, 10 Dec 2019 12:51:01 +0200 Subject: [PATCH 0809/1437] perf/x86/intel: Fix PT PMI handling Commit: ccbebba4c6bf ("perf/x86/intel/pt: Bypass PT vs. LBR exclusivity if the core supports it") skips the PT/LBR exclusivity check on CPUs where PT and LBRs coexist, but also inadvertently skips the active_events bump for PT in that case, which is a bug. If there aren't any hardware events at the same time as PT, the PMI handler will ignore PT PMIs, as active_events reads zero in that case, resulting in the "Uhhuh" spurious NMI warning and PT data loss. Fix this by always increasing active_events for PT events. Fixes: ccbebba4c6bf ("perf/x86/intel/pt: Bypass PT vs. LBR exclusivity if the core supports it") Reported-by: Vitaly Slobodskoy Signed-off-by: Alexander Shishkin Signed-off-by: Peter Zijlstra (Intel) Acked-by: Alexey Budankov Cc: Jiri Olsa Cc: Ingo Molnar Cc: Arnaldo Carvalho de Melo Link: https://lkml.kernel.org/r/20191210105101.77210-1-alexander.shishkin@linux.intel.com --- arch/x86/events/core.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/arch/x86/events/core.c b/arch/x86/events/core.c index 84fe1becbe26b..f118af9f0718b 100644 --- a/arch/x86/events/core.c +++ b/arch/x86/events/core.c @@ -376,7 +376,7 @@ int x86_add_exclusive(unsigned int what) * LBR and BTS are still mutually exclusive. */ if (x86_pmu.lbr_pt_coexist && what == x86_lbr_exclusive_pt) - return 0; + goto out; if (!atomic_inc_not_zero(&x86_pmu.lbr_exclusive[what])) { mutex_lock(&pmc_reserve_mutex); @@ -388,6 +388,7 @@ int x86_add_exclusive(unsigned int what) mutex_unlock(&pmc_reserve_mutex); } +out: atomic_inc(&active_events); return 0; @@ -398,11 +399,15 @@ fail_unlock: void x86_del_exclusive(unsigned int what) { + atomic_dec(&active_events); + + /* + * See the comment in x86_add_exclusive(). + */ if (x86_pmu.lbr_pt_coexist && what == x86_lbr_exclusive_pt) return; atomic_dec(&x86_pmu.lbr_exclusive[what]); - atomic_dec(&active_events); } int x86_setup_perfctr(struct perf_event *event) -- GitLab From 9f0bff1180efc9ea988fed3fd93da7647151ac8b Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Tue, 19 Nov 2019 13:14:29 +0100 Subject: [PATCH 0810/1437] perf/core: Add SRCU annotation for pmus list walk Since commit 28875945ba98d ("rcu: Add support for consolidated-RCU reader checking") there is an additional check to ensure that a RCU related lock is held while the RCU list is iterated. This section holds the SRCU reader lock instead. Add annotation to list_for_each_entry_rcu() that pmus_srcu must be acquired during the list traversal. Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Peter Zijlstra (Intel) Reviewed-by: Joel Fernandes (Google) Link: https://lkml.kernel.org/r/20191119121429.zhcubzdhm672zasg@linutronix.de --- kernel/events/core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/events/core.c b/kernel/events/core.c index 4ff86d57f9e53..a1f8bde19b561 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -10523,7 +10523,7 @@ again: goto unlock; } - list_for_each_entry_rcu(pmu, &pmus, entry) { + list_for_each_entry_rcu(pmu, &pmus, entry, lockdep_is_held(&pmus_srcu)) { ret = perf_try_init_event(pmu, event); if (!ret) goto unlock; -- GitLab From 3dfbe25c27eab7c90c8a7e97b4c354a9d24dd985 Mon Sep 17 00:00:00 2001 From: Johannes Weiner Date: Tue, 3 Dec 2019 13:35:23 -0500 Subject: [PATCH 0811/1437] sched/psi: Fix sampling error and rare div0 crashes with cgroups and high uptime Jingfeng reports rare div0 crashes in psi on systems with some uptime: [58914.066423] divide error: 0000 [#1] SMP [58914.070416] Modules linked in: ipmi_poweroff ipmi_watchdog toa overlay fuse tcp_diag inet_diag binfmt_misc aisqos(O) aisqos_hotfixes(O) [58914.083158] CPU: 94 PID: 140364 Comm: kworker/94:2 Tainted: G W OE K 4.9.151-015.ali3000.alios7.x86_64 #1 [58914.093722] Hardware name: Alibaba Alibaba Cloud ECS/Alibaba Cloud ECS, BIOS 3.23.34 02/14/2019 [58914.102728] Workqueue: events psi_update_work [58914.107258] task: ffff8879da83c280 task.stack: ffffc90059dcc000 [58914.113336] RIP: 0010:[] [] psi_update_stats+0x1c1/0x330 [58914.122183] RSP: 0018:ffffc90059dcfd60 EFLAGS: 00010246 [58914.127650] RAX: 0000000000000000 RBX: ffff8858fe98be50 RCX: 000000007744d640 [58914.134947] RDX: 0000000000000000 RSI: 0000000000000000 RDI: 00003594f700648e [58914.142243] RBP: ffffc90059dcfdf8 R08: 0000359500000000 R09: 0000000000000000 [58914.149538] R10: 0000000000000000 R11: 0000000000000000 R12: 0000359500000000 [58914.156837] R13: 0000000000000000 R14: 0000000000000000 R15: ffff8858fe98bd78 [58914.164136] FS: 0000000000000000(0000) GS:ffff887f7f380000(0000) knlGS:0000000000000000 [58914.172529] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 [58914.178467] CR2: 00007f2240452090 CR3: 0000005d5d258000 CR4: 00000000007606f0 [58914.185765] DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 [58914.193061] DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400 [58914.200360] PKRU: 55555554 [58914.203221] Stack: [58914.205383] ffff8858fe98bd48 00000000000002f0 0000002e81036d09 ffffc90059dcfde8 [58914.213168] ffff8858fe98bec8 0000000000000000 0000000000000000 0000000000000000 [58914.220951] 0000000000000000 0000000000000000 0000000000000000 0000000000000000 [58914.228734] Call Trace: [58914.231337] [] psi_update_work+0x22/0x60 [58914.237067] [] process_one_work+0x189/0x420 [58914.243063] [] worker_thread+0x4e/0x4b0 [58914.248701] [] ? process_one_work+0x420/0x420 [58914.254869] [] kthread+0xe6/0x100 [58914.259994] [] ? kthread_park+0x60/0x60 [58914.265640] [] ret_from_fork+0x39/0x50 [58914.271193] Code: 41 29 c3 4d 39 dc 4d 0f 42 dc <49> f7 f1 48 8b 13 48 89 c7 48 c1 [58914.279691] RIP [] psi_update_stats+0x1c1/0x330 The crashing instruction is trying to divide the observed stall time by the sampling period. The period, stored in R8, is not 0, but we are dividing by the lower 32 bits only, which are all 0 in this instance. We could switch to a 64-bit division, but the period shouldn't be that big in the first place. It's the time between the last update and the next scheduled one, and so should always be around 2s and comfortably fit into 32 bits. The bug is in the initialization of new cgroups: we schedule the first sampling event in a cgroup as an offset of sched_clock(), but fail to initialize the last_update timestamp, and it defaults to 0. That results in a bogusly large sampling period the first time we run the sampling code, and consequently we underreport pressure for the first 2s of a cgroup's life. But worse, if sched_clock() is sufficiently advanced on the system, and the user gets unlucky, the period's lower 32 bits can all be 0 and the sampling division will crash. Fix this by initializing the last update timestamp to the creation time of the cgroup, thus correctly marking the start of the first pressure sampling period in a new cgroup. Reported-by: Jingfeng Xie Signed-off-by: Johannes Weiner Signed-off-by: Peter Zijlstra (Intel) Cc: Suren Baghdasaryan Link: https://lkml.kernel.org/r/20191203183524.41378-2-hannes@cmpxchg.org --- kernel/sched/psi.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/kernel/sched/psi.c b/kernel/sched/psi.c index 517e3719027e6..970db4686dd44 100644 --- a/kernel/sched/psi.c +++ b/kernel/sched/psi.c @@ -185,7 +185,8 @@ static void group_init(struct psi_group *group) for_each_possible_cpu(cpu) seqcount_init(&per_cpu_ptr(group->pcpu, cpu)->seq); - group->avg_next_update = sched_clock() + psi_period; + group->avg_last_update = sched_clock(); + group->avg_next_update = group->avg_last_update + psi_period; INIT_DELAYED_WORK(&group->avgs_work, psi_avgs_work); mutex_init(&group->avgs_lock); /* Init trigger-related members */ -- GitLab From c3466952ca1514158d7c16c9cfc48c27d5c5dc0f Mon Sep 17 00:00:00 2001 From: Johannes Weiner Date: Tue, 3 Dec 2019 13:35:24 -0500 Subject: [PATCH 0812/1437] psi: Fix a division error in psi poll() The psi window size is a u64 an can be up to 10 seconds right now, which exceeds the lower 32 bits of the variable. We currently use div_u64 for it, which is meant only for 32-bit divisors. The result is garbage pressure sampling values and even potential div0 crashes. Use div64_u64. Signed-off-by: Johannes Weiner Signed-off-by: Peter Zijlstra (Intel) Reviewed-by: Suren Baghdasaryan Cc: Jingfeng Xie Link: https://lkml.kernel.org/r/20191203183524.41378-3-hannes@cmpxchg.org --- kernel/sched/psi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/sched/psi.c b/kernel/sched/psi.c index 970db4686dd44..ce8f6748678a2 100644 --- a/kernel/sched/psi.c +++ b/kernel/sched/psi.c @@ -482,7 +482,7 @@ static u64 window_update(struct psi_window *win, u64 now, u64 value) u32 remaining; remaining = win->size - elapsed; - growth += div_u64(win->prev_growth * remaining, win->size); + growth += div64_u64(win->prev_growth * remaining, win->size); } return growth; -- GitLab From 7ed735c33104f3c6194fbc67e3a8b6e64ae84ad1 Mon Sep 17 00:00:00 2001 From: Vincent Guittot Date: Wed, 4 Dec 2019 19:21:40 +0100 Subject: [PATCH 0813/1437] sched/fair: Fix find_idlest_group() to handle CPU affinity Because of CPU affinity, the local group can be skipped which breaks the assumption that statistics are always collected for local group. With uninitialized local_sgs, the comparison is meaningless and the behavior unpredictable. This can even end up to use local pointer which is to NULL in this case. If the local group has been skipped because of CPU affinity, we return the idlest group. Fixes: 57abff067a08 ("sched/fair: Rework find_idlest_group()") Reported-by: John Stultz Signed-off-by: Vincent Guittot Signed-off-by: Peter Zijlstra (Intel) Reviewed-by: Valentin Schneider Tested-by: John Stultz Cc: rostedt@goodmis.org Cc: valentin.schneider@arm.com Cc: mingo@redhat.com Cc: mgorman@suse.de Cc: juri.lelli@redhat.com Cc: dietmar.eggemann@arm.com Cc: bsegall@google.com Cc: qais.yousef@arm.com Link: https://lkml.kernel.org/r/1575483700-22153-1-git-send-email-vincent.guittot@linaro.org --- kernel/sched/fair.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index 08a233e97a019..146b6c83633f7 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -8417,6 +8417,10 @@ find_idlest_group(struct sched_domain *sd, struct task_struct *p, if (!idlest) return NULL; + /* The local group has been skipped because of CPU affinity */ + if (!local) + return idlest; + /* * If the local group is idler than the selected idlest group * don't try and push the task. -- GitLab From 6cf82d559e1a1d89f06ff4d428aca479c1dd0be6 Mon Sep 17 00:00:00 2001 From: Vincent Guittot Date: Fri, 29 Nov 2019 15:04:47 +0100 Subject: [PATCH 0814/1437] sched/cfs: fix spurious active migration The load balance can fail to find a suitable task during the periodic check because the imbalance is smaller than half of the load of the waiting tasks. This results in the increase of the number of failed load balance, which can end up to start an active migration. This active migration is useless because the current running task is not a better choice than the waiting ones. In fact, the current task was probably not running but waiting for the CPU during one of the previous attempts and it had already not been selected. When load balance fails too many times to migrate a task, we should relax the contraint on the maximum load of the tasks that can be migrated similarly to what is done with cache hotness. Before the rework, load balance used to set the imbalance to the average load_per_task in order to mitigate such situation. This increased the likelihood of migrating a task but also of selecting a larger task than needed while more appropriate ones were in the list. Signed-off-by: Vincent Guittot Signed-off-by: Peter Zijlstra (Intel) Link: https://lkml.kernel.org/r/1575036287-6052-1-git-send-email-vincent.guittot@linaro.org --- kernel/sched/fair.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index 146b6c83633f7..ba749f579714c 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -7328,7 +7328,14 @@ static int detach_tasks(struct lb_env *env) load < 16 && !env->sd->nr_balance_failed) goto next; - if (load/2 > env->imbalance) + /* + * Make sure that we don't migrate too much load. + * Nevertheless, let relax the constraint if + * scheduler fails to find a good waiting task to + * migrate. + */ + if (load/2 > env->imbalance && + env->sd->nr_balance_failed <= env->sd->cache_nice_tries) goto next; env->imbalance -= load; -- GitLab From fb2b90014d782d80d7ebf663e50f96d8c507a73c Mon Sep 17 00:00:00 2001 From: Sudip Mukherjee Date: Thu, 12 Dec 2019 13:16:02 +0000 Subject: [PATCH 0815/1437] tty: link tty and port before configuring it as console There seems to be a race condition in tty drivers and I could see on many boot cycles a NULL pointer dereference as tty_init_dev() tries to do 'tty->port->itty = tty' even though tty->port is NULL. 'tty->port' will be set by the driver and if the driver has not yet done it before we open the tty device we can get to this situation. By adding some extra debug prints, I noticed that: 6.650130: uart_add_one_port 6.663849: register_console 6.664846: tty_open 6.674391: tty_init_dev 6.675456: tty_port_link_device uart_add_one_port() registers the console, as soon as it registers, the userspace tries to use it and that leads to tty_open() but uart_add_one_port() has not yet done tty_port_link_device() and so tty->port is not yet configured when control reaches tty_init_dev(). Further look into the code and tty_port_link_device() is done by uart_add_one_port(). After registering the console uart_add_one_port() will call tty_port_register_device_attr_serdev() and tty_port_link_device() is called from this. Call add tty_port_link_device() before uart_configure_port() is done and add a check in tty_port_link_device() so that it only links the port if it has not been done yet. Suggested-by: Jiri Slaby Signed-off-by: Sudip Mukherjee Cc: stable Link: https://lore.kernel.org/r/20191212131602.29504-1-sudipm.mukherjee@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/serial_core.c | 1 + drivers/tty/tty_port.c | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index b0a6eb106edb8..7c27827857363 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -2834,6 +2834,7 @@ int uart_add_one_port(struct uart_driver *drv, struct uart_port *uport) if (uport->cons && uport->dev) of_console_check(uport->dev->of_node, uport->cons->name, uport->line); + tty_port_link_device(port, drv->tty_driver, uport->line); uart_configure_port(drv, state, uport); port->console = uart_console(uport); diff --git a/drivers/tty/tty_port.c b/drivers/tty/tty_port.c index 044c3cbdcfa40..5023c85ebc6e0 100644 --- a/drivers/tty/tty_port.c +++ b/drivers/tty/tty_port.c @@ -89,7 +89,8 @@ void tty_port_link_device(struct tty_port *port, { if (WARN_ON(index >= driver->num)) return; - driver->ports[index] = port; + if (!driver->ports[index]) + driver->ports[index] = port; } EXPORT_SYMBOL_GPL(tty_port_link_device); -- GitLab From cb47b9f8630ae3fa3f5fbd0c7003faba7abdf711 Mon Sep 17 00:00:00 2001 From: David Engraf Date: Mon, 16 Dec 2019 09:54:03 +0100 Subject: [PATCH 0816/1437] tty/serial: atmel: fix out of range clock divider handling Use MCK_DIV8 when the clock divider is > 65535. Unfortunately the mode register was already written thus the clock selection is ignored. Fix by doing the baud rate calulation before setting the mode. Fixes: 5bf5635ac170 ("tty/serial: atmel: add fractional baud rate support") Signed-off-by: David Engraf Acked-by: Ludovic Desroches Acked-by: Richard Genoud Cc: stable Link: https://lore.kernel.org/r/20191216085403.17050-1-david.engraf@sysgo.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/atmel_serial.c | 43 ++++++++++++++++--------------- 1 file changed, 22 insertions(+), 21 deletions(-) diff --git a/drivers/tty/serial/atmel_serial.c b/drivers/tty/serial/atmel_serial.c index a8dc8af83f39a..1ba9bc667e136 100644 --- a/drivers/tty/serial/atmel_serial.c +++ b/drivers/tty/serial/atmel_serial.c @@ -2270,27 +2270,6 @@ static void atmel_set_termios(struct uart_port *port, struct ktermios *termios, mode |= ATMEL_US_USMODE_NORMAL; } - /* set the mode, clock divisor, parity, stop bits and data size */ - atmel_uart_writel(port, ATMEL_US_MR, mode); - - /* - * when switching the mode, set the RTS line state according to the - * new mode, otherwise keep the former state - */ - if ((old_mode & ATMEL_US_USMODE) != (mode & ATMEL_US_USMODE)) { - unsigned int rts_state; - - if ((mode & ATMEL_US_USMODE) == ATMEL_US_USMODE_HWHS) { - /* let the hardware control the RTS line */ - rts_state = ATMEL_US_RTSDIS; - } else { - /* force RTS line to low level */ - rts_state = ATMEL_US_RTSEN; - } - - atmel_uart_writel(port, ATMEL_US_CR, rts_state); - } - /* * Set the baud rate: * Fractional baudrate allows to setup output frequency more @@ -2317,6 +2296,28 @@ static void atmel_set_termios(struct uart_port *port, struct ktermios *termios, if (!(port->iso7816.flags & SER_ISO7816_ENABLED)) atmel_uart_writel(port, ATMEL_US_BRGR, quot); + + /* set the mode, clock divisor, parity, stop bits and data size */ + atmel_uart_writel(port, ATMEL_US_MR, mode); + + /* + * when switching the mode, set the RTS line state according to the + * new mode, otherwise keep the former state + */ + if ((old_mode & ATMEL_US_USMODE) != (mode & ATMEL_US_USMODE)) { + unsigned int rts_state; + + if ((mode & ATMEL_US_USMODE) == ATMEL_US_USMODE_HWHS) { + /* let the hardware control the RTS line */ + rts_state = ATMEL_US_RTSDIS; + } else { + /* force RTS line to low level */ + rts_state = ATMEL_US_RTSEN; + } + + atmel_uart_writel(port, ATMEL_US_CR, rts_state); + } + atmel_uart_writel(port, ATMEL_US_CR, ATMEL_US_RSTSTA | ATMEL_US_RSTRX); atmel_uart_writel(port, ATMEL_US_CR, ATMEL_US_TXEN | ATMEL_US_RXEN); atmel_port->tx_stopped = false; -- GitLab From 13591a1c3899cfe6891ae0df46637097fdde36a4 Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Tue, 10 Dec 2019 12:07:32 +0200 Subject: [PATCH 0817/1437] ath11k: register HE mesh capabilities The capabilities for the HE mesh are generated from the capabilities reported by the fw. But the firmware only reports the overall capabilities and not the one which are specific for mesh. Some of them (TWT, MU UL/DL, TB PPDU, ...) require an infrastructure setup with a main STA (AP) controlling the operations. This is not the case for mesh and thus these capabilities are removed from the list of capabilities. Signed-off-by: Sven Eckelmann Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath11k/mac.c | 75 +++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) diff --git a/drivers/net/wireless/ath/ath11k/mac.c b/drivers/net/wireless/ath/ath11k/mac.c index 978d8768d68a2..65f6a25855772 100644 --- a/drivers/net/wireless/ath/ath11k/mac.c +++ b/drivers/net/wireless/ath/ath11k/mac.c @@ -3346,6 +3346,77 @@ static void ath11k_gen_ppe_thresh(struct ath11k_ppe_threshold *fw_ppet, } } +static void +ath11k_mac_filter_he_cap_mesh(struct ieee80211_he_cap_elem *he_cap_elem) +{ + u8 m; + + m = IEEE80211_HE_MAC_CAP0_TWT_RES | + IEEE80211_HE_MAC_CAP0_TWT_REQ; + he_cap_elem->mac_cap_info[0] &= ~m; + + m = IEEE80211_HE_MAC_CAP2_TRS | + IEEE80211_HE_MAC_CAP2_BCAST_TWT | + IEEE80211_HE_MAC_CAP2_MU_CASCADING; + he_cap_elem->mac_cap_info[2] &= ~m; + + m = IEEE80211_HE_MAC_CAP3_FLEX_TWT_SCHED | + IEEE80211_HE_MAC_CAP2_BCAST_TWT | + IEEE80211_HE_MAC_CAP2_MU_CASCADING; + he_cap_elem->mac_cap_info[3] &= ~m; + + m = IEEE80211_HE_MAC_CAP4_BSRP_BQRP_A_MPDU_AGG | + IEEE80211_HE_MAC_CAP4_BQR; + he_cap_elem->mac_cap_info[4] &= ~m; + + m = IEEE80211_HE_MAC_CAP5_SUBCHAN_SELECVITE_TRANSMISSION | + IEEE80211_HE_MAC_CAP5_UL_2x996_TONE_RU | + IEEE80211_HE_MAC_CAP5_PUNCTURED_SOUNDING | + IEEE80211_HE_MAC_CAP5_HT_VHT_TRIG_FRAME_RX; + he_cap_elem->mac_cap_info[5] &= ~m; + + m = IEEE80211_HE_PHY_CAP2_UL_MU_FULL_MU_MIMO | + IEEE80211_HE_PHY_CAP2_UL_MU_PARTIAL_MU_MIMO; + he_cap_elem->phy_cap_info[2] &= ~m; + + m = IEEE80211_HE_PHY_CAP3_RX_HE_MU_PPDU_FROM_NON_AP_STA | + IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_TX_MASK | + IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_RX_MASK; + he_cap_elem->phy_cap_info[3] &= ~m; + + m = IEEE80211_HE_PHY_CAP4_MU_BEAMFORMER; + he_cap_elem->phy_cap_info[4] &= ~m; + + m = IEEE80211_HE_PHY_CAP5_NG16_MU_FEEDBACK; + he_cap_elem->phy_cap_info[5] &= ~m; + + m = IEEE80211_HE_PHY_CAP6_CODEBOOK_SIZE_75_MU | + IEEE80211_HE_PHY_CAP6_TRIG_MU_BEAMFORMER_FB | + IEEE80211_HE_PHY_CAP6_TRIG_CQI_FB | + IEEE80211_HE_PHY_CAP6_PARTIAL_BANDWIDTH_DL_MUMIMO; + he_cap_elem->phy_cap_info[6] &= ~m; + + m = IEEE80211_HE_PHY_CAP7_SRP_BASED_SR | + IEEE80211_HE_PHY_CAP7_POWER_BOOST_FACTOR_AR | + IEEE80211_HE_PHY_CAP7_STBC_TX_ABOVE_80MHZ | + IEEE80211_HE_PHY_CAP7_STBC_RX_ABOVE_80MHZ; + he_cap_elem->phy_cap_info[7] &= ~m; + + m = IEEE80211_HE_PHY_CAP8_HE_ER_SU_PPDU_4XLTF_AND_08_US_GI | + IEEE80211_HE_PHY_CAP8_20MHZ_IN_40MHZ_HE_PPDU_IN_2G | + IEEE80211_HE_PHY_CAP8_20MHZ_IN_160MHZ_HE_PPDU | + IEEE80211_HE_PHY_CAP8_80MHZ_IN_160MHZ_HE_PPDU; + he_cap_elem->phy_cap_info[8] &= ~m; + + m = IEEE80211_HE_PHY_CAP9_LONGER_THAN_16_SIGB_OFDM_SYM | + IEEE80211_HE_PHY_CAP9_NON_TRIGGERED_CQI_FEEDBACK | + IEEE80211_HE_PHY_CAP9_RX_1024_QAM_LESS_THAN_242_TONE_RU | + IEEE80211_HE_PHY_CAP9_TX_1024_QAM_LESS_THAN_242_TONE_RU | + IEEE80211_HE_PHY_CAP9_RX_FULL_BW_SU_USING_MU_WITH_COMP_SIGB | + IEEE80211_HE_PHY_CAP9_RX_FULL_BW_SU_USING_MU_WITH_NON_COMP_SIGB; + he_cap_elem->phy_cap_info[9] &= ~m; +} + static int ath11k_mac_copy_he_cap(struct ath11k *ar, struct ath11k_pdev_cap *cap, struct ieee80211_sband_iftype_data *data, @@ -3362,6 +3433,7 @@ static int ath11k_mac_copy_he_cap(struct ath11k *ar, switch (i) { case NL80211_IFTYPE_STATION: case NL80211_IFTYPE_AP: + case NL80211_IFTYPE_MESH_POINT: break; default: @@ -3402,6 +3474,9 @@ static int ath11k_mac_copy_he_cap(struct ath11k *ar, he_cap_elem->phy_cap_info[9] |= IEEE80211_HE_PHY_CAP9_TX_1024_QAM_LESS_THAN_242_TONE_RU; break; + case NL80211_IFTYPE_MESH_POINT: + ath11k_mac_filter_he_cap_mesh(he_cap_elem); + break; } he_cap->he_mcs_nss_supp.rx_mcs_80 = -- GitLab From a67bcec3569f12da7c99891fa68f227791630fa1 Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Tue, 10 Dec 2019 12:08:00 +0200 Subject: [PATCH 0818/1437] ath10k: Fix some typo in some warning messages Fix some typo: s/to to/to/ s/even/event/ Signed-off-by: Christophe JAILLET Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/mac.c | 2 +- drivers/net/wireless/ath/ath10k/testmode.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index 767c7bf169758..0969e9bf5d5e5 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -1098,7 +1098,7 @@ static int ath10k_monitor_vdev_stop(struct ath10k *ar) ret = ath10k_wmi_vdev_stop(ar, ar->monitor_vdev_id); if (ret) - ath10k_warn(ar, "failed to to request monitor vdev %i stop: %d\n", + ath10k_warn(ar, "failed to request monitor vdev %i stop: %d\n", ar->monitor_vdev_id, ret); ret = ath10k_vdev_setup_sync(ar); diff --git a/drivers/net/wireless/ath/ath10k/testmode.c b/drivers/net/wireless/ath/ath10k/testmode.c index 1bffe3fbea3f3..7a9b9bbcdbfc5 100644 --- a/drivers/net/wireless/ath/ath10k/testmode.c +++ b/drivers/net/wireless/ath/ath10k/testmode.c @@ -65,7 +65,7 @@ bool ath10k_tm_event_wmi(struct ath10k *ar, u32 cmd_id, struct sk_buff *skb) ret = nla_put_u32(nl_skb, ATH10K_TM_ATTR_CMD, ATH10K_TM_CMD_WMI); if (ret) { ath10k_warn(ar, - "failed to to put testmode wmi event cmd attribute: %d\n", + "failed to put testmode wmi event cmd attribute: %d\n", ret); kfree_skb(nl_skb); goto out; @@ -74,7 +74,7 @@ bool ath10k_tm_event_wmi(struct ath10k *ar, u32 cmd_id, struct sk_buff *skb) ret = nla_put_u32(nl_skb, ATH10K_TM_ATTR_WMI_CMDID, cmd_id); if (ret) { ath10k_warn(ar, - "failed to to put testmode wmi even cmd_id: %d\n", + "failed to put testmode wmi event cmd_id: %d\n", ret); kfree_skb(nl_skb); goto out; -- GitLab From e4de2a5d51f97a6e720a1c0911f93e2d8c2f1c08 Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Tue, 17 Dec 2019 13:55:24 +0200 Subject: [PATCH 0819/1437] intel_th: pci: Add Comet Lake PCH-V support This adds Intel(R) Trace Hub PCI ID for Comet Lake PCH-V. Signed-off-by: Alexander Shishkin Reviewed-by: Andy Shevchenko Cc: Link: https://lore.kernel.org/r/20191217115527.74383-2-alexander.shishkin@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/intel_th/pci.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/hwtracing/intel_th/pci.c b/drivers/hwtracing/intel_th/pci.c index ebf3e30e989af..4b2f37578da3b 100644 --- a/drivers/hwtracing/intel_th/pci.c +++ b/drivers/hwtracing/intel_th/pci.c @@ -204,6 +204,11 @@ static const struct pci_device_id intel_th_pci_id_table[] = { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x06a6), .driver_data = (kernel_ulong_t)&intel_th_2x, }, + { + /* Comet Lake PCH-V */ + PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0xa3a6), + .driver_data = (kernel_ulong_t)&intel_th_2x, + }, { /* Ice Lake NNPI */ PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x45c5), -- GitLab From 88385866bab8d5e18c7f45d1023052c783572e03 Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Tue, 17 Dec 2019 13:55:25 +0200 Subject: [PATCH 0820/1437] intel_th: pci: Add Elkhart Lake SOC support This adds support for Intel Trace Hub in Elkhart Lake. Signed-off-by: Alexander Shishkin Reviewed-by: Andy Shevchenko Cc: stable@vger.kernel.org Link: https://lore.kernel.org/r/20191217115527.74383-3-alexander.shishkin@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/intel_th/pci.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/hwtracing/intel_th/pci.c b/drivers/hwtracing/intel_th/pci.c index 4b2f37578da3b..e9d90b53bbc46 100644 --- a/drivers/hwtracing/intel_th/pci.c +++ b/drivers/hwtracing/intel_th/pci.c @@ -234,6 +234,11 @@ static const struct pci_device_id intel_th_pci_id_table[] = { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x4da6), .driver_data = (kernel_ulong_t)&intel_th_2x, }, + { + /* Elkhart Lake */ + PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x4b26), + .driver_data = (kernel_ulong_t)&intel_th_2x, + }, { 0 }, }; -- GitLab From ab832e38e4f0f45b16c3633714d868b7ec6b33b4 Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Tue, 17 Dec 2019 13:55:26 +0200 Subject: [PATCH 0821/1437] intel_th: Fix freeing IRQs Commit aac8da65174a ("intel_th: msu: Start handling IRQs") implicitly relies on the use of devm_request_irq() to subsequently free the irqs on device removal, but in case of the pci_free_irq_vectors() API, the handlers need to be freed before it is called. Therefore, at the moment the driver's remove path trips a BUG_ON(irq_has_action()): > kernel BUG at drivers/pci/msi.c:375! > invalid opcode: 0000 1 SMP > CPU: 2 PID: 818 Comm: rmmod Not tainted 5.5.0-rc1+ #1 > RIP: 0010:free_msi_irqs+0x67/0x1c0 > pci_disable_msi+0x116/0x150 > pci_free_irq_vectors+0x1b/0x20 > intel_th_pci_remove+0x22/0x30 [intel_th_pci] > pci_device_remove+0x3e/0xb0 > device_release_driver_internal+0xf0/0x1c0 > driver_detach+0x4c/0x8f > bus_remove_driver+0x5c/0xd0 > driver_unregister+0x31/0x50 > pci_unregister_driver+0x40/0x90 > intel_th_pci_driver_exit+0x10/0xad6 [intel_th_pci] > __x64_sys_delete_module+0x147/0x290 > ? exit_to_usermode_loop+0xd7/0x120 > do_syscall_64+0x57/0x1b0 > entry_SYSCALL_64_after_hwframe+0x44/0xa9 Fix this by explicitly freeing irqs before freeing the vectors. We keep using the devm_* variants because they are still useful in early error paths. Signed-off-by: Alexander Shishkin Reviewed-by: Andy Shevchenko Fixes: aac8da65174a ("intel_th: msu: Start handling IRQs") Reported-by: Ammy Yi Tested-by: Ammy Yi Cc: stable@vger.kernel.org # v5.2+ Link: https://lore.kernel.org/r/20191217115527.74383-4-alexander.shishkin@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/intel_th/core.c | 7 ++++--- drivers/hwtracing/intel_th/intel_th.h | 2 ++ 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/hwtracing/intel_th/core.c b/drivers/hwtracing/intel_th/core.c index 0dfd97bbde9ee..ca232ec565e83 100644 --- a/drivers/hwtracing/intel_th/core.c +++ b/drivers/hwtracing/intel_th/core.c @@ -834,9 +834,6 @@ static irqreturn_t intel_th_irq(int irq, void *data) ret |= d->irq(th->thdev[i]); } - if (ret == IRQ_NONE) - pr_warn_ratelimited("nobody cared for irq\n"); - return ret; } @@ -887,6 +884,7 @@ intel_th_alloc(struct device *dev, struct intel_th_drvdata *drvdata, if (th->irq == -1) th->irq = devres[r].start; + th->num_irqs++; break; default: dev_warn(dev, "Unknown resource type %lx\n", @@ -940,6 +938,9 @@ void intel_th_free(struct intel_th *th) th->num_thdevs = 0; + for (i = 0; i < th->num_irqs; i++) + devm_free_irq(th->dev, th->irq + i, th); + pm_runtime_get_sync(th->dev); pm_runtime_forbid(th->dev); diff --git a/drivers/hwtracing/intel_th/intel_th.h b/drivers/hwtracing/intel_th/intel_th.h index 0df480072b6ca..6f4f5486fe6dc 100644 --- a/drivers/hwtracing/intel_th/intel_th.h +++ b/drivers/hwtracing/intel_th/intel_th.h @@ -261,6 +261,7 @@ enum th_mmio_idx { * @num_thdevs: number of devices in the @thdev array * @num_resources: number of resources in the @resource array * @irq: irq number + * @num_irqs: number of IRQs is use * @id: this Intel TH controller's device ID in the system * @major: device node major for output devices */ @@ -277,6 +278,7 @@ struct intel_th { unsigned int num_thdevs; unsigned int num_resources; int irq; + int num_irqs; int id; int major; -- GitLab From 05b686b573cfb35a227c30787083a6631ff0f0c9 Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Tue, 17 Dec 2019 13:55:27 +0200 Subject: [PATCH 0822/1437] intel_th: msu: Fix window switching without windows Commit 6cac7866c2741 ("intel_th: msu: Add a sysfs attribute to trigger window switch") adds a NULL pointer dereference in the case when there are no windows allocated: > BUG: kernel NULL pointer dereference, address: 0000000000000000 > #PF: supervisor read access in kernel mode > #PF: error_code(0x0000) - not-present page > PGD 0 P4D 0 > Oops: 0000 1 SMP > CPU: 5 PID: 1110 Comm: bash Not tainted 5.5.0-rc1+ #1 > RIP: 0010:msc_win_switch+0xa/0x80 [intel_th_msu] > Call Trace: > ? win_switch_store+0x9b/0xc0 [intel_th_msu] > dev_attr_store+0x17/0x30 > sysfs_kf_write+0x3e/0x50 > kernfs_fop_write+0xda/0x1b0 > __vfs_write+0x1b/0x40 > vfs_write+0xb9/0x1a0 > ksys_write+0x67/0xe0 > __x64_sys_write+0x1a/0x20 > do_syscall_64+0x57/0x1d0 > entry_SYSCALL_64_after_hwframe+0x44/0xa9 Fix that by disallowing window switching with multiwindow buffers without windows. Signed-off-by: Alexander Shishkin Fixes: 6cac7866c274 ("intel_th: msu: Add a sysfs attribute to trigger window switch") Reviewed-by: Andy Shevchenko Reported-by: Ammy Yi Tested-by: Ammy Yi Cc: stable@vger.kernel.org # v5.2+ Link: https://lore.kernel.org/r/20191217115527.74383-5-alexander.shishkin@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/intel_th/msu.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/drivers/hwtracing/intel_th/msu.c b/drivers/hwtracing/intel_th/msu.c index 6d240dfae9d90..8e48c7458aa35 100644 --- a/drivers/hwtracing/intel_th/msu.c +++ b/drivers/hwtracing/intel_th/msu.c @@ -1676,10 +1676,13 @@ static int intel_th_msc_init(struct msc *msc) return 0; } -static void msc_win_switch(struct msc *msc) +static int msc_win_switch(struct msc *msc) { struct msc_window *first; + if (list_empty(&msc->win_list)) + return -EINVAL; + first = list_first_entry(&msc->win_list, struct msc_window, entry); if (msc_is_last_win(msc->cur_win)) @@ -1691,6 +1694,8 @@ static void msc_win_switch(struct msc *msc) msc->base_addr = msc_win_base_dma(msc->cur_win); intel_th_trace_switch(msc->thdev); + + return 0; } /** @@ -2025,16 +2030,15 @@ win_switch_store(struct device *dev, struct device_attribute *attr, if (val != 1) return -EINVAL; + ret = -EINVAL; mutex_lock(&msc->buf_mutex); /* * Window switch can only happen in the "multi" mode. * If a external buffer is engaged, they have the full * control over window switching. */ - if (msc->mode != MSC_MODE_MULTI || msc->mbuf) - ret = -ENOTSUPP; - else - msc_win_switch(msc); + if (msc->mode == MSC_MODE_MULTI && !msc->mbuf) + ret = msc_win_switch(msc); mutex_unlock(&msc->buf_mutex); return ret ? ret : size; -- GitLab From 547fc228755d79af648898187e7831a825d4f42c Mon Sep 17 00:00:00 2001 From: zhong jiang Date: Fri, 13 Dec 2019 20:16:18 +0800 Subject: [PATCH 0823/1437] usb: typec: fusb302: Fix an undefined reference to 'extcon_get_state' Fixes the following compile error: drivers/usb/typec/tcpm/fusb302.o: In function `tcpm_get_current_limit': fusb302.c:(.text+0x3ee): undefined reference to `extcon_get_state' fusb302.c:(.text+0x422): undefined reference to `extcon_get_state' fusb302.c:(.text+0x450): undefined reference to `extcon_get_state' fusb302.c:(.text+0x48c): undefined reference to `extcon_get_state' drivers/usb/typec/tcpm/fusb302.o: In function `fusb302_probe': fusb302.c:(.text+0x980): undefined reference to `extcon_get_extcon_dev' make: *** [vmlinux] Error 1 It is because EXTCON is build as a module, but FUSB302 is not. Suggested-by: Heikki Krogerus Signed-off-by: zhong jiang Acked-by: Heikki Krogerus Reviewed-by: Guenter Roeck Link: https://lore.kernel.org/r/1576239378-50795-1-git-send-email-zhongjiang@huawei.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/tcpm/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/usb/typec/tcpm/Kconfig b/drivers/usb/typec/tcpm/Kconfig index 72481bbb2af39..5b986d6c801d9 100644 --- a/drivers/usb/typec/tcpm/Kconfig +++ b/drivers/usb/typec/tcpm/Kconfig @@ -32,6 +32,7 @@ endif # TYPEC_TCPCI config TYPEC_FUSB302 tristate "Fairchild FUSB302 Type-C chip driver" depends on I2C + depends on EXTCON || !EXTCON help The Fairchild FUSB302 Type-C chip driver that works with Type-C Port Controller Manager to provide USB PD and USB -- GitLab From 64cc3f12d1c7dd054a215bc1ff9cc2abcfe35832 Mon Sep 17 00:00:00 2001 From: Erkka Talvitie Date: Wed, 11 Dec 2019 10:08:39 +0200 Subject: [PATCH 0824/1437] USB: EHCI: Do not return -EPIPE when hub is disconnected When disconnecting a USB hub that has some child device(s) connected to it (such as a USB mouse), then the stack tries to clear halt and reset device(s) which are _already_ physically disconnected. The issue has been reproduced with: CPU: IMX6D5EYM10AD or MCIMX6D5EYM10AE. SW: U-Boot 2019.07 and kernel 4.19.40. CPU: HP Proliant Microserver Gen8. SW: Linux version 4.2.3-300.fc23.x86_64 In this situation there will be error bit for MMF active yet the CERR equals EHCI_TUNE_CERR + halt. Existing implementation interprets this as a stall [1] (chapter 8.4.5). The possible conditions when the MMF will be active + halt can be found from [2] (Table 4-13). Fix for the issue is to check whether MMF is active and PID Code is IN before checking for the stall. If these conditions are true then it is not a stall. What happens after the fix is that when disconnecting a hub with attached device(s) the situation is not interpret as a stall. [1] [https://www.usb.org/document-library/usb-20-specification, usb_20.pdf] [2] [https://www.intel.com/content/dam/www/public/us/en/documents/ technical-specifications/ehci-specification-for-usb.pdf] Signed-off-by: Erkka Talvitie Reviewed-by: Alan Stern Cc: stable Link: https://lore.kernel.org/r/ef70941d5f349767f19c0ed26b0dd9eed8ad81bb.1576050523.git.erkka.talvitie@vincit.fi Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-q.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c index aa2f77f1506dd..8a5c9b3ebe1e7 100644 --- a/drivers/usb/host/ehci-q.c +++ b/drivers/usb/host/ehci-q.c @@ -27,6 +27,10 @@ /*-------------------------------------------------------------------------*/ +/* PID Codes that are used here, from EHCI specification, Table 3-16. */ +#define PID_CODE_IN 1 +#define PID_CODE_SETUP 2 + /* fill a qtd, returning how much of the buffer we were able to queue up */ static int @@ -190,7 +194,7 @@ static int qtd_copy_status ( int status = -EINPROGRESS; /* count IN/OUT bytes, not SETUP (even short packets) */ - if (likely (QTD_PID (token) != 2)) + if (likely(QTD_PID(token) != PID_CODE_SETUP)) urb->actual_length += length - QTD_LENGTH (token); /* don't modify error codes */ @@ -206,6 +210,13 @@ static int qtd_copy_status ( if (token & QTD_STS_BABBLE) { /* FIXME "must" disable babbling device's port too */ status = -EOVERFLOW; + /* + * When MMF is active and PID Code is IN, queue is halted. + * EHCI Specification, Table 4-13. + */ + } else if ((token & QTD_STS_MMF) && + (QTD_PID(token) == PID_CODE_IN)) { + status = -EPROTO; /* CERR nonzero + halt --> stall */ } else if (QTD_CERR(token)) { status = -EPIPE; -- GitLab From d986294ee55d719562b20aabe15a39bf8f863415 Mon Sep 17 00:00:00 2001 From: Suwan Kim Date: Fri, 13 Dec 2019 11:30:54 +0900 Subject: [PATCH 0825/1437] usbip: Fix receive error in vhci-hcd when using scatter-gather MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When vhci uses SG and receives data whose size is smaller than SG buffer size, it tries to receive more data even if it acutally receives all the data from the server. If then, it erroneously adds error event and triggers connection shutdown. vhci-hcd should check if it received all the data even if there are more SG entries left. So, check if it receivces all the data from the server in for_each_sg() loop. Fixes: ea44d190764b ("usbip: Implement SG support to vhci-hcd and stub driver") Reported-by: Marek Marczykowski-Górecki Tested-by: Marek Marczykowski-Górecki Signed-off-by: Suwan Kim Acked-by: Shuah Khan Cc: stable Link: https://lore.kernel.org/r/20191213023055.19933-2-suwan.kim027@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/usbip/usbip_common.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/usb/usbip/usbip_common.c b/drivers/usb/usbip/usbip_common.c index 6532d68e8808d..e4b96674c4052 100644 --- a/drivers/usb/usbip/usbip_common.c +++ b/drivers/usb/usbip/usbip_common.c @@ -727,6 +727,9 @@ int usbip_recv_xbuff(struct usbip_device *ud, struct urb *urb) copy -= recv; ret += recv; + + if (!copy) + break; } if (ret != size) -- GitLab From aabb5b833872524eaf28f52187e5987984982264 Mon Sep 17 00:00:00 2001 From: Suwan Kim Date: Fri, 13 Dec 2019 11:30:55 +0900 Subject: [PATCH 0826/1437] usbip: Fix error path of vhci_recv_ret_submit() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If a transaction error happens in vhci_recv_ret_submit(), event handler closes connection and changes port status to kick hub_event. Then hub tries to flush the endpoint URBs, but that causes infinite loop between usb_hub_flush_endpoint() and vhci_urb_dequeue() because "vhci_priv" in vhci_urb_dequeue() was already released by vhci_recv_ret_submit() before a transmission error occurred. Thus, vhci_urb_dequeue() terminates early and usb_hub_flush_endpoint() continuously calls vhci_urb_dequeue(). The root cause of this issue is that vhci_recv_ret_submit() terminates early without giving back URB when transaction error occurs in vhci_recv_ret_submit(). That causes the error URB to still be linked at endpoint list without “vhci_priv". So, in the case of transaction error in vhci_recv_ret_submit(), unlink URB from the endpoint, insert proper error code in urb->status and give back URB. Reported-by: Marek Marczykowski-Górecki Tested-by: Marek Marczykowski-Górecki Signed-off-by: Suwan Kim Cc: stable Acked-by: Shuah Khan Link: https://lore.kernel.org/r/20191213023055.19933-3-suwan.kim027@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/usbip/vhci_rx.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/drivers/usb/usbip/vhci_rx.c b/drivers/usb/usbip/vhci_rx.c index 33f8972ba842f..00fc98741c5d1 100644 --- a/drivers/usb/usbip/vhci_rx.c +++ b/drivers/usb/usbip/vhci_rx.c @@ -77,16 +77,21 @@ static void vhci_recv_ret_submit(struct vhci_device *vdev, usbip_pack_pdu(pdu, urb, USBIP_RET_SUBMIT, 0); /* recv transfer buffer */ - if (usbip_recv_xbuff(ud, urb) < 0) - return; + if (usbip_recv_xbuff(ud, urb) < 0) { + urb->status = -EPROTO; + goto error; + } /* recv iso_packet_descriptor */ - if (usbip_recv_iso(ud, urb) < 0) - return; + if (usbip_recv_iso(ud, urb) < 0) { + urb->status = -EPROTO; + goto error; + } /* restore the padding in iso packets */ usbip_pad_iso(ud, urb); +error: if (usbip_dbg_flag_vhci_rx) usbip_dump_urb(urb); -- GitLab From c58c1f83436b501d45d4050fd1296d71a9760bcb Mon Sep 17 00:00:00 2001 From: Roman Penyaev Date: Tue, 17 Dec 2019 16:54:07 +0100 Subject: [PATCH 0827/1437] block: end bio with BLK_STS_AGAIN in case of non-mq devs and REQ_NOWAIT Non-mq devs do not honor REQ_NOWAIT so give a chance to the caller to repeat request gracefully on -EAGAIN error. The problem is well reproduced using io_uring: mkfs.ext4 /dev/ram0 mount /dev/ram0 /mnt # Preallocate a file dd if=/dev/zero of=/mnt/file bs=1M count=1 # Start fio with io_uring and get -EIO fio --rw=write --ioengine=io_uring --size=1M --direct=1 --name=job --filename=/mnt/file Signed-off-by: Roman Penyaev Signed-off-by: Jens Axboe --- block/blk-core.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/block/blk-core.c b/block/blk-core.c index e0a094fddee5f..089e890ab208f 100644 --- a/block/blk-core.c +++ b/block/blk-core.c @@ -885,11 +885,14 @@ generic_make_request_checks(struct bio *bio) } /* - * For a REQ_NOWAIT based request, return -EOPNOTSUPP - * if queue is not a request based queue. + * Non-mq queues do not honor REQ_NOWAIT, so complete a bio + * with BLK_STS_AGAIN status in order to catch -EAGAIN and + * to give a chance to the caller to repeat request gracefully. */ - if ((bio->bi_opf & REQ_NOWAIT) && !queue_is_mq(q)) - goto not_supported; + if ((bio->bi_opf & REQ_NOWAIT) && !queue_is_mq(q)) { + status = BLK_STS_AGAIN; + goto end_io; + } if (should_fail_bio(bio)) goto end_io; -- GitLab From e47304232b373362228bf233f17bd12b11c9aafc Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Tue, 17 Dec 2019 13:28:16 +0100 Subject: [PATCH 0828/1437] bpf: Fix cgroup local storage prog tracking Recently noticed that we're tracking programs related to local storage maps through their prog pointer. This is a wrong assumption since the prog pointer can still change throughout the verification process, for example, whenever bpf_patch_insn_single() is called. Therefore, the prog pointer that was assigned via bpf_cgroup_storage_assign() is not guaranteed to be the same as we pass in bpf_cgroup_storage_release() and the map would therefore remain in busy state forever. Fix this by using the prog's aux pointer which is stable throughout verification and beyond. Fixes: de9cbbaadba5 ("bpf: introduce cgroup storage maps") Signed-off-by: Daniel Borkmann Signed-off-by: Alexei Starovoitov Cc: Roman Gushchin Cc: Martin KaFai Lau Link: https://lore.kernel.org/bpf/1471c69eca3022218666f909bc927a92388fd09e.1576580332.git.daniel@iogearbox.net --- include/linux/bpf-cgroup.h | 8 ++++---- kernel/bpf/core.c | 3 +-- kernel/bpf/local_storage.c | 24 ++++++++++++------------ kernel/bpf/verifier.c | 2 +- 4 files changed, 18 insertions(+), 19 deletions(-) diff --git a/include/linux/bpf-cgroup.h b/include/linux/bpf-cgroup.h index 169fd25f6bc2d..9be71c195d745 100644 --- a/include/linux/bpf-cgroup.h +++ b/include/linux/bpf-cgroup.h @@ -157,8 +157,8 @@ void bpf_cgroup_storage_link(struct bpf_cgroup_storage *storage, struct cgroup *cgroup, enum bpf_attach_type type); void bpf_cgroup_storage_unlink(struct bpf_cgroup_storage *storage); -int bpf_cgroup_storage_assign(struct bpf_prog *prog, struct bpf_map *map); -void bpf_cgroup_storage_release(struct bpf_prog *prog, struct bpf_map *map); +int bpf_cgroup_storage_assign(struct bpf_prog_aux *aux, struct bpf_map *map); +void bpf_cgroup_storage_release(struct bpf_prog_aux *aux, struct bpf_map *map); int bpf_percpu_cgroup_storage_copy(struct bpf_map *map, void *key, void *value); int bpf_percpu_cgroup_storage_update(struct bpf_map *map, void *key, @@ -360,9 +360,9 @@ static inline int cgroup_bpf_prog_query(const union bpf_attr *attr, static inline void bpf_cgroup_storage_set( struct bpf_cgroup_storage *storage[MAX_BPF_CGROUP_STORAGE_TYPE]) {} -static inline int bpf_cgroup_storage_assign(struct bpf_prog *prog, +static inline int bpf_cgroup_storage_assign(struct bpf_prog_aux *aux, struct bpf_map *map) { return 0; } -static inline void bpf_cgroup_storage_release(struct bpf_prog *prog, +static inline void bpf_cgroup_storage_release(struct bpf_prog_aux *aux, struct bpf_map *map) {} static inline struct bpf_cgroup_storage *bpf_cgroup_storage_alloc( struct bpf_prog *prog, enum bpf_cgroup_storage_type stype) { return NULL; } diff --git a/kernel/bpf/core.c b/kernel/bpf/core.c index 6231858df7239..af6b738cf435c 100644 --- a/kernel/bpf/core.c +++ b/kernel/bpf/core.c @@ -2043,8 +2043,7 @@ static void bpf_free_cgroup_storage(struct bpf_prog_aux *aux) for_each_cgroup_storage_type(stype) { if (!aux->cgroup_storage[stype]) continue; - bpf_cgroup_storage_release(aux->prog, - aux->cgroup_storage[stype]); + bpf_cgroup_storage_release(aux, aux->cgroup_storage[stype]); } } diff --git a/kernel/bpf/local_storage.c b/kernel/bpf/local_storage.c index 2ba750725cb26..6bf605dd4b94f 100644 --- a/kernel/bpf/local_storage.c +++ b/kernel/bpf/local_storage.c @@ -20,7 +20,7 @@ struct bpf_cgroup_storage_map { struct bpf_map map; spinlock_t lock; - struct bpf_prog *prog; + struct bpf_prog_aux *aux; struct rb_root root; struct list_head list; }; @@ -420,7 +420,7 @@ const struct bpf_map_ops cgroup_storage_map_ops = { .map_seq_show_elem = cgroup_storage_seq_show_elem, }; -int bpf_cgroup_storage_assign(struct bpf_prog *prog, struct bpf_map *_map) +int bpf_cgroup_storage_assign(struct bpf_prog_aux *aux, struct bpf_map *_map) { enum bpf_cgroup_storage_type stype = cgroup_storage_type(_map); struct bpf_cgroup_storage_map *map = map_to_storage(_map); @@ -428,14 +428,14 @@ int bpf_cgroup_storage_assign(struct bpf_prog *prog, struct bpf_map *_map) spin_lock_bh(&map->lock); - if (map->prog && map->prog != prog) + if (map->aux && map->aux != aux) goto unlock; - if (prog->aux->cgroup_storage[stype] && - prog->aux->cgroup_storage[stype] != _map) + if (aux->cgroup_storage[stype] && + aux->cgroup_storage[stype] != _map) goto unlock; - map->prog = prog; - prog->aux->cgroup_storage[stype] = _map; + map->aux = aux; + aux->cgroup_storage[stype] = _map; ret = 0; unlock: spin_unlock_bh(&map->lock); @@ -443,16 +443,16 @@ unlock: return ret; } -void bpf_cgroup_storage_release(struct bpf_prog *prog, struct bpf_map *_map) +void bpf_cgroup_storage_release(struct bpf_prog_aux *aux, struct bpf_map *_map) { enum bpf_cgroup_storage_type stype = cgroup_storage_type(_map); struct bpf_cgroup_storage_map *map = map_to_storage(_map); spin_lock_bh(&map->lock); - if (map->prog == prog) { - WARN_ON(prog->aux->cgroup_storage[stype] != _map); - map->prog = NULL; - prog->aux->cgroup_storage[stype] = NULL; + if (map->aux == aux) { + WARN_ON(aux->cgroup_storage[stype] != _map); + map->aux = NULL; + aux->cgroup_storage[stype] = NULL; } spin_unlock_bh(&map->lock); } diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index a1acdce770709..6ef71429d997f 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -8268,7 +8268,7 @@ static int replace_map_fd_with_map_ptr(struct bpf_verifier_env *env) env->used_maps[env->used_map_cnt++] = map; if (bpf_map_is_cgroup_storage(map) && - bpf_cgroup_storage_assign(env->prog, map)) { + bpf_cgroup_storage_assign(env->prog->aux, map)) { verbose(env, "only one cgroup storage of each type is allowed\n"); fdput(f); return -EBUSY; -- GitLab From 4aa37c463764052c68c5c430af2a67b5d784c1e0 Mon Sep 17 00:00:00 2001 From: "Jason A. Donenfeld" Date: Tue, 17 Dec 2019 18:24:55 +0100 Subject: [PATCH 0829/1437] random: don't forget compat_ioctl on urandom Recently, there's been some compat ioctl cleanup, in which large hardcoded lists were replaced with compat_ptr_ioctl. One of these changes involved removing the random.c hardcoded list entries and adding a compat ioctl function pointer to the random.c fops. In the process, urandom was forgotten about, so this commit fixes that oversight. Fixes: 507e4e2b430b ("compat_ioctl: remove /dev/random commands") Cc: Arnd Bergmann Signed-off-by: Jason A. Donenfeld Link: https://lore.kernel.org/r/20191217172455.186395-1-Jason@zx2c4.com Signed-off-by: Greg Kroah-Hartman --- drivers/char/random.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/char/random.c b/drivers/char/random.c index 909e0c3d82ea4..cda12933a17da 100644 --- a/drivers/char/random.c +++ b/drivers/char/random.c @@ -2175,6 +2175,7 @@ const struct file_operations urandom_fops = { .read = urandom_read, .write = random_write, .unlocked_ioctl = random_ioctl, + .compat_ioctl = compat_ptr_ioctl, .fasync = random_fasync, .llseek = noop_llseek, }; -- GitLab From b1de6fc7520fe12949c070af0e8c0e4044cd3420 Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Wed, 11 Dec 2019 13:19:07 -0800 Subject: [PATCH 0830/1437] xfs: fix log reservation overflows when allocating large rt extents Omar Sandoval reported that a 4G fallocate on the realtime device causes filesystem shutdowns due to a log reservation overflow that happens when we log the rtbitmap updates. Factor rtbitmap/rtsummary updates into the the tr_write and tr_itruncate log reservation calculation. "The following reproducer results in a transaction log overrun warning for me: mkfs.xfs -f -r rtdev=/dev/vdc -d rtinherit=1 -m reflink=0 /dev/vdb mount -o rtdev=/dev/vdc /dev/vdb /mnt fallocate -l 4G /mnt/foo Reported-by: Omar Sandoval Tested-by: Omar Sandoval Signed-off-by: Darrick J. Wong Reviewed-by: Brian Foster --- fs/xfs/libxfs/xfs_trans_resv.c | 96 +++++++++++++++++++++++++++------- 1 file changed, 77 insertions(+), 19 deletions(-) diff --git a/fs/xfs/libxfs/xfs_trans_resv.c b/fs/xfs/libxfs/xfs_trans_resv.c index c55cd9a3dec94..824073a839acb 100644 --- a/fs/xfs/libxfs/xfs_trans_resv.c +++ b/fs/xfs/libxfs/xfs_trans_resv.c @@ -196,6 +196,24 @@ xfs_calc_inode_chunk_res( return res; } +/* + * Per-extent log reservation for the btree changes involved in freeing or + * allocating a realtime extent. We have to be able to log as many rtbitmap + * blocks as needed to mark inuse MAXEXTLEN blocks' worth of realtime extents, + * as well as the realtime summary block. + */ +unsigned int +xfs_rtalloc_log_count( + struct xfs_mount *mp, + unsigned int num_ops) +{ + unsigned int blksz = XFS_FSB_TO_B(mp, 1); + unsigned int rtbmp_bytes; + + rtbmp_bytes = (MAXEXTLEN / mp->m_sb.sb_rextsize) / NBBY; + return (howmany(rtbmp_bytes, blksz) + 1) * num_ops; +} + /* * Various log reservation values. * @@ -218,13 +236,21 @@ xfs_calc_inode_chunk_res( /* * In a write transaction we can allocate a maximum of 2 - * extents. This gives: + * extents. This gives (t1): * the inode getting the new extents: inode size * the inode's bmap btree: max depth * block size * the agfs of the ags from which the extents are allocated: 2 * sector * the superblock free block counter: sector size * the allocation btrees: 2 exts * 2 trees * (2 * max depth - 1) * block size - * And the bmap_finish transaction can free bmap blocks in a join: + * Or, if we're writing to a realtime file (t2): + * the inode getting the new extents: inode size + * the inode's bmap btree: max depth * block size + * the agfs of the ags from which the extents are allocated: 2 * sector + * the superblock free block counter: sector size + * the realtime bitmap: ((MAXEXTLEN / rtextsize) / NBBY) bytes + * the realtime summary: 1 block + * the allocation btrees: 2 trees * (2 * max depth - 1) * block size + * And the bmap_finish transaction can free bmap blocks in a join (t3): * the agfs of the ags containing the blocks: 2 * sector size * the agfls of the ags containing the blocks: 2 * sector size * the super block free block counter: sector size @@ -234,40 +260,72 @@ STATIC uint xfs_calc_write_reservation( struct xfs_mount *mp) { - return XFS_DQUOT_LOGRES(mp) + - max((xfs_calc_inode_res(mp, 1) + + unsigned int t1, t2, t3; + unsigned int blksz = XFS_FSB_TO_B(mp, 1); + + t1 = xfs_calc_inode_res(mp, 1) + + xfs_calc_buf_res(XFS_BM_MAXLEVELS(mp, XFS_DATA_FORK), blksz) + + xfs_calc_buf_res(3, mp->m_sb.sb_sectsize) + + xfs_calc_buf_res(xfs_allocfree_log_count(mp, 2), blksz); + + if (xfs_sb_version_hasrealtime(&mp->m_sb)) { + t2 = xfs_calc_inode_res(mp, 1) + xfs_calc_buf_res(XFS_BM_MAXLEVELS(mp, XFS_DATA_FORK), - XFS_FSB_TO_B(mp, 1)) + + blksz) + xfs_calc_buf_res(3, mp->m_sb.sb_sectsize) + - xfs_calc_buf_res(xfs_allocfree_log_count(mp, 2), - XFS_FSB_TO_B(mp, 1))), - (xfs_calc_buf_res(5, mp->m_sb.sb_sectsize) + - xfs_calc_buf_res(xfs_allocfree_log_count(mp, 2), - XFS_FSB_TO_B(mp, 1)))); + xfs_calc_buf_res(xfs_rtalloc_log_count(mp, 1), blksz) + + xfs_calc_buf_res(xfs_allocfree_log_count(mp, 1), blksz); + } else { + t2 = 0; + } + + t3 = xfs_calc_buf_res(5, mp->m_sb.sb_sectsize) + + xfs_calc_buf_res(xfs_allocfree_log_count(mp, 2), blksz); + + return XFS_DQUOT_LOGRES(mp) + max3(t1, t2, t3); } /* - * In truncating a file we free up to two extents at once. We can modify: + * In truncating a file we free up to two extents at once. We can modify (t1): * the inode being truncated: inode size * the inode's bmap btree: (max depth + 1) * block size - * And the bmap_finish transaction can free the blocks and bmap blocks: + * And the bmap_finish transaction can free the blocks and bmap blocks (t2): * the agf for each of the ags: 4 * sector size * the agfl for each of the ags: 4 * sector size * the super block to reflect the freed blocks: sector size * worst case split in allocation btrees per extent assuming 4 extents: * 4 exts * 2 trees * (2 * max depth - 1) * block size + * Or, if it's a realtime file (t3): + * the agf for each of the ags: 2 * sector size + * the agfl for each of the ags: 2 * sector size + * the super block to reflect the freed blocks: sector size + * the realtime bitmap: 2 exts * ((MAXEXTLEN / rtextsize) / NBBY) bytes + * the realtime summary: 2 exts * 1 block + * worst case split in allocation btrees per extent assuming 2 extents: + * 2 exts * 2 trees * (2 * max depth - 1) * block size */ STATIC uint xfs_calc_itruncate_reservation( struct xfs_mount *mp) { - return XFS_DQUOT_LOGRES(mp) + - max((xfs_calc_inode_res(mp, 1) + - xfs_calc_buf_res(XFS_BM_MAXLEVELS(mp, XFS_DATA_FORK) + 1, - XFS_FSB_TO_B(mp, 1))), - (xfs_calc_buf_res(9, mp->m_sb.sb_sectsize) + - xfs_calc_buf_res(xfs_allocfree_log_count(mp, 4), - XFS_FSB_TO_B(mp, 1)))); + unsigned int t1, t2, t3; + unsigned int blksz = XFS_FSB_TO_B(mp, 1); + + t1 = xfs_calc_inode_res(mp, 1) + + xfs_calc_buf_res(XFS_BM_MAXLEVELS(mp, XFS_DATA_FORK) + 1, blksz); + + t2 = xfs_calc_buf_res(9, mp->m_sb.sb_sectsize) + + xfs_calc_buf_res(xfs_allocfree_log_count(mp, 4), blksz); + + if (xfs_sb_version_hasrealtime(&mp->m_sb)) { + t3 = xfs_calc_buf_res(5, mp->m_sb.sb_sectsize) + + xfs_calc_buf_res(xfs_rtalloc_log_count(mp, 2), blksz) + + xfs_calc_buf_res(xfs_allocfree_log_count(mp, 2), blksz); + } else { + t3 = 0; + } + + return XFS_DQUOT_LOGRES(mp) + max3(t1, t2, t3); } /* -- GitLab From 2d3145f8d2809592ef803a30c8a342b5a9e2de9a Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Tue, 17 Dec 2019 13:10:11 -0800 Subject: [PATCH 0831/1437] early init: fix error handling when opening /dev/console The comment says "this should never fail", but it definitely can fail when you have odd initial boot filesystems, or kernel configurations. So get the error handling right: filp_open() returns an error pointer. Reported-by: Jesse Barnes Reported-by: youling 257 Fixes: 8243186f0cc7 ("fs: remove ksys_dup()") Cc: Dominik Brodowski Signed-off-by: Linus Torvalds --- init/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/init/main.c b/init/main.c index ec3a1463ac692..1ecfd43ed4643 100644 --- a/init/main.c +++ b/init/main.c @@ -1163,7 +1163,7 @@ void console_on_rootfs(void) /* Open /dev/console in kernelspace, this should never fail */ file = filp_open("/dev/console", O_RDWR, 0); - if (!file) + if (IS_ERR(file)) goto err_out; /* create stdin/stdout/stderr, this should never fail */ -- GitLab From 5d57c327584ae0f88eba61ec8faa9c621f40cffd Mon Sep 17 00:00:00 2001 From: Russell King Date: Fri, 13 Dec 2019 18:22:02 +0000 Subject: [PATCH 0832/1437] net: phylink: improve clause 45 PHY ksettings_set implementation While testing ethtool with the Methode DM7052 module, it was noticed that attempting to set the advertising mask results in the mask being truncated to the support offered by the currently chosen PHY interface mode. When a PHY dynamically changes the PHY interface mode, limiting the advertising mask in this way is not correct - if the PHY happened to negotiate 10GBASE-T, and selected 10GBASE-R as the host interface, we don't want to restrict the advertisement to just 10GBASE-* modes. Rework setting the advertisement to take account of this; do not pass the requested advertisement through phylink_validate(), but rely on the advertisement restriction (supported mask) set when the PHY was initially setup. Signed-off-by: Russell King Signed-off-by: David S. Miller --- drivers/net/phy/phylink.c | 84 ++++++++++++++++++++++++--------------- 1 file changed, 53 insertions(+), 31 deletions(-) diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c index 1e0e32c466ee4..8f3dc27db95d3 100644 --- a/drivers/net/phy/phylink.c +++ b/drivers/net/phy/phylink.c @@ -1230,44 +1230,66 @@ int phylink_ethtool_ksettings_set(struct phylink *pl, __set_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, config.advertising); } - if (phylink_validate(pl, support, &config)) - return -EINVAL; - - /* If autonegotiation is enabled, we must have an advertisement */ - if (config.an_enabled && phylink_is_empty_linkmode(config.advertising)) - return -EINVAL; - - our_kset = *kset; - linkmode_copy(our_kset.link_modes.advertising, config.advertising); - our_kset.base.speed = config.speed; - our_kset.base.duplex = config.duplex; - - /* If we have a PHY, configure the phy */ if (pl->phydev) { + /* If we have a PHY, we process the kset change via phylib. + * phylib will call our link state function if the PHY + * parameters have changed, which will trigger a resolve + * and update the MAC configuration. + */ + our_kset = *kset; + linkmode_copy(our_kset.link_modes.advertising, + config.advertising); + our_kset.base.speed = config.speed; + our_kset.base.duplex = config.duplex; + ret = phy_ethtool_ksettings_set(pl->phydev, &our_kset); if (ret) return ret; - } - mutex_lock(&pl->state_mutex); - /* Configure the MAC to match the new settings */ - linkmode_copy(pl->link_config.advertising, our_kset.link_modes.advertising); - pl->link_config.interface = config.interface; - pl->link_config.speed = our_kset.base.speed; - pl->link_config.duplex = our_kset.base.duplex; - pl->link_config.an_enabled = our_kset.base.autoneg != AUTONEG_DISABLE; + mutex_lock(&pl->state_mutex); + /* Save the new configuration */ + linkmode_copy(pl->link_config.advertising, + our_kset.link_modes.advertising); + pl->link_config.interface = config.interface; + pl->link_config.speed = our_kset.base.speed; + pl->link_config.duplex = our_kset.base.duplex; + pl->link_config.an_enabled = our_kset.base.autoneg != + AUTONEG_DISABLE; + mutex_unlock(&pl->state_mutex); + } else { + /* For a fixed link, this isn't able to change any parameters, + * which just leaves inband mode. + */ + if (phylink_validate(pl, support, &config)) + return -EINVAL; - /* If we have a PHY, phylib will call our link state function if the - * mode has changed, which will trigger a resolve and update the MAC - * configuration. For a fixed link, this isn't able to change any - * parameters, which just leaves inband mode. - */ - if (pl->cur_link_an_mode == MLO_AN_INBAND && - !test_bit(PHYLINK_DISABLE_STOPPED, &pl->phylink_disable_state)) { - phylink_mac_config(pl, &pl->link_config); - phylink_mac_an_restart(pl); + /* If autonegotiation is enabled, we must have an advertisement */ + if (config.an_enabled && + phylink_is_empty_linkmode(config.advertising)) + return -EINVAL; + + mutex_lock(&pl->state_mutex); + linkmode_copy(pl->link_config.advertising, config.advertising); + pl->link_config.interface = config.interface; + pl->link_config.speed = config.speed; + pl->link_config.duplex = config.duplex; + pl->link_config.an_enabled = kset->base.autoneg != + AUTONEG_DISABLE; + + if (pl->cur_link_an_mode == MLO_AN_INBAND && + !test_bit(PHYLINK_DISABLE_STOPPED, + &pl->phylink_disable_state)) { + /* If in 802.3z mode, this updates the advertisement. + * + * If we are in SGMII mode without a PHY, there is no + * advertisement; the only thing we have is the pause + * modes which can only come from a PHY. + */ + phylink_mac_config(pl, &pl->link_config); + phylink_mac_an_restart(pl); + } + mutex_unlock(&pl->state_mutex); } - mutex_unlock(&pl->state_mutex); return 0; } -- GitLab From df3f57ac960590f4e87ec71549efd49bbfdb25d0 Mon Sep 17 00:00:00 2001 From: Russell King Date: Fri, 13 Dec 2019 18:22:07 +0000 Subject: [PATCH 0833/1437] net: phylink: extend clause 45 PHY validation workaround Commit e45d1f5288b8 ("net: phylink: support Clause 45 PHYs on SFP+ modules") added a workaround to support clause 45 PHYs which dynamically switch their interface mode on SFP+ modules. This was implemented by validating the PHYs supported/advertising using PHY_INTERFACE_MODE_NA, rather than the specific interface mode that we attached the PHY with. However, we already have a situation where phylink is used to connect a Marvell 88X3310 PHY which also behaves in exactly the same way, but which seemingly doesn't need this. The reason seems to be that the mvpp2 driver sets a whole bunch of link modes for PHY_INTERFACE_MODE_10GKR down to 10Mb/s, despite 10GBASE-R not actually supporting anything but 10Gb/s speeds. When testing with drivers that (correctly) take the mvneta approach, where the validate() method only returns what can be supported / advertised for the specified link mode, we find that Clause 45 PHYs do not behave as we expect: their advertisement is restricted to what the current link will support, rather than what the PHY supports through its dynamic switching. Extend this workaround to all such cases; if we have a Clause 45 PHY attaching via any means, except in USXGMII, XAUI and RXAUI which are all unable to support this dynamic switching or have other solutions to it, then we need to validate using PHY_INTERFACE_MODE_NA. This should allow mvpp2 to switch to a more conformant validate() implementation. Signed-off-by: Russell King Signed-off-by: David S. Miller --- drivers/net/phy/phylink.c | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c index 8f3dc27db95d3..a917f95372cd4 100644 --- a/drivers/net/phy/phylink.c +++ b/drivers/net/phy/phylink.c @@ -736,7 +736,19 @@ static int phylink_bringup_phy(struct phylink *pl, struct phy_device *phy, memset(&config, 0, sizeof(config)); linkmode_copy(supported, phy->supported); linkmode_copy(config.advertising, phy->advertising); - config.interface = interface; + + /* Clause 45 PHYs switch their Serdes lane between several different + * modes, normally 10GBASE-R, SGMII. Some use 2500BASE-X for 2.5G + * speeds. We really need to know which interface modes the PHY and + * MAC supports to properly work out which linkmodes can be supported. + */ + if (phy->is_c45 && + interface != PHY_INTERFACE_MODE_RXAUI && + interface != PHY_INTERFACE_MODE_XAUI && + interface != PHY_INTERFACE_MODE_USXGMII) + config.interface = PHY_INTERFACE_MODE_NA; + else + config.interface = interface; ret = phylink_validate(pl, supported, &config); if (ret) @@ -1902,14 +1914,6 @@ static int phylink_sfp_connect_phy(void *upstream, struct phy_device *phy) if (ret < 0) return ret; - /* Clause 45 PHYs switch their Serdes lane between several different - * modes, normally 10GBASE-R, SGMII. Some use 2500BASE-X for 2.5G - * speeds. We really need to know which interface modes the PHY and - * MAC supports to properly work out which linkmodes can be supported. - */ - if (phy->is_c45) - interface = PHY_INTERFACE_MODE_NA; - ret = phylink_bringup_phy(pl, phy, interface); if (ret) phy_detach(phy); -- GitLab From ef8e0b808b908c49670ef00b95c7848e233ca3bd Mon Sep 17 00:00:00 2001 From: Russell King Date: Fri, 13 Dec 2019 18:22:12 +0000 Subject: [PATCH 0834/1437] net: mvpp2: update mvpp2 validate() implementation Fix up the mvpp2 validate implementation to adopt the same behaviour as mvneta: - only allow the link modes that the specified PHY interface mode supports with the exception of 1000base-X and 2500base-X. - use the basex helper to deal with SFP modules that can be switched between 1000base-X vs 2500base-X. This gives consistent behaviour between mvneta and mvpp2. This commit depends on "net: phylink: extend clause 45 PHY validation workaround" so is not marked for backporting to stable kernels. Signed-off-by: Russell King Acked-by: Antoine Tenart Signed-off-by: David S. Miller --- .../net/ethernet/marvell/mvpp2/mvpp2_main.c | 22 +++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c b/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c index 62dc2f362a169..3d9eb5747d9ee 100644 --- a/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c +++ b/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c @@ -4792,6 +4792,8 @@ static void mvpp2_phylink_validate(struct phylink_config *config, phylink_set(mask, 10000baseER_Full); phylink_set(mask, 10000baseKR_Full); } + if (state->interface != PHY_INTERFACE_MODE_NA) + break; /* Fall-through */ case PHY_INTERFACE_MODE_RGMII: case PHY_INTERFACE_MODE_RGMII_ID: @@ -4802,13 +4804,23 @@ static void mvpp2_phylink_validate(struct phylink_config *config, phylink_set(mask, 10baseT_Full); phylink_set(mask, 100baseT_Half); phylink_set(mask, 100baseT_Full); + phylink_set(mask, 1000baseT_Full); + phylink_set(mask, 1000baseX_Full); + if (state->interface != PHY_INTERFACE_MODE_NA) + break; /* Fall-through */ case PHY_INTERFACE_MODE_1000BASEX: case PHY_INTERFACE_MODE_2500BASEX: - phylink_set(mask, 1000baseT_Full); - phylink_set(mask, 1000baseX_Full); - phylink_set(mask, 2500baseT_Full); - phylink_set(mask, 2500baseX_Full); + if (port->comphy || + state->interface != PHY_INTERFACE_MODE_2500BASEX) { + phylink_set(mask, 1000baseT_Full); + phylink_set(mask, 1000baseX_Full); + } + if (port->comphy || + state->interface == PHY_INTERFACE_MODE_2500BASEX) { + phylink_set(mask, 2500baseT_Full); + phylink_set(mask, 2500baseX_Full); + } break; default: goto empty_set; @@ -4817,6 +4829,8 @@ static void mvpp2_phylink_validate(struct phylink_config *config, bitmap_and(supported, supported, mask, __ETHTOOL_LINK_MODE_MASK_NBITS); bitmap_and(state->advertising, state->advertising, mask, __ETHTOOL_LINK_MODE_MASK_NBITS); + + phylink_helper_basex_speed(state); return; empty_set: -- GitLab From 71a55a2315b047352b3d65e2d24724207be85ae2 Mon Sep 17 00:00:00 2001 From: Biao Huang Date: Mon, 16 Dec 2019 13:39:57 +0800 Subject: [PATCH 0835/1437] net-next: stmmac: mediatek: add more support for RMII MT2712 SoC can provide the rmii reference clock, and the clock will output from TXC pin only, which means ref_clk pin of external PHY should connect to TXC pin in this case. Add corresponding clock and timing settings. Signed-off-by: Biao Huang Signed-off-by: David S. Miller --- .../ethernet/stmicro/stmmac/dwmac-mediatek.c | 89 +++++++++++++------ 1 file changed, 60 insertions(+), 29 deletions(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-mediatek.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-mediatek.c index bdb80421acac6..9e4b838329382 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-mediatek.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-mediatek.c @@ -55,6 +55,8 @@ struct mediatek_dwmac_plat_data { struct regmap *peri_regmap; struct device *dev; phy_interface_t phy_mode; + int num_clks_to_config; + bool rmii_clk_from_mac; bool rmii_rxc; }; @@ -73,21 +75,33 @@ struct mediatek_dwmac_variant { /* list of clocks required for mac */ static const char * const mt2712_dwmac_clk_l[] = { - "axi", "apb", "mac_main", "ptp_ref" + "axi", "apb", "mac_main", "ptp_ref", "rmii_internal" }; static int mt2712_set_interface(struct mediatek_dwmac_plat_data *plat) { + int rmii_clk_from_mac = plat->rmii_clk_from_mac ? RMII_CLK_SRC_INTERNAL : 0; int rmii_rxc = plat->rmii_rxc ? RMII_CLK_SRC_RXC : 0; u32 intf_val = 0; + /* The clock labeled as "rmii_internal" in mt2712_dwmac_clk_l is needed + * only in RMII(when MAC provides the reference clock), and useless for + * RGMII/MII/RMII(when PHY provides the reference clock). + * num_clks_to_config indicates the real number of clocks should be + * configured, equals to (plat->variant->num_clks - 1) in default for all the case, + * then +1 for rmii_clk_from_mac case. + */ + plat->num_clks_to_config = plat->variant->num_clks - 1; + /* select phy interface in top control domain */ switch (plat->phy_mode) { case PHY_INTERFACE_MODE_MII: intf_val |= PHY_INTF_MII; break; case PHY_INTERFACE_MODE_RMII: - intf_val |= (PHY_INTF_RMII | rmii_rxc); + if (plat->rmii_clk_from_mac) + plat->num_clks_to_config++; + intf_val |= (PHY_INTF_RMII | rmii_rxc | rmii_clk_from_mac); break; case PHY_INTERFACE_MODE_RGMII: case PHY_INTERFACE_MODE_RGMII_TXID: @@ -173,35 +187,50 @@ static int mt2712_set_delay(struct mediatek_dwmac_plat_data *plat) delay_val |= FIELD_PREP(ETH_DLY_RXC_INV, mac_delay->rx_inv); break; case PHY_INTERFACE_MODE_RMII: - /* the rmii reference clock is from external phy, - * and the property "rmii_rxc" indicates which pin(TXC/RXC) - * the reference clk is connected to. The reference clock is a - * received signal, so rx_delay/rx_inv are used to indicate - * the reference clock timing adjustment - */ - if (plat->rmii_rxc) { - /* the rmii reference clock from outside is connected - * to RXC pin, the reference clock will be adjusted - * by RXC delay macro circuit. - */ - delay_val |= FIELD_PREP(ETH_DLY_RXC_ENABLE, !!mac_delay->rx_delay); - delay_val |= FIELD_PREP(ETH_DLY_RXC_STAGES, mac_delay->rx_delay); - delay_val |= FIELD_PREP(ETH_DLY_RXC_INV, mac_delay->rx_inv); - } else { - /* the rmii reference clock from outside is connected - * to TXC pin, the reference clock will be adjusted - * by TXC delay macro circuit. + if (plat->rmii_clk_from_mac) { + /* case 1: mac provides the rmii reference clock, + * and the clock output to TXC pin. + * The egress timing can be adjusted by GTXC delay macro circuit. + * The ingress timing can be adjusted by TXC delay macro circuit. */ delay_val |= FIELD_PREP(ETH_DLY_TXC_ENABLE, !!mac_delay->rx_delay); delay_val |= FIELD_PREP(ETH_DLY_TXC_STAGES, mac_delay->rx_delay); delay_val |= FIELD_PREP(ETH_DLY_TXC_INV, mac_delay->rx_inv); + + delay_val |= FIELD_PREP(ETH_DLY_GTXC_ENABLE, !!mac_delay->tx_delay); + delay_val |= FIELD_PREP(ETH_DLY_GTXC_STAGES, mac_delay->tx_delay); + delay_val |= FIELD_PREP(ETH_DLY_GTXC_INV, mac_delay->tx_inv); + } else { + /* case 2: the rmii reference clock is from external phy, + * and the property "rmii_rxc" indicates which pin(TXC/RXC) + * the reference clk is connected to. The reference clock is a + * received signal, so rx_delay/rx_inv are used to indicate + * the reference clock timing adjustment + */ + if (plat->rmii_rxc) { + /* the rmii reference clock from outside is connected + * to RXC pin, the reference clock will be adjusted + * by RXC delay macro circuit. + */ + delay_val |= FIELD_PREP(ETH_DLY_RXC_ENABLE, !!mac_delay->rx_delay); + delay_val |= FIELD_PREP(ETH_DLY_RXC_STAGES, mac_delay->rx_delay); + delay_val |= FIELD_PREP(ETH_DLY_RXC_INV, mac_delay->rx_inv); + } else { + /* the rmii reference clock from outside is connected + * to TXC pin, the reference clock will be adjusted + * by TXC delay macro circuit. + */ + delay_val |= FIELD_PREP(ETH_DLY_TXC_ENABLE, !!mac_delay->rx_delay); + delay_val |= FIELD_PREP(ETH_DLY_TXC_STAGES, mac_delay->rx_delay); + delay_val |= FIELD_PREP(ETH_DLY_TXC_INV, mac_delay->rx_inv); + } + /* tx_inv will inverse the tx clock inside mac relateive to + * reference clock from external phy, + * and this bit is located in the same register with fine-tune + */ + if (mac_delay->tx_inv) + fine_val = ETH_RMII_DLY_TX_INV; } - /* tx_inv will inverse the tx clock inside mac relateive to - * reference clock from external phy, - * and this bit is located in the same register with fine-tune - */ - if (mac_delay->tx_inv) - fine_val = ETH_RMII_DLY_TX_INV; break; case PHY_INTERFACE_MODE_RGMII: case PHY_INTERFACE_MODE_RGMII_TXID: @@ -278,6 +307,7 @@ static int mediatek_dwmac_config_dt(struct mediatek_dwmac_plat_data *plat) mac_delay->tx_inv = of_property_read_bool(plat->np, "mediatek,txc-inverse"); mac_delay->rx_inv = of_property_read_bool(plat->np, "mediatek,rxc-inverse"); plat->rmii_rxc = of_property_read_bool(plat->np, "mediatek,rmii-rxc"); + plat->rmii_clk_from_mac = of_property_read_bool(plat->np, "mediatek,rmii-clk-from-mac"); return 0; } @@ -294,6 +324,8 @@ static int mediatek_dwmac_clk_init(struct mediatek_dwmac_plat_data *plat) for (i = 0; i < num; i++) plat->clks[i].id = variant->clk_list[i]; + plat->num_clks_to_config = variant->num_clks; + return devm_clk_bulk_get(plat->dev, num, plat->clks); } @@ -321,7 +353,7 @@ static int mediatek_dwmac_init(struct platform_device *pdev, void *priv) return ret; } - ret = clk_bulk_prepare_enable(variant->num_clks, plat->clks); + ret = clk_bulk_prepare_enable(plat->num_clks_to_config, plat->clks); if (ret) { dev_err(plat->dev, "failed to enable clks, err = %d\n", ret); return ret; @@ -336,9 +368,8 @@ static int mediatek_dwmac_init(struct platform_device *pdev, void *priv) static void mediatek_dwmac_exit(struct platform_device *pdev, void *priv) { struct mediatek_dwmac_plat_data *plat = priv; - const struct mediatek_dwmac_variant *variant = plat->variant; - clk_bulk_disable_unprepare(variant->num_clks, plat->clks); + clk_bulk_disable_unprepare(plat->num_clks_to_config, plat->clks); pm_runtime_put_sync(&pdev->dev); pm_runtime_disable(&pdev->dev); -- GitLab From 882007ed783211c27e859234e78328d35ded03c7 Mon Sep 17 00:00:00 2001 From: Biao Huang Date: Mon, 16 Dec 2019 13:39:58 +0800 Subject: [PATCH 0836/1437] net-next: dt-binding: dwmac-mediatek: add more description for RMII MT2712 SoC can provide RMII reference clock, so add corresponding description in dt-binding. Signed-off-by: Biao Huang Signed-off-by: David S. Miller --- .../bindings/net/mediatek-dwmac.txt | 33 +++++++++++++------ 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/Documentation/devicetree/bindings/net/mediatek-dwmac.txt b/Documentation/devicetree/bindings/net/mediatek-dwmac.txt index 8a08621a5b546..afbcaebf062e8 100644 --- a/Documentation/devicetree/bindings/net/mediatek-dwmac.txt +++ b/Documentation/devicetree/bindings/net/mediatek-dwmac.txt @@ -14,7 +14,7 @@ Required properties: Should be "macirq" for the main MAC IRQ - clocks: Must contain a phandle for each entry in clock-names. - clock-names: The name of the clock listed in the clocks property. These are - "axi", "apb", "mac_main", "ptp_ref" for MT2712 SoC + "axi", "apb", "mac_main", "ptp_ref", "rmii_internal" for MT2712 SoC. - mac-address: See ethernet.txt in the same directory - phy-mode: See ethernet.txt in the same directory - mediatek,pericfg: A phandle to the syscon node that control ethernet @@ -23,8 +23,10 @@ Required properties: Optional properties: - mediatek,tx-delay-ps: TX clock delay macro value. Default is 0. It should be defined for RGMII/MII interface. + It should be defined for RMII interface when the reference clock is from MT2712 SoC. - mediatek,rx-delay-ps: RX clock delay macro value. Default is 0. - It should be defined for RGMII/MII/RMII interface. + It should be defined for RGMII/MII interface. + It should be defined for RMII interface. Both delay properties need to be a multiple of 170 for RGMII interface, or will round down. Range 0~31*170. Both delay properties need to be a multiple of 550 for MII/RMII interface, @@ -34,13 +36,20 @@ or will round down. Range 0~31*550. reference clock, which is from external PHYs, is connected to RXC pin on MT2712 SoC. Otherwise, is connected to TXC pin. +- mediatek,rmii-clk-from-mac: boolean property, if present indicates that + MT2712 SoC provides the RMII reference clock, which outputs to TXC pin only. - mediatek,txc-inverse: boolean property, if present indicates that 1. tx clock will be inversed in MII/RGMII case, 2. tx clock inside MAC will be inversed relative to reference clock which is from external PHYs in RMII case, and it rarely happen. + 3. the reference clock, which outputs to TXC pin will be inversed in RMII case + when the reference clock is from MT2712 SoC. - mediatek,rxc-inverse: boolean property, if present indicates that 1. rx clock will be inversed in MII/RGMII case. - 2. reference clock will be inversed when arrived at MAC in RMII case. + 2. reference clock will be inversed when arrived at MAC in RMII case, when + the reference clock is from external PHYs. + 3. the inside clock, which be sent to MAC, will be inversed in RMII case when + the reference clock is from MT2712 SoC. - assigned-clocks: mac_main and ptp_ref clocks - assigned-clock-parents: parent clocks of the assigned clocks @@ -50,29 +59,33 @@ Example: reg = <0 0x1101c000 0 0x1300>; interrupts = ; interrupt-names = "macirq"; - phy-mode ="rgmii"; + phy-mode ="rgmii-rxid"; mac-address = [00 55 7b b5 7d f7]; clock-names = "axi", "apb", "mac_main", "ptp_ref", - "ptp_top"; + "rmii_internal"; clocks = <&pericfg CLK_PERI_GMAC>, <&pericfg CLK_PERI_GMAC_PCLK>, <&topckgen CLK_TOP_ETHER_125M_SEL>, - <&topckgen CLK_TOP_ETHER_50M_SEL>; + <&topckgen CLK_TOP_ETHER_50M_SEL>, + <&topckgen CLK_TOP_ETHER_50M_RMII_SEL>; assigned-clocks = <&topckgen CLK_TOP_ETHER_125M_SEL>, - <&topckgen CLK_TOP_ETHER_50M_SEL>; + <&topckgen CLK_TOP_ETHER_50M_SEL>, + <&topckgen CLK_TOP_ETHER_50M_RMII_SEL>; assigned-clock-parents = <&topckgen CLK_TOP_ETHERPLL_125M>, - <&topckgen CLK_TOP_APLL1_D3>; + <&topckgen CLK_TOP_APLL1_D3>, + <&topckgen CLK_TOP_ETHERPLL_50M>; + power-domains = <&scpsys MT2712_POWER_DOMAIN_AUDIO>; mediatek,pericfg = <&pericfg>; mediatek,tx-delay-ps = <1530>; mediatek,rx-delay-ps = <1530>; mediatek,rmii-rxc; mediatek,txc-inverse; mediatek,rxc-inverse; - snps,txpbl = <32>; - snps,rxpbl = <32>; + snps,txpbl = <1>; + snps,rxpbl = <1>; snps,reset-gpio = <&pio 87 GPIO_ACTIVE_LOW>; snps,reset-active-low; }; -- GitLab From b59c43e03cee8537e0403be6cd4dda2cf5149455 Mon Sep 17 00:00:00 2001 From: Ajay Gupta Date: Sun, 15 Dec 2019 22:14:51 -0800 Subject: [PATCH 0837/1437] net: stmmac: dwc-qos: use generic device api Use generic device api so that driver can work both with DT or ACPI based devices. Signed-off-by: Ajay Gupta Signed-off-by: David S. Miller --- .../stmicro/stmmac/dwmac-dwc-qos-eth.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-dwc-qos-eth.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-dwc-qos-eth.c index dd9967aeda221..f87306b3cdae8 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-dwc-qos-eth.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-dwc-qos-eth.c @@ -40,7 +40,7 @@ struct tegra_eqos { static int dwc_eth_dwmac_config_dt(struct platform_device *pdev, struct plat_stmmacenet_data *plat_dat) { - struct device_node *np = pdev->dev.of_node; + struct device *dev = &pdev->dev; u32 burst_map = 0; u32 bit_index = 0; u32 a_index = 0; @@ -52,9 +52,10 @@ static int dwc_eth_dwmac_config_dt(struct platform_device *pdev, return -ENOMEM; } - plat_dat->axi->axi_lpi_en = of_property_read_bool(np, "snps,en-lpi"); - if (of_property_read_u32(np, "snps,write-requests", - &plat_dat->axi->axi_wr_osr_lmt)) { + plat_dat->axi->axi_lpi_en = device_property_read_bool(dev, + "snps,en-lpi"); + if (device_property_read_u32(dev, "snps,write-requests", + &plat_dat->axi->axi_wr_osr_lmt)) { /** * Since the register has a reset value of 1, if property * is missing, default to 1. @@ -68,8 +69,8 @@ static int dwc_eth_dwmac_config_dt(struct platform_device *pdev, plat_dat->axi->axi_wr_osr_lmt--; } - if (of_property_read_u32(np, "snps,read-requests", - &plat_dat->axi->axi_rd_osr_lmt)) { + if (device_property_read_u32(dev, "snps,read-requests", + &plat_dat->axi->axi_rd_osr_lmt)) { /** * Since the register has a reset value of 1, if property * is missing, default to 1. @@ -82,7 +83,7 @@ static int dwc_eth_dwmac_config_dt(struct platform_device *pdev, */ plat_dat->axi->axi_rd_osr_lmt--; } - of_property_read_u32(np, "snps,burst-map", &burst_map); + device_property_read_u32(dev, "snps,burst-map", &burst_map); /* converts burst-map bitmask to burst array */ for (bit_index = 0; bit_index < 7; bit_index++) { @@ -421,7 +422,7 @@ static int dwc_eth_dwmac_probe(struct platform_device *pdev) void *priv; int ret; - data = of_device_get_match_data(&pdev->dev); + data = device_get_match_data(&pdev->dev); memset(&stmmac_res, 0, sizeof(struct stmmac_resources)); @@ -478,7 +479,7 @@ static int dwc_eth_dwmac_remove(struct platform_device *pdev) const struct dwc_eth_dwmac_data *data; int err; - data = of_device_get_match_data(&pdev->dev); + data = device_get_match_data(&pdev->dev); err = stmmac_dvr_remove(&pdev->dev); if (err < 0) -- GitLab From 1d4605e0aff9ffe5163b84ccefaa8c6c2387504d Mon Sep 17 00:00:00 2001 From: Ajay Gupta Date: Sun, 15 Dec 2019 22:14:52 -0800 Subject: [PATCH 0838/1437] net: stmmac: dwc-qos: avoid clk and reset for acpi device There are no clocks, resets or gpios referenced by Tegra ACPI device so don't access clocks, resets or gpios interface with ACPI device. Clocks, resets and GPIOs for ACPI devices will be handled via ACPI interface. Signed-off-by: Ajay Gupta Signed-off-by: David S. Miller --- drivers/net/ethernet/stmicro/stmmac/dwmac-dwc-qos-eth.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-dwc-qos-eth.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-dwc-qos-eth.c index f87306b3cdae8..2342d497348ea 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-dwc-qos-eth.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-dwc-qos-eth.c @@ -271,6 +271,7 @@ static void *tegra_eqos_probe(struct platform_device *pdev, struct plat_stmmacenet_data *data, struct stmmac_resources *res) { + struct device *dev = &pdev->dev; struct tegra_eqos *eqos; int err; @@ -283,6 +284,9 @@ static void *tegra_eqos_probe(struct platform_device *pdev, eqos->dev = &pdev->dev; eqos->regs = res->addr; + if (!is_of_node(dev->fwnode)) + goto bypass_clk_reset_gpio; + eqos->clk_master = devm_clk_get(&pdev->dev, "master_bus"); if (IS_ERR(eqos->clk_master)) { err = PTR_ERR(eqos->clk_master); @@ -355,6 +359,7 @@ static void *tegra_eqos_probe(struct platform_device *pdev, usleep_range(2000, 4000); +bypass_clk_reset_gpio: data->fix_mac_speed = tegra_eqos_fix_speed; data->init = tegra_eqos_init; data->bsp_priv = eqos; -- GitLab From 871185ace40df871a93866b2a7ce441276fc4ee8 Mon Sep 17 00:00:00 2001 From: Lukas Wunner Date: Wed, 20 Nov 2019 12:33:59 +0100 Subject: [PATCH 0839/1437] netfilter: Clean up unnecessary #ifdef If CONFIG_NETFILTER_INGRESS is not enabled, nf_ingress() becomes a no-op because it solely contains an if-clause calling nf_hook_ingress_active(), for which an empty inline stub exists in . All the symbols used in the if-clause's body are still available even if CONFIG_NETFILTER_INGRESS is not enabled. The additional "#ifdef CONFIG_NETFILTER_INGRESS" in nf_ingress() is thus unnecessary, so drop it. Signed-off-by: Lukas Wunner Cc: Daniel Borkmann Signed-off-by: Pablo Neira Ayuso --- net/core/dev.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/net/core/dev.c b/net/core/dev.c index 2c277b8aba38b..1ccead4b19bf7 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -4932,7 +4932,6 @@ static bool skb_pfmemalloc_protocol(struct sk_buff *skb) static inline int nf_ingress(struct sk_buff *skb, struct packet_type **pt_prev, int *ret, struct net_device *orig_dev) { -#ifdef CONFIG_NETFILTER_INGRESS if (nf_hook_ingress_active(skb)) { int ingress_retval; @@ -4946,7 +4945,6 @@ static inline int nf_ingress(struct sk_buff *skb, struct packet_type **pt_prev, rcu_read_unlock(); return ingress_retval; } -#endif /* CONFIG_NETFILTER_INGRESS */ return 0; } -- GitLab From 2f5e70c8ce47396bfa8f5c437574b569c02597bb Mon Sep 17 00:00:00 2001 From: Lukas Wunner Date: Wed, 20 Nov 2019 12:33:59 +0100 Subject: [PATCH 0840/1437] netfilter: Document ingress hook Amend kerneldoc of struct net_device to fix a "make htmldocs" warning: include/linux/netdevice.h:2045: warning: Function parameter or member 'nf_hooks_ingress' not described in 'net_device' Reported-by: kbuild test robot Signed-off-by: Lukas Wunner Cc: Daniel Borkmann Signed-off-by: Pablo Neira Ayuso --- include/linux/netdevice.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 30745068fb39d..0b097bbd36639 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -1708,6 +1708,7 @@ enum netdev_priv_flags { * @miniq_ingress: ingress/clsact qdisc specific data for * ingress processing * @ingress_queue: XXX: need comments on this one + * @nf_hooks_ingress: netfilter hooks executed for ingress packets * @broadcast: hw bcast address * * @rx_cpu_rmap: CPU reverse-mapping for RX completion interrupts, -- GitLab From cf3e204a1ca5442190018a317d9ec181b4639bd6 Mon Sep 17 00:00:00 2001 From: Xin Long Date: Fri, 13 Dec 2019 16:53:05 +0800 Subject: [PATCH 0841/1437] netfilter: nft_tunnel: no need to call htons() when dumping ports info->key.tp_src and tp_dst are __be16, when using nla_put_be16() to dump them, htons() is not needed, so remove it in this patch. Fixes: af308b94a2a4 ("netfilter: nf_tables: add tunnel support") Signed-off-by: Xin Long Reviewed-by: Simon Horman Signed-off-by: Pablo Neira Ayuso --- net/netfilter/nft_tunnel.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/netfilter/nft_tunnel.c b/net/netfilter/nft_tunnel.c index 3d4c2ae605a8e..ef2065dd4f8af 100644 --- a/net/netfilter/nft_tunnel.c +++ b/net/netfilter/nft_tunnel.c @@ -501,8 +501,8 @@ static int nft_tunnel_opts_dump(struct sk_buff *skb, static int nft_tunnel_ports_dump(struct sk_buff *skb, struct ip_tunnel_info *info) { - if (nla_put_be16(skb, NFTA_TUNNEL_KEY_SPORT, htons(info->key.tp_src)) < 0 || - nla_put_be16(skb, NFTA_TUNNEL_KEY_DPORT, htons(info->key.tp_dst)) < 0) + if (nla_put_be16(skb, NFTA_TUNNEL_KEY_SPORT, info->key.tp_src) < 0 || + nla_put_be16(skb, NFTA_TUNNEL_KEY_DPORT, info->key.tp_dst) < 0) return -1; return 0; -- GitLab From 0705f95c332081036d85f26691e9d3cd7d901c31 Mon Sep 17 00:00:00 2001 From: Xin Long Date: Fri, 13 Dec 2019 16:53:06 +0800 Subject: [PATCH 0842/1437] netfilter: nft_tunnel: add the missing ERSPAN_VERSION nla_policy ERSPAN_VERSION is an attribute parsed in kernel side, nla_policy type should be added for it, like other attributes. Fixes: af308b94a2a4 ("netfilter: nf_tables: add tunnel support") Signed-off-by: Xin Long Reviewed-by: Simon Horman Signed-off-by: Pablo Neira Ayuso --- net/netfilter/nft_tunnel.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/net/netfilter/nft_tunnel.c b/net/netfilter/nft_tunnel.c index ef2065dd4f8af..6538895466e09 100644 --- a/net/netfilter/nft_tunnel.c +++ b/net/netfilter/nft_tunnel.c @@ -248,8 +248,9 @@ static int nft_tunnel_obj_vxlan_init(const struct nlattr *attr, } static const struct nla_policy nft_tunnel_opts_erspan_policy[NFTA_TUNNEL_KEY_ERSPAN_MAX + 1] = { + [NFTA_TUNNEL_KEY_ERSPAN_VERSION] = { .type = NLA_U32 }, [NFTA_TUNNEL_KEY_ERSPAN_V1_INDEX] = { .type = NLA_U32 }, - [NFTA_TUNNEL_KEY_ERSPAN_V2_DIR] = { .type = NLA_U8 }, + [NFTA_TUNNEL_KEY_ERSPAN_V2_DIR] = { .type = NLA_U8 }, [NFTA_TUNNEL_KEY_ERSPAN_V2_HWID] = { .type = NLA_U8 }, }; -- GitLab From 2149f36dbd44f2d6bf5357e5d096205b92cd854a Mon Sep 17 00:00:00 2001 From: Xin Long Date: Fri, 13 Dec 2019 16:53:07 +0800 Subject: [PATCH 0843/1437] netfilter: nft_tunnel: also dump ERSPAN_VERSION This is not necessary, but it'll be easier to parse in userspace, also given that other places like act_tunnel_key, cls_flower and ip_tunnel_core are also doing so. Signed-off-by: Xin Long Reviewed-by: Simon Horman Signed-off-by: Pablo Neira Ayuso --- net/netfilter/nft_tunnel.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/netfilter/nft_tunnel.c b/net/netfilter/nft_tunnel.c index 6538895466e09..b3a9b10ff43d0 100644 --- a/net/netfilter/nft_tunnel.c +++ b/net/netfilter/nft_tunnel.c @@ -479,6 +479,9 @@ static int nft_tunnel_opts_dump(struct sk_buff *skb, htonl(opts->u.vxlan.gbp))) return -1; } else if (opts->flags & TUNNEL_ERSPAN_OPT) { + if (nla_put_be32(skb, NFTA_TUNNEL_KEY_ERSPAN_VERSION, + htonl(opts->u.erspan.version))) + return -1; switch (opts->u.erspan.version) { case ERSPAN_VERSION: if (nla_put_be32(skb, NFTA_TUNNEL_KEY_ERSPAN_V1_INDEX, -- GitLab From 73239bd9707ab2f3b7621e50468c14410cf4e2c2 Mon Sep 17 00:00:00 2001 From: Xin Long Date: Fri, 13 Dec 2019 16:53:08 +0800 Subject: [PATCH 0844/1437] netfilter: nft_tunnel: also dump OPTS_ERSPAN/VXLAN This patch is to add the nest attr OPTS_ERSPAN/VXLAN when dumping KEY_OPTS, and it would be helpful when parsing in userpace. Also, this is needed for supporting multiple geneve opts in the future patches. Signed-off-by: Xin Long Reviewed-by: Simon Horman Signed-off-by: Pablo Neira Ayuso --- net/netfilter/nft_tunnel.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/net/netfilter/nft_tunnel.c b/net/netfilter/nft_tunnel.c index b3a9b10ff43d0..eb17402365269 100644 --- a/net/netfilter/nft_tunnel.c +++ b/net/netfilter/nft_tunnel.c @@ -468,17 +468,24 @@ static int nft_tunnel_opts_dump(struct sk_buff *skb, struct nft_tunnel_obj *priv) { struct nft_tunnel_opts *opts = &priv->opts; - struct nlattr *nest; + struct nlattr *nest, *inner; nest = nla_nest_start_noflag(skb, NFTA_TUNNEL_KEY_OPTS); if (!nest) return -1; if (opts->flags & TUNNEL_VXLAN_OPT) { + inner = nla_nest_start_noflag(skb, NFTA_TUNNEL_KEY_OPTS_VXLAN); + if (!inner) + return -1; if (nla_put_be32(skb, NFTA_TUNNEL_KEY_VXLAN_GBP, htonl(opts->u.vxlan.gbp))) return -1; + nla_nest_end(skb, inner); } else if (opts->flags & TUNNEL_ERSPAN_OPT) { + inner = nla_nest_start_noflag(skb, NFTA_TUNNEL_KEY_OPTS_ERSPAN); + if (!inner) + return -1; if (nla_put_be32(skb, NFTA_TUNNEL_KEY_ERSPAN_VERSION, htonl(opts->u.erspan.version))) return -1; @@ -496,6 +503,7 @@ static int nft_tunnel_opts_dump(struct sk_buff *skb, return -1; break; } + nla_nest_end(skb, inner); } nla_nest_end(skb, nest); -- GitLab From 7e03998429ee0a27c13b8690c11f9cf40f67f6e9 Mon Sep 17 00:00:00 2001 From: Xin Long Date: Fri, 13 Dec 2019 16:53:09 +0800 Subject: [PATCH 0845/1437] netfilter: nft_tunnel: add the missing nla_nest_cancel() When nla_put_xxx() fails under nla_nest_start_noflag(), nla_nest_cancel() should be called, so that the skb can be trimmed properly. Signed-off-by: Xin Long Reviewed-by: Simon Horman Signed-off-by: Pablo Neira Ayuso --- net/netfilter/nft_tunnel.c | 38 ++++++++++++++++++++++++++------------ 1 file changed, 26 insertions(+), 12 deletions(-) diff --git a/net/netfilter/nft_tunnel.c b/net/netfilter/nft_tunnel.c index eb17402365269..23cd163689d5a 100644 --- a/net/netfilter/nft_tunnel.c +++ b/net/netfilter/nft_tunnel.c @@ -443,10 +443,15 @@ static int nft_tunnel_ip_dump(struct sk_buff *skb, struct ip_tunnel_info *info) if (!nest) return -1; - if (nla_put_in6_addr(skb, NFTA_TUNNEL_KEY_IP6_SRC, &info->key.u.ipv6.src) < 0 || - nla_put_in6_addr(skb, NFTA_TUNNEL_KEY_IP6_DST, &info->key.u.ipv6.dst) < 0 || - nla_put_be32(skb, NFTA_TUNNEL_KEY_IP6_FLOWLABEL, info->key.label)) + if (nla_put_in6_addr(skb, NFTA_TUNNEL_KEY_IP6_SRC, + &info->key.u.ipv6.src) < 0 || + nla_put_in6_addr(skb, NFTA_TUNNEL_KEY_IP6_DST, + &info->key.u.ipv6.dst) < 0 || + nla_put_be32(skb, NFTA_TUNNEL_KEY_IP6_FLOWLABEL, + info->key.label)) { + nla_nest_cancel(skb, nest); return -1; + } nla_nest_end(skb, nest); } else { @@ -454,9 +459,13 @@ static int nft_tunnel_ip_dump(struct sk_buff *skb, struct ip_tunnel_info *info) if (!nest) return -1; - if (nla_put_in_addr(skb, NFTA_TUNNEL_KEY_IP_SRC, info->key.u.ipv4.src) < 0 || - nla_put_in_addr(skb, NFTA_TUNNEL_KEY_IP_DST, info->key.u.ipv4.dst) < 0) + if (nla_put_in_addr(skb, NFTA_TUNNEL_KEY_IP_SRC, + info->key.u.ipv4.src) < 0 || + nla_put_in_addr(skb, NFTA_TUNNEL_KEY_IP_DST, + info->key.u.ipv4.dst) < 0) { + nla_nest_cancel(skb, nest); return -1; + } nla_nest_end(skb, nest); } @@ -477,37 +486,42 @@ static int nft_tunnel_opts_dump(struct sk_buff *skb, if (opts->flags & TUNNEL_VXLAN_OPT) { inner = nla_nest_start_noflag(skb, NFTA_TUNNEL_KEY_OPTS_VXLAN); if (!inner) - return -1; + goto failure; if (nla_put_be32(skb, NFTA_TUNNEL_KEY_VXLAN_GBP, htonl(opts->u.vxlan.gbp))) - return -1; + goto inner_failure; nla_nest_end(skb, inner); } else if (opts->flags & TUNNEL_ERSPAN_OPT) { inner = nla_nest_start_noflag(skb, NFTA_TUNNEL_KEY_OPTS_ERSPAN); if (!inner) - return -1; + goto failure; if (nla_put_be32(skb, NFTA_TUNNEL_KEY_ERSPAN_VERSION, htonl(opts->u.erspan.version))) - return -1; + goto inner_failure; switch (opts->u.erspan.version) { case ERSPAN_VERSION: if (nla_put_be32(skb, NFTA_TUNNEL_KEY_ERSPAN_V1_INDEX, opts->u.erspan.u.index)) - return -1; + goto inner_failure; break; case ERSPAN_VERSION2: if (nla_put_u8(skb, NFTA_TUNNEL_KEY_ERSPAN_V2_HWID, get_hwid(&opts->u.erspan.u.md2)) || nla_put_u8(skb, NFTA_TUNNEL_KEY_ERSPAN_V2_DIR, opts->u.erspan.u.md2.dir)) - return -1; + goto inner_failure; break; } nla_nest_end(skb, inner); } nla_nest_end(skb, nest); - return 0; + +inner_failure: + nla_nest_cancel(skb, inner); +failure: + nla_nest_cancel(skb, nest); + return -1; } static int nft_tunnel_ports_dump(struct sk_buff *skb, -- GitLab From 13d74c0a9708a4f1ab0164a800ce9ea3de32f47b Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Fri, 13 Dec 2019 01:58:15 +0100 Subject: [PATCH 0846/1437] netfilter: conntrack: remove two export symbols Not used anywhere, remove them. Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- net/netfilter/nf_conntrack_core.c | 1 - net/netfilter/nf_conntrack_extend.c | 1 - 2 files changed, 2 deletions(-) diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c index 0af1898af2b80..983a9481e8f86 100644 --- a/net/netfilter/nf_conntrack_core.c +++ b/net/netfilter/nf_conntrack_core.c @@ -2333,7 +2333,6 @@ int nf_conntrack_set_hashsize(const char *val, const struct kernel_param *kp) return nf_conntrack_hash_resize(hashsize); } -EXPORT_SYMBOL_GPL(nf_conntrack_set_hashsize); static __always_inline unsigned int total_extension_size(void) { diff --git a/net/netfilter/nf_conntrack_extend.c b/net/netfilter/nf_conntrack_extend.c index c24e5b64b00cb..3dbe2329c3f17 100644 --- a/net/netfilter/nf_conntrack_extend.c +++ b/net/netfilter/nf_conntrack_extend.c @@ -37,7 +37,6 @@ void nf_ct_ext_destroy(struct nf_conn *ct) kfree(ct->ext); } -EXPORT_SYMBOL(nf_ct_ext_destroy); void *nf_ct_ext_add(struct nf_conn *ct, enum nf_ct_ext_id id, gfp_t gfp) { -- GitLab From daa6eb5a149519583c8a8cb31945f06417d21902 Mon Sep 17 00:00:00 2001 From: Ioana Ciornei Date: Mon, 16 Dec 2019 17:32:30 +0200 Subject: [PATCH 0847/1437] dpaa2-ptp: fix double free of the ptp_qoriq IRQ Upon reusing the ptp_qoriq driver, the ptp_qoriq_free() function was used on the remove path to free any allocated resources. The ptp_qoriq IRQ is among these resources that are freed in ptp_qoriq_free() even though it is also a managed one (allocated using devm_request_threaded_irq). Drop the resource managed version of requesting the IRQ in order to not trigger a double free of the interrupt as below: [ 226.731005] Trying to free already-free IRQ 126 [ 226.735533] WARNING: CPU: 6 PID: 749 at kernel/irq/manage.c:1707 __free_irq+0x9c/0x2b8 [ 226.743435] Modules linked in: [ 226.746480] CPU: 6 PID: 749 Comm: bash Tainted: G W 5.4.0-03629-gfd7102c32b2c-dirty #912 [ 226.755857] Hardware name: NXP Layerscape LX2160ARDB (DT) [ 226.761244] pstate: 40000085 (nZcv daIf -PAN -UAO) [ 226.766022] pc : __free_irq+0x9c/0x2b8 [ 226.769758] lr : __free_irq+0x9c/0x2b8 [ 226.773493] sp : ffff8000125039f0 (...) [ 226.856275] Call trace: [ 226.858710] __free_irq+0x9c/0x2b8 [ 226.862098] free_irq+0x30/0x70 [ 226.865229] devm_irq_release+0x14/0x20 [ 226.869054] release_nodes+0x1b0/0x220 [ 226.872790] devres_release_all+0x34/0x50 [ 226.876790] device_release_driver_internal+0x100/0x1c0 Fixes: d346c9e86d86 ("dpaa2-ptp: reuse ptp_qoriq driver") Cc: Yangbo Lu Signed-off-by: Ioana Ciornei Reviewed-by: Yangbo Lu Signed-off-by: David S. Miller --- drivers/net/ethernet/freescale/dpaa2/dpaa2-ptp.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-ptp.c b/drivers/net/ethernet/freescale/dpaa2/dpaa2-ptp.c index a9503aea527f5..6437fe6b9abf0 100644 --- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-ptp.c +++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-ptp.c @@ -160,10 +160,10 @@ static int dpaa2_ptp_probe(struct fsl_mc_device *mc_dev) irq = mc_dev->irqs[0]; ptp_qoriq->irq = irq->msi_desc->irq; - err = devm_request_threaded_irq(dev, ptp_qoriq->irq, NULL, - dpaa2_ptp_irq_handler_thread, - IRQF_NO_SUSPEND | IRQF_ONESHOT, - dev_name(dev), ptp_qoriq); + err = request_threaded_irq(ptp_qoriq->irq, NULL, + dpaa2_ptp_irq_handler_thread, + IRQF_NO_SUSPEND | IRQF_ONESHOT, + dev_name(dev), ptp_qoriq); if (err < 0) { dev_err(dev, "devm_request_threaded_irq(): %d\n", err); goto err_free_mc_irq; @@ -173,18 +173,20 @@ static int dpaa2_ptp_probe(struct fsl_mc_device *mc_dev) DPRTC_IRQ_INDEX, 1); if (err < 0) { dev_err(dev, "dprtc_set_irq_enable(): %d\n", err); - goto err_free_mc_irq; + goto err_free_threaded_irq; } err = ptp_qoriq_init(ptp_qoriq, base, &dpaa2_ptp_caps); if (err) - goto err_free_mc_irq; + goto err_free_threaded_irq; dpaa2_phc_index = ptp_qoriq->phc_index; dev_set_drvdata(dev, ptp_qoriq); return 0; +err_free_threaded_irq: + free_irq(ptp_qoriq->irq, ptp_qoriq); err_free_mc_irq: fsl_mc_free_irqs(mc_dev); err_unmap: -- GitLab From b7ffa045e7000d5410bf206454e0cb8de0428ed5 Mon Sep 17 00:00:00 2001 From: Jon Maloy Date: Mon, 16 Dec 2019 19:21:02 +0100 Subject: [PATCH 0848/1437] tipc: don't send gap blocks in ACK messages In the commit referred to below we eliminated sending of the 'gap' indicator in regular ACK messages, reserving this to explicit NACK ditto. Unfortunately we missed to also eliminate building of the 'gap block' area in ACK messages. This area is meant to report gaps in the received packet sequence following the initial gap, so that lost packets can be retransmitted earlier and received out-of-sequence packets can be released earlier. However, the interpretation of those blocks is dependent on a complete and correct sequence of gaps and acks. Hence, when the initial gap indicator is missing a single gap block will be interpreted as an acknowledgment of all preceding packets. This may lead to packets being released prematurely from the sender's transmit queue, with easily predicatble consequences. We now fix this by not building any gap block area if there is no initial gap to report. Fixes: commit 02288248b051 ("tipc: eliminate gap indicator from ACK messages") Signed-off-by: Jon Maloy Signed-off-by: David S. Miller --- net/tipc/link.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/net/tipc/link.c b/net/tipc/link.c index 94dd48cd70a3d..467c53a1fb5cf 100644 --- a/net/tipc/link.c +++ b/net/tipc/link.c @@ -250,7 +250,7 @@ static int tipc_link_build_nack_msg(struct tipc_link *l, static void tipc_link_build_bc_init_msg(struct tipc_link *l, struct sk_buff_head *xmitq); static int tipc_link_release_pkts(struct tipc_link *l, u16 to); -static u16 tipc_build_gap_ack_blks(struct tipc_link *l, void *data); +static u16 tipc_build_gap_ack_blks(struct tipc_link *l, void *data, u16 gap); static int tipc_link_advance_transmq(struct tipc_link *l, u16 acked, u16 gap, struct tipc_gap_ack_blks *ga, struct sk_buff_head *xmitq); @@ -1423,14 +1423,14 @@ static int tipc_link_release_pkts(struct tipc_link *l, u16 acked) * * returns the actual allocated memory size */ -static u16 tipc_build_gap_ack_blks(struct tipc_link *l, void *data) +static u16 tipc_build_gap_ack_blks(struct tipc_link *l, void *data, u16 gap) { struct sk_buff *skb = skb_peek(&l->deferdq); struct tipc_gap_ack_blks *ga = data; u16 len, expect, seqno = 0; u8 n = 0; - if (!skb) + if (!skb || !gap) goto exit; expect = buf_seqno(skb); @@ -1739,7 +1739,7 @@ static void tipc_link_build_proto_msg(struct tipc_link *l, int mtyp, bool probe, msg_set_probe(hdr, probe); msg_set_is_keepalive(hdr, probe || probe_reply); if (l->peer_caps & TIPC_GAP_ACK_BLOCK) - glen = tipc_build_gap_ack_blks(l, data); + glen = tipc_build_gap_ack_blks(l, data, rcvgap); tipc_mon_prep(l->net, data + glen, &dlen, mstate, l->bearer_id); msg_set_size(hdr, INT_H_SIZE + glen + dlen); skb_trim(skb, INT_H_SIZE + glen + dlen); -- GitLab From 8ae674964e67eb7deb3f0e489bfe9c102a7990b0 Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Mon, 16 Dec 2019 10:32:47 -0800 Subject: [PATCH 0849/1437] net: dsa: Make PHYLINK related function static again Commit 77373d49de22 ("net: dsa: Move the phylink driver calls into port.c") moved and exported a bunch of symbols, but they are not used outside of net/dsa/port.c at the moment, so no reason to export them. Reported-by: Russell King Signed-off-by: Florian Fainelli Reviewed-by: Vivien Didelot Acked-by: Russell King Acked-by: Vladimir Oltean Signed-off-by: David S. Miller --- net/dsa/dsa_priv.h | 16 ---------------- net/dsa/port.c | 38 ++++++++++++++++---------------------- 2 files changed, 16 insertions(+), 38 deletions(-) diff --git a/net/dsa/dsa_priv.h b/net/dsa/dsa_priv.h index 2dd86d9bcda91..09ea2fd78c747 100644 --- a/net/dsa/dsa_priv.h +++ b/net/dsa/dsa_priv.h @@ -150,22 +150,6 @@ int dsa_port_vid_add(struct dsa_port *dp, u16 vid, u16 flags); int dsa_port_vid_del(struct dsa_port *dp, u16 vid); int dsa_port_link_register_of(struct dsa_port *dp); void dsa_port_link_unregister_of(struct dsa_port *dp); -void dsa_port_phylink_validate(struct phylink_config *config, - unsigned long *supported, - struct phylink_link_state *state); -void dsa_port_phylink_mac_pcs_get_state(struct phylink_config *config, - struct phylink_link_state *state); -void dsa_port_phylink_mac_config(struct phylink_config *config, - unsigned int mode, - const struct phylink_link_state *state); -void dsa_port_phylink_mac_an_restart(struct phylink_config *config); -void dsa_port_phylink_mac_link_down(struct phylink_config *config, - unsigned int mode, - phy_interface_t interface); -void dsa_port_phylink_mac_link_up(struct phylink_config *config, - unsigned int mode, - phy_interface_t interface, - struct phy_device *phydev); extern const struct phylink_mac_ops dsa_port_phylink_mac_ops; /* slave.c */ diff --git a/net/dsa/port.c b/net/dsa/port.c index 46ac9ba219871..ffb5601f7ed62 100644 --- a/net/dsa/port.c +++ b/net/dsa/port.c @@ -415,9 +415,9 @@ static struct phy_device *dsa_port_get_phy_device(struct dsa_port *dp) return phydev; } -void dsa_port_phylink_validate(struct phylink_config *config, - unsigned long *supported, - struct phylink_link_state *state) +static void dsa_port_phylink_validate(struct phylink_config *config, + unsigned long *supported, + struct phylink_link_state *state) { struct dsa_port *dp = container_of(config, struct dsa_port, pl_config); struct dsa_switch *ds = dp->ds; @@ -427,10 +427,9 @@ void dsa_port_phylink_validate(struct phylink_config *config, ds->ops->phylink_validate(ds, dp->index, supported, state); } -EXPORT_SYMBOL_GPL(dsa_port_phylink_validate); -void dsa_port_phylink_mac_pcs_get_state(struct phylink_config *config, - struct phylink_link_state *state) +static void dsa_port_phylink_mac_pcs_get_state(struct phylink_config *config, + struct phylink_link_state *state) { struct dsa_port *dp = container_of(config, struct dsa_port, pl_config); struct dsa_switch *ds = dp->ds; @@ -444,11 +443,10 @@ void dsa_port_phylink_mac_pcs_get_state(struct phylink_config *config, if (ds->ops->phylink_mac_link_state(ds, dp->index, state) < 0) state->link = 0; } -EXPORT_SYMBOL_GPL(dsa_port_phylink_mac_pcs_get_state); -void dsa_port_phylink_mac_config(struct phylink_config *config, - unsigned int mode, - const struct phylink_link_state *state) +static void dsa_port_phylink_mac_config(struct phylink_config *config, + unsigned int mode, + const struct phylink_link_state *state) { struct dsa_port *dp = container_of(config, struct dsa_port, pl_config); struct dsa_switch *ds = dp->ds; @@ -458,9 +456,8 @@ void dsa_port_phylink_mac_config(struct phylink_config *config, ds->ops->phylink_mac_config(ds, dp->index, mode, state); } -EXPORT_SYMBOL_GPL(dsa_port_phylink_mac_config); -void dsa_port_phylink_mac_an_restart(struct phylink_config *config) +static void dsa_port_phylink_mac_an_restart(struct phylink_config *config) { struct dsa_port *dp = container_of(config, struct dsa_port, pl_config); struct dsa_switch *ds = dp->ds; @@ -470,11 +467,10 @@ void dsa_port_phylink_mac_an_restart(struct phylink_config *config) ds->ops->phylink_mac_an_restart(ds, dp->index); } -EXPORT_SYMBOL_GPL(dsa_port_phylink_mac_an_restart); -void dsa_port_phylink_mac_link_down(struct phylink_config *config, - unsigned int mode, - phy_interface_t interface) +static void dsa_port_phylink_mac_link_down(struct phylink_config *config, + unsigned int mode, + phy_interface_t interface) { struct dsa_port *dp = container_of(config, struct dsa_port, pl_config); struct phy_device *phydev = NULL; @@ -491,12 +487,11 @@ void dsa_port_phylink_mac_link_down(struct phylink_config *config, ds->ops->phylink_mac_link_down(ds, dp->index, mode, interface); } -EXPORT_SYMBOL_GPL(dsa_port_phylink_mac_link_down); -void dsa_port_phylink_mac_link_up(struct phylink_config *config, - unsigned int mode, - phy_interface_t interface, - struct phy_device *phydev) +static void dsa_port_phylink_mac_link_up(struct phylink_config *config, + unsigned int mode, + phy_interface_t interface, + struct phy_device *phydev) { struct dsa_port *dp = container_of(config, struct dsa_port, pl_config); struct dsa_switch *ds = dp->ds; @@ -509,7 +504,6 @@ void dsa_port_phylink_mac_link_up(struct phylink_config *config, ds->ops->phylink_mac_link_up(ds, dp->index, mode, interface, phydev); } -EXPORT_SYMBOL_GPL(dsa_port_phylink_mac_link_up); const struct phylink_mac_ops dsa_port_phylink_mac_ops = { .validate = dsa_port_phylink_validate, -- GitLab From 92f7440ecc9331aa1e274273d4a3cb5c58b69706 Mon Sep 17 00:00:00 2001 From: Andrii Nakryiko Date: Mon, 16 Dec 2019 22:14:25 -0800 Subject: [PATCH 0850/1437] selftests/bpf: More succinct Makefile output Similarly to bpftool/libbpf output, make selftests/bpf output succinct per-item output line. Output is roughly as follows: $ make ... CLANG-LLC [test_maps] pyperf600.o CLANG-LLC [test_maps] strobemeta.o CLANG-LLC [test_maps] pyperf100.o EXTRA-OBJ [test_progs] cgroup_helpers.o EXTRA-OBJ [test_progs] trace_helpers.o BINARY test_align BINARY test_verifier_log GEN-SKEL [test_progs] fexit_bpf2bpf.skel.h GEN-SKEL [test_progs] test_global_data.skel.h GEN-SKEL [test_progs] sendmsg6_prog.skel.h ... To see the actual command invocation, verbose mode can be turned on with V=1 argument: $ make V=1 ... very verbose output ... Signed-off-by: Andrii Nakryiko Signed-off-by: Daniel Borkmann Acked-by: Yonghong Song Link: https://lore.kernel.org/bpf/20191217061425.2346359-1-andriin@fb.com --- tools/testing/selftests/bpf/Makefile | 36 ++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/tools/testing/selftests/bpf/Makefile b/tools/testing/selftests/bpf/Makefile index ba90621617a8d..c652bd84ef0e6 100644 --- a/tools/testing/selftests/bpf/Makefile +++ b/tools/testing/selftests/bpf/Makefile @@ -77,6 +77,24 @@ TEST_GEN_PROGS_EXTENDED = test_sock_addr test_skb_cgroup_id_user \ TEST_CUSTOM_PROGS = urandom_read +# Emit succinct information message describing current building step +# $1 - generic step name (e.g., CC, LINK, etc); +# $2 - optional "flavor" specifier; if provided, will be emitted as [flavor]; +# $3 - target (assumed to be file); only file name will be emitted; +# $4 - optional extra arg, emitted as-is, if provided. +ifeq ($(V),1) +msg = +else +msg = @$(info $(1)$(if $(2), [$(2)]) $(notdir $(3)))$(if $(4), $(4)) +endif + +# override lib.mk's default rules +OVERRIDE_TARGETS := 1 +override define CLEAN + $(call msg, CLEAN) + $(RM) -r $(TEST_GEN_PROGS) $(TEST_GEN_PROGS_EXTENDED) $(TEST_GEN_FILES) $(EXTRA_CLEAN) +endef + include ../lib.mk # Define simple and short `make test_progs`, `make test_sysctl`, etc targets @@ -89,10 +107,16 @@ $(notdir $(TEST_GEN_PROGS) \ $(TEST_GEN_PROGS_EXTENDED) \ $(TEST_CUSTOM_PROGS)): %: $(OUTPUT)/% ; +$(OUTPUT)/%:%.c + $(call msg, BINARY,,$@) + $(LINK.c) $^ $(LDLIBS) -o $@ + $(OUTPUT)/urandom_read: urandom_read.c + $(call msg, BINARY,,$@) $(CC) -o $@ $< -Wl,--build-id $(OUTPUT)/test_stub.o: test_stub.c + $(call msg, CC,,$@) $(CC) -c $(CFLAGS) -o $@ $< BPFOBJ := $(OUTPUT)/libbpf.a @@ -167,24 +191,28 @@ $(OUTPUT)/flow_dissector_load.o: flow_dissector_load.h # $3 - CFLAGS # $4 - LDFLAGS define CLANG_BPF_BUILD_RULE + $(call msg, CLANG-LLC,$(TRUNNER_BINARY),$2) ($(CLANG) $3 -O2 -target bpf -emit-llvm \ -c $1 -o - || echo "BPF obj compilation failed") | \ $(LLC) -mattr=dwarfris -march=bpf -mcpu=probe $4 -filetype=obj -o $2 endef # Similar to CLANG_BPF_BUILD_RULE, but with disabled alu32 define CLANG_NOALU32_BPF_BUILD_RULE + $(call msg, CLANG-LLC,$(TRUNNER_BINARY),$2) ($(CLANG) $3 -O2 -target bpf -emit-llvm \ -c $1 -o - || echo "BPF obj compilation failed") | \ $(LLC) -march=bpf -mcpu=v2 $4 -filetype=obj -o $2 endef # Similar to CLANG_BPF_BUILD_RULE, but using native Clang and bpf LLC define CLANG_NATIVE_BPF_BUILD_RULE + $(call msg, CLANG-BPF,$(TRUNNER_BINARY),$2) ($(CLANG) $3 -O2 -emit-llvm \ -c $1 -o - || echo "BPF obj compilation failed") | \ $(LLC) -march=bpf -mcpu=probe $4 -filetype=obj -o $2 endef # Build BPF object using GCC define GCC_BPF_BUILD_RULE + $(call msg, GCC-BPF,$(TRUNNER_BINARY),$2) $(BPF_GCC) $3 $4 -O2 -c $1 -o $2 endef @@ -243,6 +271,7 @@ $(TRUNNER_BPF_OBJS): $(TRUNNER_OUTPUT)/%.o: \ $(TRUNNER_BPF_SKELS): $(TRUNNER_OUTPUT)/%.skel.h: \ $(TRUNNER_OUTPUT)/%.o \ | $(BPFTOOL) $(TRUNNER_OUTPUT) + $$(call msg, GEN-SKEL,$(TRUNNER_BINARY),$$@) $$(BPFTOOL) gen skeleton $$< > $$@ endif @@ -250,6 +279,7 @@ endif ifeq ($($(TRUNNER_TESTS_DIR)-tests-hdr),) $(TRUNNER_TESTS_DIR)-tests-hdr := y $(TRUNNER_TESTS_HDR): $(TRUNNER_TESTS_DIR)/*.c + $$(call msg, TEST-HDR,$(TRUNNER_BINARY),$$@) $$(shell ( cd $(TRUNNER_TESTS_DIR); \ echo '/* Generated header, do not edit */'; \ ls *.c 2> /dev/null | \ @@ -265,6 +295,7 @@ $(TRUNNER_TEST_OBJS): $(TRUNNER_OUTPUT)/%.test.o: \ $(TRUNNER_BPF_OBJS) \ $(TRUNNER_BPF_SKELS) \ $$(BPFOBJ) | $(TRUNNER_OUTPUT) + $$(call msg, TEST-OBJ,$(TRUNNER_BINARY),$$@) cd $$(@D) && $$(CC) $$(CFLAGS) -c $(CURDIR)/$$< $$(LDLIBS) -o $$(@F) $(TRUNNER_EXTRA_OBJS): $(TRUNNER_OUTPUT)/%.o: \ @@ -272,17 +303,20 @@ $(TRUNNER_EXTRA_OBJS): $(TRUNNER_OUTPUT)/%.o: \ $(TRUNNER_EXTRA_HDRS) \ $(TRUNNER_TESTS_HDR) \ $$(BPFOBJ) | $(TRUNNER_OUTPUT) + $$(call msg, EXTRA-OBJ,$(TRUNNER_BINARY),$$@) $$(CC) $$(CFLAGS) -c $$< $$(LDLIBS) -o $$@ # only copy extra resources if in flavored build $(TRUNNER_BINARY)-extras: $(TRUNNER_EXTRA_FILES) | $(TRUNNER_OUTPUT) ifneq ($2,) + $$(call msg, EXTRAS-CP,$(TRUNNER_BINARY),$(TRUNNER_EXTRA_FILES)) cp -a $$^ $(TRUNNER_OUTPUT)/ endif $(OUTPUT)/$(TRUNNER_BINARY): $(TRUNNER_TEST_OBJS) \ $(TRUNNER_EXTRA_OBJS) $$(BPFOBJ) \ | $(TRUNNER_BINARY)-extras + $$(call msg, BINARY,,$$@) $$(CC) $$(CFLAGS) $$(filter %.a %.o,$$^) $$(LDLIBS) -o $$@ endef @@ -334,10 +368,12 @@ verifier/tests.h: verifier/*.c echo '#endif' \ ) > verifier/tests.h) $(OUTPUT)/test_verifier: test_verifier.c verifier/tests.h $(BPFOBJ) | $(OUTPUT) + $(call msg, BINARY,,$@) $(CC) $(CFLAGS) $(filter %.a %.o %.c,$^) $(LDLIBS) -o $@ # Make sure we are able to include and link libbpf against c++. $(OUTPUT)/test_cpp: test_cpp.cpp $(BPFOBJ) + $(call msg, CXX,,$@) $(CXX) $(CFLAGS) $^ $(LDLIBS) -o $@ EXTRA_CLEAN := $(TEST_CUSTOM_PROGS) \ -- GitLab From fb9a98e160f10555936e1c7041d4efda4954d1df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Toke=20H=C3=B8iland-J=C3=B8rgensen?= Date: Tue, 17 Dec 2019 12:28:10 +0100 Subject: [PATCH 0851/1437] libbpf: Fix libbpf_common.h when installing libbpf through 'make install' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This fixes two issues with the newly introduced libbpf_common.h file: - The header failed to include for the definition of memset() - The new file was not included in the install_headers rule in the Makefile Both of these issues cause breakage when installing libbpf with 'make install' and trying to use it in applications. Fixes: 544402d4b493 ("libbpf: Extract common user-facing helpers") Signed-off-by: Toke Høiland-Jørgensen Signed-off-by: Daniel Borkmann Acked-by: Yonghong Song Link: https://lore.kernel.org/bpf/20191217112810.768078-1-toke@redhat.com --- tools/lib/bpf/Makefile | 1 + tools/lib/bpf/libbpf_common.h | 2 ++ 2 files changed, 3 insertions(+) diff --git a/tools/lib/bpf/Makefile b/tools/lib/bpf/Makefile index a3718cb275f2e..d4790121adf49 100644 --- a/tools/lib/bpf/Makefile +++ b/tools/lib/bpf/Makefile @@ -251,6 +251,7 @@ install_headers: bpf_helper_defs.h $(call do_install,libbpf.h,$(prefix)/include/bpf,644); \ $(call do_install,btf.h,$(prefix)/include/bpf,644); \ $(call do_install,libbpf_util.h,$(prefix)/include/bpf,644); \ + $(call do_install,libbpf_common.h,$(prefix)/include/bpf,644); \ $(call do_install,xsk.h,$(prefix)/include/bpf,644); \ $(call do_install,bpf_helpers.h,$(prefix)/include/bpf,644); \ $(call do_install,bpf_helper_defs.h,$(prefix)/include/bpf,644); \ diff --git a/tools/lib/bpf/libbpf_common.h b/tools/lib/bpf/libbpf_common.h index 4fb833840961f..a23ae1ac27ebc 100644 --- a/tools/lib/bpf/libbpf_common.h +++ b/tools/lib/bpf/libbpf_common.h @@ -9,6 +9,8 @@ #ifndef __LIBBPF_LIBBPF_COMMON_H #define __LIBBPF_LIBBPF_COMMON_H +#include + #ifndef LIBBPF_API #define LIBBPF_API __attribute__((visibility("default"))) #endif -- GitLab From dbce0b65046d1735d7054c54ec2387dba84ba258 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Tue, 17 Dec 2019 10:27:12 -0600 Subject: [PATCH 0852/1437] dt-bindings: Add missing 'properties' keyword enclosing 'snps,tso' DT property definitions must be under a 'properties' keyword. This was missing for 'snps,tso' in an if/then clause. A meta-schema fix will catch future errors like this. Fixes: 7db3545aef5f ("dt-bindings: net: stmmac: Convert the binding to a schemas") Cc: "David S. Miller" Acked-by: Maxime Ripard Signed-off-by: Rob Herring --- Documentation/devicetree/bindings/net/snps,dwmac.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/net/snps,dwmac.yaml b/Documentation/devicetree/bindings/net/snps,dwmac.yaml index 4845e29411e46..e08cd4c4d5682 100644 --- a/Documentation/devicetree/bindings/net/snps,dwmac.yaml +++ b/Documentation/devicetree/bindings/net/snps,dwmac.yaml @@ -347,6 +347,7 @@ allOf: - st,spear600-gmac then: + properties: snps,tso: $ref: /schemas/types.yaml#definitions/flag description: -- GitLab From 4a3d6c6a6e4d652808729e7a2a8c3774a5a5c814 Mon Sep 17 00:00:00 2001 From: Andrii Nakryiko Date: Tue, 17 Dec 2019 15:42:28 -0800 Subject: [PATCH 0853/1437] libbpf: Reduce log level for custom section names Libbpf is trying to recognize BPF program type based on its section name during bpf_object__open() phase. This is not strictly enforced and user code has ability to specify/override correct BPF program type after open. But if BPF program is using custom section name, libbpf will still emit warnings, which can be quite annoying to users. This patch reduces log level of information messages emitted by libbpf if section name is not canonical. User can still get a list of all supported section names as debug-level message. Signed-off-by: Andrii Nakryiko Signed-off-by: Alexei Starovoitov Link: https://lore.kernel.org/bpf/20191217234228.1739308-1-andriin@fb.com --- tools/lib/bpf/libbpf.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c index 3fe42d6b0c2ff..906bbbf7b2e42 100644 --- a/tools/lib/bpf/libbpf.c +++ b/tools/lib/bpf/libbpf.c @@ -5883,7 +5883,7 @@ int libbpf_prog_type_by_name(const char *name, enum bpf_prog_type *prog_type, return 0; } - pr_warn("failed to guess program type from ELF section '%s'\n", name); + pr_debug("failed to guess program type from ELF section '%s'\n", name); type_names = libbpf_get_type_names(false); if (type_names != NULL) { pr_debug("supported section(type) names are:%s\n", type_names); @@ -6001,10 +6001,10 @@ int libbpf_attach_type_by_name(const char *name, *attach_type = section_defs[i].attach_type; return 0; } - pr_warn("failed to guess attach type based on ELF section name '%s'\n", name); + pr_debug("failed to guess attach type based on ELF section name '%s'\n", name); type_names = libbpf_get_type_names(true); if (type_names != NULL) { - pr_info("attachable section(type) names are:%s\n", type_names); + pr_debug("attachable section(type) names are:%s\n", type_names); free(type_names); } -- GitLab From 525b305d61ede489ce2118b000a5dabd6d869dac Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Tue, 17 Dec 2019 14:13:37 -0700 Subject: [PATCH 0854/1437] io-wq: re-add io_wq_current_is_worker() This reverts commit 8cdda87a4414, we now have several use csaes for this helper. Reinstate it. Signed-off-by: Jens Axboe --- fs/io-wq.h | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/fs/io-wq.h b/fs/io-wq.h index fb993b2bd0ef0..3f5e356de9805 100644 --- a/fs/io-wq.h +++ b/fs/io-wq.h @@ -120,6 +120,10 @@ static inline void io_wq_worker_sleeping(struct task_struct *tsk) static inline void io_wq_worker_running(struct task_struct *tsk) { } -#endif /* CONFIG_IO_WQ */ +#endif -#endif /* INTERNAL_IO_WQ_H */ +static inline bool io_wq_current_is_worker(void) +{ + return in_task() && (current->flags & PF_IO_WORKER); +} +#endif -- GitLab From b7bb4f7da0a1a92f142697f1c9ce335e7a44f4b1 Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Sun, 15 Dec 2019 22:13:43 -0700 Subject: [PATCH 0855/1437] io_uring: fix pre-prepped issue with force_nonblock == true Some of these code paths assume that any force_nonblock == true issue is not prepped, but that's not true if we did prep as part of link setup earlier. Check if we already have an async context allocate before setting up a new one. Cleanup the async context setup in general, we have a lot of duplicated code there. Fixes: 03b1230ca12a ("io_uring: ensure async punted sendmsg/recvmsg requests copy data") Fixes: f67676d160c6 ("io_uring: ensure async punted read/write requests copy iovec") Signed-off-by: Jens Axboe --- fs/io_uring.c | 175 ++++++++++++++++++++++++++++---------------------- 1 file changed, 98 insertions(+), 77 deletions(-) diff --git a/fs/io_uring.c b/fs/io_uring.c index 0e01cdc8a120a..582c7c19bdd75 100644 --- a/fs/io_uring.c +++ b/fs/io_uring.c @@ -1701,7 +1701,7 @@ static ssize_t loop_rw_iter(int rw, struct file *file, struct kiocb *kiocb, return ret; } -static void io_req_map_io(struct io_kiocb *req, ssize_t io_size, +static void io_req_map_rw(struct io_kiocb *req, ssize_t io_size, struct iovec *iovec, struct iovec *fast_iov, struct iov_iter *iter) { @@ -1715,19 +1715,39 @@ static void io_req_map_io(struct io_kiocb *req, ssize_t io_size, } } -static int io_setup_async_io(struct io_kiocb *req, ssize_t io_size, - struct iovec *iovec, struct iovec *fast_iov, - struct iov_iter *iter) +static int io_alloc_async_ctx(struct io_kiocb *req) { req->io = kmalloc(sizeof(*req->io), GFP_KERNEL); if (req->io) { - io_req_map_io(req, io_size, iovec, fast_iov, iter); memcpy(&req->io->sqe, req->sqe, sizeof(req->io->sqe)); req->sqe = &req->io->sqe; return 0; } - return -ENOMEM; + return 1; +} + +static void io_rw_async(struct io_wq_work **workptr) +{ + struct io_kiocb *req = container_of(*workptr, struct io_kiocb, work); + struct iovec *iov = NULL; + + if (req->io->rw.iov != req->io->rw.fast_iov) + iov = req->io->rw.iov; + io_wq_submit_work(workptr); + kfree(iov); +} + +static int io_setup_async_rw(struct io_kiocb *req, ssize_t io_size, + struct iovec *iovec, struct iovec *fast_iov, + struct iov_iter *iter) +{ + if (!req->io && io_alloc_async_ctx(req)) + return -ENOMEM; + + io_req_map_rw(req, io_size, iovec, fast_iov, iter); + req->work.func = io_rw_async; + return 0; } static int io_read_prep(struct io_kiocb *req, struct iovec **iovec, @@ -1806,7 +1826,7 @@ static int io_read(struct io_kiocb *req, struct io_kiocb **nxt, kiocb_done(kiocb, ret2, nxt, req->in_async); } else { copy_iov: - ret = io_setup_async_io(req, io_size, iovec, + ret = io_setup_async_rw(req, io_size, iovec, inline_vecs, &iter); if (ret) goto out_free; @@ -1814,7 +1834,8 @@ copy_iov: } } out_free: - kfree(iovec); + if (!io_wq_current_is_worker()) + kfree(iovec); return ret; } @@ -1900,7 +1921,7 @@ static int io_write(struct io_kiocb *req, struct io_kiocb **nxt, kiocb_done(kiocb, ret2, nxt, req->in_async); } else { copy_iov: - ret = io_setup_async_io(req, io_size, iovec, + ret = io_setup_async_rw(req, io_size, iovec, inline_vecs, &iter); if (ret) goto out_free; @@ -1908,7 +1929,8 @@ copy_iov: } } out_free: - kfree(iovec); + if (!io_wq_current_is_worker()) + kfree(iovec); return ret; } @@ -2021,6 +2043,19 @@ static int io_sync_file_range(struct io_kiocb *req, return 0; } +#if defined(CONFIG_NET) +static void io_sendrecv_async(struct io_wq_work **workptr) +{ + struct io_kiocb *req = container_of(*workptr, struct io_kiocb, work); + struct iovec *iov = NULL; + + if (req->io->rw.iov != req->io->rw.fast_iov) + iov = req->io->msg.iov; + io_wq_submit_work(workptr); + kfree(iov); +} +#endif + static int io_sendmsg_prep(struct io_kiocb *req, struct io_async_ctx *io) { #if defined(CONFIG_NET) @@ -2050,7 +2085,7 @@ static int io_sendmsg(struct io_kiocb *req, const struct io_uring_sqe *sqe, sock = sock_from_file(req->file, &ret); if (sock) { - struct io_async_ctx io, *copy; + struct io_async_ctx io; struct sockaddr_storage addr; unsigned flags; @@ -2077,15 +2112,12 @@ static int io_sendmsg(struct io_kiocb *req, const struct io_uring_sqe *sqe, ret = __sys_sendmsg_sock(sock, &kmsg->msg, flags); if (force_nonblock && ret == -EAGAIN) { - copy = kmalloc(sizeof(*copy), GFP_KERNEL); - if (!copy) { - ret = -ENOMEM; - goto out; - } - memcpy(©->msg, &io.msg, sizeof(copy->msg)); - req->io = copy; - memcpy(&req->io->sqe, req->sqe, sizeof(*req->sqe)); - req->sqe = &req->io->sqe; + if (req->io) + return -EAGAIN; + if (io_alloc_async_ctx(req)) + return -ENOMEM; + memcpy(&req->io->msg, &io.msg, sizeof(io.msg)); + req->work.func = io_sendrecv_async; return -EAGAIN; } if (ret == -ERESTARTSYS) @@ -2093,7 +2125,7 @@ static int io_sendmsg(struct io_kiocb *req, const struct io_uring_sqe *sqe, } out: - if (kmsg && kmsg->iov != kmsg->fast_iov) + if (!io_wq_current_is_worker() && kmsg && kmsg->iov != kmsg->fast_iov) kfree(kmsg->iov); io_cqring_add_event(req, ret); if (ret < 0) @@ -2136,7 +2168,7 @@ static int io_recvmsg(struct io_kiocb *req, const struct io_uring_sqe *sqe, sock = sock_from_file(req->file, &ret); if (sock) { struct user_msghdr __user *msg; - struct io_async_ctx io, *copy; + struct io_async_ctx io; struct sockaddr_storage addr; unsigned flags; @@ -2165,15 +2197,12 @@ static int io_recvmsg(struct io_kiocb *req, const struct io_uring_sqe *sqe, ret = __sys_recvmsg_sock(sock, &kmsg->msg, msg, kmsg->uaddr, flags); if (force_nonblock && ret == -EAGAIN) { - copy = kmalloc(sizeof(*copy), GFP_KERNEL); - if (!copy) { - ret = -ENOMEM; - goto out; - } - memcpy(copy, &io, sizeof(*copy)); - req->io = copy; - memcpy(&req->io->sqe, req->sqe, sizeof(*req->sqe)); - req->sqe = &req->io->sqe; + if (req->io) + return -EAGAIN; + if (io_alloc_async_ctx(req)) + return -ENOMEM; + memcpy(&req->io->msg, &io.msg, sizeof(io.msg)); + req->work.func = io_sendrecv_async; return -EAGAIN; } if (ret == -ERESTARTSYS) @@ -2181,7 +2210,7 @@ static int io_recvmsg(struct io_kiocb *req, const struct io_uring_sqe *sqe, } out: - if (kmsg && kmsg->iov != kmsg->fast_iov) + if (!io_wq_current_is_worker() && kmsg && kmsg->iov != kmsg->fast_iov) kfree(kmsg->iov); io_cqring_add_event(req, ret); if (ret < 0) @@ -2272,15 +2301,13 @@ static int io_connect(struct io_kiocb *req, const struct io_uring_sqe *sqe, ret = __sys_connect_file(req->file, &io->connect.address, addr_len, file_flags); if ((ret == -EAGAIN || ret == -EINPROGRESS) && force_nonblock) { - io = kmalloc(sizeof(*io), GFP_KERNEL); - if (!io) { + if (req->io) + return -EAGAIN; + if (io_alloc_async_ctx(req)) { ret = -ENOMEM; goto out; } - memcpy(&io->connect, &__io.connect, sizeof(io->connect)); - req->io = io; - memcpy(&io->sqe, req->sqe, sizeof(*req->sqe)); - req->sqe = &io->sqe; + memcpy(&req->io->connect, &__io.connect, sizeof(__io.connect)); return -EAGAIN; } if (ret == -ERESTARTSYS) @@ -2511,7 +2538,6 @@ static int io_poll_add(struct io_kiocb *req, const struct io_uring_sqe *sqe, if (!poll->file) return -EBADF; - req->io = NULL; INIT_IO_WORK(&req->work, io_poll_complete_work); events = READ_ONCE(sqe->poll_events); poll->events = demangle_poll(events) | EPOLLERR | EPOLLHUP; @@ -2692,7 +2718,6 @@ static int io_timeout_prep(struct io_kiocb *req, struct io_async_ctx *io, data->mode = HRTIMER_MODE_REL; hrtimer_init(&data->timer, CLOCK_MONOTONIC, data->mode); - req->io = io; return 0; } @@ -2701,22 +2726,16 @@ static int io_timeout(struct io_kiocb *req, const struct io_uring_sqe *sqe) unsigned count; struct io_ring_ctx *ctx = req->ctx; struct io_timeout_data *data; - struct io_async_ctx *io; struct list_head *entry; unsigned span = 0; + int ret; - io = req->io; - if (!io) { - int ret; - - io = kmalloc(sizeof(*io), GFP_KERNEL); - if (!io) + if (!req->io) { + if (io_alloc_async_ctx(req)) return -ENOMEM; - ret = io_timeout_prep(req, io, false); - if (ret) { - kfree(io); + ret = io_timeout_prep(req, req->io, false); + if (ret) return ret; - } } data = &req->io->timeout; @@ -2858,23 +2877,35 @@ static int io_async_cancel(struct io_kiocb *req, const struct io_uring_sqe *sqe, return 0; } -static int io_req_defer_prep(struct io_kiocb *req, struct io_async_ctx *io) +static int io_req_defer_prep(struct io_kiocb *req) { struct iovec inline_vecs[UIO_FASTIOV], *iovec = inline_vecs; + struct io_async_ctx *io = req->io; struct iov_iter iter; ssize_t ret; - memcpy(&io->sqe, req->sqe, sizeof(io->sqe)); - req->sqe = &io->sqe; - switch (io->sqe.opcode) { case IORING_OP_READV: case IORING_OP_READ_FIXED: + /* ensure prep does right import */ + req->io = NULL; ret = io_read_prep(req, &iovec, &iter, true); + req->io = io; + if (ret < 0) + break; + io_req_map_rw(req, ret, iovec, inline_vecs, &iter); + ret = 0; break; case IORING_OP_WRITEV: case IORING_OP_WRITE_FIXED: + /* ensure prep does right import */ + req->io = NULL; ret = io_write_prep(req, &iovec, &iter, true); + req->io = io; + if (ret < 0) + break; + io_req_map_rw(req, ret, iovec, inline_vecs, &iter); + ret = 0; break; case IORING_OP_SENDMSG: ret = io_sendmsg_prep(req, io); @@ -2886,41 +2917,34 @@ static int io_req_defer_prep(struct io_kiocb *req, struct io_async_ctx *io) ret = io_connect_prep(req, io); break; case IORING_OP_TIMEOUT: - return io_timeout_prep(req, io, false); + ret = io_timeout_prep(req, io, false); + break; case IORING_OP_LINK_TIMEOUT: - return io_timeout_prep(req, io, true); + ret = io_timeout_prep(req, io, true); + break; default: - req->io = io; - return 0; + ret = 0; + break; } - if (ret < 0) - return ret; - - req->io = io; - io_req_map_io(req, ret, iovec, inline_vecs, &iter); - return 0; + return ret; } static int io_req_defer(struct io_kiocb *req) { struct io_ring_ctx *ctx = req->ctx; - struct io_async_ctx *io; int ret; /* Still need defer if there is pending req in defer list. */ if (!req_need_defer(req) && list_empty(&ctx->defer_list)) return 0; - io = kmalloc(sizeof(*io), GFP_KERNEL); - if (!io) + if (io_alloc_async_ctx(req)) return -EAGAIN; - ret = io_req_defer_prep(req, io); - if (ret < 0) { - kfree(io); + ret = io_req_defer_prep(req); + if (ret < 0) return ret; - } spin_lock_irq(&ctx->completion_lock); if (!req_need_defer(req) && list_empty(&ctx->defer_list)) { @@ -3366,7 +3390,6 @@ err_req: */ if (*link) { struct io_kiocb *prev = *link; - struct io_async_ctx *io; if (req->sqe->flags & IOSQE_IO_DRAIN) (*link)->flags |= REQ_F_DRAIN_LINK | REQ_F_IO_DRAIN; @@ -3374,15 +3397,13 @@ err_req: if (req->sqe->flags & IOSQE_IO_HARDLINK) req->flags |= REQ_F_HARDLINK; - io = kmalloc(sizeof(*io), GFP_KERNEL); - if (!io) { + if (io_alloc_async_ctx(req)) { ret = -EAGAIN; goto err_req; } - ret = io_req_defer_prep(req, io); + ret = io_req_defer_prep(req); if (ret) { - kfree(io); /* fail even hard links since we don't submit */ prev->flags |= REQ_F_FAIL_LINK; goto err_req; -- GitLab From fc4df999e24fc3006441acd4ce6250e6a76ac851 Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Tue, 10 Dec 2019 14:38:45 -0700 Subject: [PATCH 0856/1437] io_uring: remove 'sqe' parameter to the OP helpers that take it We pass in req->sqe for all of them, no need to pass it in as the request is always passed in. This is a necessary prep patch to be able to cleanup/fix the request prep path. Signed-off-by: Jens Axboe --- fs/io_uring.c | 80 ++++++++++++++++++++++++++++----------------------- 1 file changed, 44 insertions(+), 36 deletions(-) diff --git a/fs/io_uring.c b/fs/io_uring.c index 582c7c19bdd75..0298dd0abac09 100644 --- a/fs/io_uring.c +++ b/fs/io_uring.c @@ -1949,8 +1949,9 @@ static int io_nop(struct io_kiocb *req) return 0; } -static int io_prep_fsync(struct io_kiocb *req, const struct io_uring_sqe *sqe) +static int io_prep_fsync(struct io_kiocb *req) { + const struct io_uring_sqe *sqe = req->sqe; struct io_ring_ctx *ctx = req->ctx; if (!req->file) @@ -1964,9 +1965,10 @@ static int io_prep_fsync(struct io_kiocb *req, const struct io_uring_sqe *sqe) return 0; } -static int io_fsync(struct io_kiocb *req, const struct io_uring_sqe *sqe, - struct io_kiocb **nxt, bool force_nonblock) +static int io_fsync(struct io_kiocb *req, struct io_kiocb **nxt, + bool force_nonblock) { + const struct io_uring_sqe *sqe = req->sqe; loff_t sqe_off = READ_ONCE(sqe->off); loff_t sqe_len = READ_ONCE(sqe->len); loff_t end = sqe_off + sqe_len; @@ -1977,7 +1979,7 @@ static int io_fsync(struct io_kiocb *req, const struct io_uring_sqe *sqe, if (unlikely(fsync_flags & ~IORING_FSYNC_DATASYNC)) return -EINVAL; - ret = io_prep_fsync(req, sqe); + ret = io_prep_fsync(req); if (ret) return ret; @@ -1996,8 +1998,9 @@ static int io_fsync(struct io_kiocb *req, const struct io_uring_sqe *sqe, return 0; } -static int io_prep_sfr(struct io_kiocb *req, const struct io_uring_sqe *sqe) +static int io_prep_sfr(struct io_kiocb *req) { + const struct io_uring_sqe *sqe = req->sqe; struct io_ring_ctx *ctx = req->ctx; int ret = 0; @@ -2012,17 +2015,16 @@ static int io_prep_sfr(struct io_kiocb *req, const struct io_uring_sqe *sqe) return ret; } -static int io_sync_file_range(struct io_kiocb *req, - const struct io_uring_sqe *sqe, - struct io_kiocb **nxt, +static int io_sync_file_range(struct io_kiocb *req, struct io_kiocb **nxt, bool force_nonblock) { + const struct io_uring_sqe *sqe = req->sqe; loff_t sqe_off; loff_t sqe_len; unsigned flags; int ret; - ret = io_prep_sfr(req, sqe); + ret = io_prep_sfr(req); if (ret) return ret; @@ -2072,10 +2074,11 @@ static int io_sendmsg_prep(struct io_kiocb *req, struct io_async_ctx *io) #endif } -static int io_sendmsg(struct io_kiocb *req, const struct io_uring_sqe *sqe, - struct io_kiocb **nxt, bool force_nonblock) +static int io_sendmsg(struct io_kiocb *req, struct io_kiocb **nxt, + bool force_nonblock) { #if defined(CONFIG_NET) + const struct io_uring_sqe *sqe = req->sqe; struct io_async_msghdr *kmsg = NULL; struct socket *sock; int ret; @@ -2154,10 +2157,11 @@ static int io_recvmsg_prep(struct io_kiocb *req, struct io_async_ctx *io) #endif } -static int io_recvmsg(struct io_kiocb *req, const struct io_uring_sqe *sqe, - struct io_kiocb **nxt, bool force_nonblock) +static int io_recvmsg(struct io_kiocb *req, struct io_kiocb **nxt, + bool force_nonblock) { #if defined(CONFIG_NET) + const struct io_uring_sqe *sqe = req->sqe; struct io_async_msghdr *kmsg = NULL; struct socket *sock; int ret; @@ -2222,10 +2226,11 @@ out: #endif } -static int io_accept(struct io_kiocb *req, const struct io_uring_sqe *sqe, - struct io_kiocb **nxt, bool force_nonblock) +static int io_accept(struct io_kiocb *req, struct io_kiocb **nxt, + bool force_nonblock) { #if defined(CONFIG_NET) + const struct io_uring_sqe *sqe = req->sqe; struct sockaddr __user *addr; int __user *addr_len; unsigned file_flags; @@ -2273,10 +2278,11 @@ static int io_connect_prep(struct io_kiocb *req, struct io_async_ctx *io) #endif } -static int io_connect(struct io_kiocb *req, const struct io_uring_sqe *sqe, - struct io_kiocb **nxt, bool force_nonblock) +static int io_connect(struct io_kiocb *req, struct io_kiocb **nxt, + bool force_nonblock) { #if defined(CONFIG_NET) + const struct io_uring_sqe *sqe = req->sqe; struct io_async_ctx __io, *io; unsigned file_flags; int addr_len, ret; @@ -2374,8 +2380,9 @@ static int io_poll_cancel(struct io_ring_ctx *ctx, __u64 sqe_addr) * Find a running poll command that matches one specified in sqe->addr, * and remove it if found. */ -static int io_poll_remove(struct io_kiocb *req, const struct io_uring_sqe *sqe) +static int io_poll_remove(struct io_kiocb *req) { + const struct io_uring_sqe *sqe = req->sqe; struct io_ring_ctx *ctx = req->ctx; int ret; @@ -2521,9 +2528,9 @@ static void io_poll_req_insert(struct io_kiocb *req) hlist_add_head(&req->hash_node, list); } -static int io_poll_add(struct io_kiocb *req, const struct io_uring_sqe *sqe, - struct io_kiocb **nxt) +static int io_poll_add(struct io_kiocb *req, struct io_kiocb **nxt) { + const struct io_uring_sqe *sqe = req->sqe; struct io_poll_iocb *poll = &req->poll; struct io_ring_ctx *ctx = req->ctx; struct io_poll_table ipt; @@ -2660,9 +2667,9 @@ static int io_timeout_cancel(struct io_ring_ctx *ctx, __u64 user_data) /* * Remove or update an existing timeout command */ -static int io_timeout_remove(struct io_kiocb *req, - const struct io_uring_sqe *sqe) +static int io_timeout_remove(struct io_kiocb *req) { + const struct io_uring_sqe *sqe = req->sqe; struct io_ring_ctx *ctx = req->ctx; unsigned flags; int ret; @@ -2721,8 +2728,9 @@ static int io_timeout_prep(struct io_kiocb *req, struct io_async_ctx *io, return 0; } -static int io_timeout(struct io_kiocb *req, const struct io_uring_sqe *sqe) +static int io_timeout(struct io_kiocb *req) { + const struct io_uring_sqe *sqe = req->sqe; unsigned count; struct io_ring_ctx *ctx = req->ctx; struct io_timeout_data *data; @@ -2862,9 +2870,9 @@ done: io_put_req_find_next(req, nxt); } -static int io_async_cancel(struct io_kiocb *req, const struct io_uring_sqe *sqe, - struct io_kiocb **nxt) +static int io_async_cancel(struct io_kiocb *req, struct io_kiocb **nxt) { + const struct io_uring_sqe *sqe = req->sqe; struct io_ring_ctx *ctx = req->ctx; if (unlikely(ctx->flags & IORING_SETUP_IOPOLL)) @@ -2987,37 +2995,37 @@ static int io_issue_sqe(struct io_kiocb *req, struct io_kiocb **nxt, ret = io_write(req, nxt, force_nonblock); break; case IORING_OP_FSYNC: - ret = io_fsync(req, req->sqe, nxt, force_nonblock); + ret = io_fsync(req, nxt, force_nonblock); break; case IORING_OP_POLL_ADD: - ret = io_poll_add(req, req->sqe, nxt); + ret = io_poll_add(req, nxt); break; case IORING_OP_POLL_REMOVE: - ret = io_poll_remove(req, req->sqe); + ret = io_poll_remove(req); break; case IORING_OP_SYNC_FILE_RANGE: - ret = io_sync_file_range(req, req->sqe, nxt, force_nonblock); + ret = io_sync_file_range(req, nxt, force_nonblock); break; case IORING_OP_SENDMSG: - ret = io_sendmsg(req, req->sqe, nxt, force_nonblock); + ret = io_sendmsg(req, nxt, force_nonblock); break; case IORING_OP_RECVMSG: - ret = io_recvmsg(req, req->sqe, nxt, force_nonblock); + ret = io_recvmsg(req, nxt, force_nonblock); break; case IORING_OP_TIMEOUT: - ret = io_timeout(req, req->sqe); + ret = io_timeout(req); break; case IORING_OP_TIMEOUT_REMOVE: - ret = io_timeout_remove(req, req->sqe); + ret = io_timeout_remove(req); break; case IORING_OP_ACCEPT: - ret = io_accept(req, req->sqe, nxt, force_nonblock); + ret = io_accept(req, nxt, force_nonblock); break; case IORING_OP_CONNECT: - ret = io_connect(req, req->sqe, nxt, force_nonblock); + ret = io_connect(req, nxt, force_nonblock); break; case IORING_OP_ASYNC_CANCEL: - ret = io_async_cancel(req, req->sqe, nxt); + ret = io_async_cancel(req, nxt); break; default: ret = -EINVAL; -- GitLab From 8ed8d3c3bc32bf5b442c9f54013b4a47d5cae740 Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Mon, 16 Dec 2019 11:55:28 -0700 Subject: [PATCH 0857/1437] io_uring: any deferred command must have stable sqe data We're currently not retaining sqe data for accept, fsync, and sync_file_range. None of these commands need data outside of what is directly provided, hence it can't go stale when the request is deferred. However, it can get reused, if an application reuses SQE entries. Ensure that we retain the information we need and only read the sqe contents once, off the submission path. Most of this is just moving code into a prep and finish function. Signed-off-by: Jens Axboe --- fs/io_uring.c | 221 +++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 172 insertions(+), 49 deletions(-) diff --git a/fs/io_uring.c b/fs/io_uring.c index 0298dd0abac09..67e1758bc937b 100644 --- a/fs/io_uring.c +++ b/fs/io_uring.c @@ -304,6 +304,20 @@ struct io_timeout_data { u32 seq_offset; }; +struct io_accept { + struct file *file; + struct sockaddr __user *addr; + int __user *addr_len; + int flags; +}; + +struct io_sync { + struct file *file; + loff_t len; + loff_t off; + int flags; +}; + struct io_async_connect { struct sockaddr_storage address; }; @@ -343,6 +357,8 @@ struct io_kiocb { struct file *file; struct kiocb rw; struct io_poll_iocb poll; + struct io_accept accept; + struct io_sync sync; }; const struct io_uring_sqe *sqe; @@ -378,6 +394,7 @@ struct io_kiocb { #define REQ_F_INFLIGHT 16384 /* on inflight list */ #define REQ_F_COMP_LOCKED 32768 /* completion under lock */ #define REQ_F_HARDLINK 65536 /* doesn't sever on completion < 0 */ +#define REQ_F_PREPPED 131072 /* request already opcode prepared */ u64 user_data; u32 result; u32 sequence; @@ -1954,6 +1971,8 @@ static int io_prep_fsync(struct io_kiocb *req) const struct io_uring_sqe *sqe = req->sqe; struct io_ring_ctx *ctx = req->ctx; + if (req->flags & REQ_F_PREPPED) + return 0; if (!req->file) return -EBADF; @@ -1962,39 +1981,70 @@ static int io_prep_fsync(struct io_kiocb *req) if (unlikely(sqe->addr || sqe->ioprio || sqe->buf_index)) return -EINVAL; + req->sync.flags = READ_ONCE(sqe->fsync_flags); + if (unlikely(req->sync.flags & ~IORING_FSYNC_DATASYNC)) + return -EINVAL; + + req->sync.off = READ_ONCE(sqe->off); + req->sync.len = READ_ONCE(sqe->len); + req->flags |= REQ_F_PREPPED; return 0; } +static bool io_req_cancelled(struct io_kiocb *req) +{ + if (req->work.flags & IO_WQ_WORK_CANCEL) { + req_set_fail_links(req); + io_cqring_add_event(req, -ECANCELED); + io_put_req(req); + return true; + } + + return false; +} + +static void io_fsync_finish(struct io_wq_work **workptr) +{ + struct io_kiocb *req = container_of(*workptr, struct io_kiocb, work); + loff_t end = req->sync.off + req->sync.len; + struct io_kiocb *nxt = NULL; + int ret; + + if (io_req_cancelled(req)) + return; + + ret = vfs_fsync_range(req->rw.ki_filp, req->sync.off, + end > 0 ? end : LLONG_MAX, + req->sync.flags & IORING_FSYNC_DATASYNC); + if (ret < 0) + req_set_fail_links(req); + io_cqring_add_event(req, ret); + io_put_req_find_next(req, &nxt); + if (nxt) + *workptr = &nxt->work; +} + static int io_fsync(struct io_kiocb *req, struct io_kiocb **nxt, bool force_nonblock) { - const struct io_uring_sqe *sqe = req->sqe; - loff_t sqe_off = READ_ONCE(sqe->off); - loff_t sqe_len = READ_ONCE(sqe->len); - loff_t end = sqe_off + sqe_len; - unsigned fsync_flags; + struct io_wq_work *work, *old_work; int ret; - fsync_flags = READ_ONCE(sqe->fsync_flags); - if (unlikely(fsync_flags & ~IORING_FSYNC_DATASYNC)) - return -EINVAL; - ret = io_prep_fsync(req); if (ret) return ret; /* fsync always requires a blocking context */ - if (force_nonblock) + if (force_nonblock) { + io_put_req(req); + req->work.func = io_fsync_finish; return -EAGAIN; + } - ret = vfs_fsync_range(req->rw.ki_filp, sqe_off, - end > 0 ? end : LLONG_MAX, - fsync_flags & IORING_FSYNC_DATASYNC); - - if (ret < 0) - req_set_fail_links(req); - io_cqring_add_event(req, ret); - io_put_req_find_next(req, nxt); + work = old_work = &req->work; + io_fsync_finish(&work); + if (work && work != old_work) + *nxt = container_of(work, struct io_kiocb, work); return 0; } @@ -2002,8 +2052,9 @@ static int io_prep_sfr(struct io_kiocb *req) { const struct io_uring_sqe *sqe = req->sqe; struct io_ring_ctx *ctx = req->ctx; - int ret = 0; + if (req->flags & REQ_F_PREPPED) + return 0; if (!req->file) return -EBADF; @@ -2012,16 +2063,36 @@ static int io_prep_sfr(struct io_kiocb *req) if (unlikely(sqe->addr || sqe->ioprio || sqe->buf_index)) return -EINVAL; - return ret; + req->sync.off = READ_ONCE(sqe->off); + req->sync.len = READ_ONCE(sqe->len); + req->sync.flags = READ_ONCE(sqe->sync_range_flags); + req->flags |= REQ_F_PREPPED; + return 0; +} + +static void io_sync_file_range_finish(struct io_wq_work **workptr) +{ + struct io_kiocb *req = container_of(*workptr, struct io_kiocb, work); + struct io_kiocb *nxt = NULL; + int ret; + + if (io_req_cancelled(req)) + return; + + ret = sync_file_range(req->rw.ki_filp, req->sync.off, req->sync.len, + req->sync.flags); + if (ret < 0) + req_set_fail_links(req); + io_cqring_add_event(req, ret); + io_put_req_find_next(req, &nxt); + if (nxt) + *workptr = &nxt->work; } static int io_sync_file_range(struct io_kiocb *req, struct io_kiocb **nxt, bool force_nonblock) { - const struct io_uring_sqe *sqe = req->sqe; - loff_t sqe_off; - loff_t sqe_len; - unsigned flags; + struct io_wq_work *work, *old_work; int ret; ret = io_prep_sfr(req); @@ -2029,19 +2100,16 @@ static int io_sync_file_range(struct io_kiocb *req, struct io_kiocb **nxt, return ret; /* sync_file_range always requires a blocking context */ - if (force_nonblock) + if (force_nonblock) { + io_put_req(req); + req->work.func = io_sync_file_range_finish; return -EAGAIN; + } - sqe_off = READ_ONCE(sqe->off); - sqe_len = READ_ONCE(sqe->len); - flags = READ_ONCE(sqe->sync_range_flags); - - ret = sync_file_range(req->rw.ki_filp, sqe_off, sqe_len, flags); - - if (ret < 0) - req_set_fail_links(req); - io_cqring_add_event(req, ret); - io_put_req_find_next(req, nxt); + work = old_work = &req->work; + io_sync_file_range_finish(&work); + if (work && work != old_work) + *nxt = container_of(work, struct io_kiocb, work); return 0; } @@ -2226,31 +2294,44 @@ out: #endif } -static int io_accept(struct io_kiocb *req, struct io_kiocb **nxt, - bool force_nonblock) +static int io_accept_prep(struct io_kiocb *req) { #if defined(CONFIG_NET) const struct io_uring_sqe *sqe = req->sqe; - struct sockaddr __user *addr; - int __user *addr_len; - unsigned file_flags; - int flags, ret; + struct io_accept *accept = &req->accept; + + if (req->flags & REQ_F_PREPPED) + return 0; if (unlikely(req->ctx->flags & (IORING_SETUP_IOPOLL|IORING_SETUP_SQPOLL))) return -EINVAL; if (sqe->ioprio || sqe->len || sqe->buf_index) return -EINVAL; - addr = (struct sockaddr __user *) (unsigned long) READ_ONCE(sqe->addr); - addr_len = (int __user *) (unsigned long) READ_ONCE(sqe->addr2); - flags = READ_ONCE(sqe->accept_flags); - file_flags = force_nonblock ? O_NONBLOCK : 0; + accept->addr = (struct sockaddr __user *) + (unsigned long) READ_ONCE(sqe->addr); + accept->addr_len = (int __user *) (unsigned long) READ_ONCE(sqe->addr2); + accept->flags = READ_ONCE(sqe->accept_flags); + req->flags |= REQ_F_PREPPED; + return 0; +#else + return -EOPNOTSUPP; +#endif +} - ret = __sys_accept4_file(req->file, file_flags, addr, addr_len, flags); - if (ret == -EAGAIN && force_nonblock) { - req->work.flags |= IO_WQ_WORK_NEEDS_FILES; +#if defined(CONFIG_NET) +static int __io_accept(struct io_kiocb *req, struct io_kiocb **nxt, + bool force_nonblock) +{ + struct io_accept *accept = &req->accept; + unsigned file_flags; + int ret; + + file_flags = force_nonblock ? O_NONBLOCK : 0; + ret = __sys_accept4_file(req->file, file_flags, accept->addr, + accept->addr_len, accept->flags); + if (ret == -EAGAIN && force_nonblock) return -EAGAIN; - } if (ret == -ERESTARTSYS) ret = -EINTR; if (ret < 0) @@ -2258,6 +2339,39 @@ static int io_accept(struct io_kiocb *req, struct io_kiocb **nxt, io_cqring_add_event(req, ret); io_put_req_find_next(req, nxt); return 0; +} + +static void io_accept_finish(struct io_wq_work **workptr) +{ + struct io_kiocb *req = container_of(*workptr, struct io_kiocb, work); + struct io_kiocb *nxt = NULL; + + if (io_req_cancelled(req)) + return; + __io_accept(req, &nxt, false); + if (nxt) + *workptr = &nxt->work; +} +#endif + +static int io_accept(struct io_kiocb *req, struct io_kiocb **nxt, + bool force_nonblock) +{ +#if defined(CONFIG_NET) + int ret; + + ret = io_accept_prep(req); + if (ret) + return ret; + + ret = __io_accept(req, nxt, force_nonblock); + if (ret == -EAGAIN && force_nonblock) { + req->work.func = io_accept_finish; + req->work.flags |= IO_WQ_WORK_NEEDS_FILES; + io_put_req(req); + return -EAGAIN; + } + return 0; #else return -EOPNOTSUPP; #endif @@ -2915,6 +3029,12 @@ static int io_req_defer_prep(struct io_kiocb *req) io_req_map_rw(req, ret, iovec, inline_vecs, &iter); ret = 0; break; + case IORING_OP_FSYNC: + ret = io_prep_fsync(req); + break; + case IORING_OP_SYNC_FILE_RANGE: + ret = io_prep_sfr(req); + break; case IORING_OP_SENDMSG: ret = io_sendmsg_prep(req, io); break; @@ -2930,6 +3050,9 @@ static int io_req_defer_prep(struct io_kiocb *req) case IORING_OP_LINK_TIMEOUT: ret = io_timeout_prep(req, io, true); break; + case IORING_OP_ACCEPT: + ret = io_accept_prep(req); + break; default: ret = 0; break; -- GitLab From ffbb8d6b76910d4f3a2bafeaf68c419011e98d05 Mon Sep 17 00:00:00 2001 From: Pavel Begunkov Date: Tue, 17 Dec 2019 20:57:05 +0300 Subject: [PATCH 0858/1437] io_uring: make HARDLINK imply LINK The rules are as follows, if IOSQE_IO_HARDLINK is specified, then it's a link and there is no need to set IOSQE_IO_LINK separately, though it could be there. Add proper check and ensure that IOSQE_IO_HARDLINK implies IOSQE_IO_LINK. Signed-off-by: Pavel Begunkov Signed-off-by: Jens Axboe --- fs/io_uring.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/io_uring.c b/fs/io_uring.c index 67e1758bc937b..b476bd304045c 100644 --- a/fs/io_uring.c +++ b/fs/io_uring.c @@ -3698,7 +3698,7 @@ static int io_submit_sqes(struct io_ring_ctx *ctx, unsigned int nr, * If previous wasn't linked and we have a linked command, * that's the end of the chain. Submit the previous link. */ - if (!(sqe_flags & IOSQE_IO_LINK) && link) { + if (!(sqe_flags & (IOSQE_IO_LINK|IOSQE_IO_HARDLINK)) && link) { io_queue_link_head(link); link = NULL; } -- GitLab From 0969e783e3a8913f79df27286501a6c21e961524 Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Tue, 17 Dec 2019 18:40:57 -0700 Subject: [PATCH 0859/1437] io_uring: make IORING_POLL_ADD and IORING_POLL_REMOVE deferrable If we defer these commands as part of a link, we have to make sure that the SQE data has been read upfront. Integrate the poll add/remove into the prep handling to make it safe for SQE reuse. Signed-off-by: Jens Axboe --- fs/io_uring.c | 68 ++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 54 insertions(+), 14 deletions(-) diff --git a/fs/io_uring.c b/fs/io_uring.c index b476bd304045c..b0411406c50a7 100644 --- a/fs/io_uring.c +++ b/fs/io_uring.c @@ -289,7 +289,10 @@ struct io_ring_ctx { */ struct io_poll_iocb { struct file *file; - struct wait_queue_head *head; + union { + struct wait_queue_head *head; + u64 addr; + }; __poll_t events; bool done; bool canceled; @@ -2490,24 +2493,40 @@ static int io_poll_cancel(struct io_ring_ctx *ctx, __u64 sqe_addr) return -ENOENT; } +static int io_poll_remove_prep(struct io_kiocb *req) +{ + const struct io_uring_sqe *sqe = req->sqe; + + if (req->flags & REQ_F_PREPPED) + return 0; + if (unlikely(req->ctx->flags & IORING_SETUP_IOPOLL)) + return -EINVAL; + if (sqe->ioprio || sqe->off || sqe->len || sqe->buf_index || + sqe->poll_events) + return -EINVAL; + + req->poll.addr = READ_ONCE(sqe->addr); + req->flags |= REQ_F_PREPPED; + return 0; +} + /* * Find a running poll command that matches one specified in sqe->addr, * and remove it if found. */ static int io_poll_remove(struct io_kiocb *req) { - const struct io_uring_sqe *sqe = req->sqe; struct io_ring_ctx *ctx = req->ctx; + u64 addr; int ret; - if (unlikely(req->ctx->flags & IORING_SETUP_IOPOLL)) - return -EINVAL; - if (sqe->ioprio || sqe->off || sqe->len || sqe->buf_index || - sqe->poll_events) - return -EINVAL; + ret = io_poll_remove_prep(req); + if (ret) + return ret; + addr = req->poll.addr; spin_lock_irq(&ctx->completion_lock); - ret = io_poll_cancel(ctx, READ_ONCE(sqe->addr)); + ret = io_poll_cancel(ctx, addr); spin_unlock_irq(&ctx->completion_lock); io_cqring_add_event(req, ret); @@ -2642,16 +2661,14 @@ static void io_poll_req_insert(struct io_kiocb *req) hlist_add_head(&req->hash_node, list); } -static int io_poll_add(struct io_kiocb *req, struct io_kiocb **nxt) +static int io_poll_add_prep(struct io_kiocb *req) { const struct io_uring_sqe *sqe = req->sqe; struct io_poll_iocb *poll = &req->poll; - struct io_ring_ctx *ctx = req->ctx; - struct io_poll_table ipt; - bool cancel = false; - __poll_t mask; u16 events; + if (req->flags & REQ_F_PREPPED) + return 0; if (unlikely(req->ctx->flags & IORING_SETUP_IOPOLL)) return -EINVAL; if (sqe->addr || sqe->ioprio || sqe->off || sqe->len || sqe->buf_index) @@ -2659,9 +2676,26 @@ static int io_poll_add(struct io_kiocb *req, struct io_kiocb **nxt) if (!poll->file) return -EBADF; - INIT_IO_WORK(&req->work, io_poll_complete_work); + req->flags |= REQ_F_PREPPED; events = READ_ONCE(sqe->poll_events); poll->events = demangle_poll(events) | EPOLLERR | EPOLLHUP; + return 0; +} + +static int io_poll_add(struct io_kiocb *req, struct io_kiocb **nxt) +{ + struct io_poll_iocb *poll = &req->poll; + struct io_ring_ctx *ctx = req->ctx; + struct io_poll_table ipt; + bool cancel = false; + __poll_t mask; + int ret; + + ret = io_poll_add_prep(req); + if (ret) + return ret; + + INIT_IO_WORK(&req->work, io_poll_complete_work); INIT_HLIST_NODE(&req->hash_node); poll->head = NULL; @@ -3029,6 +3063,12 @@ static int io_req_defer_prep(struct io_kiocb *req) io_req_map_rw(req, ret, iovec, inline_vecs, &iter); ret = 0; break; + case IORING_OP_POLL_ADD: + ret = io_poll_add_prep(req); + break; + case IORING_OP_POLL_REMOVE: + ret = io_poll_remove_prep(req); + break; case IORING_OP_FSYNC: ret = io_prep_fsync(req); break; -- GitLab From fbf23849b1724d3ea362e346d0877a8d87978fe6 Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Tue, 17 Dec 2019 18:45:56 -0700 Subject: [PATCH 0860/1437] io_uring: make IORING_OP_CANCEL_ASYNC deferrable If we defer this command as part of a link, we have to make sure that the SQE data has been read upfront. Integrate the async cancel op into the prep handling to make it safe for SQE reuse. Signed-off-by: Jens Axboe --- fs/io_uring.c | 32 ++++++++++++++++++++++++++++---- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/fs/io_uring.c b/fs/io_uring.c index b0411406c50a7..1d6a5083f37f1 100644 --- a/fs/io_uring.c +++ b/fs/io_uring.c @@ -321,6 +321,11 @@ struct io_sync { int flags; }; +struct io_cancel { + struct file *file; + u64 addr; +}; + struct io_async_connect { struct sockaddr_storage address; }; @@ -362,6 +367,7 @@ struct io_kiocb { struct io_poll_iocb poll; struct io_accept accept; struct io_sync sync; + struct io_cancel cancel; }; const struct io_uring_sqe *sqe; @@ -3018,18 +3024,33 @@ done: io_put_req_find_next(req, nxt); } -static int io_async_cancel(struct io_kiocb *req, struct io_kiocb **nxt) +static int io_async_cancel_prep(struct io_kiocb *req) { const struct io_uring_sqe *sqe = req->sqe; - struct io_ring_ctx *ctx = req->ctx; - if (unlikely(ctx->flags & IORING_SETUP_IOPOLL)) + if (req->flags & REQ_F_PREPPED) + return 0; + if (unlikely(req->ctx->flags & IORING_SETUP_IOPOLL)) return -EINVAL; if (sqe->flags || sqe->ioprio || sqe->off || sqe->len || sqe->cancel_flags) return -EINVAL; - io_async_find_and_cancel(ctx, req, READ_ONCE(sqe->addr), nxt, 0); + req->flags |= REQ_F_PREPPED; + req->cancel.addr = READ_ONCE(sqe->addr); + return 0; +} + +static int io_async_cancel(struct io_kiocb *req, struct io_kiocb **nxt) +{ + struct io_ring_ctx *ctx = req->ctx; + int ret; + + ret = io_async_cancel_prep(req); + if (ret) + return ret; + + io_async_find_and_cancel(ctx, req, req->cancel.addr, nxt, 0); return 0; } @@ -3087,6 +3108,9 @@ static int io_req_defer_prep(struct io_kiocb *req) case IORING_OP_TIMEOUT: ret = io_timeout_prep(req, io, false); break; + case IORING_OP_ASYNC_CANCEL: + ret = io_async_cancel_prep(req); + break; case IORING_OP_LINK_TIMEOUT: ret = io_timeout_prep(req, io, true); break; -- GitLab From b29472ee7b53784f44011069fad15e539fd25bcf Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Tue, 17 Dec 2019 18:50:29 -0700 Subject: [PATCH 0861/1437] io_uring: make IORING_OP_TIMEOUT_REMOVE deferrable If we defer this command as part of a link, we have to make sure that the SQE data has been read upfront. Integrate the timeout remove op into the prep handling to make it safe for SQE reuse. Signed-off-by: Jens Axboe --- fs/io_uring.c | 44 ++++++++++++++++++++++++++++++++++---------- 1 file changed, 34 insertions(+), 10 deletions(-) diff --git a/fs/io_uring.c b/fs/io_uring.c index 1d6a5083f37f1..9d4f8274ee1eb 100644 --- a/fs/io_uring.c +++ b/fs/io_uring.c @@ -326,6 +326,12 @@ struct io_cancel { u64 addr; }; +struct io_timeout { + struct file *file; + u64 addr; + int flags; +}; + struct io_async_connect { struct sockaddr_storage address; }; @@ -368,6 +374,7 @@ struct io_kiocb { struct io_accept accept; struct io_sync sync; struct io_cancel cancel; + struct io_timeout timeout; }; const struct io_uring_sqe *sqe; @@ -2818,26 +2825,40 @@ static int io_timeout_cancel(struct io_ring_ctx *ctx, __u64 user_data) return 0; } +static int io_timeout_remove_prep(struct io_kiocb *req) +{ + const struct io_uring_sqe *sqe = req->sqe; + + if (req->flags & REQ_F_PREPPED) + return 0; + if (unlikely(req->ctx->flags & IORING_SETUP_IOPOLL)) + return -EINVAL; + if (sqe->flags || sqe->ioprio || sqe->buf_index || sqe->len) + return -EINVAL; + + req->timeout.addr = READ_ONCE(sqe->addr); + req->timeout.flags = READ_ONCE(sqe->timeout_flags); + if (req->timeout.flags) + return -EINVAL; + + req->flags |= REQ_F_PREPPED; + return 0; +} + /* * Remove or update an existing timeout command */ static int io_timeout_remove(struct io_kiocb *req) { - const struct io_uring_sqe *sqe = req->sqe; struct io_ring_ctx *ctx = req->ctx; - unsigned flags; int ret; - if (unlikely(ctx->flags & IORING_SETUP_IOPOLL)) - return -EINVAL; - if (sqe->flags || sqe->ioprio || sqe->buf_index || sqe->len) - return -EINVAL; - flags = READ_ONCE(sqe->timeout_flags); - if (flags) - return -EINVAL; + ret = io_timeout_remove_prep(req); + if (ret) + return ret; spin_lock_irq(&ctx->completion_lock); - ret = io_timeout_cancel(ctx, READ_ONCE(sqe->addr)); + ret = io_timeout_cancel(ctx, req->timeout.addr); io_cqring_fill_event(req, ret); io_commit_cqring(ctx); @@ -3108,6 +3129,9 @@ static int io_req_defer_prep(struct io_kiocb *req) case IORING_OP_TIMEOUT: ret = io_timeout_prep(req, io, false); break; + case IORING_OP_TIMEOUT_REMOVE: + ret = io_timeout_remove_prep(req); + break; case IORING_OP_ASYNC_CANCEL: ret = io_async_cancel_prep(req); break; -- GitLab From d625c6ee4975000140c57da7e1ff244efefde274 Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Tue, 17 Dec 2019 19:53:05 -0700 Subject: [PATCH 0862/1437] io_uring: read opcode and user_data from SQE exactly once If we defer a request, we can't be reading the opcode again. Ensure that the user_data and opcode fields are stable. For the user_data we already have a place for it, for the opcode we can fill a one byte hold and store that as well. For both of them, assign them when we originally read the SQE in io_get_sqring(). Any code that uses sqe->opcode or sqe->user_data is switched to req->opcode and req->user_data. Signed-off-by: Jens Axboe --- fs/io_uring.c | 45 ++++++++++++++++++++------------------------- 1 file changed, 20 insertions(+), 25 deletions(-) diff --git a/fs/io_uring.c b/fs/io_uring.c index 9d4f8274ee1eb..e0fc195d0d2d3 100644 --- a/fs/io_uring.c +++ b/fs/io_uring.c @@ -384,6 +384,7 @@ struct io_kiocb { bool has_user; bool in_async; bool needs_fixed_file; + u8 opcode; struct io_ring_ctx *ctx; union { @@ -597,12 +598,10 @@ static void __io_commit_cqring(struct io_ring_ctx *ctx) } } -static inline bool io_sqe_needs_user(const struct io_uring_sqe *sqe) +static inline bool io_req_needs_user(struct io_kiocb *req) { - u8 opcode = READ_ONCE(sqe->opcode); - - return !(opcode == IORING_OP_READ_FIXED || - opcode == IORING_OP_WRITE_FIXED); + return !(req->opcode == IORING_OP_READ_FIXED || + req->opcode == IORING_OP_WRITE_FIXED); } static inline bool io_prep_async_work(struct io_kiocb *req, @@ -611,7 +610,7 @@ static inline bool io_prep_async_work(struct io_kiocb *req, bool do_hashed = false; if (req->sqe) { - switch (req->sqe->opcode) { + switch (req->opcode) { case IORING_OP_WRITEV: case IORING_OP_WRITE_FIXED: /* only regular files should be hashed for writes */ @@ -634,7 +633,7 @@ static inline bool io_prep_async_work(struct io_kiocb *req, req->work.flags |= IO_WQ_WORK_UNBOUND; break; } - if (io_sqe_needs_user(req->sqe)) + if (io_req_needs_user(req)) req->work.flags |= IO_WQ_WORK_NEEDS_USER; } @@ -1005,7 +1004,7 @@ static void io_fail_links(struct io_kiocb *req) trace_io_uring_fail_link(req, link); if ((req->flags & REQ_F_LINK_TIMEOUT) && - link->sqe->opcode == IORING_OP_LINK_TIMEOUT) { + link->opcode == IORING_OP_LINK_TIMEOUT) { io_link_cancel_timeout(link); } else { io_cqring_fill_event(link, -ECANCELED); @@ -1648,7 +1647,7 @@ static ssize_t io_import_iovec(int rw, struct io_kiocb *req, * for that purpose and instead let the caller pass in the read/write * flag. */ - opcode = READ_ONCE(sqe->opcode); + opcode = req->opcode; if (opcode == IORING_OP_READ_FIXED || opcode == IORING_OP_WRITE_FIXED) { *iovec = NULL; return io_import_fixed(req->ctx, rw, sqe, iter); @@ -3082,7 +3081,7 @@ static int io_req_defer_prep(struct io_kiocb *req) struct iov_iter iter; ssize_t ret; - switch (io->sqe.opcode) { + switch (req->opcode) { case IORING_OP_READV: case IORING_OP_READ_FIXED: /* ensure prep does right import */ @@ -3181,11 +3180,10 @@ __attribute__((nonnull)) static int io_issue_sqe(struct io_kiocb *req, struct io_kiocb **nxt, bool force_nonblock) { - int ret, opcode; struct io_ring_ctx *ctx = req->ctx; + int ret; - opcode = READ_ONCE(req->sqe->opcode); - switch (opcode) { + switch (req->opcode) { case IORING_OP_NOP: ret = io_nop(req); break; @@ -3322,11 +3320,9 @@ static bool io_req_op_valid(int op) return op >= IORING_OP_NOP && op < IORING_OP_LAST; } -static int io_op_needs_file(const struct io_uring_sqe *sqe) +static int io_req_needs_file(struct io_kiocb *req) { - int op = READ_ONCE(sqe->opcode); - - switch (op) { + switch (req->opcode) { case IORING_OP_NOP: case IORING_OP_POLL_REMOVE: case IORING_OP_TIMEOUT: @@ -3335,7 +3331,7 @@ static int io_op_needs_file(const struct io_uring_sqe *sqe) case IORING_OP_LINK_TIMEOUT: return 0; default: - if (io_req_op_valid(op)) + if (io_req_op_valid(req->opcode)) return 1; return -EINVAL; } @@ -3362,7 +3358,7 @@ static int io_req_set_file(struct io_submit_state *state, struct io_kiocb *req) if (flags & IOSQE_IO_DRAIN) req->flags |= REQ_F_IO_DRAIN; - ret = io_op_needs_file(req->sqe); + ret = io_req_needs_file(req); if (ret <= 0) return ret; @@ -3482,7 +3478,7 @@ static struct io_kiocb *io_prep_linked_timeout(struct io_kiocb *req) nxt = list_first_entry_or_null(&req->link_list, struct io_kiocb, link_list); - if (!nxt || nxt->sqe->opcode != IORING_OP_LINK_TIMEOUT) + if (!nxt || nxt->opcode != IORING_OP_LINK_TIMEOUT) return NULL; req->flags |= REQ_F_LINK_TIMEOUT; @@ -3584,8 +3580,6 @@ static bool io_submit_sqe(struct io_kiocb *req, struct io_submit_state *state, struct io_ring_ctx *ctx = req->ctx; int ret; - req->user_data = req->sqe->user_data; - /* enforce forwards compatibility on users */ if (unlikely(req->sqe->flags & ~SQE_VALID_FLAGS)) { ret = -EINVAL; @@ -3717,6 +3711,8 @@ static bool io_get_sqring(struct io_ring_ctx *ctx, struct io_kiocb *req) */ req->sequence = ctx->cached_sq_head; req->sqe = &ctx->sq_sqes[head]; + req->opcode = READ_ONCE(req->sqe->opcode); + req->user_data = READ_ONCE(req->sqe->user_data); ctx->cached_sq_head++; return true; } @@ -3762,7 +3758,7 @@ static int io_submit_sqes(struct io_ring_ctx *ctx, unsigned int nr, break; } - if (io_sqe_needs_user(req->sqe) && !*mm) { + if (io_req_needs_user(req) && !*mm) { mm_fault = mm_fault || !mmget_not_zero(ctx->sqo_mm); if (!mm_fault) { use_mm(ctx->sqo_mm); @@ -3778,8 +3774,7 @@ static int io_submit_sqes(struct io_ring_ctx *ctx, unsigned int nr, req->has_user = *mm != NULL; req->in_async = async; req->needs_fixed_file = async; - trace_io_uring_submit_sqe(ctx, req->sqe->user_data, - true, async); + trace_io_uring_submit_sqe(ctx, req->user_data, true, async); if (!io_submit_sqe(req, statep, &link)) break; /* -- GitLab From e781573e2fb1b75acdba61dcb9bcbfc16f288442 Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Tue, 17 Dec 2019 19:45:06 -0700 Subject: [PATCH 0863/1437] io_uring: warn about unhandled opcode Now that we have all the opcodes handled in terms of command prep and SQE reuse, add a printk_once() to warn about any potentially new and unhandled ones. Signed-off-by: Jens Axboe --- fs/io_uring.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/fs/io_uring.c b/fs/io_uring.c index e0fc195d0d2d3..1d4e7332ccae6 100644 --- a/fs/io_uring.c +++ b/fs/io_uring.c @@ -3079,9 +3079,11 @@ static int io_req_defer_prep(struct io_kiocb *req) struct iovec inline_vecs[UIO_FASTIOV], *iovec = inline_vecs; struct io_async_ctx *io = req->io; struct iov_iter iter; - ssize_t ret; + ssize_t ret = 0; switch (req->opcode) { + case IORING_OP_NOP: + break; case IORING_OP_READV: case IORING_OP_READ_FIXED: /* ensure prep does right import */ @@ -3141,7 +3143,9 @@ static int io_req_defer_prep(struct io_kiocb *req) ret = io_accept_prep(req); break; default: - ret = 0; + printk_once(KERN_WARNING "io_uring: unhandled opcode %d\n", + req->opcode); + ret = -EINVAL; break; } -- GitLab From d89c69f42bf0fe42d1f52ea9b3dca15b1ade7601 Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Wed, 18 Dec 2019 11:43:06 +1100 Subject: [PATCH 0864/1437] KVM: PPC: Book3S HV: Don't do ultravisor calls on systems without ultravisor Commit 22945688acd4 ("KVM: PPC: Book3S HV: Support reset of secure guest") added a call to uv_svm_terminate, which is an ultravisor call, without any check that the guest is a secure guest or even that the system has an ultravisor. On a system without an ultravisor, the ultracall will degenerate to a hypercall, but since we are not in KVM guest context, the hypercall will get treated as a system call, which could have random effects depending on what happens to be in r0, and could also corrupt the current task's kernel stack. Hence this adds a test for the guest being a secure guest before doing uv_svm_terminate(). Fixes: 22945688acd4 ("KVM: PPC: Book3S HV: Support reset of secure guest") Signed-off-by: Paul Mackerras --- arch/powerpc/kvm/book3s_hv.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/powerpc/kvm/book3s_hv.c b/arch/powerpc/kvm/book3s_hv.c index dc53578193ee0..6ff3f896d9081 100644 --- a/arch/powerpc/kvm/book3s_hv.c +++ b/arch/powerpc/kvm/book3s_hv.c @@ -4983,7 +4983,8 @@ static void kvmppc_core_destroy_vm_hv(struct kvm *kvm) if (nesting_enabled(kvm)) kvmhv_release_all_nested(kvm); kvm->arch.process_table = 0; - uv_svm_terminate(kvm->arch.lpid); + if (kvm->arch.secure_guest) + uv_svm_terminate(kvm->arch.lpid); kvmhv_set_ptbl_entry(kvm->arch.lpid, 0, 0); } -- GitLab From d98c9e83b5e7ca78175df1b13ac4a6d460d3962d Mon Sep 17 00:00:00 2001 From: Andrey Ryabinin Date: Tue, 17 Dec 2019 20:51:38 -0800 Subject: [PATCH 0865/1437] kasan: fix crashes on access to memory mapped by vm_map_ram() With CONFIG_KASAN_VMALLOC=y any use of memory obtained via vm_map_ram() will crash because there is no shadow backing that memory. Instead of sprinkling additional kasan_populate_vmalloc() calls all over the vmalloc code, move it into alloc_vmap_area(). This will fix vm_map_ram() and simplify the code a bit. [aryabinin@virtuozzo.com: v2] Link: http://lkml.kernel.org/r/20191205095942.1761-1-aryabinin@virtuozzo.comLink: http://lkml.kernel.org/r/20191204204534.32202-1-aryabinin@virtuozzo.com Fixes: 3c5c3cfb9ef4 ("kasan: support backing vmalloc space with real shadow memory") Signed-off-by: Andrey Ryabinin Reported-by: Dmitry Vyukov Reviewed-by: Uladzislau Rezki (Sony) Cc: Daniel Axtens Cc: Alexander Potapenko Cc: Daniel Axtens Cc: Qian Cai Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/kasan.h | 15 +++++--- mm/kasan/common.c | 27 +++++++++----- mm/vmalloc.c | 85 ++++++++++++++++++++----------------------- 3 files changed, 67 insertions(+), 60 deletions(-) diff --git a/include/linux/kasan.h b/include/linux/kasan.h index 4f404c565db1b..e18fe54969e97 100644 --- a/include/linux/kasan.h +++ b/include/linux/kasan.h @@ -205,20 +205,23 @@ static inline void *kasan_reset_tag(const void *addr) #endif /* CONFIG_KASAN_SW_TAGS */ #ifdef CONFIG_KASAN_VMALLOC -int kasan_populate_vmalloc(unsigned long requested_size, - struct vm_struct *area); -void kasan_poison_vmalloc(void *start, unsigned long size); +int kasan_populate_vmalloc(unsigned long addr, unsigned long size); +void kasan_poison_vmalloc(const void *start, unsigned long size); +void kasan_unpoison_vmalloc(const void *start, unsigned long size); void kasan_release_vmalloc(unsigned long start, unsigned long end, unsigned long free_region_start, unsigned long free_region_end); #else -static inline int kasan_populate_vmalloc(unsigned long requested_size, - struct vm_struct *area) +static inline int kasan_populate_vmalloc(unsigned long start, + unsigned long size) { return 0; } -static inline void kasan_poison_vmalloc(void *start, unsigned long size) {} +static inline void kasan_poison_vmalloc(const void *start, unsigned long size) +{ } +static inline void kasan_unpoison_vmalloc(const void *start, unsigned long size) +{ } static inline void kasan_release_vmalloc(unsigned long start, unsigned long end, unsigned long free_region_start, diff --git a/mm/kasan/common.c b/mm/kasan/common.c index 2fa710bb6358a..e04e73603dfc9 100644 --- a/mm/kasan/common.c +++ b/mm/kasan/common.c @@ -778,15 +778,17 @@ static int kasan_populate_vmalloc_pte(pte_t *ptep, unsigned long addr, return 0; } -int kasan_populate_vmalloc(unsigned long requested_size, struct vm_struct *area) +int kasan_populate_vmalloc(unsigned long addr, unsigned long size) { unsigned long shadow_start, shadow_end; int ret; - shadow_start = (unsigned long)kasan_mem_to_shadow(area->addr); + if (!is_vmalloc_or_module_addr((void *)addr)) + return 0; + + shadow_start = (unsigned long)kasan_mem_to_shadow((void *)addr); shadow_start = ALIGN_DOWN(shadow_start, PAGE_SIZE); - shadow_end = (unsigned long)kasan_mem_to_shadow(area->addr + - area->size); + shadow_end = (unsigned long)kasan_mem_to_shadow((void *)addr + size); shadow_end = ALIGN(shadow_end, PAGE_SIZE); ret = apply_to_page_range(&init_mm, shadow_start, @@ -797,10 +799,6 @@ int kasan_populate_vmalloc(unsigned long requested_size, struct vm_struct *area) flush_cache_vmap(shadow_start, shadow_end); - kasan_unpoison_shadow(area->addr, requested_size); - - area->flags |= VM_KASAN; - /* * We need to be careful about inter-cpu effects here. Consider: * @@ -843,12 +841,23 @@ int kasan_populate_vmalloc(unsigned long requested_size, struct vm_struct *area) * Poison the shadow for a vmalloc region. Called as part of the * freeing process at the time the region is freed. */ -void kasan_poison_vmalloc(void *start, unsigned long size) +void kasan_poison_vmalloc(const void *start, unsigned long size) { + if (!is_vmalloc_or_module_addr(start)) + return; + size = round_up(size, KASAN_SHADOW_SCALE_SIZE); kasan_poison_shadow(start, size, KASAN_VMALLOC_INVALID); } +void kasan_unpoison_vmalloc(const void *start, unsigned long size) +{ + if (!is_vmalloc_or_module_addr(start)) + return; + + kasan_unpoison_shadow(start, size); +} + static int kasan_depopulate_vmalloc_pte(pte_t *ptep, unsigned long addr, void *unused) { diff --git a/mm/vmalloc.c b/mm/vmalloc.c index 4d3b3d60d8939..6e865cea846c8 100644 --- a/mm/vmalloc.c +++ b/mm/vmalloc.c @@ -1061,6 +1061,26 @@ __alloc_vmap_area(unsigned long size, unsigned long align, return nva_start_addr; } +/* + * Free a region of KVA allocated by alloc_vmap_area + */ +static void free_vmap_area(struct vmap_area *va) +{ + /* + * Remove from the busy tree/list. + */ + spin_lock(&vmap_area_lock); + unlink_va(va, &vmap_area_root); + spin_unlock(&vmap_area_lock); + + /* + * Insert/Merge it back to the free tree/list. + */ + spin_lock(&free_vmap_area_lock); + merge_or_add_vmap_area(va, &free_vmap_area_root, &free_vmap_area_list); + spin_unlock(&free_vmap_area_lock); +} + /* * Allocate a region of KVA of the specified size and alignment, within the * vstart and vend. @@ -1073,6 +1093,7 @@ static struct vmap_area *alloc_vmap_area(unsigned long size, struct vmap_area *va, *pva; unsigned long addr; int purged = 0; + int ret; BUG_ON(!size); BUG_ON(offset_in_page(size)); @@ -1139,6 +1160,7 @@ retry: va->va_end = addr + size; va->vm = NULL; + spin_lock(&vmap_area_lock); insert_vmap_area(va, &vmap_area_root, &vmap_area_list); spin_unlock(&vmap_area_lock); @@ -1147,6 +1169,12 @@ retry: BUG_ON(va->va_start < vstart); BUG_ON(va->va_end > vend); + ret = kasan_populate_vmalloc(addr, size); + if (ret) { + free_vmap_area(va); + return ERR_PTR(ret); + } + return va; overflow: @@ -1185,26 +1213,6 @@ int unregister_vmap_purge_notifier(struct notifier_block *nb) } EXPORT_SYMBOL_GPL(unregister_vmap_purge_notifier); -/* - * Free a region of KVA allocated by alloc_vmap_area - */ -static void free_vmap_area(struct vmap_area *va) -{ - /* - * Remove from the busy tree/list. - */ - spin_lock(&vmap_area_lock); - unlink_va(va, &vmap_area_root); - spin_unlock(&vmap_area_lock); - - /* - * Insert/Merge it back to the free tree/list. - */ - spin_lock(&free_vmap_area_lock); - merge_or_add_vmap_area(va, &free_vmap_area_root, &free_vmap_area_list); - spin_unlock(&free_vmap_area_lock); -} - /* * Clear the pagetable entries of a given vmap_area */ @@ -1771,6 +1779,8 @@ void vm_unmap_ram(const void *mem, unsigned int count) BUG_ON(addr > VMALLOC_END); BUG_ON(!PAGE_ALIGNED(addr)); + kasan_poison_vmalloc(mem, size); + if (likely(count <= VMAP_MAX_ALLOC)) { debug_check_no_locks_freed(mem, size); vb_free(mem, size); @@ -1821,6 +1831,9 @@ void *vm_map_ram(struct page **pages, unsigned int count, int node, pgprot_t pro addr = va->va_start; mem = (void *)addr; } + + kasan_unpoison_vmalloc(mem, size); + if (vmap_page_range(addr, addr + size, prot, pages) < 0) { vm_unmap_ram(mem, count); return NULL; @@ -2075,6 +2088,7 @@ static struct vm_struct *__get_vm_area_node(unsigned long size, { struct vmap_area *va; struct vm_struct *area; + unsigned long requested_size = size; BUG_ON(in_interrupt()); size = PAGE_ALIGN(size); @@ -2098,23 +2112,9 @@ static struct vm_struct *__get_vm_area_node(unsigned long size, return NULL; } - setup_vmalloc_vm(area, va, flags, caller); + kasan_unpoison_vmalloc((void *)va->va_start, requested_size); - /* - * For KASAN, if we are in vmalloc space, we need to cover the shadow - * area with real memory. If we come here through VM_ALLOC, this is - * done by a higher level function that has access to the true size, - * which might not be a full page. - * - * We assume module space comes via VM_ALLOC path. - */ - if (is_vmalloc_addr(area->addr) && !(area->flags & VM_ALLOC)) { - if (kasan_populate_vmalloc(area->size, area)) { - unmap_vmap_area(va); - kfree(area); - return NULL; - } - } + setup_vmalloc_vm(area, va, flags, caller); return area; } @@ -2293,8 +2293,7 @@ static void __vunmap(const void *addr, int deallocate_pages) debug_check_no_locks_freed(area->addr, get_vm_area_size(area)); debug_check_no_obj_freed(area->addr, get_vm_area_size(area)); - if (area->flags & VM_KASAN) - kasan_poison_vmalloc(area->addr, area->size); + kasan_poison_vmalloc(area->addr, area->size); vm_remove_mappings(area, deallocate_pages); @@ -2539,7 +2538,7 @@ void *__vmalloc_node_range(unsigned long size, unsigned long align, if (!size || (size >> PAGE_SHIFT) > totalram_pages()) goto fail; - area = __get_vm_area_node(size, align, VM_ALLOC | VM_UNINITIALIZED | + area = __get_vm_area_node(real_size, align, VM_ALLOC | VM_UNINITIALIZED | vm_flags, start, end, node, gfp_mask, caller); if (!area) goto fail; @@ -2548,11 +2547,6 @@ void *__vmalloc_node_range(unsigned long size, unsigned long align, if (!addr) return NULL; - if (is_vmalloc_or_module_addr(area->addr)) { - if (kasan_populate_vmalloc(real_size, area)) - return NULL; - } - /* * In this function, newly allocated vm_struct has VM_UNINITIALIZED * flag. It means that vm_struct is not fully initialized. @@ -3437,7 +3431,8 @@ retry: /* populate the shadow space outside of the lock */ for (area = 0; area < nr_vms; area++) { /* assume success here */ - kasan_populate_vmalloc(sizes[area], vms[area]); + kasan_populate_vmalloc(vas[area]->va_start, sizes[area]); + kasan_unpoison_vmalloc((void *)vms[area]->addr, sizes[area]); } kfree(vas); -- GitLab From be1db4753ee6a0db80a900df9dbbf6ad2acc4bd1 Mon Sep 17 00:00:00 2001 From: Daniel Axtens Date: Tue, 17 Dec 2019 20:51:41 -0800 Subject: [PATCH 0866/1437] mm/memory.c: add apply_to_existing_page_range() helper apply_to_page_range() takes an address range, and if any parts of it are not covered by the existing page table hierarchy, it allocates memory to fill them in. In some use cases, this is not what we want - we want to be able to operate exclusively on PTEs that are already in the tables. Add apply_to_existing_page_range() for this. Adjust the walker functions for apply_to_page_range to take 'create', which switches them between the old and new modes. This will be used in KASAN vmalloc. [akpm@linux-foundation.org: reduce code duplication] [akpm@linux-foundation.org: s/apply_to_existing_pages/apply_to_existing_page_range/] [akpm@linux-foundation.org: initialize __apply_to_page_range::err] Link: http://lkml.kernel.org/r/20191205140407.1874-1-dja@axtens.net Signed-off-by: Daniel Axtens Cc: Dmitry Vyukov Cc: Uladzislau Rezki (Sony) Cc: Alexander Potapenko Cc: Daniel Axtens Cc: Qian Cai Cc: Andrey Ryabinin Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/mm.h | 3 + mm/memory.c | 136 +++++++++++++++++++++++++++++++-------------- 2 files changed, 97 insertions(+), 42 deletions(-) diff --git a/include/linux/mm.h b/include/linux/mm.h index c97ea3b694e65..80a9162b406c2 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -2621,6 +2621,9 @@ static inline int vm_fault_to_errno(vm_fault_t vm_fault, int foll_flags) typedef int (*pte_fn_t)(pte_t *pte, unsigned long addr, void *data); extern int apply_to_page_range(struct mm_struct *mm, unsigned long address, unsigned long size, pte_fn_t fn, void *data); +extern int apply_to_existing_page_range(struct mm_struct *mm, + unsigned long address, unsigned long size, + pte_fn_t fn, void *data); #ifdef CONFIG_PAGE_POISONING extern bool page_poisoning_enabled(void); diff --git a/mm/memory.c b/mm/memory.c index 606da187d1de9..45442d9a4f525 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -2021,26 +2021,34 @@ EXPORT_SYMBOL(vm_iomap_memory); static int apply_to_pte_range(struct mm_struct *mm, pmd_t *pmd, unsigned long addr, unsigned long end, - pte_fn_t fn, void *data) + pte_fn_t fn, void *data, bool create) { pte_t *pte; - int err; + int err = 0; spinlock_t *uninitialized_var(ptl); - pte = (mm == &init_mm) ? - pte_alloc_kernel(pmd, addr) : - pte_alloc_map_lock(mm, pmd, addr, &ptl); - if (!pte) - return -ENOMEM; + if (create) { + pte = (mm == &init_mm) ? + pte_alloc_kernel(pmd, addr) : + pte_alloc_map_lock(mm, pmd, addr, &ptl); + if (!pte) + return -ENOMEM; + } else { + pte = (mm == &init_mm) ? + pte_offset_kernel(pmd, addr) : + pte_offset_map_lock(mm, pmd, addr, &ptl); + } BUG_ON(pmd_huge(*pmd)); arch_enter_lazy_mmu_mode(); do { - err = fn(pte++, addr, data); - if (err) - break; + if (create || !pte_none(*pte)) { + err = fn(pte++, addr, data); + if (err) + break; + } } while (addr += PAGE_SIZE, addr != end); arch_leave_lazy_mmu_mode(); @@ -2052,77 +2060,95 @@ static int apply_to_pte_range(struct mm_struct *mm, pmd_t *pmd, static int apply_to_pmd_range(struct mm_struct *mm, pud_t *pud, unsigned long addr, unsigned long end, - pte_fn_t fn, void *data) + pte_fn_t fn, void *data, bool create) { pmd_t *pmd; unsigned long next; - int err; + int err = 0; BUG_ON(pud_huge(*pud)); - pmd = pmd_alloc(mm, pud, addr); - if (!pmd) - return -ENOMEM; + if (create) { + pmd = pmd_alloc(mm, pud, addr); + if (!pmd) + return -ENOMEM; + } else { + pmd = pmd_offset(pud, addr); + } do { next = pmd_addr_end(addr, end); - err = apply_to_pte_range(mm, pmd, addr, next, fn, data); - if (err) - break; + if (create || !pmd_none_or_clear_bad(pmd)) { + err = apply_to_pte_range(mm, pmd, addr, next, fn, data, + create); + if (err) + break; + } } while (pmd++, addr = next, addr != end); return err; } static int apply_to_pud_range(struct mm_struct *mm, p4d_t *p4d, unsigned long addr, unsigned long end, - pte_fn_t fn, void *data) + pte_fn_t fn, void *data, bool create) { pud_t *pud; unsigned long next; - int err; + int err = 0; - pud = pud_alloc(mm, p4d, addr); - if (!pud) - return -ENOMEM; + if (create) { + pud = pud_alloc(mm, p4d, addr); + if (!pud) + return -ENOMEM; + } else { + pud = pud_offset(p4d, addr); + } do { next = pud_addr_end(addr, end); - err = apply_to_pmd_range(mm, pud, addr, next, fn, data); - if (err) - break; + if (create || !pud_none_or_clear_bad(pud)) { + err = apply_to_pmd_range(mm, pud, addr, next, fn, data, + create); + if (err) + break; + } } while (pud++, addr = next, addr != end); return err; } static int apply_to_p4d_range(struct mm_struct *mm, pgd_t *pgd, unsigned long addr, unsigned long end, - pte_fn_t fn, void *data) + pte_fn_t fn, void *data, bool create) { p4d_t *p4d; unsigned long next; - int err; + int err = 0; - p4d = p4d_alloc(mm, pgd, addr); - if (!p4d) - return -ENOMEM; + if (create) { + p4d = p4d_alloc(mm, pgd, addr); + if (!p4d) + return -ENOMEM; + } else { + p4d = p4d_offset(pgd, addr); + } do { next = p4d_addr_end(addr, end); - err = apply_to_pud_range(mm, p4d, addr, next, fn, data); - if (err) - break; + if (create || !p4d_none_or_clear_bad(p4d)) { + err = apply_to_pud_range(mm, p4d, addr, next, fn, data, + create); + if (err) + break; + } } while (p4d++, addr = next, addr != end); return err; } -/* - * Scan a region of virtual memory, filling in page tables as necessary - * and calling a provided function on each leaf page table. - */ -int apply_to_page_range(struct mm_struct *mm, unsigned long addr, - unsigned long size, pte_fn_t fn, void *data) +static int __apply_to_page_range(struct mm_struct *mm, unsigned long addr, + unsigned long size, pte_fn_t fn, + void *data, bool create) { pgd_t *pgd; unsigned long next; unsigned long end = addr + size; - int err; + int err = 0; if (WARN_ON(addr >= end)) return -EINVAL; @@ -2130,15 +2156,41 @@ int apply_to_page_range(struct mm_struct *mm, unsigned long addr, pgd = pgd_offset(mm, addr); do { next = pgd_addr_end(addr, end); - err = apply_to_p4d_range(mm, pgd, addr, next, fn, data); + if (!create && pgd_none_or_clear_bad(pgd)) + continue; + err = apply_to_p4d_range(mm, pgd, addr, next, fn, data, create); if (err) break; } while (pgd++, addr = next, addr != end); return err; } + +/* + * Scan a region of virtual memory, filling in page tables as necessary + * and calling a provided function on each leaf page table. + */ +int apply_to_page_range(struct mm_struct *mm, unsigned long addr, + unsigned long size, pte_fn_t fn, void *data) +{ + return __apply_to_page_range(mm, addr, size, fn, data, true); +} EXPORT_SYMBOL_GPL(apply_to_page_range); +/* + * Scan a region of virtual memory, calling a provided function on + * each leaf page table where it exists. + * + * Unlike apply_to_page_range, this does _not_ fill in page tables + * where they are absent. + */ +int apply_to_existing_page_range(struct mm_struct *mm, unsigned long addr, + unsigned long size, pte_fn_t fn, void *data) +{ + return __apply_to_page_range(mm, addr, size, fn, data, false); +} +EXPORT_SYMBOL_GPL(apply_to_existing_page_range); + /* * handle_pte_fault chooses page fault handler according to an entry which was * read non-atomically. Before making any commitment, on those architectures -- GitLab From e218f1ca3971e5bcaae1fe8e6f007f9a206e32e9 Mon Sep 17 00:00:00 2001 From: Daniel Axtens Date: Tue, 17 Dec 2019 20:51:46 -0800 Subject: [PATCH 0867/1437] kasan: use apply_to_existing_page_range() for releasing vmalloc shadow kasan_release_vmalloc uses apply_to_page_range to release vmalloc shadow. Unfortunately, apply_to_page_range can allocate memory to fill in page table entries, which is not what we want. Also, kasan_release_vmalloc is called under free_vmap_area_lock, so if apply_to_page_range does allocate memory, we get a sleep in atomic bug: BUG: sleeping function called from invalid context at mm/page_alloc.c:4681 in_atomic(): 1, irqs_disabled(): 0, non_block: 0, pid: 15087, name: Call Trace: __dump_stack lib/dump_stack.c:77 [inline] dump_stack+0x199/0x216 lib/dump_stack.c:118 ___might_sleep.cold.97+0x1f5/0x238 kernel/sched/core.c:6800 __might_sleep+0x95/0x190 kernel/sched/core.c:6753 prepare_alloc_pages mm/page_alloc.c:4681 [inline] __alloc_pages_nodemask+0x3cd/0x890 mm/page_alloc.c:4730 alloc_pages_current+0x10c/0x210 mm/mempolicy.c:2211 alloc_pages include/linux/gfp.h:532 [inline] __get_free_pages+0xc/0x40 mm/page_alloc.c:4786 __pte_alloc_one_kernel include/asm-generic/pgalloc.h:21 [inline] pte_alloc_one_kernel include/asm-generic/pgalloc.h:33 [inline] __pte_alloc_kernel+0x1d/0x200 mm/memory.c:459 apply_to_pte_range mm/memory.c:2031 [inline] apply_to_pmd_range mm/memory.c:2068 [inline] apply_to_pud_range mm/memory.c:2088 [inline] apply_to_p4d_range mm/memory.c:2108 [inline] apply_to_page_range+0x77d/0xa00 mm/memory.c:2133 kasan_release_vmalloc+0xa7/0xc0 mm/kasan/common.c:970 __purge_vmap_area_lazy+0xcbb/0x1f30 mm/vmalloc.c:1313 try_purge_vmap_area_lazy mm/vmalloc.c:1332 [inline] free_vmap_area_noflush+0x2ca/0x390 mm/vmalloc.c:1368 free_unmap_vmap_area mm/vmalloc.c:1381 [inline] remove_vm_area+0x1cc/0x230 mm/vmalloc.c:2209 vm_remove_mappings mm/vmalloc.c:2236 [inline] __vunmap+0x223/0xa20 mm/vmalloc.c:2299 __vfree+0x3f/0xd0 mm/vmalloc.c:2356 __vmalloc_area_node mm/vmalloc.c:2507 [inline] __vmalloc_node_range+0x5d5/0x810 mm/vmalloc.c:2547 __vmalloc_node mm/vmalloc.c:2607 [inline] __vmalloc_node_flags mm/vmalloc.c:2621 [inline] vzalloc+0x6f/0x80 mm/vmalloc.c:2666 alloc_one_pg_vec_page net/packet/af_packet.c:4233 [inline] alloc_pg_vec net/packet/af_packet.c:4258 [inline] packet_set_ring+0xbc0/0x1b50 net/packet/af_packet.c:4342 packet_setsockopt+0xed7/0x2d90 net/packet/af_packet.c:3695 __sys_setsockopt+0x29b/0x4d0 net/socket.c:2117 __do_sys_setsockopt net/socket.c:2133 [inline] __se_sys_setsockopt net/socket.c:2130 [inline] __x64_sys_setsockopt+0xbe/0x150 net/socket.c:2130 do_syscall_64+0xfa/0x780 arch/x86/entry/common.c:294 entry_SYSCALL_64_after_hwframe+0x49/0xbe Switch to using the apply_to_existing_page_range() helper instead, which won't allocate memory. [akpm@linux-foundation.org: s/apply_to_existing_pages/apply_to_existing_page_range/] Link: http://lkml.kernel.org/r/20191205140407.1874-2-dja@axtens.net Fixes: 3c5c3cfb9ef4 ("kasan: support backing vmalloc space with real shadow memory") Signed-off-by: Daniel Axtens Reported-by: Dmitry Vyukov Reviewed-by: Andrey Ryabinin Cc: Alexander Potapenko Cc: Qian Cai Cc: Uladzislau Rezki (Sony) Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- mm/kasan/common.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/mm/kasan/common.c b/mm/kasan/common.c index e04e73603dfc9..c15d8ae68c967 100644 --- a/mm/kasan/common.c +++ b/mm/kasan/common.c @@ -957,6 +957,7 @@ void kasan_release_vmalloc(unsigned long start, unsigned long end, { void *shadow_start, *shadow_end; unsigned long region_start, region_end; + unsigned long size; region_start = ALIGN(start, PAGE_SIZE * KASAN_SHADOW_SCALE_SIZE); region_end = ALIGN_DOWN(end, PAGE_SIZE * KASAN_SHADOW_SCALE_SIZE); @@ -979,9 +980,11 @@ void kasan_release_vmalloc(unsigned long start, unsigned long end, shadow_end = kasan_mem_to_shadow((void *)region_end); if (shadow_end > shadow_start) { - apply_to_page_range(&init_mm, (unsigned long)shadow_start, - (unsigned long)(shadow_end - shadow_start), - kasan_depopulate_vmalloc_pte, NULL); + size = shadow_end - shadow_start; + apply_to_existing_page_range(&init_mm, + (unsigned long)shadow_start, + size, kasan_depopulate_vmalloc_pte, + NULL); flush_tlb_kernel_range((unsigned long)shadow_start, (unsigned long)shadow_end); } -- GitLab From 253a496d8e57275d458eb3c988470525b0b2c545 Mon Sep 17 00:00:00 2001 From: Daniel Axtens Date: Tue, 17 Dec 2019 20:51:49 -0800 Subject: [PATCH 0868/1437] kasan: don't assume percpu shadow allocations will succeed syzkaller and the fault injector showed that I was wrong to assume that we could ignore percpu shadow allocation failures. Handle failures properly. Merge all the allocated areas back into the free list and release the shadow, then clean up and return NULL. The shadow is released unconditionally, which relies upon the fact that the release function is able to tolerate pages not being present. Also clean up shadows in the recovery path - currently they are not released, which leaks a bit of memory. Link: http://lkml.kernel.org/r/20191205140407.1874-3-dja@axtens.net Fixes: 3c5c3cfb9ef4 ("kasan: support backing vmalloc space with real shadow memory") Signed-off-by: Daniel Axtens Reported-by: syzbot+82e323920b78d54aaed5@syzkaller.appspotmail.com Reported-by: syzbot+59b7daa4315e07a994f1@syzkaller.appspotmail.com Reviewed-by: Andrey Ryabinin Cc: Dmitry Vyukov Cc: Alexander Potapenko Cc: Qian Cai Cc: Uladzislau Rezki (Sony) Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- mm/vmalloc.c | 48 ++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 38 insertions(+), 10 deletions(-) diff --git a/mm/vmalloc.c b/mm/vmalloc.c index 6e865cea846c8..e9681dc4aa754 100644 --- a/mm/vmalloc.c +++ b/mm/vmalloc.c @@ -3288,7 +3288,7 @@ struct vm_struct **pcpu_get_vm_areas(const unsigned long *offsets, struct vmap_area **vas, *va; struct vm_struct **vms; int area, area2, last_area, term_area; - unsigned long base, start, size, end, last_end; + unsigned long base, start, size, end, last_end, orig_start, orig_end; bool purged = false; enum fit_type type; @@ -3418,6 +3418,15 @@ retry: spin_unlock(&free_vmap_area_lock); + /* populate the kasan shadow space */ + for (area = 0; area < nr_vms; area++) { + if (kasan_populate_vmalloc(vas[area]->va_start, sizes[area])) + goto err_free_shadow; + + kasan_unpoison_vmalloc((void *)vas[area]->va_start, + sizes[area]); + } + /* insert all vm's */ spin_lock(&vmap_area_lock); for (area = 0; area < nr_vms; area++) { @@ -3428,13 +3437,6 @@ retry: } spin_unlock(&vmap_area_lock); - /* populate the shadow space outside of the lock */ - for (area = 0; area < nr_vms; area++) { - /* assume success here */ - kasan_populate_vmalloc(vas[area]->va_start, sizes[area]); - kasan_unpoison_vmalloc((void *)vms[area]->addr, sizes[area]); - } - kfree(vas); return vms; @@ -3446,8 +3448,12 @@ recovery: * and when pcpu_get_vm_areas() is success. */ while (area--) { - merge_or_add_vmap_area(vas[area], &free_vmap_area_root, - &free_vmap_area_list); + orig_start = vas[area]->va_start; + orig_end = vas[area]->va_end; + va = merge_or_add_vmap_area(vas[area], &free_vmap_area_root, + &free_vmap_area_list); + kasan_release_vmalloc(orig_start, orig_end, + va->va_start, va->va_end); vas[area] = NULL; } @@ -3482,6 +3488,28 @@ err_free2: kfree(vas); kfree(vms); return NULL; + +err_free_shadow: + spin_lock(&free_vmap_area_lock); + /* + * We release all the vmalloc shadows, even the ones for regions that + * hadn't been successfully added. This relies on kasan_release_vmalloc + * being able to tolerate this case. + */ + for (area = 0; area < nr_vms; area++) { + orig_start = vas[area]->va_start; + orig_end = vas[area]->va_end; + va = merge_or_add_vmap_area(vas[area], &free_vmap_area_root, + &free_vmap_area_list); + kasan_release_vmalloc(orig_start, orig_end, + va->va_start, va->va_end); + vas[area] = NULL; + kfree(vms[area]); + } + spin_unlock(&free_vmap_area_lock); + kfree(vas); + kfree(vms); + return NULL; } /** -- GitLab From 42a9a53bb394a1de2247ef78f0b802ae86798122 Mon Sep 17 00:00:00 2001 From: Yang Shi Date: Tue, 17 Dec 2019 20:51:52 -0800 Subject: [PATCH 0869/1437] mm: vmscan: protect shrinker idr replace with CONFIG_MEMCG Since commit 0a432dcbeb32 ("mm: shrinker: make shrinker not depend on memcg kmem"), shrinkers' idr is protected by CONFIG_MEMCG instead of CONFIG_MEMCG_KMEM, so it makes no sense to protect shrinker idr replace with CONFIG_MEMCG_KMEM. And in the CONFIG_MEMCG && CONFIG_SLOB case, shrinker_idr contains only shrinker, and it is deferred_split_shrinker. But it is never actually called, since idr_replace() is never compiled due to the wrong #ifdef. The deferred_split_shrinker all the time is staying in half-registered state, and it's never called for subordinate mem cgroups. Link: http://lkml.kernel.org/r/1575486978-45249-1-git-send-email-yang.shi@linux.alibaba.com Fixes: 0a432dcbeb32 ("mm: shrinker: make shrinker not depend on memcg kmem") Signed-off-by: Yang Shi Reviewed-by: Kirill Tkhai Acked-by: Michal Hocko Cc: Johannes Weiner Cc: Shakeel Butt Cc: Roman Gushchin Cc: [5.4+] Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- mm/vmscan.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mm/vmscan.c b/mm/vmscan.c index 74e8edce83ca4..572fb17c62733 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -387,7 +387,7 @@ void register_shrinker_prepared(struct shrinker *shrinker) { down_write(&shrinker_rwsem); list_add_tail(&shrinker->list, &shrinker_list); -#ifdef CONFIG_MEMCG_KMEM +#ifdef CONFIG_MEMCG if (shrinker->flags & SHRINKER_MEMCG_AWARE) idr_replace(&shrinker_idr, shrinker, shrinker->id); #endif -- GitLab From 045f6d7942be248fbda6e85b2393f2735695ed39 Mon Sep 17 00:00:00 2001 From: Changbin Du Date: Tue, 17 Dec 2019 20:51:56 -0800 Subject: [PATCH 0870/1437] lib/Kconfig.debug: fix some messed up configurations Some configuration items are messed up during conflict resolving. For example, STRICT_DEVMEM should not in testing menu, but kunit should. This patch fixes all of them. [akpm@linux-foundation.org: coding style fixes] Link: http://lkml.kernel.org/r/20191209155653.7509-1-changbin.du@gmail.com Signed-off-by: Changbin Du Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- lib/Kconfig.debug | 100 +++++++++++++++++++++++----------------------- 1 file changed, 51 insertions(+), 49 deletions(-) diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug index d1842fe756d55..5ffe144c97945 100644 --- a/lib/Kconfig.debug +++ b/lib/Kconfig.debug @@ -1483,6 +1483,55 @@ config PROVIDE_OHCI1394_DMA_INIT See Documentation/debugging-via-ohci1394.txt for more information. +source "samples/Kconfig" + +config ARCH_HAS_DEVMEM_IS_ALLOWED + bool + +config STRICT_DEVMEM + bool "Filter access to /dev/mem" + depends on MMU && DEVMEM + depends on ARCH_HAS_DEVMEM_IS_ALLOWED + default y if PPC || X86 || ARM64 + help + If this option is disabled, you allow userspace (root) access to all + of memory, including kernel and userspace memory. Accidental + access to this is obviously disastrous, but specific access can + be used by people debugging the kernel. Note that with PAT support + enabled, even in this case there are restrictions on /dev/mem + use due to the cache aliasing requirements. + + If this option is switched on, and IO_STRICT_DEVMEM=n, the /dev/mem + file only allows userspace access to PCI space and the BIOS code and + data regions. This is sufficient for dosemu and X and all common + users of /dev/mem. + + If in doubt, say Y. + +config IO_STRICT_DEVMEM + bool "Filter I/O access to /dev/mem" + depends on STRICT_DEVMEM + help + If this option is disabled, you allow userspace (root) access to all + io-memory regardless of whether a driver is actively using that + range. Accidental access to this is obviously disastrous, but + specific access can be used by people debugging kernel drivers. + + If this option is switched on, the /dev/mem file only allows + userspace access to *idle* io-memory ranges (see /proc/iomem) This + may break traditional users of /dev/mem (dosemu, legacy X, etc...) + if the driver using a given range cannot be disabled. + + If in doubt, say Y. + +menu "$(SRCARCH) Debugging" + +source "arch/$(SRCARCH)/Kconfig.debug" + +endmenu + +menu "Kernel Testing and Coverage" + source "lib/kunit/Kconfig" config NOTIFIER_ERROR_INJECTION @@ -1643,10 +1692,6 @@ config FAULT_INJECTION_STACKTRACE_FILTER help Provide stacktrace filter for fault-injection capabilities -endmenu # "Kernel Testing and Coverage" - -menu "Kernel Testing and Coverage" - config ARCH_HAS_KCOV bool help @@ -2130,52 +2175,7 @@ config MEMTEST memtest=17, mean do 17 test patterns. If you are unsure how to answer this question, answer N. -source "samples/Kconfig" - -config ARCH_HAS_DEVMEM_IS_ALLOWED - bool - -config STRICT_DEVMEM - bool "Filter access to /dev/mem" - depends on MMU && DEVMEM - depends on ARCH_HAS_DEVMEM_IS_ALLOWED - default y if PPC || X86 || ARM64 - ---help--- - If this option is disabled, you allow userspace (root) access to all - of memory, including kernel and userspace memory. Accidental - access to this is obviously disastrous, but specific access can - be used by people debugging the kernel. Note that with PAT support - enabled, even in this case there are restrictions on /dev/mem - use due to the cache aliasing requirements. - - If this option is switched on, and IO_STRICT_DEVMEM=n, the /dev/mem - file only allows userspace access to PCI space and the BIOS code and - data regions. This is sufficient for dosemu and X and all common - users of /dev/mem. - - If in doubt, say Y. -config IO_STRICT_DEVMEM - bool "Filter I/O access to /dev/mem" - depends on STRICT_DEVMEM - ---help--- - If this option is disabled, you allow userspace (root) access to all - io-memory regardless of whether a driver is actively using that - range. Accidental access to this is obviously disastrous, but - specific access can be used by people debugging kernel drivers. - - If this option is switched on, the /dev/mem file only allows - userspace access to *idle* io-memory ranges (see /proc/iomem) This - may break traditional users of /dev/mem (dosemu, legacy X, etc...) - if the driver using a given range cannot be disabled. - - If in doubt, say Y. - -menu "$(SRCARCH) Debugging" - -source "arch/$(SRCARCH)/Kconfig.debug" - -endmenu config HYPERV_TESTING bool "Microsoft Hyper-V driver testing" @@ -2184,4 +2184,6 @@ config HYPERV_TESTING help Select this option to enable Hyper-V vmbus testing. +endmenu # "Kernel Testing and Coverage" + endmenu # Kernel hacking -- GitLab From 04646aebd30b99f2cfa0182435a2ec252fcb16d0 Mon Sep 17 00:00:00 2001 From: Eric Sandeen Date: Fri, 6 Dec 2019 10:54:23 -0600 Subject: [PATCH 0871/1437] fs: avoid softlockups in s_inodes iterators Anything that walks all inodes on sb->s_inodes list without rescheduling risks softlockups. Previous efforts were made in 2 functions, see: c27d82f fs/drop_caches.c: avoid softlockups in drop_pagecache_sb() ac05fbb inode: don't softlockup when evicting inodes but there hasn't been an audit of all walkers, so do that now. This also consistently moves the cond_resched() calls to the bottom of each loop in cases where it already exists. One loop remains: remove_dquot_ref(), because I'm not quite sure how to deal with that one w/o taking the i_lock. Signed-off-by: Eric Sandeen Reviewed-by: Jan Kara Signed-off-by: Al Viro --- fs/drop_caches.c | 2 +- fs/inode.c | 7 +++++++ fs/notify/fsnotify.c | 1 + fs/quota/dquot.c | 1 + 4 files changed, 10 insertions(+), 1 deletion(-) diff --git a/fs/drop_caches.c b/fs/drop_caches.c index d31b6c72b4764..dc1a1d5d825b4 100644 --- a/fs/drop_caches.c +++ b/fs/drop_caches.c @@ -35,11 +35,11 @@ static void drop_pagecache_sb(struct super_block *sb, void *unused) spin_unlock(&inode->i_lock); spin_unlock(&sb->s_inode_list_lock); - cond_resched(); invalidate_mapping_pages(inode->i_mapping, 0, -1); iput(toput_inode); toput_inode = inode; + cond_resched(); spin_lock(&sb->s_inode_list_lock); } spin_unlock(&sb->s_inode_list_lock); diff --git a/fs/inode.c b/fs/inode.c index fef457a42882b..96d62d97694ef 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -676,6 +676,7 @@ int invalidate_inodes(struct super_block *sb, bool kill_dirty) struct inode *inode, *next; LIST_HEAD(dispose); +again: spin_lock(&sb->s_inode_list_lock); list_for_each_entry_safe(inode, next, &sb->s_inodes, i_sb_list) { spin_lock(&inode->i_lock); @@ -698,6 +699,12 @@ int invalidate_inodes(struct super_block *sb, bool kill_dirty) inode_lru_list_del(inode); spin_unlock(&inode->i_lock); list_add(&inode->i_lru, &dispose); + if (need_resched()) { + spin_unlock(&sb->s_inode_list_lock); + cond_resched(); + dispose_list(&dispose); + goto again; + } } spin_unlock(&sb->s_inode_list_lock); diff --git a/fs/notify/fsnotify.c b/fs/notify/fsnotify.c index 3e77b728a22bd..6c902c0a1a5e3 100644 --- a/fs/notify/fsnotify.c +++ b/fs/notify/fsnotify.c @@ -77,6 +77,7 @@ static void fsnotify_unmount_inodes(struct super_block *sb) iput_inode = inode; + cond_resched(); spin_lock(&sb->s_inode_list_lock); } spin_unlock(&sb->s_inode_list_lock); diff --git a/fs/quota/dquot.c b/fs/quota/dquot.c index b0688c02dc90d..b6a4f692d3457 100644 --- a/fs/quota/dquot.c +++ b/fs/quota/dquot.c @@ -984,6 +984,7 @@ static int add_dquot_ref(struct super_block *sb, int type) * later. */ old_inode = inode; + cond_resched(); spin_lock(&sb->s_inode_list_lock); } spin_unlock(&sb->s_inode_list_lock); -- GitLab From 1edc8eb2e93130e36ac74ac9c80913815a57d413 Mon Sep 17 00:00:00 2001 From: Eric Sandeen Date: Fri, 6 Dec 2019 10:55:59 -0600 Subject: [PATCH 0872/1437] fs: call fsnotify_sb_delete after evict_inodes When a filesystem is unmounted, we currently call fsnotify_sb_delete() before evict_inodes(), which means that fsnotify_unmount_inodes() must iterate over all inodes on the superblock looking for any inodes with watches. This is inefficient and can lead to livelocks as it iterates over many unwatched inodes. At this point, SB_ACTIVE is gone and dropping refcount to zero kicks the inode out out immediately, so anything processed by fsnotify_sb_delete / fsnotify_unmount_inodes gets evicted in that loop. After that, the call to evict_inodes will evict everything else with a zero refcount. This should speed things up overall, and avoid livelocks in fsnotify_unmount_inodes(). Signed-off-by: Eric Sandeen Reviewed-by: Jan Kara Signed-off-by: Al Viro --- fs/notify/fsnotify.c | 3 +++ fs/super.c | 4 +++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/fs/notify/fsnotify.c b/fs/notify/fsnotify.c index 6c902c0a1a5e3..46f2255800091 100644 --- a/fs/notify/fsnotify.c +++ b/fs/notify/fsnotify.c @@ -57,6 +57,9 @@ static void fsnotify_unmount_inodes(struct super_block *sb) * doing an __iget/iput with SB_ACTIVE clear would actually * evict all inodes with zero i_count from icache which is * unnecessarily violent and may in fact be illegal to do. + * However, we should have been called /after/ evict_inodes + * removed all zero refcount inodes, in any case. Test to + * be sure. */ if (!atomic_read(&inode->i_count)) { spin_unlock(&inode->i_lock); diff --git a/fs/super.c b/fs/super.c index cfadab2cbf35f..cd352530eca90 100644 --- a/fs/super.c +++ b/fs/super.c @@ -448,10 +448,12 @@ void generic_shutdown_super(struct super_block *sb) sync_filesystem(sb); sb->s_flags &= ~SB_ACTIVE; - fsnotify_sb_delete(sb); cgroup_writeback_umount(); + /* evict all inodes with zero refcount */ evict_inodes(sb); + /* only nonzero refcount inodes can have marks */ + fsnotify_sb_delete(sb); if (sb->s_dio_done_wq) { destroy_workqueue(sb->s_dio_done_wq); -- GitLab From 951c6db954a1adefab492f6da805decacabbd1a7 Mon Sep 17 00:00:00 2001 From: Marcelo Ricardo Leitner Date: Mon, 16 Dec 2019 22:01:16 -0300 Subject: [PATCH 0873/1437] sctp: fix memleak on err handling of stream initialization syzbot reported a memory leak when an allocation fails within genradix_prealloc() for output streams. That's because genradix_prealloc() leaves initialized members initialized when the issue happens and SCTP stack will abort the current initialization but without cleaning up such members. The fix here is to always call genradix_free() when genradix_prealloc() fails, for output and also input streams, as it suffers from the same issue. Reported-by: syzbot+772d9e36c490b18d51d1@syzkaller.appspotmail.com Fixes: 2075e50caf5e ("sctp: convert to genradix") Signed-off-by: Marcelo Ricardo Leitner Tested-by: Xin Long Signed-off-by: David S. Miller --- net/sctp/stream.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/net/sctp/stream.c b/net/sctp/stream.c index e83cdaa2ab765..6a30392068a04 100644 --- a/net/sctp/stream.c +++ b/net/sctp/stream.c @@ -84,8 +84,10 @@ static int sctp_stream_alloc_out(struct sctp_stream *stream, __u16 outcnt, return 0; ret = genradix_prealloc(&stream->out, outcnt, gfp); - if (ret) + if (ret) { + genradix_free(&stream->out); return ret; + } stream->outcnt = outcnt; return 0; @@ -100,8 +102,10 @@ static int sctp_stream_alloc_in(struct sctp_stream *stream, __u16 incnt, return 0; ret = genradix_prealloc(&stream->in, incnt, gfp); - if (ret) + if (ret) { + genradix_free(&stream->in); return ret; + } stream->incnt = incnt; return 0; -- GitLab From cad46039e4c99812db067c8ac22a864960e7acc4 Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Tue, 17 Dec 2019 01:57:40 +0000 Subject: [PATCH 0874/1437] net: qlogic: Fix error paths in ql_alloc_large_buffers() ql_alloc_large_buffers() has the usual RX buffer allocation loop where it allocates skbs and maps them for DMA. It also treats failure as a fatal error. There are (at least) three bugs in the error paths: 1. ql_free_large_buffers() assumes that the lrg_buf[] entry for the first buffer that couldn't be allocated will have .skb == NULL. But the qla_buf[] array is not zero-initialised. 2. ql_free_large_buffers() DMA-unmaps all skbs in lrg_buf[]. This is incorrect for the last allocated skb, if DMA mapping failed. 3. Commit 1acb8f2a7a9f ("net: qlogic: Fix memory leak in ql_alloc_large_buffers") added a direct call to dev_kfree_skb_any() after the skb is recorded in lrg_buf[], so ql_free_large_buffers() will double-free it. The bugs are somewhat inter-twined, so fix them all at once: * Clear each entry in qla_buf[] before attempting to allocate an skb for it. This goes half-way to fixing bug 1. * Set the .skb field only after the skb is DMA-mapped. This fixes the rest. Fixes: 1357bfcf7106 ("qla3xxx: Dynamically size the rx buffer queue ...") Fixes: 0f8ab89e825f ("qla3xxx: Check return code from pci_map_single() ...") Fixes: 1acb8f2a7a9f ("net: qlogic: Fix memory leak in ql_alloc_large_buffers") Signed-off-by: Ben Hutchings Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/qla3xxx.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/qlogic/qla3xxx.c b/drivers/net/ethernet/qlogic/qla3xxx.c index b4b8ba00ee015..986f26578d344 100644 --- a/drivers/net/ethernet/qlogic/qla3xxx.c +++ b/drivers/net/ethernet/qlogic/qla3xxx.c @@ -2756,6 +2756,9 @@ static int ql_alloc_large_buffers(struct ql3_adapter *qdev) int err; for (i = 0; i < qdev->num_large_buffers; i++) { + lrg_buf_cb = &qdev->lrg_buf[i]; + memset(lrg_buf_cb, 0, sizeof(struct ql_rcv_buf_cb)); + skb = netdev_alloc_skb(qdev->ndev, qdev->lrg_buffer_len); if (unlikely(!skb)) { @@ -2766,11 +2769,7 @@ static int ql_alloc_large_buffers(struct ql3_adapter *qdev) ql_free_large_buffers(qdev); return -ENOMEM; } else { - - lrg_buf_cb = &qdev->lrg_buf[i]; - memset(lrg_buf_cb, 0, sizeof(struct ql_rcv_buf_cb)); lrg_buf_cb->index = i; - lrg_buf_cb->skb = skb; /* * We save some space to copy the ethhdr from first * buffer @@ -2792,6 +2791,7 @@ static int ql_alloc_large_buffers(struct ql3_adapter *qdev) return -ENOMEM; } + lrg_buf_cb->skb = skb; dma_unmap_addr_set(lrg_buf_cb, mapaddr, map); dma_unmap_len_set(lrg_buf_cb, maplen, qdev->lrg_buffer_len - -- GitLab From 7c68fa2bddda6d942bd387c9ba5b4300737fd991 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Mon, 16 Dec 2019 18:51:03 -0800 Subject: [PATCH 0875/1437] net: annotate lockless accesses to sk->sk_pacing_shift sk->sk_pacing_shift can be read and written without lock synchronization. This patch adds annotations to document this fact and avoid future syzbot complains. This might also avoid unexpected false sharing in sk_pacing_shift_update(), as the compiler could remove the conditional check and always write over sk->sk_pacing_shift : if (sk->sk_pacing_shift != val) sk->sk_pacing_shift = val; Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- include/net/sock.h | 4 ++-- net/core/sock.c | 2 +- net/ipv4/tcp_bbr.c | 3 ++- net/ipv4/tcp_output.c | 4 ++-- 4 files changed, 7 insertions(+), 6 deletions(-) diff --git a/include/net/sock.h b/include/net/sock.h index 04c274a20620b..22be668457bf3 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -2588,9 +2588,9 @@ static inline int sk_get_rmem0(const struct sock *sk, const struct proto *proto) */ static inline void sk_pacing_shift_update(struct sock *sk, int val) { - if (!sk || !sk_fullsock(sk) || sk->sk_pacing_shift == val) + if (!sk || !sk_fullsock(sk) || READ_ONCE(sk->sk_pacing_shift) == val) return; - sk->sk_pacing_shift = val; + WRITE_ONCE(sk->sk_pacing_shift, val); } /* if a socket is bound to a device, check that the given device diff --git a/net/core/sock.c b/net/core/sock.c index 043db3ce023e5..8459ad579f735 100644 --- a/net/core/sock.c +++ b/net/core/sock.c @@ -2916,7 +2916,7 @@ void sock_init_data(struct socket *sock, struct sock *sk) sk->sk_max_pacing_rate = ~0UL; sk->sk_pacing_rate = ~0UL; - sk->sk_pacing_shift = 10; + WRITE_ONCE(sk->sk_pacing_shift, 10); sk->sk_incoming_cpu = -1; sk_rx_queue_clear(sk); diff --git a/net/ipv4/tcp_bbr.c b/net/ipv4/tcp_bbr.c index 32772d6ded4ed..a6545ef0d27b6 100644 --- a/net/ipv4/tcp_bbr.c +++ b/net/ipv4/tcp_bbr.c @@ -306,7 +306,8 @@ static u32 bbr_tso_segs_goal(struct sock *sk) /* Sort of tcp_tso_autosize() but ignoring * driver provided sk_gso_max_size. */ - bytes = min_t(unsigned long, sk->sk_pacing_rate >> sk->sk_pacing_shift, + bytes = min_t(unsigned long, + sk->sk_pacing_rate >> READ_ONCE(sk->sk_pacing_shift), GSO_MAX_SIZE - 1 - MAX_TCP_HEADER); segs = max_t(u32, bytes / tp->mss_cache, bbr_min_tso_segs(sk)); diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index 36902d08473ec..1f7735ca8f22d 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -1725,7 +1725,7 @@ static u32 tcp_tso_autosize(const struct sock *sk, unsigned int mss_now, u32 bytes, segs; bytes = min_t(unsigned long, - sk->sk_pacing_rate >> sk->sk_pacing_shift, + sk->sk_pacing_rate >> READ_ONCE(sk->sk_pacing_shift), sk->sk_gso_max_size - 1 - MAX_TCP_HEADER); /* Goal is to send at least one packet per ms, @@ -2260,7 +2260,7 @@ static bool tcp_small_queue_check(struct sock *sk, const struct sk_buff *skb, limit = max_t(unsigned long, 2 * skb->truesize, - sk->sk_pacing_rate >> sk->sk_pacing_shift); + sk->sk_pacing_rate >> READ_ONCE(sk->sk_pacing_shift)); if (sk->sk_pacing_status == SK_PACING_NONE) limit = min_t(unsigned long, limit, sock_net(sk)->ipv4.sysctl_tcp_limit_output_bytes); -- GitLab From 1f26c0d3d24125992ab0026b0dab16c08df947c7 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Mon, 16 Dec 2019 18:52:45 -0800 Subject: [PATCH 0876/1437] net: fix kernel-doc warning in Fix missing '*' kernel-doc notation that causes this warning: ../include/linux/netdevice.h:1779: warning: bad line: spinlock Fixes: ab92d68fc22f ("net: core: add generic lockdep keys") Signed-off-by: Randy Dunlap Cc: Taehee Yoo Signed-off-by: David S. Miller --- include/linux/netdevice.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 9ef20389622d7..ae5e260911e28 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -1775,7 +1775,7 @@ enum netdev_priv_flags { * for hardware timestamping * @sfp_bus: attached &struct sfp_bus structure. * @qdisc_tx_busylock_key: lockdep class annotating Qdisc->busylock - spinlock + * spinlock * @qdisc_running_key: lockdep class annotating Qdisc->running seqcount * @qdisc_xmit_lock_key: lockdep class annotating * netdev_queue->_xmit_lock spinlock -- GitLab From 5dc7a8b2114479fd945956ece9875f747a996a8e Mon Sep 17 00:00:00 2001 From: Andrii Nakryiko Date: Tue, 17 Dec 2019 21:25:50 -0800 Subject: [PATCH 0877/1437] bpftool, selftests/bpf: Embed object file inside skeleton Embed contents of BPF object file used for BPF skeleton generation inside skeleton itself. This allows to keep BPF object file and its skeleton in sync at all times, and simpifies skeleton instantiation. Also switch existing selftests to not require BPF_EMBED_OBJ anymore. Signed-off-by: Andrii Nakryiko Signed-off-by: Alexei Starovoitov Acked-by: Yonghong Song Link: https://lore.kernel.org/bpf/20191218052552.2915188-2-andriin@fb.com --- tools/bpf/bpftool/gen.c | 232 +++++++++++------- .../selftests/bpf/prog_tests/attach_probe.c | 4 +- .../selftests/bpf/prog_tests/core_extern.c | 4 +- .../selftests/bpf/prog_tests/fentry_fexit.c | 10 +- .../selftests/bpf/prog_tests/fentry_test.c | 7 +- tools/testing/selftests/bpf/prog_tests/mmap.c | 4 +- .../selftests/bpf/prog_tests/skeleton.c | 4 +- .../bpf/prog_tests/stacktrace_build_id.c | 4 +- .../bpf/prog_tests/stacktrace_build_id_nmi.c | 4 +- 9 files changed, 154 insertions(+), 119 deletions(-) diff --git a/tools/bpf/bpftool/gen.c b/tools/bpf/bpftool/gen.c index f70088b4c19b9..8d93c8f90f825 100644 --- a/tools/bpf/bpftool/gen.c +++ b/tools/bpf/bpftool/gen.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include "btf.h" @@ -261,14 +262,16 @@ static int codegen(const char *template, ...) static int do_skeleton(int argc, char **argv) { char header_guard[MAX_OBJ_NAME_LEN + sizeof("__SKEL_H__")]; - size_t i, map_cnt = 0, prog_cnt = 0; - char obj_name[MAX_OBJ_NAME_LEN]; + size_t i, map_cnt = 0, prog_cnt = 0, file_sz, mmap_sz; + DECLARE_LIBBPF_OPTS(bpf_object_open_opts, opts); + char obj_name[MAX_OBJ_NAME_LEN], *obj_data; + struct bpf_object *obj = NULL; const char *file, *ident; struct bpf_program *prog; - struct bpf_object *obj; + int fd, len, err = -1; struct bpf_map *map; struct btf *btf; - int err = -1; + struct stat st; if (!REQ_ARGS(1)) { usage(); @@ -281,14 +284,31 @@ static int do_skeleton(int argc, char **argv) return -1; } - obj = bpf_object__open_file(file, NULL); - if (IS_ERR(obj)) { - p_err("failed to open BPF object file: %ld", PTR_ERR(obj)); + if (stat(file, &st)) { + p_err("failed to stat() %s: %s", file, strerror(errno)); return -1; } - + file_sz = st.st_size; + mmap_sz = roundup(file_sz, sysconf(_SC_PAGE_SIZE)); + fd = open(file, O_RDONLY); + if (fd < 0) { + p_err("failed to open() %s: %s", file, strerror(errno)); + return -1; + } + obj_data = mmap(NULL, mmap_sz, PROT_READ, MAP_PRIVATE, fd, 0); + if (obj_data == MAP_FAILED) { + obj_data = NULL; + p_err("failed to mmap() %s: %s", file, strerror(errno)); + goto out; + } get_obj_name(obj_name, file); - get_header_guard(header_guard, obj_name); + opts.object_name = obj_name; + obj = bpf_object__open_mem(obj_data, file_sz, &opts); + if (IS_ERR(obj)) { + obj = NULL; + p_err("failed to open BPF object file: %ld", PTR_ERR(obj)); + goto out; + } bpf_object__for_each_map(map, obj) { ident = get_map_ident(map); @@ -303,8 +323,11 @@ static int do_skeleton(int argc, char **argv) prog_cnt++; } + get_header_guard(header_guard, obj_name); codegen("\ \n\ + /* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */ \n\ + \n\ /* THIS FILE IS AUTOGENERATED! */ \n\ #ifndef %2$s \n\ #define %2$s \n\ @@ -356,19 +379,95 @@ static int do_skeleton(int argc, char **argv) \n\ }; \n\ \n\ - static inline struct bpf_object_skeleton * \n\ - %1$s__create_skeleton(struct %1$s *obj, struct bpf_embed_data *embed)\n\ + static void \n\ + %1$s__destroy(struct %1$s *obj) \n\ + { \n\ + if (!obj) \n\ + return; \n\ + if (obj->skeleton) \n\ + bpf_object__destroy_skeleton(obj->skeleton);\n\ + free(obj); \n\ + } \n\ + \n\ + static inline int \n\ + %1$s__create_skeleton(struct %1$s *obj); \n\ + \n\ + static inline struct %1$s * \n\ + %1$s__open_opts(const struct bpf_object_open_opts *opts) \n\ + { \n\ + struct %1$s *obj; \n\ + \n\ + obj = calloc(1, sizeof(*obj)); \n\ + if (!obj) \n\ + return NULL; \n\ + if (%1$s__create_skeleton(obj)) \n\ + goto err; \n\ + if (bpf_object__open_skeleton(obj->skeleton, opts)) \n\ + goto err; \n\ + \n\ + return obj; \n\ + err: \n\ + %1$s__destroy(obj); \n\ + return NULL; \n\ + } \n\ + \n\ + static inline struct %1$s * \n\ + %1$s__open(void) \n\ + { \n\ + return %1$s__open_opts(NULL); \n\ + } \n\ + \n\ + static inline int \n\ + %1$s__load(struct %1$s *obj) \n\ + { \n\ + return bpf_object__load_skeleton(obj->skeleton); \n\ + } \n\ + \n\ + static inline struct %1$s * \n\ + %1$s__open_and_load(void) \n\ + { \n\ + struct %1$s *obj; \n\ + \n\ + obj = %1$s__open(); \n\ + if (!obj) \n\ + return NULL; \n\ + if (%1$s__load(obj)) { \n\ + %1$s__destroy(obj); \n\ + return NULL; \n\ + } \n\ + return obj; \n\ + } \n\ + \n\ + static inline int \n\ + %1$s__attach(struct %1$s *obj) \n\ + { \n\ + return bpf_object__attach_skeleton(obj->skeleton); \n\ + } \n\ + \n\ + static inline void \n\ + %1$s__detach(struct %1$s *obj) \n\ + { \n\ + return bpf_object__detach_skeleton(obj->skeleton); \n\ + } \n\ + ", + obj_name + ); + + codegen("\ + \n\ + \n\ + static inline int \n\ + %1$s__create_skeleton(struct %1$s *obj) \n\ { \n\ struct bpf_object_skeleton *s; \n\ \n\ s = calloc(1, sizeof(*s)); \n\ if (!s) \n\ - return NULL; \n\ + return -1; \n\ + obj->skeleton = s; \n\ \n\ s->sz = sizeof(*s); \n\ s->name = \"%1$s\"; \n\ - s->data = embed->data; \n\ - s->data_sz = embed->size; \n\ s->obj = &obj->obj; \n\ ", obj_name @@ -438,90 +537,45 @@ static int do_skeleton(int argc, char **argv) codegen("\ \n\ \n\ - return s; \n\ - err: \n\ - bpf_object__destroy_skeleton(s); \n\ - return NULL; \n\ - } \n\ - \n\ - static void \n\ - %1$s__destroy(struct %1$s *obj) \n\ - { \n\ - if (!obj) \n\ - return; \n\ - if (obj->skeleton) \n\ - bpf_object__destroy_skeleton(obj->skeleton);\n\ - free(obj); \n\ - } \n\ - \n\ - static inline struct %1$s * \n\ - %1$s__open_opts(struct bpf_embed_data *embed, const struct bpf_object_open_opts *opts)\n\ - { \n\ - struct %1$s *obj; \n\ - \n\ - obj = calloc(1, sizeof(*obj)); \n\ - if (!obj) \n\ - return NULL; \n\ - \n\ - obj->skeleton = %1$s__create_skeleton(obj, embed); \n\ - if (!obj->skeleton) \n\ - goto err; \n\ - \n\ - if (bpf_object__open_skeleton(obj->skeleton, opts)) \n\ - goto err; \n\ + s->data_sz = %d; \n\ + s->data = \"\\ \n\ + ", + file_sz); + + /* embed contents of BPF object file */ + for (i = 0, len = 0; i < file_sz; i++) { + int w = obj_data[i] ? 4 : 2; + + len += w; + if (len > 78) { + printf("\\\n"); + len = w; + } + if (!obj_data[i]) + printf("\\0"); + else + printf("\\x%02x", (unsigned char)obj_data[i]); + } + + codegen("\ + \n\ + \"; \n\ \n\ - return obj; \n\ + return 0; \n\ err: \n\ - %1$s__destroy(obj); \n\ - return NULL; \n\ - } \n\ - \n\ - static inline struct %1$s * \n\ - %1$s__open(struct bpf_embed_data *embed) \n\ - { \n\ - return %1$s__open_opts(embed, NULL); \n\ - } \n\ - \n\ - static inline int \n\ - %1$s__load(struct %1$s *obj) \n\ - { \n\ - return bpf_object__load_skeleton(obj->skeleton); \n\ - } \n\ - \n\ - static inline struct %1$s * \n\ - %1$s__open_and_load(struct bpf_embed_data *embed) \n\ - { \n\ - struct %1$s *obj; \n\ - \n\ - obj = %1$s__open(embed); \n\ - if (!obj) \n\ - return NULL; \n\ - if (%1$s__load(obj)) { \n\ - %1$s__destroy(obj); \n\ - return NULL; \n\ - } \n\ - return obj; \n\ - } \n\ - \n\ - static inline int \n\ - %1$s__attach(struct %1$s *obj) \n\ - { \n\ - return bpf_object__attach_skeleton(obj->skeleton); \n\ - } \n\ - \n\ - static inline void \n\ - %1$s__detach(struct %1$s *obj) \n\ - { \n\ - return bpf_object__detach_skeleton(obj->skeleton); \n\ + bpf_object__destroy_skeleton(s); \n\ + return -1; \n\ } \n\ \n\ #endif /* %2$s */ \n\ ", - obj_name, header_guard - ); + obj_name, header_guard); err = 0; out: bpf_object__close(obj); + if (obj_data) + munmap(obj_data, mmap_sz); + close(fd); return err; } diff --git a/tools/testing/selftests/bpf/prog_tests/attach_probe.c b/tools/testing/selftests/bpf/prog_tests/attach_probe.c index 60da1d08daa0b..5ed90ede2f1d5 100644 --- a/tools/testing/selftests/bpf/prog_tests/attach_probe.c +++ b/tools/testing/selftests/bpf/prog_tests/attach_probe.c @@ -22,8 +22,6 @@ ssize_t get_base_addr() { return -EINVAL; } -BPF_EMBED_OBJ(probe, "test_attach_probe.o"); - void test_attach_probe(void) { int duration = 0; @@ -39,7 +37,7 @@ void test_attach_probe(void) return; uprobe_offset = (size_t)&get_base_addr - base_addr; - skel = test_attach_probe__open_and_load(&probe_embed); + skel = test_attach_probe__open_and_load(); if (CHECK(!skel, "skel_open", "failed to open skeleton\n")) return; if (CHECK(!skel->bss, "check_bss", ".bss wasn't mmap()-ed\n")) diff --git a/tools/testing/selftests/bpf/prog_tests/core_extern.c b/tools/testing/selftests/bpf/prog_tests/core_extern.c index 30a7972e90120..5f03dc1de29e6 100644 --- a/tools/testing/selftests/bpf/prog_tests/core_extern.c +++ b/tools/testing/selftests/bpf/prog_tests/core_extern.c @@ -124,8 +124,6 @@ static struct test_case { { .name = "u64 (max+1)", .fails = 1, .cfg = CFG"CONFIG_ULONG=0x10000000000000000" }, }; -BPF_EMBED_OBJ(core_extern, "test_core_extern.o"); - void test_core_extern(void) { const uint32_t kern_ver = get_kernel_version(); @@ -159,7 +157,7 @@ void test_core_extern(void) opts.kconfig_path = tmp_cfg_path; } - skel = test_core_extern__open_opts(&core_extern_embed, &opts); + skel = test_core_extern__open_opts(&opts); if (CHECK(!skel, "skel_open", "skeleton open failed\n")) goto cleanup; err = test_core_extern__load(skel); diff --git a/tools/testing/selftests/bpf/prog_tests/fentry_fexit.c b/tools/testing/selftests/bpf/prog_tests/fentry_fexit.c index 110fcf053fd05..235ac4f67f5be 100644 --- a/tools/testing/selftests/bpf/prog_tests/fentry_fexit.c +++ b/tools/testing/selftests/bpf/prog_tests/fentry_fexit.c @@ -5,10 +5,6 @@ #include "fentry_test.skel.h" #include "fexit_test.skel.h" -BPF_EMBED_OBJ(pkt_access, "test_pkt_access.o"); -BPF_EMBED_OBJ(fentry, "fentry_test.o"); -BPF_EMBED_OBJ(fexit, "fexit_test.o"); - void test_fentry_fexit(void) { struct test_pkt_access *pkt_skel = NULL; @@ -18,13 +14,13 @@ void test_fentry_fexit(void) __u32 duration = 0, retval; int err, pkt_fd, i; - pkt_skel = test_pkt_access__open_and_load(&pkt_access_embed); + pkt_skel = test_pkt_access__open_and_load(); if (CHECK(!pkt_skel, "pkt_skel_load", "pkt_access skeleton failed\n")) return; - fentry_skel = fentry_test__open_and_load(&fentry_embed); + fentry_skel = fentry_test__open_and_load(); if (CHECK(!fentry_skel, "fentry_skel_load", "fentry skeleton failed\n")) goto close_prog; - fexit_skel = fexit_test__open_and_load(&fexit_embed); + fexit_skel = fexit_test__open_and_load(); if (CHECK(!fexit_skel, "fexit_skel_load", "fexit skeleton failed\n")) goto close_prog; diff --git a/tools/testing/selftests/bpf/prog_tests/fentry_test.c b/tools/testing/selftests/bpf/prog_tests/fentry_test.c index 46a4afdf507af..e1a379f5f7d29 100644 --- a/tools/testing/selftests/bpf/prog_tests/fentry_test.c +++ b/tools/testing/selftests/bpf/prog_tests/fentry_test.c @@ -4,9 +4,6 @@ #include "test_pkt_access.skel.h" #include "fentry_test.skel.h" -BPF_EMBED_OBJ_DECLARE(pkt_access); -BPF_EMBED_OBJ_DECLARE(fentry); - void test_fentry_test(void) { struct test_pkt_access *pkt_skel = NULL; @@ -15,10 +12,10 @@ void test_fentry_test(void) __u32 duration, retval; __u64 *result; - pkt_skel = test_pkt_access__open_and_load(&pkt_access_embed); + pkt_skel = test_pkt_access__open_and_load(); if (CHECK(!pkt_skel, "pkt_skel_load", "pkt_access skeleton failed\n")) return; - fentry_skel = fentry_test__open_and_load(&fentry_embed); + fentry_skel = fentry_test__open_and_load(); if (CHECK(!fentry_skel, "fentry_skel_load", "fentry skeleton failed\n")) goto cleanup; diff --git a/tools/testing/selftests/bpf/prog_tests/mmap.c b/tools/testing/selftests/bpf/prog_tests/mmap.c index 95a44d37ccea5..16a814eb4d645 100644 --- a/tools/testing/selftests/bpf/prog_tests/mmap.c +++ b/tools/testing/selftests/bpf/prog_tests/mmap.c @@ -13,8 +13,6 @@ static size_t roundup_page(size_t sz) return (sz + page_size - 1) / page_size * page_size; } -BPF_EMBED_OBJ(test_mmap, "test_mmap.o"); - void test_mmap(void) { const size_t bss_sz = roundup_page(sizeof(struct test_mmap__bss)); @@ -30,7 +28,7 @@ void test_mmap(void) __u64 val = 0; - skel = test_mmap__open_and_load(&test_mmap_embed); + skel = test_mmap__open_and_load(); if (CHECK(!skel, "skel_open_and_load", "skeleton open/load failed\n")) return; diff --git a/tools/testing/selftests/bpf/prog_tests/skeleton.c b/tools/testing/selftests/bpf/prog_tests/skeleton.c index 151cdad3ad0d0..ec6f2aec3853b 100644 --- a/tools/testing/selftests/bpf/prog_tests/skeleton.c +++ b/tools/testing/selftests/bpf/prog_tests/skeleton.c @@ -10,8 +10,6 @@ struct s { #include "test_skeleton.skel.h" -BPF_EMBED_OBJ(skeleton, "test_skeleton.o"); - void test_skeleton(void) { int duration = 0, err; @@ -19,7 +17,7 @@ void test_skeleton(void) struct test_skeleton__bss *bss; struct test_skeleton__externs *exts; - skel = test_skeleton__open(&skeleton_embed); + skel = test_skeleton__open(); if (CHECK(!skel, "skel_open", "failed to open skeleton\n")) return; diff --git a/tools/testing/selftests/bpf/prog_tests/stacktrace_build_id.c b/tools/testing/selftests/bpf/prog_tests/stacktrace_build_id.c index 4af8b8253f25a..e8399ae50e77e 100644 --- a/tools/testing/selftests/bpf/prog_tests/stacktrace_build_id.c +++ b/tools/testing/selftests/bpf/prog_tests/stacktrace_build_id.c @@ -2,8 +2,6 @@ #include #include "test_stacktrace_build_id.skel.h" -BPF_EMBED_OBJ(stacktrace_build_id, "test_stacktrace_build_id.o"); - void test_stacktrace_build_id(void) { @@ -18,7 +16,7 @@ void test_stacktrace_build_id(void) int retry = 1; retry: - skel = test_stacktrace_build_id__open_and_load(&stacktrace_build_id_embed); + skel = test_stacktrace_build_id__open_and_load(); if (CHECK(!skel, "skel_open_and_load", "skeleton open/load failed\n")) return; diff --git a/tools/testing/selftests/bpf/prog_tests/stacktrace_build_id_nmi.c b/tools/testing/selftests/bpf/prog_tests/stacktrace_build_id_nmi.c index 32fb03881a7b3..8974450a4bdb0 100644 --- a/tools/testing/selftests/bpf/prog_tests/stacktrace_build_id_nmi.c +++ b/tools/testing/selftests/bpf/prog_tests/stacktrace_build_id_nmi.c @@ -15,8 +15,6 @@ static __u64 read_perf_max_sample_freq(void) return sample_freq; } -BPF_EMBED_OBJ_DECLARE(stacktrace_build_id); - void test_stacktrace_build_id_nmi(void) { int control_map_fd, stackid_hmap_fd, stackmap_fd; @@ -37,7 +35,7 @@ void test_stacktrace_build_id_nmi(void) attr.sample_freq = read_perf_max_sample_freq(); retry: - skel = test_stacktrace_build_id__open(&stacktrace_build_id_embed); + skel = test_stacktrace_build_id__open(); if (CHECK(!skel, "skel_open", "skeleton open failed\n")) return; -- GitLab From 3bf3c79b772f4155312c000310abab700aba2200 Mon Sep 17 00:00:00 2001 From: Andrii Nakryiko Date: Tue, 17 Dec 2019 21:25:51 -0800 Subject: [PATCH 0878/1437] libbpf: Remove BPF_EMBED_OBJ macro from libbpf.h Drop BPF_EMBED_OBJ and struct bpf_embed_data now that skeleton automatically embeds contents of its source object file. While BPF_EMBED_OBJ is useful independently of skeleton, we are currently don't have any use cases utilizing it, so let's remove them until/if we need it. Signed-off-by: Andrii Nakryiko Signed-off-by: Alexei Starovoitov Acked-by: Yonghong Song Link: https://lore.kernel.org/bpf/20191218052552.2915188-3-andriin@fb.com --- tools/lib/bpf/libbpf.h | 35 ----------------------------------- 1 file changed, 35 deletions(-) diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h index 6340823871e20..f7084235bae95 100644 --- a/tools/lib/bpf/libbpf.h +++ b/tools/lib/bpf/libbpf.h @@ -600,41 +600,6 @@ bpf_program__bpil_offs_to_addr(struct bpf_prog_info_linear *info_linear); */ LIBBPF_API int libbpf_num_possible_cpus(void); -struct bpf_embed_data { - void *data; - size_t size; -}; - -#define BPF_EMBED_OBJ_DECLARE(NAME) \ -extern struct bpf_embed_data NAME##_embed; \ -extern char NAME##_data[]; \ -extern char NAME##_data_end[]; - -#define __BPF_EMBED_OBJ(NAME, PATH, SZ, ASM_TYPE) \ -asm ( \ -" .pushsection \".rodata\", \"a\", @progbits \n" \ -" .global "#NAME"_data \n" \ -#NAME"_data: \n" \ -" .incbin \"" PATH "\" \n" \ -" .global "#NAME"_data_end \n" \ -#NAME"_data_end: \n" \ -" .global "#NAME"_embed \n" \ -" .type "#NAME"_embed, @object \n" \ -" .size "#NAME"_size, "#SZ" \n" \ -" .align 8, \n" \ -#NAME"_embed: \n" \ -" "ASM_TYPE" "#NAME"_data \n" \ -" "ASM_TYPE" "#NAME"_data_end - "#NAME"_data \n" \ -" .popsection \n" \ -); \ -BPF_EMBED_OBJ_DECLARE(NAME) - -#if __SIZEOF_POINTER__ == 4 -#define BPF_EMBED_OBJ(NAME, PATH) __BPF_EMBED_OBJ(NAME, PATH, 8, ".long") -#else -#define BPF_EMBED_OBJ(NAME, PATH) __BPF_EMBED_OBJ(NAME, PATH, 16, ".quad") -#endif - struct bpf_map_skeleton { const char *name; struct bpf_map **map; -- GitLab From cb21ac58854605e6c15fd7da2c619823967b3140 Mon Sep 17 00:00:00 2001 From: Andrii Nakryiko Date: Tue, 17 Dec 2019 21:25:52 -0800 Subject: [PATCH 0879/1437] bpftool: Add gen subcommand manpage Add bpftool-gen.rst describing skeleton on the high level. Also include a small, but complete, example BPF app (BPF side, userspace side, generated skeleton) in example section to demonstrate skeleton API and its usage. Signed-off-by: Andrii Nakryiko Signed-off-by: Alexei Starovoitov Acked-by: Yonghong Song Link: https://lore.kernel.org/bpf/20191218052552.2915188-4-andriin@fb.com --- .../bpf/bpftool/Documentation/bpftool-gen.rst | 304 ++++++++++++++++++ tools/bpf/bpftool/Documentation/bpftool.rst | 3 +- 2 files changed, 306 insertions(+), 1 deletion(-) create mode 100644 tools/bpf/bpftool/Documentation/bpftool-gen.rst diff --git a/tools/bpf/bpftool/Documentation/bpftool-gen.rst b/tools/bpf/bpftool/Documentation/bpftool-gen.rst new file mode 100644 index 0000000000000..b6a114bf908de --- /dev/null +++ b/tools/bpf/bpftool/Documentation/bpftool-gen.rst @@ -0,0 +1,304 @@ +================ +bpftool-gen +================ +------------------------------------------------------------------------------- +tool for BPF code-generation +------------------------------------------------------------------------------- + +:Manual section: 8 + +SYNOPSIS +======== + + **bpftool** [*OPTIONS*] **gen** *COMMAND* + + *OPTIONS* := { { **-j** | **--json** } [{ **-p** | **--pretty** }] } + + *COMMAND* := { **skeleton | **help** } + +GEN COMMANDS +============= + +| **bpftool** **gen skeleton** *FILE* +| **bpftool** **gen help** + +DESCRIPTION +=========== + **bpftool gen skeleton** *FILE* + Generate BPF skeleton C header file for a given *FILE*. + + BPF skeleton is an alternative interface to existing libbpf + APIs for working with BPF objects. Skeleton code is intended + to significantly shorten and simplify code to load and work + with BPF programs from userspace side. Generated code is + tailored to specific input BPF object *FILE*, reflecting its + structure by listing out available maps, program, variables, + etc. Skeleton eliminates the need to lookup mentioned + components by name. Instead, if skeleton instantiation + succeeds, they are populated in skeleton structure as valid + libbpf types (e.g., struct bpf_map pointer) and can be + passed to existing generic libbpf APIs. + + In addition to simple and reliable access to maps and + programs, skeleton provides a storage for BPF links (struct + bpf_link) for each BPF program within BPF object. When + requested, supported BPF programs will be automatically + attached and resulting BPF links stored for further use by + user in pre-allocated fields in skeleton struct. For BPF + programs that can't be automatically attached by libbpf, + user can attach them manually, but store resulting BPF link + in per-program link field. All such set up links will be + automatically destroyed on BPF skeleton destruction. This + eliminates the need for users to manage links manually and + rely on libbpf support to detach programs and free up + resources. + + Another facility provided by BPF skeleton is an interface to + global variables of all supported kinds: mutable, read-only, + as well as extern ones. This interface allows to pre-setup + initial values of variables before BPF object is loaded and + verified by kernel. For non-read-only variables, the same + interface can be used to fetch values of global variables on + userspace side, even if they are modified by BPF code. + + During skeleton generation, contents of source BPF object + *FILE* is embedded within generated code and is thus not + necessary to keep around. This ensures skeleton and BPF + object file are matching 1-to-1 and always stay in sync. + Generated code is dual-licensed under LGPL-2.1 and + BSD-2-Clause licenses. + + It is a design goal and guarantee that skeleton interfaces + are interoperable with generic libbpf APIs. User should + always be able to use skeleton API to create and load BPF + object, and later use libbpf APIs to keep working with + specific maps, programs, etc. + + As part of skeleton, few custom functions are generated. + Each of them is prefixed with object name, derived from + object file name. I.e., if BPF object file name is + **example.o**, BPF object name will be **example**. The + following custom functions are provided in such case: + + - **example__open** and **example__open_opts**. + These functions are used to instantiate skeleton. It + corresponds to libbpf's **bpf_object__open()** API. + **_opts** variants accepts extra **bpf_object_open_opts** + options. + + - **example__load**. + This function creates maps, loads and verifies BPF + programs, initializes global data maps. It corresponds to + libppf's **bpf_object__load** API. + + - **example__open_and_load** combines **example__open** and + **example__load** invocations in one commonly used + operation. + + - **example__attach** and **example__detach** + This pair of functions allow to attach and detach, + correspondingly, already loaded BPF object. Only BPF + programs of types supported by libbpf for auto-attachment + will be auto-attached and their corresponding BPF links + instantiated. For other BPF programs, user can manually + create a BPF link and assign it to corresponding fields in + skeleton struct. **example__detach** will detach both + links created automatically, as well as those populated by + user manually. + + - **example__destroy** + Detach and unload BPF programs, free up all the resources + used by skeleton and BPF object. + + If BPF object has global variables, corresponding structs + with memory layout corresponding to global data data section + layout will be created. Currently supported ones are: .data, + .bss, .rodata, and .extern structs/data sections. These + data sections/structs can be used to set up initial values of + variables, if set before **example__load**. Afterwards, if + target kernel supports memory-mapped BPF arrays, same + structs can be used to fetch and update (non-read-only) + data from userspace, with same simplicity as for BPF side. + + **bpftool gen help** + Print short help message. + +OPTIONS +======= + -h, --help + Print short generic help message (similar to **bpftool help**). + + -V, --version + Print version number (similar to **bpftool version**). + + -j, --json + Generate JSON output. For commands that cannot produce JSON, + this option has no effect. + + -p, --pretty + Generate human-readable JSON output. Implies **-j**. + + -d, --debug + Print all logs available from libbpf, including debug-level + information. + +EXAMPLES +======== +**$ cat example.c** +:: + + #include + #include + #include + #include "bpf_helpers.h" + + const volatile int param1 = 42; + bool global_flag = true; + struct { int x; } data = {}; + + struct { + __uint(type, BPF_MAP_TYPE_HASH); + __uint(max_entries, 128); + __type(key, int); + __type(value, long); + } my_map SEC(".maps"); + + SEC("raw_tp/sys_enter") + int handle_sys_enter(struct pt_regs *ctx) + { + static long my_static_var; + if (global_flag) + my_static_var++; + else + data.x += param1; + return 0; + } + + SEC("raw_tp/sys_exit") + int handle_sys_exit(struct pt_regs *ctx) + { + int zero = 0; + bpf_map_lookup_elem(&my_map, &zero); + return 0; + } + +This is example BPF application with two BPF programs and a mix of BPF maps +and global variables. + +**$ bpftool gen skeleton example.o** +:: + + /* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */ + + /* THIS FILE IS AUTOGENERATED! */ + #ifndef __EXAMPLE_SKEL_H__ + #define __EXAMPLE_SKEL_H__ + + #include + #include + + struct example { + struct bpf_object_skeleton *skeleton; + struct bpf_object *obj; + struct { + struct bpf_map *rodata; + struct bpf_map *data; + struct bpf_map *bss; + struct bpf_map *my_map; + } maps; + struct { + struct bpf_program *handle_sys_enter; + struct bpf_program *handle_sys_exit; + } progs; + struct { + struct bpf_link *handle_sys_enter; + struct bpf_link *handle_sys_exit; + } links; + struct example__bss { + struct { + int x; + } data; + } *bss; + struct example__data { + _Bool global_flag; + long int handle_sys_enter_my_static_var; + } *data; + struct example__rodata { + int param1; + } *rodata; + }; + + static void example__destroy(struct example *obj); + static inline struct example *example__open_opts( + const struct bpf_object_open_opts *opts); + static inline struct example *example__open(); + static inline int example__load(struct example *obj); + static inline struct example *example__open_and_load(); + static inline int example__attach(struct example *obj); + static inline void example__detach(struct example *obj); + + #endif /* __EXAMPLE_SKEL_H__ */ + +**$ cat example_user.c** +:: + + #include "example.skel.h" + + int main() + { + struct example *skel; + int err = 0; + + skel = example__open(); + if (!skel) + goto cleanup; + + skel->rodata->param1 = 128; + + err = example__load(skel); + if (err) + goto cleanup; + + err = example__attach(skel); + if (err) + goto cleanup; + + /* all libbpf APIs are usable */ + printf("my_map name: %s\n", bpf_map__name(skel->maps.my_map)); + printf("sys_enter prog FD: %d\n", + bpf_program__fd(skel->progs.handle_sys_enter)); + + /* detach and re-attach sys_exit program */ + bpf_link__destroy(skel->links.handle_sys_exit); + skel->links.handle_sys_exit = + bpf_program__attach(skel->progs.handle_sys_exit); + + printf("my_static_var: %ld\n", + skel->bss->handle_sys_enter_my_static_var); + + cleanup: + example__destroy(skel); + return err; + } + +**# ./example_user** +:: + + my_map name: my_map + sys_enter prog FD: 8 + my_static_var: 7 + +This is a stripped-out version of skeleton generated for above example code. + +SEE ALSO +======== + **bpf**\ (2), + **bpf-helpers**\ (7), + **bpftool**\ (8), + **bpftool-map**\ (8), + **bpftool-prog**\ (8), + **bpftool-cgroup**\ (8), + **bpftool-feature**\ (8), + **bpftool-net**\ (8), + **bpftool-perf**\ (8), + **bpftool-btf**\ (8) diff --git a/tools/bpf/bpftool/Documentation/bpftool.rst b/tools/bpf/bpftool/Documentation/bpftool.rst index 6a9c52ef84a9e..34239fda69ed0 100644 --- a/tools/bpf/bpftool/Documentation/bpftool.rst +++ b/tools/bpf/bpftool/Documentation/bpftool.rst @@ -81,4 +81,5 @@ SEE ALSO **bpftool-feature**\ (8), **bpftool-net**\ (8), **bpftool-perf**\ (8), - **bpftool-btf**\ (8) + **bpftool-btf**\ (8), + **bpftool-gen**\ (8), -- GitLab From 3646ae0de90eb271ef48ec1fa75e63873ab20fc6 Mon Sep 17 00:00:00 2001 From: Shahjada Abul Husain Date: Tue, 17 Dec 2019 12:12:08 +0530 Subject: [PATCH 0880/1437] cxgb4: fix missed high priority region calculation commit c21939998802 ("cxgb4: add support for high priority filters") has missed considering high priority region calculation in some code paths. This patch fixes them. Fixes: c21939998802 ("cxgb4: add support for high priority filters") Signed-off-by: Shahjada Abul Husain Signed-off-by: David S. Miller --- .../net/ethernet/chelsio/cxgb4/cxgb4_filter.c | 21 +++++++++++++------ .../ethernet/chelsio/cxgb4/cxgb4_tc_flower.c | 3 ++- .../chelsio/cxgb4/cxgb4_tc_matchall.c | 2 +- 3 files changed, 18 insertions(+), 8 deletions(-) diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_filter.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_filter.c index 6a5b0346dad93..44deabfa32b51 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_filter.c +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_filter.c @@ -369,12 +369,16 @@ static int get_filter_count(struct adapter *adapter, unsigned int fidx, return -E2BIG; } } else { - if ((fidx != (adapter->tids.nftids + - adapter->tids.nsftids - 1)) && - fidx >= adapter->tids.nftids) + if ((fidx != (adapter->tids.nftids + adapter->tids.nsftids + + adapter->tids.nhpftids - 1)) && + fidx >= (adapter->tids.nftids + adapter->tids.nhpftids)) return -E2BIG; - f = &adapter->tids.ftid_tab[fidx]; + if (fidx < adapter->tids.nhpftids) + f = &adapter->tids.hpftid_tab[fidx]; + else + f = &adapter->tids.ftid_tab[fidx - + adapter->tids.nhpftids]; if (!f->valid) return -EINVAL; } @@ -480,6 +484,7 @@ int cxgb4_get_free_ftid(struct net_device *dev, int family) ftid -= n; } spin_unlock_bh(&t->ftid_lock); + ftid += t->nhpftids; return found ? ftid : -ENOMEM; } @@ -815,10 +820,14 @@ int delete_filter(struct adapter *adapter, unsigned int fidx) struct filter_entry *f; int ret; - if (fidx >= adapter->tids.nftids + adapter->tids.nsftids) + if (fidx >= adapter->tids.nftids + adapter->tids.nsftids + + adapter->tids.nhpftids) return -EINVAL; - f = &adapter->tids.ftid_tab[fidx]; + if (fidx < adapter->tids.nhpftids) + f = &adapter->tids.hpftid_tab[fidx]; + else + f = &adapter->tids.ftid_tab[fidx - adapter->tids.nhpftids]; ret = writable_filter(f); if (ret) return ret; diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_flower.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_flower.c index 706b71e1b6c16..bb5513bdd293e 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_flower.c +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_flower.c @@ -672,7 +672,8 @@ int cxgb4_tc_flower_replace(struct net_device *dev, * 0 to driver. However, the hardware TCAM index * starts from 0. Hence, the -1 here. */ - if (cls->common.prio <= adap->tids.nftids) { + if (cls->common.prio <= (adap->tids.nftids + + adap->tids.nhpftids)) { fidx = cls->common.prio - 1; if (fidx < adap->tids.nhpftids) fs->prio = 1; diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_matchall.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_matchall.c index 53e8db8b25469..24c3c2dc7171a 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_matchall.c +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_matchall.c @@ -137,7 +137,7 @@ static int cxgb4_matchall_alloc_filter(struct net_device *dev, * -1 here. 1 slot is enough to create a wildcard matchall * VIID rule. */ - if (cls->common.prio <= adap->tids.nftids) + if (cls->common.prio <= (adap->tids.nftids + adap->tids.nhpftids)) fidx = cls->common.prio - 1; else fidx = cxgb4_get_free_ftid(dev, PF_INET); -- GitLab From 59437d78f088e506cabdd5e0f0601c531a9830b4 Mon Sep 17 00:00:00 2001 From: Shahjada Abul Husain Date: Tue, 17 Dec 2019 12:12:09 +0530 Subject: [PATCH 0881/1437] cxgb4/chtls: fix ULD connection failures due to wrong TID base Currently, the hardware TID index is assumed to start from index 0. However, with the following changeset, commit c21939998802 ("cxgb4: add support for high priority filters") hardware TID index can start after the high priority region, which has introduced a regression resulting in connection failures for ULDs. So, fix all related code to properly recalculate the TID start index based on whether high priority filters are enabled or not. Fixes: c21939998802 ("cxgb4: add support for high priority filters") Signed-off-by: Shahjada Abul Husain Signed-off-by: David S. Miller --- drivers/crypto/chelsio/chtls/chtls_cm.c | 2 +- .../ethernet/chelsio/cxgb4/cxgb4_debugfs.c | 22 +++++++++---------- .../net/ethernet/chelsio/cxgb4/cxgb4_filter.c | 12 +++++----- .../net/ethernet/chelsio/cxgb4/cxgb4_main.c | 13 ++++++----- .../net/ethernet/chelsio/cxgb4/cxgb4_uld.h | 9 +++++++- 5 files changed, 32 insertions(+), 26 deletions(-) diff --git a/drivers/crypto/chelsio/chtls/chtls_cm.c b/drivers/crypto/chelsio/chtls/chtls_cm.c index aca75237bbcf8..72e5b0f65a916 100644 --- a/drivers/crypto/chelsio/chtls/chtls_cm.c +++ b/drivers/crypto/chelsio/chtls/chtls_cm.c @@ -1273,7 +1273,7 @@ static int chtls_pass_accept_req(struct chtls_dev *cdev, struct sk_buff *skb) ctx = (struct listen_ctx *)data; lsk = ctx->lsk; - if (unlikely(tid >= cdev->tids->ntids)) { + if (unlikely(tid_out_of_range(cdev->tids, tid))) { pr_info("passive open TID %u too large\n", tid); return 1; } diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c index b6a79536e1432..9a96b01dad560 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c @@ -3171,14 +3171,12 @@ static const struct file_operations mem_debugfs_fops = { static int tid_info_show(struct seq_file *seq, void *v) { - unsigned int tid_start = 0; struct adapter *adap = seq->private; - const struct tid_info *t = &adap->tids; - enum chip_type chip = CHELSIO_CHIP_VERSION(adap->params.chip); - - if (chip > CHELSIO_T5) - tid_start = t4_read_reg(adap, LE_DB_ACTIVE_TABLE_START_INDEX_A); + const struct tid_info *t; + enum chip_type chip; + t = &adap->tids; + chip = CHELSIO_CHIP_VERSION(adap->params.chip); if (t4_read_reg(adap, LE_DB_CONFIG_A) & HASHEN_F) { unsigned int sb; seq_printf(seq, "Connections in use: %u\n", @@ -3190,9 +3188,9 @@ static int tid_info_show(struct seq_file *seq, void *v) sb = t4_read_reg(adap, LE_DB_SRVR_START_INDEX_A); if (sb) { - seq_printf(seq, "TID range: %u..%u/%u..%u", tid_start, + seq_printf(seq, "TID range: %u..%u/%u..%u", t->tid_base, sb - 1, adap->tids.hash_base, - t->ntids - 1); + t->tid_base + t->ntids - 1); seq_printf(seq, ", in use: %u/%u\n", atomic_read(&t->tids_in_use), atomic_read(&t->hash_tids_in_use)); @@ -3201,14 +3199,14 @@ static int tid_info_show(struct seq_file *seq, void *v) t->aftid_base, t->aftid_end, adap->tids.hash_base, - t->ntids - 1); + t->tid_base + t->ntids - 1); seq_printf(seq, ", in use: %u/%u\n", atomic_read(&t->tids_in_use), atomic_read(&t->hash_tids_in_use)); } else { seq_printf(seq, "TID range: %u..%u", adap->tids.hash_base, - t->ntids - 1); + t->tid_base + t->ntids - 1); seq_printf(seq, ", in use: %u\n", atomic_read(&t->hash_tids_in_use)); } @@ -3216,8 +3214,8 @@ static int tid_info_show(struct seq_file *seq, void *v) seq_printf(seq, "Connections in use: %u\n", atomic_read(&t->conns_in_use)); - seq_printf(seq, "TID range: %u..%u", tid_start, - tid_start + t->ntids - 1); + seq_printf(seq, "TID range: %u..%u", t->tid_base, + t->tid_base + t->ntids - 1); seq_printf(seq, ", in use: %u\n", atomic_read(&t->tids_in_use)); } diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_filter.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_filter.c index 44deabfa32b51..2a2938bbb93a3 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_filter.c +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_filter.c @@ -361,13 +361,11 @@ static int get_filter_count(struct adapter *adapter, unsigned int fidx, tcb_base = t4_read_reg(adapter, TP_CMM_TCB_BASE_A); if (is_hashfilter(adapter) && hash) { - if (fidx < adapter->tids.ntids) { - f = adapter->tids.tid_tab[fidx]; - if (!f) - return -EINVAL; - } else { + if (tid_out_of_range(&adapter->tids, fidx)) return -E2BIG; - } + f = adapter->tids.tid_tab[fidx - adapter->tids.tid_base]; + if (!f) + return -EINVAL; } else { if ((fidx != (adapter->tids.nftids + adapter->tids.nsftids + adapter->tids.nhpftids - 1)) && @@ -1615,7 +1613,7 @@ static int cxgb4_del_hash_filter(struct net_device *dev, int filter_id, netdev_dbg(dev, "%s: filter_id = %d ; nftids = %d\n", __func__, filter_id, adapter->tids.nftids); - if (filter_id > adapter->tids.ntids) + if (tid_out_of_range(t, filter_id)) return -E2BIG; f = lookup_tid(t, filter_id); diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c index be750f2de23cd..1930e39f195e9 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c @@ -1447,8 +1447,8 @@ static void mk_tid_release(struct sk_buff *skb, unsigned int chan, static void cxgb4_queue_tid_release(struct tid_info *t, unsigned int chan, unsigned int tid) { - void **p = &t->tid_tab[tid]; struct adapter *adap = container_of(t, struct adapter, tids); + void **p = &t->tid_tab[tid - t->tid_base]; spin_lock_bh(&adap->tid_release_lock); *p = adap->tid_release_head; @@ -1500,13 +1500,13 @@ static void process_tid_release_list(struct work_struct *work) void cxgb4_remove_tid(struct tid_info *t, unsigned int chan, unsigned int tid, unsigned short family) { - struct sk_buff *skb; struct adapter *adap = container_of(t, struct adapter, tids); + struct sk_buff *skb; - WARN_ON(tid >= t->ntids); + WARN_ON(tid_out_of_range(&adap->tids, tid)); - if (t->tid_tab[tid]) { - t->tid_tab[tid] = NULL; + if (t->tid_tab[tid - adap->tids.tid_base]) { + t->tid_tab[tid - adap->tids.tid_base] = NULL; atomic_dec(&t->conns_in_use); if (t->hash_base && (tid >= t->hash_base)) { if (family == AF_INET6) @@ -4727,6 +4727,9 @@ static int adap_init0(struct adapter *adap, int vpd_skip) adap->rawf_start = val[0]; adap->rawf_cnt = val[1] - val[0] + 1; } + + adap->tids.tid_base = + t4_read_reg(adap, LE_DB_ACTIVE_TABLE_START_INDEX_A); } /* qids (ingress/egress) returned from firmware can be anywhere diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_uld.h b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_uld.h index e69de9a296aeb..d9d27bc1ae67c 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_uld.h +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_uld.h @@ -99,6 +99,7 @@ struct eotid_entry { */ struct tid_info { void **tid_tab; + unsigned int tid_base; unsigned int ntids; struct serv_entry *stid_tab; @@ -152,9 +153,15 @@ struct tid_info { static inline void *lookup_tid(const struct tid_info *t, unsigned int tid) { + tid -= t->tid_base; return tid < t->ntids ? t->tid_tab[tid] : NULL; } +static inline bool tid_out_of_range(const struct tid_info *t, unsigned int tid) +{ + return ((tid - t->tid_base) >= t->ntids); +} + static inline void *lookup_atid(const struct tid_info *t, unsigned int atid) { return atid < t->natids ? t->atid_tab[atid].data : NULL; @@ -176,7 +183,7 @@ static inline void *lookup_stid(const struct tid_info *t, unsigned int stid) static inline void cxgb4_insert_tid(struct tid_info *t, void *data, unsigned int tid, unsigned short family) { - t->tid_tab[tid] = data; + t->tid_tab[tid - t->tid_base] = data; if (t->hash_base && (tid >= t->hash_base)) { if (family == AF_INET6) atomic_add(2, &t->hash_tids_in_use); -- GitLab From 9d4b98af8a2eb2ddb7779f2929700b5c174e9cc9 Mon Sep 17 00:00:00 2001 From: Oleksij Rempel Date: Tue, 17 Dec 2019 07:51:45 +0100 Subject: [PATCH 0882/1437] net: ag71xx: fix compile warnings drivers/net/ethernet/atheros/ag71xx.c: In function 'ag71xx_probe': drivers/net/ethernet/atheros/ag71xx.c:1776:30: warning: passing argument 2 of 'of_get_phy_mode' makes pointer from integer without a cast [-Wint-conversion] In file included from drivers/net/ethernet/atheros/ag71xx.c:33: ./include/linux/of_net.h:15:69: note: expected 'phy_interface_t *' {aka 'enum *'} but argument is of type 'int' Fixes: 0c65b2b90d13c1 ("net: of_get_phy_mode: Change API to solve int/unit warnings") Signed-off-by: Oleksij Rempel Reviewed-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/ethernet/atheros/ag71xx.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/atheros/ag71xx.c b/drivers/net/ethernet/atheros/ag71xx.c index 8f5021091eeed..61a334d1b5e68 100644 --- a/drivers/net/ethernet/atheros/ag71xx.c +++ b/drivers/net/ethernet/atheros/ag71xx.c @@ -313,7 +313,7 @@ struct ag71xx { struct ag71xx_desc *stop_desc; dma_addr_t stop_desc_dma; - int phy_if_mode; + phy_interface_t phy_if_mode; struct delayed_work restart_work; struct timer_list oom_timer; @@ -1744,7 +1744,7 @@ static int ag71xx_probe(struct platform_device *pdev) eth_random_addr(ndev->dev_addr); } - err = of_get_phy_mode(np, ag->phy_if_mode); + err = of_get_phy_mode(np, &ag->phy_if_mode); if (err) { netif_err(ag, probe, ndev, "missing phy-mode property in DT\n"); goto err_free; -- GitLab From 4e2ce6e55050125d313408770d20dbf387dfc79c Mon Sep 17 00:00:00 2001 From: "Ben Dooks (Codethink)" Date: Tue, 17 Dec 2019 11:20:38 +0000 Subject: [PATCH 0883/1437] net: dsa: make unexported dsa_link_touch() static dsa_link_touch() is not exported, or defined outside of the file it is in so make it static to avoid the following warning: net/dsa/dsa2.c:127:17: warning: symbol 'dsa_link_touch' was not declared. Should it be static? Signed-off-by: Ben Dooks (Codethink) Signed-off-by: David S. Miller --- net/dsa/dsa2.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/net/dsa/dsa2.c b/net/dsa/dsa2.c index 9ef2caa13f278..c66abbed4daf2 100644 --- a/net/dsa/dsa2.c +++ b/net/dsa/dsa2.c @@ -124,7 +124,8 @@ static struct dsa_port *dsa_tree_find_port_by_node(struct dsa_switch_tree *dst, return NULL; } -struct dsa_link *dsa_link_touch(struct dsa_port *dp, struct dsa_port *link_dp) +static struct dsa_link *dsa_link_touch(struct dsa_port *dp, + struct dsa_port *link_dp) { struct dsa_switch *ds = dp->ds; struct dsa_switch_tree *dst; -- GitLab From 39f14c00b13c47186739a4cbc7a16e04d7fdbb60 Mon Sep 17 00:00:00 2001 From: John Hurley Date: Tue, 17 Dec 2019 11:28:56 +0000 Subject: [PATCH 0884/1437] nfp: flower: fix stats id allocation As flower rules are added, they are given a stats ID based on the number of rules that can be supported in firmware. Only after the initial allocation of all available IDs does the driver begin to reuse those that have been released. The initial allocation of IDs was modified to account for multiple memory units on the offloaded device. However, this introduced a bug whereby the counter that controls the IDs could be decremented before the ID was assigned (where it is further decremented). This means that the stats ID could be assigned as -1/0xfffffff which is out of range. Fix this by only decrementing the main counter after the current ID has been assigned. Fixes: 467322e2627f ("nfp: flower: support multiple memory units for filter offloads") Signed-off-by: John Hurley Reviewed-by: Jakub Kicinski Signed-off-by: David S. Miller --- drivers/net/ethernet/netronome/nfp/flower/metadata.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/net/ethernet/netronome/nfp/flower/metadata.c b/drivers/net/ethernet/netronome/nfp/flower/metadata.c index 7c4a15e967df3..5defd31d481c2 100644 --- a/drivers/net/ethernet/netronome/nfp/flower/metadata.c +++ b/drivers/net/ethernet/netronome/nfp/flower/metadata.c @@ -65,17 +65,17 @@ static int nfp_get_stats_entry(struct nfp_app *app, u32 *stats_context_id) freed_stats_id = priv->stats_ring_size; /* Check for unallocated entries first. */ if (priv->stats_ids.init_unalloc > 0) { - if (priv->active_mem_unit == priv->total_mem_units) { - priv->stats_ids.init_unalloc--; - priv->active_mem_unit = 0; - } - *stats_context_id = FIELD_PREP(NFP_FL_STAT_ID_STAT, priv->stats_ids.init_unalloc - 1) | FIELD_PREP(NFP_FL_STAT_ID_MU_NUM, priv->active_mem_unit); - priv->active_mem_unit++; + + if (++priv->active_mem_unit == priv->total_mem_units) { + priv->stats_ids.init_unalloc--; + priv->active_mem_unit = 0; + } + return 0; } -- GitLab From ddd9b5e3e765d8ed5a35786a6cb00111713fe161 Mon Sep 17 00:00:00 2001 From: Jouni Hogander Date: Tue, 17 Dec 2019 13:46:34 +0200 Subject: [PATCH 0885/1437] net-sysfs: Call dev_hold always in rx_queue_add_kobject Dev_hold has to be called always in rx_queue_add_kobject. Otherwise usage count drops below 0 in case of failure in kobject_init_and_add. Fixes: b8eb718348b8 ("net-sysfs: Fix reference count leak in rx|netdev_queue_add_kobject") Reported-by: syzbot Cc: Tetsuo Handa Cc: David Miller Cc: Lukas Bulwahn Signed-off-by: Jouni Hogander Signed-off-by: David S. Miller --- net/core/net-sysfs.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/net/core/net-sysfs.c b/net/core/net-sysfs.c index 5c4624298996c..4c826b8bf9b1e 100644 --- a/net/core/net-sysfs.c +++ b/net/core/net-sysfs.c @@ -919,14 +919,17 @@ static int rx_queue_add_kobject(struct net_device *dev, int index) struct kobject *kobj = &queue->kobj; int error = 0; + /* Kobject_put later will trigger rx_queue_release call which + * decreases dev refcount: Take that reference here + */ + dev_hold(queue->dev); + kobj->kset = dev->queues_kset; error = kobject_init_and_add(kobj, &rx_queue_ktype, NULL, "rx-%u", index); if (error) goto err; - dev_hold(queue->dev); - if (dev->sysfs_rx_queue_group) { error = sysfs_create_group(kobj, dev->sysfs_rx_queue_group); if (error) -- GitLab From 92fbeb43b6c44fb03e024c4c5b9f3d6b8ebd0cf0 Mon Sep 17 00:00:00 2001 From: Paul Durrant Date: Tue, 17 Dec 2019 13:32:16 +0000 Subject: [PATCH 0886/1437] xen-netback: move netback_probe() and netback_remove() to the end... ...of xenbus.c This is a cosmetic function re-ordering to reduce churn in a subsequent patch. Some style fix-up was done to make checkpatch.pl happier. No functional change. Signed-off-by: Paul Durrant Acked-by: Wei Liu Signed-off-by: David S. Miller --- drivers/net/xen-netback/xenbus.c | 353 +++++++++++++++---------------- 1 file changed, 174 insertions(+), 179 deletions(-) diff --git a/drivers/net/xen-netback/xenbus.c b/drivers/net/xen-netback/xenbus.c index f533b7372d598..bb61316d79de7 100644 --- a/drivers/net/xen-netback/xenbus.c +++ b/drivers/net/xen-netback/xenbus.c @@ -195,185 +195,6 @@ static void xenvif_debugfs_delif(struct xenvif *vif) } #endif /* CONFIG_DEBUG_FS */ -static int netback_remove(struct xenbus_device *dev) -{ - struct backend_info *be = dev_get_drvdata(&dev->dev); - - set_backend_state(be, XenbusStateClosed); - - unregister_hotplug_status_watch(be); - if (be->vif) { - kobject_uevent(&dev->dev.kobj, KOBJ_OFFLINE); - xen_unregister_watchers(be->vif); - xenbus_rm(XBT_NIL, dev->nodename, "hotplug-status"); - xenvif_free(be->vif); - be->vif = NULL; - } - kfree(be->hotplug_script); - kfree(be); - dev_set_drvdata(&dev->dev, NULL); - return 0; -} - - -/** - * Entry point to this code when a new device is created. Allocate the basic - * structures and switch to InitWait. - */ -static int netback_probe(struct xenbus_device *dev, - const struct xenbus_device_id *id) -{ - const char *message; - struct xenbus_transaction xbt; - int err; - int sg; - const char *script; - struct backend_info *be = kzalloc(sizeof(struct backend_info), - GFP_KERNEL); - if (!be) { - xenbus_dev_fatal(dev, -ENOMEM, - "allocating backend structure"); - return -ENOMEM; - } - - be->dev = dev; - dev_set_drvdata(&dev->dev, be); - - be->state = XenbusStateInitialising; - err = xenbus_switch_state(dev, XenbusStateInitialising); - if (err) - goto fail; - - sg = 1; - - do { - err = xenbus_transaction_start(&xbt); - if (err) { - xenbus_dev_fatal(dev, err, "starting transaction"); - goto fail; - } - - err = xenbus_printf(xbt, dev->nodename, "feature-sg", "%d", sg); - if (err) { - message = "writing feature-sg"; - goto abort_transaction; - } - - err = xenbus_printf(xbt, dev->nodename, "feature-gso-tcpv4", - "%d", sg); - if (err) { - message = "writing feature-gso-tcpv4"; - goto abort_transaction; - } - - err = xenbus_printf(xbt, dev->nodename, "feature-gso-tcpv6", - "%d", sg); - if (err) { - message = "writing feature-gso-tcpv6"; - goto abort_transaction; - } - - /* We support partial checksum setup for IPv6 packets */ - err = xenbus_printf(xbt, dev->nodename, - "feature-ipv6-csum-offload", - "%d", 1); - if (err) { - message = "writing feature-ipv6-csum-offload"; - goto abort_transaction; - } - - /* We support rx-copy path. */ - err = xenbus_printf(xbt, dev->nodename, - "feature-rx-copy", "%d", 1); - if (err) { - message = "writing feature-rx-copy"; - goto abort_transaction; - } - - /* - * We don't support rx-flip path (except old guests who don't - * grok this feature flag). - */ - err = xenbus_printf(xbt, dev->nodename, - "feature-rx-flip", "%d", 0); - if (err) { - message = "writing feature-rx-flip"; - goto abort_transaction; - } - - /* We support dynamic multicast-control. */ - err = xenbus_printf(xbt, dev->nodename, - "feature-multicast-control", "%d", 1); - if (err) { - message = "writing feature-multicast-control"; - goto abort_transaction; - } - - err = xenbus_printf(xbt, dev->nodename, - "feature-dynamic-multicast-control", - "%d", 1); - if (err) { - message = "writing feature-dynamic-multicast-control"; - goto abort_transaction; - } - - err = xenbus_transaction_end(xbt, 0); - } while (err == -EAGAIN); - - if (err) { - xenbus_dev_fatal(dev, err, "completing transaction"); - goto fail; - } - - /* - * Split event channels support, this is optional so it is not - * put inside the above loop. - */ - err = xenbus_printf(XBT_NIL, dev->nodename, - "feature-split-event-channels", - "%u", separate_tx_rx_irq); - if (err) - pr_debug("Error writing feature-split-event-channels\n"); - - /* Multi-queue support: This is an optional feature. */ - err = xenbus_printf(XBT_NIL, dev->nodename, - "multi-queue-max-queues", "%u", xenvif_max_queues); - if (err) - pr_debug("Error writing multi-queue-max-queues\n"); - - err = xenbus_printf(XBT_NIL, dev->nodename, - "feature-ctrl-ring", - "%u", true); - if (err) - pr_debug("Error writing feature-ctrl-ring\n"); - - script = xenbus_read(XBT_NIL, dev->nodename, "script", NULL); - if (IS_ERR(script)) { - err = PTR_ERR(script); - xenbus_dev_fatal(dev, err, "reading script"); - goto fail; - } - - be->hotplug_script = script; - - - /* This kicks hotplug scripts, so do it immediately. */ - err = backend_create_xenvif(be); - if (err) - goto fail; - - return 0; - -abort_transaction: - xenbus_transaction_end(xbt, 1); - xenbus_dev_fatal(dev, err, "%s", message); -fail: - pr_debug("failed\n"); - netback_remove(dev); - return err; -} - - /* * Handle the creation of the hotplug script environment. We add the script * and vif variables to the environment, for the benefit of the vif-* hotplug @@ -1128,6 +949,180 @@ static int read_xenbus_vif_flags(struct backend_info *be) return 0; } +static int netback_remove(struct xenbus_device *dev) +{ + struct backend_info *be = dev_get_drvdata(&dev->dev); + + set_backend_state(be, XenbusStateClosed); + + unregister_hotplug_status_watch(be); + if (be->vif) { + kobject_uevent(&dev->dev.kobj, KOBJ_OFFLINE); + xen_unregister_watchers(be->vif); + xenbus_rm(XBT_NIL, dev->nodename, "hotplug-status"); + xenvif_free(be->vif); + be->vif = NULL; + } + kfree(be->hotplug_script); + kfree(be); + dev_set_drvdata(&dev->dev, NULL); + return 0; +} + +/** + * Entry point to this code when a new device is created. Allocate the basic + * structures and switch to InitWait. + */ +static int netback_probe(struct xenbus_device *dev, + const struct xenbus_device_id *id) +{ + const char *message; + struct xenbus_transaction xbt; + int err; + int sg; + const char *script; + struct backend_info *be = kzalloc(sizeof(*be), GFP_KERNEL); + + if (!be) { + xenbus_dev_fatal(dev, -ENOMEM, + "allocating backend structure"); + return -ENOMEM; + } + + be->dev = dev; + dev_set_drvdata(&dev->dev, be); + + be->state = XenbusStateInitialising; + err = xenbus_switch_state(dev, XenbusStateInitialising); + if (err) + goto fail; + + sg = 1; + + do { + err = xenbus_transaction_start(&xbt); + if (err) { + xenbus_dev_fatal(dev, err, "starting transaction"); + goto fail; + } + + err = xenbus_printf(xbt, dev->nodename, "feature-sg", "%d", sg); + if (err) { + message = "writing feature-sg"; + goto abort_transaction; + } + + err = xenbus_printf(xbt, dev->nodename, "feature-gso-tcpv4", + "%d", sg); + if (err) { + message = "writing feature-gso-tcpv4"; + goto abort_transaction; + } + + err = xenbus_printf(xbt, dev->nodename, "feature-gso-tcpv6", + "%d", sg); + if (err) { + message = "writing feature-gso-tcpv6"; + goto abort_transaction; + } + + /* We support partial checksum setup for IPv6 packets */ + err = xenbus_printf(xbt, dev->nodename, + "feature-ipv6-csum-offload", + "%d", 1); + if (err) { + message = "writing feature-ipv6-csum-offload"; + goto abort_transaction; + } + + /* We support rx-copy path. */ + err = xenbus_printf(xbt, dev->nodename, + "feature-rx-copy", "%d", 1); + if (err) { + message = "writing feature-rx-copy"; + goto abort_transaction; + } + + /* We don't support rx-flip path (except old guests who + * don't grok this feature flag). + */ + err = xenbus_printf(xbt, dev->nodename, + "feature-rx-flip", "%d", 0); + if (err) { + message = "writing feature-rx-flip"; + goto abort_transaction; + } + + /* We support dynamic multicast-control. */ + err = xenbus_printf(xbt, dev->nodename, + "feature-multicast-control", "%d", 1); + if (err) { + message = "writing feature-multicast-control"; + goto abort_transaction; + } + + err = xenbus_printf(xbt, dev->nodename, + "feature-dynamic-multicast-control", + "%d", 1); + if (err) { + message = "writing feature-dynamic-multicast-control"; + goto abort_transaction; + } + + err = xenbus_transaction_end(xbt, 0); + } while (err == -EAGAIN); + + if (err) { + xenbus_dev_fatal(dev, err, "completing transaction"); + goto fail; + } + + /* Split event channels support, this is optional so it is not + * put inside the above loop. + */ + err = xenbus_printf(XBT_NIL, dev->nodename, + "feature-split-event-channels", + "%u", separate_tx_rx_irq); + if (err) + pr_debug("Error writing feature-split-event-channels\n"); + + /* Multi-queue support: This is an optional feature. */ + err = xenbus_printf(XBT_NIL, dev->nodename, + "multi-queue-max-queues", "%u", xenvif_max_queues); + if (err) + pr_debug("Error writing multi-queue-max-queues\n"); + + err = xenbus_printf(XBT_NIL, dev->nodename, + "feature-ctrl-ring", + "%u", true); + if (err) + pr_debug("Error writing feature-ctrl-ring\n"); + + script = xenbus_read(XBT_NIL, dev->nodename, "script", NULL); + if (IS_ERR(script)) { + err = PTR_ERR(script); + xenbus_dev_fatal(dev, err, "reading script"); + goto fail; + } + + be->hotplug_script = script; + + /* This kicks hotplug scripts, so do it immediately. */ + err = backend_create_xenvif(be); + if (err) + goto fail; + + return 0; + +abort_transaction: + xenbus_transaction_end(xbt, 1); + xenbus_dev_fatal(dev, err, "%s", message); +fail: + pr_debug("failed\n"); + netback_remove(dev); + return err; +} + static const struct xenbus_device_id netback_ids[] = { { "vif" }, { "" } -- GitLab From f55c3188df84248558f33b13bba4c2817dda78a7 Mon Sep 17 00:00:00 2001 From: Paul Durrant Date: Tue, 17 Dec 2019 13:32:17 +0000 Subject: [PATCH 0887/1437] xen-netback: switch state to InitWait at the end of netback_probe()... ...as the comment above the function states. The switch to Initialising at the start of the function is somewhat bogus as the toolstack will have set that initial state anyway. To behave correctly, a backend should switch to InitWait once it has set up all xenstore values that may be required by a initialising frontend. This patch calls backend_switch_state() to make the transition at the appropriate point. NOTE: backend_switch_state() ignores errors from xenbus_switch_state() and so this patch removes an error path from netback_probe(). This means a failure to change state at this stage (in the absence of other failures) will leave the device instantiated. This is highly unlikley to happen as a failure to change state would indicate a failure to write to xenstore, and that will trigger other error paths. Also, a 'stuck' device can still be cleaned up using 'unbind' in any case. Signed-off-by: Paul Durrant Acked-by: Wei Liu Signed-off-by: David S. Miller --- drivers/net/xen-netback/xenbus.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/drivers/net/xen-netback/xenbus.c b/drivers/net/xen-netback/xenbus.c index bb61316d79de7..682e5e20971b1 100644 --- a/drivers/net/xen-netback/xenbus.c +++ b/drivers/net/xen-netback/xenbus.c @@ -992,11 +992,6 @@ static int netback_probe(struct xenbus_device *dev, be->dev = dev; dev_set_drvdata(&dev->dev, be); - be->state = XenbusStateInitialising; - err = xenbus_switch_state(dev, XenbusStateInitialising); - if (err) - goto fail; - sg = 1; do { @@ -1098,6 +1093,8 @@ static int netback_probe(struct xenbus_device *dev, if (err) pr_debug("Error writing feature-ctrl-ring\n"); + backend_switch_state(be, XenbusStateInitWait); + script = xenbus_read(XBT_NIL, dev->nodename, "script", NULL); if (IS_ERR(script)) { err = PTR_ERR(script); -- GitLab From 1f2565780e9b7218cf92c7630130e82dcc0fe9c2 Mon Sep 17 00:00:00 2001 From: Paul Durrant Date: Tue, 17 Dec 2019 13:32:18 +0000 Subject: [PATCH 0888/1437] xen-netback: remove 'hotplug-status' once it has served its purpose Removing the 'hotplug-status' node in netback_remove() is wrong; the script may not have completed. Only remove the node once the watch has fired and has been unregistered. Signed-off-by: Paul Durrant Acked-by: Wei Liu Signed-off-by: David S. Miller --- drivers/net/xen-netback/xenbus.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/xen-netback/xenbus.c b/drivers/net/xen-netback/xenbus.c index 682e5e20971b1..17b4950ec051f 100644 --- a/drivers/net/xen-netback/xenbus.c +++ b/drivers/net/xen-netback/xenbus.c @@ -648,6 +648,7 @@ static void hotplug_status_changed(struct xenbus_watch *watch, /* Not interested in this watch anymore. */ unregister_hotplug_status_watch(be); + xenbus_rm(XBT_NIL, be->dev->nodename, "hotplug-status"); } kfree(str); } @@ -959,7 +960,6 @@ static int netback_remove(struct xenbus_device *dev) if (be->vif) { kobject_uevent(&dev->dev.kobj, KOBJ_OFFLINE); xen_unregister_watchers(be->vif); - xenbus_rm(XBT_NIL, dev->nodename, "hotplug-status"); xenvif_free(be->vif); be->vif = NULL; } -- GitLab From a76cf56920966a384b578f7b099bd1f073cc2a92 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Tue, 17 Dec 2019 13:47:29 +0000 Subject: [PATCH 0889/1437] drm/i915/gem: Keep request alive while attaching fences Since commit e5dadff4b093 ("drm/i915: Protect request retirement with timeline->mutex"), the request retirement can happen outside of the struct_mutex serialised only by the timeline->mutex. We drop the timeline->mutex on submitting the request (i915_request_add) so after that point, it is liable to be freed. Make sure our local reference is kept alive until we have finished attaching it to the signalers. (Note that this erodes the argument that i915_request_add should consume the reference, but that is a slightly larger patch!) Fixes: e5dadff4b093 ("drm/i915: Protect request retirement with timeline->mutex") Signed-off-by: Chris Wilson Cc: Matthew Auld Cc: Tvrtko Ursulin Reviewed-by: Matthew Auld Link: https://patchwork.freedesktop.org/patch/msgid/20191217134729.3297818-1-chris@chris-wilson.co.uk (cherry picked from commit e14177f19739d74839eb496a27f5f5d958beaa5b) Signed-off-by: Joonas Lahtinen --- drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c index f0998f1225af1..bc3a672261635 100644 --- a/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c +++ b/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c @@ -2694,6 +2694,7 @@ i915_gem_do_execbuffer(struct drm_device *dev, err = eb_submit(&eb); err_request: add_to_client(eb.request, file); + i915_request_get(eb.request); i915_request_add(eb.request); if (fences) @@ -2709,6 +2710,7 @@ err_request: fput(out_fence->file); } } + i915_request_put(eb.request); err_batch_unpin: if (eb.batch_flags & I915_DISPATCH_SECURE) -- GitLab From f0f3a6cecf3b98990985cd42f7bf5a0313894822 Mon Sep 17 00:00:00 2001 From: Tvrtko Ursulin Date: Tue, 17 Dec 2019 17:09:33 +0000 Subject: [PATCH 0890/1437] drm/i915: Fix pid leak with banned clients Get_pid_task() needs to be paired with a put_pid or we leak a pid reference every time a banned client tries to create a context. v2: * task_pid_nr helper exists! (Chris) Signed-off-by: Tvrtko Ursulin Fixes: b083a0870c79 ("drm/i915: Add per client max context ban limit") Cc: Chris Wilson Cc: Mika Kuoppala Reviewed-by: Chris Wilson Signed-off-by: Chris Wilson Link: https://patchwork.freedesktop.org/patch/msgid/20191217170933.8108-1-tvrtko.ursulin@linux.intel.com (cherry picked from commit ba16a48af797db124ac100417f9229b1650ce1fb) Signed-off-by: Joonas Lahtinen --- drivers/gpu/drm/i915/gem/i915_gem_context.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/gem/i915_gem_context.c b/drivers/gpu/drm/i915/gem/i915_gem_context.c index 337ba17b1e0ee..42385277c6843 100644 --- a/drivers/gpu/drm/i915/gem/i915_gem_context.c +++ b/drivers/gpu/drm/i915/gem/i915_gem_context.c @@ -2167,8 +2167,7 @@ int i915_gem_context_create_ioctl(struct drm_device *dev, void *data, ext_data.fpriv = file->driver_priv; if (client_is_banned(ext_data.fpriv)) { DRM_DEBUG("client %s[%d] banned from creating ctx\n", - current->comm, - pid_nr(get_task_pid(current, PIDTYPE_PID))); + current->comm, task_pid_nr(current)); return -EIO; } -- GitLab From fe0acab448f68c3146235afe03fb932e242ec94c Mon Sep 17 00:00:00 2001 From: Yangbo Lu Date: Mon, 16 Dec 2019 11:18:42 +0800 Subject: [PATCH 0891/1437] mmc: sdhci-of-esdhc: fix P2020 errata handling Two previous patches introduced below quirks for P2020 platforms. - SDHCI_QUIRK_RESET_AFTER_REQUEST - SDHCI_QUIRK_BROKEN_TIMEOUT_VAL The patches made a mistake to add them in quirks2 of sdhci_host structure, while they were defined for quirks. host->quirks2 |= SDHCI_QUIRK_RESET_AFTER_REQUEST; host->quirks2 |= SDHCI_QUIRK_BROKEN_TIMEOUT_VAL; This patch is to fix them. host->quirks |= SDHCI_QUIRK_RESET_AFTER_REQUEST; host->quirks |= SDHCI_QUIRK_BROKEN_TIMEOUT_VAL; Fixes: 05cb6b2a66fa ("mmc: sdhci-of-esdhc: add erratum eSDHC-A001 and A-008358 support") Fixes: a46e42712596 ("mmc: sdhci-of-esdhc: add erratum eSDHC5 support") Signed-off-by: Yangbo Lu Cc: stable@vger.kernel.org Link: https://lore.kernel.org/r/20191216031842.40068-1-yangbo.lu@nxp.com Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-of-esdhc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/mmc/host/sdhci-of-esdhc.c b/drivers/mmc/host/sdhci-of-esdhc.c index 7f87a90bf56a0..4ca640e6fd557 100644 --- a/drivers/mmc/host/sdhci-of-esdhc.c +++ b/drivers/mmc/host/sdhci-of-esdhc.c @@ -1300,8 +1300,8 @@ static int sdhci_esdhc_probe(struct platform_device *pdev) host->quirks &= ~SDHCI_QUIRK_NO_BUSY_IRQ; if (of_find_compatible_node(NULL, NULL, "fsl,p2020-esdhc")) { - host->quirks2 |= SDHCI_QUIRK_RESET_AFTER_REQUEST; - host->quirks2 |= SDHCI_QUIRK_BROKEN_TIMEOUT_VAL; + host->quirks |= SDHCI_QUIRK_RESET_AFTER_REQUEST; + host->quirks |= SDHCI_QUIRK_BROKEN_TIMEOUT_VAL; } if (of_device_is_compatible(np, "fsl,p5040-esdhc") || -- GitLab From bedf9fc01ff1f40cfd1a79ccacedd9f3cd8e652a Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Tue, 17 Dec 2019 11:53:48 +0200 Subject: [PATCH 0892/1437] mmc: sdhci: Workaround broken command queuing on Intel GLK Command queuing has been reported broken on some Lenovo systems based on Intel GLK. This is likely a BIOS issue, so disable command queuing for Intel GLK if the BIOS vendor string is "LENOVO". Fixes: 8ee82bda230f ("mmc: sdhci-pci: Add CQHCI support for Intel GLK") Signed-off-by: Adrian Hunter Cc: stable@vger.kernel.org Link: https://lore.kernel.org/r/20191217095349.14592-1-adrian.hunter@intel.com Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-pci-core.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/host/sdhci-pci-core.c b/drivers/mmc/host/sdhci-pci-core.c index acefb76b4e153..5091e2c1c0e54 100644 --- a/drivers/mmc/host/sdhci-pci-core.c +++ b/drivers/mmc/host/sdhci-pci-core.c @@ -27,6 +27,7 @@ #include #include #include +#include #ifdef CONFIG_X86 #include @@ -783,11 +784,18 @@ static int byt_emmc_probe_slot(struct sdhci_pci_slot *slot) return 0; } +static bool glk_broken_cqhci(struct sdhci_pci_slot *slot) +{ + return slot->chip->pdev->device == PCI_DEVICE_ID_INTEL_GLK_EMMC && + dmi_match(DMI_BIOS_VENDOR, "LENOVO"); +} + static int glk_emmc_probe_slot(struct sdhci_pci_slot *slot) { int ret = byt_emmc_probe_slot(slot); - slot->host->mmc->caps2 |= MMC_CAP2_CQE; + if (!glk_broken_cqhci(slot)) + slot->host->mmc->caps2 |= MMC_CAP2_CQE; if (slot->chip->pdev->device != PCI_DEVICE_ID_INTEL_GLK_EMMC) { slot->host->mmc->caps2 |= MMC_CAP2_HS400_ES, -- GitLab From 75d27ea1abf7af3cc2cdec3513e74f52191605c8 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Tue, 17 Dec 2019 11:53:49 +0200 Subject: [PATCH 0893/1437] mmc: sdhci: Add a quirk for broken command queuing Command queuing has been reported broken on some systems based on Intel GLK. A separate patch disables command queuing in some cases. This patch adds a quirk for broken command queuing, which enables users with problems to disable command queuing using sdhci module parameters for quirks. Fixes: 8ee82bda230f ("mmc: sdhci-pci: Add CQHCI support for Intel GLK") Signed-off-by: Adrian Hunter Cc: stable@vger.kernel.org Link: https://lore.kernel.org/r/20191217095349.14592-2-adrian.hunter@intel.com Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci.c | 3 +++ drivers/mmc/host/sdhci.h | 2 ++ 2 files changed, 5 insertions(+) diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 42a9c8179da7c..1b1c26da3fe0b 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -3767,6 +3767,9 @@ int sdhci_setup_host(struct sdhci_host *host) mmc_hostname(mmc), host->version); } + if (host->quirks & SDHCI_QUIRK_BROKEN_CQE) + mmc->caps2 &= ~MMC_CAP2_CQE; + if (host->quirks & SDHCI_QUIRK_FORCE_DMA) host->flags |= SDHCI_USE_SDMA; else if (!(host->caps & SDHCI_CAN_DO_SDMA)) diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h index 0ed3e0eaef5f9..fe83ece6965b1 100644 --- a/drivers/mmc/host/sdhci.h +++ b/drivers/mmc/host/sdhci.h @@ -409,6 +409,8 @@ struct sdhci_host { #define SDHCI_QUIRK_BROKEN_CARD_DETECTION (1<<15) /* Controller reports inverted write-protect state */ #define SDHCI_QUIRK_INVERTED_WRITE_PROTECT (1<<16) +/* Controller has unusable command queue engine */ +#define SDHCI_QUIRK_BROKEN_CQE (1<<17) /* Controller does not like fast PIO transfers */ #define SDHCI_QUIRK_PIO_NEEDS_DELAY (1<<18) /* Controller does not have a LED */ -- GitLab From 8f268881d7d278047b00eed54bbb9288dbd6ab23 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Tue, 17 Dec 2019 20:51:51 +0900 Subject: [PATCH 0894/1437] kconfig: remove ---help--- from documentation Since commit 84af7a6194e4 ("checkpatch: kconfig: prefer 'help' over '---help---'"), scripts/checkpatch.pl warns the use of ---help---. Kconfig still supports ---help---, but new code should avoid using it. Let's stop advertising it in documentation. Signed-off-by: Masahiro Yamada --- Documentation/kbuild/kconfig-language.rst | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/Documentation/kbuild/kconfig-language.rst b/Documentation/kbuild/kconfig-language.rst index 74bef19f69f0a..231e6a64957fc 100644 --- a/Documentation/kbuild/kconfig-language.rst +++ b/Documentation/kbuild/kconfig-language.rst @@ -196,14 +196,11 @@ applicable everywhere (see syntax). or equal to the first symbol and smaller than or equal to the second symbol. -- help text: "help" or "---help---" +- help text: "help" This defines a help text. The end of the help text is determined by the indentation level, this means it ends at the first line which has a smaller indentation than the first line of the help text. - "---help---" and "help" do not differ in behaviour, "---help---" is - used to help visually separate configuration logic from help within - the file as an aid to developers. - misc options: "option" [=] -- GitLab From b0ac999ebe37c931e76fbdffffd7a5b77b2d788e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Pouiller?= Date: Tue, 17 Dec 2019 16:14:27 +0000 Subject: [PATCH 0895/1437] staging: wfx: fix the cache of rate policies on interface reset MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Device and driver maintain a cache of rate policies (aka. tx_retry_policy in hardware API). When hif_reset() is sent to hardware, device resets its cache of rate policies. In order to keep driver in sync, it is necessary to do the same on driver. Note, when driver tries to use a rate policy that has not been defined on device, data is sent at 1Mbps. So, this patch should fix abnormal throughput observed sometime after a reset of the interface. Signed-off-by: Jérôme Pouiller Link: https://lore.kernel.org/r/20191217161318.31402-2-Jerome.Pouiller@silabs.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/wfx/data_tx.c | 3 +-- drivers/staging/wfx/data_tx.h | 1 + drivers/staging/wfx/sta.c | 6 +++++- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/drivers/staging/wfx/data_tx.c b/drivers/staging/wfx/data_tx.c index df2640a79f02f..bc769d1b6bc5a 100644 --- a/drivers/staging/wfx/data_tx.c +++ b/drivers/staging/wfx/data_tx.c @@ -249,7 +249,7 @@ static int wfx_tx_policy_upload(struct wfx_vif *wvif) return 0; } -static void wfx_tx_policy_upload_work(struct work_struct *work) +void wfx_tx_policy_upload_work(struct work_struct *work) { struct wfx_vif *wvif = container_of(work, struct wfx_vif, tx_policy_upload_work); @@ -270,7 +270,6 @@ void wfx_tx_policy_init(struct wfx_vif *wvif) spin_lock_init(&cache->lock); INIT_LIST_HEAD(&cache->used); INIT_LIST_HEAD(&cache->free); - INIT_WORK(&wvif->tx_policy_upload_work, wfx_tx_policy_upload_work); for (i = 0; i < HIF_MIB_NUM_TX_RATE_RETRY_POLICIES; ++i) list_add(&cache->cache[i].link, &cache->free); diff --git a/drivers/staging/wfx/data_tx.h b/drivers/staging/wfx/data_tx.h index 29faa5640516d..a0f9ae69baf58 100644 --- a/drivers/staging/wfx/data_tx.h +++ b/drivers/staging/wfx/data_tx.h @@ -61,6 +61,7 @@ struct wfx_tx_priv { } __packed; void wfx_tx_policy_init(struct wfx_vif *wvif); +void wfx_tx_policy_upload_work(struct work_struct *work); void wfx_tx(struct ieee80211_hw *hw, struct ieee80211_tx_control *control, struct sk_buff *skb); diff --git a/drivers/staging/wfx/sta.c b/drivers/staging/wfx/sta.c index 29848a202ab4e..471dd15b227fe 100644 --- a/drivers/staging/wfx/sta.c +++ b/drivers/staging/wfx/sta.c @@ -592,6 +592,7 @@ static void wfx_do_unjoin(struct wfx_vif *wvif) wfx_tx_flush(wvif->wdev); hif_keep_alive_period(wvif, 0); hif_reset(wvif, false); + wfx_tx_policy_init(wvif); hif_set_output_power(wvif, wvif->wdev->output_power * 10); wvif->dtim_period = 0; hif_set_macaddr(wvif, wvif->vif->addr); @@ -880,8 +881,10 @@ static int wfx_update_beaconing(struct wfx_vif *wvif) if (wvif->state != WFX_STATE_AP || wvif->beacon_int != conf->beacon_int) { wfx_tx_lock_flush(wvif->wdev); - if (wvif->state != WFX_STATE_PASSIVE) + if (wvif->state != WFX_STATE_PASSIVE) { hif_reset(wvif, false); + wfx_tx_policy_init(wvif); + } wvif->state = WFX_STATE_PASSIVE; wfx_start_ap(wvif); wfx_tx_unlock(wvif->wdev); @@ -1567,6 +1570,7 @@ int wfx_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) INIT_WORK(&wvif->set_cts_work, wfx_set_cts_work); INIT_WORK(&wvif->unjoin_work, wfx_unjoin_work); + INIT_WORK(&wvif->tx_policy_upload_work, wfx_tx_policy_upload_work); mutex_unlock(&wdev->conf_mutex); hif_set_macaddr(wvif, vif->addr); -- GitLab From 89606bb3a9286a64bcdc2e58552edf6e2478c3bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Pouiller?= Date: Tue, 17 Dec 2019 16:14:29 +0000 Subject: [PATCH 0896/1437] staging: wfx: fix case of lack of tx_retry_policies MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In some rare cases, driver may not have any available tx_retry_policies. In this case, the driver asks to mac80211 to stop sending data. However, it seems that a race is possible and a few frames can be sent to the driver. In this case, driver can't wait for free tx_retry_policies since wfx_tx() must be atomic. So, this patch fix this case by sending these frames with the special policy number 15. The firmware normally use policy 15 to send internal frames (PS-poll, beacons, etc...). So, it is not a so bad fallback. Signed-off-by: Jérôme Pouiller Link: https://lore.kernel.org/r/20191217161318.31402-3-Jerome.Pouiller@silabs.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/wfx/data_tx.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/staging/wfx/data_tx.c b/drivers/staging/wfx/data_tx.c index bc769d1b6bc5a..4edf8bb964e6a 100644 --- a/drivers/staging/wfx/data_tx.c +++ b/drivers/staging/wfx/data_tx.c @@ -16,7 +16,7 @@ #include "traces.h" #include "hif_tx_mib.h" -#define WFX_INVALID_RATE_ID (0xFF) +#define WFX_INVALID_RATE_ID 15 #define WFX_LINK_ID_NO_ASSOC 15 #define WFX_LINK_ID_GC_TIMEOUT ((unsigned long)(10 * HZ)) @@ -202,6 +202,8 @@ static void wfx_tx_policy_put(struct wfx_vif *wvif, int idx) int usage, locked; struct tx_policy_cache *cache = &wvif->tx_policy_cache; + if (idx == WFX_INVALID_RATE_ID) + return; spin_lock_bh(&cache->lock); locked = list_empty(&cache->free); usage = wfx_tx_policy_release(cache, &cache->cache[idx]); @@ -549,7 +551,8 @@ static u8 wfx_tx_get_rate_id(struct wfx_vif *wvif, rate_id = wfx_tx_policy_get(wvif, tx_info->driver_rates, &tx_policy_renew); - WARN(rate_id == WFX_INVALID_RATE_ID, "unable to get a valid Tx policy"); + if (rate_id == WFX_INVALID_RATE_ID) + dev_warn(wvif->wdev->dev, "unable to get a valid Tx policy"); if (tx_policy_renew) { /* FIXME: It's not so optimal to stop TX queues every now and -- GitLab From 5eee614700e0dc06760bcf5aff840565637cddfc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Pouiller?= Date: Tue, 17 Dec 2019 16:14:30 +0000 Subject: [PATCH 0897/1437] staging: wfx: fix counter overflow MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some weird behaviors were observed when connection is really good and packets are small. It appears that sometime, number of packets in queues can exceed 255 and generate an overflow in field usage_count. Signed-off-by: Jérôme Pouiller Link: https://lore.kernel.org/r/20191217161318.31402-4-Jerome.Pouiller@silabs.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/wfx/data_tx.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/wfx/data_tx.h b/drivers/staging/wfx/data_tx.h index a0f9ae69baf58..f63e5d8cf929b 100644 --- a/drivers/staging/wfx/data_tx.h +++ b/drivers/staging/wfx/data_tx.h @@ -39,8 +39,8 @@ struct wfx_link_entry { struct tx_policy { struct list_head link; + int usage_count; u8 rates[12]; - u8 usage_count; u8 uploaded; }; -- GitLab From d77280311d929887fa11b1c3345953f23c5742e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Pouiller?= Date: Tue, 17 Dec 2019 16:14:32 +0000 Subject: [PATCH 0898/1437] staging: wfx: use boolean appropriately MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The field 'uploaded' is used as a boolean, so call it a boolean. Signed-off-by: Jérôme Pouiller Link: https://lore.kernel.org/r/20191217161318.31402-5-Jerome.Pouiller@silabs.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/wfx/data_tx.c | 4 ++-- drivers/staging/wfx/data_tx.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/staging/wfx/data_tx.c b/drivers/staging/wfx/data_tx.c index 4edf8bb964e6a..f0003176a3145 100644 --- a/drivers/staging/wfx/data_tx.c +++ b/drivers/staging/wfx/data_tx.c @@ -184,7 +184,7 @@ static int wfx_tx_policy_get(struct wfx_vif *wvif, */ entry = list_entry(cache->free.prev, struct tx_policy, link); memcpy(entry->rates, wanted.rates, sizeof(entry->rates)); - entry->uploaded = 0; + entry->uploaded = false; entry->usage_count = 0; idx = entry - cache->cache; } @@ -241,7 +241,7 @@ static int wfx_tx_policy_upload(struct wfx_vif *wvif) dst->terminate = 1; dst->count_init = 1; memcpy(&dst->rates, src->rates, sizeof(src->rates)); - src->uploaded = 1; + src->uploaded = true; arg->num_tx_rate_policies++; } } diff --git a/drivers/staging/wfx/data_tx.h b/drivers/staging/wfx/data_tx.h index f63e5d8cf929b..0fc388db62e0d 100644 --- a/drivers/staging/wfx/data_tx.h +++ b/drivers/staging/wfx/data_tx.h @@ -41,7 +41,7 @@ struct tx_policy { struct list_head link; int usage_count; u8 rates[12]; - u8 uploaded; + bool uploaded; }; struct tx_policy_cache { -- GitLab From fcde3e6ba57f754dcd87c9d56bb2b05dc1305486 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Pouiller?= Date: Tue, 17 Dec 2019 16:14:33 +0000 Subject: [PATCH 0899/1437] staging: wfx: firmware does not support more than 32 total retries MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The sum of all retries for a Tx frame cannot be superior to 32. There are 4 rates at most. So this patch limits number of retries per rate to 8. Signed-off-by: Jérôme Pouiller Link: https://lore.kernel.org/r/20191217161318.31402-6-Jerome.Pouiller@silabs.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/wfx/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/wfx/main.c b/drivers/staging/wfx/main.c index 986a2ef678b93..3b47b6c21ea13 100644 --- a/drivers/staging/wfx/main.c +++ b/drivers/staging/wfx/main.c @@ -289,7 +289,7 @@ struct wfx_dev *wfx_init_common(struct device *dev, hw->sta_data_size = sizeof(struct wfx_sta_priv); hw->queues = 4; hw->max_rates = 8; - hw->max_rate_tries = 15; + hw->max_rate_tries = 8; hw->extra_tx_headroom = sizeof(struct hif_sl_msg_hdr) + sizeof(struct hif_msg) + sizeof(struct hif_req_tx) -- GitLab From cefc2033627f922e425e4b624b6a152df4ce29d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Pouiller?= Date: Tue, 17 Dec 2019 16:14:34 +0000 Subject: [PATCH 0900/1437] staging: wfx: fix rate control handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A tx_retry_policy (the equivalent of a list of ieee80211_tx_rate in hardware API) is not able to include a rate multiple time. So currently, the driver merges the identical rates from the policy provided by minstrel (and it try to do the best choice it can in the associated flags) before to sent it to firmware. Until now, when rates are merged, field "count" is set to max(count1, count2). But, it means that the sum of retries for all rates could be far less than initial number of retries. So, this patch changes the value of field "count" to count1 + count2. Thus, sum of all retries for all rates stay the same. Signed-off-by: Jérôme Pouiller Link: https://lore.kernel.org/r/20191217161318.31402-7-Jerome.Pouiller@silabs.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/wfx/data_tx.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/staging/wfx/data_tx.c b/drivers/staging/wfx/data_tx.c index f0003176a3145..a24a63b70014c 100644 --- a/drivers/staging/wfx/data_tx.c +++ b/drivers/staging/wfx/data_tx.c @@ -524,9 +524,9 @@ static void wfx_tx_fixup_rates(struct ieee80211_tx_rate *rates) for (i = 0; i < IEEE80211_TX_MAX_RATES - 1; i++) { if (rates[i + 1].idx == rates[i].idx && rates[i].idx != -1) { - rates[i].count = - max_t(int, rates[i].count, - rates[i + 1].count); + rates[i].count += rates[i + 1].count; + if (rates[i].count > 15) + rates[i].count = 15; rates[i + 1].idx = -1; rates[i + 1].count = 0; -- GitLab From 2e12ab7f05093a8595782a598df20455f78a4264 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Pouiller?= Date: Tue, 17 Dec 2019 16:14:36 +0000 Subject: [PATCH 0901/1437] staging: wfx: ensure that retry policy always fallbacks to MCS0 / 1Mbps MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When not using HT mode, minstrel always includes 1Mbps as fallback rate. But, when using HT mode, this fallback is not included. Yet, it seems that it could save some frames. So, this patch add it unconditionally. Signed-off-by: Jérôme Pouiller Link: https://lore.kernel.org/r/20191217161318.31402-8-Jerome.Pouiller@silabs.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/wfx/data_tx.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/drivers/staging/wfx/data_tx.c b/drivers/staging/wfx/data_tx.c index a24a63b70014c..29529e26c9746 100644 --- a/drivers/staging/wfx/data_tx.c +++ b/drivers/staging/wfx/data_tx.c @@ -538,6 +538,17 @@ static void wfx_tx_fixup_rates(struct ieee80211_tx_rate *rates) } } } while (!finished); + // Ensure that MCS0 or 1Mbps is present at the end of the retry list + for (i = 0; i < IEEE80211_TX_MAX_RATES; i++) { + if (rates[i].idx == 0) + break; + if (rates[i].idx == -1) { + rates[i].idx = 0; + rates[i].count = 8; // == hw->max_rate_tries + rates[i].flags = rates[i - 1].flags & IEEE80211_TX_RC_MCS; + break; + } + } // All retries use long GI for (i = 1; i < IEEE80211_TX_MAX_RATES; i++) rates[i].flags &= ~IEEE80211_TX_RC_SHORT_GI; -- GitLab From 1a6ae3edcd6ae03a7ea5c08856fe79717d96cfb5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Pouiller?= Date: Tue, 17 Dec 2019 16:14:37 +0000 Subject: [PATCH 0902/1437] staging: wfx: detect race condition in WEP authentication MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Current code has a special case to handle association with WEP. Before to rework the tx data handling, let's try to detect any possible misuse of this code. Signed-off-by: Jérôme Pouiller Link: https://lore.kernel.org/r/20191217161318.31402-9-Jerome.Pouiller@silabs.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/wfx/queue.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/staging/wfx/queue.c b/drivers/staging/wfx/queue.c index c7ee90888f69c..680fed31cefb0 100644 --- a/drivers/staging/wfx/queue.c +++ b/drivers/staging/wfx/queue.c @@ -422,6 +422,7 @@ static bool hif_handle_tx_data(struct wfx_vif *wvif, struct sk_buff *skb, break; case do_wep: wfx_tx_lock(wvif->wdev); + WARN_ON(wvif->wep_pending_skb); wvif->wep_default_key_id = tx_priv->hw_key->keyidx; wvif->wep_pending_skb = skb; if (!schedule_work(&wvif->wep_key_work)) -- GitLab From 632441134d0f7428211cd2e9aa115b5e830b32b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Pouiller?= Date: Tue, 17 Dec 2019 16:14:38 +0000 Subject: [PATCH 0903/1437] staging: wfx: fix hif_set_mfp() with big endian hosts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit struct hif_mib_protected_mgmt_policy is an array of u8. There is no reason to swap its bytes. Signed-off-by: Jérôme Pouiller Link: https://lore.kernel.org/r/20191217161318.31402-10-Jerome.Pouiller@silabs.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/wfx/hif_tx_mib.h | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/staging/wfx/hif_tx_mib.h b/drivers/staging/wfx/hif_tx_mib.h index bb091e395ff59..9be74881c56c9 100644 --- a/drivers/staging/wfx/hif_tx_mib.h +++ b/drivers/staging/wfx/hif_tx_mib.h @@ -147,7 +147,6 @@ static inline int hif_set_mfp(struct wfx_vif *wvif, bool capable, bool required) } if (!required) val.unpmf_allowed = 1; - cpu_to_le32s((u32 *) &val); return hif_write_mib(wvif->wdev, wvif->id, HIF_MIB_ID_PROTECTED_MGMT_POLICY, &val, sizeof(val)); -- GitLab From c05c403b1d123031f86e65e867be2c2e9ee1e7e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Pouiller?= Date: Tue, 17 Dec 2019 16:14:40 +0000 Subject: [PATCH 0904/1437] staging: wfx: fix wrong error message MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The driver checks that the number of retries made by the device is coherent with the rate policy. However, this check make sense only if the device has returned RETRY_EXCEEDED. Signed-off-by: Jérôme Pouiller Link: https://lore.kernel.org/r/20191217161318.31402-11-Jerome.Pouiller@silabs.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/wfx/data_tx.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/staging/wfx/data_tx.c b/drivers/staging/wfx/data_tx.c index 29529e26c9746..b13d7341f8bba 100644 --- a/drivers/staging/wfx/data_tx.c +++ b/drivers/staging/wfx/data_tx.c @@ -748,7 +748,9 @@ void wfx_tx_confirm_cb(struct wfx_vif *wvif, struct hif_cnf_tx *arg) rate = &tx_info->status.rates[i]; if (rate->idx < 0) break; - if (tx_count < rate->count && arg->status && arg->ack_failures) + if (tx_count < rate->count && + arg->status == HIF_STATUS_RETRY_EXCEEDED && + arg->ack_failures) dev_dbg(wvif->wdev->dev, "all retries were not consumed: %d != %d\n", rate->count, tx_count); if (tx_count <= rate->count && tx_count && -- GitLab From f81b846dcd9a1e6d120f73970a9a98b7fcaaffba Mon Sep 17 00:00:00 2001 From: Lu Baolu Date: Wed, 20 Nov 2019 14:10:16 +0800 Subject: [PATCH 0905/1437] iommu/vt-d: Remove incorrect PSI capability check The PSI (Page Selective Invalidation) bit in the capability register is only valid for second-level translation. Intel IOMMU supporting scalable mode must support page/address selective IOTLB invalidation for first-level translation. Remove the PSI capability check in SVA cache invalidation code. Fixes: 8744daf4b0699 ("iommu/vt-d: Remove global page flush support") Cc: Jacob Pan Signed-off-by: Lu Baolu Signed-off-by: Joerg Roedel --- drivers/iommu/intel-svm.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/drivers/iommu/intel-svm.c b/drivers/iommu/intel-svm.c index 9b159132405dc..dca88f9fdf29a 100644 --- a/drivers/iommu/intel-svm.c +++ b/drivers/iommu/intel-svm.c @@ -104,11 +104,7 @@ static void intel_flush_svm_range_dev (struct intel_svm *svm, struct intel_svm_d { struct qi_desc desc; - /* - * Do PASID granu IOTLB invalidation if page selective capability is - * not available. - */ - if (pages == -1 || !cap_pgsel_inv(svm->iommu->cap)) { + if (pages == -1) { desc.qw0 = QI_EIOTLB_PASID(svm->pasid) | QI_EIOTLB_DID(sdev->did) | QI_EIOTLB_GRAN(QI_GRAN_NONG_PASID) | -- GitLab From 8ae4bcf4821c18a8fbfa0b2c1df26c1085e9d923 Mon Sep 17 00:00:00 2001 From: Hanjun Guo Date: Wed, 11 Dec 2019 14:43:06 +0800 Subject: [PATCH 0906/1437] perf/smmuv3: Remove the leftover put_cpu() in error path In smmu_pmu_probe(), there is put_cpu() in the error path, which is wrong because we use raw_smp_processor_id() to get the cpu ID, not get_cpu(), remove it. While we are at it, kill 'out_cpuhp_err' altogether and just return err if we fail to add the hotplug instance. Acked-by: Robin Murphy Acked-by: Will Deacon Signed-off-by: Hanjun Guo Signed-off-by: Catalin Marinas --- drivers/perf/arm_smmuv3_pmu.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/perf/arm_smmuv3_pmu.c b/drivers/perf/arm_smmuv3_pmu.c index 773128f411f1e..d704eccc548f6 100644 --- a/drivers/perf/arm_smmuv3_pmu.c +++ b/drivers/perf/arm_smmuv3_pmu.c @@ -814,7 +814,7 @@ static int smmu_pmu_probe(struct platform_device *pdev) if (err) { dev_err(dev, "Error %d registering hotplug, PMU @%pa\n", err, &res_0->start); - goto out_cpuhp_err; + return err; } err = perf_pmu_register(&smmu_pmu->pmu, name, -1); @@ -833,8 +833,6 @@ static int smmu_pmu_probe(struct platform_device *pdev) out_unregister: cpuhp_state_remove_instance_nocalls(cpuhp_state_num, &smmu_pmu->node); -out_cpuhp_err: - put_cpu(); return err; } -- GitLab From c18647900ec864d401ba09b3bbd5b34f331f8d26 Mon Sep 17 00:00:00 2001 From: Robin Murphy Date: Mon, 9 Dec 2019 19:47:25 +0000 Subject: [PATCH 0907/1437] iommu/dma: Relax locking in iommu_dma_prepare_msi() Since commit ece6e6f0218b ("iommu/dma-iommu: Split iommu_dma_map_msi_msg() in two parts"), iommu_dma_prepare_msi() should no longer have to worry about preempting itself, nor being called in atomic context at all. Thus we can downgrade the IRQ-safe locking to a simple mutex to avoid angering the new might_sleep() check in iommu_map(). Reported-by: Qian Cai Tested-by: Jean-Philippe Brucker Signed-off-by: Robin Murphy Signed-off-by: Joerg Roedel --- drivers/iommu/dma-iommu.c | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c index 6e573d1cb8bf0..c363294b3bb93 100644 --- a/drivers/iommu/dma-iommu.c +++ b/drivers/iommu/dma-iommu.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -44,7 +45,6 @@ struct iommu_dma_cookie { dma_addr_t msi_iova; }; struct list_head msi_page_list; - spinlock_t msi_lock; /* Domain for flush queue callback; NULL if flush queue not in use */ struct iommu_domain *fq_domain; @@ -63,7 +63,6 @@ static struct iommu_dma_cookie *cookie_alloc(enum iommu_dma_cookie_type type) cookie = kzalloc(sizeof(*cookie), GFP_KERNEL); if (cookie) { - spin_lock_init(&cookie->msi_lock); INIT_LIST_HEAD(&cookie->msi_page_list); cookie->type = type; } @@ -1176,7 +1175,7 @@ static struct iommu_dma_msi_page *iommu_dma_get_msi_page(struct device *dev, if (msi_page->phys == msi_addr) return msi_page; - msi_page = kzalloc(sizeof(*msi_page), GFP_ATOMIC); + msi_page = kzalloc(sizeof(*msi_page), GFP_KERNEL); if (!msi_page) return NULL; @@ -1206,7 +1205,7 @@ int iommu_dma_prepare_msi(struct msi_desc *desc, phys_addr_t msi_addr) struct iommu_domain *domain = iommu_get_domain_for_dev(dev); struct iommu_dma_cookie *cookie; struct iommu_dma_msi_page *msi_page; - unsigned long flags; + static DEFINE_MUTEX(msi_prepare_lock); /* see below */ if (!domain || !domain->iova_cookie) { desc->iommu_cookie = NULL; @@ -1216,13 +1215,13 @@ int iommu_dma_prepare_msi(struct msi_desc *desc, phys_addr_t msi_addr) cookie = domain->iova_cookie; /* - * We disable IRQs to rule out a possible inversion against - * irq_desc_lock if, say, someone tries to retarget the affinity - * of an MSI from within an IPI handler. + * In fact the whole prepare operation should already be serialised by + * irq_domain_mutex further up the callchain, but that's pretty subtle + * on its own, so consider this locking as failsafe documentation... */ - spin_lock_irqsave(&cookie->msi_lock, flags); + mutex_lock(&msi_prepare_lock); msi_page = iommu_dma_get_msi_page(dev, msi_addr, domain); - spin_unlock_irqrestore(&cookie->msi_lock, flags); + mutex_unlock(&msi_prepare_lock); msi_desc_set_iommu_cookie(desc, msi_page); -- GitLab From 396d2e878f92ec108e4293f1c77ea3bc90b414ff Mon Sep 17 00:00:00 2001 From: Jim Mattson Date: Fri, 13 Dec 2019 16:15:15 -0800 Subject: [PATCH 0908/1437] kvm: x86: Host feature SSBD doesn't imply guest feature SPEC_CTRL_SSBD The host reports support for the synthetic feature X86_FEATURE_SSBD when any of the three following hardware features are set: CPUID.(EAX=7,ECX=0):EDX.SSBD[bit 31] CPUID.80000008H:EBX.AMD_SSBD[bit 24] CPUID.80000008H:EBX.VIRT_SSBD[bit 25] Either of the first two hardware features implies the existence of the IA32_SPEC_CTRL MSR, but CPUID.80000008H:EBX.VIRT_SSBD[bit 25] does not. Therefore, CPUID.(EAX=7,ECX=0):EDX.SSBD[bit 31] should only be set in the guest if CPUID.(EAX=7,ECX=0):EDX.SSBD[bit 31] or CPUID.80000008H:EBX.AMD_SSBD[bit 24] is set on the host. Fixes: 0c54914d0c52a ("KVM: x86: use Intel speculation bugs and features as derived in generic x86 code") Signed-off-by: Jim Mattson Reviewed-by: Jacob Xu Reviewed-by: Peter Shier Cc: Paolo Bonzini Cc: stable@vger.kernel.org Reported-by: Eric Biggers Signed-off-by: Paolo Bonzini --- arch/x86/kvm/cpuid.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/x86/kvm/cpuid.c b/arch/x86/kvm/cpuid.c index c0aa07487eb82..dd18aa6fa3173 100644 --- a/arch/x86/kvm/cpuid.c +++ b/arch/x86/kvm/cpuid.c @@ -402,7 +402,8 @@ static inline void do_cpuid_7_mask(struct kvm_cpuid_entry2 *entry, int index) entry->edx |= F(SPEC_CTRL); if (boot_cpu_has(X86_FEATURE_STIBP)) entry->edx |= F(INTEL_STIBP); - if (boot_cpu_has(X86_FEATURE_SSBD)) + if (boot_cpu_has(X86_FEATURE_SPEC_CTRL_SSBD) || + boot_cpu_has(X86_FEATURE_AMD_SSBD)) entry->edx |= F(SPEC_CTRL_SSBD); /* * We emulate ARCH_CAPABILITIES in software even -- GitLab From 8715f05269bfbc6453e25e80825d781a82902f8e Mon Sep 17 00:00:00 2001 From: Jim Mattson Date: Fri, 13 Dec 2019 16:15:16 -0800 Subject: [PATCH 0909/1437] kvm: x86: Host feature SSBD doesn't imply guest feature AMD_SSBD The host reports support for the synthetic feature X86_FEATURE_SSBD when any of the three following hardware features are set: CPUID.(EAX=7,ECX=0):EDX.SSBD[bit 31] CPUID.80000008H:EBX.AMD_SSBD[bit 24] CPUID.80000008H:EBX.VIRT_SSBD[bit 25] Either of the first two hardware features implies the existence of the IA32_SPEC_CTRL MSR, but CPUID.80000008H:EBX.VIRT_SSBD[bit 25] does not. Therefore, CPUID.80000008H:EBX.AMD_SSBD[bit 24] should only be set in the guest if CPUID.(EAX=7,ECX=0):EDX.SSBD[bit 31] or CPUID.80000008H:EBX.AMD_SSBD[bit 24] is set on the host. Fixes: 4c6903a0f9d76 ("KVM: x86: fix reporting of AMD speculation bug CPUID leaf") Signed-off-by: Jim Mattson Reviewed-by: Jacob Xu Reviewed-by: Peter Shier Cc: Paolo Bonzini Cc: stable@vger.kernel.org Reported-by: Eric Biggers Signed-off-by: Paolo Bonzini --- arch/x86/kvm/cpuid.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/x86/kvm/cpuid.c b/arch/x86/kvm/cpuid.c index dd18aa6fa3173..bf715e5fd35c0 100644 --- a/arch/x86/kvm/cpuid.c +++ b/arch/x86/kvm/cpuid.c @@ -760,7 +760,8 @@ static inline int __do_cpuid_func(struct kvm_cpuid_entry2 *entry, u32 function, entry->ebx |= F(AMD_IBRS); if (boot_cpu_has(X86_FEATURE_STIBP)) entry->ebx |= F(AMD_STIBP); - if (boot_cpu_has(X86_FEATURE_SSBD)) + if (boot_cpu_has(X86_FEATURE_SPEC_CTRL_SSBD) || + boot_cpu_has(X86_FEATURE_AMD_SSBD)) entry->ebx |= F(AMD_SSBD); if (!boot_cpu_has_bug(X86_BUG_SPEC_STORE_BYPASS)) entry->ebx |= F(AMD_SSB_NO); -- GitLab From 7c504e65206a4379ff38fe41d21b32b6c2c3e53e Mon Sep 17 00:00:00 2001 From: Pavel Begunkov Date: Wed, 18 Dec 2019 19:53:45 +0300 Subject: [PATCH 0910/1437] io_uring: don't wait when under-submitting There is no reliable way to submit and wait in a single syscall, as io_submit_sqes() may under-consume sqes (in case of an early error). Then it will wait for not-yet-submitted requests, deadlocking the user in most cases. Don't wait/poll if can't submit all sqes Signed-off-by: Pavel Begunkov Signed-off-by: Jens Axboe --- fs/io_uring.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/fs/io_uring.c b/fs/io_uring.c index 1d4e7332ccae6..81e7fe6dee189 100644 --- a/fs/io_uring.c +++ b/fs/io_uring.c @@ -5135,6 +5135,9 @@ SYSCALL_DEFINE6(io_uring_enter, unsigned int, fd, u32, to_submit, submitted = io_submit_sqes(ctx, to_submit, f.file, fd, &cur_mm, false); mutex_unlock(&ctx->uring_lock); + + if (submitted != to_submit) + goto out; } if (flags & IORING_ENTER_GETEVENTS) { unsigned nr_events = 0; @@ -5148,6 +5151,7 @@ SYSCALL_DEFINE6(io_uring_enter, unsigned int, fd, u32, to_submit, } } +out: percpu_ref_put(&ctx->refs); out_fput: fdput(f); -- GitLab From 97c63746ebb9eebe34551fdb4370d6e3fff52059 Mon Sep 17 00:00:00 2001 From: John Crispin Date: Fri, 13 Dec 2019 16:38:31 +0100 Subject: [PATCH 0911/1437] ath11k: add wmi helper for turning STA PS on/off Add a WMI call helper to set the powersave mode of a STA interface. Signed-off-by: John Crispin Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath11k/wmi.c | 30 +++++++++++++++++++++++++++ drivers/net/wireless/ath/ath11k/wmi.h | 7 +++++++ 2 files changed, 37 insertions(+) diff --git a/drivers/net/wireless/ath/ath11k/wmi.c b/drivers/net/wireless/ath/ath11k/wmi.c index b05642617b78c..bf3c246ce8839 100644 --- a/drivers/net/wireless/ath/ath11k/wmi.c +++ b/drivers/net/wireless/ath/ath11k/wmi.c @@ -1178,6 +1178,36 @@ int ath11k_wmi_pdev_set_param(struct ath11k *ar, u32 param_id, return ret; } +int ath11k_wmi_pdev_set_ps_mode(struct ath11k *ar, int vdev_id, u32 enable) +{ + struct ath11k_pdev_wmi *wmi = ar->wmi; + struct wmi_pdev_set_ps_mode_cmd *cmd; + struct sk_buff *skb; + int ret; + + skb = ath11k_wmi_alloc_skb(wmi->wmi_sc, sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_pdev_set_ps_mode_cmd *)skb->data; + cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_STA_POWERSAVE_MODE_CMD) | + FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE); + cmd->vdev_id = vdev_id; + cmd->sta_ps_mode = enable; + + ret = ath11k_wmi_cmd_send(wmi, skb, WMI_STA_POWERSAVE_MODE_CMDID); + if (ret) { + ath11k_warn(ar->ab, "failed to send WMI_PDEV_SET_PARAM cmd\n"); + dev_kfree_skb(skb); + } + + ath11k_dbg(ar->ab, ATH11K_DBG_WMI, + "WMI vdev set psmode %d vdev id %d\n", + enable, vdev_id); + + return ret; +} + int ath11k_wmi_pdev_suspend(struct ath11k *ar, u32 suspend_opt, u32 pdev_id) { diff --git a/drivers/net/wireless/ath/ath11k/wmi.h b/drivers/net/wireless/ath/ath11k/wmi.h index 4a518d406bc52..9919d8bf297b9 100644 --- a/drivers/net/wireless/ath/ath11k/wmi.h +++ b/drivers/net/wireless/ath/ath11k/wmi.h @@ -2827,6 +2827,12 @@ struct wmi_pdev_set_param_cmd { u32 param_value; } __packed; +struct wmi_pdev_set_ps_mode_cmd { + u32 tlv_header; + u32 vdev_id; + u32 sta_ps_mode; +} __packed; + struct wmi_pdev_suspend_cmd { u32 tlv_header; u32 pdev_id; @@ -4682,6 +4688,7 @@ int ath11k_wmi_set_peer_param(struct ath11k *ar, const u8 *peer_addr, u32 vdev_id, u32 param_id, u32 param_val); int ath11k_wmi_pdev_set_param(struct ath11k *ar, u32 param_id, u32 param_value, u8 pdev_id); +int ath11k_wmi_pdev_set_ps_mode(struct ath11k *ar, int vdev_id, u32 enable); int ath11k_wmi_wait_for_unified_ready(struct ath11k_base *ab); int ath11k_wmi_cmd_init(struct ath11k_base *ab); int ath11k_wmi_wait_for_service_ready(struct ath11k_base *ab); -- GitLab From 6bfebd4bf93928958cd57df9a8ccd5862f88468c Mon Sep 17 00:00:00 2001 From: John Crispin Date: Fri, 13 Dec 2019 16:38:32 +0100 Subject: [PATCH 0912/1437] ath11k: disable PS for STA interfaces by default upon bringup After applying this setting the TX performance issue of STA interfaces is gone and we can see TX performance go up to ~900mbit on HE80. Signed-off-by: John Crispin Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath11k/mac.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/net/wireless/ath/ath11k/mac.c b/drivers/net/wireless/ath/ath11k/mac.c index 65f6a25855772..f5f7d8188e6eb 100644 --- a/drivers/net/wireless/ath/ath11k/mac.c +++ b/drivers/net/wireless/ath/ath11k/mac.c @@ -4172,6 +4172,13 @@ static int ath11k_mac_op_add_interface(struct ieee80211_hw *hw, arvif->vdev_id, ret); goto err_peer_del; } + + ret = ath11k_wmi_pdev_set_ps_mode(ar, arvif->vdev_id, false); + if (ret) { + ath11k_warn(ar->ab, "failed to disable vdev %d ps mode: %d\n", + arvif->vdev_id, ret); + goto err_peer_del; + } break; default: break; -- GitLab From bde4d95e92d56111c0548cb81dfe1ccb8b2bbfbd Mon Sep 17 00:00:00 2001 From: John Crispin Date: Fri, 13 Dec 2019 16:38:33 +0100 Subject: [PATCH 0913/1437] ath11k: drop memset when setting up a tx cmd desc There is no point in zero'ing out the structure if we set all values in the following line. Signed-off-by: John Crispin Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath11k/dp_tx.c | 2 -- drivers/net/wireless/ath/ath11k/hal_tx.c | 1 + 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/net/wireless/ath/ath11k/dp_tx.c b/drivers/net/wireless/ath/ath11k/dp_tx.c index a8b9557c2346f..28ebc414533ea 100644 --- a/drivers/net/wireless/ath/ath11k/dp_tx.c +++ b/drivers/net/wireless/ath/ath11k/dp_tx.c @@ -167,8 +167,6 @@ int ath11k_dp_tx(struct ath11k *ar, struct ath11k_vif *arvif, skb_cb->vif = arvif->vif; skb_cb->ar = ar; - memset(cached_desc, 0, HAL_TCL_DESC_LEN); - ath11k_hal_tx_cmd_desc_setup(ab, cached_desc, &ti); hal_ring_id = tx_ring->tcl_data_ring.ring_id; diff --git a/drivers/net/wireless/ath/ath11k/hal_tx.c b/drivers/net/wireless/ath/ath11k/hal_tx.c index cbe5497987620..72a51ed65a513 100644 --- a/drivers/net/wireless/ath/ath11k/hal_tx.c +++ b/drivers/net/wireless/ath/ath11k/hal_tx.c @@ -71,6 +71,7 @@ void ath11k_hal_tx_cmd_desc_setup(struct ath11k_base *ab, void *cmd, ti->dscp_tid_tbl_idx) | FIELD_PREP(HAL_TCL_DATA_CMD_INFO3_SEARCH_INDEX, ti->bss_ast_hash); + tcl_cmd->info4 = 0; } /* Commit the descriptor to hardware */ -- GitLab From 6bc9d6f786a56c4b8d267d7e9e52bbcde6a0b03a Mon Sep 17 00:00:00 2001 From: John Crispin Date: Fri, 13 Dec 2019 16:38:34 +0100 Subject: [PATCH 0914/1437] ath11k: rename ath11k_wmi_base instances from wmi_sc to wmi_ab This makes the code consistent with the recent sc to ab rename. Signed-off-by: John Crispin Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath11k/core.c | 4 +- drivers/net/wireless/ath/ath11k/core.h | 2 +- drivers/net/wireless/ath/ath11k/htc.c | 2 +- drivers/net/wireless/ath/ath11k/mac.c | 4 +- drivers/net/wireless/ath/ath11k/testmode.c | 2 +- drivers/net/wireless/ath/ath11k/wmi.c | 146 ++++++++++----------- drivers/net/wireless/ath/ath11k/wmi.h | 2 +- 7 files changed, 81 insertions(+), 81 deletions(-) diff --git a/drivers/net/wireless/ath/ath11k/core.c b/drivers/net/wireless/ath/ath11k/core.c index 5cdc9b2aee51a..9e823056e6735 100644 --- a/drivers/net/wireless/ath/ath11k/core.c +++ b/drivers/net/wireless/ath/ath11k/core.c @@ -651,7 +651,7 @@ static void ath11k_core_restart(struct work_struct *work) idr_destroy(&ar->txmgmt_idr); } - wake_up(&ab->wmi_sc.tx_credits_wq); + wake_up(&ab->wmi_ab.tx_credits_wq); wake_up(&ab->peer_mapping_wq); ret = ath11k_core_reconfigure_on_crash(ab); @@ -761,7 +761,7 @@ struct ath11k_base *ath11k_core_alloc(struct device *dev) INIT_LIST_HEAD(&ab->peers); init_waitqueue_head(&ab->peer_mapping_wq); - init_waitqueue_head(&ab->wmi_sc.tx_credits_wq); + init_waitqueue_head(&ab->wmi_ab.tx_credits_wq); INIT_WORK(&ab->restart_work, ath11k_core_restart); timer_setup(&ab->rx_replenish_retry, ath11k_ce_rx_replenish_retry, 0); ab->dev = dev; diff --git a/drivers/net/wireless/ath/ath11k/core.h b/drivers/net/wireless/ath/ath11k/core.h index 06482a5c102d8..25cdcf71d0c48 100644 --- a/drivers/net/wireless/ath/ath11k/core.h +++ b/drivers/net/wireless/ath/ath11k/core.h @@ -578,7 +578,7 @@ struct ath11k_base { struct platform_device *pdev; struct device *dev; struct ath11k_qmi qmi; - struct ath11k_wmi_base wmi_sc; + struct ath11k_wmi_base wmi_ab; struct completion fw_ready; struct rproc *tgt_rproc; int num_radios; diff --git a/drivers/net/wireless/ath/ath11k/htc.c b/drivers/net/wireless/ath/ath11k/htc.c index 9d4115f25ceb4..8f54f58b83e6f 100644 --- a/drivers/net/wireless/ath/ath11k/htc.c +++ b/drivers/net/wireless/ath/ath11k/htc.c @@ -736,7 +736,7 @@ int ath11k_htc_init(struct ath11k_base *ab) htc->ab = ab; - switch (ab->wmi_sc.preferred_hw_mode) { + switch (ab->wmi_ab.preferred_hw_mode) { case WMI_HOST_HW_MODE_SINGLE: htc->wmi_ep_count = 1; break; diff --git a/drivers/net/wireless/ath/ath11k/mac.c b/drivers/net/wireless/ath/ath11k/mac.c index f5f7d8188e6eb..3450c22cab647 100644 --- a/drivers/net/wireless/ath/ath11k/mac.c +++ b/drivers/net/wireless/ath/ath11k/mac.c @@ -5320,7 +5320,7 @@ ath11k_mac_update_bss_chan_survey(struct ath11k *ar, lockdep_assert_held(&ar->conf_mutex); - if (!test_bit(WMI_TLV_SERVICE_BSS_CHANNEL_INFO_64, ar->ab->wmi_sc.svc_map) || + if (!test_bit(WMI_TLV_SERVICE_BSS_CHANNEL_INFO_64, ar->ab->wmi_ab.svc_map) || ar->rx_channel != channel) return; @@ -5838,7 +5838,7 @@ int ath11k_mac_allocate(struct ath11k_base *ab) ar->pdev_idx = i; ar->lmac_id = ath11k_core_get_hw_mac_id(ab, i); - ar->wmi = &ab->wmi_sc.wmi[i]; + ar->wmi = &ab->wmi_ab.wmi[i]; /* FIXME wmi[0] is already initialized during attach, * Should we do this again? */ diff --git a/drivers/net/wireless/ath/ath11k/testmode.c b/drivers/net/wireless/ath/ath11k/testmode.c index 932bee6d17407..d2dc9db01491b 100644 --- a/drivers/net/wireless/ath/ath11k/testmode.c +++ b/drivers/net/wireless/ath/ath11k/testmode.c @@ -150,7 +150,7 @@ static int ath11k_tm_cmd_wmi(struct ath11k *ar, struct nlattr *tb[]) ath11k_dbg_dump(ar->ab, ATH11K_DBG_TESTMODE, NULL, "", buf, buf_len); - skb = ath11k_wmi_alloc_skb(wmi->wmi_sc, buf_len); + skb = ath11k_wmi_alloc_skb(wmi->wmi_ab, buf_len); if (!skb) { ret = -ENOMEM; goto out; diff --git a/drivers/net/wireless/ath/ath11k/wmi.c b/drivers/net/wireless/ath/ath11k/wmi.c index bf3c246ce8839..b16bfb50d73e5 100644 --- a/drivers/net/wireless/ath/ath11k/wmi.c +++ b/drivers/net/wireless/ath/ath11k/wmi.c @@ -205,7 +205,7 @@ static int ath11k_wmi_cmd_send_nowait(struct ath11k_pdev_wmi *wmi, struct sk_buf u32 cmd_id) { struct ath11k_skb_cb *skb_cb = ATH11K_SKB_CB(skb); - struct ath11k_base *ab = wmi->wmi_sc->ab; + struct ath11k_base *ab = wmi->wmi_ab->ab; struct wmi_cmd_hdr *cmd_hdr; int ret; u32 cmd = 0; @@ -234,7 +234,7 @@ err_pull: int ath11k_wmi_cmd_send(struct ath11k_pdev_wmi *wmi, struct sk_buff *skb, u32 cmd_id) { - struct ath11k_wmi_base *wmi_sc = wmi->wmi_sc; + struct ath11k_wmi_base *wmi_sc = wmi->wmi_ab; int ret = -EOPNOTSUPP; might_sleep(); @@ -448,7 +448,7 @@ static void ath11k_wmi_service_bitmap_copy(struct ath11k_pdev_wmi *wmi, for (i = 0, j = 0; i < WMI_SERVICE_BM_SIZE && j < WMI_MAX_SERVICE; i++) { do { if (wmi_svc_bm[i] & BIT(j % WMI_SERVICE_BITS_IN_SIZE32)) - set_bit(j, wmi->wmi_sc->svc_map); + set_bit(j, wmi->wmi_ab->svc_map); } while (++j % WMI_SERVICE_BITS_IN_SIZE32); } } @@ -457,7 +457,7 @@ static int ath11k_wmi_tlv_svc_rdy_parse(struct ath11k_base *ab, u16 tag, u16 len const void *ptr, void *data) { struct wmi_tlv_svc_ready_parse *svc_ready = data; - struct ath11k_pdev_wmi *wmi_handle = &ab->wmi_sc.wmi[0]; + struct ath11k_pdev_wmi *wmi_handle = &ab->wmi_ab.wmi[0]; u16 expect_len; switch (tag) { @@ -538,7 +538,7 @@ int ath11k_wmi_mgmt_send(struct ath11k *ar, u32 vdev_id, u32 buf_id, len = sizeof(*cmd) + sizeof(*frame_tlv) + roundup(buf_len, 4); - skb = ath11k_wmi_alloc_skb(wmi->wmi_sc, len); + skb = ath11k_wmi_alloc_skb(wmi->wmi_ab, len); if (!skb) return -ENOMEM; @@ -590,7 +590,7 @@ int ath11k_wmi_vdev_create(struct ath11k *ar, u8 *macaddr, len = sizeof(*cmd) + TLV_HDR_SIZE + (WMI_NUM_SUPPORTED_BAND_MAX * sizeof(*txrx_streams)); - skb = ath11k_wmi_alloc_skb(wmi->wmi_sc, len); + skb = ath11k_wmi_alloc_skb(wmi->wmi_ab, len); if (!skb) return -ENOMEM; @@ -656,7 +656,7 @@ int ath11k_wmi_vdev_delete(struct ath11k *ar, u8 vdev_id) struct sk_buff *skb; int ret; - skb = ath11k_wmi_alloc_skb(wmi->wmi_sc, sizeof(*cmd)); + skb = ath11k_wmi_alloc_skb(wmi->wmi_ab, sizeof(*cmd)); if (!skb) return -ENOMEM; @@ -683,7 +683,7 @@ int ath11k_wmi_vdev_stop(struct ath11k *ar, u8 vdev_id) struct sk_buff *skb; int ret; - skb = ath11k_wmi_alloc_skb(wmi->wmi_sc, sizeof(*cmd)); + skb = ath11k_wmi_alloc_skb(wmi->wmi_ab, sizeof(*cmd)); if (!skb) return -ENOMEM; @@ -711,7 +711,7 @@ int ath11k_wmi_vdev_down(struct ath11k *ar, u8 vdev_id) struct sk_buff *skb; int ret; - skb = ath11k_wmi_alloc_skb(wmi->wmi_sc, sizeof(*cmd)); + skb = ath11k_wmi_alloc_skb(wmi->wmi_ab, sizeof(*cmd)); if (!skb) return -ENOMEM; @@ -789,7 +789,7 @@ int ath11k_wmi_vdev_start(struct ath11k *ar, struct wmi_vdev_start_req_arg *arg, len = sizeof(*cmd) + sizeof(*chan) + TLV_HDR_SIZE; - skb = ath11k_wmi_alloc_skb(wmi->wmi_sc, len); + skb = ath11k_wmi_alloc_skb(wmi->wmi_ab, len); if (!skb) return -ENOMEM; @@ -867,7 +867,7 @@ int ath11k_wmi_vdev_up(struct ath11k *ar, u32 vdev_id, u32 aid, const u8 *bssid) struct sk_buff *skb; int ret; - skb = ath11k_wmi_alloc_skb(wmi->wmi_sc, sizeof(*cmd)); + skb = ath11k_wmi_alloc_skb(wmi->wmi_ab, sizeof(*cmd)); if (!skb) return -ENOMEM; @@ -901,7 +901,7 @@ int ath11k_wmi_send_peer_create_cmd(struct ath11k *ar, struct sk_buff *skb; int ret; - skb = ath11k_wmi_alloc_skb(wmi->wmi_sc, sizeof(*cmd)); + skb = ath11k_wmi_alloc_skb(wmi->wmi_ab, sizeof(*cmd)); if (!skb) return -ENOMEM; @@ -934,7 +934,7 @@ int ath11k_wmi_send_peer_delete_cmd(struct ath11k *ar, struct sk_buff *skb; int ret; - skb = ath11k_wmi_alloc_skb(wmi->wmi_sc, sizeof(*cmd)); + skb = ath11k_wmi_alloc_skb(wmi->wmi_ab, sizeof(*cmd)); if (!skb) return -ENOMEM; @@ -966,7 +966,7 @@ int ath11k_wmi_send_pdev_set_regdomain(struct ath11k *ar, struct sk_buff *skb; int ret; - skb = ath11k_wmi_alloc_skb(wmi->wmi_sc, sizeof(*cmd)); + skb = ath11k_wmi_alloc_skb(wmi->wmi_ab, sizeof(*cmd)); if (!skb) return -ENOMEM; @@ -1006,7 +1006,7 @@ int ath11k_wmi_set_peer_param(struct ath11k *ar, const u8 *peer_addr, struct sk_buff *skb; int ret; - skb = ath11k_wmi_alloc_skb(wmi->wmi_sc, sizeof(*cmd)); + skb = ath11k_wmi_alloc_skb(wmi->wmi_ab, sizeof(*cmd)); if (!skb) return -ENOMEM; @@ -1040,7 +1040,7 @@ int ath11k_wmi_send_peer_flush_tids_cmd(struct ath11k *ar, struct sk_buff *skb; int ret; - skb = ath11k_wmi_alloc_skb(wmi->wmi_sc, sizeof(*cmd)); + skb = ath11k_wmi_alloc_skb(wmi->wmi_ab, sizeof(*cmd)); if (!skb) return -ENOMEM; @@ -1076,7 +1076,7 @@ int ath11k_wmi_peer_rx_reorder_queue_setup(struct ath11k *ar, struct sk_buff *skb; int ret; - skb = ath11k_wmi_alloc_skb(ar->wmi->wmi_sc, sizeof(*cmd)); + skb = ath11k_wmi_alloc_skb(ar->wmi->wmi_ab, sizeof(*cmd)); if (!skb) return -ENOMEM; @@ -1118,7 +1118,7 @@ ath11k_wmi_rx_reord_queue_remove(struct ath11k *ar, struct sk_buff *skb; int ret; - skb = ath11k_wmi_alloc_skb(wmi->wmi_sc, sizeof(*cmd)); + skb = ath11k_wmi_alloc_skb(wmi->wmi_ab, sizeof(*cmd)); if (!skb) return -ENOMEM; @@ -1154,7 +1154,7 @@ int ath11k_wmi_pdev_set_param(struct ath11k *ar, u32 param_id, struct sk_buff *skb; int ret; - skb = ath11k_wmi_alloc_skb(wmi->wmi_sc, sizeof(*cmd)); + skb = ath11k_wmi_alloc_skb(wmi->wmi_ab, sizeof(*cmd)); if (!skb) return -ENOMEM; @@ -1185,7 +1185,7 @@ int ath11k_wmi_pdev_set_ps_mode(struct ath11k *ar, int vdev_id, u32 enable) struct sk_buff *skb; int ret; - skb = ath11k_wmi_alloc_skb(wmi->wmi_sc, sizeof(*cmd)); + skb = ath11k_wmi_alloc_skb(wmi->wmi_ab, sizeof(*cmd)); if (!skb) return -ENOMEM; @@ -1216,7 +1216,7 @@ int ath11k_wmi_pdev_suspend(struct ath11k *ar, u32 suspend_opt, struct sk_buff *skb; int ret; - skb = ath11k_wmi_alloc_skb(wmi->wmi_sc, sizeof(*cmd)); + skb = ath11k_wmi_alloc_skb(wmi->wmi_ab, sizeof(*cmd)); if (!skb) return -ENOMEM; @@ -1247,7 +1247,7 @@ int ath11k_wmi_pdev_resume(struct ath11k *ar, u32 pdev_id) struct sk_buff *skb; int ret; - skb = ath11k_wmi_alloc_skb(wmi->wmi_sc, sizeof(*cmd)); + skb = ath11k_wmi_alloc_skb(wmi->wmi_ab, sizeof(*cmd)); if (!skb) return -ENOMEM; @@ -1281,7 +1281,7 @@ int ath11k_wmi_pdev_bss_chan_info_request(struct ath11k *ar, struct sk_buff *skb; int ret; - skb = ath11k_wmi_alloc_skb(wmi->wmi_sc, sizeof(*cmd)); + skb = ath11k_wmi_alloc_skb(wmi->wmi_ab, sizeof(*cmd)); if (!skb) return -ENOMEM; @@ -1314,7 +1314,7 @@ int ath11k_wmi_send_set_ap_ps_param_cmd(struct ath11k *ar, u8 *peer_addr, struct sk_buff *skb; int ret; - skb = ath11k_wmi_alloc_skb(wmi->wmi_sc, sizeof(*cmd)); + skb = ath11k_wmi_alloc_skb(wmi->wmi_ab, sizeof(*cmd)); if (!skb) return -ENOMEM; @@ -1349,7 +1349,7 @@ int ath11k_wmi_set_sta_ps_param(struct ath11k *ar, u32 vdev_id, struct sk_buff *skb; int ret; - skb = ath11k_wmi_alloc_skb(wmi->wmi_sc, sizeof(*cmd)); + skb = ath11k_wmi_alloc_skb(wmi->wmi_ab, sizeof(*cmd)); if (!skb) return -ENOMEM; @@ -1384,7 +1384,7 @@ int ath11k_wmi_force_fw_hang_cmd(struct ath11k *ar, u32 type, u32 delay_time_ms) len = sizeof(*cmd); - skb = ath11k_wmi_alloc_skb(wmi->wmi_sc, len); + skb = ath11k_wmi_alloc_skb(wmi->wmi_ab, len); if (!skb) return -ENOMEM; @@ -1412,7 +1412,7 @@ int ath11k_wmi_vdev_set_param_cmd(struct ath11k *ar, u32 vdev_id, struct sk_buff *skb; int ret; - skb = ath11k_wmi_alloc_skb(wmi->wmi_sc, sizeof(*cmd)); + skb = ath11k_wmi_alloc_skb(wmi->wmi_ab, sizeof(*cmd)); if (!skb) return -ENOMEM; @@ -1446,7 +1446,7 @@ int ath11k_wmi_send_stats_request_cmd(struct ath11k *ar, struct sk_buff *skb; int ret; - skb = ath11k_wmi_alloc_skb(wmi->wmi_sc, sizeof(*cmd)); + skb = ath11k_wmi_alloc_skb(wmi->wmi_ab, sizeof(*cmd)); if (!skb) return -ENOMEM; @@ -1479,7 +1479,7 @@ int ath11k_wmi_send_bcn_offload_control_cmd(struct ath11k *ar, struct sk_buff *skb; int ret; - skb = ath11k_wmi_alloc_skb(wmi->wmi_sc, sizeof(*cmd)); + skb = ath11k_wmi_alloc_skb(wmi->wmi_ab, sizeof(*cmd)); if (!skb) return -ENOMEM; @@ -1520,7 +1520,7 @@ int ath11k_wmi_bcn_tmpl(struct ath11k *ar, u32 vdev_id, len = sizeof(*cmd) + sizeof(*bcn_prb_info) + TLV_HDR_SIZE + aligned_len; - skb = ath11k_wmi_alloc_skb(wmi->wmi_sc, len); + skb = ath11k_wmi_alloc_skb(wmi->wmi_ab, len); if (!skb) return -ENOMEM; @@ -1571,7 +1571,7 @@ int ath11k_wmi_vdev_install_key(struct ath11k *ar, len = sizeof(*cmd) + TLV_HDR_SIZE + key_len_aligned; - skb = ath11k_wmi_alloc_skb(wmi->wmi_sc, len); + skb = ath11k_wmi_alloc_skb(wmi->wmi_ab, len); if (!skb) return -ENOMEM; @@ -1717,7 +1717,7 @@ int ath11k_wmi_send_peer_assoc_cmd(struct ath11k *ar, sizeof(*mcs) + TLV_HDR_SIZE + (sizeof(*he_mcs) * param->peer_he_mcs_count); - skb = ath11k_wmi_alloc_skb(wmi->wmi_sc, len); + skb = ath11k_wmi_alloc_skb(wmi->wmi_ab, len); if (!skb) return -ENOMEM; @@ -1983,7 +1983,7 @@ int ath11k_wmi_send_scan_start_cmd(struct ath11k *ar, roundup(params->extraie.len, sizeof(u32)); len += extraie_len_with_pad; - skb = ath11k_wmi_alloc_skb(wmi->wmi_sc, len); + skb = ath11k_wmi_alloc_skb(wmi->wmi_ab, len); if (!skb) return -ENOMEM; @@ -2099,7 +2099,7 @@ int ath11k_wmi_send_scan_stop_cmd(struct ath11k *ar, struct sk_buff *skb; int ret; - skb = ath11k_wmi_alloc_skb(wmi->wmi_sc, sizeof(*cmd)); + skb = ath11k_wmi_alloc_skb(wmi->wmi_ab, sizeof(*cmd)); if (!skb) return -ENOMEM; @@ -2155,7 +2155,7 @@ int ath11k_wmi_send_scan_chan_list_cmd(struct ath11k *ar, len = sizeof(*cmd) + TLV_HDR_SIZE + sizeof(*chan_info) * chan_list->nallchans; - skb = ath11k_wmi_alloc_skb(wmi->wmi_sc, len); + skb = ath11k_wmi_alloc_skb(wmi->wmi_ab, len); if (!skb) return -ENOMEM; @@ -2247,7 +2247,7 @@ int ath11k_wmi_send_wmm_update_cmd_tlv(struct ath11k *ar, u32 vdev_id, struct sk_buff *skb; int ret, ac; - skb = ath11k_wmi_alloc_skb(wmi->wmi_sc, sizeof(*cmd)); + skb = ath11k_wmi_alloc_skb(wmi->wmi_ab, sizeof(*cmd)); if (!skb) return -ENOMEM; @@ -2314,7 +2314,7 @@ int ath11k_wmi_send_dfs_phyerr_offload_enable_cmd(struct ath11k *ar, struct sk_buff *skb; int ret; - skb = ath11k_wmi_alloc_skb(wmi->wmi_sc, sizeof(*cmd)); + skb = ath11k_wmi_alloc_skb(wmi->wmi_ab, sizeof(*cmd)); if (!skb) return -ENOMEM; @@ -2351,7 +2351,7 @@ int ath11k_wmi_pdev_peer_pktlog_filter(struct ath11k *ar, u8 *addr, u8 enable) int ret, len; len = sizeof(*cmd) + sizeof(*info) + TLV_HDR_SIZE; - skb = ath11k_wmi_alloc_skb(wmi->wmi_sc, len); + skb = ath11k_wmi_alloc_skb(wmi->wmi_ab, len); if (!skb) return -ENOMEM; @@ -2397,7 +2397,7 @@ ath11k_wmi_send_init_country_cmd(struct ath11k *ar, struct sk_buff *skb; int ret; - skb = ath11k_wmi_alloc_skb(wmi->wmi_sc, sizeof(*cmd)); + skb = ath11k_wmi_alloc_skb(wmi->wmi_ab, sizeof(*cmd)); if (!skb) return -ENOMEM; @@ -2449,7 +2449,7 @@ int ath11k_wmi_pdev_pktlog_enable(struct ath11k *ar, u32 pktlog_filter) struct sk_buff *skb; int ret; - skb = ath11k_wmi_alloc_skb(wmi->wmi_sc, sizeof(*cmd)); + skb = ath11k_wmi_alloc_skb(wmi->wmi_ab, sizeof(*cmd)); if (!skb) return -ENOMEM; @@ -2479,7 +2479,7 @@ int ath11k_wmi_pdev_pktlog_disable(struct ath11k *ar) struct sk_buff *skb; int ret; - skb = ath11k_wmi_alloc_skb(wmi->wmi_sc, sizeof(*cmd)); + skb = ath11k_wmi_alloc_skb(wmi->wmi_ab, sizeof(*cmd)); if (!skb) return -ENOMEM; @@ -2504,14 +2504,14 @@ int ath11k_wmi_send_twt_enable_cmd(struct ath11k *ar, u32 pdev_id) { struct ath11k_pdev_wmi *wmi = ar->wmi; - struct ath11k_base *ab = wmi->wmi_sc->ab; + struct ath11k_base *ab = wmi->wmi_ab->ab; struct wmi_twt_enable_params_cmd *cmd; struct sk_buff *skb; int ret, len; len = sizeof(*cmd); - skb = ath11k_wmi_alloc_skb(wmi->wmi_sc, len); + skb = ath11k_wmi_alloc_skb(wmi->wmi_ab, len); if (!skb) return -ENOMEM; @@ -2555,14 +2555,14 @@ int ath11k_wmi_send_twt_disable_cmd(struct ath11k *ar, u32 pdev_id) { struct ath11k_pdev_wmi *wmi = ar->wmi; - struct ath11k_base *ab = wmi->wmi_sc->ab; + struct ath11k_base *ab = wmi->wmi_ab->ab; struct wmi_twt_disable_params_cmd *cmd; struct sk_buff *skb; int ret, len; len = sizeof(*cmd); - skb = ath11k_wmi_alloc_skb(wmi->wmi_sc, len); + skb = ath11k_wmi_alloc_skb(wmi->wmi_ab, len); if (!skb) return -ENOMEM; @@ -2585,14 +2585,14 @@ ath11k_wmi_send_obss_spr_cmd(struct ath11k *ar, u32 vdev_id, struct ieee80211_he_obss_pd *he_obss_pd) { struct ath11k_pdev_wmi *wmi = ar->wmi; - struct ath11k_base *ab = wmi->wmi_sc->ab; + struct ath11k_base *ab = wmi->wmi_ab->ab; struct wmi_obss_spatial_reuse_params_cmd *cmd; struct sk_buff *skb; int ret, len; len = sizeof(*cmd); - skb = ath11k_wmi_alloc_skb(wmi->wmi_sc, len); + skb = ath11k_wmi_alloc_skb(wmi->wmi_ab, len); if (!skb) return -ENOMEM; @@ -2714,7 +2714,7 @@ ath11k_wmi_copy_resource_config(struct wmi_resource_config *wmi_cfg, static int ath11k_init_cmd_send(struct ath11k_pdev_wmi *wmi, struct wmi_init_cmd_param *param) { - struct ath11k_base *ab = wmi->wmi_sc->ab; + struct ath11k_base *ab = wmi->wmi_ab->ab; struct sk_buff *skb; struct wmi_init_cmd *cmd; struct wmi_resource_config *cfg; @@ -2734,7 +2734,7 @@ static int ath11k_init_cmd_send(struct ath11k_pdev_wmi *wmi, len = sizeof(*cmd) + TLV_HDR_SIZE + sizeof(*cfg) + hw_mode_len + (sizeof(*host_mem_chunks) * WMI_MAX_MEM_REQS); - skb = ath11k_wmi_alloc_skb(wmi->wmi_sc, len); + skb = ath11k_wmi_alloc_skb(wmi->wmi_ab, len); if (!skb) return -ENOMEM; @@ -2829,7 +2829,7 @@ int ath11k_wmi_wait_for_service_ready(struct ath11k_base *ab) { unsigned long time_left; - time_left = wait_for_completion_timeout(&ab->wmi_sc.service_ready, + time_left = wait_for_completion_timeout(&ab->wmi_ab.service_ready, WMI_SERVICE_READY_TIMEOUT_HZ); if (!time_left) return -ETIMEDOUT; @@ -2841,7 +2841,7 @@ int ath11k_wmi_wait_for_unified_ready(struct ath11k_base *ab) { unsigned long time_left; - time_left = wait_for_completion_timeout(&ab->wmi_sc.unified_ready, + time_left = wait_for_completion_timeout(&ab->wmi_ab.unified_ready, WMI_SERVICE_READY_TIMEOUT_HZ); if (!time_left) return -ETIMEDOUT; @@ -2851,7 +2851,7 @@ int ath11k_wmi_wait_for_unified_ready(struct ath11k_base *ab) int ath11k_wmi_cmd_init(struct ath11k_base *ab) { - struct ath11k_wmi_base *wmi_sc = &ab->wmi_sc; + struct ath11k_wmi_base *wmi_sc = &ab->wmi_ab; struct wmi_init_cmd_param init_param; struct target_resource_config config; @@ -2971,16 +2971,16 @@ static int ath11k_wmi_tlv_hw_mode_caps(struct ath11k_base *soc, while (i < svc_rdy_ext->n_hw_mode_caps) { hw_mode_caps = &svc_rdy_ext->hw_mode_caps[i]; mode = hw_mode_caps->hw_mode_id; - pref = soc->wmi_sc.preferred_hw_mode; + pref = soc->wmi_ab.preferred_hw_mode; if (ath11k_hw_mode_pri_map[mode] < ath11k_hw_mode_pri_map[pref]) { svc_rdy_ext->pref_hw_mode_caps = *hw_mode_caps; - soc->wmi_sc.preferred_hw_mode = mode; + soc->wmi_ab.preferred_hw_mode = mode; } i++; } - if (soc->wmi_sc.preferred_hw_mode == WMI_HOST_HW_MODE_MAX) + if (soc->wmi_ab.preferred_hw_mode == WMI_HOST_HW_MODE_MAX) return -EINVAL; return 0; @@ -3030,7 +3030,7 @@ static int ath11k_wmi_tlv_ext_hal_reg_caps_parse(struct ath11k_base *soc, static int ath11k_wmi_tlv_ext_hal_reg_caps(struct ath11k_base *soc, u16 len, const void *ptr, void *data) { - struct ath11k_pdev_wmi *wmi_handle = &soc->wmi_sc.wmi[0]; + struct ath11k_pdev_wmi *wmi_handle = &soc->wmi_ab.wmi[0]; struct wmi_tlv_svc_rdy_ext_parse *svc_rdy_ext = data; struct ath11k_hal_reg_capabilities_ext reg_cap; int ret; @@ -3066,7 +3066,7 @@ static int ath11k_wmi_tlv_ext_soc_hal_reg_caps_parse(struct ath11k_base *soc, u16 len, const void *ptr, void *data) { - struct ath11k_pdev_wmi *wmi_handle = &soc->wmi_sc.wmi[0]; + struct ath11k_pdev_wmi *wmi_handle = &soc->wmi_ab.wmi[0]; struct wmi_tlv_svc_rdy_ext_parse *svc_rdy_ext = data; u8 hw_mode_id = svc_rdy_ext->pref_hw_mode_caps.hw_mode_id; u32 phy_id_map; @@ -3104,7 +3104,7 @@ static int ath11k_wmi_tlv_svc_rdy_ext_parse(struct ath11k_base *ab, u16 tag, u16 len, const void *ptr, void *data) { - struct ath11k_pdev_wmi *wmi_handle = &ab->wmi_sc.wmi[0]; + struct ath11k_pdev_wmi *wmi_handle = &ab->wmi_ab.wmi[0]; struct wmi_tlv_svc_rdy_ext_parse *svc_rdy_ext = data; int ret; @@ -3156,7 +3156,7 @@ static int ath11k_wmi_tlv_svc_rdy_ext_parse(struct ath11k_base *ab, return ret; svc_rdy_ext->ext_hal_reg_done = true; - complete(&ab->wmi_sc.service_ready); + complete(&ab->wmi_ab.service_ready); } break; @@ -4498,7 +4498,7 @@ unlock: static void ath11k_wmi_op_ep_tx_credits(struct ath11k_base *ab) { /* try to send pending beacons first. they take priority */ - wake_up(&ab->wmi_sc.tx_credits_wq); + wake_up(&ab->wmi_ab.tx_credits_wq); } static void ath11k_wmi_htc_tx_complete(struct ath11k_base *ab, @@ -4668,7 +4668,7 @@ static int ath11k_ready_event(struct ath11k_base *ab, struct sk_buff *skb) return ret; } - complete(&ab->wmi_sc.unified_ready); + complete(&ab->wmi_ab.unified_ready); return 0; } @@ -5319,7 +5319,7 @@ static void ath11k_service_available_event(struct ath11k_base *ab, struct sk_buf do { if (ev->wmi_service_segment_bitmap[i] & BIT(j % WMI_AVAIL_SERVICE_BITS_IN_SIZE32)) - set_bit(j, ab->wmi_sc.svc_map); + set_bit(j, ab->wmi_ab.svc_map); } while (++j % WMI_AVAIL_SERVICE_BITS_IN_SIZE32); } @@ -5639,9 +5639,9 @@ static int ath11k_connect_pdev_htc_service(struct ath11k_base *ab, return status; } - ab->wmi_sc.wmi_endpoint_id[pdev_idx] = conn_resp.eid; - ab->wmi_sc.wmi[pdev_idx].eid = conn_resp.eid; - ab->wmi_sc.max_msg_len[pdev_idx] = conn_resp.max_msg_len; + ab->wmi_ab.wmi_endpoint_id[pdev_idx] = conn_resp.eid; + ab->wmi_ab.wmi[pdev_idx].eid = conn_resp.eid; + ab->wmi_ab.max_msg_len[pdev_idx] = conn_resp.max_msg_len; return 0; } @@ -5664,7 +5664,7 @@ ath11k_wmi_send_unit_test_cmd(struct ath11k *ar, arg_len = sizeof(u32) * ut_cmd.num_args; buf_len = sizeof(ut_cmd) + arg_len + TLV_HDR_SIZE; - skb = ath11k_wmi_alloc_skb(wmi->wmi_sc, buf_len); + skb = ath11k_wmi_alloc_skb(wmi->wmi_ab, buf_len); if (!skb) return -ENOMEM; @@ -5771,11 +5771,11 @@ int ath11k_wmi_pdev_attach(struct ath11k_base *ab, if (pdev_id >= MAX_RADIOS) return -EINVAL; - wmi_handle = &ab->wmi_sc.wmi[pdev_id]; + wmi_handle = &ab->wmi_ab.wmi[pdev_id]; - wmi_handle->wmi_sc = &ab->wmi_sc; + wmi_handle->wmi_ab = &ab->wmi_ab; - ab->wmi_sc.ab = ab; + ab->wmi_ab.ab = ab; /* TODO: Init remaining resource specific to pdev */ return 0; @@ -5789,12 +5789,12 @@ int ath11k_wmi_attach(struct ath11k_base *ab) if (ret) return ret; - ab->wmi_sc.ab = ab; - ab->wmi_sc.preferred_hw_mode = WMI_HOST_HW_MODE_MAX; + ab->wmi_ab.ab = ab; + ab->wmi_ab.preferred_hw_mode = WMI_HOST_HW_MODE_MAX; /* TODO: Init remaining wmi soc resources required */ - init_completion(&ab->wmi_sc.service_ready); - init_completion(&ab->wmi_sc.unified_ready); + init_completion(&ab->wmi_ab.service_ready); + init_completion(&ab->wmi_ab.unified_ready); return 0; } diff --git a/drivers/net/wireless/ath/ath11k/wmi.h b/drivers/net/wireless/ath/ath11k/wmi.h index 9919d8bf297b9..ab983aac604b4 100644 --- a/drivers/net/wireless/ath/ath11k/wmi.h +++ b/drivers/net/wireless/ath/ath11k/wmi.h @@ -2354,7 +2354,7 @@ struct wmi_service_available_event { } __packed; struct ath11k_pdev_wmi { - struct ath11k_wmi_base *wmi_sc; + struct ath11k_wmi_base *wmi_ab; enum ath11k_htc_ep_id eid; const struct wmi_peer_flags_map *peer_flags; u32 rx_decap_mode; -- GitLab From 8cfa7ef8127b00de1ce0275a85220c331fe9a4d0 Mon Sep 17 00:00:00 2001 From: John Crispin Date: Fri, 13 Dec 2019 16:38:37 +0100 Subject: [PATCH 0915/1437] ath11k: move some tx_status parsing to debugfs code Some of the fields are only used by debugfs. Move the parsing of these from the data hot path to the debugfs code. Signed-off-by: John Crispin Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath11k/debugfs_sta.c | 63 ++++++++++--------- drivers/net/wireless/ath/ath11k/hal_tx.c | 19 +----- drivers/net/wireless/ath/ath11k/hal_tx.h | 7 +-- 3 files changed, 36 insertions(+), 53 deletions(-) diff --git a/drivers/net/wireless/ath/ath11k/debugfs_sta.c b/drivers/net/wireless/ath/ath11k/debugfs_sta.c index 3c5f931e22a9d..743760c9bcae4 100644 --- a/drivers/net/wireless/ath/ath11k/debugfs_sta.c +++ b/drivers/net/wireless/ath/ath11k/debugfs_sta.c @@ -129,12 +129,16 @@ void ath11k_update_per_peer_stats_from_txcompl(struct ath11k *ar, { struct ath11k_base *ab = ar->ab; struct ath11k_per_peer_tx_stats *peer_stats = &ar->cached_stats; + enum hal_tx_rate_stats_pkt_type pkt_type; + enum hal_tx_rate_stats_sgi sgi; + enum hal_tx_rate_stats_bw bw; struct ath11k_peer *peer; struct ath11k_sta *arsta; struct ieee80211_sta *sta; u16 rate; u8 rate_idx; int ret; + u8 mcs; rcu_read_lock(); spin_lock_bh(&ab->base_lock); @@ -150,51 +154,52 @@ void ath11k_update_per_peer_stats_from_txcompl(struct ath11k *ar, arsta = (struct ath11k_sta *)sta->drv_priv; memset(&arsta->txrate, 0, sizeof(arsta->txrate)); - - if (ts->pkt_type == HAL_TX_RATE_STATS_PKT_TYPE_11A || - ts->pkt_type == HAL_TX_RATE_STATS_PKT_TYPE_11B) { - ret = ath11k_mac_hw_ratecode_to_legacy_rate(ts->mcs, - ts->pkt_type, + pkt_type = FIELD_GET(HAL_TX_RATE_STATS_INFO0_PKT_TYPE, + ts->rate_stats); + mcs = FIELD_GET(HAL_TX_RATE_STATS_INFO0_MCS, + ts->rate_stats); + sgi = FIELD_GET(HAL_TX_RATE_STATS_INFO0_SGI, + ts->rate_stats); + bw = FIELD_GET(HAL_TX_RATE_STATS_INFO0_BW, ts->rate_stats); + + if (pkt_type == HAL_TX_RATE_STATS_PKT_TYPE_11A || + pkt_type == HAL_TX_RATE_STATS_PKT_TYPE_11B) { + ret = ath11k_mac_hw_ratecode_to_legacy_rate(mcs, + pkt_type, &rate_idx, &rate); - if (ret < 0) { - spin_unlock_bh(&ab->base_lock); - rcu_read_unlock(); - return; - } + if (ret < 0) + goto err_out; arsta->txrate.legacy = rate; - } else if (ts->pkt_type == HAL_TX_RATE_STATS_PKT_TYPE_11N) { - if (ts->mcs > 7) { - ath11k_warn(ab, "Invalid HT mcs index %d\n", ts->mcs); - spin_unlock_bh(&ab->base_lock); - rcu_read_unlock(); - return; + } else if (pkt_type == HAL_TX_RATE_STATS_PKT_TYPE_11N) { + if (mcs > 7) { + ath11k_warn(ab, "Invalid HT mcs index %d\n", mcs); + goto err_out; } - arsta->txrate.mcs = ts->mcs + 8 * (arsta->last_txrate.nss - 1); + arsta->txrate.mcs = mcs + 8 * (arsta->last_txrate.nss - 1); arsta->txrate.flags = RATE_INFO_FLAGS_MCS; - if (ts->sgi) + if (sgi) arsta->txrate.flags |= RATE_INFO_FLAGS_SHORT_GI; - } else if (ts->pkt_type == HAL_TX_RATE_STATS_PKT_TYPE_11AC) { - if (ts->mcs > 9) { - ath11k_warn(ab, "Invalid VHT mcs index %d\n", ts->mcs); - spin_unlock_bh(&ab->base_lock); - rcu_read_unlock(); - return; + } else if (pkt_type == HAL_TX_RATE_STATS_PKT_TYPE_11AC) { + if (mcs > 9) { + ath11k_warn(ab, "Invalid VHT mcs index %d\n", mcs); + goto err_out; } - arsta->txrate.mcs = ts->mcs; + arsta->txrate.mcs = mcs; arsta->txrate.flags = RATE_INFO_FLAGS_VHT_MCS; - if (ts->sgi) + if (sgi) arsta->txrate.flags |= RATE_INFO_FLAGS_SHORT_GI; - } else { - /*TODO: update HE rates */ + } else if (pkt_type == HAL_TX_RATE_STATS_PKT_TYPE_11AX) { + /* TODO */ } arsta->txrate.nss = arsta->last_txrate.nss; - arsta->txrate.bw = ath11k_mac_bw_to_mac80211_bw(ts->bw); + arsta->txrate.bw = ath11k_mac_bw_to_mac80211_bw(bw); ath11k_accumulate_per_peer_tx_stats(arsta, peer_stats, rate_idx); +err_out: spin_unlock_bh(&ab->base_lock); rcu_read_unlock(); } diff --git a/drivers/net/wireless/ath/ath11k/hal_tx.c b/drivers/net/wireless/ath/ath11k/hal_tx.c index 72a51ed65a513..c2a3a9d2e5340 100644 --- a/drivers/net/wireless/ath/ath11k/hal_tx.c +++ b/drivers/net/wireless/ath/ath11k/hal_tx.c @@ -127,24 +127,7 @@ void ath11k_hal_tx_status_parse(struct ath11k_base *ab, if (!(desc->rate_stats.info0 & HAL_TX_RATE_STATS_INFO0_VALID)) return; - ts->flags |= HAL_TX_STATUS_FLAGS_RATE_STATS_VALID; - ts->tsf = desc->rate_stats.tsf; - ts->bw = FIELD_GET(HAL_TX_RATE_STATS_INFO0_BW, desc->rate_stats.info0); - ts->pkt_type = FIELD_GET(HAL_TX_RATE_STATS_INFO0_PKT_TYPE, - desc->rate_stats.info0); - if (desc->rate_stats.info0 & HAL_TX_RATE_STATS_INFO0_STBC) - ts->flags |= HAL_TX_STATUS_FLAGS_RATE_STBC; - if (desc->rate_stats.info0 & HAL_TX_RATE_STATS_INFO0_LDPC) - ts->flags |= HAL_TX_STATUS_FLAGS_RATE_LDPC; - if (desc->rate_stats.info0 & HAL_TX_RATE_STATS_INFO0_OFDMA_TX) - ts->flags |= HAL_TX_STATUS_FLAGS_OFDMA; - - ts->sgi = FIELD_GET(HAL_TX_RATE_STATS_INFO0_SGI, - desc->rate_stats.info0); - ts->mcs = FIELD_GET(HAL_TX_RATE_STATS_INFO0_MCS, - desc->rate_stats.info0); - ts->num_tones_in_ru = FIELD_GET(HAL_TX_RATE_STATS_INFO0_TONES_IN_RU, - desc->rate_stats.info0); + ts->rate_stats = desc->rate_stats.info0; } void ath11k_hal_tx_set_dscp_tid_map(struct ath11k_base *ab, int id) diff --git a/drivers/net/wireless/ath/ath11k/hal_tx.h b/drivers/net/wireless/ath/ath11k/hal_tx.h index 5217eaf9da501..cf99e2b31f9f1 100644 --- a/drivers/net/wireless/ath/ath11k/hal_tx.h +++ b/drivers/net/wireless/ath/ath11k/hal_tx.h @@ -51,17 +51,12 @@ struct hal_tx_status { u32 desc_id; enum hal_wbm_tqm_rel_reason status; u8 ack_rssi; - enum hal_tx_rate_stats_bw bw; - enum hal_tx_rate_stats_pkt_type pkt_type; - enum hal_tx_rate_stats_sgi sgi; - u8 mcs; - u16 num_tones_in_ru; u32 flags; /* %HAL_TX_STATUS_FLAGS_ */ - u32 tsf; u32 ppdu_id; u8 try_cnt; u8 tid; u16 peer_id; + u32 rate_stats; }; void ath11k_hal_tx_cmd_desc_setup(struct ath11k_base *ab, void *cmd, -- GitLab From d0998eb84ed3cc02c15f92b07223735ddbbe9b06 Mon Sep 17 00:00:00 2001 From: John Crispin Date: Fri, 13 Dec 2019 16:38:38 +0100 Subject: [PATCH 0916/1437] ath11k: optimise ath11k_dp_tx_completion_handler the current code does 4 memcpys for each completion frame. 1) duplicate the desc 2 + 3) inside kfifo insertion 4) kfifo remove The code simply drops the kfifo and uses a trivial ring buffer. This requires a single memcpy for insertion. There is no removal needed as we can simply use the inserted data for processing. As the code runs inside the NAPI context it is atomic and there is no need for most of the locking. Signed-off-by: John Crispin Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath11k/dp.c | 16 ++++----- drivers/net/wireless/ath/ath11k/dp.h | 11 +++---- drivers/net/wireless/ath/ath11k/dp_tx.c | 42 ++++++++++-------------- drivers/net/wireless/ath/ath11k/hal_tx.c | 13 -------- drivers/net/wireless/ath/ath11k/hal_tx.h | 2 -- 5 files changed, 28 insertions(+), 56 deletions(-) diff --git a/drivers/net/wireless/ath/ath11k/dp.c b/drivers/net/wireless/ath/ath11k/dp.c index b966a16a930fd..b112825a52edf 100644 --- a/drivers/net/wireless/ath/ath11k/dp.c +++ b/drivers/net/wireless/ath/ath11k/dp.c @@ -3,7 +3,6 @@ * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. */ -#include #include "core.h" #include "dp_tx.h" #include "hal_tx.h" @@ -828,10 +827,7 @@ void ath11k_dp_free(struct ath11k_base *ab) ath11k_dp_tx_pending_cleanup, ab); idr_destroy(&dp->tx_ring[i].txbuf_idr); spin_unlock_bh(&dp->tx_ring[i].tx_idr_lock); - - spin_lock_bh(&dp->tx_ring[i].tx_status_lock); - kfifo_free(&dp->tx_ring[i].tx_status_fifo); - spin_unlock_bh(&dp->tx_ring[i].tx_status_lock); + kfree(dp->tx_ring[i].tx_status); } /* Deinit any SOC level resource */ @@ -871,17 +867,17 @@ int ath11k_dp_alloc(struct ath11k_base *ab) if (ret) goto fail_link_desc_cleanup; - size = roundup_pow_of_two(DP_TX_COMP_RING_SIZE); + size = sizeof(struct hal_wbm_release_ring) * DP_TX_COMP_RING_SIZE; for (i = 0; i < DP_TCL_NUM_RING_MAX; i++) { idr_init(&dp->tx_ring[i].txbuf_idr); spin_lock_init(&dp->tx_ring[i].tx_idr_lock); dp->tx_ring[i].tcl_data_ring_id = i; - spin_lock_init(&dp->tx_ring[i].tx_status_lock); - ret = kfifo_alloc(&dp->tx_ring[i].tx_status_fifo, size, - GFP_KERNEL); - if (ret) + dp->tx_ring[i].tx_status_head = 0; + dp->tx_ring[i].tx_status_tail = DP_TX_COMP_RING_SIZE - 1; + dp->tx_ring[i].tx_status = kmalloc(size, GFP_KERNEL); + if (!dp->tx_ring[i].tx_status) goto fail_cmn_srng_cleanup; } diff --git a/drivers/net/wireless/ath/ath11k/dp.h b/drivers/net/wireless/ath/ath11k/dp.h index f7e53509ae074..2f0980f2c762a 100644 --- a/drivers/net/wireless/ath/ath11k/dp.h +++ b/drivers/net/wireless/ath/ath11k/dp.h @@ -6,7 +6,6 @@ #ifndef ATH11K_DP_H #define ATH11K_DP_H -#include #include "hal_rx.h" struct ath11k_base; @@ -58,6 +57,8 @@ struct dp_rxdma_ring { int bufs_max; }; +#define ATH11K_TX_COMPL_NEXT(x) (((x) + 1) % DP_TX_COMP_RING_SIZE) + struct dp_tx_ring { u8 tcl_data_ring_id; struct dp_srng tcl_data_ring; @@ -65,11 +66,9 @@ struct dp_tx_ring { struct idr txbuf_idr; /* Protects txbuf_idr and num_pending */ spinlock_t tx_idr_lock; - DECLARE_KFIFO_PTR(tx_status_fifo, struct hal_wbm_release_ring); - /* lock to protect tx_status_fifo because tx_status_fifo can be - * accessed concurrently. - */ - spinlock_t tx_status_lock; + struct hal_wbm_release_ring *tx_status; + int tx_status_head; + int tx_status_tail; }; struct ath11k_pdev_mon_stats { diff --git a/drivers/net/wireless/ath/ath11k/dp_tx.c b/drivers/net/wireless/ath/ath11k/dp_tx.c index 28ebc414533ea..3eff9e0468c5c 100644 --- a/drivers/net/wireless/ath/ath11k/dp_tx.c +++ b/drivers/net/wireless/ath/ath11k/dp_tx.c @@ -79,7 +79,6 @@ int ath11k_dp_tx(struct ath11k *ar, struct ath11k_vif *arvif, struct hal_srng *tcl_ring; struct ieee80211_hdr *hdr = (void *)skb->data; struct dp_tx_ring *tx_ring; - u8 cached_desc[HAL_TCL_DESC_LEN]; void *hal_tcl_desc; u8 pool_id; u8 hal_ring_id; @@ -167,8 +166,6 @@ int ath11k_dp_tx(struct ath11k *ar, struct ath11k_vif *arvif, skb_cb->vif = arvif->vif; skb_cb->ar = ar; - ath11k_hal_tx_cmd_desc_setup(ab, cached_desc, &ti); - hal_ring_id = tx_ring->tcl_data_ring.ring_id; tcl_ring = &ab->hal.srng_list[hal_ring_id]; @@ -188,7 +185,8 @@ int ath11k_dp_tx(struct ath11k *ar, struct ath11k_vif *arvif, goto fail_unmap_dma; } - ath11k_hal_tx_desc_sync(cached_desc, hal_tcl_desc); + ath11k_hal_tx_cmd_desc_setup(ab, hal_tcl_desc + + sizeof(struct hal_tlv_hdr), &ti); ath11k_hal_srng_access_end(ab, tcl_ring); @@ -432,47 +430,45 @@ void ath11k_dp_tx_completion_handler(struct ath11k_base *ab, int ring_id) int hal_ring_id = dp->tx_ring[ring_id].tcl_comp_ring.ring_id; struct hal_srng *status_ring = &ab->hal.srng_list[hal_ring_id]; struct sk_buff *msdu; - struct hal_wbm_release_ring tx_status; struct hal_tx_status ts; struct dp_tx_ring *tx_ring = &dp->tx_ring[ring_id]; u32 *desc; u32 msdu_id; u8 mac_id; - spin_lock_bh(&status_ring->lock); - ath11k_hal_srng_access_begin(ab, status_ring); - spin_lock_bh(&tx_ring->tx_status_lock); - while (!kfifo_is_full(&tx_ring->tx_status_fifo) && + while ((ATH11K_TX_COMPL_NEXT(tx_ring->tx_status_head) != + tx_ring->tx_status_tail) && (desc = ath11k_hal_srng_dst_get_next_entry(ab, status_ring))) { - ath11k_hal_tx_status_desc_sync((void *)desc, - (void *)&tx_status); - kfifo_put(&tx_ring->tx_status_fifo, tx_status); + memcpy(&tx_ring->tx_status[tx_ring->tx_status_head], + desc, sizeof(struct hal_wbm_release_ring)); + tx_ring->tx_status_head = + ATH11K_TX_COMPL_NEXT(tx_ring->tx_status_head); } if ((ath11k_hal_srng_dst_peek(ab, status_ring) != NULL) && - kfifo_is_full(&tx_ring->tx_status_fifo)) { + (ATH11K_TX_COMPL_NEXT(tx_ring->tx_status_head) == tx_ring->tx_status_tail)) { /* TODO: Process pending tx_status messages when kfifo_is_full() */ ath11k_warn(ab, "Unable to process some of the tx_status ring desc because status_fifo is full\n"); } - spin_unlock_bh(&tx_ring->tx_status_lock); - ath11k_hal_srng_access_end(ab, status_ring); - spin_unlock_bh(&status_ring->lock); - spin_lock_bh(&tx_ring->tx_status_lock); - while (kfifo_get(&tx_ring->tx_status_fifo, &tx_status)) { - memset(&ts, 0, sizeof(ts)); - ath11k_hal_tx_status_parse(ab, &tx_status, &ts); + while (ATH11K_TX_COMPL_NEXT(tx_ring->tx_status_tail) != tx_ring->tx_status_head) { + struct hal_wbm_release_ring *tx_status; + + tx_ring->tx_status_tail = + ATH11K_TX_COMPL_NEXT(tx_ring->tx_status_tail); + tx_status = &tx_ring->tx_status[tx_ring->tx_status_tail]; + ath11k_hal_tx_status_parse(ab, tx_status, &ts); mac_id = FIELD_GET(DP_TX_DESC_ID_MAC_ID, ts.desc_id); msdu_id = FIELD_GET(DP_TX_DESC_ID_MSDU_ID, ts.desc_id); if (ts.buf_rel_source == HAL_WBM_REL_SRC_MODULE_FW) { ath11k_dp_tx_process_htt_tx_complete(ab, - (void *)&tx_status, + (void *)tx_status, mac_id, msdu_id, tx_ring); continue; @@ -494,12 +490,8 @@ void ath11k_dp_tx_completion_handler(struct ath11k_base *ab, int ring_id) if (atomic_dec_and_test(&ar->dp.num_tx_pending)) wake_up(&ar->dp.tx_empty_waitq); - /* TODO: Locking optimization so that tx_completion for an msdu - * is not called with tx_status_lock acquired - */ ath11k_dp_tx_complete_msdu(ar, msdu, &ts); } - spin_unlock_bh(&tx_ring->tx_status_lock); } int ath11k_dp_tx_send_reo_cmd(struct ath11k_base *ab, struct dp_rx_tid *rx_tid, diff --git a/drivers/net/wireless/ath/ath11k/hal_tx.c b/drivers/net/wireless/ath/ath11k/hal_tx.c index c2a3a9d2e5340..e8710bbbbc3a7 100644 --- a/drivers/net/wireless/ath/ath11k/hal_tx.c +++ b/drivers/net/wireless/ath/ath11k/hal_tx.c @@ -74,19 +74,6 @@ void ath11k_hal_tx_cmd_desc_setup(struct ath11k_base *ab, void *cmd, tcl_cmd->info4 = 0; } -/* Commit the descriptor to hardware */ -void ath11k_hal_tx_desc_sync(void *tx_desc_cached, void *hw_desc) -{ - memcpy(hw_desc + sizeof(struct hal_tlv_hdr), tx_desc_cached, - sizeof(struct hal_tcl_data_cmd)); -} - -/* Get the descriptor status from hardware */ -void ath11k_hal_tx_status_desc_sync(void *hw_desc, void *local_desc) -{ - memcpy(local_desc, hw_desc, HAL_TX_STATUS_DESC_LEN); -} - void ath11k_hal_tx_status_parse(struct ath11k_base *ab, struct hal_wbm_release_ring *desc, struct hal_tx_status *ts) diff --git a/drivers/net/wireless/ath/ath11k/hal_tx.h b/drivers/net/wireless/ath/ath11k/hal_tx.h index cf99e2b31f9f1..5fe89b729a6e7 100644 --- a/drivers/net/wireless/ath/ath11k/hal_tx.h +++ b/drivers/net/wireless/ath/ath11k/hal_tx.h @@ -61,11 +61,9 @@ struct hal_tx_status { void ath11k_hal_tx_cmd_desc_setup(struct ath11k_base *ab, void *cmd, struct hal_tx_info *ti); -void ath11k_hal_tx_desc_sync(void *tx_desc_cached, void *hw_desc); void ath11k_hal_tx_status_parse(struct ath11k_base *ab, struct hal_wbm_release_ring *desc, struct hal_tx_status *ts); -void ath11k_hal_tx_status_desc_sync(void *hw_desc, void *local_desc); void ath11k_hal_tx_set_dscp_tid_map(struct ath11k_base *ab, int id); int ath11k_hal_reo_cmd_send(struct ath11k_base *ab, struct hal_srng *srng, enum hal_reo_cmd_type type, -- GitLab From 2ad578fdb7b544e2000cbec7bc1c2796208f3fcf Mon Sep 17 00:00:00 2001 From: John Crispin Date: Fri, 13 Dec 2019 16:38:39 +0100 Subject: [PATCH 0917/1437] ath11k: optimize ath11k_hal_tx_status_parse Moving the function into dp_tx.c allows gcc to optimize the code better and also avoid chace invalidates and context switches. Signed-off-by: John Crispin Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath11k/dp_tx.c | 40 ++++++++++++++++++++-- drivers/net/wireless/ath/ath11k/hal_tx.c | 43 ------------------------ drivers/net/wireless/ath/ath11k/hal_tx.h | 4 --- 3 files changed, 37 insertions(+), 50 deletions(-) diff --git a/drivers/net/wireless/ath/ath11k/dp_tx.c b/drivers/net/wireless/ath/ath11k/dp_tx.c index 3eff9e0468c5c..e994021e93552 100644 --- a/drivers/net/wireless/ath/ath11k/dp_tx.c +++ b/drivers/net/wireless/ath/ath11k/dp_tx.c @@ -423,6 +423,37 @@ exit: rcu_read_unlock(); } +static inline void ath11k_dp_tx_status_parse(struct ath11k_base *ab, + struct hal_wbm_release_ring *desc, + struct hal_tx_status *ts) +{ + ts->buf_rel_source = + FIELD_GET(HAL_WBM_RELEASE_INFO0_REL_SRC_MODULE, desc->info0); + if (ts->buf_rel_source != HAL_WBM_REL_SRC_MODULE_FW && + ts->buf_rel_source != HAL_WBM_REL_SRC_MODULE_TQM) + return; + + if (ts->buf_rel_source == HAL_WBM_REL_SRC_MODULE_FW) + return; + + ts->status = FIELD_GET(HAL_WBM_RELEASE_INFO0_TQM_RELEASE_REASON, + desc->info0); + ts->ppdu_id = FIELD_GET(HAL_WBM_RELEASE_INFO1_TQM_STATUS_NUMBER, + desc->info1); + ts->try_cnt = FIELD_GET(HAL_WBM_RELEASE_INFO1_TRANSMIT_COUNT, + desc->info1); + ts->ack_rssi = FIELD_GET(HAL_WBM_RELEASE_INFO2_ACK_FRAME_RSSI, + desc->info2); + if (desc->info2 & HAL_WBM_RELEASE_INFO2_FIRST_MSDU) + ts->flags |= HAL_TX_STATUS_FLAGS_FIRST_MSDU; + ts->peer_id = FIELD_GET(HAL_WBM_RELEASE_INFO3_PEER_ID, desc->info3); + ts->tid = FIELD_GET(HAL_WBM_RELEASE_INFO3_TID, desc->info3); + if (desc->rate_stats.info0 & HAL_TX_RATE_STATS_INFO0_VALID) + ts->rate_stats = desc->rate_stats.info0; + else + ts->rate_stats = 0; +} + void ath11k_dp_tx_completion_handler(struct ath11k_base *ab, int ring_id) { struct ath11k *ar; @@ -457,14 +488,17 @@ void ath11k_dp_tx_completion_handler(struct ath11k_base *ab, int ring_id) while (ATH11K_TX_COMPL_NEXT(tx_ring->tx_status_tail) != tx_ring->tx_status_head) { struct hal_wbm_release_ring *tx_status; + u32 desc_id; tx_ring->tx_status_tail = ATH11K_TX_COMPL_NEXT(tx_ring->tx_status_tail); tx_status = &tx_ring->tx_status[tx_ring->tx_status_tail]; - ath11k_hal_tx_status_parse(ab, tx_status, &ts); + ath11k_dp_tx_status_parse(ab, tx_status, &ts); - mac_id = FIELD_GET(DP_TX_DESC_ID_MAC_ID, ts.desc_id); - msdu_id = FIELD_GET(DP_TX_DESC_ID_MSDU_ID, ts.desc_id); + desc_id = FIELD_GET(BUFFER_ADDR_INFO1_SW_COOKIE, + tx_status->buf_addr_info.info1); + mac_id = FIELD_GET(DP_TX_DESC_ID_MAC_ID, desc_id); + msdu_id = FIELD_GET(DP_TX_DESC_ID_MSDU_ID, desc_id); if (ts.buf_rel_source == HAL_WBM_REL_SRC_MODULE_FW) { ath11k_dp_tx_process_htt_tx_complete(ab, diff --git a/drivers/net/wireless/ath/ath11k/hal_tx.c b/drivers/net/wireless/ath/ath11k/hal_tx.c index e8710bbbbc3a7..e4aa7e8a1284c 100644 --- a/drivers/net/wireless/ath/ath11k/hal_tx.c +++ b/drivers/net/wireless/ath/ath11k/hal_tx.c @@ -74,49 +74,6 @@ void ath11k_hal_tx_cmd_desc_setup(struct ath11k_base *ab, void *cmd, tcl_cmd->info4 = 0; } -void ath11k_hal_tx_status_parse(struct ath11k_base *ab, - struct hal_wbm_release_ring *desc, - struct hal_tx_status *ts) -{ - ts->buf_rel_source = - FIELD_GET(HAL_WBM_RELEASE_INFO0_REL_SRC_MODULE, desc->info0); - if (ts->buf_rel_source != HAL_WBM_REL_SRC_MODULE_FW && - ts->buf_rel_source != HAL_WBM_REL_SRC_MODULE_TQM) - return; - - ts->desc_id = FIELD_GET(BUFFER_ADDR_INFO1_SW_COOKIE, - desc->buf_addr_info.info1); - - if (ts->buf_rel_source == HAL_WBM_REL_SRC_MODULE_FW) - return; - - ts->status = FIELD_GET(HAL_WBM_RELEASE_INFO0_TQM_RELEASE_REASON, - desc->info0); - ts->ppdu_id = FIELD_GET(HAL_WBM_RELEASE_INFO1_TQM_STATUS_NUMBER, - desc->info1); - ts->try_cnt = FIELD_GET(HAL_WBM_RELEASE_INFO1_TRANSMIT_COUNT, - desc->info1); - - ts->ack_rssi = FIELD_GET(HAL_WBM_RELEASE_INFO2_ACK_FRAME_RSSI, - desc->info2); - if (desc->info2 & HAL_WBM_RELEASE_INFO2_FIRST_MSDU) - ts->flags |= HAL_TX_STATUS_FLAGS_FIRST_MSDU; - - if (desc->info2 & HAL_WBM_RELEASE_INFO2_LAST_MSDU) - ts->flags |= HAL_TX_STATUS_FLAGS_LAST_MSDU; - - if (desc->info2 & HAL_WBM_RELEASE_INFO2_MSDU_IN_AMSDU) - ts->flags |= HAL_TX_STATUS_FLAGS_MSDU_IN_AMSDU; - - ts->peer_id = FIELD_GET(HAL_WBM_RELEASE_INFO3_PEER_ID, desc->info3); - ts->tid = FIELD_GET(HAL_WBM_RELEASE_INFO3_TID, desc->info3); - - if (!(desc->rate_stats.info0 & HAL_TX_RATE_STATS_INFO0_VALID)) - return; - - ts->rate_stats = desc->rate_stats.info0; -} - void ath11k_hal_tx_set_dscp_tid_map(struct ath11k_base *ab, int id) { u32 ctrl_reg_val; diff --git a/drivers/net/wireless/ath/ath11k/hal_tx.h b/drivers/net/wireless/ath/ath11k/hal_tx.h index 5fe89b729a6e7..ce48a61bfb667 100644 --- a/drivers/net/wireless/ath/ath11k/hal_tx.h +++ b/drivers/net/wireless/ath/ath11k/hal_tx.h @@ -48,7 +48,6 @@ struct hal_tx_info { /* Tx status parsed from srng desc */ struct hal_tx_status { enum hal_wbm_rel_src_module buf_rel_source; - u32 desc_id; enum hal_wbm_tqm_rel_reason status; u8 ack_rssi; u32 flags; /* %HAL_TX_STATUS_FLAGS_ */ @@ -61,9 +60,6 @@ struct hal_tx_status { void ath11k_hal_tx_cmd_desc_setup(struct ath11k_base *ab, void *cmd, struct hal_tx_info *ti); -void ath11k_hal_tx_status_parse(struct ath11k_base *ab, - struct hal_wbm_release_ring *desc, - struct hal_tx_status *ts); void ath11k_hal_tx_set_dscp_tid_map(struct ath11k_base *ab, int id); int ath11k_hal_reo_cmd_send(struct ath11k_base *ab, struct hal_srng *srng, enum hal_reo_cmd_type type, -- GitLab From 485add35771b07e56833cff77e07936ba78e731c Mon Sep 17 00:00:00 2001 From: Pradeep Kumar Chitrapu Date: Fri, 13 Dec 2019 16:34:56 +0100 Subject: [PATCH 0918/1437] ath11k: fix pdev when invoking ath11k_wmi_send_twt_enable_cmd() The code currently uses the wrong pdev id when enabling TWT. Fix this by using the correct ones. Fixes: e65a616f4e74 ("ath11k: add TWT support") Signed-off-by: John Crispin Signed-off-by: Pradeep Kumar Chitrapu Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath11k/mac.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/ath/ath11k/mac.c b/drivers/net/wireless/ath/ath11k/mac.c index 3450c22cab647..b8cae42d7f7ca 100644 --- a/drivers/net/wireless/ath/ath11k/mac.c +++ b/drivers/net/wireless/ath/ath11k/mac.c @@ -1917,9 +1917,9 @@ static void ath11k_mac_op_bss_info_changed(struct ieee80211_hw *hw, if (changed & BSS_CHANGED_TWT) { if (info->twt_requester || info->twt_responder) - ath11k_wmi_send_twt_enable_cmd(ar, ar->pdev_idx); + ath11k_wmi_send_twt_enable_cmd(ar, ar->pdev->pdev_id); else - ath11k_wmi_send_twt_disable_cmd(ar, ar->pdev_idx); + ath11k_wmi_send_twt_disable_cmd(ar, ar->pdev->pdev_id); } if (changed & BSS_CHANGED_HE_OBSS_PD) -- GitLab From aacb46223e084d4e6c14b48962c4816b0baa1f0f Mon Sep 17 00:00:00 2001 From: Pradeep Kumar Chitrapu Date: Fri, 13 Dec 2019 16:34:57 +0100 Subject: [PATCH 0919/1437] ath11k: set the BA buffer size to 256 when HE is enabled This patch sets the correct BA buffer size when we are in HE mode. Without this change we are not able to receive 256 bitmapped BA frames. Signed-off-by: John Crispin Signed-off-by: Pradeep Kumar Chitrapu Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath11k/mac.c | 14 ++++++++++++++ drivers/net/wireless/ath/ath11k/wmi.h | 2 ++ 2 files changed, 16 insertions(+) diff --git a/drivers/net/wireless/ath/ath11k/mac.c b/drivers/net/wireless/ath/ath11k/mac.c index b8cae42d7f7ca..556eef9881a7a 100644 --- a/drivers/net/wireless/ath/ath11k/mac.c +++ b/drivers/net/wireless/ath/ath11k/mac.c @@ -1758,6 +1758,20 @@ static void ath11k_mac_op_bss_info_changed(struct ieee80211_hw *hw, if (ret) ath11k_warn(ar->ab, "failed to update bcn template: %d\n", ret); + + if (vif->bss_conf.he_support) { + ret = ath11k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id, + WMI_VDEV_PARAM_BA_MODE, + WMI_BA_MODE_BUFFER_SIZE_256); + if (ret) + ath11k_warn(ar->ab, + "failed to set BA BUFFER SIZE 256 for vdev: %d\n", + arvif->vdev_id); + else + ath11k_dbg(ar->ab, ATH11K_DBG_MAC, + "Set BA BUFFER SIZE 256 for VDEV: %d\n", + arvif->vdev_id); + } } if (changed & (BSS_CHANGED_BEACON_INFO | BSS_CHANGED_BEACON)) { diff --git a/drivers/net/wireless/ath/ath11k/wmi.h b/drivers/net/wireless/ath/ath11k/wmi.h index ab983aac604b4..58a19857185e7 100644 --- a/drivers/net/wireless/ath/ath11k/wmi.h +++ b/drivers/net/wireless/ath/ath11k/wmi.h @@ -54,6 +54,7 @@ struct wmi_tlv { #define WLAN_SCAN_PARAMS_MAX_BSSID 4 #define WLAN_SCAN_PARAMS_MAX_IE_LEN 256 +#define WMI_BA_MODE_BUFFER_SIZE_256 3 /* * HW mode config type replicated from FW header * @WMI_HOST_HW_MODE_SINGLE: Only one PHY is active. @@ -1003,6 +1004,7 @@ enum wmi_tlv_vdev_param { WMI_VDEV_PARAM_HE_RANGE_EXT, WMI_VDEV_PARAM_ENABLE_BCAST_PROBE_RESPONSE, WMI_VDEV_PARAM_FILS_MAX_CHANNEL_GUARD_TIME, + WMI_VDEV_PARAM_BA_MODE = 0x7e, WMI_VDEV_PARAM_SET_HE_SOUNDING_MODE = 0x87, WMI_VDEV_PARAM_PROTOTYPE = 0x8000, WMI_VDEV_PARAM_BSS_COLOR, -- GitLab From 20c3c4fd39677fd5d909b6953a21411c7d200d2f Mon Sep 17 00:00:00 2001 From: John Crispin Date: Thu, 5 Dec 2019 07:26:44 +0100 Subject: [PATCH 0920/1437] ath11k: add some missing __packed qualifiers A few of the WMI parameter structs were missing this. Signed-off-by: John Crispin Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath11k/wmi.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/ath/ath11k/wmi.h b/drivers/net/wireless/ath/ath11k/wmi.h index 58a19857185e7..eed6a1af8a2a5 100644 --- a/drivers/net/wireless/ath/ath11k/wmi.h +++ b/drivers/net/wireless/ath/ath11k/wmi.h @@ -4564,12 +4564,12 @@ struct wmi_twt_enable_params_cmd { u32 mode_check_interval; u32 add_sta_slot_interval; u32 remove_sta_slot_interval; -}; +} __packed; struct wmi_twt_disable_params_cmd { u32 tlv_header; u32 pdev_id; -}; +} __packed; struct wmi_obss_spatial_reuse_params_cmd { u32 tlv_header; @@ -4578,7 +4578,7 @@ struct wmi_obss_spatial_reuse_params_cmd { s32 obss_min; s32 obss_max; u32 vdev_id; -}; +} __packed; struct target_resource_config { u32 num_vdevs; -- GitLab From 345a4f223a7c9c6f10fb5ed7ec468ef858e9ec8a Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Wed, 11 Dec 2019 08:34:43 +0000 Subject: [PATCH 0921/1437] ath11k: fix several spelling mistakes There are several spelling mistakes in warning and debug messages, fix them. Signed-off-by: Colin Ian King Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath11k/debug.c | 4 ++-- drivers/net/wireless/ath/ath11k/debug_htt_stats.c | 2 +- drivers/net/wireless/ath/ath11k/wmi.c | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/ath/ath11k/debug.c b/drivers/net/wireless/ath/ath11k/debug.c index c27fffd13a5d8..e00b5739fb004 100644 --- a/drivers/net/wireless/ath/ath11k/debug.c +++ b/drivers/net/wireless/ath/ath11k/debug.c @@ -704,7 +704,7 @@ static ssize_t ath11k_write_extd_rx_stats(struct file *file, DP_RX_BUFFER_SIZE, &tlv_filter); if (ret) { - ath11k_warn(ar->ab, "failed to set rx filter for moniter status ring\n"); + ath11k_warn(ar->ab, "failed to set rx filter for monitor status ring\n"); goto exit; } @@ -948,7 +948,7 @@ static ssize_t ath11k_write_pktlog_filter(struct file *file, HAL_RXDMA_MONITOR_STATUS, DP_RX_BUFFER_SIZE, &tlv_filter); if (ret) { - ath11k_warn(ar->ab, "failed to set rx filter for moniter status ring\n"); + ath11k_warn(ar->ab, "failed to set rx filter for monitor status ring\n"); goto out; } diff --git a/drivers/net/wireless/ath/ath11k/debug_htt_stats.c b/drivers/net/wireless/ath/ath11k/debug_htt_stats.c index 27b301bc1a1b1..a824826f562c5 100644 --- a/drivers/net/wireless/ath/ath11k/debug_htt_stats.c +++ b/drivers/net/wireless/ath/ath11k/debug_htt_stats.c @@ -3512,7 +3512,7 @@ htt_print_rx_pdev_fw_stats_phy_err_tlv(const void *tag_buf, len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_RX_PDEV_FW_STATS_PHY_ERR_TLV:"); len += HTT_DBG_OUT(buf + len, buf_len - len, "mac_id__word = %u", htt_stats_buf->mac_id__word); - len += HTT_DBG_OUT(buf + len, buf_len - len, "tota_phy_err_nct = %u", + len += HTT_DBG_OUT(buf + len, buf_len - len, "total_phy_err_nct = %u", htt_stats_buf->total_phy_err_cnt); ARRAY_TO_STRING(phy_errs, diff --git a/drivers/net/wireless/ath/ath11k/wmi.c b/drivers/net/wireless/ath/ath11k/wmi.c index b16bfb50d73e5..473f67620b9b0 100644 --- a/drivers/net/wireless/ath/ath11k/wmi.c +++ b/drivers/net/wireless/ath/ath11k/wmi.c @@ -2574,7 +2574,7 @@ ath11k_wmi_send_twt_disable_cmd(struct ath11k *ar, u32 pdev_id) ret = ath11k_wmi_cmd_send(wmi, skb, WMI_TWT_DISABLE_CMDID); if (ret) { - ath11k_warn(ab, "Failed to send WMI_TWT_DIeABLE_CMDID"); + ath11k_warn(ab, "Failed to send WMI_TWT_DISABLE_CMDID"); dev_kfree_skb(skb); } return ret; -- GitLab From 509421acab6926a0a3edc3120d8b2a62ef2ee7a9 Mon Sep 17 00:00:00 2001 From: Nathan Chancellor Date: Wed, 11 Dec 2019 12:22:52 -0700 Subject: [PATCH 0922/1437] ath11k: Remove unnecessary enum scan_priority Clang warns: drivers/net/wireless/ath/ath11k/wmi.c:1827:23: warning: implicit conversion from enumeration type 'enum wmi_scan_priority' to different enumeration type 'enum scan_priority' [-Wenum-conversion] arg->scan_priority = WMI_SCAN_PRIORITY_LOW; ~ ^~~~~~~~~~~~~~~~~~~~~ 1 warning generated. wmi_scan_priority and scan_priority have the same values but the wmi one has WMI prefixed to the names. Since that enum is already being used, get rid of scan_priority and switch its one use to wmi_scan_priority to fix this warning. Fixes: d5c65159f289 ("ath11k: driver for Qualcomm IEEE 802.11ax devices") Link: https://github.com/ClangBuiltLinux/linux/issues/808 Signed-off-by: Nathan Chancellor Reviewed-by: Nick Desaulniers Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath11k/wmi.h | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/drivers/net/wireless/ath/ath11k/wmi.h b/drivers/net/wireless/ath/ath11k/wmi.h index eed6a1af8a2a5..5b64eb2d56efd 100644 --- a/drivers/net/wireless/ath/ath11k/wmi.h +++ b/drivers/net/wireless/ath/ath11k/wmi.h @@ -2904,15 +2904,6 @@ struct wmi_bcn_offload_ctrl_cmd { u32 bcn_ctrl_op; } __packed; -enum scan_priority { - SCAN_PRIORITY_VERY_LOW, - SCAN_PRIORITY_LOW, - SCAN_PRIORITY_MEDIUM, - SCAN_PRIORITY_HIGH, - SCAN_PRIORITY_VERY_HIGH, - SCAN_PRIORITY_COUNT, -}; - enum scan_dwelltime_adaptive_mode { SCAN_DWELL_MODE_DEFAULT = 0, SCAN_DWELL_MODE_CONSERVATIVE = 1, @@ -3064,7 +3055,7 @@ struct scan_req_params { u32 scan_req_id; u32 vdev_id; u32 pdev_id; - enum scan_priority scan_priority; + enum wmi_scan_priority scan_priority; union { struct { u32 scan_ev_started:1, -- GitLab From b5316db229d5afd7ba4cdba05b4c4b4c6307b5e7 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Thu, 12 Dec 2019 18:32:28 +0000 Subject: [PATCH 0923/1437] ath11k: fix memory leak on reg_info Currently a return path is leaking the previously allocate reg_info. Fix this by exiting via the return path mem_free. Since the return value ret is defaulted to 0, there is no need to re-assign ret to the 0 before the goto. Addresses-Coverity: ("Resource leak") Fixes: d5c65159f289 ("ath11k: driver for Qualcomm IEEE 802.11ax devices") Signed-off-by: Colin Ian King Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath11k/wmi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath11k/wmi.c b/drivers/net/wireless/ath/ath11k/wmi.c index 473f67620b9b0..0cc58d55b3085 100644 --- a/drivers/net/wireless/ath/ath11k/wmi.c +++ b/drivers/net/wireless/ath/ath11k/wmi.c @@ -4552,7 +4552,7 @@ static int ath11k_reg_chan_list_event(struct ath11k_base *ab, struct sk_buff *sk if (ab->default_regd[pdev_idx] && !ab->new_regd[pdev_idx] && !memcmp((char *)ab->default_regd[pdev_idx]->alpha2, (char *)reg_info->alpha2, 2)) - return 0; + goto mem_free; /* Intersect new rules with default regd if a new country setting was * requested, i.e a default regd was already set during initialization -- GitLab From 08cc0f44f5ed5b5a91370f99929730dc3fe94b68 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Thu, 12 Dec 2019 19:10:44 +0000 Subject: [PATCH 0924/1437] ath11k: fix uninitialized variable radioup The variable radioup is not uninitalized so it may contain a garbage value and hence the detection of a radio that is not up is buggy. Fix this by initializing it to zero. Addresses-Coverity: ("Uninitalized scalar variable") Fixes: d5c65159f289 ("ath11k: driver for Qualcomm IEEE 802.11ax devices") Signed-off-by: Colin Ian King Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath11k/debug.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath11k/debug.c b/drivers/net/wireless/ath/ath11k/debug.c index e00b5739fb004..f48daf17f2d27 100644 --- a/drivers/net/wireless/ath/ath11k/debug.c +++ b/drivers/net/wireless/ath/ath11k/debug.c @@ -541,7 +541,7 @@ static ssize_t ath11k_write_simulate_fw_crash(struct file *file, struct ath11k *ar = ab->pdevs[0].ar; char buf[32] = {0}; ssize_t rc; - int i, ret, radioup; + int i, ret, radioup = 0; for (i = 0; i < ab->num_radios; i++) { pdev = &ab->pdevs[i]; -- GitLab From 4358bcb54bb9ede559e60685b9e544e723197ca1 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Thu, 12 Dec 2019 19:42:51 +0000 Subject: [PATCH 0925/1437] ath11k: fix missing free of skb on error return path The error handling when the call to ath11k_hal_srng_get_entrysize fails leaks skb, fix this by returning via the err_free return path that will ensure the skb is free'd. Addresses-Coverity: ("Resource leak") Fixes: d5c65159f289 ("ath11k: driver for Qualcomm IEEE 802.11ax devices") Signed-off-by: Colin Ian King Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath11k/dp_tx.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/ath/ath11k/dp_tx.c b/drivers/net/wireless/ath/ath11k/dp_tx.c index e994021e93552..918305dda1061 100644 --- a/drivers/net/wireless/ath/ath11k/dp_tx.c +++ b/drivers/net/wireless/ath/ath11k/dp_tx.c @@ -669,8 +669,10 @@ int ath11k_dp_tx_htt_srng_setup(struct ath11k_base *ab, u32 ring_id, HAL_ADDR_MSB_REG_SHIFT; ret = ath11k_hal_srng_get_entrysize(ring_type); - if (ret < 0) - return -EINVAL; + if (ret < 0) { + ret = -EINVAL; + goto err_free; + } ring_entry_sz = ret; -- GitLab From cd21c827a1555276325880ea431a6a27a9186e30 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Fri, 13 Dec 2019 13:47:54 +0300 Subject: [PATCH 0926/1437] ath11k: delete a stray unlock in ath11k_dbg_htt_stats_req() The callers unlock this lock so this error path has a double unlock. Fixes: d5c65159f289 ("ath11k: driver for Qualcomm IEEE 802.11ax devices") Signed-off-by: Dan Carpenter Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath11k/debug_htt_stats.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath11k/debug_htt_stats.c b/drivers/net/wireless/ath/ath11k/debug_htt_stats.c index a824826f562c5..090fffa5e53c5 100644 --- a/drivers/net/wireless/ath/ath11k/debug_htt_stats.c +++ b/drivers/net/wireless/ath/ath11k/debug_htt_stats.c @@ -4287,7 +4287,6 @@ int ath11k_dbg_htt_stats_req(struct ath11k *ar) ret = ath11k_dp_tx_htt_h2t_ext_stats_req(ar, type, &cfg_params, cookie); if (ret) { ath11k_warn(ar->ab, "failed to send htt stats request: %d\n", ret); - mutex_unlock(&ar->conf_mutex); return ret; } -- GitLab From c76fa846b0e1a1cdf6fa1e04c0c3985411cc973d Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Fri, 13 Dec 2019 13:51:27 +0300 Subject: [PATCH 0927/1437] ath11k: checking for NULL vs IS_ERR() The ath11k_ce_alloc_ring() function returns error pointers on error, not NULL. The rest of the driver assumes that "pipe->src_ring" is either valid or NULL so this patch introduces a temporary varaible to avoid leaving it as an error pointer. Fixes: d5c65159f289 ("ath11k: driver for Qualcomm IEEE 802.11ax devices") Signed-off-by: Dan Carpenter Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath11k/ce.c | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/drivers/net/wireless/ath/ath11k/ce.c b/drivers/net/wireless/ath/ath11k/ce.c index a6572b4143033..cdd40c8fc8671 100644 --- a/drivers/net/wireless/ath/ath11k/ce.c +++ b/drivers/net/wireless/ath/ath11k/ce.c @@ -441,6 +441,7 @@ static int ath11k_ce_alloc_pipe(struct ath11k_base *ab, int ce_id) { struct ath11k_ce_pipe *pipe = &ab->ce.ce_pipe[ce_id]; const struct ce_attr *attr = &host_ce_config_wlan[ce_id]; + struct ath11k_ce_ring *ring; int nentries; int desc_sz; @@ -450,24 +451,26 @@ static int ath11k_ce_alloc_pipe(struct ath11k_base *ab, int ce_id) pipe->send_cb = ath11k_ce_send_done_cb; nentries = roundup_pow_of_two(attr->src_nentries); desc_sz = ath11k_hal_ce_get_desc_size(HAL_CE_DESC_SRC); - pipe->src_ring = ath11k_ce_alloc_ring(ab, nentries, desc_sz); - if (!pipe->src_ring) - return -ENOMEM; + ring = ath11k_ce_alloc_ring(ab, nentries, desc_sz); + if (IS_ERR(ring)) + return PTR_ERR(ring); + pipe->src_ring = ring; } if (attr->dest_nentries) { pipe->recv_cb = attr->recv_cb; nentries = roundup_pow_of_two(attr->dest_nentries); desc_sz = ath11k_hal_ce_get_desc_size(HAL_CE_DESC_DST); - pipe->dest_ring = ath11k_ce_alloc_ring(ab, nentries, desc_sz); - - if (!pipe->dest_ring) - return -ENOMEM; + ring = ath11k_ce_alloc_ring(ab, nentries, desc_sz); + if (IS_ERR(ring)) + return PTR_ERR(ring); + pipe->dest_ring = ring; desc_sz = ath11k_hal_ce_get_desc_size(HAL_CE_DESC_DST_STATUS); - pipe->status_ring = ath11k_ce_alloc_ring(ab, nentries, desc_sz); - if (!pipe->status_ring) - return -ENOMEM; + ring = ath11k_ce_alloc_ring(ab, nentries, desc_sz); + if (IS_ERR(ring)) + return PTR_ERR(ring); + pipe->status_ring = ring; } return 0; -- GitLab From 269663f18474b739fcf45389c8fea82de3332e2f Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Fri, 13 Dec 2019 13:52:19 +0300 Subject: [PATCH 0928/1437] ath11k: remove an unneeded NULL check The list iterator is always non-NULL so it doesn't need to be checked. I also removed the unnecessary initializer because the list iterator is always initialized. Signed-off-by: Dan Carpenter Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath11k/dp_rx.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/ath/ath11k/dp_rx.c b/drivers/net/wireless/ath/ath11k/dp_rx.c index 1b3b65c0038c8..3a3dc76806222 100644 --- a/drivers/net/wireless/ath/ath11k/dp_rx.c +++ b/drivers/net/wireless/ath/ath11k/dp_rx.c @@ -1225,12 +1225,12 @@ static struct htt_ppdu_stats_info *ath11k_dp_htt_get_ppdu_desc(struct ath11k *ar, u32 ppdu_id) { - struct htt_ppdu_stats_info *ppdu_info = NULL; + struct htt_ppdu_stats_info *ppdu_info; spin_lock_bh(&ar->data_lock); if (!list_empty(&ar->ppdu_stats_info)) { list_for_each_entry(ppdu_info, &ar->ppdu_stats_info, list) { - if (ppdu_info && ppdu_info->ppdu_id == ppdu_id) { + if (ppdu_info->ppdu_id == ppdu_id) { spin_unlock_bh(&ar->data_lock); return ppdu_info; } -- GitLab From d1389e19e68270601c75acc14a890264f66b80a1 Mon Sep 17 00:00:00 2001 From: zhengbin Date: Sat, 14 Dec 2019 18:22:34 +0800 Subject: [PATCH 0929/1437] ath11k: Remove unneeded semicolon Fixes coccicheck warning: drivers/net/wireless/ath/ath11k/wmi.h:2570:2-3: Unneeded semicolon Reported-by: Hulk Robot Signed-off-by: zhengbin Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath11k/wmi.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath11k/wmi.h b/drivers/net/wireless/ath/ath11k/wmi.h index 5b64eb2d56efd..780e72c2844a1 100644 --- a/drivers/net/wireless/ath/ath11k/wmi.h +++ b/drivers/net/wireless/ath/ath11k/wmi.h @@ -2569,7 +2569,7 @@ static inline const char *ath11k_wmi_phymode_str(enum wmi_phy_mode mode) /* no default handler to allow compiler to check that the * enum is fully handled */ - }; + } return ""; } -- GitLab From 0ef332951e856efa89507cdd13ba8f4fb8d4db12 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 10 Dec 2019 12:44:20 +0100 Subject: [PATCH 0930/1437] ath9k: fix storage endpoint lookup Make sure to use the current alternate setting when verifying the storage interface descriptors to avoid submitting an URB to an invalid endpoint. Failing to do so could cause the driver to misbehave or trigger a WARN() in usb_submit_urb() that kernels with panic_on_warn set would choke on. Fixes: 36bcce430657 ("ath9k_htc: Handle storage devices") Cc: stable # 2.6.39 Signed-off-by: Johan Hovold Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath9k/hif_usb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath9k/hif_usb.c b/drivers/net/wireless/ath/ath9k/hif_usb.c index fb649d85b8fc7..dd0c32379375a 100644 --- a/drivers/net/wireless/ath/ath9k/hif_usb.c +++ b/drivers/net/wireless/ath/ath9k/hif_usb.c @@ -1216,7 +1216,7 @@ err_fw: static int send_eject_command(struct usb_interface *interface) { struct usb_device *udev = interface_to_usbdev(interface); - struct usb_host_interface *iface_desc = &interface->altsetting[0]; + struct usb_host_interface *iface_desc = interface->cur_altsetting; struct usb_endpoint_descriptor *endpoint; unsigned char *cmd; u8 bulk_out_ep; -- GitLab From f998f9fcf9033b1e7f76bf4c17969b74be664dce Mon Sep 17 00:00:00 2001 From: Eduardo Abinader Date: Tue, 17 Dec 2019 15:12:47 +0100 Subject: [PATCH 0931/1437] wcn36xx: disable HW_CONNECTION_MONITOR Whenever the signal stregth decays smoothly and physical connnection is already gone and no deauth has arrived, the qcom soc is not able to indicate neither WCN36XX_HAL_MISSED_BEACON_IND nor WCN36XX_HAL_MISSED_BEACON_IND. It was noticed that such situation gets even more reproducible, when the driver fails to enter bmps mode - which is highly likely to occur. Thus, in order to provide proper disconnection of the connected STA, let mac80211 handle it, instead of wcn3xx driver. Signed-off-by: Eduardo Abinader Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/wcn36xx/main.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/net/wireless/ath/wcn36xx/main.c b/drivers/net/wireless/ath/wcn36xx/main.c index c30fdd0cbf1e7..e49c306e0eefd 100644 --- a/drivers/net/wireless/ath/wcn36xx/main.c +++ b/drivers/net/wireless/ath/wcn36xx/main.c @@ -1169,7 +1169,6 @@ static int wcn36xx_init_ieee80211(struct wcn36xx *wcn) ieee80211_hw_set(wcn->hw, TIMING_BEACON_ONLY); ieee80211_hw_set(wcn->hw, AMPDU_AGGREGATION); - ieee80211_hw_set(wcn->hw, CONNECTION_MONITOR); ieee80211_hw_set(wcn->hw, SUPPORTS_PS); ieee80211_hw_set(wcn->hw, SIGNAL_DBM); ieee80211_hw_set(wcn->hw, HAS_RATE_CONTROL); -- GitLab From c771256ee7a03d3fb3c0443319ae6249c455849d Mon Sep 17 00:00:00 2001 From: Olof Johansson Date: Wed, 18 Dec 2019 09:56:21 -0800 Subject: [PATCH 0932/1437] clk: Move clk_core_reparent_orphans() under CONFIG_OF A recent addition exposed a helper that is only used for CONFIG_OF. Move it into the CONFIG_OF zone in this file to make the compiler stop warning about an unused function. Fixes: 66d9506440bb ("clk: walk orphan list on clock provider registration") Signed-off-by: Olof Johansson Link: https://lkml.kernel.org/r/20191217082501.424892072D@mail.kernel.org [sboyd@kernel.org: "Simply" move the function instead] Signed-off-by: Stephen Boyd --- drivers/clk/clk.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index ae2795b30e060..6a11239ccde31 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -3277,13 +3277,6 @@ static void clk_core_reparent_orphans_nolock(void) } } -static void clk_core_reparent_orphans(void) -{ - clk_prepare_lock(); - clk_core_reparent_orphans_nolock(); - clk_prepare_unlock(); -} - /** * __clk_core_init - initialize the data structures in a struct clk_core * @core: clk_core being initialized @@ -4193,6 +4186,13 @@ int clk_notifier_unregister(struct clk *clk, struct notifier_block *nb) EXPORT_SYMBOL_GPL(clk_notifier_unregister); #ifdef CONFIG_OF +static void clk_core_reparent_orphans(void) +{ + clk_prepare_lock(); + clk_core_reparent_orphans_nolock(); + clk_prepare_unlock(); +} + /** * struct of_clk_provider - Clock provider registration structure * @link: Entry in global list of clock providers -- GitLab From 5cc509aa83c6acd2c5cd94f99065c39d2bd0a490 Mon Sep 17 00:00:00 2001 From: Navid Emamdoost Date: Fri, 22 Nov 2019 13:19:48 -0600 Subject: [PATCH 0933/1437] brcmfmac: Fix memory leak in brcmf_p2p_create_p2pdev() In the implementation of brcmf_p2p_create_p2pdev() the allocated memory for p2p_vif is leaked when the mac address is the same as primary interface. To fix this, go to error path to release p2p_vif via brcmf_free_vif(). Fixes: cb746e47837a ("brcmfmac: check p2pdev mac address uniqueness") Signed-off-by: Navid Emamdoost Signed-off-by: Kalle Valo --- drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c index 7ba9f6a686459..1f5deea5a288e 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c @@ -2092,7 +2092,8 @@ static struct wireless_dev *brcmf_p2p_create_p2pdev(struct brcmf_p2p_info *p2p, /* firmware requires unique mac address for p2pdev interface */ if (addr && ether_addr_equal(addr, pri_ifp->mac_addr)) { bphy_err(drvr, "discovery vif must be different from primary interface\n"); - return ERR_PTR(-EINVAL); + err = -EINVAL; + goto fail; } brcmf_p2p_generate_bss_mac(p2p, addr); -- GitLab From b8e1ada81fb71f54c5577b46e562016623f2567c Mon Sep 17 00:00:00 2001 From: Larry Finger Date: Mon, 25 Nov 2019 12:32:26 -0600 Subject: [PATCH 0934/1437] rtlwifi: rtl8192de: Remove unused GET_XXX and SET_XXX macros As the first step in converting from macros that get/set information in the RX and TX descriptors, unused macros are being removed. Signed-off-by: Larry Finger Signed-off-by: Kalle Valo --- .../wireless/realtek/rtlwifi/rtl8192de/trx.h | 329 +----------------- 1 file changed, 3 insertions(+), 326 deletions(-) diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192de/trx.h b/drivers/net/wireless/realtek/rtlwifi/rtl8192de/trx.h index 635989e152828..40746125f1ae6 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192de/trx.h +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192de/trx.h @@ -42,8 +42,6 @@ SET_BITS_OFFSET_LE(__pdesc, 0, 16, __val) #define SET_TX_DESC_OFFSET(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc, 16, 8, __val) -#define SET_TX_DESC_BMC(__pdesc, __val) \ - SET_BITS_OFFSET_LE(__pdesc, 24, 1, __val) #define SET_TX_DESC_HTC(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc, 25, 1, __val) #define SET_TX_DESC_LAST_SEG(__pdesc, __val) \ @@ -52,31 +50,9 @@ SET_BITS_OFFSET_LE(__pdesc, 27, 1, __val) #define SET_TX_DESC_LINIP(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc, 28, 1, __val) -#define SET_TX_DESC_NO_ACM(__pdesc, __val) \ - SET_BITS_OFFSET_LE(__pdesc, 29, 1, __val) -#define SET_TX_DESC_GF(__pdesc, __val) \ - SET_BITS_OFFSET_LE(__pdesc, 30, 1, __val) #define SET_TX_DESC_OWN(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc, 31, 1, __val) -#define GET_TX_DESC_PKT_SIZE(__pdesc) \ - SHIFT_AND_MASK_LE(__pdesc, 0, 16) -#define GET_TX_DESC_OFFSET(__pdesc) \ - SHIFT_AND_MASK_LE(__pdesc, 16, 8) -#define GET_TX_DESC_BMC(__pdesc) \ - SHIFT_AND_MASK_LE(__pdesc, 24, 1) -#define GET_TX_DESC_HTC(__pdesc) \ - SHIFT_AND_MASK_LE(__pdesc, 25, 1) -#define GET_TX_DESC_LAST_SEG(__pdesc) \ - SHIFT_AND_MASK_LE(__pdesc, 26, 1) -#define GET_TX_DESC_FIRST_SEG(__pdesc) \ - SHIFT_AND_MASK_LE(__pdesc, 27, 1) -#define GET_TX_DESC_LINIP(__pdesc) \ - SHIFT_AND_MASK_LE(__pdesc, 28, 1) -#define GET_TX_DESC_NO_ACM(__pdesc) \ - SHIFT_AND_MASK_LE(__pdesc, 29, 1) -#define GET_TX_DESC_GF(__pdesc) \ - SHIFT_AND_MASK_LE(__pdesc, 30, 1) #define GET_TX_DESC_OWN(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc, 31, 1) @@ -84,136 +60,35 @@ SET_BITS_OFFSET_LE(__pdesc+4, 0, 5, __val) #define SET_TX_DESC_AGG_ENABLE(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+4, 5, 1, __val) -#define SET_TX_DESC_BK(__pdesc, __val) \ - SET_BITS_OFFSET_LE(__pdesc+4, 6, 1, __val) #define SET_TX_DESC_RDG_ENABLE(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+4, 7, 1, __val) #define SET_TX_DESC_QUEUE_SEL(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+4, 8, 5, __val) -#define SET_TX_DESC_RDG_NAV_EXT(__pdesc, __val) \ - SET_BITS_OFFSET_LE(__pdesc+4, 13, 1, __val) -#define SET_TX_DESC_LSIG_TXOP_EN(__pdesc, __val) \ - SET_BITS_OFFSET_LE(__pdesc+4, 14, 1, __val) -#define SET_TX_DESC_PIFS(__pdesc, __val) \ - SET_BITS_OFFSET_LE(__pdesc+4, 15, 1, __val) #define SET_TX_DESC_RATE_ID(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+4, 16, 4, __val) -#define SET_TX_DESC_NAV_USE_HDR(__pdesc, __val) \ - SET_BITS_OFFSET_LE(__pdesc+4, 20, 1, __val) -#define SET_TX_DESC_EN_DESC_ID(__pdesc, __val) \ - SET_BITS_OFFSET_LE(__pdesc+4, 21, 1, __val) #define SET_TX_DESC_SEC_TYPE(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+4, 22, 2, __val) #define SET_TX_DESC_PKT_OFFSET(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+4, 26, 8, __val) -#define GET_TX_DESC_MACID(__pdesc) \ - SHIFT_AND_MASK_LE(__pdesc+4, 0, 5) -#define GET_TX_DESC_AGG_ENABLE(__pdesc) \ - SHIFT_AND_MASK_LE(__pdesc+4, 5, 1) -#define GET_TX_DESC_AGG_BREAK(__pdesc) \ - SHIFT_AND_MASK_LE(__pdesc+4, 6, 1) -#define GET_TX_DESC_RDG_ENABLE(__pdesc) \ - SHIFT_AND_MASK_LE(__pdesc+4, 7, 1) -#define GET_TX_DESC_QUEUE_SEL(__pdesc) \ - SHIFT_AND_MASK_LE(__pdesc+4, 8, 5) -#define GET_TX_DESC_RDG_NAV_EXT(__pdesc) \ - SHIFT_AND_MASK_LE(__pdesc+4, 13, 1) -#define GET_TX_DESC_LSIG_TXOP_EN(__pdesc) \ - SHIFT_AND_MASK_LE(__pdesc+4, 14, 1) -#define GET_TX_DESC_PIFS(__pdesc) \ - SHIFT_AND_MASK_LE(__pdesc+4, 15, 1) -#define GET_TX_DESC_RATE_ID(__pdesc) \ - SHIFT_AND_MASK_LE(__pdesc+4, 16, 4) -#define GET_TX_DESC_NAV_USE_HDR(__pdesc) \ - SHIFT_AND_MASK_LE(__pdesc+4, 20, 1) -#define GET_TX_DESC_EN_DESC_ID(__pdesc) \ - SHIFT_AND_MASK_LE(__pdesc+4, 21, 1) -#define GET_TX_DESC_SEC_TYPE(__pdesc) \ - SHIFT_AND_MASK_LE(__pdesc+4, 22, 2) -#define GET_TX_DESC_PKT_OFFSET(__pdesc) \ - SHIFT_AND_MASK_LE(__pdesc+4, 24, 8) - -#define SET_TX_DESC_RTS_RC(__pdesc, __val) \ - SET_BITS_OFFSET_LE(__pdesc+8, 0, 6, __val) -#define SET_TX_DESC_DATA_RC(__pdesc, __val) \ - SET_BITS_OFFSET_LE(__pdesc+8, 6, 6, __val) -#define SET_TX_DESC_BAR_RTY_TH(__pdesc, __val) \ - SET_BITS_OFFSET_LE(__pdesc+8, 14, 2, __val) #define SET_TX_DESC_MORE_FRAG(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+8, 17, 1, __val) -#define SET_TX_DESC_RAW(__pdesc, __val) \ - SET_BITS_OFFSET_LE(__pdesc+8, 18, 1, __val) -#define SET_TX_DESC_CCX(__pdesc, __val) \ - SET_BITS_OFFSET_LE(__pdesc+8, 19, 1, __val) #define SET_TX_DESC_AMPDU_DENSITY(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+8, 20, 3, __val) -#define SET_TX_DESC_ANTSEL_A(__pdesc, __val) \ - SET_BITS_OFFSET_LE(__pdesc+8, 24, 1, __val) -#define SET_TX_DESC_ANTSEL_B(__pdesc, __val) \ - SET_BITS_OFFSET_LE(__pdesc+8, 25, 1, __val) -#define SET_TX_DESC_TX_ANT_CCK(__pdesc, __val) \ - SET_BITS_OFFSET_LE(__pdesc+8, 26, 2, __val) -#define SET_TX_DESC_TX_ANTL(__pdesc, __val) \ - SET_BITS_OFFSET_LE(__pdesc+8, 28, 2, __val) -#define SET_TX_DESC_TX_ANT_HT(__pdesc, __val) \ - SET_BITS_OFFSET_LE(__pdesc+8, 30, 2, __val) - -#define GET_TX_DESC_RTS_RC(__pdesc) \ - SHIFT_AND_MASK_LE(__pdesc+8, 0, 6) -#define GET_TX_DESC_DATA_RC(__pdesc) \ - SHIFT_AND_MASK_LE(__pdesc+8, 6, 6) -#define GET_TX_DESC_BAR_RTY_TH(__pdesc) \ - SHIFT_AND_MASK_LE(__pdesc+8, 14, 2) -#define GET_TX_DESC_MORE_FRAG(__pdesc) \ - SHIFT_AND_MASK_LE(__pdesc+8, 17, 1) -#define GET_TX_DESC_RAW(__pdesc) \ - SHIFT_AND_MASK_LE(__pdesc+8, 18, 1) -#define GET_TX_DESC_CCX(__pdesc) \ - SHIFT_AND_MASK_LE(__pdesc+8, 19, 1) -#define GET_TX_DESC_AMPDU_DENSITY(__pdesc) \ - SHIFT_AND_MASK_LE(__pdesc+8, 20, 3) -#define GET_TX_DESC_ANTSEL_A(__pdesc) \ - SHIFT_AND_MASK_LE(__pdesc+8, 24, 1) -#define GET_TX_DESC_ANTSEL_B(__pdesc) \ - SHIFT_AND_MASK_LE(__pdesc+8, 25, 1) -#define GET_TX_DESC_TX_ANT_CCK(__pdesc) \ - SHIFT_AND_MASK_LE(__pdesc+8, 26, 2) -#define GET_TX_DESC_TX_ANTL(__pdesc) \ - SHIFT_AND_MASK_LE(__pdesc+8, 28, 2) -#define GET_TX_DESC_TX_ANT_HT(__pdesc) \ - SHIFT_AND_MASK_LE(__pdesc+8, 30, 2) - -#define SET_TX_DESC_NEXT_HEAP_PAGE(__pdesc, __val) \ - SET_BITS_OFFSET_LE(__pdesc+12, 0, 8, __val) -#define SET_TX_DESC_TAIL_PAGE(__pdesc, __val) \ - SET_BITS_OFFSET_LE(__pdesc+12, 8, 8, __val) + #define SET_TX_DESC_SEQ(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+12, 16, 12, __val) #define SET_TX_DESC_PKT_ID(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+12, 28, 4, __val) -#define GET_TX_DESC_NEXT_HEAP_PAGE(__pdesc) \ - SHIFT_AND_MASK_LE(__pdesc+12, 0, 8) -#define GET_TX_DESC_TAIL_PAGE(__pdesc) \ - SHIFT_AND_MASK_LE(__pdesc+12, 8, 8) -#define GET_TX_DESC_SEQ(__pdesc) \ - SHIFT_AND_MASK_LE(__pdesc+12, 16, 12) -#define GET_TX_DESC_PKT_ID(__pdesc) \ - SHIFT_AND_MASK_LE(__pdesc+12, 28, 4) - #define SET_TX_DESC_RTS_RATE(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+16, 0, 5, __val) -#define SET_TX_DESC_AP_DCFE(__pdesc, __val) \ - SET_BITS_OFFSET_LE(__pdesc+16, 5, 1, __val) #define SET_TX_DESC_QOS(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+16, 6, 1, __val) #define SET_TX_DESC_HWSEQ_EN(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+16, 7, 1, __val) #define SET_TX_DESC_USE_RATE(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+16, 8, 1, __val) -#define SET_TX_DESC_DISABLE_RTS_FB(__pdesc, __val) \ - SET_BITS_OFFSET_LE(__pdesc+16, 9, 1, __val) #define SET_TX_DESC_DISABLE_FB(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+16, 10, 1, __val) #define SET_TX_DESC_CTS2SELF(__pdesc, __val) \ @@ -222,18 +97,8 @@ SET_BITS_OFFSET_LE(__pdesc+16, 12, 1, __val) #define SET_TX_DESC_HW_RTS_ENABLE(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+16, 13, 1, __val) -#define SET_TX_DESC_PORT_ID(__pdesc, __val) \ - SET_BITS_OFFSET_LE(__pdesc+16, 14, 1, __val) -#define SET_TX_DESC_WAIT_DCTS(__pdesc, __val) \ - SET_BITS_OFFSET_LE(__pdesc+16, 18, 1, __val) -#define SET_TX_DESC_CTS2AP_EN(__pdesc, __val) \ - SET_BITS_OFFSET_LE(__pdesc+16, 19, 1, __val) #define SET_TX_DESC_TX_SUB_CARRIER(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+16, 20, 2, __val) -#define SET_TX_DESC_TX_STBC(__pdesc, __val) \ - SET_BITS_OFFSET_LE(__pdesc+16, 22, 2, __val) -#define SET_TX_DESC_DATA_SHORT(__pdesc, __val) \ - SET_BITS_OFFSET_LE(__pdesc+16, 24, 1, __val) #define SET_TX_DESC_DATA_BW(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+16, 25, 1, __val) #define SET_TX_DESC_RTS_SHORT(__pdesc, __val) \ @@ -245,158 +110,29 @@ #define SET_TX_DESC_RTS_STBC(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+16, 30, 2, __val) -#define GET_TX_DESC_RTS_RATE(__pdesc) \ - SHIFT_AND_MASK_LE(__pdesc+16, 0, 5) -#define GET_TX_DESC_AP_DCFE(__pdesc) \ - SHIFT_AND_MASK_LE(__pdesc+16, 5, 1) -#define GET_TX_DESC_QOS(__pdesc) \ - SHIFT_AND_MASK_LE(__pdesc+16, 6, 1) -#define GET_TX_DESC_HWSEQ_EN(__pdesc) \ - SHIFT_AND_MASK_LE(__pdesc+16, 7, 1) -#define GET_TX_DESC_USE_RATE(__pdesc) \ - SHIFT_AND_MASK_LE(__pdesc+16, 8, 1) -#define GET_TX_DESC_DISABLE_RTS_FB(__pdesc) \ - SHIFT_AND_MASK_LE(__pdesc+16, 9, 1) -#define GET_TX_DESC_DISABLE_FB(__pdesc) \ - SHIFT_AND_MASK_LE(__pdesc+16, 10, 1) -#define GET_TX_DESC_CTS2SELF(__pdesc) \ - SHIFT_AND_MASK_LE(__pdesc+16, 11, 1) -#define GET_TX_DESC_RTS_ENABLE(__pdesc) \ - SHIFT_AND_MASK_LE(__pdesc+16, 12, 1) -#define GET_TX_DESC_HW_RTS_ENABLE(__pdesc) \ - SHIFT_AND_MASK_LE(__pdesc+16, 13, 1) -#define GET_TX_DESC_PORT_ID(__pdesc) \ - SHIFT_AND_MASK_LE(__pdesc+16, 14, 1) -#define GET_TX_DESC_WAIT_DCTS(__pdesc) \ - SHIFT_AND_MASK_LE(__pdesc+16, 18, 1) -#define GET_TX_DESC_CTS2AP_EN(__pdesc) \ - SHIFT_AND_MASK_LE(__pdesc+16, 19, 1) -#define GET_TX_DESC_TX_SUB_CARRIER(__pdesc) \ - SHIFT_AND_MASK_LE(__pdesc+16, 20, 2) -#define GET_TX_DESC_TX_STBC(__pdesc) \ - SHIFT_AND_MASK_LE(__pdesc+16, 22, 2) -#define GET_TX_DESC_DATA_SHORT(__pdesc) \ - SHIFT_AND_MASK_LE(__pdesc+16, 24, 1) -#define GET_TX_DESC_DATA_BW(__pdesc) \ - SHIFT_AND_MASK_LE(__pdesc+16, 25, 1) -#define GET_TX_DESC_RTS_SHORT(__pdesc) \ - SHIFT_AND_MASK_LE(__pdesc+16, 26, 1) -#define GET_TX_DESC_RTS_BW(__pdesc) \ - SHIFT_AND_MASK_LE(__pdesc+16, 27, 1) -#define GET_TX_DESC_RTS_SC(__pdesc) \ - SHIFT_AND_MASK_LE(__pdesc+16, 28, 2) -#define GET_TX_DESC_RTS_STBC(__pdesc) \ - SHIFT_AND_MASK_LE(__pdesc+16, 30, 2) - #define SET_TX_DESC_TX_RATE(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+20, 0, 6, __val) #define SET_TX_DESC_DATA_SHORTGI(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+20, 6, 1, __val) -#define SET_TX_DESC_CCX_TAG(__pdesc, __val) \ - SET_BITS_OFFSET_LE(__pdesc+20, 7, 1, __val) #define SET_TX_DESC_DATA_RATE_FB_LIMIT(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+20, 8, 5, __val) #define SET_TX_DESC_RTS_RATE_FB_LIMIT(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+20, 13, 4, __val) -#define SET_TX_DESC_RETRY_LIMIT_ENABLE(__pdesc, __val) \ - SET_BITS_OFFSET_LE(__pdesc+20, 17, 1, __val) -#define SET_TX_DESC_DATA_RETRY_LIMIT(__pdesc, __val) \ - SET_BITS_OFFSET_LE(__pdesc+20, 18, 6, __val) -#define SET_TX_DESC_USB_TXAGG_NUM(__pdesc, __val) \ - SET_BITS_OFFSET_LE(__pdesc+20, 24, 8, __val) - -#define GET_TX_DESC_TX_RATE(__pdesc) \ - SHIFT_AND_MASK_LE(__pdesc+20, 0, 6) -#define GET_TX_DESC_DATA_SHORTGI(__pdesc) \ - SHIFT_AND_MASK_LE(__pdesc+20, 6, 1) -#define GET_TX_DESC_CCX_TAG(__pdesc) \ - SHIFT_AND_MASK_LE(__pdesc+20, 7, 1) -#define GET_TX_DESC_DATA_RATE_FB_LIMIT(__pdesc) \ - SHIFT_AND_MASK_LE(__pdesc+20, 8, 5) -#define GET_TX_DESC_RTS_RATE_FB_LIMIT(__pdesc) \ - SHIFT_AND_MASK_LE(__pdesc+20, 13, 4) -#define GET_TX_DESC_RETRY_LIMIT_ENABLE(__pdesc) \ - SHIFT_AND_MASK_LE(__pdesc+20, 17, 1) -#define GET_TX_DESC_DATA_RETRY_LIMIT(__pdesc) \ - SHIFT_AND_MASK_LE(__pdesc+20, 18, 6) -#define GET_TX_DESC_USB_TXAGG_NUM(__pdesc) \ - SHIFT_AND_MASK_LE(__pdesc+20, 24, 8) - -#define SET_TX_DESC_TXAGC_A(__pdesc, __val) \ - SET_BITS_OFFSET_LE(__pdesc+24, 0, 5, __val) -#define SET_TX_DESC_TXAGC_B(__pdesc, __val) \ - SET_BITS_OFFSET_LE(__pdesc+24, 5, 5, __val) -#define SET_TX_DESC_USE_MAX_LEN(__pdesc, __val) \ - SET_BITS_OFFSET_LE(__pdesc+24, 10, 1, __val) + #define SET_TX_DESC_MAX_AGG_NUM(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+24, 11, 5, __val) -#define SET_TX_DESC_MCSG1_MAX_LEN(__pdesc, __val) \ - SET_BITS_OFFSET_LE(__pdesc+24, 16, 4, __val) -#define SET_TX_DESC_MCSG2_MAX_LEN(__pdesc, __val) \ - SET_BITS_OFFSET_LE(__pdesc+24, 20, 4, __val) -#define SET_TX_DESC_MCSG3_MAX_LEN(__pdesc, __val) \ - SET_BITS_OFFSET_LE(__pdesc+24, 24, 4, __val) -#define SET_TX_DESC_MCS7_SGI_MAX_LEN(__pdesc, __val) \ - SET_BITS_OFFSET_LE(__pdesc+24, 28, 4, __val) - -#define GET_TX_DESC_TXAGC_A(__pdesc) \ - SHIFT_AND_MASK_LE(__pdesc+24, 0, 5) -#define GET_TX_DESC_TXAGC_B(__pdesc) \ - SHIFT_AND_MASK_LE(__pdesc+24, 5, 5) -#define GET_TX_DESC_USE_MAX_LEN(__pdesc) \ - SHIFT_AND_MASK_LE(__pdesc+24, 10, 1) -#define GET_TX_DESC_MAX_AGG_NUM(__pdesc) \ - SHIFT_AND_MASK_LE(__pdesc+24, 11, 5) -#define GET_TX_DESC_MCSG1_MAX_LEN(__pdesc) \ - SHIFT_AND_MASK_LE(__pdesc+24, 16, 4) -#define GET_TX_DESC_MCSG2_MAX_LEN(__pdesc) \ - SHIFT_AND_MASK_LE(__pdesc+24, 20, 4) -#define GET_TX_DESC_MCSG3_MAX_LEN(__pdesc) \ - SHIFT_AND_MASK_LE(__pdesc+24, 24, 4) -#define GET_TX_DESC_MCS7_SGI_MAX_LEN(__pdesc) \ - SHIFT_AND_MASK_LE(__pdesc+24, 28, 4) #define SET_TX_DESC_TX_BUFFER_SIZE(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+28, 0, 16, __val) -#define SET_TX_DESC_MCSG4_MAX_LEN(__pdesc, __val) \ - SET_BITS_OFFSET_LE(__pdesc+28, 16, 4, __val) -#define SET_TX_DESC_MCSG5_MAX_LEN(__pdesc, __val) \ - SET_BITS_OFFSET_LE(__pdesc+28, 20, 4, __val) -#define SET_TX_DESC_MCSG6_MAX_LEN(__pdesc, __val) \ - SET_BITS_OFFSET_LE(__pdesc+28, 24, 4, __val) -#define SET_TX_DESC_MCS15_SGI_MAX_LEN(__pdesc, __val) \ - SET_BITS_OFFSET_LE(__pdesc+28, 28, 4, __val) - -#define GET_TX_DESC_TX_BUFFER_SIZE(__pdesc) \ - SHIFT_AND_MASK_LE(__pdesc+28, 0, 16) -#define GET_TX_DESC_MCSG4_MAX_LEN(__pdesc) \ - SHIFT_AND_MASK_LE(__pdesc+28, 16, 4) -#define GET_TX_DESC_MCSG5_MAX_LEN(__pdesc) \ - SHIFT_AND_MASK_LE(__pdesc+28, 20, 4) -#define GET_TX_DESC_MCSG6_MAX_LEN(__pdesc) \ - SHIFT_AND_MASK_LE(__pdesc+28, 24, 4) -#define GET_TX_DESC_MCS15_SGI_MAX_LEN(__pdesc) \ - SHIFT_AND_MASK_LE(__pdesc+28, 28, 4) #define SET_TX_DESC_TX_BUFFER_ADDRESS(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+32, 0, 32, __val) -#define SET_TX_DESC_TX_BUFFER_ADDRESS64(__pdesc, __val) \ - SET_BITS_OFFSET_LE(__pdesc+36, 0, 32, __val) #define GET_TX_DESC_TX_BUFFER_ADDRESS(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+32, 0, 32) -#define GET_TX_DESC_TX_BUFFER_ADDRESS64(__pdesc) \ - SHIFT_AND_MASK_LE(__pdesc+36, 0, 32) #define SET_TX_DESC_NEXT_DESC_ADDRESS(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+40, 0, 32, __val) -#define SET_TX_DESC_NEXT_DESC_ADDRESS64(__pdesc, __val) \ - SET_BITS_OFFSET_LE(__pdesc+44, 0, 32, __val) - -#define GET_TX_DESC_NEXT_DESC_ADDRESS(__pdesc) \ - SHIFT_AND_MASK_LE(__pdesc+40, 0, 32) -#define GET_TX_DESC_NEXT_DESC_ADDRESS64(__pdesc) \ - SHIFT_AND_MASK_LE(__pdesc+44, 0, 32) #define GET_RX_DESC_PKT_LEN(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc, 0, 14) @@ -406,22 +142,12 @@ SHIFT_AND_MASK_LE(__pdesc, 15, 1) #define GET_RX_DESC_DRV_INFO_SIZE(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc, 16, 4) -#define GET_RX_DESC_SECURITY(__pdesc) \ - SHIFT_AND_MASK_LE(__pdesc, 20, 3) -#define GET_RX_DESC_QOS(__pdesc) \ - SHIFT_AND_MASK_LE(__pdesc, 23, 1) #define GET_RX_DESC_SHIFT(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc, 24, 2) #define GET_RX_DESC_PHYST(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc, 26, 1) #define GET_RX_DESC_SWDEC(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc, 27, 1) -#define GET_RX_DESC_LS(__pdesc) \ - SHIFT_AND_MASK_LE(__pdesc, 28, 1) -#define GET_RX_DESC_FS(__pdesc) \ - SHIFT_AND_MASK_LE(__pdesc, 29, 1) -#define GET_RX_DESC_EOR(__pdesc) \ - SHIFT_AND_MASK_LE(__pdesc, 30, 1) #define GET_RX_DESC_OWN(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc, 31, 1) @@ -432,44 +158,10 @@ #define SET_RX_DESC_OWN(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc, 31, 1, __val) -#define GET_RX_DESC_MACID(__pdesc) \ - SHIFT_AND_MASK_LE(__pdesc+4, 0, 5) -#define GET_RX_DESC_TID(__pdesc) \ - SHIFT_AND_MASK_LE(__pdesc+4, 5, 4) -#define GET_RX_DESC_HWRSVD(__pdesc) \ - SHIFT_AND_MASK_LE(__pdesc+4, 9, 5) #define GET_RX_DESC_PAGGR(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+4, 14, 1) #define GET_RX_DESC_FAGGR(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+4, 15, 1) -#define GET_RX_DESC_A1_FIT(__pdesc) \ - SHIFT_AND_MASK_LE(__pdesc+4, 16, 4) -#define GET_RX_DESC_A2_FIT(__pdesc) \ - SHIFT_AND_MASK_LE(__pdesc+4, 20, 4) -#define GET_RX_DESC_PAM(__pdesc) \ - SHIFT_AND_MASK_LE(__pdesc+4, 24, 1) -#define GET_RX_DESC_PWR(__pdesc) \ - SHIFT_AND_MASK_LE(__pdesc+4, 25, 1) -#define GET_RX_DESC_MD(__pdesc) \ - SHIFT_AND_MASK_LE(__pdesc+4, 26, 1) -#define GET_RX_DESC_MF(__pdesc) \ - SHIFT_AND_MASK_LE(__pdesc+4, 27, 1) -#define GET_RX_DESC_TYPE(__pdesc) \ - SHIFT_AND_MASK_LE(__pdesc+4, 28, 2) -#define GET_RX_DESC_MC(__pdesc) \ - SHIFT_AND_MASK_LE(__pdesc+4, 30, 1) -#define GET_RX_DESC_BC(__pdesc) \ - SHIFT_AND_MASK_LE(__pdesc+4, 31, 1) -#define GET_RX_DESC_SEQ(__pdesc) \ - SHIFT_AND_MASK_LE(__pdesc+8, 0, 12) -#define GET_RX_DESC_FRAG(__pdesc) \ - SHIFT_AND_MASK_LE(__pdesc+8, 12, 4) -#define GET_RX_DESC_NEXT_PKT_LEN(__pdesc) \ - SHIFT_AND_MASK_LE(__pdesc+8, 16, 14) -#define GET_RX_DESC_NEXT_IND(__pdesc) \ - SHIFT_AND_MASK_LE(__pdesc+8, 30, 1) -#define GET_RX_DESC_RSVD(__pdesc) \ - SHIFT_AND_MASK_LE(__pdesc+8, 31, 1) #define GET_RX_DESC_RXMCS(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+12, 0, 6) @@ -479,29 +171,14 @@ SHIFT_AND_MASK_LE(__pdesc+12, 8, 1) #define GET_RX_DESC_BW(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+12, 9, 1) -#define GET_RX_DESC_HTC(__pdesc) \ - SHIFT_AND_MASK_LE(__pdesc+12, 10, 1) -#define GET_RX_DESC_HWPC_ERR(__pdesc) \ - SHIFT_AND_MASK_LE(__pdesc+12, 14, 1) -#define GET_RX_DESC_HWPC_IND(__pdesc) \ - SHIFT_AND_MASK_LE(__pdesc+12, 15, 1) -#define GET_RX_DESC_IV0(__pdesc) \ - SHIFT_AND_MASK_LE(__pdesc+12, 16, 16) - -#define GET_RX_DESC_IV1(__pdesc) \ - SHIFT_AND_MASK_LE(__pdesc+16, 0, 32) + #define GET_RX_DESC_TSFL(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+20, 0, 32) #define GET_RX_DESC_BUFF_ADDR(__pdesc) \ SHIFT_AND_MASK_LE(__pdesc+24, 0, 32) -#define GET_RX_DESC_BUFF_ADDR64(__pdesc) \ - SHIFT_AND_MASK_LE(__pdesc+28, 0, 32) - #define SET_RX_DESC_BUFF_ADDR(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc+24, 0, 32, __val) -#define SET_RX_DESC_BUFF_ADDR64(__pdesc, __val) \ - SET_BITS_OFFSET_LE(__pdesc+28, 0, 32, __val) #define CLEAR_PCI_TX_DESC_CONTENT(__pdesc, _size) \ memset((void *)__pdesc, 0, \ -- GitLab From 08f6ef39bc3d40cc946dd3c3bbe50c088c498d9d Mon Sep 17 00:00:00 2001 From: Larry Finger Date: Mon, 25 Nov 2019 12:32:27 -0600 Subject: [PATCH 0935/1437] rtlwifi: rtl8192de: Replace local bit manipulation macros This driver uses a set of local macros to manipulate the RX and TX descriptors, which are all little-endian quantities. These macros are replaced by the bitfield macros le32p_replace_bits() and le32_get_bits(). In several places, the macros operated on an entire 32-bit word. In these cases, a direct read or replacement is used. Signed-off-by: Larry Finger Signed-off-by: Kalle Valo --- .../wireless/realtek/rtlwifi/rtl8192de/trx.h | 160 ++++++++---------- 1 file changed, 69 insertions(+), 91 deletions(-) diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192de/trx.h b/drivers/net/wireless/realtek/rtlwifi/rtl8192de/trx.h index 40746125f1ae6..5ff1000b24003 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192de/trx.h +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192de/trx.h @@ -14,171 +14,149 @@ #define USB_HWDESC_HEADER_LEN 32 #define CRCLENGTH 4 -/* Define a macro that takes a le32 word, converts it to host ordering, - * right shifts by a specified count, creates a mask of the specified - * bit count, and extracts that number of bits. - */ - -#define SHIFT_AND_MASK_LE(__pdesc, __shift, __mask) \ - ((le32_to_cpu(*(((__le32 *)(__pdesc)))) >> (__shift)) & \ - BIT_LEN_MASK_32(__mask)) - -/* Define a macro that clears a bit field in an le32 word and - * sets the specified value into that bit field. The resulting - * value remains in le32 ordering; however, it is properly converted - * to host ordering for the clear and set operations before conversion - * back to le32. - */ - -#define SET_BITS_OFFSET_LE(__pdesc, __shift, __len, __val) \ - (*(__le32 *)(__pdesc) = \ - (cpu_to_le32((le32_to_cpu(*((__le32 *)(__pdesc))) & \ - (~(BIT_OFFSET_LEN_MASK_32((__shift), __len)))) | \ - (((u32)(__val) & BIT_LEN_MASK_32(__len)) << (__shift))))); - /* macros to read/write various fields in RX or TX descriptors */ #define SET_TX_DESC_PKT_SIZE(__pdesc, __val) \ - SET_BITS_OFFSET_LE(__pdesc, 0, 16, __val) + le32p_replace_bits((__le32 *)__pdesc, __val, GENMASK(15, 0)) #define SET_TX_DESC_OFFSET(__pdesc, __val) \ - SET_BITS_OFFSET_LE(__pdesc, 16, 8, __val) + le32p_replace_bits((__le32 *)__pdesc, __val, GENMASK(23, 16)) #define SET_TX_DESC_HTC(__pdesc, __val) \ - SET_BITS_OFFSET_LE(__pdesc, 25, 1, __val) + le32p_replace_bits((__le32 *)__pdesc, __val, BIT(25)) #define SET_TX_DESC_LAST_SEG(__pdesc, __val) \ - SET_BITS_OFFSET_LE(__pdesc, 26, 1, __val) + le32p_replace_bits((__le32 *)__pdesc, __val, BIT(26)) #define SET_TX_DESC_FIRST_SEG(__pdesc, __val) \ - SET_BITS_OFFSET_LE(__pdesc, 27, 1, __val) + le32p_replace_bits((__le32 *)__pdesc, __val, BIT(27)) #define SET_TX_DESC_LINIP(__pdesc, __val) \ - SET_BITS_OFFSET_LE(__pdesc, 28, 1, __val) + le32p_replace_bits((__le32 *)__pdesc, __val, BIT(28)) #define SET_TX_DESC_OWN(__pdesc, __val) \ - SET_BITS_OFFSET_LE(__pdesc, 31, 1, __val) + le32p_replace_bits((__le32 *)__pdesc, __val, BIT(31)) #define GET_TX_DESC_OWN(__pdesc) \ - SHIFT_AND_MASK_LE(__pdesc, 31, 1) + le32_get_bits(*(__le32 *)__pdesc, BIT(31)) #define SET_TX_DESC_MACID(__pdesc, __val) \ - SET_BITS_OFFSET_LE(__pdesc+4, 0, 5, __val) + le32p_replace_bits((__le32 *)(__pdesc + 4), __val, GENMASK(4, 0)) #define SET_TX_DESC_AGG_ENABLE(__pdesc, __val) \ - SET_BITS_OFFSET_LE(__pdesc+4, 5, 1, __val) + le32p_replace_bits((__le32 *)(__pdesc + 4), __val, BIT(5)) #define SET_TX_DESC_RDG_ENABLE(__pdesc, __val) \ - SET_BITS_OFFSET_LE(__pdesc+4, 7, 1, __val) + le32p_replace_bits((__le32 *)(__pdesc + 4), __val, BIT(7)) #define SET_TX_DESC_QUEUE_SEL(__pdesc, __val) \ - SET_BITS_OFFSET_LE(__pdesc+4, 8, 5, __val) + le32p_replace_bits((__le32 *)(__pdesc + 4), __val, GENMASK(12, 8)) #define SET_TX_DESC_RATE_ID(__pdesc, __val) \ - SET_BITS_OFFSET_LE(__pdesc+4, 16, 4, __val) + le32p_replace_bits((__le32 *)(__pdesc + 4), __val, GENMASK(19, 16)) #define SET_TX_DESC_SEC_TYPE(__pdesc, __val) \ - SET_BITS_OFFSET_LE(__pdesc+4, 22, 2, __val) + le32p_replace_bits((__le32 *)(__pdesc + 4), __val, GENMASK(23, 22)) #define SET_TX_DESC_PKT_OFFSET(__pdesc, __val) \ - SET_BITS_OFFSET_LE(__pdesc+4, 26, 8, __val) + le32p_replace_bits((__le32 *)(__pdesc + 4), __val, GENMASK(30, 26)) #define SET_TX_DESC_MORE_FRAG(__pdesc, __val) \ - SET_BITS_OFFSET_LE(__pdesc+8, 17, 1, __val) + le32p_replace_bits((__le32 *)(__pdesc + 8), __val, BIT(17)) #define SET_TX_DESC_AMPDU_DENSITY(__pdesc, __val) \ - SET_BITS_OFFSET_LE(__pdesc+8, 20, 3, __val) + le32p_replace_bits((__le32 *)(__pdesc + 8), __val, GENMASK(22, 20)) #define SET_TX_DESC_SEQ(__pdesc, __val) \ - SET_BITS_OFFSET_LE(__pdesc+12, 16, 12, __val) + le32p_replace_bits((__le32 *)(__pdesc + 12), __val, GENMASK(27, 16)) #define SET_TX_DESC_PKT_ID(__pdesc, __val) \ - SET_BITS_OFFSET_LE(__pdesc+12, 28, 4, __val) + le32p_replace_bits((__le32 *)(__pdesc + 12), __val, GENMASK(31, 28)) #define SET_TX_DESC_RTS_RATE(__pdesc, __val) \ - SET_BITS_OFFSET_LE(__pdesc+16, 0, 5, __val) + le32p_replace_bits((__le32 *)(__pdesc + 16), __val, GENMASK(4, 0)) #define SET_TX_DESC_QOS(__pdesc, __val) \ - SET_BITS_OFFSET_LE(__pdesc+16, 6, 1, __val) + le32p_replace_bits((__le32 *)(__pdesc + 16), __val, BIT(6)) #define SET_TX_DESC_HWSEQ_EN(__pdesc, __val) \ - SET_BITS_OFFSET_LE(__pdesc+16, 7, 1, __val) + le32p_replace_bits((__le32 *)(__pdesc + 16), __val, BIT(7)) #define SET_TX_DESC_USE_RATE(__pdesc, __val) \ - SET_BITS_OFFSET_LE(__pdesc+16, 8, 1, __val) + le32p_replace_bits((__le32 *)(__pdesc + 16), __val, BIT(8)) #define SET_TX_DESC_DISABLE_FB(__pdesc, __val) \ - SET_BITS_OFFSET_LE(__pdesc+16, 10, 1, __val) + le32p_replace_bits((__le32 *)(__pdesc + 16), __val, BIT(10)) #define SET_TX_DESC_CTS2SELF(__pdesc, __val) \ - SET_BITS_OFFSET_LE(__pdesc+16, 11, 1, __val) + le32p_replace_bits((__le32 *)(__pdesc + 16), __val, BIT(11)) #define SET_TX_DESC_RTS_ENABLE(__pdesc, __val) \ - SET_BITS_OFFSET_LE(__pdesc+16, 12, 1, __val) + le32p_replace_bits((__le32 *)(__pdesc + 16), __val, BIT(12)) #define SET_TX_DESC_HW_RTS_ENABLE(__pdesc, __val) \ - SET_BITS_OFFSET_LE(__pdesc+16, 13, 1, __val) + le32p_replace_bits((__le32 *)(__pdesc + 16), __val, BIT(13)) #define SET_TX_DESC_TX_SUB_CARRIER(__pdesc, __val) \ - SET_BITS_OFFSET_LE(__pdesc+16, 20, 2, __val) + le32p_replace_bits((__le32 *)(__pdesc + 16), __val, GENMASK(21, 20)) #define SET_TX_DESC_DATA_BW(__pdesc, __val) \ - SET_BITS_OFFSET_LE(__pdesc+16, 25, 1, __val) + le32p_replace_bits((__le32 *)(__pdesc + 16), __val, BIT(25)) #define SET_TX_DESC_RTS_SHORT(__pdesc, __val) \ - SET_BITS_OFFSET_LE(__pdesc+16, 26, 1, __val) + le32p_replace_bits((__le32 *)(__pdesc + 16), __val, BIT(26)) #define SET_TX_DESC_RTS_BW(__pdesc, __val) \ - SET_BITS_OFFSET_LE(__pdesc+16, 27, 1, __val) + le32p_replace_bits((__le32 *)(__pdesc + 16), __val, BIT(27)) #define SET_TX_DESC_RTS_SC(__pdesc, __val) \ - SET_BITS_OFFSET_LE(__pdesc+16, 28, 2, __val) + le32p_replace_bits((__le32 *)(__pdesc + 16), __val, GENMASK(29, 28)) #define SET_TX_DESC_RTS_STBC(__pdesc, __val) \ - SET_BITS_OFFSET_LE(__pdesc+16, 30, 2, __val) + le32p_replace_bits((__le32 *)(__pdesc + 16), __val, GENMASK(31, 30)) #define SET_TX_DESC_TX_RATE(__pdesc, __val) \ - SET_BITS_OFFSET_LE(__pdesc+20, 0, 6, __val) + le32p_replace_bits((__le32 *)(__pdesc + 20), __val, GENMASK(5, 0)) #define SET_TX_DESC_DATA_SHORTGI(__pdesc, __val) \ - SET_BITS_OFFSET_LE(__pdesc+20, 6, 1, __val) + le32p_replace_bits((__le32 *)(__pdesc + 20), __val, BIT(6)) #define SET_TX_DESC_DATA_RATE_FB_LIMIT(__pdesc, __val) \ - SET_BITS_OFFSET_LE(__pdesc+20, 8, 5, __val) + le32p_replace_bits((__le32 *)(__pdesc + 20), __val, GENMASK(12, 8)) #define SET_TX_DESC_RTS_RATE_FB_LIMIT(__pdesc, __val) \ - SET_BITS_OFFSET_LE(__pdesc+20, 13, 4, __val) + le32p_replace_bits((__le32 *)(__pdesc + 20), __val, GENMASK(16, 13)) #define SET_TX_DESC_MAX_AGG_NUM(__pdesc, __val) \ - SET_BITS_OFFSET_LE(__pdesc+24, 11, 5, __val) + le32p_replace_bits((__le32 *)(__pdesc + 24), __val, GENMASK(15, 11)) #define SET_TX_DESC_TX_BUFFER_SIZE(__pdesc, __val) \ - SET_BITS_OFFSET_LE(__pdesc+28, 0, 16, __val) + le32p_replace_bits((__le32 *)(__pdesc + 28), __val, GENMASK(15, 0)) #define SET_TX_DESC_TX_BUFFER_ADDRESS(__pdesc, __val) \ - SET_BITS_OFFSET_LE(__pdesc+32, 0, 32, __val) + *(__le32 *)(__pdesc + 32) = cpu_to_le32(__val) #define GET_TX_DESC_TX_BUFFER_ADDRESS(__pdesc) \ - SHIFT_AND_MASK_LE(__pdesc+32, 0, 32) + le32_to_cpu(*(__le32 *)(__pdesc + 32)) #define SET_TX_DESC_NEXT_DESC_ADDRESS(__pdesc, __val) \ - SET_BITS_OFFSET_LE(__pdesc+40, 0, 32, __val) + *(__le32 *)(__pdesc + 40) = cpu_to_le32(__val) #define GET_RX_DESC_PKT_LEN(__pdesc) \ - SHIFT_AND_MASK_LE(__pdesc, 0, 14) + le32_get_bits(*(__le32 *)__pdesc, GENMASK(13, 0)) #define GET_RX_DESC_CRC32(__pdesc) \ - SHIFT_AND_MASK_LE(__pdesc, 14, 1) + le32_get_bits(*(__le32 *)__pdesc, BIT(14)) #define GET_RX_DESC_ICV(__pdesc) \ - SHIFT_AND_MASK_LE(__pdesc, 15, 1) + le32_get_bits(*(__le32 *)__pdesc, BIT(15)) #define GET_RX_DESC_DRV_INFO_SIZE(__pdesc) \ - SHIFT_AND_MASK_LE(__pdesc, 16, 4) + le32_get_bits(*(__le32 *)__pdesc, GENMASK(19, 16)) #define GET_RX_DESC_SHIFT(__pdesc) \ - SHIFT_AND_MASK_LE(__pdesc, 24, 2) + le32_get_bits(*(__le32 *)__pdesc, GENMASK(25, 24)) #define GET_RX_DESC_PHYST(__pdesc) \ - SHIFT_AND_MASK_LE(__pdesc, 26, 1) + le32_get_bits(*(__le32 *)__pdesc, BIT(26)) #define GET_RX_DESC_SWDEC(__pdesc) \ - SHIFT_AND_MASK_LE(__pdesc, 27, 1) + le32_get_bits(*(__le32 *)__pdesc, BIT(27)) #define GET_RX_DESC_OWN(__pdesc) \ - SHIFT_AND_MASK_LE(__pdesc, 31, 1) + le32_get_bits(*(__le32 *)__pdesc, BIT(31)) #define SET_RX_DESC_PKT_LEN(__pdesc, __val) \ - SET_BITS_OFFSET_LE(__pdesc, 0, 14, __val) + le32p_replace_bits((__le32 *)__pdesc, __val, GENMASK(13, 0)) #define SET_RX_DESC_EOR(__pdesc, __val) \ - SET_BITS_OFFSET_LE(__pdesc, 30, 1, __val) + le32p_replace_bits((__le32 *)__pdesc, __val, BIT(30)) #define SET_RX_DESC_OWN(__pdesc, __val) \ - SET_BITS_OFFSET_LE(__pdesc, 31, 1, __val) + le32p_replace_bits((__le32 *)__pdesc, __val, BIT(31)) #define GET_RX_DESC_PAGGR(__pdesc) \ - SHIFT_AND_MASK_LE(__pdesc+4, 14, 1) + le32_get_bits(*(__le32 *)(__pdesc + 4), BIT(14)) #define GET_RX_DESC_FAGGR(__pdesc) \ - SHIFT_AND_MASK_LE(__pdesc+4, 15, 1) + le32_get_bits(*(__le32 *)(__pdesc + 4), BIT(15)) #define GET_RX_DESC_RXMCS(__pdesc) \ - SHIFT_AND_MASK_LE(__pdesc+12, 0, 6) + le32_get_bits(*(__le32 *)(__pdesc + 12), GENMASK(5, 0)) #define GET_RX_DESC_RXHT(__pdesc) \ - SHIFT_AND_MASK_LE(__pdesc+12, 6, 1) + le32_get_bits(*(__le32 *)(__pdesc + 12), BIT(6)) #define GET_RX_DESC_SPLCP(__pdesc) \ - SHIFT_AND_MASK_LE(__pdesc+12, 8, 1) + le32_get_bits(*(__le32 *)(__pdesc + 12), BIT(8)) #define GET_RX_DESC_BW(__pdesc) \ - SHIFT_AND_MASK_LE(__pdesc+12, 9, 1) + le32_get_bits(*(__le32 *)(__pdesc + 12), BIT(9)) #define GET_RX_DESC_TSFL(__pdesc) \ - SHIFT_AND_MASK_LE(__pdesc+20, 0, 32) + le32_to_cpu(*(__le32 *)(__pdesc + 20)) #define GET_RX_DESC_BUFF_ADDR(__pdesc) \ - SHIFT_AND_MASK_LE(__pdesc+24, 0, 32) + le32_to_cpu(*(__le32 *)(__pdesc + 24)) #define SET_RX_DESC_BUFF_ADDR(__pdesc, __val) \ - SET_BITS_OFFSET_LE(__pdesc+24, 0, 32, __val) + *(__le32 *)(__pdesc + 24) = cpu_to_le32(__val) #define CLEAR_PCI_TX_DESC_CONTENT(__pdesc, _size) \ memset((void *)__pdesc, 0, \ @@ -186,19 +164,19 @@ /* For 92D early mode */ #define SET_EARLYMODE_PKTNUM(__paddr, __value) \ - SET_BITS_OFFSET_LE(__paddr, 0, 3, __value) + le32p_replace_bits((__le32 *)__paddr, __value, GENMASK(2, 0)) #define SET_EARLYMODE_LEN0(__paddr, __value) \ - SET_BITS_OFFSET_LE(__paddr, 4, 12, __value) + le32p_replace_bits((__le32 *)__paddr, __value, GENMASK(15, 4)) #define SET_EARLYMODE_LEN1(__paddr, __value) \ - SET_BITS_OFFSET_LE(__paddr, 16, 12, __value) + le32p_replace_bits((__le32 *)__paddr, __value, GENMASK(27, 16)) #define SET_EARLYMODE_LEN2_1(__paddr, __value) \ - SET_BITS_OFFSET_LE(__paddr, 28, 4, __value) + le32p_replace_bits((__le32 *)__paddr, __value, GENMASK(31, 28)) #define SET_EARLYMODE_LEN2_2(__paddr, __value) \ - SET_BITS_OFFSET_LE(__paddr+4, 0, 8, __value) + le32p_replace_bits((__le32 *)(__paddr + 4), __value, GENMASK(7, 0)) #define SET_EARLYMODE_LEN3(__paddr, __value) \ - SET_BITS_OFFSET_LE(__paddr+4, 8, 12, __value) + le32p_replace_bits((__le32 *)(__paddr + 4), __value, GENMASK(19, 8)) #define SET_EARLYMODE_LEN4(__paddr, __value) \ - SET_BITS_OFFSET_LE(__paddr+4, 20, 12, __value) + le32p_replace_bits((__le32 *)(__paddr + 4), __value, GENMASK(31, 20)) struct rx_fwinfo_92d { u8 gain_trsw[4]; -- GitLab From fa2b36a344f50ef6792315173567d9ce16832295 Mon Sep 17 00:00:00 2001 From: Larry Finger Date: Mon, 25 Nov 2019 12:32:28 -0600 Subject: [PATCH 0936/1437] rtlwifi: rtl8192de: Convert macros that set descriptor As a first step in the conversion, the macros that set the RX and TX descriptors are converted to static inline routines, and the names are changed from upper to lower case. To minimize the changes in a given step, the input descriptor information is left as as a byte array (u8 *), even though it should be a little-endian word array (__le32 *). That will be changed in the next patch. Several places where checkpatch.pl complains about a space after a cast and other warnings are fixed. Signed-off-by: Larry Finger Signed-off-by: Kalle Valo --- .../wireless/realtek/rtlwifi/rtl8192de/trx.c | 229 ++++---- .../wireless/realtek/rtlwifi/rtl8192de/trx.h | 498 ++++++++++++------ 2 files changed, 458 insertions(+), 269 deletions(-) diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192de/trx.c b/drivers/net/wireless/realtek/rtlwifi/rtl8192de/trx.c index 92c9fb45f8004..18361f22337dd 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192de/trx.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192de/trx.c @@ -438,49 +438,49 @@ static void _rtl92de_translate_rx_signal_stuff(struct ieee80211_hw *hw, bool rtl92de_rx_query_desc(struct ieee80211_hw *hw, struct rtl_stats *stats, struct ieee80211_rx_status *rx_status, - u8 *p_desc, struct sk_buff *skb) + u8 *pdesc, struct sk_buff *skb) { struct rx_fwinfo_92d *p_drvinfo; - struct rx_desc_92d *pdesc = (struct rx_desc_92d *)p_desc; - u32 phystatus = GET_RX_DESC_PHYST(pdesc); + u32 phystatus = get_rx_desc_physt(pdesc); - stats->length = (u16) GET_RX_DESC_PKT_LEN(pdesc); - stats->rx_drvinfo_size = (u8) GET_RX_DESC_DRV_INFO_SIZE(pdesc) * + stats->length = (u16)get_rx_desc_pkt_len(pdesc); + stats->rx_drvinfo_size = (u8)get_rx_desc_drv_info_size(pdesc) * RX_DRV_INFO_SIZE_UNIT; - stats->rx_bufshift = (u8) (GET_RX_DESC_SHIFT(pdesc) & 0x03); - stats->icv = (u16) GET_RX_DESC_ICV(pdesc); - stats->crc = (u16) GET_RX_DESC_CRC32(pdesc); + stats->rx_bufshift = (u8)(get_rx_desc_shift(pdesc) & 0x03); + stats->icv = (u16)get_rx_desc_icv(pdesc); + stats->crc = (u16)get_rx_desc_crc32(pdesc); stats->hwerror = (stats->crc | stats->icv); - stats->decrypted = !GET_RX_DESC_SWDEC(pdesc); - stats->rate = (u8) GET_RX_DESC_RXMCS(pdesc); - stats->shortpreamble = (u16) GET_RX_DESC_SPLCP(pdesc); - stats->isampdu = (bool) (GET_RX_DESC_PAGGR(pdesc) == 1); - stats->isfirst_ampdu = (bool) ((GET_RX_DESC_PAGGR(pdesc) == 1) - && (GET_RX_DESC_FAGGR(pdesc) == 1)); - stats->timestamp_low = GET_RX_DESC_TSFL(pdesc); - stats->rx_is40mhzpacket = (bool)GET_RX_DESC_BW(pdesc); - stats->is_ht = (bool)GET_RX_DESC_RXHT(pdesc); + stats->decrypted = !get_rx_desc_swdec(pdesc); + stats->rate = (u8)get_rx_desc_rxmcs(pdesc); + stats->shortpreamble = (u16)get_rx_desc_splcp(pdesc); + stats->isampdu = (bool)(get_rx_desc_paggr(pdesc) == 1); + stats->isfirst_ampdu = (bool)((get_rx_desc_paggr(pdesc) == 1) && + (get_rx_desc_faggr(pdesc) == 1)); + stats->timestamp_low = get_rx_desc_tsfl(pdesc); + stats->rx_is40mhzpacket = (bool)get_rx_desc_bw(pdesc); + stats->is_ht = (bool)get_rx_desc_rxht(pdesc); rx_status->freq = hw->conf.chandef.chan->center_freq; rx_status->band = hw->conf.chandef.chan->band; - if (GET_RX_DESC_CRC32(pdesc)) + if (get_rx_desc_crc32(pdesc)) rx_status->flag |= RX_FLAG_FAILED_FCS_CRC; - if (!GET_RX_DESC_SWDEC(pdesc)) + if (!get_rx_desc_swdec(pdesc)) rx_status->flag |= RX_FLAG_DECRYPTED; - if (GET_RX_DESC_BW(pdesc)) + if (get_rx_desc_bw(pdesc)) rx_status->bw = RATE_INFO_BW_40; - if (GET_RX_DESC_RXHT(pdesc)) + if (get_rx_desc_rxht(pdesc)) rx_status->encoding = RX_ENC_HT; rx_status->flag |= RX_FLAG_MACTIME_START; if (stats->decrypted) rx_status->flag |= RX_FLAG_DECRYPTED; rx_status->rate_idx = rtlwifi_rate_mapping(hw, stats->is_ht, false, stats->rate); - rx_status->mactime = GET_RX_DESC_TSFL(pdesc); + rx_status->mactime = get_rx_desc_tsfl(pdesc); if (phystatus) { p_drvinfo = (struct rx_fwinfo_92d *)(skb->data + stats->rx_bufshift); _rtl92de_translate_rx_signal_stuff(hw, - skb, stats, pdesc, + skb, stats, + (struct rx_desc_92d *)pdesc, p_drvinfo); } /*rx_status->qual = stats->signal; */ @@ -493,13 +493,13 @@ static void _rtl92de_insert_emcontent(struct rtl_tcb_desc *ptcb_desc, { memset(virtualaddress, 0, 8); - SET_EARLYMODE_PKTNUM(virtualaddress, ptcb_desc->empkt_num); - SET_EARLYMODE_LEN0(virtualaddress, ptcb_desc->empkt_len[0]); - SET_EARLYMODE_LEN1(virtualaddress, ptcb_desc->empkt_len[1]); - SET_EARLYMODE_LEN2_1(virtualaddress, ptcb_desc->empkt_len[2] & 0xF); - SET_EARLYMODE_LEN2_2(virtualaddress, ptcb_desc->empkt_len[2] >> 4); - SET_EARLYMODE_LEN3(virtualaddress, ptcb_desc->empkt_len[3]); - SET_EARLYMODE_LEN4(virtualaddress, ptcb_desc->empkt_len[4]); + set_earlymode_pktnum(virtualaddress, ptcb_desc->empkt_num); + set_earlymode_len0(virtualaddress, ptcb_desc->empkt_len[0]); + set_earlymode_len1(virtualaddress, ptcb_desc->empkt_len[1]); + set_earlymode_len2_1(virtualaddress, ptcb_desc->empkt_len[2] & 0xF); + set_earlymode_len2_2(virtualaddress, ptcb_desc->empkt_len[2] >> 4); + set_earlymode_len3(virtualaddress, ptcb_desc->empkt_len[3]); + set_earlymode_len4(virtualaddress, ptcb_desc->empkt_len[4]); } void rtl92de_tx_fill_desc(struct ieee80211_hw *hw, @@ -556,8 +556,8 @@ void rtl92de_tx_fill_desc(struct ieee80211_hw *hw, } if (firstseg) { if (rtlhal->earlymode_enable) { - SET_TX_DESC_PKT_OFFSET(pdesc, 1); - SET_TX_DESC_OFFSET(pdesc, USB_HWDESC_HEADER_LEN + + set_tx_desc_pkt_offset(pdesc, 1); + set_tx_desc_offset(pdesc, USB_HWDESC_HEADER_LEN + EM_HDR_LEN); if (ptcb_desc->empkt_num) { RT_TRACE(rtlpriv, COMP_SEND, DBG_LOUD, @@ -567,60 +567,61 @@ void rtl92de_tx_fill_desc(struct ieee80211_hw *hw, (u8 *)(skb->data)); } } else { - SET_TX_DESC_OFFSET(pdesc, USB_HWDESC_HEADER_LEN); + set_tx_desc_offset(pdesc, USB_HWDESC_HEADER_LEN); } /* 5G have no CCK rate */ if (rtlhal->current_bandtype == BAND_ON_5G) if (ptcb_desc->hw_rate < DESC_RATE6M) ptcb_desc->hw_rate = DESC_RATE6M; - SET_TX_DESC_TX_RATE(pdesc, ptcb_desc->hw_rate); + set_tx_desc_tx_rate(pdesc, ptcb_desc->hw_rate); if (ptcb_desc->use_shortgi || ptcb_desc->use_shortpreamble) - SET_TX_DESC_DATA_SHORTGI(pdesc, 1); + set_tx_desc_data_shortgi(pdesc, 1); if (rtlhal->macphymode == DUALMAC_DUALPHY && ptcb_desc->hw_rate == DESC_RATEMCS7) - SET_TX_DESC_DATA_SHORTGI(pdesc, 1); + set_tx_desc_data_shortgi(pdesc, 1); if (info->flags & IEEE80211_TX_CTL_AMPDU) { - SET_TX_DESC_AGG_ENABLE(pdesc, 1); - SET_TX_DESC_MAX_AGG_NUM(pdesc, 0x14); + set_tx_desc_agg_enable(pdesc, 1); + set_tx_desc_max_agg_num(pdesc, 0x14); } - SET_TX_DESC_SEQ(pdesc, seq_number); - SET_TX_DESC_RTS_ENABLE(pdesc, ((ptcb_desc->rts_enable && - !ptcb_desc->cts_enable) ? 1 : 0)); - SET_TX_DESC_HW_RTS_ENABLE(pdesc, ((ptcb_desc->rts_enable + set_tx_desc_seq(pdesc, seq_number); + set_tx_desc_rts_enable(pdesc, + ((ptcb_desc->rts_enable && + !ptcb_desc->cts_enable) ? 1 : 0)); + set_tx_desc_hw_rts_enable(pdesc, ((ptcb_desc->rts_enable || ptcb_desc->cts_enable) ? 1 : 0)); - SET_TX_DESC_CTS2SELF(pdesc, ((ptcb_desc->cts_enable) ? 1 : 0)); - SET_TX_DESC_RTS_STBC(pdesc, ((ptcb_desc->rts_stbc) ? 1 : 0)); + set_tx_desc_cts2self(pdesc, ((ptcb_desc->cts_enable) ? 1 : 0)); + set_tx_desc_rts_stbc(pdesc, ((ptcb_desc->rts_stbc) ? 1 : 0)); /* 5G have no CCK rate */ if (rtlhal->current_bandtype == BAND_ON_5G) if (ptcb_desc->rts_rate < DESC_RATE6M) ptcb_desc->rts_rate = DESC_RATE6M; - SET_TX_DESC_RTS_RATE(pdesc, ptcb_desc->rts_rate); - SET_TX_DESC_RTS_BW(pdesc, 0); - SET_TX_DESC_RTS_SC(pdesc, ptcb_desc->rts_sc); - SET_TX_DESC_RTS_SHORT(pdesc, ((ptcb_desc->rts_rate <= + set_tx_desc_rts_rate(pdesc, ptcb_desc->rts_rate); + set_tx_desc_rts_bw(pdesc, 0); + set_tx_desc_rts_sc(pdesc, ptcb_desc->rts_sc); + set_tx_desc_rts_short(pdesc, ((ptcb_desc->rts_rate <= DESC_RATE54M) ? (ptcb_desc->rts_use_shortpreamble ? 1 : 0) : (ptcb_desc->rts_use_shortgi ? 1 : 0))); if (bw_40) { if (ptcb_desc->packet_bw) { - SET_TX_DESC_DATA_BW(pdesc, 1); - SET_TX_DESC_TX_SUB_CARRIER(pdesc, 3); + set_tx_desc_data_bw(pdesc, 1); + set_tx_desc_tx_sub_carrier(pdesc, 3); } else { - SET_TX_DESC_DATA_BW(pdesc, 0); - SET_TX_DESC_TX_SUB_CARRIER(pdesc, + set_tx_desc_data_bw(pdesc, 0); + set_tx_desc_tx_sub_carrier(pdesc, mac->cur_40_prime_sc); } } else { - SET_TX_DESC_DATA_BW(pdesc, 0); - SET_TX_DESC_TX_SUB_CARRIER(pdesc, 0); + set_tx_desc_data_bw(pdesc, 0); + set_tx_desc_tx_sub_carrier(pdesc, 0); } - SET_TX_DESC_LINIP(pdesc, 0); - SET_TX_DESC_PKT_SIZE(pdesc, (u16) skb_len); + set_tx_desc_linip(pdesc, 0); + set_tx_desc_pkt_size(pdesc, (u16)skb_len); if (sta) { u8 ampdu_density = sta->ht_cap.ampdu_density; - SET_TX_DESC_AMPDU_DENSITY(pdesc, ampdu_density); + set_tx_desc_ampdu_density(pdesc, ampdu_density); } if (info->control.hw_key) { struct ieee80211_key_conf *keyconf; @@ -630,61 +631,61 @@ void rtl92de_tx_fill_desc(struct ieee80211_hw *hw, case WLAN_CIPHER_SUITE_WEP40: case WLAN_CIPHER_SUITE_WEP104: case WLAN_CIPHER_SUITE_TKIP: - SET_TX_DESC_SEC_TYPE(pdesc, 0x1); + set_tx_desc_sec_type(pdesc, 0x1); break; case WLAN_CIPHER_SUITE_CCMP: - SET_TX_DESC_SEC_TYPE(pdesc, 0x3); + set_tx_desc_sec_type(pdesc, 0x3); break; default: - SET_TX_DESC_SEC_TYPE(pdesc, 0x0); + set_tx_desc_sec_type(pdesc, 0x0); break; } } - SET_TX_DESC_PKT_ID(pdesc, 0); - SET_TX_DESC_QUEUE_SEL(pdesc, fw_qsel); - SET_TX_DESC_DATA_RATE_FB_LIMIT(pdesc, 0x1F); - SET_TX_DESC_RTS_RATE_FB_LIMIT(pdesc, 0xF); - SET_TX_DESC_DISABLE_FB(pdesc, ptcb_desc->disable_ratefallback ? + set_tx_desc_pkt_id(pdesc, 0); + set_tx_desc_queue_sel(pdesc, fw_qsel); + set_tx_desc_data_rate_fb_limit(pdesc, 0x1F); + set_tx_desc_rts_rate_fb_limit(pdesc, 0xF); + set_tx_desc_disable_fb(pdesc, ptcb_desc->disable_ratefallback ? 1 : 0); - SET_TX_DESC_USE_RATE(pdesc, ptcb_desc->use_driver_rate ? 1 : 0); + set_tx_desc_use_rate(pdesc, ptcb_desc->use_driver_rate ? 1 : 0); /* Set TxRate and RTSRate in TxDesc */ /* This prevent Tx initial rate of new-coming packets */ /* from being overwritten by retried packet rate.*/ if (!ptcb_desc->use_driver_rate) { - SET_TX_DESC_RTS_RATE(pdesc, 0x08); - /* SET_TX_DESC_TX_RATE(pdesc, 0x0b); */ + set_tx_desc_rts_rate(pdesc, 0x08); + /* set_tx_desc_tx_rate(pdesc, 0x0b); */ } if (ieee80211_is_data_qos(fc)) { if (mac->rdg_en) { RT_TRACE(rtlpriv, COMP_SEND, DBG_TRACE, "Enable RDG function\n"); - SET_TX_DESC_RDG_ENABLE(pdesc, 1); - SET_TX_DESC_HTC(pdesc, 1); + set_tx_desc_rdg_enable(pdesc, 1); + set_tx_desc_htc(pdesc, 1); } } } - SET_TX_DESC_FIRST_SEG(pdesc, (firstseg ? 1 : 0)); - SET_TX_DESC_LAST_SEG(pdesc, (lastseg ? 1 : 0)); - SET_TX_DESC_TX_BUFFER_SIZE(pdesc, (u16) buf_len); - SET_TX_DESC_TX_BUFFER_ADDRESS(pdesc, mapping); + set_tx_desc_first_seg(pdesc, (firstseg ? 1 : 0)); + set_tx_desc_last_seg(pdesc, (lastseg ? 1 : 0)); + set_tx_desc_tx_buffer_size(pdesc, (u16)buf_len); + set_tx_desc_tx_buffer_address(pdesc, mapping); if (rtlpriv->dm.useramask) { - SET_TX_DESC_RATE_ID(pdesc, ptcb_desc->ratr_index); - SET_TX_DESC_MACID(pdesc, ptcb_desc->mac_id); + set_tx_desc_rate_id(pdesc, ptcb_desc->ratr_index); + set_tx_desc_macid(pdesc, ptcb_desc->mac_id); } else { - SET_TX_DESC_RATE_ID(pdesc, 0xC + ptcb_desc->ratr_index); - SET_TX_DESC_MACID(pdesc, ptcb_desc->ratr_index); + set_tx_desc_rate_id(pdesc, 0xC + ptcb_desc->ratr_index); + set_tx_desc_macid(pdesc, ptcb_desc->ratr_index); } if (ieee80211_is_data_qos(fc)) - SET_TX_DESC_QOS(pdesc, 1); + set_tx_desc_qos(pdesc, 1); if ((!ieee80211_is_data_qos(fc)) && ppsc->fwctrl_lps) { - SET_TX_DESC_HWSEQ_EN(pdesc, 1); - SET_TX_DESC_PKT_ID(pdesc, 8); + set_tx_desc_hwseq_en(pdesc, 1); + set_tx_desc_pkt_id(pdesc, 8); } - SET_TX_DESC_MORE_FRAG(pdesc, (lastseg ? 0 : 1)); + set_tx_desc_more_frag(pdesc, (lastseg ? 0 : 1)); RT_TRACE(rtlpriv, COMP_SEND, DBG_TRACE, "\n"); } @@ -709,40 +710,40 @@ void rtl92de_tx_fill_cmddesc(struct ieee80211_hw *hw, } CLEAR_PCI_TX_DESC_CONTENT(pdesc, TX_DESC_SIZE); if (firstseg) - SET_TX_DESC_OFFSET(pdesc, USB_HWDESC_HEADER_LEN); + set_tx_desc_offset(pdesc, USB_HWDESC_HEADER_LEN); /* 5G have no CCK rate * Caution: The macros below are multi-line expansions. * The braces are needed no matter what checkpatch says */ if (rtlhal->current_bandtype == BAND_ON_5G) { - SET_TX_DESC_TX_RATE(pdesc, DESC_RATE6M); + set_tx_desc_tx_rate(pdesc, DESC_RATE6M); } else { - SET_TX_DESC_TX_RATE(pdesc, DESC_RATE1M); + set_tx_desc_tx_rate(pdesc, DESC_RATE1M); } - SET_TX_DESC_SEQ(pdesc, 0); - SET_TX_DESC_LINIP(pdesc, 0); - SET_TX_DESC_QUEUE_SEL(pdesc, fw_queue); - SET_TX_DESC_FIRST_SEG(pdesc, 1); - SET_TX_DESC_LAST_SEG(pdesc, 1); - SET_TX_DESC_TX_BUFFER_SIZE(pdesc, (u16)skb->len); - SET_TX_DESC_TX_BUFFER_ADDRESS(pdesc, mapping); - SET_TX_DESC_RATE_ID(pdesc, 7); - SET_TX_DESC_MACID(pdesc, 0); - SET_TX_DESC_PKT_SIZE(pdesc, (u16) (skb->len)); - SET_TX_DESC_FIRST_SEG(pdesc, 1); - SET_TX_DESC_LAST_SEG(pdesc, 1); - SET_TX_DESC_OFFSET(pdesc, 0x20); - SET_TX_DESC_USE_RATE(pdesc, 1); + set_tx_desc_seq(pdesc, 0); + set_tx_desc_linip(pdesc, 0); + set_tx_desc_queue_sel(pdesc, fw_queue); + set_tx_desc_first_seg(pdesc, 1); + set_tx_desc_last_seg(pdesc, 1); + set_tx_desc_tx_buffer_size(pdesc, (u16)skb->len); + set_tx_desc_tx_buffer_address(pdesc, mapping); + set_tx_desc_rate_id(pdesc, 7); + set_tx_desc_macid(pdesc, 0); + set_tx_desc_pkt_size(pdesc, (u16)(skb->len)); + set_tx_desc_first_seg(pdesc, 1); + set_tx_desc_last_seg(pdesc, 1); + set_tx_desc_offset(pdesc, 0x20); + set_tx_desc_use_rate(pdesc, 1); if (!ieee80211_is_data_qos(fc) && ppsc->fwctrl_lps) { - SET_TX_DESC_HWSEQ_EN(pdesc, 1); - SET_TX_DESC_PKT_ID(pdesc, 8); + set_tx_desc_hwseq_en(pdesc, 1); + set_tx_desc_pkt_id(pdesc, 8); } RT_PRINT_DATA(rtlpriv, COMP_CMD, DBG_LOUD, "H2C Tx Cmd Content", pdesc, TX_DESC_SIZE); wmb(); - SET_TX_DESC_OWN(pdesc, 1); + set_tx_desc_own(pdesc, 1); } void rtl92de_set_desc(struct ieee80211_hw *hw, u8 *pdesc, bool istx, @@ -752,10 +753,10 @@ void rtl92de_set_desc(struct ieee80211_hw *hw, u8 *pdesc, bool istx, switch (desc_name) { case HW_DESC_OWN: wmb(); - SET_TX_DESC_OWN(pdesc, 1); + set_tx_desc_own(pdesc, 1); break; case HW_DESC_TX_NEXTDESC_ADDR: - SET_TX_DESC_NEXT_DESC_ADDRESS(pdesc, *(u32 *) val); + set_tx_desc_next_desc_address(pdesc, *(u32 *)val); break; default: WARN_ONCE(true, "rtl8192de: ERR txdesc :%d not processed\n", @@ -766,16 +767,16 @@ void rtl92de_set_desc(struct ieee80211_hw *hw, u8 *pdesc, bool istx, switch (desc_name) { case HW_DESC_RXOWN: wmb(); - SET_RX_DESC_OWN(pdesc, 1); + set_rx_desc_own(pdesc, 1); break; case HW_DESC_RXBUFF_ADDR: - SET_RX_DESC_BUFF_ADDR(pdesc, *(u32 *) val); + set_rx_desc_buff_addr(pdesc, *(u32 *)val); break; case HW_DESC_RXPKT_LEN: - SET_RX_DESC_PKT_LEN(pdesc, *(u32 *) val); + set_rx_desc_pkt_len(pdesc, *(u32 *)val); break; case HW_DESC_RXERO: - SET_RX_DESC_EOR(pdesc, 1); + set_rx_desc_eor(pdesc, 1); break; default: WARN_ONCE(true, "rtl8192de: ERR rxdesc :%d not processed\n", @@ -793,10 +794,10 @@ u64 rtl92de_get_desc(struct ieee80211_hw *hw, if (istx) { switch (desc_name) { case HW_DESC_OWN: - ret = GET_TX_DESC_OWN(p_desc); + ret = get_tx_desc_own(p_desc); break; case HW_DESC_TXBUFF_ADDR: - ret = GET_TX_DESC_TX_BUFFER_ADDRESS(p_desc); + ret = get_tx_desc_tx_buffer_address(p_desc); break; default: WARN_ONCE(true, "rtl8192de: ERR txdesc :%d not processed\n", @@ -806,13 +807,13 @@ u64 rtl92de_get_desc(struct ieee80211_hw *hw, } else { switch (desc_name) { case HW_DESC_OWN: - ret = GET_RX_DESC_OWN(p_desc); + ret = get_rx_desc_own(p_desc); break; case HW_DESC_RXPKT_LEN: - ret = GET_RX_DESC_PKT_LEN(p_desc); - break; + ret = get_rx_desc_pkt_len(p_desc); + break; case HW_DESC_RXBUFF_ADDR: - ret = GET_RX_DESC_BUFF_ADDR(p_desc); + ret = get_rx_desc_buff_addr(p_desc); break; default: WARN_ONCE(true, "rtl8192de: ERR rxdesc :%d not processed\n", diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192de/trx.h b/drivers/net/wireless/realtek/rtlwifi/rtl8192de/trx.h index 5ff1000b24003..fe81196e46b78 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192de/trx.h +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192de/trx.h @@ -16,167 +16,355 @@ /* macros to read/write various fields in RX or TX descriptors */ -#define SET_TX_DESC_PKT_SIZE(__pdesc, __val) \ - le32p_replace_bits((__le32 *)__pdesc, __val, GENMASK(15, 0)) -#define SET_TX_DESC_OFFSET(__pdesc, __val) \ - le32p_replace_bits((__le32 *)__pdesc, __val, GENMASK(23, 16)) -#define SET_TX_DESC_HTC(__pdesc, __val) \ - le32p_replace_bits((__le32 *)__pdesc, __val, BIT(25)) -#define SET_TX_DESC_LAST_SEG(__pdesc, __val) \ - le32p_replace_bits((__le32 *)__pdesc, __val, BIT(26)) -#define SET_TX_DESC_FIRST_SEG(__pdesc, __val) \ - le32p_replace_bits((__le32 *)__pdesc, __val, BIT(27)) -#define SET_TX_DESC_LINIP(__pdesc, __val) \ - le32p_replace_bits((__le32 *)__pdesc, __val, BIT(28)) -#define SET_TX_DESC_OWN(__pdesc, __val) \ - le32p_replace_bits((__le32 *)__pdesc, __val, BIT(31)) - -#define GET_TX_DESC_OWN(__pdesc) \ - le32_get_bits(*(__le32 *)__pdesc, BIT(31)) - -#define SET_TX_DESC_MACID(__pdesc, __val) \ - le32p_replace_bits((__le32 *)(__pdesc + 4), __val, GENMASK(4, 0)) -#define SET_TX_DESC_AGG_ENABLE(__pdesc, __val) \ - le32p_replace_bits((__le32 *)(__pdesc + 4), __val, BIT(5)) -#define SET_TX_DESC_RDG_ENABLE(__pdesc, __val) \ - le32p_replace_bits((__le32 *)(__pdesc + 4), __val, BIT(7)) -#define SET_TX_DESC_QUEUE_SEL(__pdesc, __val) \ - le32p_replace_bits((__le32 *)(__pdesc + 4), __val, GENMASK(12, 8)) -#define SET_TX_DESC_RATE_ID(__pdesc, __val) \ - le32p_replace_bits((__le32 *)(__pdesc + 4), __val, GENMASK(19, 16)) -#define SET_TX_DESC_SEC_TYPE(__pdesc, __val) \ - le32p_replace_bits((__le32 *)(__pdesc + 4), __val, GENMASK(23, 22)) -#define SET_TX_DESC_PKT_OFFSET(__pdesc, __val) \ - le32p_replace_bits((__le32 *)(__pdesc + 4), __val, GENMASK(30, 26)) - -#define SET_TX_DESC_MORE_FRAG(__pdesc, __val) \ - le32p_replace_bits((__le32 *)(__pdesc + 8), __val, BIT(17)) -#define SET_TX_DESC_AMPDU_DENSITY(__pdesc, __val) \ - le32p_replace_bits((__le32 *)(__pdesc + 8), __val, GENMASK(22, 20)) - -#define SET_TX_DESC_SEQ(__pdesc, __val) \ - le32p_replace_bits((__le32 *)(__pdesc + 12), __val, GENMASK(27, 16)) -#define SET_TX_DESC_PKT_ID(__pdesc, __val) \ - le32p_replace_bits((__le32 *)(__pdesc + 12), __val, GENMASK(31, 28)) - -#define SET_TX_DESC_RTS_RATE(__pdesc, __val) \ - le32p_replace_bits((__le32 *)(__pdesc + 16), __val, GENMASK(4, 0)) -#define SET_TX_DESC_QOS(__pdesc, __val) \ - le32p_replace_bits((__le32 *)(__pdesc + 16), __val, BIT(6)) -#define SET_TX_DESC_HWSEQ_EN(__pdesc, __val) \ - le32p_replace_bits((__le32 *)(__pdesc + 16), __val, BIT(7)) -#define SET_TX_DESC_USE_RATE(__pdesc, __val) \ - le32p_replace_bits((__le32 *)(__pdesc + 16), __val, BIT(8)) -#define SET_TX_DESC_DISABLE_FB(__pdesc, __val) \ - le32p_replace_bits((__le32 *)(__pdesc + 16), __val, BIT(10)) -#define SET_TX_DESC_CTS2SELF(__pdesc, __val) \ - le32p_replace_bits((__le32 *)(__pdesc + 16), __val, BIT(11)) -#define SET_TX_DESC_RTS_ENABLE(__pdesc, __val) \ - le32p_replace_bits((__le32 *)(__pdesc + 16), __val, BIT(12)) -#define SET_TX_DESC_HW_RTS_ENABLE(__pdesc, __val) \ - le32p_replace_bits((__le32 *)(__pdesc + 16), __val, BIT(13)) -#define SET_TX_DESC_TX_SUB_CARRIER(__pdesc, __val) \ - le32p_replace_bits((__le32 *)(__pdesc + 16), __val, GENMASK(21, 20)) -#define SET_TX_DESC_DATA_BW(__pdesc, __val) \ - le32p_replace_bits((__le32 *)(__pdesc + 16), __val, BIT(25)) -#define SET_TX_DESC_RTS_SHORT(__pdesc, __val) \ - le32p_replace_bits((__le32 *)(__pdesc + 16), __val, BIT(26)) -#define SET_TX_DESC_RTS_BW(__pdesc, __val) \ - le32p_replace_bits((__le32 *)(__pdesc + 16), __val, BIT(27)) -#define SET_TX_DESC_RTS_SC(__pdesc, __val) \ - le32p_replace_bits((__le32 *)(__pdesc + 16), __val, GENMASK(29, 28)) -#define SET_TX_DESC_RTS_STBC(__pdesc, __val) \ - le32p_replace_bits((__le32 *)(__pdesc + 16), __val, GENMASK(31, 30)) - -#define SET_TX_DESC_TX_RATE(__pdesc, __val) \ - le32p_replace_bits((__le32 *)(__pdesc + 20), __val, GENMASK(5, 0)) -#define SET_TX_DESC_DATA_SHORTGI(__pdesc, __val) \ - le32p_replace_bits((__le32 *)(__pdesc + 20), __val, BIT(6)) -#define SET_TX_DESC_DATA_RATE_FB_LIMIT(__pdesc, __val) \ - le32p_replace_bits((__le32 *)(__pdesc + 20), __val, GENMASK(12, 8)) -#define SET_TX_DESC_RTS_RATE_FB_LIMIT(__pdesc, __val) \ - le32p_replace_bits((__le32 *)(__pdesc + 20), __val, GENMASK(16, 13)) - -#define SET_TX_DESC_MAX_AGG_NUM(__pdesc, __val) \ - le32p_replace_bits((__le32 *)(__pdesc + 24), __val, GENMASK(15, 11)) - -#define SET_TX_DESC_TX_BUFFER_SIZE(__pdesc, __val) \ - le32p_replace_bits((__le32 *)(__pdesc + 28), __val, GENMASK(15, 0)) - -#define SET_TX_DESC_TX_BUFFER_ADDRESS(__pdesc, __val) \ - *(__le32 *)(__pdesc + 32) = cpu_to_le32(__val) - -#define GET_TX_DESC_TX_BUFFER_ADDRESS(__pdesc) \ - le32_to_cpu(*(__le32 *)(__pdesc + 32)) - -#define SET_TX_DESC_NEXT_DESC_ADDRESS(__pdesc, __val) \ - *(__le32 *)(__pdesc + 40) = cpu_to_le32(__val) - -#define GET_RX_DESC_PKT_LEN(__pdesc) \ - le32_get_bits(*(__le32 *)__pdesc, GENMASK(13, 0)) -#define GET_RX_DESC_CRC32(__pdesc) \ - le32_get_bits(*(__le32 *)__pdesc, BIT(14)) -#define GET_RX_DESC_ICV(__pdesc) \ - le32_get_bits(*(__le32 *)__pdesc, BIT(15)) -#define GET_RX_DESC_DRV_INFO_SIZE(__pdesc) \ - le32_get_bits(*(__le32 *)__pdesc, GENMASK(19, 16)) -#define GET_RX_DESC_SHIFT(__pdesc) \ - le32_get_bits(*(__le32 *)__pdesc, GENMASK(25, 24)) -#define GET_RX_DESC_PHYST(__pdesc) \ - le32_get_bits(*(__le32 *)__pdesc, BIT(26)) -#define GET_RX_DESC_SWDEC(__pdesc) \ - le32_get_bits(*(__le32 *)__pdesc, BIT(27)) -#define GET_RX_DESC_OWN(__pdesc) \ - le32_get_bits(*(__le32 *)__pdesc, BIT(31)) - -#define SET_RX_DESC_PKT_LEN(__pdesc, __val) \ - le32p_replace_bits((__le32 *)__pdesc, __val, GENMASK(13, 0)) -#define SET_RX_DESC_EOR(__pdesc, __val) \ - le32p_replace_bits((__le32 *)__pdesc, __val, BIT(30)) -#define SET_RX_DESC_OWN(__pdesc, __val) \ - le32p_replace_bits((__le32 *)__pdesc, __val, BIT(31)) - -#define GET_RX_DESC_PAGGR(__pdesc) \ - le32_get_bits(*(__le32 *)(__pdesc + 4), BIT(14)) -#define GET_RX_DESC_FAGGR(__pdesc) \ - le32_get_bits(*(__le32 *)(__pdesc + 4), BIT(15)) - -#define GET_RX_DESC_RXMCS(__pdesc) \ - le32_get_bits(*(__le32 *)(__pdesc + 12), GENMASK(5, 0)) -#define GET_RX_DESC_RXHT(__pdesc) \ - le32_get_bits(*(__le32 *)(__pdesc + 12), BIT(6)) -#define GET_RX_DESC_SPLCP(__pdesc) \ - le32_get_bits(*(__le32 *)(__pdesc + 12), BIT(8)) -#define GET_RX_DESC_BW(__pdesc) \ - le32_get_bits(*(__le32 *)(__pdesc + 12), BIT(9)) - -#define GET_RX_DESC_TSFL(__pdesc) \ - le32_to_cpu(*(__le32 *)(__pdesc + 20)) - -#define GET_RX_DESC_BUFF_ADDR(__pdesc) \ - le32_to_cpu(*(__le32 *)(__pdesc + 24)) -#define SET_RX_DESC_BUFF_ADDR(__pdesc, __val) \ - *(__le32 *)(__pdesc + 24) = cpu_to_le32(__val) +static inline void set_tx_desc_pkt_size(u8 *__pdesc, u32 __val) +{ + le32p_replace_bits((__le32 *)__pdesc, __val, GENMASK(15, 0)); +} + +static inline void set_tx_desc_offset(u8 *__pdesc, u32 __val) +{ + le32p_replace_bits((__le32 *)__pdesc, __val, GENMASK(23, 16)); +} + +static inline void set_tx_desc_htc(u8 *__pdesc, u32 __val) +{ + le32p_replace_bits((__le32 *)__pdesc, __val, BIT(25)); +} + +static inline void set_tx_desc_last_seg(u8 *__pdesc, u32 __val) +{ + le32p_replace_bits((__le32 *)__pdesc, __val, BIT(26)); +} + +static inline void set_tx_desc_first_seg(u8 *__pdesc, u32 __val) +{ + le32p_replace_bits((__le32 *)__pdesc, __val, BIT(27)); +} + +static inline void set_tx_desc_linip(u8 *__pdesc, u32 __val) +{ + le32p_replace_bits((__le32 *)__pdesc, __val, BIT(28)); +} + +static inline void set_tx_desc_own(u8 *__pdesc, u32 __val) +{ + le32p_replace_bits((__le32 *)__pdesc, __val, BIT(31)); +} + +static inline u32 get_tx_desc_own(u8 *__pdesc) +{ + return le32_get_bits(*(__le32 *)__pdesc, BIT(31)); +} + +static inline void set_tx_desc_macid(u8 *__pdesc, u32 __val) +{ + le32p_replace_bits((__le32 *)(__pdesc + 4), __val, GENMASK(4, 0)); +} + +static inline void set_tx_desc_agg_enable(u8 *__pdesc, u32 __val) +{ + le32p_replace_bits((__le32 *)(__pdesc + 4), __val, BIT(5)); +} + +static inline void set_tx_desc_rdg_enable(u8 *__pdesc, u32 __val) +{ + le32p_replace_bits((__le32 *)(__pdesc + 4), __val, BIT(7)); +} + +static inline void set_tx_desc_queue_sel(u8 *__pdesc, u32 __val) +{ + le32p_replace_bits((__le32 *)(__pdesc + 4), __val, GENMASK(12, 8)); +} + +static inline void set_tx_desc_rate_id(u8 *__pdesc, u32 __val) +{ + le32p_replace_bits((__le32 *)(__pdesc + 4), __val, GENMASK(19, 16)); +} + +static inline void set_tx_desc_sec_type(u8 *__pdesc, u32 __val) +{ + le32p_replace_bits((__le32 *)(__pdesc + 4), __val, GENMASK(23, 22)); +} + +static inline void set_tx_desc_pkt_offset(u8 *__pdesc, u32 __val) +{ + le32p_replace_bits((__le32 *)(__pdesc + 4), __val, GENMASK(30, 26)); +} + +static inline void set_tx_desc_more_frag(u8 *__pdesc, u32 __val) +{ + le32p_replace_bits((__le32 *)(__pdesc + 8), __val, BIT(17)); +} + +static inline void set_tx_desc_ampdu_density(u8 *__pdesc, u32 __val) +{ + le32p_replace_bits((__le32 *)(__pdesc + 8), __val, GENMASK(22, 20)); +} + +static inline void set_tx_desc_seq(u8 *__pdesc, u32 __val) +{ + le32p_replace_bits((__le32 *)(__pdesc + 12), __val, GENMASK(27, 16)); +} + +static inline void set_tx_desc_pkt_id(u8 *__pdesc, u32 __val) +{ + le32p_replace_bits((__le32 *)(__pdesc + 12), __val, GENMASK(31, 28)); +} + +static inline void set_tx_desc_rts_rate(u8 *__pdesc, u32 __val) +{ + le32p_replace_bits((__le32 *)(__pdesc + 16), __val, GENMASK(4, 0)); +} + +static inline void set_tx_desc_qos(u8 *__pdesc, u32 __val) +{ + le32p_replace_bits((__le32 *)(__pdesc + 16), __val, BIT(6)); +} + +static inline void set_tx_desc_hwseq_en(u8 *__pdesc, u32 __val) +{ + le32p_replace_bits((__le32 *)(__pdesc + 16), __val, BIT(7)); +} + +static inline void set_tx_desc_use_rate(u8 *__pdesc, u32 __val) +{ + le32p_replace_bits((__le32 *)(__pdesc + 16), __val, BIT(8)); +} + +static inline void set_tx_desc_disable_fb(u8 *__pdesc, u32 __val) +{ + le32p_replace_bits((__le32 *)(__pdesc + 16), __val, BIT(10)); +} + +static inline void set_tx_desc_cts2self(u8 *__pdesc, u32 __val) +{ + le32p_replace_bits((__le32 *)(__pdesc + 16), __val, BIT(11)); +} + +static inline void set_tx_desc_rts_enable(u8 *__pdesc, u32 __val) +{ + le32p_replace_bits((__le32 *)(__pdesc + 16), __val, BIT(12)); +} + +static inline void set_tx_desc_hw_rts_enable(u8 *__pdesc, u32 __val) +{ + le32p_replace_bits((__le32 *)(__pdesc + 16), __val, BIT(13)); +} + +static inline void set_tx_desc_tx_sub_carrier(u8 *__pdesc, u32 __val) +{ + le32p_replace_bits((__le32 *)(__pdesc + 16), __val, GENMASK(21, 20)); +} + +static inline void set_tx_desc_data_bw(u8 *__pdesc, u32 __val) +{ + le32p_replace_bits((__le32 *)(__pdesc + 16), __val, BIT(25)); +} + +static inline void set_tx_desc_rts_short(u8 *__pdesc, u32 __val) +{ + le32p_replace_bits((__le32 *)(__pdesc + 16), __val, BIT(26)); +} + +static inline void set_tx_desc_rts_bw(u8 *__pdesc, u32 __val) +{ + le32p_replace_bits((__le32 *)(__pdesc + 16), __val, BIT(27)); +} + +static inline void set_tx_desc_rts_sc(u8 *__pdesc, u32 __val) +{ + le32p_replace_bits((__le32 *)(__pdesc + 16), __val, GENMASK(29, 28)); +} + +static inline void set_tx_desc_rts_stbc(u8 *__pdesc, u32 __val) +{ + le32p_replace_bits((__le32 *)(__pdesc + 16), __val, GENMASK(31, 30)); +} + +static inline void set_tx_desc_tx_rate(u8 *__pdesc, u32 __val) +{ + le32p_replace_bits((__le32 *)(__pdesc + 20), __val, GENMASK(5, 0)); +} + +static inline void set_tx_desc_data_shortgi(u8 *__pdesc, u32 __val) +{ + le32p_replace_bits((__le32 *)(__pdesc + 20), __val, BIT(6)); +} + +static inline void set_tx_desc_data_rate_fb_limit(u8 *__pdesc, u32 __val) +{ + le32p_replace_bits((__le32 *)(__pdesc + 20), __val, GENMASK(12, 8)); +} + +static inline void set_tx_desc_rts_rate_fb_limit(u8 *__pdesc, u32 __val) +{ + le32p_replace_bits((__le32 *)(__pdesc + 20), __val, GENMASK(16, 13)); +} + +static inline void set_tx_desc_max_agg_num(u8 *__pdesc, u32 __val) +{ + le32p_replace_bits((__le32 *)(__pdesc + 24), __val, GENMASK(15, 11)); +} + +static inline void set_tx_desc_tx_buffer_size(u8 *__pdesc, u32 __val) +{ + le32p_replace_bits((__le32 *)(__pdesc + 28), __val, GENMASK(15, 0)); +} + +static inline void set_tx_desc_tx_buffer_address(u8 *__pdesc, u32 __val) +{ + *(__le32 *)(__pdesc + 32) = cpu_to_le32(__val); +} + +static inline u32 get_tx_desc_tx_buffer_address(u8 *__pdesc) +{ + return le32_to_cpu(*(__le32 *)(__pdesc + 32)); +} + +static inline void set_tx_desc_next_desc_address(u8 *__pdesc, u32 __val) +{ + *(__le32 *)(__pdesc + 40) = cpu_to_le32(__val); +} + +static inline u32 get_rx_desc_pkt_len(u8 *__pdesc) +{ + return le32_get_bits(*(__le32 *)__pdesc, GENMASK(13, 0)); +} + +static inline u32 get_rx_desc_crc32(u8 *__pdesc) +{ + return le32_get_bits(*(__le32 *)__pdesc, BIT(14)); +} + +static inline u32 get_rx_desc_icv(u8 *__pdesc) +{ + return le32_get_bits(*(__le32 *)__pdesc, BIT(15)); +} + +static inline u32 get_rx_desc_drv_info_size(u8 *__pdesc) +{ + return le32_get_bits(*(__le32 *)__pdesc, GENMASK(19, 16)); +} + +static inline u32 get_rx_desc_shift(u8 *__pdesc) +{ + return le32_get_bits(*(__le32 *)__pdesc, GENMASK(25, 24)); +} + +static inline u32 get_rx_desc_physt(u8 *__pdesc) +{ + return le32_get_bits(*(__le32 *)__pdesc, BIT(26)); +} + +static inline u32 get_rx_desc_swdec(u8 *__pdesc) +{ + return le32_get_bits(*(__le32 *)__pdesc, BIT(27)); +} + +static inline u32 get_rx_desc_own(u8 *__pdesc) +{ + return le32_get_bits(*(__le32 *)__pdesc, BIT(31)); +} + +static inline void set_rx_desc_pkt_len(u8 *__pdesc, u32 __val) +{ + le32p_replace_bits((__le32 *)__pdesc, __val, GENMASK(13, 0)); +} + +static inline void set_rx_desc_eor(u8 *__pdesc, u32 __val) +{ + le32p_replace_bits((__le32 *)__pdesc, __val, BIT(30)); +} + +static inline void set_rx_desc_own(u8 *__pdesc, u32 __val) +{ + le32p_replace_bits((__le32 *)__pdesc, __val, BIT(31)); +} + +static inline u32 get_rx_desc_paggr(u8 *__pdesc) +{ + return le32_get_bits(*(__le32 *)(__pdesc + 4), BIT(14)); +} + +static inline u32 get_rx_desc_faggr(u8 *__pdesc) +{ + return le32_get_bits(*(__le32 *)(__pdesc + 4), BIT(15)); +} + +static inline u32 get_rx_desc_rxmcs(u8 *__pdesc) +{ + return le32_get_bits(*(__le32 *)(__pdesc + 12), GENMASK(5, 0)); +} + +static inline u32 get_rx_desc_rxht(u8 *__pdesc) +{ + return le32_get_bits(*(__le32 *)(__pdesc + 12), BIT(6)); +} + +static inline u32 get_rx_desc_splcp(u8 *__pdesc) +{ + return le32_get_bits(*(__le32 *)(__pdesc + 12), BIT(8)); +} + +static inline u32 get_rx_desc_bw(u8 *__pdesc) +{ + return le32_get_bits(*(__le32 *)(__pdesc + 12), BIT(9)); +} + +static inline u32 get_rx_desc_tsfl(u8 *__pdesc) +{ + return le32_to_cpu(*(__le32 *)(__pdesc + 20)); +} + +static inline u32 get_rx_desc_buff_addr(u8 *__pdesc) +{ + return le32_to_cpu(*(__le32 *)(__pdesc + 24)); +} + +static inline void set_rx_desc_buff_addr(u8 *__pdesc, u32 __val) +{ + *(__le32 *)(__pdesc + 24) = cpu_to_le32(__val); +} #define CLEAR_PCI_TX_DESC_CONTENT(__pdesc, _size) \ memset((void *)__pdesc, 0, \ min_t(size_t, _size, TX_DESC_NEXT_DESC_OFFSET)) /* For 92D early mode */ -#define SET_EARLYMODE_PKTNUM(__paddr, __value) \ - le32p_replace_bits((__le32 *)__paddr, __value, GENMASK(2, 0)) -#define SET_EARLYMODE_LEN0(__paddr, __value) \ - le32p_replace_bits((__le32 *)__paddr, __value, GENMASK(15, 4)) -#define SET_EARLYMODE_LEN1(__paddr, __value) \ - le32p_replace_bits((__le32 *)__paddr, __value, GENMASK(27, 16)) -#define SET_EARLYMODE_LEN2_1(__paddr, __value) \ - le32p_replace_bits((__le32 *)__paddr, __value, GENMASK(31, 28)) -#define SET_EARLYMODE_LEN2_2(__paddr, __value) \ - le32p_replace_bits((__le32 *)(__paddr + 4), __value, GENMASK(7, 0)) -#define SET_EARLYMODE_LEN3(__paddr, __value) \ - le32p_replace_bits((__le32 *)(__paddr + 4), __value, GENMASK(19, 8)) -#define SET_EARLYMODE_LEN4(__paddr, __value) \ - le32p_replace_bits((__le32 *)(__paddr + 4), __value, GENMASK(31, 20)) +static inline void set_earlymode_pktnum(u8 *__paddr, u32 __value) +{ + le32p_replace_bits((__le32 *)__paddr, __value, GENMASK(2, 0)); +} + +static inline void set_earlymode_len0(u8 *__paddr, u32 __value) +{ + le32p_replace_bits((__le32 *)__paddr, __value, GENMASK(15, 4)); +} + +static inline void set_earlymode_len1(u8 *__paddr, u32 __value) +{ + le32p_replace_bits((__le32 *)__paddr, __value, GENMASK(27, 16)); +} + +static inline void set_earlymode_len2_1(u8 *__paddr, u32 __value) +{ + le32p_replace_bits((__le32 *)__paddr, __value, GENMASK(31, 28)); +} + +static inline void set_earlymode_len2_2(u8 *__paddr, u32 __value) +{ + le32p_replace_bits((__le32 *)(__paddr + 4), __value, GENMASK(7, 0)); +} + +static inline void set_earlymode_len3(u8 *__paddr, u32 __value) +{ + le32p_replace_bits((__le32 *)(__paddr + 4), __value, GENMASK(19, 8)); +} + +static inline void set_earlymode_len4(u8 *__paddr, u32 __value) +{ + le32p_replace_bits((__le32 *)(__paddr + 4), __value, GENMASK(31, 20)); +} struct rx_fwinfo_92d { u8 gain_trsw[4]; -- GitLab From 96c21a254053539bca12c5a750d9762ea419363d Mon Sep 17 00:00:00 2001 From: Larry Finger Date: Mon, 25 Nov 2019 12:32:29 -0600 Subject: [PATCH 0937/1437] rtlwifi: rtl8192de: Convert inline routines to little-endian words In this step, the read/write routines for the descriptors are converted to use __le32 quantities, thus a lot of casts can be removed. Callback routines still use the 8-bit arrays, but these are changed within the specified routine. Reported-by: kbuild test robot Signed-off-by: Larry Finger Signed-off-by: Kalle Valo --- .../wireless/realtek/rtlwifi/rtl8192de/trx.c | 25 +- .../wireless/realtek/rtlwifi/rtl8192de/trx.h | 284 +++++++++--------- 2 files changed, 159 insertions(+), 150 deletions(-) diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192de/trx.c b/drivers/net/wireless/realtek/rtlwifi/rtl8192de/trx.c index 18361f22337dd..bb64612654b9e 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192de/trx.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192de/trx.c @@ -438,8 +438,9 @@ static void _rtl92de_translate_rx_signal_stuff(struct ieee80211_hw *hw, bool rtl92de_rx_query_desc(struct ieee80211_hw *hw, struct rtl_stats *stats, struct ieee80211_rx_status *rx_status, - u8 *pdesc, struct sk_buff *skb) + u8 *pdesc8, struct sk_buff *skb) { + __le32 *pdesc = (__le32 *)pdesc8; struct rx_fwinfo_92d *p_drvinfo; u32 phystatus = get_rx_desc_physt(pdesc); @@ -489,8 +490,10 @@ bool rtl92de_rx_query_desc(struct ieee80211_hw *hw, struct rtl_stats *stats, } static void _rtl92de_insert_emcontent(struct rtl_tcb_desc *ptcb_desc, - u8 *virtualaddress) + u8 *virtualaddress8) { + __le32 *virtualaddress = (__le32 *)virtualaddress8; + memset(virtualaddress, 0, 8); set_earlymode_pktnum(virtualaddress, ptcb_desc->empkt_num); @@ -503,7 +506,7 @@ static void _rtl92de_insert_emcontent(struct rtl_tcb_desc *ptcb_desc, } void rtl92de_tx_fill_desc(struct ieee80211_hw *hw, - struct ieee80211_hdr *hdr, u8 *pdesc_tx, + struct ieee80211_hdr *hdr, u8 *pdesc8, u8 *pbd_desc_tx, struct ieee80211_tx_info *info, struct ieee80211_sta *sta, struct sk_buff *skb, @@ -514,7 +517,7 @@ void rtl92de_tx_fill_desc(struct ieee80211_hw *hw, struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); struct rtl_hal *rtlhal = rtl_hal(rtlpriv); struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); - u8 *pdesc = pdesc_tx; + __le32 *pdesc = (__le32 *)pdesc8; u16 seq_number; __le16 fc = hdr->frame_control; unsigned int buf_len = 0; @@ -549,7 +552,7 @@ void rtl92de_tx_fill_desc(struct ieee80211_hw *hw, "DMA mapping error\n"); return; } - CLEAR_PCI_TX_DESC_CONTENT(pdesc, sizeof(struct tx_desc_92d)); + clear_pci_tx_desc_content(pdesc, sizeof(struct tx_desc_92d)); if (ieee80211_is_nullfunc(fc) || ieee80211_is_ctl(fc)) { firstseg = true; lastseg = true; @@ -690,7 +693,7 @@ void rtl92de_tx_fill_desc(struct ieee80211_hw *hw, } void rtl92de_tx_fill_cmddesc(struct ieee80211_hw *hw, - u8 *pdesc, bool firstseg, + u8 *pdesc8, bool firstseg, bool lastseg, struct sk_buff *skb) { struct rtl_priv *rtlpriv = rtl_priv(hw); @@ -702,13 +705,14 @@ void rtl92de_tx_fill_cmddesc(struct ieee80211_hw *hw, skb->data, skb->len, PCI_DMA_TODEVICE); struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)(skb->data); __le16 fc = hdr->frame_control; + __le32 *pdesc = (__le32 *)pdesc8; if (pci_dma_mapping_error(rtlpci->pdev, mapping)) { RT_TRACE(rtlpriv, COMP_SEND, DBG_TRACE, "DMA mapping error\n"); return; } - CLEAR_PCI_TX_DESC_CONTENT(pdesc, TX_DESC_SIZE); + clear_pci_tx_desc_content(pdesc, TX_DESC_SIZE); if (firstseg) set_tx_desc_offset(pdesc, USB_HWDESC_HEADER_LEN); /* 5G have no CCK rate @@ -746,9 +750,11 @@ void rtl92de_tx_fill_cmddesc(struct ieee80211_hw *hw, set_tx_desc_own(pdesc, 1); } -void rtl92de_set_desc(struct ieee80211_hw *hw, u8 *pdesc, bool istx, +void rtl92de_set_desc(struct ieee80211_hw *hw, u8 *pdesc8, bool istx, u8 desc_name, u8 *val) { + __le32 *pdesc = (__le32 *)pdesc8; + if (istx) { switch (desc_name) { case HW_DESC_OWN: @@ -787,8 +793,9 @@ void rtl92de_set_desc(struct ieee80211_hw *hw, u8 *pdesc, bool istx, } u64 rtl92de_get_desc(struct ieee80211_hw *hw, - u8 *p_desc, bool istx, u8 desc_name) + u8 *p_desc8, bool istx, u8 desc_name) { + __le32 *p_desc = (__le32 *)p_desc8; u32 ret = 0; if (istx) { diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192de/trx.h b/drivers/net/wireless/realtek/rtlwifi/rtl8192de/trx.h index fe81196e46b78..d01578875cd5f 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192de/trx.h +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192de/trx.h @@ -16,354 +16,356 @@ /* macros to read/write various fields in RX or TX descriptors */ -static inline void set_tx_desc_pkt_size(u8 *__pdesc, u32 __val) +static inline void set_tx_desc_pkt_size(__le32 *__pdesc, u32 __val) { - le32p_replace_bits((__le32 *)__pdesc, __val, GENMASK(15, 0)); + le32p_replace_bits(__pdesc, __val, GENMASK(15, 0)); } -static inline void set_tx_desc_offset(u8 *__pdesc, u32 __val) +static inline void set_tx_desc_offset(__le32 *__pdesc, u32 __val) { - le32p_replace_bits((__le32 *)__pdesc, __val, GENMASK(23, 16)); + le32p_replace_bits(__pdesc, __val, GENMASK(23, 16)); } -static inline void set_tx_desc_htc(u8 *__pdesc, u32 __val) +static inline void set_tx_desc_htc(__le32 *__pdesc, u32 __val) { - le32p_replace_bits((__le32 *)__pdesc, __val, BIT(25)); + le32p_replace_bits(__pdesc, __val, BIT(25)); } -static inline void set_tx_desc_last_seg(u8 *__pdesc, u32 __val) +static inline void set_tx_desc_last_seg(__le32 *__pdesc, u32 __val) { - le32p_replace_bits((__le32 *)__pdesc, __val, BIT(26)); + le32p_replace_bits(__pdesc, __val, BIT(26)); } -static inline void set_tx_desc_first_seg(u8 *__pdesc, u32 __val) +static inline void set_tx_desc_first_seg(__le32 *__pdesc, u32 __val) { - le32p_replace_bits((__le32 *)__pdesc, __val, BIT(27)); + le32p_replace_bits(__pdesc, __val, BIT(27)); } -static inline void set_tx_desc_linip(u8 *__pdesc, u32 __val) +static inline void set_tx_desc_linip(__le32 *__pdesc, u32 __val) { - le32p_replace_bits((__le32 *)__pdesc, __val, BIT(28)); + le32p_replace_bits(__pdesc, __val, BIT(28)); } -static inline void set_tx_desc_own(u8 *__pdesc, u32 __val) +static inline void set_tx_desc_own(__le32 *__pdesc, u32 __val) { - le32p_replace_bits((__le32 *)__pdesc, __val, BIT(31)); + le32p_replace_bits(__pdesc, __val, BIT(31)); } -static inline u32 get_tx_desc_own(u8 *__pdesc) +static inline u32 get_tx_desc_own(__le32 *__pdesc) { - return le32_get_bits(*(__le32 *)__pdesc, BIT(31)); + return le32_get_bits(*__pdesc, BIT(31)); } -static inline void set_tx_desc_macid(u8 *__pdesc, u32 __val) +static inline void set_tx_desc_macid(__le32 *__pdesc, u32 __val) { - le32p_replace_bits((__le32 *)(__pdesc + 4), __val, GENMASK(4, 0)); + le32p_replace_bits((__pdesc + 1), __val, GENMASK(4, 0)); } -static inline void set_tx_desc_agg_enable(u8 *__pdesc, u32 __val) +static inline void set_tx_desc_agg_enable(__le32 *__pdesc, u32 __val) { - le32p_replace_bits((__le32 *)(__pdesc + 4), __val, BIT(5)); + le32p_replace_bits((__pdesc + 1), __val, BIT(5)); } -static inline void set_tx_desc_rdg_enable(u8 *__pdesc, u32 __val) +static inline void set_tx_desc_rdg_enable(__le32 *__pdesc, u32 __val) { - le32p_replace_bits((__le32 *)(__pdesc + 4), __val, BIT(7)); + le32p_replace_bits((__pdesc + 1), __val, BIT(7)); } -static inline void set_tx_desc_queue_sel(u8 *__pdesc, u32 __val) +static inline void set_tx_desc_queue_sel(__le32 *__pdesc, u32 __val) { - le32p_replace_bits((__le32 *)(__pdesc + 4), __val, GENMASK(12, 8)); + le32p_replace_bits((__pdesc + 1), __val, GENMASK(12, 8)); } -static inline void set_tx_desc_rate_id(u8 *__pdesc, u32 __val) +static inline void set_tx_desc_rate_id(__le32 *__pdesc, u32 __val) { - le32p_replace_bits((__le32 *)(__pdesc + 4), __val, GENMASK(19, 16)); + le32p_replace_bits((__pdesc + 1), __val, GENMASK(19, 16)); } -static inline void set_tx_desc_sec_type(u8 *__pdesc, u32 __val) +static inline void set_tx_desc_sec_type(__le32 *__pdesc, u32 __val) { - le32p_replace_bits((__le32 *)(__pdesc + 4), __val, GENMASK(23, 22)); + le32p_replace_bits((__pdesc + 1), __val, GENMASK(23, 22)); } -static inline void set_tx_desc_pkt_offset(u8 *__pdesc, u32 __val) +static inline void set_tx_desc_pkt_offset(__le32 *__pdesc, u32 __val) { - le32p_replace_bits((__le32 *)(__pdesc + 4), __val, GENMASK(30, 26)); + le32p_replace_bits((__pdesc + 1), __val, GENMASK(30, 26)); } -static inline void set_tx_desc_more_frag(u8 *__pdesc, u32 __val) +static inline void set_tx_desc_more_frag(__le32 *__pdesc, u32 __val) { - le32p_replace_bits((__le32 *)(__pdesc + 8), __val, BIT(17)); + le32p_replace_bits((__pdesc + 2), __val, BIT(17)); } -static inline void set_tx_desc_ampdu_density(u8 *__pdesc, u32 __val) +static inline void set_tx_desc_ampdu_density(__le32 *__pdesc, u32 __val) { - le32p_replace_bits((__le32 *)(__pdesc + 8), __val, GENMASK(22, 20)); + le32p_replace_bits((__pdesc + 2), __val, GENMASK(22, 20)); } -static inline void set_tx_desc_seq(u8 *__pdesc, u32 __val) +static inline void set_tx_desc_seq(__le32 *__pdesc, u32 __val) { - le32p_replace_bits((__le32 *)(__pdesc + 12), __val, GENMASK(27, 16)); + le32p_replace_bits((__pdesc + 3), __val, GENMASK(27, 16)); } -static inline void set_tx_desc_pkt_id(u8 *__pdesc, u32 __val) +static inline void set_tx_desc_pkt_id(__le32 *__pdesc, u32 __val) { - le32p_replace_bits((__le32 *)(__pdesc + 12), __val, GENMASK(31, 28)); + le32p_replace_bits((__pdesc + 3), __val, GENMASK(31, 28)); } -static inline void set_tx_desc_rts_rate(u8 *__pdesc, u32 __val) +static inline void set_tx_desc_rts_rate(__le32 *__pdesc, u32 __val) { - le32p_replace_bits((__le32 *)(__pdesc + 16), __val, GENMASK(4, 0)); + le32p_replace_bits((__pdesc + 4), __val, GENMASK(4, 0)); } -static inline void set_tx_desc_qos(u8 *__pdesc, u32 __val) +static inline void set_tx_desc_qos(__le32 *__pdesc, u32 __val) { - le32p_replace_bits((__le32 *)(__pdesc + 16), __val, BIT(6)); + le32p_replace_bits((__pdesc + 4), __val, BIT(6)); } -static inline void set_tx_desc_hwseq_en(u8 *__pdesc, u32 __val) +static inline void set_tx_desc_hwseq_en(__le32 *__pdesc, u32 __val) { - le32p_replace_bits((__le32 *)(__pdesc + 16), __val, BIT(7)); + le32p_replace_bits((__pdesc + 4), __val, BIT(7)); } -static inline void set_tx_desc_use_rate(u8 *__pdesc, u32 __val) +static inline void set_tx_desc_use_rate(__le32 *__pdesc, u32 __val) { - le32p_replace_bits((__le32 *)(__pdesc + 16), __val, BIT(8)); + le32p_replace_bits((__pdesc + 4), __val, BIT(8)); } -static inline void set_tx_desc_disable_fb(u8 *__pdesc, u32 __val) +static inline void set_tx_desc_disable_fb(__le32 *__pdesc, u32 __val) { - le32p_replace_bits((__le32 *)(__pdesc + 16), __val, BIT(10)); + le32p_replace_bits((__pdesc + 4), __val, BIT(10)); } -static inline void set_tx_desc_cts2self(u8 *__pdesc, u32 __val) +static inline void set_tx_desc_cts2self(__le32 *__pdesc, u32 __val) { - le32p_replace_bits((__le32 *)(__pdesc + 16), __val, BIT(11)); + le32p_replace_bits((__pdesc + 4), __val, BIT(11)); } -static inline void set_tx_desc_rts_enable(u8 *__pdesc, u32 __val) +static inline void set_tx_desc_rts_enable(__le32 *__pdesc, u32 __val) { - le32p_replace_bits((__le32 *)(__pdesc + 16), __val, BIT(12)); + le32p_replace_bits((__pdesc + 4), __val, BIT(12)); } -static inline void set_tx_desc_hw_rts_enable(u8 *__pdesc, u32 __val) +static inline void set_tx_desc_hw_rts_enable(__le32 *__pdesc, u32 __val) { - le32p_replace_bits((__le32 *)(__pdesc + 16), __val, BIT(13)); + le32p_replace_bits((__pdesc + 4), __val, BIT(13)); } -static inline void set_tx_desc_tx_sub_carrier(u8 *__pdesc, u32 __val) +static inline void set_tx_desc_tx_sub_carrier(__le32 *__pdesc, u32 __val) { - le32p_replace_bits((__le32 *)(__pdesc + 16), __val, GENMASK(21, 20)); + le32p_replace_bits((__pdesc + 4), __val, GENMASK(21, 20)); } -static inline void set_tx_desc_data_bw(u8 *__pdesc, u32 __val) +static inline void set_tx_desc_data_bw(__le32 *__pdesc, u32 __val) { - le32p_replace_bits((__le32 *)(__pdesc + 16), __val, BIT(25)); + le32p_replace_bits((__pdesc + 4), __val, BIT(25)); } -static inline void set_tx_desc_rts_short(u8 *__pdesc, u32 __val) +static inline void set_tx_desc_rts_short(__le32 *__pdesc, u32 __val) { - le32p_replace_bits((__le32 *)(__pdesc + 16), __val, BIT(26)); + le32p_replace_bits((__pdesc + 4), __val, BIT(26)); } -static inline void set_tx_desc_rts_bw(u8 *__pdesc, u32 __val) +static inline void set_tx_desc_rts_bw(__le32 *__pdesc, u32 __val) { - le32p_replace_bits((__le32 *)(__pdesc + 16), __val, BIT(27)); + le32p_replace_bits((__pdesc + 4), __val, BIT(27)); } -static inline void set_tx_desc_rts_sc(u8 *__pdesc, u32 __val) +static inline void set_tx_desc_rts_sc(__le32 *__pdesc, u32 __val) { - le32p_replace_bits((__le32 *)(__pdesc + 16), __val, GENMASK(29, 28)); + le32p_replace_bits((__pdesc + 4), __val, GENMASK(29, 28)); } -static inline void set_tx_desc_rts_stbc(u8 *__pdesc, u32 __val) +static inline void set_tx_desc_rts_stbc(__le32 *__pdesc, u32 __val) { - le32p_replace_bits((__le32 *)(__pdesc + 16), __val, GENMASK(31, 30)); + le32p_replace_bits((__pdesc + 4), __val, GENMASK(31, 30)); } -static inline void set_tx_desc_tx_rate(u8 *__pdesc, u32 __val) +static inline void set_tx_desc_tx_rate(__le32 *__pdesc, u32 __val) { - le32p_replace_bits((__le32 *)(__pdesc + 20), __val, GENMASK(5, 0)); + le32p_replace_bits((__pdesc + 5), __val, GENMASK(5, 0)); } -static inline void set_tx_desc_data_shortgi(u8 *__pdesc, u32 __val) +static inline void set_tx_desc_data_shortgi(__le32 *__pdesc, u32 __val) { - le32p_replace_bits((__le32 *)(__pdesc + 20), __val, BIT(6)); + le32p_replace_bits((__pdesc + 5), __val, BIT(6)); } -static inline void set_tx_desc_data_rate_fb_limit(u8 *__pdesc, u32 __val) +static inline void set_tx_desc_data_rate_fb_limit(__le32 *__pdesc, u32 __val) { - le32p_replace_bits((__le32 *)(__pdesc + 20), __val, GENMASK(12, 8)); + le32p_replace_bits((__pdesc + 5), __val, GENMASK(12, 8)); } -static inline void set_tx_desc_rts_rate_fb_limit(u8 *__pdesc, u32 __val) +static inline void set_tx_desc_rts_rate_fb_limit(__le32 *__pdesc, u32 __val) { - le32p_replace_bits((__le32 *)(__pdesc + 20), __val, GENMASK(16, 13)); + le32p_replace_bits((__pdesc + 5), __val, GENMASK(16, 13)); } -static inline void set_tx_desc_max_agg_num(u8 *__pdesc, u32 __val) +static inline void set_tx_desc_max_agg_num(__le32 *__pdesc, u32 __val) { - le32p_replace_bits((__le32 *)(__pdesc + 24), __val, GENMASK(15, 11)); + le32p_replace_bits((__pdesc + 6), __val, GENMASK(15, 11)); } -static inline void set_tx_desc_tx_buffer_size(u8 *__pdesc, u32 __val) +static inline void set_tx_desc_tx_buffer_size(__le32 *__pdesc, u32 __val) { - le32p_replace_bits((__le32 *)(__pdesc + 28), __val, GENMASK(15, 0)); + le32p_replace_bits((__pdesc + 7), __val, GENMASK(15, 0)); } -static inline void set_tx_desc_tx_buffer_address(u8 *__pdesc, u32 __val) +static inline void set_tx_desc_tx_buffer_address(__le32 *__pdesc, u32 __val) { - *(__le32 *)(__pdesc + 32) = cpu_to_le32(__val); + *(__pdesc + 8) = cpu_to_le32(__val); } -static inline u32 get_tx_desc_tx_buffer_address(u8 *__pdesc) +static inline u32 get_tx_desc_tx_buffer_address(__le32 *__pdesc) { - return le32_to_cpu(*(__le32 *)(__pdesc + 32)); + return le32_to_cpu(*(__pdesc + 8)); } -static inline void set_tx_desc_next_desc_address(u8 *__pdesc, u32 __val) +static inline void set_tx_desc_next_desc_address(__le32 *__pdesc, u32 __val) { - *(__le32 *)(__pdesc + 40) = cpu_to_le32(__val); + *(__pdesc + 10) = cpu_to_le32(__val); } -static inline u32 get_rx_desc_pkt_len(u8 *__pdesc) +static inline u32 get_rx_desc_pkt_len(__le32 *__pdesc) { - return le32_get_bits(*(__le32 *)__pdesc, GENMASK(13, 0)); + return le32_get_bits(*__pdesc, GENMASK(13, 0)); } -static inline u32 get_rx_desc_crc32(u8 *__pdesc) +static inline u32 get_rx_desc_crc32(__le32 *__pdesc) { - return le32_get_bits(*(__le32 *)__pdesc, BIT(14)); + return le32_get_bits(*__pdesc, BIT(14)); } -static inline u32 get_rx_desc_icv(u8 *__pdesc) +static inline u32 get_rx_desc_icv(__le32 *__pdesc) { - return le32_get_bits(*(__le32 *)__pdesc, BIT(15)); + return le32_get_bits(*__pdesc, BIT(15)); } -static inline u32 get_rx_desc_drv_info_size(u8 *__pdesc) +static inline u32 get_rx_desc_drv_info_size(__le32 *__pdesc) { - return le32_get_bits(*(__le32 *)__pdesc, GENMASK(19, 16)); + return le32_get_bits(*__pdesc, GENMASK(19, 16)); } -static inline u32 get_rx_desc_shift(u8 *__pdesc) +static inline u32 get_rx_desc_shift(__le32 *__pdesc) { - return le32_get_bits(*(__le32 *)__pdesc, GENMASK(25, 24)); + return le32_get_bits(*__pdesc, GENMASK(25, 24)); } -static inline u32 get_rx_desc_physt(u8 *__pdesc) +static inline u32 get_rx_desc_physt(__le32 *__pdesc) { - return le32_get_bits(*(__le32 *)__pdesc, BIT(26)); + return le32_get_bits(*__pdesc, BIT(26)); } -static inline u32 get_rx_desc_swdec(u8 *__pdesc) +static inline u32 get_rx_desc_swdec(__le32 *__pdesc) { - return le32_get_bits(*(__le32 *)__pdesc, BIT(27)); + return le32_get_bits(*__pdesc, BIT(27)); } -static inline u32 get_rx_desc_own(u8 *__pdesc) +static inline u32 get_rx_desc_own(__le32 *__pdesc) { - return le32_get_bits(*(__le32 *)__pdesc, BIT(31)); + return le32_get_bits(*__pdesc, BIT(31)); } -static inline void set_rx_desc_pkt_len(u8 *__pdesc, u32 __val) +static inline void set_rx_desc_pkt_len(__le32 *__pdesc, u32 __val) { - le32p_replace_bits((__le32 *)__pdesc, __val, GENMASK(13, 0)); + le32p_replace_bits(__pdesc, __val, GENMASK(13, 0)); } -static inline void set_rx_desc_eor(u8 *__pdesc, u32 __val) +static inline void set_rx_desc_eor(__le32 *__pdesc, u32 __val) { - le32p_replace_bits((__le32 *)__pdesc, __val, BIT(30)); + le32p_replace_bits(__pdesc, __val, BIT(30)); } -static inline void set_rx_desc_own(u8 *__pdesc, u32 __val) +static inline void set_rx_desc_own(__le32 *__pdesc, u32 __val) { - le32p_replace_bits((__le32 *)__pdesc, __val, BIT(31)); + le32p_replace_bits(__pdesc, __val, BIT(31)); } -static inline u32 get_rx_desc_paggr(u8 *__pdesc) +static inline u32 get_rx_desc_paggr(__le32 *__pdesc) { - return le32_get_bits(*(__le32 *)(__pdesc + 4), BIT(14)); + return le32_get_bits(*(__pdesc + 1), BIT(14)); } -static inline u32 get_rx_desc_faggr(u8 *__pdesc) +static inline u32 get_rx_desc_faggr(__le32 *__pdesc) { - return le32_get_bits(*(__le32 *)(__pdesc + 4), BIT(15)); + return le32_get_bits(*(__pdesc + 1), BIT(15)); } -static inline u32 get_rx_desc_rxmcs(u8 *__pdesc) +static inline u32 get_rx_desc_rxmcs(__le32 *__pdesc) { - return le32_get_bits(*(__le32 *)(__pdesc + 12), GENMASK(5, 0)); + return le32_get_bits(*(__pdesc + 3), GENMASK(5, 0)); } -static inline u32 get_rx_desc_rxht(u8 *__pdesc) +static inline u32 get_rx_desc_rxht(__le32 *__pdesc) { - return le32_get_bits(*(__le32 *)(__pdesc + 12), BIT(6)); + return le32_get_bits(*(__pdesc + 3), BIT(6)); } -static inline u32 get_rx_desc_splcp(u8 *__pdesc) +static inline u32 get_rx_desc_splcp(__le32 *__pdesc) { - return le32_get_bits(*(__le32 *)(__pdesc + 12), BIT(8)); + return le32_get_bits(*(__pdesc + 3), BIT(8)); } -static inline u32 get_rx_desc_bw(u8 *__pdesc) +static inline u32 get_rx_desc_bw(__le32 *__pdesc) { - return le32_get_bits(*(__le32 *)(__pdesc + 12), BIT(9)); + return le32_get_bits(*(__pdesc + 3), BIT(9)); } -static inline u32 get_rx_desc_tsfl(u8 *__pdesc) +static inline u32 get_rx_desc_tsfl(__le32 *__pdesc) { - return le32_to_cpu(*(__le32 *)(__pdesc + 20)); + return le32_to_cpu(*(__pdesc + 5)); } -static inline u32 get_rx_desc_buff_addr(u8 *__pdesc) +static inline u32 get_rx_desc_buff_addr(__le32 *__pdesc) { - return le32_to_cpu(*(__le32 *)(__pdesc + 24)); + return le32_to_cpu(*(__pdesc + 6)); } -static inline void set_rx_desc_buff_addr(u8 *__pdesc, u32 __val) +static inline void set_rx_desc_buff_addr(__le32 *__pdesc, u32 __val) { - *(__le32 *)(__pdesc + 24) = cpu_to_le32(__val); + *(__pdesc + 6) = cpu_to_le32(__val); } -#define CLEAR_PCI_TX_DESC_CONTENT(__pdesc, _size) \ - memset((void *)__pdesc, 0, \ - min_t(size_t, _size, TX_DESC_NEXT_DESC_OFFSET)) +static inline void clear_pci_tx_desc_content(__le32 *__pdesc, u32 _size) +{ + memset((void *)__pdesc, 0, + min_t(size_t, _size, TX_DESC_NEXT_DESC_OFFSET)); +} /* For 92D early mode */ -static inline void set_earlymode_pktnum(u8 *__paddr, u32 __value) +static inline void set_earlymode_pktnum(__le32 *__paddr, u32 __value) { - le32p_replace_bits((__le32 *)__paddr, __value, GENMASK(2, 0)); + le32p_replace_bits(__paddr, __value, GENMASK(2, 0)); } -static inline void set_earlymode_len0(u8 *__paddr, u32 __value) +static inline void set_earlymode_len0(__le32 *__paddr, u32 __value) { - le32p_replace_bits((__le32 *)__paddr, __value, GENMASK(15, 4)); + le32p_replace_bits(__paddr, __value, GENMASK(15, 4)); } -static inline void set_earlymode_len1(u8 *__paddr, u32 __value) +static inline void set_earlymode_len1(__le32 *__paddr, u32 __value) { - le32p_replace_bits((__le32 *)__paddr, __value, GENMASK(27, 16)); + le32p_replace_bits(__paddr, __value, GENMASK(27, 16)); } -static inline void set_earlymode_len2_1(u8 *__paddr, u32 __value) +static inline void set_earlymode_len2_1(__le32 *__paddr, u32 __value) { - le32p_replace_bits((__le32 *)__paddr, __value, GENMASK(31, 28)); + le32p_replace_bits(__paddr, __value, GENMASK(31, 28)); } -static inline void set_earlymode_len2_2(u8 *__paddr, u32 __value) +static inline void set_earlymode_len2_2(__le32 *__paddr, u32 __value) { - le32p_replace_bits((__le32 *)(__paddr + 4), __value, GENMASK(7, 0)); + le32p_replace_bits((__paddr + 1), __value, GENMASK(7, 0)); } -static inline void set_earlymode_len3(u8 *__paddr, u32 __value) +static inline void set_earlymode_len3(__le32 *__paddr, u32 __value) { - le32p_replace_bits((__le32 *)(__paddr + 4), __value, GENMASK(19, 8)); + le32p_replace_bits((__paddr + 1), __value, GENMASK(19, 8)); } -static inline void set_earlymode_len4(u8 *__paddr, u32 __value) +static inline void set_earlymode_len4(__le32 *__paddr, u32 __value) { - le32p_replace_bits((__le32 *)(__paddr + 4), __value, GENMASK(31, 20)); + le32p_replace_bits((__paddr + 1), __value, GENMASK(31, 20)); } struct rx_fwinfo_92d { -- GitLab From 7031fb7f1cd12720921bf2c62e1c09aea7bcb890 Mon Sep 17 00:00:00 2001 From: Larry Finger Date: Mon, 25 Nov 2019 16:09:26 -0600 Subject: [PATCH 0938/1437] rtlwifi: rtl8192de: Remove usage of private bit manipulation macros Besides the previously changes macros in trx.h, there are some of these local macros in the driver. These are replaced with standard bit manipulations. Unused macros are deleted. Signed-off-by: Larry Finger Signed-off-by: Kalle Valo --- .../wireless/realtek/rtlwifi/rtl8192de/fw.h | 61 +++---------------- 1 file changed, 7 insertions(+), 54 deletions(-) diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192de/fw.h b/drivers/net/wireless/realtek/rtlwifi/rtl8192de/fw.h index bd8ea6d66dfff..7f0a17c1a9ea8 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192de/fw.h +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192de/fw.h @@ -16,73 +16,26 @@ (GET_FIRMWARE_HDR_SIGNATURE(_pfwhdr) & 0xFFFF) == 0x92D2 || \ (GET_FIRMWARE_HDR_SIGNATURE(_pfwhdr) & 0xFFFF) == 0x92D3) -/* Define a macro that takes an le32 word, converts it to host ordering, - * right shifts by a specified count, creates a mask of the specified - * bit count, and extracts that number of bits. - */ - -#define SHIFT_AND_MASK_LE(__pdesc, __shift, __mask) \ - ((le32_to_cpu(*(((__le32 *)(__pdesc)))) >> (__shift)) & \ - BIT_LEN_MASK_32(__mask)) - /* Firmware Header(8-byte alinment required) */ /* --- LONG WORD 0 ---- */ #define GET_FIRMWARE_HDR_SIGNATURE(__fwhdr) \ - SHIFT_AND_MASK_LE(__fwhdr, 0, 16) -#define GET_FIRMWARE_HDR_CATEGORY(__fwhdr) \ - SHIFT_AND_MASK_LE(__fwhdr, 16, 8) -#define GET_FIRMWARE_HDR_FUNCTION(__fwhdr) \ - SHIFT_AND_MASK_LE(__fwhdr, 24, 8) + le32_get_bits(*(__le32 *)__fwhdr, GENMASK(15, 0)) #define GET_FIRMWARE_HDR_VERSION(__fwhdr) \ - SHIFT_AND_MASK_LE(__fwhdr + 4, 0, 16) + le32_get_bits(*(__le32 *)(__fwhdr + 4), GENMASK(15, 0)) #define GET_FIRMWARE_HDR_SUB_VER(__fwhdr) \ - SHIFT_AND_MASK_LE(__fwhdr + 4, 16, 8) -#define GET_FIRMWARE_HDR_RSVD1(__fwhdr) \ - SHIFT_AND_MASK_LE(__fwhdr + 4, 24, 8) - -/* --- LONG WORD 1 ---- */ -#define GET_FIRMWARE_HDR_MONTH(__fwhdr) \ - SHIFT_AND_MASK_LE(__fwhdr + 8, 0, 8) -#define GET_FIRMWARE_HDR_DATE(__fwhdr) \ - SHIFT_AND_MASK_LE(__fwhdr + 8, 8, 8) -#define GET_FIRMWARE_HDR_HOUR(__fwhdr) \ - SHIFT_AND_MASK_LE(__fwhdr + 8, 16, 8) -#define GET_FIRMWARE_HDR_MINUTE(__fwhdr) \ - SHIFT_AND_MASK_LE(__fwhdr + 8, 24, 8) -#define GET_FIRMWARE_HDR_ROMCODE_SIZE(__fwhdr) \ - SHIFT_AND_MASK_LE(__fwhdr + 12, 0, 16) -#define GET_FIRMWARE_HDR_RSVD2(__fwhdr) \ - SHIFT_AND_MASK_LE(__fwhdr + 12, 16, 16) - -/* --- LONG WORD 2 ---- */ -#define GET_FIRMWARE_HDR_SVN_IDX(__fwhdr) \ - SHIFT_AND_MASK_LE(__fwhdr + 16, 0, 32) -#define GET_FIRMWARE_HDR_RSVD3(__fwhdr) \ - SHIFT_AND_MASK_LE(__fwhdr + 20, 0, 32) - -/* --- LONG WORD 3 ---- */ -#define GET_FIRMWARE_HDR_RSVD4(__fwhdr) \ - SHIFT_AND_MASK_LE(__fwhdr + 24, 0, 32) -#define GET_FIRMWARE_HDR_RSVD5(__fwhdr) \ - SHIFT_AND_MASK_LE(__fwhdr + 28, 0, 32) + le32_get_bits(*(__le32 *)(__fwhdr + 4), GENMASK(23, 16)) #define pagenum_128(_len) \ (u32)(((_len) >> 7) + ((_len) & 0x7F ? 1 : 0)) -#define SET_H2CCMD_PWRMODE_PARM_MODE(__ph2ccmd, __val) \ - SET_BITS_TO_LE_1BYTE(__ph2ccmd, 0, 8, __val) -#define SET_H2CCMD_PWRMODE_PARM_SMART_PS(__ph2ccmd, __val) \ - SET_BITS_TO_LE_1BYTE((__ph2ccmd) + 1, 0, 8, __val) -#define SET_H2CCMD_PWRMODE_PARM_BCN_PASS_TIME(__ph2ccmd, __val) \ - SET_BITS_TO_LE_1BYTE((__ph2ccmd) + 2, 0, 8, __val) #define SET_H2CCMD_JOINBSSRPT_PARM_OPMODE(__ph2ccmd, __val) \ - SET_BITS_TO_LE_1BYTE(__ph2ccmd, 0, 8, __val) + *(u8 *)__ph2ccmd = __val; #define SET_H2CCMD_RSVDPAGE_LOC_PROBE_RSP(__ph2ccmd, __val) \ - SET_BITS_TO_LE_1BYTE(__ph2ccmd, 0, 8, __val) + *(u8 *)__ph2ccmd = __val; #define SET_H2CCMD_RSVDPAGE_LOC_PSPOLL(__ph2ccmd, __val) \ - SET_BITS_TO_LE_1BYTE((__ph2ccmd) + 1, 0, 8, __val) + *(u8 *)(__ph2ccmd + 1) = __val; #define SET_H2CCMD_RSVDPAGE_LOC_NULL_DATA(__ph2ccmd, __val) \ - SET_BITS_TO_LE_1BYTE((__ph2ccmd) + 2, 0, 8, __val) + *(u8 *)(__ph2ccmd + 2) = __val; int rtl92d_download_fw(struct ieee80211_hw *hw); void rtl92d_fill_h2c_cmd(struct ieee80211_hw *hw, u8 element_id, -- GitLab From 5ea5c93d937cb5fb5891835ab134221a2881e32a Mon Sep 17 00:00:00 2001 From: Larry Finger Date: Mon, 25 Nov 2019 16:09:27 -0600 Subject: [PATCH 0939/1437] rtlwifi: rtl8188ee: Remove usage of private bit manipulation Besides the previously changes macros for the RX and TX descriptors. the local macros are used in other places. These are replaced with standard bit manipulation macros. Signed-off-by: Larry Finger Signed-off-by: Kalle Valo --- .../wireless/realtek/rtlwifi/rtl8188ee/fw.c | 12 +- .../wireless/realtek/rtlwifi/rtl8188ee/fw.h | 103 +++++++----------- .../wireless/realtek/rtlwifi/rtl8188ee/hw.c | 11 +- 3 files changed, 49 insertions(+), 77 deletions(-) diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8188ee/fw.c b/drivers/net/wireless/realtek/rtlwifi/rtl8188ee/fw.c index e2e0bfbc24fe2..fc7b9ad7e5d06 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8188ee/fw.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8188ee/fw.c @@ -372,20 +372,20 @@ void rtl88e_set_fw_pwrmode_cmd(struct ieee80211_hw *hw, u8 mode) u8 rlbm, power_state = 0; RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD, "FW LPS mode = %d\n", mode); - SET_H2CCMD_PWRMODE_PARM_MODE(u1_h2c_set_pwrmode, ((mode) ? 1 : 0)); + set_h2ccmd_pwrmode_parm_mode(u1_h2c_set_pwrmode, ((mode) ? 1 : 0)); rlbm = 0;/*YJ, temp, 120316. FW now not support RLBM=2.*/ - SET_H2CCMD_PWRMODE_PARM_RLBM(u1_h2c_set_pwrmode, rlbm); - SET_H2CCMD_PWRMODE_PARM_SMART_PS(u1_h2c_set_pwrmode, + set_h2ccmd_pwrmode_parm_rlbm(u1_h2c_set_pwrmode, rlbm); + set_h2ccmd_pwrmode_parm_smart_ps(u1_h2c_set_pwrmode, (rtlpriv->mac80211.p2p) ? ppsc->smart_ps : 1); - SET_H2CCMD_PWRMODE_PARM_AWAKE_INTERVAL(u1_h2c_set_pwrmode, + set_h2ccmd_pwrmode_parm_awake_interval(u1_h2c_set_pwrmode, ppsc->reg_max_lps_awakeintvl); - SET_H2CCMD_PWRMODE_PARM_ALL_QUEUE_UAPSD(u1_h2c_set_pwrmode, 0); + set_h2ccmd_pwrmode_parm_all_queue_uapsd(u1_h2c_set_pwrmode, 0); if (mode == FW_PS_ACTIVE_MODE) power_state |= FW_PWR_STATE_ACTIVE; else power_state |= FW_PWR_STATE_RF_OFF; - SET_H2CCMD_PWRMODE_PARM_PWR_STATE(u1_h2c_set_pwrmode, power_state); + set_h2ccmd_pwrmode_parm_pwr_state(u1_h2c_set_pwrmode, power_state); RT_PRINT_DATA(rtlpriv, COMP_CMD, DBG_DMESG, "rtl92c_set_fw_pwrmode(): u1_h2c_set_pwrmode\n", diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8188ee/fw.h b/drivers/net/wireless/realtek/rtlwifi/rtl8188ee/fw.h index 39ddb7afea9dd..79f095e47d71b 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8188ee/fw.h +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8188ee/fw.h @@ -169,82 +169,55 @@ enum rtl8188e_h2c_cmd { SET_BITS_TO_LE_1BYTE((__cmd)+2, 0, 8, __value) -#define SET_H2CCMD_PWRMODE_PARM_MODE(__ph2ccmd, __val) \ - SET_BITS_TO_LE_1BYTE(__ph2ccmd, 0, 8, __val) -#define SET_H2CCMD_PWRMODE_PARM_RLBM(__cmd, __value) \ - SET_BITS_TO_LE_1BYTE((__cmd)+1, 0, 4, __value) -#define SET_H2CCMD_PWRMODE_PARM_SMART_PS(__cmd, __value) \ - SET_BITS_TO_LE_1BYTE((__cmd)+1, 4, 4, __value) -#define SET_H2CCMD_PWRMODE_PARM_AWAKE_INTERVAL(__cmd, __value) \ - SET_BITS_TO_LE_1BYTE((__cmd)+2, 0, 8, __value) -#define SET_H2CCMD_PWRMODE_PARM_ALL_QUEUE_UAPSD(__cmd, __value) \ - SET_BITS_TO_LE_1BYTE((__cmd)+3, 0, 8, __value) -#define SET_H2CCMD_PWRMODE_PARM_PWR_STATE(__cmd, __value) \ - SET_BITS_TO_LE_1BYTE((__cmd)+4, 0, 8, __value) -#define GET_88E_H2CCMD_PWRMODE_PARM_MODE(__cmd) \ - LE_BITS_TO_1BYTE(__cmd, 0, 8) +static inline void set_h2ccmd_pwrmode_parm_mode(u8 *__ph2ccmd, u8 __val) +{ + *(u8 *)(__ph2ccmd) = __val; +} + +static inline void set_h2ccmd_pwrmode_parm_rlbm(u8 *__cmd, u8 __value) +{ + u8p_replace_bits(__cmd + 1, __value, GENMASK(3, 0)); +} + +static inline void set_h2ccmd_pwrmode_parm_smart_ps(u8 *__cmd, u8 __value) +{ + u8p_replace_bits(__cmd + 1, __value, GENMASK(7, 4)); +} + +static inline void set_h2ccmd_pwrmode_parm_awake_interval(u8 *__cmd, u8 __value) +{ + *(u8 *)(__cmd + 2) = __value; +} + +static inline void set_h2ccmd_pwrmode_parm_all_queue_uapsd(u8 *__cmd, + u8 __value) +{ + *(u8 *)(__cmd + 3) = __value; +} + +static inline void set_h2ccmd_pwrmode_parm_pwr_state(u8 *__cmd, u8 __value) +{ + *(u8 *)(__cmd + 4) = __value; +} #define SET_H2CCMD_JOINBSSRPT_PARM_OPMODE(__ph2ccmd, __val) \ - SET_BITS_TO_LE_1BYTE(__ph2ccmd, 0, 8, __val) + *(u8 *)(__ph2ccmd) = __val; #define SET_H2CCMD_RSVDPAGE_LOC_PROBE_RSP(__ph2ccmd, __val) \ - SET_BITS_TO_LE_1BYTE(__ph2ccmd, 0, 8, __val) + *(u8 *)(__ph2ccmd) = __val; #define SET_H2CCMD_RSVDPAGE_LOC_PSPOLL(__ph2ccmd, __val) \ - SET_BITS_TO_LE_1BYTE((__ph2ccmd)+1, 0, 8, __val) + *(u8 *)(__ph2ccmd + 1) = __val; #define SET_H2CCMD_RSVDPAGE_LOC_NULL_DATA(__ph2ccmd, __val) \ - SET_BITS_TO_LE_1BYTE((__ph2ccmd)+2, 0, 8, __val) + *(u8 *)(__ph2ccmd + 2) = __val; /* AP_OFFLOAD */ #define SET_H2CCMD_AP_OFFLOAD_ON(__cmd, __value) \ - SET_BITS_TO_LE_1BYTE(__cmd, 0, 8, __value) + *(u8 *)__cmd = __value; #define SET_H2CCMD_AP_OFFLOAD_HIDDEN(__cmd, __value) \ - SET_BITS_TO_LE_1BYTE((__cmd)+1, 0, 8, __value) + *(u8 *)(__cmd + 1) = __value; #define SET_H2CCMD_AP_OFFLOAD_DENYANY(__cmd, __value) \ - SET_BITS_TO_LE_1BYTE((__cmd)+2, 0, 8, __value) + *(u8 *)(__cmd + 2) = __value; #define SET_H2CCMD_AP_OFFLOAD_WAKEUP_EVT_RPT(__cmd, __value) \ - SET_BITS_TO_LE_1BYTE((__cmd)+3, 0, 8, __value) - -/* Keep Alive Control*/ -#define SET_88E_H2CCMD_KEEP_ALIVE_ENABLE(__cmd, __value) \ - SET_BITS_TO_LE_1BYTE(__cmd, 0, 1, __value) -#define SET_88E_H2CCMD_KEEP_ALIVE_ACCPEPT_USER_DEFINED(__cmd, __value) \ - SET_BITS_TO_LE_1BYTE(__cmd, 1, 1, __value) -#define SET_88E_H2CCMD_KEEP_ALIVE_PERIOD(__cmd, __value) \ - SET_BITS_TO_LE_1BYTE((__cmd)+1, 0, 8, __value) - -/*REMOTE_WAKE_CTRL */ -#define SET_88E_H2CCMD_REMOTE_WAKE_CTRL_EN(__cmd, __value) \ - SET_BITS_TO_LE_1BYTE(__cmd, 0, 1, __value) -#if (USE_OLD_WOWLAN_DEBUG_FW == 0) -#define SET_88E_H2CCMD_REMOTE_WAKE_CTRL_ARP_OFFLOAD_EN(__cmd, __value) \ - SET_BITS_TO_LE_1BYTE(__cmd, 1, 1, __value) -#define SET_88E_H2CCMD_REMOTE_WAKE_CTRL_NDP_OFFLOAD_EN(__cmd, __value) \ - SET_BITS_TO_LE_1BYTE(__cmd, 2, 1, __value) -#define SET_88E_H2CCMD_REMOTE_WAKE_CTRL_GTK_OFFLOAD_EN(__cmd, __value) \ - SET_BITS_TO_LE_1BYTE(__cmd, 3, 1, __value) -#else -#define SET_88E_H2_REM_WAKE_ENC_ALG(__cmd, __value) \ - SET_BITS_TO_LE_1BYTE((__cmd)+1, 0, 8, __value) -#define SET_88E_H2CCMD_REMOTE_WAKE_CTRL_GROUP_ENC_ALG(__cmd, __value) \ - SET_BITS_TO_LE_1BYTE((__cmd)+2, 0, 8, __value) -#endif - -/* GTK_OFFLOAD */ -#define SET_88E_H2CCMD_AOAC_GLOBAL_INFO_PAIRWISE_ENC_ALG(__cmd, __value) \ - SET_BITS_TO_LE_1BYTE(__cmd, 0, 8, __value) -#define SET_88E_H2CCMD_AOAC_GLOBAL_INFO_GROUP_ENC_ALG(__cmd, __value) \ - SET_BITS_TO_LE_1BYTE((__cmd)+1, 0, 8, __value) - -/* AOAC_RSVDPAGE_LOC */ -#define SET_88E_H2CCMD_AOAC_RSVD_LOC_REM_WAKE_CTRL_INFO(__cmd, __value) \ - SET_BITS_TO_LE_1BYTE((__cmd), 0, 8, __value) -#define SET_88E_H2CCMD_AOAC_RSVDPAGE_LOC_ARP_RSP(__cmd, __value) \ - SET_BITS_TO_LE_1BYTE((__cmd)+1, 0, 8, __value) -#define SET_88E_H2CCMD_AOAC_RSVDPAGE_LOC_NEIGHBOR_ADV(__cmd, __value) \ - SET_BITS_TO_LE_1BYTE((__cmd)+2, 0, 8, __value) -#define SET_88E_H2CCMD_AOAC_RSVDPAGE_LOC_GTK_RSP(__cmd, __value) \ - SET_BITS_TO_LE_1BYTE((__cmd)+3, 0, 8, __value) -#define SET_88E_H2CCMD_AOAC_RSVDPAGE_LOC_GTK_INFO(__cmd, __value) \ - SET_BITS_TO_LE_1BYTE((__cmd)+4, 0, 8, __value) + *(u8 *)(__cmd + 3) = __value; int rtl88e_download_fw(struct ieee80211_hw *hw, bool buse_wake_on_wlan_fw); diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8188ee/hw.c b/drivers/net/wireless/realtek/rtlwifi/rtl8188ee/hw.c index f92e95f5494f3..70716631de85d 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8188ee/hw.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8188ee/hw.c @@ -2486,13 +2486,12 @@ void rtl8188ee_bt_hw_init(struct ieee80211_hw *hw) if (rtlpriv->btcoexist.bt_ant_isolation) rtl_write_byte(rtlpriv, REG_GPIO_MUXCFG, 0xa0); - u1_tmp = rtl_read_byte(rtlpriv, 0x4fd) & - BIT_OFFSET_LEN_MASK_32(0, 1); + u1_tmp = rtl_read_byte(rtlpriv, 0x4fd) & BIT(0); u1_tmp = u1_tmp | ((rtlpriv->btcoexist.bt_ant_isolation == 1) ? - 0 : BIT_OFFSET_LEN_MASK_32(1, 1)) | + 0 : BIT((1)) | ((rtlpriv->btcoexist.bt_service == BT_SCO) ? - 0 : BIT_OFFSET_LEN_MASK_32(2, 1)); + 0 : BIT(2))); rtl_write_byte(rtlpriv, 0x4fd, u1_tmp); rtl_write_dword(rtlpriv, REG_BT_COEX_TABLE+4, 0xaaaa9aaa); @@ -2502,11 +2501,11 @@ void rtl8188ee_bt_hw_init(struct ieee80211_hw *hw) /* Config to 1T1R. */ if (rtlphy->rf_type == RF_1T1R) { u1_tmp = rtl_read_byte(rtlpriv, ROFDM0_TRXPATHENABLE); - u1_tmp &= ~(BIT_OFFSET_LEN_MASK_32(1, 1)); + u1_tmp &= ~(BIT(1)); rtl_write_byte(rtlpriv, ROFDM0_TRXPATHENABLE, u1_tmp); u1_tmp = rtl_read_byte(rtlpriv, ROFDM1_TRXPATHENABLE); - u1_tmp &= ~(BIT_OFFSET_LEN_MASK_32(1, 1)); + u1_tmp &= ~(BIT(1)); rtl_write_byte(rtlpriv, ROFDM1_TRXPATHENABLE, u1_tmp); } } -- GitLab From 4c8692d4939d2704e9ba3894a5510d0e323b6fbd Mon Sep 17 00:00:00 2001 From: Larry Finger Date: Mon, 25 Nov 2019 16:09:28 -0600 Subject: [PATCH 0940/1437] rtlwifi: rtl8192ce: rtl8192c_com: Remove usage of private bit manipulation macros Besides the previously changes macros for the RX and TX descriptors. the local macros are used in other places. These are replaced with standard bit manipulation macros. Signed-off-by: Larry Finger Signed-off-by: Kalle Valo --- .../realtek/rtlwifi/rtl8192c/dm_common.c | 19 +++++++++---------- .../realtek/rtlwifi/rtl8192c/fw_common.h | 14 +++++++------- .../wireless/realtek/rtlwifi/rtl8192ce/hw.c | 11 +++++------ 3 files changed, 21 insertions(+), 23 deletions(-) diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192c/dm_common.c b/drivers/net/wireless/realtek/rtlwifi/rtl8192c/dm_common.c index 4bef237f488d2..06fc9b5cdd8ff 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192c/dm_common.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192c/dm_common.c @@ -8,11 +8,11 @@ #include "../base.h" #include "../core.h" -#define BT_RSSI_STATE_NORMAL_POWER BIT_OFFSET_LEN_MASK_32(0, 1) -#define BT_RSSI_STATE_AMDPU_OFF BIT_OFFSET_LEN_MASK_32(1, 1) -#define BT_RSSI_STATE_SPECIAL_LOW BIT_OFFSET_LEN_MASK_32(2, 1) -#define BT_RSSI_STATE_BG_EDCA_LOW BIT_OFFSET_LEN_MASK_32(3, 1) -#define BT_RSSI_STATE_TXPOWER_LOW BIT_OFFSET_LEN_MASK_32(4, 1) +#define BT_RSSI_STATE_NORMAL_POWER BIT(0) +#define BT_RSSI_STATE_AMDPU_OFF BIT(1) +#define BT_RSSI_STATE_SPECIAL_LOW BIT(2) +#define BT_RSSI_STATE_BG_EDCA_LOW BIT(3) +#define BT_RSSI_STATE_TXPOWER_LOW BIT(4) #define BT_MASK 0x00ffffff #define RTLPRIV (struct rtl_priv *) @@ -1515,7 +1515,7 @@ static bool rtl92c_bt_state_change(struct ieee80211_hw *hw) polling == 0xffffffff && bt_state == 0xff) return false; - bt_state &= BIT_OFFSET_LEN_MASK_32(0, 1); + bt_state &= BIT(0); if (bt_state != rtlpriv->btcoexist.bt_cur_state) { rtlpriv->btcoexist.bt_cur_state = bt_state; @@ -1524,8 +1524,7 @@ static bool rtl92c_bt_state_change(struct ieee80211_hw *hw) bt_state = bt_state | ((rtlpriv->btcoexist.bt_ant_isolation == 1) ? - 0 : BIT_OFFSET_LEN_MASK_32(1, 1)) | - BIT_OFFSET_LEN_MASK_32(2, 1); + 0 : BIT(1)) | BIT(2); rtl_write_byte(rtlpriv, 0x4fd, bt_state); } return true; @@ -1555,9 +1554,9 @@ static bool rtl92c_bt_state_change(struct ieee80211_hw *hw) rtlpriv->btcoexist.bt_service = cur_service_type; bt_state = bt_state | ((rtlpriv->btcoexist.bt_ant_isolation == 1) ? - 0 : BIT_OFFSET_LEN_MASK_32(1, 1)) | + 0 : BIT(1)) | ((rtlpriv->btcoexist.bt_service != BT_IDLE) ? - 0 : BIT_OFFSET_LEN_MASK_32(2, 1)); + 0 : BIT(2)); /* Add interrupt migration when bt is not ini * idle state (no traffic). */ diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192c/fw_common.h b/drivers/net/wireless/realtek/rtlwifi/rtl8192c/fw_common.h index 888d9fc94bc2f..706fc753dfe65 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192c/fw_common.h +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192c/fw_common.h @@ -46,19 +46,19 @@ #define pagenum_128(_len) (u32)(((_len)>>7) + ((_len)&0x7F ? 1 : 0)) #define SET_H2CCMD_PWRMODE_PARM_MODE(__ph2ccmd, __val) \ - SET_BITS_TO_LE_1BYTE(__ph2ccmd, 0, 8, __val) + *(u8 *)(__ph2ccmd) = __val #define SET_H2CCMD_PWRMODE_PARM_SMART_PS(__ph2ccmd, __val) \ - SET_BITS_TO_LE_1BYTE((__ph2ccmd)+1, 0, 8, __val) + *(u8 *)(__ph2ccmd + 1) = __val #define SET_H2CCMD_PWRMODE_PARM_BCN_PASS_TIME(__ph2ccmd, __val) \ - SET_BITS_TO_LE_1BYTE((__ph2ccmd)+2, 0, 8, __val) + *(u8 *)(__ph2ccmd + 2) = __val #define SET_H2CCMD_JOINBSSRPT_PARM_OPMODE(__ph2ccmd, __val) \ - SET_BITS_TO_LE_1BYTE(__ph2ccmd, 0, 8, __val) + *(u8 *)(__ph2ccmd) = __val #define SET_H2CCMD_RSVDPAGE_LOC_PROBE_RSP(__ph2ccmd, __val) \ - SET_BITS_TO_LE_1BYTE(__ph2ccmd, 0, 8, __val) + *(u8 *)(__ph2ccmd) = __val #define SET_H2CCMD_RSVDPAGE_LOC_PSPOLL(__ph2ccmd, __val) \ - SET_BITS_TO_LE_1BYTE((__ph2ccmd)+1, 0, 8, __val) + *(u8 *)(__ph2ccmd + 1) = __val #define SET_H2CCMD_RSVDPAGE_LOC_NULL_DATA(__ph2ccmd, __val) \ - SET_BITS_TO_LE_1BYTE((__ph2ccmd)+2, 0, 8, __val) + *(u8 *)(__ph2ccmd + 2) = __val int rtl92c_download_fw(struct ieee80211_hw *hw); void rtl92c_fill_h2c_cmd(struct ieee80211_hw *hw, u8 element_id, diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192ce/hw.c b/drivers/net/wireless/realtek/rtlwifi/rtl8192ce/hw.c index a52dd64d528df..6402a9e09be7e 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192ce/hw.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192ce/hw.c @@ -2299,13 +2299,12 @@ void rtl8192ce_bt_hw_init(struct ieee80211_hw *hw) if (rtlpriv->btcoexist.bt_ant_isolation) rtl_write_byte(rtlpriv, REG_GPIO_MUXCFG, 0xa0); - u1_tmp = rtl_read_byte(rtlpriv, 0x4fd) & - BIT_OFFSET_LEN_MASK_32(0, 1); + u1_tmp = rtl_read_byte(rtlpriv, 0x4fd) & BIT(0); u1_tmp = u1_tmp | ((rtlpriv->btcoexist.bt_ant_isolation == 1) ? - 0 : BIT_OFFSET_LEN_MASK_32(1, 1)) | + 0 : BIT(1)) | ((rtlpriv->btcoexist.bt_service == BT_SCO) ? - 0 : BIT_OFFSET_LEN_MASK_32(2, 1)); + 0 : BIT(2)); rtl_write_byte(rtlpriv, 0x4fd, u1_tmp); rtl_write_dword(rtlpriv, REG_BT_COEX_TABLE+4, 0xaaaa9aaa); @@ -2315,11 +2314,11 @@ void rtl8192ce_bt_hw_init(struct ieee80211_hw *hw) /* Config to 1T1R. */ if (rtlphy->rf_type == RF_1T1R) { u1_tmp = rtl_read_byte(rtlpriv, ROFDM0_TRXPATHENABLE); - u1_tmp &= ~(BIT_OFFSET_LEN_MASK_32(1, 1)); + u1_tmp &= ~(BIT(1)); rtl_write_byte(rtlpriv, ROFDM0_TRXPATHENABLE, u1_tmp); u1_tmp = rtl_read_byte(rtlpriv, ROFDM1_TRXPATHENABLE); - u1_tmp &= ~(BIT_OFFSET_LEN_MASK_32(1, 1)); + u1_tmp &= ~(BIT(1)); rtl_write_byte(rtlpriv, ROFDM1_TRXPATHENABLE, u1_tmp); } } -- GitLab From f0dcd57e0b417152190e9f68e5ef981e1c316162 Mon Sep 17 00:00:00 2001 From: Larry Finger Date: Mon, 25 Nov 2019 16:09:29 -0600 Subject: [PATCH 0941/1437] rtlwifi: Remove dependence on special bit manipulation macros for common driver The special macros are used in several places in the base driver. These are replaced by standard bit manipulation macros or code. Signed-off-by: Larry Finger Signed-off-by: Kalle Valo --- drivers/net/wireless/realtek/rtlwifi/base.h | 4 ++-- drivers/net/wireless/realtek/rtlwifi/ps.c | 20 ++++++++++---------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/drivers/net/wireless/realtek/rtlwifi/base.h b/drivers/net/wireless/realtek/rtlwifi/base.h index e4a7e074ae3ff..fa92e29fffda8 100644 --- a/drivers/net/wireless/realtek/rtlwifi/base.h +++ b/drivers/net/wireless/realtek/rtlwifi/base.h @@ -61,9 +61,9 @@ enum ap_peer { CP_MACADDR((u8 *)(_hdr)+FRAME_OFFSET_ADDRESS3, (u8 *)(_val)) #define SET_TX_DESC_SPE_RPT(__pdesc, __val) \ - SET_BITS_TO_LE_4BYTE((__pdesc) + 8, 19, 1, __val) + le32p_replace_bits((__le32 *)(__pdesc + 8), __val, BIT(19)) #define SET_TX_DESC_SW_DEFINE(__pdesc, __val) \ - SET_BITS_TO_LE_4BYTE((__pdesc) + 24, 0, 12, __val) + le32p_replace_bits((__le32 *)(__pdesc + 24), __val, GENMASK(11, 0)) int rtl_init_core(struct ieee80211_hw *hw); void rtl_deinit_core(struct ieee80211_hw *hw); diff --git a/drivers/net/wireless/realtek/rtlwifi/ps.c b/drivers/net/wireless/realtek/rtlwifi/ps.c index e5e1ec5a41dc2..bc0ac96ee615e 100644 --- a/drivers/net/wireless/realtek/rtlwifi/ps.c +++ b/drivers/net/wireless/realtek/rtlwifi/ps.c @@ -737,7 +737,7 @@ static void rtl_p2p_noa_ie(struct ieee80211_hw *hw, void *data, find_p2p_ie = true; /*to find noa ie*/ while (ie + 1 < end) { - noa_len = READEF2BYTE((__le16 *)&ie[1]); + noa_len = le16_to_cpu(*((__le16 *)&ie[1])); if (ie + 3 + ie[1] > end) return; @@ -766,16 +766,16 @@ static void rtl_p2p_noa_ie(struct ieee80211_hw *hw, void *data, index = 5; for (i = 0; i < noa_num; i++) { p2pinfo->noa_count_type[i] = - READEF1BYTE(ie+index); + *(u8 *)(ie + index); index += 1; p2pinfo->noa_duration[i] = - READEF4BYTE((__le32 *)ie+index); + le32_to_cpu(*(__le32 *)ie + index); index += 4; p2pinfo->noa_interval[i] = - READEF4BYTE((__le32 *)ie+index); + le32_to_cpu(*(__le32 *)ie + index); index += 4; p2pinfo->noa_start_time[i] = - READEF4BYTE((__le32 *)ie+index); + le32_to_cpu(*(__le32 *)ie + index); index += 4; } @@ -832,7 +832,7 @@ static void rtl_p2p_action_ie(struct ieee80211_hw *hw, void *data, RT_TRACE(rtlpriv, COMP_FW, DBG_LOUD, "action frame find P2P IE.\n"); /*to find noa ie*/ while (ie + 1 < end) { - noa_len = READEF2BYTE((__le16 *)&ie[1]); + noa_len = le16_to_cpu(*(__le16 *)&ie[1]); if (ie + 3 + ie[1] > end) return; @@ -861,16 +861,16 @@ static void rtl_p2p_action_ie(struct ieee80211_hw *hw, void *data, index = 5; for (i = 0; i < noa_num; i++) { p2pinfo->noa_count_type[i] = - READEF1BYTE(ie+index); + *(u8 *)(ie + index); index += 1; p2pinfo->noa_duration[i] = - READEF4BYTE((__le32 *)ie+index); + le32_to_cpu(*(__le32 *)ie + index); index += 4; p2pinfo->noa_interval[i] = - READEF4BYTE((__le32 *)ie+index); + le32_to_cpu(*(__le32 *)ie + index); index += 4; p2pinfo->noa_start_time[i] = - READEF4BYTE((__le32 *)ie+index); + le32_to_cpu(*(__le32 *)ie + index); index += 4; } -- GitLab From 39f40710d0b5231e1f4adca16be4da0dcf6434cb Mon Sep 17 00:00:00 2001 From: Larry Finger Date: Mon, 25 Nov 2019 16:09:30 -0600 Subject: [PATCH 0942/1437] rtlwifi: rtl88821ae: Remove usage of private bit manipulation macros Besides the previously changed macros for the RX and TX descriptors. the local macros are used in other places. These are replaced with standard bit manipulation macros. Signed-off-by: Larry Finger Signed-off-by: Kalle Valo --- .../wireless/realtek/rtlwifi/rtl8821ae/fw.h | 102 ++++++++---------- 1 file changed, 47 insertions(+), 55 deletions(-) diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/fw.h b/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/fw.h index e11e496b72775..c269942b3f4ab 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/fw.h +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/fw.h @@ -151,123 +151,115 @@ enum rtl8821a_h2c_cmd { #define pagenum_128(_len) (u32)(((_len)>>7) + ((_len)&0x7F ? 1 : 0)) #define SET_8812_H2CCMD_WOWLAN_FUNC_ENABLE(__cmd, __value) \ - SET_BITS_TO_LE_1BYTE(__cmd, 0, 1, __value) + u8p_replace_bits(__cmd, __value, BIT(0)) #define SET_8812_H2CCMD_WOWLAN_PATTERN_MATCH_ENABLE(__cmd, __value) \ - SET_BITS_TO_LE_1BYTE(__cmd, 1, 1, __value) + u8p_replace_bits(__cmd, __value, BIT(1)) #define SET_8812_H2CCMD_WOWLAN_MAGIC_PKT_ENABLE(__cmd, __value) \ - SET_BITS_TO_LE_1BYTE(__cmd, 2, 1, __value) + u8p_replace_bits(__cmd, __value, BIT(2)) #define SET_8812_H2CCMD_WOWLAN_UNICAST_PKT_ENABLE(__cmd, __value) \ - SET_BITS_TO_LE_1BYTE(__cmd, 3, 1, __value) + u8p_replace_bits(__cmd, __value, BIT(3)) #define SET_8812_H2CCMD_WOWLAN_ALL_PKT_DROP(__cmd, __value) \ - SET_BITS_TO_LE_1BYTE(__cmd, 4, 1, __value) + u8p_replace_bits(__cmd, __value, BIT(4)) #define SET_8812_H2CCMD_WOWLAN_GPIO_ACTIVE(__cmd, __value) \ - SET_BITS_TO_LE_1BYTE(__cmd, 5, 1, __value) + u8p_replace_bits(__cmd, __value, BIT(5)) #define SET_8812_H2CCMD_WOWLAN_REKEY_WAKE_UP(__cmd, __value) \ - SET_BITS_TO_LE_1BYTE(__cmd, 6, 1, __value) + u8p_replace_bits(__cmd, __value, BIT(6)) #define SET_8812_H2CCMD_WOWLAN_DISCONNECT_WAKE_UP(__cmd, __value) \ - SET_BITS_TO_LE_1BYTE(__cmd, 7, 1, __value) + u8p_replace_bits(__cmd, __value, BIT(7)) #define SET_8812_H2CCMD_WOWLAN_GPIONUM(__cmd, __value) \ - SET_BITS_TO_LE_1BYTE((__cmd) + 1, 0, 8, __value) + *(u8 *)(__cmd + 1) = __value #define SET_8812_H2CCMD_WOWLAN_GPIO_DURATION(__cmd, __value) \ - SET_BITS_TO_LE_1BYTE((__cmd) + 2, 0, 8, __value) + *(u8 *)(__cmd + 2) = __value #define SET_H2CCMD_PWRMODE_PARM_MODE(__ph2ccmd, __val) \ - SET_BITS_TO_LE_1BYTE(__ph2ccmd, 0, 8, __val) + *(u8 *)__ph2ccmd = __val #define SET_H2CCMD_PWRMODE_PARM_RLBM(__cmd, __value) \ - SET_BITS_TO_LE_1BYTE((__cmd)+1, 0, 4, __value) + u8p_replace_bits(__cmd + 1, __value, GENMASK(3, 0)) #define SET_H2CCMD_PWRMODE_PARM_SMART_PS(__cmd, __value) \ - SET_BITS_TO_LE_1BYTE((__cmd)+1, 4, 4, __value) + u8p_replace_bits(__cmd + 1, __value, GENMASK(7, 4)) #define SET_H2CCMD_PWRMODE_PARM_AWAKE_INTERVAL(__cmd, __value) \ - SET_BITS_TO_LE_1BYTE((__cmd)+2, 0, 8, __value) + *(u8 *)(__cmd + 2) = __value #define SET_H2CCMD_PWRMODE_PARM_ALL_QUEUE_UAPSD(__cmd, __value) \ - SET_BITS_TO_LE_1BYTE((__cmd)+3, 0, 8, __value) + *(u8 *)(__cmd + 3) = __value #define SET_H2CCMD_PWRMODE_PARM_PWR_STATE(__cmd, __value) \ - SET_BITS_TO_LE_1BYTE((__cmd)+4, 0, 8, __value) + *(u8 *)(__cmd + 4) = __value #define SET_H2CCMD_PWRMODE_PARM_BYTE5(__cmd, __value) \ - SET_BITS_TO_LE_1BYTE((__cmd) + 5, 0, 8, __value) -#define GET_8821AE_H2CCMD_PWRMODE_PARM_MODE(__cmd) \ - LE_BITS_TO_1BYTE(__cmd, 0, 8) + *(u8 *)(__cmd + 5) = __value -#define SET_H2CCMD_JOINBSSRPT_PARM_OPMODE(__ph2ccmd, __val) \ - SET_BITS_TO_LE_1BYTE(__ph2ccmd, 0, 8, __val) #define SET_H2CCMD_RSVDPAGE_LOC_PSPOLL(__ph2ccmd, __val) \ - SET_BITS_TO_LE_1BYTE((__ph2ccmd)+1, 0, 8, __val) + *(u8 *)(__ph2ccmd + 1) = __val #define SET_H2CCMD_RSVDPAGE_LOC_NULL_DATA(__ph2ccmd, __val) \ - SET_BITS_TO_LE_1BYTE((__ph2ccmd)+2, 0, 8, __val) + *(u8 *)(__ph2ccmd + 2) = __val #define SET_H2CCMD_RSVDPAGE_LOC_QOS_NULL_DATA(__ph2ccmd, __val) \ - SET_BITS_TO_LE_1BYTE((__ph2ccmd)+3, 0, 8, __val) + *(u8 *)(__ph2ccmd + 3) = __val #define SET_H2CCMD_RSVDPAGE_LOC_BT_QOS_NULL_DATA(__ph2ccmd, __val) \ - SET_BITS_TO_LE_1BYTE((__ph2ccmd) + 4, 0, 8, __val) + *(u8 *)(__ph2ccmd + 4) = __val /* _MEDIA_STATUS_RPT_PARM_CMD1 */ #define SET_H2CCMD_MSRRPT_PARM_OPMODE(__cmd, __value) \ - SET_BITS_TO_LE_1BYTE(__cmd, 0, 1, __value) + u8p_replace_bits(__cmd + 1, __value, BIT(0)) #define SET_H2CCMD_MSRRPT_PARM_MACID_IND(__cmd, __value) \ - SET_BITS_TO_LE_1BYTE(__cmd, 1, 1, __value) -#define SET_H2CCMD_MSRRPT_PARM_MACID(__cmd, __value) \ - SET_BITS_TO_LE_1BYTE(__cmd+1, 0, 8, __value) -#define SET_H2CCMD_MSRRPT_PARM_MACID_END(__cmd, __value) \ - SET_BITS_TO_LE_1BYTE(__cmd+2, 0, 8, __value) + u8p_replace_bits(__cmd + 1, __value, BIT(1)) /* AP_OFFLOAD */ #define SET_H2CCMD_AP_OFFLOAD_ON(__cmd, __value) \ - SET_BITS_TO_LE_1BYTE(__cmd, 0, 8, __value) + *(u8 *)__cmd = __value #define SET_H2CCMD_AP_OFFLOAD_HIDDEN(__cmd, __value) \ - SET_BITS_TO_LE_1BYTE((__cmd)+1, 0, 8, __value) + *(u8 *)(__cmd + 1) = __value #define SET_H2CCMD_AP_OFFLOAD_DENYANY(__cmd, __value) \ - SET_BITS_TO_LE_1BYTE((__cmd)+2, 0, 8, __value) + *(u8 *)(__cmd + 2) = __value #define SET_H2CCMD_AP_OFFLOAD_WAKEUP_EVT_RPT(__cmd, __value) \ - SET_BITS_TO_LE_1BYTE((__cmd)+3, 0, 8, __value) + *(u8 *)(__cmd + 3) = __value /* Keep Alive Control*/ #define SET_8812_H2CCMD_KEEP_ALIVE_ENABLE(__cmd, __value) \ - SET_BITS_TO_LE_1BYTE(__cmd, 0, 1, __value) + u8p_replace_bits(__cmd, __value, BIT(0)) #define SET_8812_H2CCMD_KEEP_ALIVE_ACCPEPT_USER_DEFINED(__cmd, __value) \ - SET_BITS_TO_LE_1BYTE(__cmd, 1, 1, __value) + u8p_replace_bits(__cmd, __value, BIT(1)) #define SET_8812_H2CCMD_KEEP_ALIVE_PERIOD(__cmd, __value) \ - SET_BITS_TO_LE_1BYTE((__cmd)+1, 0, 8, __value) + *(u8 *)(__cmd + 1) = __value /*REMOTE_WAKE_CTRL */ #define SET_8812_H2CCMD_REMOTE_WAKECTRL_ENABLE(__cmd, __value) \ - SET_BITS_TO_LE_1BYTE(__cmd, 0, 1, __value) + u8p_replace_bits(__cmd, __value, BIT(0)) #define SET_8812_H2CCMD_REMOTE_WAKE_CTRL_ARP_OFFLOAD_EN(__cmd, __value)\ - SET_BITS_TO_LE_1BYTE(__cmd, 1, 1, __value) + u8p_replace_bits(__cmd, __value, BIT(1)) #define SET_8812_H2CCMD_REMOTE_WAKE_CTRL_NDP_OFFLOAD_EN(__cmd, __value)\ - SET_BITS_TO_LE_1BYTE(__cmd, 2, 1, __value) + u8p_replace_bits(__cmd, __value, BIT(2)) #define SET_8812_H2CCMD_REMOTE_WAKE_CTRL_GTK_OFFLOAD_EN(__cmd, __value)\ - SET_BITS_TO_LE_1BYTE(__cmd, 3, 1, __value) + u8p_replace_bits(__cmd, __value, BIT(3)) #define SET_8812_H2CCMD_REMOTE_WAKE_CTRL_REALWOWV2_EN(__cmd, __value)\ - SET_BITS_TO_LE_1BYTE(__cmd, 6, 1, __value) + u8p_replace_bits(__cmd, __value, BIT(6)) /* GTK_OFFLOAD */ #define SET_8812_H2CCMD_AOAC_GLOBAL_INFO_PAIRWISE_ENC_ALG(__cmd, __value)\ - SET_BITS_TO_LE_1BYTE(__cmd, 0, 8, __value) + *(u8 *)__cmd = __value #define SET_8812_H2CCMD_AOAC_GLOBAL_INFO_GROUP_ENC_ALG(__cmd, __value) \ - SET_BITS_TO_LE_1BYTE((__cmd)+1, 0, 8, __value) + *(u8 *)(__cmd + 1) = __value /* AOAC_RSVDPAGE_LOC */ #define SET_8821AE_H2CCMD_AOAC_RSVDPAGE_LOC_REMOTE_WAKE_CTRL_INFO(__cmd, __value) \ - SET_BITS_TO_LE_1BYTE((__cmd), 0, 8, __value) + *(u8 *)__cmd = __value #define SET_8821AE_H2CCMD_AOAC_RSVDPAGE_LOC_ARP_RSP(__cmd, __value) \ - SET_BITS_TO_LE_1BYTE((__cmd)+1, 0, 8, __value) + *(u8 *)(__cmd + 1) = __value #define SET_8821AE_H2CCMD_AOAC_RSVDPAGE_LOC_NEIGHBOR_ADV(__cmd, __value)\ - SET_BITS_TO_LE_1BYTE((__cmd)+2, 0, 8, __value) + *(u8 *)(__cmd + 2) = __value #define SET_8821AE_H2CCMD_AOAC_RSVDPAGE_LOC_GTK_RSP(__cmd, __value) \ - SET_BITS_TO_LE_1BYTE((__cmd)+3, 0, 8, __value) + *(u8 *)(__cmd + 3) = __value #define SET_8821AE_H2CCMD_AOAC_RSVDPAGE_LOC_GTK_INFO(__cmd, __value) \ - SET_BITS_TO_LE_1BYTE((__cmd)+4, 0, 8, __value) + *(u8 *)(__cmd + 4) = __value #define SET_8821AE_H2CCMD_AOAC_RSVDPAGE_LOC_GTK_EXT_MEM(__cmd, __value) \ - SET_BITS_TO_LE_1BYTE((__cmd)+5, 0, 8, __value) + *(u8 *)(__cmd + 5) = __value /* Disconnect_Decision_Control */ #define SET_8812_H2CCMD_DISCONNECT_DECISION_CTRL_ENABLE(__cmd, __value) \ - SET_BITS_TO_LE_1BYTE(__cmd, 0, 1, __value) + u8p_replace_bits(__cmd, __value, BIT(0)) #define SET_8812_H2CCMD_DISCONNECT_DECISION_CTRL_USER_SETTING(__cmd, __value)\ - SET_BITS_TO_LE_1BYTE(__cmd, 1, 1, __value) + u8p_replace_bits(__cmd, __value, BIT(1)) #define SET_8812_H2CCMD_DISCONNECT_DECISION_CTRL_CHECK_PERIOD(__cmd, __value)\ - SET_BITS_TO_LE_1BYTE((__cmd)+1, 0, 8, __value) /* unit: beacon period */ + *(u8 *)(__cmd + 1) = __value #define SET_8812_H2CCMD_DISCONNECT_DECISION_CTRL_TRYPKT_NUM(__cmd, __value)\ - SET_BITS_TO_LE_1BYTE((__cmd)+2, 0, 8, __value) + *(u8 *)(__cmd + 2) = __value int rtl8821ae_download_fw(struct ieee80211_hw *hw, bool buse_wake_on_wlan_fw); #if (USE_SPECIFIC_FW_TO_SUPPORT_WOWLAN == 1) -- GitLab From 8e345762126374412e4ce783bf979c4fad0ea725 Mon Sep 17 00:00:00 2001 From: Larry Finger Date: Mon, 25 Nov 2019 16:09:31 -0600 Subject: [PATCH 0943/1437] rtlwifi: rtl8192ee: Remove usage of private bit manipulation macros Besides the previously changed macros for the RX and TX descriptors. the local macros are used in other places. These are replaced with standard bit manipulation macros. Signed-off-by: Larry Finger Signed-off-by: Kalle Valo --- .../wireless/realtek/rtlwifi/rtl8192ee/fw.h | 36 +++++++++---------- 1 file changed, 16 insertions(+), 20 deletions(-) diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192ee/fw.h b/drivers/net/wireless/realtek/rtlwifi/rtl8192ee/fw.h index cbfecea232a3b..27ac4f8b9a9bc 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192ee/fw.h +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192ee/fw.h @@ -111,44 +111,40 @@ enum rtl8192e_h2c_cmd { (u32)(((_len) >> 7) + ((_len) & 0x7F ? 1 : 0)) #define SET_H2CCMD_PWRMODE_PARM_MODE(__ph2ccmd, __val) \ - SET_BITS_TO_LE_1BYTE(__ph2ccmd, 0, 8, __val) + *(u8 *)__ph2ccmd = __val; #define SET_H2CCMD_PWRMODE_PARM_RLBM(__cmd, __val) \ - SET_BITS_TO_LE_1BYTE((__cmd)+1, 0, 4, __val) + u8p_replace_bits(__cmd + 1, __val, GENMASK(3, 0)) #define SET_H2CCMD_PWRMODE_PARM_SMART_PS(__cmd, __val) \ - SET_BITS_TO_LE_1BYTE((__cmd)+1, 4, 4, __val) + u8p_replace_bits(__cmd + 1, __val, GENMASK(7, 4)) #define SET_H2CCMD_PWRMODE_PARM_AWAKE_INTERVAL(__cmd, __val) \ - SET_BITS_TO_LE_1BYTE((__cmd)+2, 0, 8, __val) + *(u8 *)(__cmd + 2) = __val; #define SET_H2CCMD_PWRMODE_PARM_ALL_QUEUE_UAPSD(__cmd, __val) \ - SET_BITS_TO_LE_1BYTE((__cmd)+3, 0, 8, __val) + *(u8 *)(__cmd + 3) = __val; #define SET_H2CCMD_PWRMODE_PARM_PWR_STATE(__cmd, __val) \ - SET_BITS_TO_LE_1BYTE((__cmd)+4, 0, 8, __val) + *(u8 *)(__cmd + 4) = __val; #define SET_H2CCMD_PWRMODE_PARM_BYTE5(__cmd, __val) \ - SET_BITS_TO_LE_1BYTE((__cmd) + 5, 0, 8, __val) -#define GET_92E_H2CCMD_PWRMODE_PARM_MODE(__cmd) \ - LE_BITS_TO_1BYTE(__cmd, 0, 8) + *(u8 *)(__cmd + 5) = __val; -#define SET_H2CCMD_JOINBSSRPT_PARM_OPMODE(__ph2ccmd, __val) \ - SET_BITS_TO_LE_1BYTE(__ph2ccmd, 0, 8, __val) #define SET_H2CCMD_RSVDPAGE_LOC_PROBE_RSP(__ph2ccmd, __val) \ - SET_BITS_TO_LE_1BYTE(__ph2ccmd, 0, 8, __val) + *(u8 *)__ph2ccmd = __val; #define SET_H2CCMD_RSVDPAGE_LOC_PSPOLL(__ph2ccmd, __val) \ - SET_BITS_TO_LE_1BYTE((__ph2ccmd)+1, 0, 8, __val) + *(u8 *)(__ph2ccmd + 1) = __val; #define SET_H2CCMD_RSVDPAGE_LOC_NULL_DATA(__ph2ccmd, __val) \ - SET_BITS_TO_LE_1BYTE((__ph2ccmd)+2, 0, 8, __val) + *(u8 *)(__ph2ccmd + 2) = __val; #define SET_H2CCMD_RSVDPAGE_LOC_QOS_NULL_DATA(__ph2ccmd, __val) \ - SET_BITS_TO_LE_1BYTE((__ph2ccmd) + 3, 0, 8, __val) + *(u8 *)(__ph2ccmd + 3) = __val; #define SET_H2CCMD_RSVDPAGE_LOC_BT_QOS_NULL_DATA(__ph2ccmd, __val) \ - SET_BITS_TO_LE_1BYTE((__ph2ccmd) + 4, 0, 8, __val) + *(u8 *)(__ph2ccmd + 4) = __val; /* _MEDIA_STATUS_RPT_PARM_CMD1 */ #define SET_H2CCMD_MSRRPT_PARM_OPMODE(__cmd, __val) \ - SET_BITS_TO_LE_1BYTE(__cmd, 0, 1, __val) + u8p_replace_bits(__cmd, __val, BIT(0)) #define SET_H2CCMD_MSRRPT_PARM_MACID_IND(__cmd, __val) \ - SET_BITS_TO_LE_1BYTE(__cmd, 1, 1, __val) + u8p_replace_bits(__cmd, __val, BIT(1)) #define SET_H2CCMD_MSRRPT_PARM_MACID(__cmd, __val) \ - SET_BITS_TO_LE_1BYTE(__cmd+1, 0, 8, __val) + *(u8 *)(__ph2ccmd + 1) = __val; #define SET_H2CCMD_MSRRPT_PARM_MACID_END(__cmd, __val) \ - SET_BITS_TO_LE_1BYTE(__cmd+2, 0, 8, __val) + *(u8 *)(__ph2ccmd + 2) = __val; int rtl92ee_download_fw(struct ieee80211_hw *hw, bool buse_wake_on_wlan_fw); void rtl92ee_fill_h2c_cmd(struct ieee80211_hw *hw, u8 element_id, -- GitLab From 1626f192957552bd567261ad5103e7bd033e9ef0 Mon Sep 17 00:00:00 2001 From: Larry Finger Date: Mon, 25 Nov 2019 16:09:32 -0600 Subject: [PATCH 0944/1437] rtlwifi: rtl8723ae: Remove usage of private bit manipulation macros Besides the previously changed macros for the RX and TX descriptors. the local macros are used in other places. These are replaced with standard bit manipulation macros. Signed-off-by: Larry Finger Signed-off-by: Kalle Valo --- .../net/wireless/realtek/rtlwifi/rtl8723ae/fw.h | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8723ae/fw.h b/drivers/net/wireless/realtek/rtlwifi/rtl8723ae/fw.h index 5c843736de8de..3f9ed9b4428e6 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8723ae/fw.h +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8723ae/fw.h @@ -18,19 +18,19 @@ #define pagenum_128(_len) (u32)(((_len)>>7) + ((_len)&0x7F ? 1 : 0)) #define SET_H2CCMD_PWRMODE_PARM_MODE(__ph2ccmd, __val) \ - SET_BITS_TO_LE_1BYTE(__ph2ccmd, 0, 8, __val) + *(u8 *)__ph2ccmd = __val #define SET_H2CCMD_PWRMODE_PARM_SMART_PS(__ph2ccmd, __val) \ - SET_BITS_TO_LE_1BYTE((__ph2ccmd)+1, 0, 8, __val) + *(u8 *)(__ph2ccmd + 1) = __val #define SET_H2CCMD_PWRMODE_PARM_BCN_PASS_TIME(__ph2ccmd, __val) \ - SET_BITS_TO_LE_1BYTE((__ph2ccmd)+2, 0, 8, __val) + *(u8 *)(__ph2ccmd + 2) = __val #define SET_H2CCMD_JOINBSSRPT_PARM_OPMODE(__ph2ccmd, __val) \ - SET_BITS_TO_LE_1BYTE(__ph2ccmd, 0, 8, __val) + *(u8 *)__ph2ccmd = __val #define SET_H2CCMD_RSVDPAGE_LOC_PROBE_RSP(__ph2ccmd, __val) \ - SET_BITS_TO_LE_1BYTE(__ph2ccmd, 0, 8, __val) + *(u8 *)__ph2ccmd = __val #define SET_H2CCMD_RSVDPAGE_LOC_PSPOLL(__ph2ccmd, __val) \ - SET_BITS_TO_LE_1BYTE((__ph2ccmd)+1, 0, 8, __val) + *(u8 *)(__ph2ccmd + 1) = __val #define SET_H2CCMD_RSVDPAGE_LOC_NULL_DATA(__ph2ccmd, __val) \ - SET_BITS_TO_LE_1BYTE((__ph2ccmd)+2, 0, 8, __val) + *(u8 *)(__ph2ccmd + 2) = __val void rtl8723e_fill_h2c_cmd(struct ieee80211_hw *hw, u8 element_id, u32 cmd_len, u8 *p_cmdbuffer); -- GitLab From 84bc9a58326a037a9472d8d899a22159542d706e Mon Sep 17 00:00:00 2001 From: Larry Finger Date: Mon, 25 Nov 2019 16:09:33 -0600 Subject: [PATCH 0945/1437] rtlwifi: rtl8723be: Remove usage of private bit manipulation macros Besides the previously changed macros for the RX and TX descriptors. the local macros are used in other places. These are replaced with standard bit manipulation macros. Signed-off-by: Larry Finger Signed-off-by: Kalle Valo --- .../wireless/realtek/rtlwifi/rtl8723be/fw.h | 30 +++++++++---------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8723be/fw.h b/drivers/net/wireless/realtek/rtlwifi/rtl8723be/fw.h index 97ea77464139b..7c5e5e9162747 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8723be/fw.h +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8723be/fw.h @@ -83,37 +83,35 @@ enum rtl8723b_h2c_cmd { #define SET_H2CCMD_PWRMODE_PARM_MODE(__ph2ccmd, __val) \ - SET_BITS_TO_LE_1BYTE(__ph2ccmd, 0, 8, __val) + *(u8 *)__ph2ccmd = __val #define SET_H2CCMD_PWRMODE_PARM_RLBM(__ph2ccmd, __val) \ - SET_BITS_TO_LE_1BYTE((__ph2ccmd)+1, 0, 4, __val) + u8p_replace_bits(__ph2ccmd + 1, __val, GENMASK(3, 0)) #define SET_H2CCMD_PWRMODE_PARM_SMART_PS(__ph2ccmd, __val) \ - SET_BITS_TO_LE_1BYTE((__ph2ccmd)+1, 4, 4, __val) + u8p_replace_bits(__ph2ccmd + 1, __val, GENMASK(7, 4)) #define SET_H2CCMD_PWRMODE_PARM_AWAKE_INTERVAL(__ph2ccmd, __val) \ - SET_BITS_TO_LE_1BYTE((__ph2ccmd)+2, 0, 8, __val) + *(u8 *)(__ph2ccmd + 2) = __val #define SET_H2CCMD_PWRMODE_PARM_ALL_QUEUE_UAPSD(__ph2ccmd, __val) \ - SET_BITS_TO_LE_1BYTE((__ph2ccmd)+3, 0, 8, __val) + *(u8 *)(__ph2ccmd + 3) = __val #define SET_H2CCMD_PWRMODE_PARM_PWR_STATE(__ph2ccmd, __val) \ - SET_BITS_TO_LE_1BYTE((__ph2ccmd)+4, 0, 8, __val) + *(u8 *)(__ph2ccmd + 4) = __val #define SET_H2CCMD_PWRMODE_PARM_BYTE5(__ph2ccmd, __val) \ - SET_BITS_TO_LE_1BYTE((__ph2ccmd) + 5, 0, 8, __val) -#define GET_88E_H2CCMD_PWRMODE_PARM_MODE(__ph2ccmd) \ - LE_BITS_TO_1BYTE(__ph2ccmd, 0, 8) + *(u8 *)(__ph2ccmd + 5) = __val #define SET_H2CCMD_MSRRPT_PARM_OPMODE(__ph2ccmd, __val) \ - SET_BITS_TO_LE_1BYTE(__ph2ccmd, 0, 1, __val) + u8p_replace_bits(__ph2ccmd, __val, BIT(0)) #define SET_H2CCMD_MSRRPT_PARM_MACID_IND(__ph2ccmd, __val) \ - SET_BITS_TO_LE_1BYTE(__ph2ccmd, 1, 1, __val) + u8p_replace_bits(__ph2ccmd, __val, BIT(1)) #define SET_H2CCMD_RSVDPAGE_LOC_PROBE_RSP(__ph2ccmd, __val) \ - SET_BITS_TO_LE_1BYTE(__ph2ccmd, 0, 8, __val) + *(u8 *)(__ph2ccmd) = __val #define SET_H2CCMD_RSVDPAGE_LOC_PSPOLL(__ph2ccmd, __val) \ - SET_BITS_TO_LE_1BYTE((__ph2ccmd)+1, 0, 8, __val) + *(u8 *)(__ph2ccmd + 1) = __val #define SET_H2CCMD_RSVDPAGE_LOC_NULL_DATA(__ph2ccmd, __val) \ - SET_BITS_TO_LE_1BYTE((__ph2ccmd)+2, 0, 8, __val) + *(u8 *)(__ph2ccmd + 2) = __val #define SET_H2CCMD_RSVDPAGE_LOC_QOS_NULL_DATA(__ph2ccmd, __val) \ - SET_BITS_TO_LE_1BYTE((__ph2ccmd) + 3, 0, 8, __val) + *(u8 *)(__ph2ccmd + 3) = __val #define SET_H2CCMD_RSVDPAGE_LOC_BT_QOS_NULL_DATA(__ph2ccmd, __val) \ - SET_BITS_TO_LE_1BYTE((__ph2ccmd) + 4, 0, 8, __val) + *(u8 *)(__ph2ccmd + 4) = __val void rtl8723be_fill_h2c_cmd(struct ieee80211_hw *hw, u8 element_id, -- GitLab From bb7e6d85536b4f1bfbc15e21dab829bf1377c8f8 Mon Sep 17 00:00:00 2001 From: Larry Finger Date: Mon, 25 Nov 2019 16:09:34 -0600 Subject: [PATCH 0946/1437] rtlwifi: Remove last definitions of local bit manipulation macros All usage of the local macros have been removed, thus the remaining definitions are deleted. Signed-off-by: Larry Finger Signed-off-by: Kalle Valo --- drivers/net/wireless/realtek/rtlwifi/wifi.h | 115 -------------------- 1 file changed, 115 deletions(-) diff --git a/drivers/net/wireless/realtek/rtlwifi/wifi.h b/drivers/net/wireless/realtek/rtlwifi/wifi.h index 3bdda1c983394..1cff9f07c9e99 100644 --- a/drivers/net/wireless/realtek/rtlwifi/wifi.h +++ b/drivers/net/wireless/realtek/rtlwifi/wifi.h @@ -2898,121 +2898,6 @@ enum bt_radio_shared { * 2. Before write integer to IO. * 3. After read integer from IO. ****************************************/ -/* Convert little data endian to host ordering */ -#define EF1BYTE(_val) \ - ((u8)(_val)) -#define EF2BYTE(_val) \ - (le16_to_cpu(_val)) -#define EF4BYTE(_val) \ - (le32_to_cpu(_val)) - -/* Read data from memory */ -#define READEF1BYTE(_ptr) \ - EF1BYTE(*((u8 *)(_ptr))) -/* Read le16 data from memory and convert to host ordering */ -#define READEF2BYTE(_ptr) \ - EF2BYTE(*(_ptr)) -#define READEF4BYTE(_ptr) \ - EF4BYTE(*(_ptr)) - -/* Create a bit mask - * Examples: - * BIT_LEN_MASK_32(0) => 0x00000000 - * BIT_LEN_MASK_32(1) => 0x00000001 - * BIT_LEN_MASK_32(2) => 0x00000003 - * BIT_LEN_MASK_32(32) => 0xFFFFFFFF - */ -#define BIT_LEN_MASK_32(__bitlen) \ - (0xFFFFFFFF >> (32 - (__bitlen))) -#define BIT_LEN_MASK_16(__bitlen) \ - (0xFFFF >> (16 - (__bitlen))) -#define BIT_LEN_MASK_8(__bitlen) \ - (0xFF >> (8 - (__bitlen))) - -/* Create an offset bit mask - * Examples: - * BIT_OFFSET_LEN_MASK_32(0, 2) => 0x00000003 - * BIT_OFFSET_LEN_MASK_32(16, 2) => 0x00030000 - */ -#define BIT_OFFSET_LEN_MASK_32(__bitoffset, __bitlen) \ - (BIT_LEN_MASK_32(__bitlen) << (__bitoffset)) -#define BIT_OFFSET_LEN_MASK_16(__bitoffset, __bitlen) \ - (BIT_LEN_MASK_16(__bitlen) << (__bitoffset)) -#define BIT_OFFSET_LEN_MASK_8(__bitoffset, __bitlen) \ - (BIT_LEN_MASK_8(__bitlen) << (__bitoffset)) - -/*Description: - * Return 4-byte value in host byte ordering from - * 4-byte pointer in little-endian system. - */ -#define LE_P4BYTE_TO_HOST_4BYTE(__pstart) \ - (EF4BYTE(*((__le32 *)(__pstart)))) -#define LE_P2BYTE_TO_HOST_2BYTE(__pstart) \ - (EF2BYTE(*((__le16 *)(__pstart)))) -#define LE_P1BYTE_TO_HOST_1BYTE(__pstart) \ - (EF1BYTE(*((u8 *)(__pstart)))) - -/*Description: - * Translate subfield (continuous bits in little-endian) of 4-byte - * value to host byte ordering. - */ -#define LE_BITS_TO_4BYTE(__pstart, __bitoffset, __bitlen) \ - ( \ - (LE_P4BYTE_TO_HOST_4BYTE(__pstart) >> (__bitoffset)) & \ - BIT_LEN_MASK_32(__bitlen) \ - ) -#define LE_BITS_TO_2BYTE(__pstart, __bitoffset, __bitlen) \ - ( \ - (LE_P2BYTE_TO_HOST_2BYTE(__pstart) >> (__bitoffset)) & \ - BIT_LEN_MASK_16(__bitlen) \ - ) -#define LE_BITS_TO_1BYTE(__pstart, __bitoffset, __bitlen) \ - ( \ - (LE_P1BYTE_TO_HOST_1BYTE(__pstart) >> (__bitoffset)) & \ - BIT_LEN_MASK_8(__bitlen) \ - ) - -/* Description: - * Mask subfield (continuous bits in little-endian) of 4-byte value - * and return the result in 4-byte value in host byte ordering. - */ -#define LE_BITS_CLEARED_TO_4BYTE(__pstart, __bitoffset, __bitlen) \ - ( \ - LE_P4BYTE_TO_HOST_4BYTE(__pstart) & \ - (~BIT_OFFSET_LEN_MASK_32(__bitoffset, __bitlen)) \ - ) -#define LE_BITS_CLEARED_TO_2BYTE(__pstart, __bitoffset, __bitlen) \ - ( \ - LE_P2BYTE_TO_HOST_2BYTE(__pstart) & \ - (~BIT_OFFSET_LEN_MASK_16(__bitoffset, __bitlen)) \ - ) -#define LE_BITS_CLEARED_TO_1BYTE(__pstart, __bitoffset, __bitlen) \ - ( \ - LE_P1BYTE_TO_HOST_1BYTE(__pstart) & \ - (~BIT_OFFSET_LEN_MASK_8(__bitoffset, __bitlen)) \ - ) - -/* Description: - * Set subfield of little-endian 4-byte value to specified value. - */ -#define SET_BITS_TO_LE_4BYTE(__pstart, __bitoffset, __bitlen, __val) \ - *((__le32 *)(__pstart)) = \ - cpu_to_le32( \ - LE_BITS_CLEARED_TO_4BYTE(__pstart, __bitoffset, __bitlen) | \ - ((((u32)__val) & BIT_LEN_MASK_32(__bitlen)) << (__bitoffset)) \ - ) -#define SET_BITS_TO_LE_2BYTE(__pstart, __bitoffset, __bitlen, __val) \ - *((__le16 *)(__pstart)) = \ - cpu_to_le16( \ - LE_BITS_CLEARED_TO_2BYTE(__pstart, __bitoffset, __bitlen) | \ - ((((u16)__val) & BIT_LEN_MASK_16(__bitlen)) << (__bitoffset)) \ - ) -#define SET_BITS_TO_LE_1BYTE(__pstart, __bitoffset, __bitlen, __val) \ - *((u8 *)(__pstart)) = EF1BYTE \ - ( \ - LE_BITS_CLEARED_TO_1BYTE(__pstart, __bitoffset, __bitlen) | \ - ((((u8)__val) & BIT_LEN_MASK_8(__bitlen)) << (__bitoffset)) \ - ) #define N_BYTE_ALIGMENT(__value, __aligment) ((__aligment == 1) ? \ (__value) : (((__value + __aligment - 1) / __aligment) * __aligment)) -- GitLab From 6056a0f8ede27b296d10ef46f7f677cc9d715371 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Tue, 17 Dec 2019 17:19:11 -0800 Subject: [PATCH 0947/1437] usb: xhci: Fix build warning seen with CONFIG_PM=n The following build warning is seen if CONFIG_PM is disabled. drivers/usb/host/xhci-pci.c:498:13: warning: unused function 'xhci_pci_shutdown' Fixes: f2c710f7dca8 ("usb: xhci: only set D3hot for pci device") Cc: Henry Lin Cc: stable@vger.kernel.org # all stable releases with f2c710f7dca8 Signed-off-by: Guenter Roeck Acked-by: Mathias Nyman Link: https://lore.kernel.org/r/20191218011911.6907-1-linux@roeck-us.net Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-pci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c index 2907fe4d78ddd..4917c5b033fac 100644 --- a/drivers/usb/host/xhci-pci.c +++ b/drivers/usb/host/xhci-pci.c @@ -519,7 +519,6 @@ static int xhci_pci_resume(struct usb_hcd *hcd, bool hibernated) retval = xhci_resume(xhci, hibernated); return retval; } -#endif /* CONFIG_PM */ static void xhci_pci_shutdown(struct usb_hcd *hcd) { @@ -532,6 +531,7 @@ static void xhci_pci_shutdown(struct usb_hcd *hcd) if (xhci->quirks & XHCI_SPURIOUS_WAKEUP) pci_set_power_state(pdev, PCI_D3hot); } +#endif /* CONFIG_PM */ /*-------------------------------------------------------------------------*/ -- GitLab From 37bc6c72f5b76f3c6ae00e3001d3187100fbeb1b Mon Sep 17 00:00:00 2001 From: Austin Kim Date: Wed, 27 Nov 2019 14:43:58 +0900 Subject: [PATCH 0948/1437] brcmsmac: Remove always false 'channel < 0' statement As 'channel' is declared as u16, the following expression is always false. channel < 0 So we can remove unnecessary 'always false' statement. Signed-off-by: Austin Kim Signed-off-by: Kalle Valo --- drivers/net/wireless/broadcom/brcm80211/brcmsmac/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/main.c b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/main.c index 3f09d89ba9224..7f2c15c799d27 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/main.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/main.c @@ -5408,7 +5408,7 @@ int brcms_c_set_channel(struct brcms_c_info *wlc, u16 channel) { u16 chspec = ch20mhz_chspec(channel); - if (channel < 0 || channel > MAXCHANNEL) + if (channel > MAXCHANNEL) return -EINVAL; if (!brcms_c_valid_chanspec_db(wlc->cmi, chspec)) -- GitLab From 475eec112e4267232d10f4afe2f939a241692b6c Mon Sep 17 00:00:00 2001 From: Phong Tran Date: Wed, 27 Nov 2019 00:55:26 +0700 Subject: [PATCH 0949/1437] b43legacy: Fix -Wcast-function-type correct usage prototype of callback in tasklet_init(). Report by https://github.com/KSPP/linux/issues/20 Tested-by: Larry Finger Signed-off-by: Phong Tran Reviewed-by: Kees Cook Signed-off-by: Kalle Valo --- drivers/net/wireless/broadcom/b43legacy/main.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/broadcom/b43legacy/main.c b/drivers/net/wireless/broadcom/b43legacy/main.c index 4325e91736eb3..8b6b657c4b85b 100644 --- a/drivers/net/wireless/broadcom/b43legacy/main.c +++ b/drivers/net/wireless/broadcom/b43legacy/main.c @@ -1275,8 +1275,9 @@ static void handle_irq_ucode_debug(struct b43legacy_wldev *dev) } /* Interrupt handler bottom-half */ -static void b43legacy_interrupt_tasklet(struct b43legacy_wldev *dev) +static void b43legacy_interrupt_tasklet(unsigned long data) { + struct b43legacy_wldev *dev = (struct b43legacy_wldev *)data; u32 reason; u32 dma_reason[ARRAY_SIZE(dev->dma_reason)]; u32 merged_dma_reason = 0; @@ -3741,7 +3742,7 @@ static int b43legacy_one_core_attach(struct ssb_device *dev, b43legacy_set_status(wldev, B43legacy_STAT_UNINIT); wldev->bad_frames_preempt = modparam_bad_frames_preempt; tasklet_init(&wldev->isr_tasklet, - (void (*)(unsigned long))b43legacy_interrupt_tasklet, + b43legacy_interrupt_tasklet, (unsigned long)wldev); if (modparam_pio) wldev->__using_pio = true; -- GitLab From ebd77feb27e91bb5fe35a7818b7c13ea7435fb98 Mon Sep 17 00:00:00 2001 From: Phong Tran Date: Wed, 27 Nov 2019 00:55:27 +0700 Subject: [PATCH 0950/1437] ipw2x00: Fix -Wcast-function-type correct usage prototype of callback in tasklet_init(). Report by https://github.com/KSPP/linux/issues/20 Signed-off-by: Phong Tran Reviewed-by: Kees Cook Signed-off-by: Kalle Valo --- drivers/net/wireless/intel/ipw2x00/ipw2100.c | 7 ++++--- drivers/net/wireless/intel/ipw2x00/ipw2200.c | 5 +++-- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/intel/ipw2x00/ipw2100.c b/drivers/net/wireless/intel/ipw2x00/ipw2100.c index c4c83ab60cbc4..0579554ed4b38 100644 --- a/drivers/net/wireless/intel/ipw2x00/ipw2100.c +++ b/drivers/net/wireless/intel/ipw2x00/ipw2100.c @@ -3206,8 +3206,9 @@ static void ipw2100_tx_send_data(struct ipw2100_priv *priv) } } -static void ipw2100_irq_tasklet(struct ipw2100_priv *priv) +static void ipw2100_irq_tasklet(unsigned long data) { + struct ipw2100_priv *priv = (struct ipw2100_priv *)data; struct net_device *dev = priv->net_dev; unsigned long flags; u32 inta, tmp; @@ -6006,7 +6007,7 @@ static void ipw2100_rf_kill(struct work_struct *work) spin_unlock_irqrestore(&priv->low_lock, flags); } -static void ipw2100_irq_tasklet(struct ipw2100_priv *priv); +static void ipw2100_irq_tasklet(unsigned long data); static const struct net_device_ops ipw2100_netdev_ops = { .ndo_open = ipw2100_open, @@ -6136,7 +6137,7 @@ static struct net_device *ipw2100_alloc_device(struct pci_dev *pci_dev, INIT_DELAYED_WORK(&priv->rf_kill, ipw2100_rf_kill); INIT_DELAYED_WORK(&priv->scan_event, ipw2100_scan_event); - tasklet_init(&priv->irq_tasklet, (void (*)(unsigned long)) + tasklet_init(&priv->irq_tasklet, ipw2100_irq_tasklet, (unsigned long)priv); /* NOTE: We do not start the deferred work for status checks yet */ diff --git a/drivers/net/wireless/intel/ipw2x00/ipw2200.c b/drivers/net/wireless/intel/ipw2x00/ipw2200.c index 31e43fc1d12b3..5ef6f87a48ac7 100644 --- a/drivers/net/wireless/intel/ipw2x00/ipw2200.c +++ b/drivers/net/wireless/intel/ipw2x00/ipw2200.c @@ -1945,8 +1945,9 @@ static void notify_wx_assoc_event(struct ipw_priv *priv) wireless_send_event(priv->net_dev, SIOCGIWAP, &wrqu, NULL); } -static void ipw_irq_tasklet(struct ipw_priv *priv) +static void ipw_irq_tasklet(unsigned long data) { + struct ipw_priv *priv = (struct ipw_priv *)data; u32 inta, inta_mask, handled = 0; unsigned long flags; int rc = 0; @@ -10677,7 +10678,7 @@ static int ipw_setup_deferred_work(struct ipw_priv *priv) INIT_WORK(&priv->qos_activate, ipw_bg_qos_activate); #endif /* CONFIG_IPW2200_QOS */ - tasklet_init(&priv->irq_tasklet, (void (*)(unsigned long)) + tasklet_init(&priv->irq_tasklet, ipw_irq_tasklet, (unsigned long)priv); return ret; -- GitLab From da5e57e8a6a3e69dac2937ba63fa86355628fbb2 Mon Sep 17 00:00:00 2001 From: Phong Tran Date: Wed, 27 Nov 2019 00:55:28 +0700 Subject: [PATCH 0951/1437] iwlegacy: Fix -Wcast-function-type correct usage prototype of callback in tasklet_init(). Report by https://github.com/KSPP/linux/issues/20 Signed-off-by: Phong Tran Reviewed-by: Kees Cook Signed-off-by: Kalle Valo --- drivers/net/wireless/intel/iwlegacy/3945-mac.c | 5 +++-- drivers/net/wireless/intel/iwlegacy/4965-mac.c | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/intel/iwlegacy/3945-mac.c b/drivers/net/wireless/intel/iwlegacy/3945-mac.c index 1168055da1828..206b43b9dff86 100644 --- a/drivers/net/wireless/intel/iwlegacy/3945-mac.c +++ b/drivers/net/wireless/intel/iwlegacy/3945-mac.c @@ -1376,8 +1376,9 @@ il3945_dump_nic_error_log(struct il_priv *il) } static void -il3945_irq_tasklet(struct il_priv *il) +il3945_irq_tasklet(unsigned long data) { + struct il_priv *il = (struct il_priv *)data; u32 inta, handled = 0; u32 inta_fh; unsigned long flags; @@ -3401,7 +3402,7 @@ il3945_setup_deferred_work(struct il_priv *il) timer_setup(&il->watchdog, il_bg_watchdog, 0); tasklet_init(&il->irq_tasklet, - (void (*)(unsigned long))il3945_irq_tasklet, + il3945_irq_tasklet, (unsigned long)il); } diff --git a/drivers/net/wireless/intel/iwlegacy/4965-mac.c b/drivers/net/wireless/intel/iwlegacy/4965-mac.c index 3664f56f8cbd0..d1e17589dbeb7 100644 --- a/drivers/net/wireless/intel/iwlegacy/4965-mac.c +++ b/drivers/net/wireless/intel/iwlegacy/4965-mac.c @@ -4343,8 +4343,9 @@ il4965_synchronize_irq(struct il_priv *il) } static void -il4965_irq_tasklet(struct il_priv *il) +il4965_irq_tasklet(unsigned long data) { + struct il_priv *il = (struct il_priv *)data; u32 inta, handled = 0; u32 inta_fh; unsigned long flags; @@ -6237,7 +6238,7 @@ il4965_setup_deferred_work(struct il_priv *il) timer_setup(&il->watchdog, il_bg_watchdog, 0); tasklet_init(&il->irq_tasklet, - (void (*)(unsigned long))il4965_irq_tasklet, + il4965_irq_tasklet, (unsigned long)il); } -- GitLab From cb775c88da5d48a85d99d95219f637b6fad2e0e9 Mon Sep 17 00:00:00 2001 From: Phong Tran Date: Wed, 27 Nov 2019 00:55:29 +0700 Subject: [PATCH 0952/1437] rtlwifi: rtl_pci: Fix -Wcast-function-type correct usage prototype of callback in tasklet_init(). Report by https://github.com/KSPP/linux/issues/20 Signed-off-by: Phong Tran Reviewed-by: Kees Cook Signed-off-by: Kalle Valo --- drivers/net/wireless/realtek/rtlwifi/pci.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/realtek/rtlwifi/pci.c b/drivers/net/wireless/realtek/rtlwifi/pci.c index f88d26535978d..25335bd2873b6 100644 --- a/drivers/net/wireless/realtek/rtlwifi/pci.c +++ b/drivers/net/wireless/realtek/rtlwifi/pci.c @@ -1061,13 +1061,15 @@ done: return ret; } -static void _rtl_pci_irq_tasklet(struct ieee80211_hw *hw) +static void _rtl_pci_irq_tasklet(unsigned long data) { + struct ieee80211_hw *hw = (struct ieee80211_hw *)data; _rtl_pci_tx_chk_waitq(hw); } -static void _rtl_pci_prepare_bcn_tasklet(struct ieee80211_hw *hw) +static void _rtl_pci_prepare_bcn_tasklet(unsigned long data) { + struct ieee80211_hw *hw = (struct ieee80211_hw *)data; struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); @@ -1193,10 +1195,10 @@ static void _rtl_pci_init_struct(struct ieee80211_hw *hw, /*task */ tasklet_init(&rtlpriv->works.irq_tasklet, - (void (*)(unsigned long))_rtl_pci_irq_tasklet, + _rtl_pci_irq_tasklet, (unsigned long)hw); tasklet_init(&rtlpriv->works.irq_prepare_bcn_tasklet, - (void (*)(unsigned long))_rtl_pci_prepare_bcn_tasklet, + _rtl_pci_prepare_bcn_tasklet, (unsigned long)hw); INIT_WORK(&rtlpriv->works.lps_change_work, rtl_lps_change_work_callback); -- GitLab From f427939391f290cbeabe0231eb8a116429d823f0 Mon Sep 17 00:00:00 2001 From: yu kuai Date: Wed, 27 Nov 2019 20:44:33 +0800 Subject: [PATCH 0953/1437] bcma: remove set but not used variable 'sizel' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes gcc '-Wunused-but-set-variable' warning: drivers/bcma/scan.c: In function ‘bcma_erom_get_addr_desc’: drivers/bcma/scan.c:222:20: warning: variable ‘sizel’ set but not used [-Wunused-but-set-variable] It is never used, and so can be removed. Fixes: 8369ae33b705 ("bcma: add Broadcom specific AMBA bus driver") Signed-off-by: yu kuai Signed-off-by: Kalle Valo --- drivers/bcma/scan.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/drivers/bcma/scan.c b/drivers/bcma/scan.c index 4a2d1b235fb5a..1f2de714b4017 100644 --- a/drivers/bcma/scan.c +++ b/drivers/bcma/scan.c @@ -219,7 +219,7 @@ static s32 bcma_erom_get_mst_port(struct bcma_bus *bus, u32 __iomem **eromptr) static u32 bcma_erom_get_addr_desc(struct bcma_bus *bus, u32 __iomem **eromptr, u32 type, u8 port) { - u32 addrl, addrh, sizel, sizeh = 0; + u32 addrl, addrh, sizeh = 0; u32 size; u32 ent = bcma_erom_get_ent(bus, eromptr); @@ -239,12 +239,9 @@ static u32 bcma_erom_get_addr_desc(struct bcma_bus *bus, u32 __iomem **eromptr, if ((ent & SCAN_ADDR_SZ) == SCAN_ADDR_SZ_SZD) { size = bcma_erom_get_ent(bus, eromptr); - sizel = size & SCAN_SIZE_SZ; if (size & SCAN_SIZE_SG32) sizeh = bcma_erom_get_ent(bus, eromptr); - } else - sizel = SCAN_ADDR_SZ_BASE << - ((ent & SCAN_ADDR_SZ) >> SCAN_ADDR_SZ_SHIFT); + } return addrl; } -- GitLab From 57f9807d1ea2bd1154d9f58c39670fbc49b1975e Mon Sep 17 00:00:00 2001 From: Stanislaw Gruszka Date: Thu, 28 Nov 2019 10:11:23 +0100 Subject: [PATCH 0954/1437] rt2x00: implement reconfig_complete mac80211 has .reconfig_complete that can be used as indicator of HW restart end. This will allow to configure keys/IV differently for HW restart and normal configuration. Signed-off-by: Stanislaw Gruszka Signed-off-by: Kalle Valo --- .../net/wireless/ralink/rt2x00/rt2800pci.c | 1 + .../net/wireless/ralink/rt2x00/rt2800soc.c | 1 + .../net/wireless/ralink/rt2x00/rt2800usb.c | 1 + drivers/net/wireless/ralink/rt2x00/rt2x00.h | 2 ++ .../net/wireless/ralink/rt2x00/rt2x00dev.c | 11 ---------- .../net/wireless/ralink/rt2x00/rt2x00mac.c | 20 +++++++++++++++++++ 6 files changed, 25 insertions(+), 11 deletions(-) diff --git a/drivers/net/wireless/ralink/rt2x00/rt2800pci.c b/drivers/net/wireless/ralink/rt2x00/rt2800pci.c index a23c26574002a..3868c07672bdf 100644 --- a/drivers/net/wireless/ralink/rt2x00/rt2800pci.c +++ b/drivers/net/wireless/ralink/rt2x00/rt2800pci.c @@ -311,6 +311,7 @@ static const struct ieee80211_ops rt2800pci_mac80211_ops = { .get_survey = rt2800_get_survey, .get_ringparam = rt2x00mac_get_ringparam, .tx_frames_pending = rt2x00mac_tx_frames_pending, + .reconfig_complete = rt2x00mac_reconfig_complete, }; static const struct rt2800_ops rt2800pci_rt2800_ops = { diff --git a/drivers/net/wireless/ralink/rt2x00/rt2800soc.c b/drivers/net/wireless/ralink/rt2x00/rt2800soc.c index 7b931bb96a9e6..bbfe1425c0ee5 100644 --- a/drivers/net/wireless/ralink/rt2x00/rt2800soc.c +++ b/drivers/net/wireless/ralink/rt2x00/rt2800soc.c @@ -156,6 +156,7 @@ static const struct ieee80211_ops rt2800soc_mac80211_ops = { .get_survey = rt2800_get_survey, .get_ringparam = rt2x00mac_get_ringparam, .tx_frames_pending = rt2x00mac_tx_frames_pending, + .reconfig_complete = rt2x00mac_reconfig_complete, }; static const struct rt2800_ops rt2800soc_rt2800_ops = { diff --git a/drivers/net/wireless/ralink/rt2x00/rt2800usb.c b/drivers/net/wireless/ralink/rt2x00/rt2800usb.c index 0dfb55c69b738..4cc64fe481a71 100644 --- a/drivers/net/wireless/ralink/rt2x00/rt2800usb.c +++ b/drivers/net/wireless/ralink/rt2x00/rt2800usb.c @@ -654,6 +654,7 @@ static const struct ieee80211_ops rt2800usb_mac80211_ops = { .get_survey = rt2800_get_survey, .get_ringparam = rt2x00mac_get_ringparam, .tx_frames_pending = rt2x00mac_tx_frames_pending, + .reconfig_complete = rt2x00mac_reconfig_complete, }; static const struct rt2800_ops rt2800usb_rt2800_ops = { diff --git a/drivers/net/wireless/ralink/rt2x00/rt2x00.h b/drivers/net/wireless/ralink/rt2x00/rt2x00.h index a90a518b40d36..ea8a34ecae14f 100644 --- a/drivers/net/wireless/ralink/rt2x00/rt2x00.h +++ b/drivers/net/wireless/ralink/rt2x00/rt2x00.h @@ -1439,6 +1439,8 @@ void rt2x00mac_tx(struct ieee80211_hw *hw, struct sk_buff *skb); int rt2x00mac_start(struct ieee80211_hw *hw); void rt2x00mac_stop(struct ieee80211_hw *hw); +void rt2x00mac_reconfig_complete(struct ieee80211_hw *hw, + enum ieee80211_reconfig_type reconfig_type); int rt2x00mac_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif); void rt2x00mac_remove_interface(struct ieee80211_hw *hw, diff --git a/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c b/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c index c3eab767bc211..7f9e43a4f805c 100644 --- a/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c +++ b/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c @@ -1255,16 +1255,6 @@ int rt2x00lib_start(struct rt2x00_dev *rt2x00dev) { int retval = 0; - if (test_bit(DEVICE_STATE_STARTED, &rt2x00dev->flags)) { - /* - * This is special case for ieee80211_restart_hw(), otherwise - * mac80211 never call start() two times in row without stop(); - */ - set_bit(DEVICE_STATE_RESET, &rt2x00dev->flags); - rt2x00dev->ops->lib->pre_reset_hw(rt2x00dev); - rt2x00lib_stop(rt2x00dev); - } - /* * If this is the first interface which is added, * we should load the firmware now. @@ -1292,7 +1282,6 @@ int rt2x00lib_start(struct rt2x00_dev *rt2x00dev) set_bit(DEVICE_STATE_STARTED, &rt2x00dev->flags); out: - clear_bit(DEVICE_STATE_RESET, &rt2x00dev->flags); return retval; } diff --git a/drivers/net/wireless/ralink/rt2x00/rt2x00mac.c b/drivers/net/wireless/ralink/rt2x00/rt2x00mac.c index beb20c5faf5f4..32efbc8e9f925 100644 --- a/drivers/net/wireless/ralink/rt2x00/rt2x00mac.c +++ b/drivers/net/wireless/ralink/rt2x00/rt2x00mac.c @@ -165,6 +165,15 @@ int rt2x00mac_start(struct ieee80211_hw *hw) if (!test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags)) return 0; + if (test_bit(DEVICE_STATE_STARTED, &rt2x00dev->flags)) { + /* + * This is special case for ieee80211_restart_hw(), otherwise + * mac80211 never call start() two times in row without stop(); + */ + set_bit(DEVICE_STATE_RESET, &rt2x00dev->flags); + rt2x00dev->ops->lib->pre_reset_hw(rt2x00dev); + rt2x00lib_stop(rt2x00dev); + } return rt2x00lib_start(rt2x00dev); } EXPORT_SYMBOL_GPL(rt2x00mac_start); @@ -180,6 +189,17 @@ void rt2x00mac_stop(struct ieee80211_hw *hw) } EXPORT_SYMBOL_GPL(rt2x00mac_stop); +void +rt2x00mac_reconfig_complete(struct ieee80211_hw *hw, + enum ieee80211_reconfig_type reconfig_type) +{ + struct rt2x00_dev *rt2x00dev = hw->priv; + + if (reconfig_type == IEEE80211_RECONFIG_TYPE_RESTART) + clear_bit(DEVICE_STATE_RESET, &rt2x00dev->flags); +} +EXPORT_SYMBOL_GPL(rt2x00mac_reconfig_complete); + int rt2x00mac_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { -- GitLab From 2d52378a0ad0c2c3e8a2572aa883da46ddfaac4e Mon Sep 17 00:00:00 2001 From: Stanislaw Gruszka Date: Thu, 28 Nov 2019 10:11:24 +0100 Subject: [PATCH 0955/1437] rt2x00: use RESET state bit to prevent IV changes on restart We can not set IV when we are under reset. Previously we read previous IV value from registers and zeroed IV registers when key was deleted, but this could possibly not work if DEL_KEY command was not performed. Now when we have ->reconfig_complete ops and we fully mark mac80211 reset state via DEVICE_STATE_RESET flag, we can just skip setting IV when keys are set during reset. Signed-off-by: Stanislaw Gruszka Signed-off-by: Kalle Valo --- .../net/wireless/ralink/rt2x00/rt2800lib.c | 21 ++++++++----------- 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/drivers/net/wireless/ralink/rt2x00/rt2800lib.c b/drivers/net/wireless/ralink/rt2x00/rt2800lib.c index a36c3fea7495a..6beac1f74e7cb 100644 --- a/drivers/net/wireless/ralink/rt2x00/rt2800lib.c +++ b/drivers/net/wireless/ralink/rt2x00/rt2800lib.c @@ -1652,20 +1652,17 @@ static void rt2800_config_wcid_attr_cipher(struct rt2x00_dev *rt2x00dev, rt2800_register_write(rt2x00dev, offset, reg); } - offset = MAC_IVEIV_ENTRY(key->hw_key_idx); + if (test_bit(DEVICE_STATE_RESET, &rt2x00dev->flags)) + return; - if (crypto->cmd == SET_KEY) { - rt2800_register_multiread(rt2x00dev, offset, - &iveiv_entry, sizeof(iveiv_entry)); - if ((crypto->cipher == CIPHER_TKIP) || - (crypto->cipher == CIPHER_TKIP_NO_MIC) || - (crypto->cipher == CIPHER_AES)) - iveiv_entry.iv[3] |= 0x20; - iveiv_entry.iv[3] |= key->keyidx << 6; - } else { - memset(&iveiv_entry, 0, sizeof(iveiv_entry)); - } + offset = MAC_IVEIV_ENTRY(key->hw_key_idx); + memset(&iveiv_entry, 0, sizeof(iveiv_entry)); + if ((crypto->cipher == CIPHER_TKIP) || + (crypto->cipher == CIPHER_TKIP_NO_MIC) || + (crypto->cipher == CIPHER_AES)) + iveiv_entry.iv[3] |= 0x20; + iveiv_entry.iv[3] |= key->keyidx << 6; rt2800_register_multiwrite(rt2x00dev, offset, &iveiv_entry, sizeof(iveiv_entry)); } -- GitLab From e93cd35101b61e4c79149be2cfc927c4b28dc60c Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 28 Nov 2019 18:22:00 +0100 Subject: [PATCH 0956/1437] rsi: fix use-after-free on failed probe and unbind Make sure to stop both URBs before returning after failed probe as well as on disconnect to avoid use-after-free in the completion handler. Reported-by: syzbot+b563b7f8dbe8223a51e8@syzkaller.appspotmail.com Fixes: a4302bff28e2 ("rsi: add bluetooth rx endpoint") Fixes: dad0d04fa7ba ("rsi: Add RS9113 wireless driver") Cc: stable # 3.15 Cc: Siva Rebbagondla Cc: Prameela Rani Garnepudi Cc: Amitkumar Karwar Cc: Fariya Fatima Signed-off-by: Johan Hovold Signed-off-by: Kalle Valo --- drivers/net/wireless/rsi/rsi_91x_usb.c | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/rsi/rsi_91x_usb.c b/drivers/net/wireless/rsi/rsi_91x_usb.c index 53f41fc2cadfb..30bed719486ec 100644 --- a/drivers/net/wireless/rsi/rsi_91x_usb.c +++ b/drivers/net/wireless/rsi/rsi_91x_usb.c @@ -292,6 +292,15 @@ out: dev_kfree_skb(rx_cb->rx_skb); } +static void rsi_rx_urb_kill(struct rsi_hw *adapter, u8 ep_num) +{ + struct rsi_91x_usbdev *dev = (struct rsi_91x_usbdev *)adapter->rsi_dev; + struct rx_usb_ctrl_block *rx_cb = &dev->rx_cb[ep_num - 1]; + struct urb *urb = rx_cb->rx_urb; + + usb_kill_urb(urb); +} + /** * rsi_rx_urb_submit() - This function submits the given URB to the USB stack. * @adapter: Pointer to the adapter structure. @@ -823,10 +832,13 @@ static int rsi_probe(struct usb_interface *pfunction, if (adapter->priv->coex_mode > 1) { status = rsi_rx_urb_submit(adapter, BT_EP); if (status) - goto err1; + goto err_kill_wlan_urb; } return 0; + +err_kill_wlan_urb: + rsi_rx_urb_kill(adapter, WLAN_EP); err1: rsi_deinit_usb_interface(adapter); err: @@ -857,6 +869,10 @@ static void rsi_disconnect(struct usb_interface *pfunction) adapter->priv->bt_adapter = NULL; } + if (adapter->priv->coex_mode > 1) + rsi_rx_urb_kill(adapter, BT_EP); + rsi_rx_urb_kill(adapter, WLAN_EP); + rsi_reset_card(adapter); rsi_deinit_usb_interface(adapter); rsi_91x_deinit(adapter); -- GitLab From 92aafe77123ab478e5f5095878856ab0424910da Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 28 Nov 2019 18:22:01 +0100 Subject: [PATCH 0957/1437] rsi: fix use-after-free on probe errors The driver would fail to stop the command timer in most error paths, something which specifically could lead to the timer being freed while still active on I/O errors during probe. Fix this by making sure that each function starting the timer also stops it in all relevant error paths. Reported-by: syzbot+1d1597a5aa3679c65b9f@syzkaller.appspotmail.com Fixes: b78e91bcfb33 ("rsi: Add new firmware loading method") Cc: stable # 4.12 Cc: Prameela Rani Garnepudi Cc: Amitkumar Karwar Signed-off-by: Johan Hovold Signed-off-by: Kalle Valo --- drivers/net/wireless/rsi/rsi_91x_hal.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/net/wireless/rsi/rsi_91x_hal.c b/drivers/net/wireless/rsi/rsi_91x_hal.c index f84250bdb8cf8..6f8d5f9a9f7e6 100644 --- a/drivers/net/wireless/rsi/rsi_91x_hal.c +++ b/drivers/net/wireless/rsi/rsi_91x_hal.c @@ -622,6 +622,7 @@ static int bl_cmd(struct rsi_hw *adapter, u8 cmd, u8 exp_resp, char *str) bl_start_cmd_timer(adapter, timeout); status = bl_write_cmd(adapter, cmd, exp_resp, ®out_val); if (status < 0) { + bl_stop_cmd_timer(adapter); rsi_dbg(ERR_ZONE, "%s: Command %s (%0x) writing failed..\n", __func__, str, cmd); @@ -737,10 +738,9 @@ static int ping_pong_write(struct rsi_hw *adapter, u8 cmd, u8 *addr, u32 size) } status = bl_cmd(adapter, cmd_req, cmd_resp, str); - if (status) { - bl_stop_cmd_timer(adapter); + if (status) return status; - } + return 0; } @@ -828,10 +828,9 @@ static int auto_fw_upgrade(struct rsi_hw *adapter, u8 *flash_content, status = bl_cmd(adapter, EOF_REACHED, FW_LOADING_SUCCESSFUL, "EOF_REACHED"); - if (status) { - bl_stop_cmd_timer(adapter); + if (status) return status; - } + rsi_dbg(INFO_ZONE, "FW loading is done and FW is running..\n"); return 0; } @@ -849,6 +848,7 @@ static int rsi_hal_prepare_fwload(struct rsi_hw *adapter) ®out_val, RSI_COMMON_REG_SIZE); if (status < 0) { + bl_stop_cmd_timer(adapter); rsi_dbg(ERR_ZONE, "%s: REGOUT read failed\n", __func__); return status; -- GitLab From 47768297481184932844ab01a86752ba31a38861 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 28 Nov 2019 18:22:02 +0100 Subject: [PATCH 0958/1437] rsi: fix memory leak on failed URB submission Make sure to free the skb on failed receive-URB submission (e.g. on disconnect or currently also due to a missing endpoint). Fixes: a1854fae1414 ("rsi: improve RX packet handling in USB interface") Cc: stable # 4.17 Cc: Prameela Rani Garnepudi Signed-off-by: Johan Hovold Signed-off-by: Kalle Valo --- drivers/net/wireless/rsi/rsi_91x_usb.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/rsi/rsi_91x_usb.c b/drivers/net/wireless/rsi/rsi_91x_usb.c index 30bed719486ec..2c869df1c62e8 100644 --- a/drivers/net/wireless/rsi/rsi_91x_usb.c +++ b/drivers/net/wireless/rsi/rsi_91x_usb.c @@ -338,8 +338,10 @@ static int rsi_rx_urb_submit(struct rsi_hw *adapter, u8 ep_num) rx_cb); status = usb_submit_urb(urb, GFP_KERNEL); - if (status) + if (status) { rsi_dbg(ERR_ZONE, "%s: Failed in urb submission\n", __func__); + dev_kfree_skb(skb); + } return status; } -- GitLab From b9b9f9fea21830f85cf0148cd8dce001ae55ead1 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 28 Nov 2019 18:22:03 +0100 Subject: [PATCH 0959/1437] rsi: fix non-atomic allocation in completion handler USB completion handlers are called in atomic context and must specifically not allocate memory using GFP_KERNEL. Fixes: a1854fae1414 ("rsi: improve RX packet handling in USB interface") Cc: stable # 4.17 Cc: Prameela Rani Garnepudi Signed-off-by: Johan Hovold Signed-off-by: Kalle Valo --- drivers/net/wireless/rsi/rsi_91x_usb.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/drivers/net/wireless/rsi/rsi_91x_usb.c b/drivers/net/wireless/rsi/rsi_91x_usb.c index 2c869df1c62e8..ead75574e10a6 100644 --- a/drivers/net/wireless/rsi/rsi_91x_usb.c +++ b/drivers/net/wireless/rsi/rsi_91x_usb.c @@ -16,6 +16,7 @@ */ #include +#include #include #include "rsi_usb.h" #include "rsi_hal.h" @@ -29,7 +30,7 @@ MODULE_PARM_DESC(dev_oper_mode, "9[Wi-Fi STA + BT LE], 13[Wi-Fi STA + BT classic + BT LE]\n" "6[AP + BT classic], 14[AP + BT classic + BT LE]"); -static int rsi_rx_urb_submit(struct rsi_hw *adapter, u8 ep_num); +static int rsi_rx_urb_submit(struct rsi_hw *adapter, u8 ep_num, gfp_t flags); /** * rsi_usb_card_write() - This function writes to the USB Card. @@ -285,7 +286,7 @@ static void rsi_rx_done_handler(struct urb *urb) status = 0; out: - if (rsi_rx_urb_submit(dev->priv, rx_cb->ep_num)) + if (rsi_rx_urb_submit(dev->priv, rx_cb->ep_num, GFP_ATOMIC)) rsi_dbg(ERR_ZONE, "%s: Failed in urb submission", __func__); if (status) @@ -307,7 +308,7 @@ static void rsi_rx_urb_kill(struct rsi_hw *adapter, u8 ep_num) * * Return: 0 on success, a negative error code on failure. */ -static int rsi_rx_urb_submit(struct rsi_hw *adapter, u8 ep_num) +static int rsi_rx_urb_submit(struct rsi_hw *adapter, u8 ep_num, gfp_t mem_flags) { struct rsi_91x_usbdev *dev = (struct rsi_91x_usbdev *)adapter->rsi_dev; struct rx_usb_ctrl_block *rx_cb = &dev->rx_cb[ep_num - 1]; @@ -337,7 +338,7 @@ static int rsi_rx_urb_submit(struct rsi_hw *adapter, u8 ep_num) rsi_rx_done_handler, rx_cb); - status = usb_submit_urb(urb, GFP_KERNEL); + status = usb_submit_urb(urb, mem_flags); if (status) { rsi_dbg(ERR_ZONE, "%s: Failed in urb submission\n", __func__); dev_kfree_skb(skb); @@ -827,12 +828,12 @@ static int rsi_probe(struct usb_interface *pfunction, rsi_dbg(INIT_ZONE, "%s: Device Init Done\n", __func__); } - status = rsi_rx_urb_submit(adapter, WLAN_EP); + status = rsi_rx_urb_submit(adapter, WLAN_EP, GFP_KERNEL); if (status) goto err1; if (adapter->priv->coex_mode > 1) { - status = rsi_rx_urb_submit(adapter, BT_EP); + status = rsi_rx_urb_submit(adapter, BT_EP, GFP_KERNEL); if (status) goto err_kill_wlan_urb; } -- GitLab From 960da557f435bfe14edb160a42b8ed3a2e255e73 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 28 Nov 2019 18:22:04 +0100 Subject: [PATCH 0960/1437] rsi: add missing endpoint sanity checks The driver expects at least one bulk-in endpoint when in "wifi-alone" operating mode and two bulk-in endpoints otherwise, and would otherwise fail to to submit the corresponding bulk URB to the default pipe during probe with a somewhat cryptic message: rsi_91x: rsi_rx_urb_submit: Failed in urb submission rsi_91x: rsi_probe: Failed in probe...Exiting RSI-USB WLAN: probe of 2-2.4:1.0 failed with error -8 The current endpoint sanity check looks broken and would only bail out early if there was no bulk-in endpoint but at least one bulk-out endpoint. Tighten this check to always require at least one bulk-in and one bulk-out endpoint, and add the missing sanity check for a Bluetooth bulk-in endpoint when in a BT operating mode. Also make sure to log an informative error message when the expected endpoints are missing. Signed-off-by: Johan Hovold Signed-off-by: Kalle Valo --- drivers/net/wireless/rsi/rsi_91x_usb.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/rsi/rsi_91x_usb.c b/drivers/net/wireless/rsi/rsi_91x_usb.c index ead75574e10a6..396b9b81c1cd6 100644 --- a/drivers/net/wireless/rsi/rsi_91x_usb.c +++ b/drivers/net/wireless/rsi/rsi_91x_usb.c @@ -149,9 +149,17 @@ static int rsi_find_bulk_in_and_out_endpoints(struct usb_interface *interface, break; } - if (!(dev->bulkin_endpoint_addr[0]) && - dev->bulkout_endpoint_addr[0]) + if (!(dev->bulkin_endpoint_addr[0] && dev->bulkout_endpoint_addr[0])) { + dev_err(&interface->dev, "missing wlan bulk endpoints\n"); return -EINVAL; + } + + if (adapter->priv->coex_mode > 1) { + if (!dev->bulkin_endpoint_addr[1]) { + dev_err(&interface->dev, "missing bt bulk-in endpoint\n"); + return -EINVAL; + } + } return 0; } -- GitLab From 216b44000ada87a63891a8214c347e05a4aea8fe Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Tue, 3 Dec 2019 12:58:55 +0300 Subject: [PATCH 0961/1437] brcmfmac: Fix use after free in brcmf_sdio_readframes() The brcmu_pkt_buf_free_skb() function frees "pkt" so it leads to a static checker warning: drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c:1974 brcmf_sdio_readframes() error: dereferencing freed memory 'pkt' It looks like there was supposed to be a continue after we free "pkt". Fixes: 4754fceeb9a6 ("brcmfmac: streamline SDIO read frame routine") Signed-off-by: Dan Carpenter Acked-by: Franky Lin Signed-off-by: Kalle Valo --- drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c index 264ad63232f87..1dea0178832ea 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c @@ -1935,6 +1935,7 @@ static uint brcmf_sdio_readframes(struct brcmf_sdio *bus, uint maxframes) BRCMF_SDIO_FT_NORMAL)) { rd->len = 0; brcmu_pkt_buf_free_skb(pkt); + continue; } bus->sdcnt.rx_readahead_cnt++; if (rd->len != roundup(rd_new.len, 16)) { -- GitLab From ffc7b2826a3cd48369e0676b4dc083080ef96d7c Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Wed, 4 Dec 2019 06:43:55 +0100 Subject: [PATCH 0962/1437] rt2x00usb: Fix a warning message in 'rt2x00usb_watchdog_tx_dma()' 'forced' is duplicated in the message, axe one of them. Signed-off-by: Christophe JAILLET Signed-off-by: Kalle Valo --- drivers/net/wireless/ralink/rt2x00/rt2x00usb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/ralink/rt2x00/rt2x00usb.c b/drivers/net/wireless/ralink/rt2x00/rt2x00usb.c index bc2dfef0de22b..92e9e023c3499 100644 --- a/drivers/net/wireless/ralink/rt2x00/rt2x00usb.c +++ b/drivers/net/wireless/ralink/rt2x00/rt2x00usb.c @@ -522,7 +522,7 @@ EXPORT_SYMBOL_GPL(rt2x00usb_flush_queue); static void rt2x00usb_watchdog_tx_dma(struct data_queue *queue) { - rt2x00_warn(queue->rt2x00dev, "TX queue %d DMA timed out, invoke forced forced reset\n", + rt2x00_warn(queue->rt2x00dev, "TX queue %d DMA timed out, invoke forced reset\n", queue->qid); rt2x00queue_stop_queue(queue); -- GitLab From d0b103a52b72a45b381332d4421ac3e2eaccecb7 Mon Sep 17 00:00:00 2001 From: Ganapathi Bhat Date: Fri, 6 Dec 2019 13:47:48 +0530 Subject: [PATCH 0963/1437] MAINTAINERS: update Ganapathi Bhat's email address I'd like to use this email-id from now on. Signed-off-by: Ganapathi Bhat Signed-off-by: Kalle Valo --- MAINTAINERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index 09a599fa3e1a7..d09519805cc09 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -9956,7 +9956,7 @@ F: drivers/net/ethernet/marvell/mvneta.* MARVELL MWIFIEX WIRELESS DRIVER M: Amitkumar Karwar M: Nishant Sarmukadam -M: Ganapathi Bhat +M: Ganapathi Bhat M: Xinming Hu L: linux-wireless@vger.kernel.org S: Maintained -- GitLab From 1c9f329b084b7b8ea6d60d91a202e884cdcf6aae Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Mon, 9 Dec 2019 16:39:11 -0800 Subject: [PATCH 0964/1437] mwifiex: delete unused mwifiex_get_intf_num() Commit 7afb94da3cd8 ("mwifiex: update set_mac_address logic") fixed the only user of this function, partly because the author seems to have noticed that, as written, it's on the borderline between highly misleading and buggy. Anyway, no sense in keeping dead code around: let's drop it. Fixes: 7afb94da3cd8 ("mwifiex: update set_mac_address logic") Signed-off-by: Brian Norris Signed-off-by: Kalle Valo --- drivers/net/wireless/marvell/mwifiex/main.h | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/drivers/net/wireless/marvell/mwifiex/main.h b/drivers/net/wireless/marvell/mwifiex/main.h index 547ff3c578eee..fa5634af40f7c 100644 --- a/drivers/net/wireless/marvell/mwifiex/main.h +++ b/drivers/net/wireless/marvell/mwifiex/main.h @@ -1295,19 +1295,6 @@ mwifiex_copy_rates(u8 *dest, u32 pos, u8 *src, int len) return pos; } -/* This function return interface number with the same bss_type. - */ -static inline u8 -mwifiex_get_intf_num(struct mwifiex_adapter *adapter, u8 bss_type) -{ - u8 i, num = 0; - - for (i = 0; i < adapter->priv_num; i++) - if (adapter->priv[i] && adapter->priv[i]->bss_type == bss_type) - num++; - return num; -} - /* * This function returns the correct private structure pointer based * upon the BSS type and BSS number. -- GitLab From 8d9627b05b2c33e4468e65739eb7caf9c3f274d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= Date: Tue, 10 Dec 2019 12:35:55 +0100 Subject: [PATCH 0965/1437] brcmfmac: set interface carrier to off by default MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It's important as brcmfmac creates one main interface for each PHY and doesn't allow deleting it. Not setting carrier could result in other subsystems misbehaving (e.g. LEDs "netdev" trigger turning LED on). Signed-off-by: Rafał Miłecki Signed-off-by: Kalle Valo --- drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c index 85cf96461ddeb..d3ddd97fe7680 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c @@ -661,6 +661,8 @@ int brcmf_net_attach(struct brcmf_if *ifp, bool rtnl_locked) goto fail; } + netif_carrier_off(ndev); + ndev->priv_destructor = brcmf_cfg80211_free_netdev; brcmf_dbg(INFO, "%s: Broadcom Dongle Host Driver\n", ndev->name); return 0; -- GitLab From af615abd15966387393471649185d15954e9cecd Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 10 Dec 2019 12:44:21 +0100 Subject: [PATCH 0966/1437] at76c50x-usb: fix endpoint debug message Make sure to use the current alternate setting, which may not be the same as the first alternate setting, also when printing the number of endpoints at probe. Signed-off-by: Johan Hovold Signed-off-by: Kalle Valo --- drivers/net/wireless/atmel/at76c50x-usb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/atmel/at76c50x-usb.c b/drivers/net/wireless/atmel/at76c50x-usb.c index db2c3b8d491e5..3b2680772f03d 100644 --- a/drivers/net/wireless/atmel/at76c50x-usb.c +++ b/drivers/net/wireless/atmel/at76c50x-usb.c @@ -2236,7 +2236,7 @@ static int at76_alloc_urbs(struct at76_priv *priv, at76_dbg(DBG_PROC_ENTRY, "%s: ENTER", __func__); at76_dbg(DBG_URB, "%s: NumEndpoints %d ", __func__, - interface->altsetting[0].desc.bNumEndpoints); + interface->cur_altsetting->desc.bNumEndpoints); ep_in = NULL; ep_out = NULL; -- GitLab From 3428fbcd6e6c0850b1a8b2a12082b7b2aabb3da3 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 10 Dec 2019 12:44:22 +0100 Subject: [PATCH 0967/1437] brcmfmac: fix interface sanity check Make sure to use the current alternate setting when verifying the interface descriptors to avoid binding to an invalid interface. Failing to do so could cause the driver to misbehave or trigger a WARN() in usb_submit_urb() that kernels with panic_on_warn set would choke on. Fixes: 71bb244ba2fd ("brcm80211: fmac: add USB support for bcm43235/6/8 chipsets") Cc: stable # 3.4 Cc: Arend van Spriel Signed-off-by: Johan Hovold Signed-off-by: Kalle Valo --- drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c index 06f3c01f10b3b..7cdfde9b3dea9 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c @@ -1348,7 +1348,7 @@ brcmf_usb_probe(struct usb_interface *intf, const struct usb_device_id *id) goto fail; } - desc = &intf->altsetting[0].desc; + desc = &intf->cur_altsetting->desc; if ((desc->bInterfaceClass != USB_CLASS_VENDOR_SPEC) || (desc->bInterfaceSubClass != 2) || (desc->bInterfaceProtocol != 0xff)) { @@ -1361,7 +1361,7 @@ brcmf_usb_probe(struct usb_interface *intf, const struct usb_device_id *id) num_of_eps = desc->bNumEndpoints; for (ep = 0; ep < num_of_eps; ep++) { - endpoint = &intf->altsetting[0].endpoint[ep].desc; + endpoint = &intf->cur_altsetting->endpoint[ep].desc; endpoint_num = usb_endpoint_num(endpoint); if (!usb_endpoint_xfer_bulk(endpoint)) continue; -- GitLab From b73e05aa543cf8db4f4927e36952360d71291d41 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 10 Dec 2019 12:44:23 +0100 Subject: [PATCH 0968/1437] orinoco_usb: fix interface sanity check Make sure to use the current alternate setting when verifying the interface descriptors to avoid binding to an invalid interface. Failing to do so could cause the driver to misbehave or trigger a WARN() in usb_submit_urb() that kernels with panic_on_warn set would choke on. Fixes: 9afac70a7305 ("orinoco: add orinoco_usb driver") Cc: stable # 2.6.35 Signed-off-by: Johan Hovold Signed-off-by: Kalle Valo --- drivers/net/wireless/intersil/orinoco/orinoco_usb.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/intersil/orinoco/orinoco_usb.c b/drivers/net/wireless/intersil/orinoco/orinoco_usb.c index 40a8b941ad5cc..8c79b963bcffb 100644 --- a/drivers/net/wireless/intersil/orinoco/orinoco_usb.c +++ b/drivers/net/wireless/intersil/orinoco/orinoco_usb.c @@ -1608,9 +1608,9 @@ static int ezusb_probe(struct usb_interface *interface, /* set up the endpoint information */ /* check out the endpoints */ - iface_desc = &interface->altsetting[0].desc; + iface_desc = &interface->cur_altsetting->desc; for (i = 0; i < iface_desc->bNumEndpoints; ++i) { - ep = &interface->altsetting[0].endpoint[i].desc; + ep = &interface->cur_altsetting->endpoint[i].desc; if (usb_endpoint_is_bulk_in(ep)) { /* we found a bulk in endpoint */ -- GitLab From 39a4281c312f2d226c710bc656ce380c621a2b16 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 10 Dec 2019 12:44:24 +0100 Subject: [PATCH 0969/1437] rtl8xxxu: fix interface sanity check Make sure to use the current alternate setting when verifying the interface descriptors to avoid binding to an invalid interface. Failing to do so could cause the driver to misbehave or trigger a WARN() in usb_submit_urb() that kernels with panic_on_warn set would choke on. Fixes: 26f1fad29ad9 ("New driver: rtl8xxxu (mac80211)") Cc: stable # 4.4 Cc: Jes Sorensen Signed-off-by: Johan Hovold Signed-off-by: Kalle Valo --- drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c index aa2bb2ae98096..54a1a4ea107b7 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c +++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c @@ -6384,7 +6384,7 @@ static int rtl8xxxu_parse_usb(struct rtl8xxxu_priv *priv, u8 dir, xtype, num; int ret = 0; - host_interface = &interface->altsetting[0]; + host_interface = interface->cur_altsetting; interface_desc = &host_interface->desc; endpoints = interface_desc->bNumEndpoints; -- GitLab From 3139b180906af43bc09bd3373fc2338a8271d9d9 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 10 Dec 2019 12:44:25 +0100 Subject: [PATCH 0970/1437] rsi_91x_usb: fix interface sanity check Make sure to use the current alternate setting when verifying the interface descriptors to avoid binding to an invalid interface. Failing to do so could cause the driver to misbehave or trigger a WARN() in usb_submit_urb() that kernels with panic_on_warn set would choke on. Fixes: dad0d04fa7ba ("rsi: Add RS9113 wireless driver") Cc: stable # 3.15 Cc: Fariya Fatima Signed-off-by: Johan Hovold Signed-off-by: Kalle Valo --- drivers/net/wireless/rsi/rsi_91x_usb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/rsi/rsi_91x_usb.c b/drivers/net/wireless/rsi/rsi_91x_usb.c index 396b9b81c1cd6..a62d41c0ccbc0 100644 --- a/drivers/net/wireless/rsi/rsi_91x_usb.c +++ b/drivers/net/wireless/rsi/rsi_91x_usb.c @@ -118,7 +118,7 @@ static int rsi_find_bulk_in_and_out_endpoints(struct usb_interface *interface, __le16 buffer_size; int ii, bin_found = 0, bout_found = 0; - iface_desc = &(interface->altsetting[0]); + iface_desc = interface->cur_altsetting; for (ii = 0; ii < iface_desc->desc.bNumEndpoints; ++ii) { endpoint = &(iface_desc->endpoint[ii].desc); -- GitLab From 2d68bb2687abb747558b933e80845ff31570a49c Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 10 Dec 2019 12:44:26 +0100 Subject: [PATCH 0971/1437] zd1211rw: fix storage endpoint lookup Make sure to use the current alternate setting when verifying the storage interface descriptors to avoid submitting an URB to an invalid endpoint. Failing to do so could cause the driver to misbehave or trigger a WARN() in usb_submit_urb() that kernels with panic_on_warn set would choke on. Fixes: a1030e92c150 ("[PATCH] zd1211rw: Convert installer CDROM device into WLAN device") Cc: stable # 2.6.19 Signed-off-by: Johan Hovold Signed-off-by: Kalle Valo --- drivers/net/wireless/zydas/zd1211rw/zd_usb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/zydas/zd1211rw/zd_usb.c b/drivers/net/wireless/zydas/zd1211rw/zd_usb.c index 7b5c2fe5bd4d9..8ff0374126e4f 100644 --- a/drivers/net/wireless/zydas/zd1211rw/zd_usb.c +++ b/drivers/net/wireless/zydas/zd1211rw/zd_usb.c @@ -1263,7 +1263,7 @@ static void print_id(struct usb_device *udev) static int eject_installer(struct usb_interface *intf) { struct usb_device *udev = interface_to_usbdev(intf); - struct usb_host_interface *iface_desc = &intf->altsetting[0]; + struct usb_host_interface *iface_desc = intf->cur_altsetting; struct usb_endpoint_descriptor *endpoint; unsigned char *cmd; u8 bulk_out_ep; -- GitLab From f365f2f67b6a6e896864dbe92b0e5bbb34147eea Mon Sep 17 00:00:00 2001 From: Michael Straube Date: Wed, 11 Dec 2019 16:47:50 +0100 Subject: [PATCH 0972/1437] rtlwifi: rtl8192ce: use generic rtl_query_rxpwrpercentage Function _rtl92c_query_rxpwrpercentage is identical to the generic version rtl_query_rxpwrpercentage. Remove _rtl92c_query_rxpwrpercentage and use the generic function. Signed-off-by: Michael Straube Signed-off-by: Kalle Valo --- .../net/wireless/realtek/rtlwifi/rtl8192ce/trx.c | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192ce/trx.c b/drivers/net/wireless/realtek/rtlwifi/rtl8192ce/trx.c index fc9a3aae047fc..dec66b3ac3659 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192ce/trx.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192ce/trx.c @@ -23,16 +23,6 @@ static u8 _rtl92ce_map_hwqueue_to_fwqueue(struct sk_buff *skb, u8 hw_queue) return skb->priority; } -static u8 _rtl92c_query_rxpwrpercentage(s8 antpower) -{ - if ((antpower <= -100) || (antpower >= 20)) - return 0; - else if (antpower >= 0) - return 100; - else - return 100 + antpower; -} - static long _rtl92ce_signal_scale_mapping(struct ieee80211_hw *hw, long currsig) { @@ -194,7 +184,7 @@ static void _rtl92ce_query_rxphystatus(struct ieee80211_hw *hw, rx_pwr[i] = ((p_drvinfo->gain_trsw[i] & 0x3f) * 2) - 110; /* Translate DBM to percentage. */ - rssi = _rtl92c_query_rxpwrpercentage(rx_pwr[i]); + rssi = rtl_query_rxpwrpercentage(rx_pwr[i]); total_rssi += rssi; /* Get Rx snr value in DB */ rtlpriv->stats.rx_snr_db[i] = @@ -209,7 +199,7 @@ static void _rtl92ce_query_rxphystatus(struct ieee80211_hw *hw, * hardware (for rate adaptive) */ rx_pwr_all = ((p_drvinfo->pwdb_all >> 1) & 0x7f) - 110; - pwdb_all = _rtl92c_query_rxpwrpercentage(rx_pwr_all); + pwdb_all = rtl_query_rxpwrpercentage(rx_pwr_all); pstats->rx_pwdb_all = pwdb_all; pstats->rxpower = rx_pwr_all; pstats->recvsignalpower = rx_pwr_all; -- GitLab From b8a19dd64501e4b5019eed27f98b2f8675513f6d Mon Sep 17 00:00:00 2001 From: Michael Straube Date: Wed, 11 Dec 2019 16:47:51 +0100 Subject: [PATCH 0973/1437] rtlwifi: rtl8192cu: use generic rtl_query_rxpwrpercentage Function _rtl92c_query_rxpwrpercentage is identical to the generic version rtl_query_rxpwrpercentage. Remove _rtl92c_query_rxpwrpercentage and use the generic function. Signed-off-by: Michael Straube Signed-off-by: Kalle Valo --- .../net/wireless/realtek/rtlwifi/rtl8192cu/mac.c | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/mac.c b/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/mac.c index cec19b32c7e2d..c8652283a5160 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/mac.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/mac.c @@ -567,16 +567,6 @@ void rtl92c_set_min_space(struct ieee80211_hw *hw, bool is2T) /*==============================================================*/ -static u8 _rtl92c_query_rxpwrpercentage(s8 antpower) -{ - if ((antpower <= -100) || (antpower >= 20)) - return 0; - else if (antpower >= 0) - return 100; - else - return 100 + antpower; -} - static long _rtl92c_signal_scale_mapping(struct ieee80211_hw *hw, long currsig) { @@ -678,7 +668,7 @@ static void _rtl92c_query_rxphystatus(struct ieee80211_hw *hw, break; } } - pwdb_all = _rtl92c_query_rxpwrpercentage(rx_pwr_all); + pwdb_all = rtl_query_rxpwrpercentage(rx_pwr_all); pstats->rx_pwdb_all = pwdb_all; pstats->recvsignalpower = rx_pwr_all; if (packet_match_bssid) { @@ -707,7 +697,7 @@ static void _rtl92c_query_rxphystatus(struct ieee80211_hw *hw, rf_rx_num++; rx_pwr[i] = ((p_drvinfo->gain_trsw[i] & 0x3f) * 2) - 110; - rssi = _rtl92c_query_rxpwrpercentage(rx_pwr[i]); + rssi = rtl_query_rxpwrpercentage(rx_pwr[i]); total_rssi += rssi; rtlpriv->stats.rx_snr_db[i] = (long)(p_drvinfo->rxsnr[i] / 2); @@ -716,7 +706,7 @@ static void _rtl92c_query_rxphystatus(struct ieee80211_hw *hw, pstats->rx_mimo_signalstrength[i] = (u8) rssi; } rx_pwr_all = ((p_drvinfo->pwdb_all >> 1) & 0x7f) - 110; - pwdb_all = _rtl92c_query_rxpwrpercentage(rx_pwr_all); + pwdb_all = rtl_query_rxpwrpercentage(rx_pwr_all); pstats->rx_pwdb_all = pwdb_all; pstats->rxpower = rx_pwr_all; pstats->recvsignalpower = rx_pwr_all; -- GitLab From 5a87ae1d676ed31ec30779f6d98ea68afc5cbde7 Mon Sep 17 00:00:00 2001 From: Michael Straube Date: Wed, 11 Dec 2019 16:47:52 +0100 Subject: [PATCH 0974/1437] rtlwifi: rtl8192de: use generic rtl_query_rxpwrpercentage Function _rtl92d_query_rxpwrpercentage is identical to the generic version rtl_query_rxpwrpercentage. Remove _rtl92d_query_rxpwrpercentage and use the generic function. Signed-off-by: Michael Straube Signed-off-by: Kalle Valo --- .../net/wireless/realtek/rtlwifi/rtl8192de/trx.c | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192de/trx.c b/drivers/net/wireless/realtek/rtlwifi/rtl8192de/trx.c index bb64612654b9e..0fb6ef7e2a0ea 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192de/trx.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192de/trx.c @@ -23,16 +23,6 @@ static u8 _rtl92de_map_hwqueue_to_fwqueue(struct sk_buff *skb, u8 hw_queue) return skb->priority; } -static u8 _rtl92d_query_rxpwrpercentage(s8 antpower) -{ - if ((antpower <= -100) || (antpower >= 20)) - return 0; - else if (antpower >= 0) - return 100; - else - return 100 + antpower; -} - static long _rtl92de_translate_todbm(struct ieee80211_hw *hw, u8 signal_strength_index) { @@ -141,7 +131,7 @@ static void _rtl92de_query_rxphystatus(struct ieee80211_hw *hw, break; } } - pwdb_all = _rtl92d_query_rxpwrpercentage(rx_pwr_all); + pwdb_all = rtl_query_rxpwrpercentage(rx_pwr_all); /* CCK gain is smaller than OFDM/MCS gain, */ /* so we add gain diff by experiences, the val is 6 */ pwdb_all += 6; @@ -183,7 +173,7 @@ static void _rtl92de_query_rxphystatus(struct ieee80211_hw *hw, rf_rx_num++; rx_pwr[i] = ((p_drvinfo->gain_trsw[i] & 0x3f) * 2) - 110; - rssi = _rtl92d_query_rxpwrpercentage(rx_pwr[i]); + rssi = rtl_query_rxpwrpercentage(rx_pwr[i]); total_rssi += rssi; rtlpriv->stats.rx_snr_db[i] = (long)(p_drvinfo->rxsnr[i] / 2); @@ -191,7 +181,7 @@ static void _rtl92de_query_rxphystatus(struct ieee80211_hw *hw, pstats->rx_mimo_signalstrength[i] = (u8) rssi; } rx_pwr_all = ((p_drvinfo->pwdb_all >> 1) & 0x7f) - 106; - pwdb_all = _rtl92d_query_rxpwrpercentage(rx_pwr_all); + pwdb_all = rtl_query_rxpwrpercentage(rx_pwr_all); pstats->rx_pwdb_all = pwdb_all; pstats->rxpower = rx_pwr_all; pstats->recvsignalpower = rx_pwr_all; -- GitLab From d01b26734297b9fc5aaf110569d86254ede1db39 Mon Sep 17 00:00:00 2001 From: Michael Straube Date: Wed, 11 Dec 2019 16:47:53 +0100 Subject: [PATCH 0975/1437] rtlwifi: rtl8192ce: use generic rtl_signal_scale_mapping Function _rtl92ce_signal_scale_mapping is identical to the generic version rtl_signal_scale_mapping. Remove _rtl92ce_signal_scale_mapping and use the generic function. Signed-off-by: Michael Straube Signed-off-by: Kalle Valo --- .../wireless/realtek/rtlwifi/rtl8192ce/trx.c | 34 ++----------------- 1 file changed, 2 insertions(+), 32 deletions(-) diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192ce/trx.c b/drivers/net/wireless/realtek/rtlwifi/rtl8192ce/trx.c index dec66b3ac3659..8fc3cb8240668 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192ce/trx.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192ce/trx.c @@ -23,35 +23,6 @@ static u8 _rtl92ce_map_hwqueue_to_fwqueue(struct sk_buff *skb, u8 hw_queue) return skb->priority; } -static long _rtl92ce_signal_scale_mapping(struct ieee80211_hw *hw, - long currsig) -{ - long retsig; - - if (currsig >= 61 && currsig <= 100) - retsig = 90 + ((currsig - 60) / 4); - else if (currsig >= 41 && currsig <= 60) - retsig = 78 + ((currsig - 40) / 2); - else if (currsig >= 31 && currsig <= 40) - retsig = 66 + (currsig - 30); - else if (currsig >= 21 && currsig <= 30) - retsig = 54 + (currsig - 20); - else if (currsig >= 5 && currsig <= 20) - retsig = 42 + (((currsig - 5) * 2) / 3); - else if (currsig == 4) - retsig = 36; - else if (currsig == 3) - retsig = 27; - else if (currsig == 2) - retsig = 18; - else if (currsig == 1) - retsig = 9; - else - retsig = currsig; - - return retsig; -} - static void _rtl92ce_query_rxphystatus(struct ieee80211_hw *hw, struct rtl_stats *pstats, struct rx_desc_92c *pdesc, @@ -231,11 +202,10 @@ static void _rtl92ce_query_rxphystatus(struct ieee80211_hw *hw, */ if (is_cck_rate) pstats->signalstrength = - (u8)(_rtl92ce_signal_scale_mapping(hw, pwdb_all)); + (u8)(rtl_signal_scale_mapping(hw, pwdb_all)); else if (rf_rx_num != 0) pstats->signalstrength = - (u8)(_rtl92ce_signal_scale_mapping - (hw, total_rssi /= rf_rx_num)); + (u8)(rtl_signal_scale_mapping(hw, total_rssi /= rf_rx_num)); } static void _rtl92ce_translate_rx_signal_stuff(struct ieee80211_hw *hw, -- GitLab From fef91a2b5f5f6631dd672063b8879ed5f2501c86 Mon Sep 17 00:00:00 2001 From: Michael Straube Date: Wed, 11 Dec 2019 16:47:54 +0100 Subject: [PATCH 0976/1437] rtlwifi: rtl8192cu: use generic rtl_signal_scale_mapping Function _rtl92c_signal_scale_mapping is identical to the generic version rtl_signal_scale_mapping. Remove _rtl92c_signal_scale_mapping and use the generic function. Signed-off-by: Michael Straube Signed-off-by: Kalle Valo --- .../wireless/realtek/rtlwifi/rtl8192cu/mac.c | 33 ++----------------- 1 file changed, 2 insertions(+), 31 deletions(-) diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/mac.c b/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/mac.c index c8652283a5160..b4b67341dc839 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/mac.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/mac.c @@ -567,34 +567,6 @@ void rtl92c_set_min_space(struct ieee80211_hw *hw, bool is2T) /*==============================================================*/ -static long _rtl92c_signal_scale_mapping(struct ieee80211_hw *hw, - long currsig) -{ - long retsig; - - if (currsig >= 61 && currsig <= 100) - retsig = 90 + ((currsig - 60) / 4); - else if (currsig >= 41 && currsig <= 60) - retsig = 78 + ((currsig - 40) / 2); - else if (currsig >= 31 && currsig <= 40) - retsig = 66 + (currsig - 30); - else if (currsig >= 21 && currsig <= 30) - retsig = 54 + (currsig - 20); - else if (currsig >= 5 && currsig <= 20) - retsig = 42 + (((currsig - 5) * 2) / 3); - else if (currsig == 4) - retsig = 36; - else if (currsig == 3) - retsig = 27; - else if (currsig == 2) - retsig = 18; - else if (currsig == 1) - retsig = 9; - else - retsig = currsig; - return retsig; -} - static void _rtl92c_query_rxphystatus(struct ieee80211_hw *hw, struct rtl_stats *pstats, struct rx_desc_92c *p_desc, @@ -729,11 +701,10 @@ static void _rtl92c_query_rxphystatus(struct ieee80211_hw *hw, } if (is_cck_rate) pstats->signalstrength = - (u8) (_rtl92c_signal_scale_mapping(hw, pwdb_all)); + (u8)(rtl_signal_scale_mapping(hw, pwdb_all)); else if (rf_rx_num != 0) pstats->signalstrength = - (u8) (_rtl92c_signal_scale_mapping - (hw, total_rssi /= rf_rx_num)); + (u8)(rtl_signal_scale_mapping(hw, total_rssi /= rf_rx_num)); } void rtl92c_translate_rx_signal_stuff(struct ieee80211_hw *hw, -- GitLab From 716c733f52ecf7f587e617c2739e7a7410301968 Mon Sep 17 00:00:00 2001 From: Michael Straube Date: Wed, 11 Dec 2019 16:47:55 +0100 Subject: [PATCH 0977/1437] rtlwifi: rtl8192de: use generic rtl_signal_scale_mapping Function _rtl92de_signal_scale_mapping is identical to the generic version rtl_signal_scale_mapping. Remove _rtl92de_signal_scale_mapping and use the generic function. Signed-off-by: Michael Straube Signed-off-by: Kalle Valo --- .../wireless/realtek/rtlwifi/rtl8192de/trx.c | 31 ++----------------- 1 file changed, 2 insertions(+), 29 deletions(-) diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192de/trx.c b/drivers/net/wireless/realtek/rtlwifi/rtl8192de/trx.c index 0fb6ef7e2a0ea..ab5b05ef168e0 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192de/trx.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192de/trx.c @@ -33,33 +33,6 @@ static long _rtl92de_translate_todbm(struct ieee80211_hw *hw, return signal_power; } -static long _rtl92de_signal_scale_mapping(struct ieee80211_hw *hw, long currsig) -{ - long retsig; - - if (currsig >= 61 && currsig <= 100) - retsig = 90 + ((currsig - 60) / 4); - else if (currsig >= 41 && currsig <= 60) - retsig = 78 + ((currsig - 40) / 2); - else if (currsig >= 31 && currsig <= 40) - retsig = 66 + (currsig - 30); - else if (currsig >= 21 && currsig <= 30) - retsig = 54 + (currsig - 20); - else if (currsig >= 5 && currsig <= 20) - retsig = 42 + (((currsig - 5) * 2) / 3); - else if (currsig == 4) - retsig = 36; - else if (currsig == 3) - retsig = 27; - else if (currsig == 2) - retsig = 18; - else if (currsig == 1) - retsig = 9; - else - retsig = currsig; - return retsig; -} - static void _rtl92de_query_rxphystatus(struct ieee80211_hw *hw, struct rtl_stats *pstats, struct rx_desc_92d *pdesc, @@ -202,10 +175,10 @@ static void _rtl92de_query_rxphystatus(struct ieee80211_hw *hw, } } if (is_cck_rate) - pstats->signalstrength = (u8)(_rtl92de_signal_scale_mapping(hw, + pstats->signalstrength = (u8)(rtl_signal_scale_mapping(hw, pwdb_all)); else if (rf_rx_num != 0) - pstats->signalstrength = (u8)(_rtl92de_signal_scale_mapping(hw, + pstats->signalstrength = (u8)(rtl_signal_scale_mapping(hw, total_rssi /= rf_rx_num)); } -- GitLab From 1b8d2e0a9e4221b99eea375c079507ce8ef655f5 Mon Sep 17 00:00:00 2001 From: Wright Feng Date: Thu, 12 Dec 2019 00:52:45 +0100 Subject: [PATCH 0978/1437] brcmfmac: reset two D11 cores if chip has two D11 cores There are two D11 cores in RSDB chips like 4359. We have to reset two D11 cores simutaneously before firmware download, or the firmware may not be initialized correctly and cause "fw initialized failed" error. Signed-off-by: Wright Feng Signed-off-by: Soeren Moch Reviewed-by: Chi-Hsien Lin Signed-off-by: Kalle Valo --- .../broadcom/brcm80211/brcmfmac/chip.c | 50 +++++++++++++++++++ .../broadcom/brcm80211/brcmfmac/chip.h | 1 + .../broadcom/brcm80211/brcmfmac/pcie.c | 2 +- 3 files changed, 52 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c index a795d781b4c5e..0b5fbe5d8270a 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c @@ -433,11 +433,25 @@ static void brcmf_chip_ai_resetcore(struct brcmf_core_priv *core, u32 prereset, { struct brcmf_chip_priv *ci; int count; + struct brcmf_core *d11core2 = NULL; + struct brcmf_core_priv *d11priv2 = NULL; ci = core->chip; + /* special handle two D11 cores reset */ + if (core->pub.id == BCMA_CORE_80211) { + d11core2 = brcmf_chip_get_d11core(&ci->pub, 1); + if (d11core2) { + brcmf_dbg(INFO, "found two d11 cores, reset both\n"); + d11priv2 = container_of(d11core2, + struct brcmf_core_priv, pub); + } + } + /* must disable first to work for arbitrary current core state */ brcmf_chip_ai_coredisable(core, prereset, reset); + if (d11priv2) + brcmf_chip_ai_coredisable(d11priv2, prereset, reset); count = 0; while (ci->ops->read32(ci->ctx, core->wrapbase + BCMA_RESET_CTL) & @@ -449,9 +463,30 @@ static void brcmf_chip_ai_resetcore(struct brcmf_core_priv *core, u32 prereset, usleep_range(40, 60); } + if (d11priv2) { + count = 0; + while (ci->ops->read32(ci->ctx, + d11priv2->wrapbase + BCMA_RESET_CTL) & + BCMA_RESET_CTL_RESET) { + ci->ops->write32(ci->ctx, + d11priv2->wrapbase + BCMA_RESET_CTL, + 0); + count++; + if (count > 50) + break; + usleep_range(40, 60); + } + } + ci->ops->write32(ci->ctx, core->wrapbase + BCMA_IOCTL, postreset | BCMA_IOCTL_CLK); ci->ops->read32(ci->ctx, core->wrapbase + BCMA_IOCTL); + + if (d11priv2) { + ci->ops->write32(ci->ctx, d11priv2->wrapbase + BCMA_IOCTL, + postreset | BCMA_IOCTL_CLK); + ci->ops->read32(ci->ctx, d11priv2->wrapbase + BCMA_IOCTL); + } } char *brcmf_chip_name(u32 id, u32 rev, char *buf, uint len) @@ -1109,6 +1144,21 @@ void brcmf_chip_detach(struct brcmf_chip *pub) kfree(chip); } +struct brcmf_core *brcmf_chip_get_d11core(struct brcmf_chip *pub, u8 unit) +{ + struct brcmf_chip_priv *chip; + struct brcmf_core_priv *core; + + chip = container_of(pub, struct brcmf_chip_priv, pub); + list_for_each_entry(core, &chip->cores, list) { + if (core->pub.id == BCMA_CORE_80211) { + if (unit-- == 0) + return &core->pub; + } + } + return NULL; +} + struct brcmf_core *brcmf_chip_get_core(struct brcmf_chip *pub, u16 coreid) { struct brcmf_chip_priv *chip; diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.h index 7b00f6a59e89e..8fa38658e727a 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.h @@ -74,6 +74,7 @@ struct brcmf_chip *brcmf_chip_attach(void *ctx, const struct brcmf_buscore_ops *ops); void brcmf_chip_detach(struct brcmf_chip *chip); struct brcmf_core *brcmf_chip_get_core(struct brcmf_chip *chip, u16 coreid); +struct brcmf_core *brcmf_chip_get_d11core(struct brcmf_chip *pub, u8 unit); struct brcmf_core *brcmf_chip_get_chipcommon(struct brcmf_chip *chip); struct brcmf_core *brcmf_chip_get_pmu(struct brcmf_chip *pub); bool brcmf_chip_iscoreup(struct brcmf_core *core); diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c index f64ce5074a55a..7ac72804e285d 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c @@ -78,7 +78,7 @@ static const struct brcmf_firmware_mapping brcmf_pcie_fwnames[] = { BRCMF_FW_ENTRY(BRCM_CC_4371_CHIP_ID, 0xFFFFFFFF, 4371), }; -#define BRCMF_PCIE_FW_UP_TIMEOUT 2000 /* msec */ +#define BRCMF_PCIE_FW_UP_TIMEOUT 5000 /* msec */ #define BRCMF_PCIE_REG_MAP_SIZE (32 * 1024) -- GitLab From 172f6854551d48d1c9530f84513b421db944e714 Mon Sep 17 00:00:00 2001 From: Chung-Hsien Hsu Date: Thu, 12 Dec 2019 00:52:46 +0100 Subject: [PATCH 0979/1437] brcmfmac: set F2 blocksize and watermark for 4359 Set F2 blocksize to 256 bytes and watermark to 0x40 for 4359. Also enable and configure F1 MesBusyCtrl. It fixes DMA error while having UDP bi-directional traffic. Signed-off-by: Chung-Hsien Hsu [slightly adapted for rebase on mainline linux] Signed-off-by: Soeren Moch Reviewed-by: Chi-Hsien Lin Signed-off-by: Kalle Valo --- .../wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c | 6 +++++- .../wireless/broadcom/brcm80211/brcmfmac/sdio.c | 15 +++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c index 96fd8e2bf773c..68baf0189305f 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c @@ -43,6 +43,7 @@ #define SDIO_FUNC1_BLOCKSIZE 64 #define SDIO_FUNC2_BLOCKSIZE 512 +#define SDIO_4359_FUNC2_BLOCKSIZE 256 /* Maximum milliseconds to wait for F2 to come up */ #define SDIO_WAIT_F2RDY 3000 @@ -903,6 +904,7 @@ static void brcmf_sdiod_host_fixup(struct mmc_host *host) static int brcmf_sdiod_probe(struct brcmf_sdio_dev *sdiodev) { int ret = 0; + unsigned int f2_blksz = SDIO_FUNC2_BLOCKSIZE; sdio_claim_host(sdiodev->func1); @@ -912,7 +914,9 @@ static int brcmf_sdiod_probe(struct brcmf_sdio_dev *sdiodev) sdio_release_host(sdiodev->func1); goto out; } - ret = sdio_set_block_size(sdiodev->func2, SDIO_FUNC2_BLOCKSIZE); + if (sdiodev->func2->device == SDIO_DEVICE_ID_BROADCOM_4359) + f2_blksz = SDIO_4359_FUNC2_BLOCKSIZE; + ret = sdio_set_block_size(sdiodev->func2, f2_blksz); if (ret) { brcmf_err("Failed to set F2 blocksize\n"); sdio_release_host(sdiodev->func1); diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c index 1dea0178832ea..4d5b7770d4eab 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c @@ -42,6 +42,8 @@ #define DEFAULT_F2_WATERMARK 0x8 #define CY_4373_F2_WATERMARK 0x40 #define CY_43012_F2_WATERMARK 0x60 +#define CY_4359_F2_WATERMARK 0x40 +#define CY_4359_F1_MESBUSYCTRL (CY_4359_F2_WATERMARK | SBSDIO_MESBUSYCTRL_ENAB) #ifdef DEBUG @@ -4206,6 +4208,19 @@ static void brcmf_sdio_firmware_callback(struct device *dev, int err, brcmf_sdiod_writeb(sdiod, SBSDIO_DEVICE_CTL, devctl, &err); break; + case SDIO_DEVICE_ID_BROADCOM_4359: + brcmf_dbg(INFO, "set F2 watermark to 0x%x*4 bytes\n", + CY_4359_F2_WATERMARK); + brcmf_sdiod_writeb(sdiod, SBSDIO_WATERMARK, + CY_4359_F2_WATERMARK, &err); + devctl = brcmf_sdiod_readb(sdiod, SBSDIO_DEVICE_CTL, + &err); + devctl |= SBSDIO_DEVCTL_F2WM_ENAB; + brcmf_sdiod_writeb(sdiod, SBSDIO_DEVICE_CTL, devctl, + &err); + brcmf_sdiod_writeb(sdiod, SBSDIO_FUNC1_MESBUSYCTRL, + CY_4359_F1_MESBUSYCTRL, &err); + break; default: brcmf_sdiod_writeb(sdiod, SBSDIO_WATERMARK, DEFAULT_F2_WATERMARK, &err); -- GitLab From 6647274ed995a172369cb04754eb5f8b85f68f6d Mon Sep 17 00:00:00 2001 From: Soeren Moch Date: Thu, 12 Dec 2019 00:52:47 +0100 Subject: [PATCH 0980/1437] brcmfmac: fix rambase for 4359/9 Newer 4359 chip revisions need a different rambase address. This fixes firmware download on such devices which fails otherwise. Signed-off-by: Soeren Moch Acked-by: Chi-Hsien Lin Signed-off-by: Kalle Valo --- drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c index 0b5fbe5d8270a..baf72e3984fc9 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c @@ -712,7 +712,6 @@ static u32 brcmf_chip_tcm_rambase(struct brcmf_chip_priv *ci) case BRCM_CC_43569_CHIP_ID: case BRCM_CC_43570_CHIP_ID: case BRCM_CC_4358_CHIP_ID: - case BRCM_CC_4359_CHIP_ID: case BRCM_CC_43602_CHIP_ID: case BRCM_CC_4371_CHIP_ID: return 0x180000; @@ -722,6 +721,8 @@ static u32 brcmf_chip_tcm_rambase(struct brcmf_chip_priv *ci) case BRCM_CC_4366_CHIP_ID: case BRCM_CC_43664_CHIP_ID: return 0x200000; + case BRCM_CC_4359_CHIP_ID: + return (ci->pub.chiprev < 9) ? 0x180000 : 0x160000; case CY_CC_4373_CHIP_ID: return 0x160000; default: -- GitLab From c12c8913d79c49ceccb38f42714d25b783833758 Mon Sep 17 00:00:00 2001 From: Soeren Moch Date: Thu, 12 Dec 2019 00:52:48 +0100 Subject: [PATCH 0981/1437] brcmfmac: make errors when setting roaming parameters non-fatal 4359 dongles do not support setting roaming parameters (error -52). Do not fail the 80211 configuration in this case. Signed-off-by: Soeren Moch Acked-by: Chi-Hsien Lin Signed-off-by: Kalle Valo --- .../wireless/broadcom/brcm80211/brcmfmac/cfg80211.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c index 5598bbd09b627..0cf13cea1dbea 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c @@ -6012,19 +6012,17 @@ static s32 brcmf_dongle_roam(struct brcmf_if *ifp) roamtrigger[1] = cpu_to_le32(BRCM_BAND_ALL); err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_ROAM_TRIGGER, (void *)roamtrigger, sizeof(roamtrigger)); - if (err) { + if (err) bphy_err(drvr, "WLC_SET_ROAM_TRIGGER error (%d)\n", err); - goto roam_setup_done; - } roam_delta[0] = cpu_to_le32(WL_ROAM_DELTA); roam_delta[1] = cpu_to_le32(BRCM_BAND_ALL); err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_ROAM_DELTA, (void *)roam_delta, sizeof(roam_delta)); - if (err) { + if (err) bphy_err(drvr, "WLC_SET_ROAM_DELTA error (%d)\n", err); - goto roam_setup_done; - } + + return 0; roam_setup_done: return err; -- GitLab From d4aef159394d5940bd7158ab789969dab82f7c76 Mon Sep 17 00:00:00 2001 From: Soeren Moch Date: Thu, 12 Dec 2019 00:52:49 +0100 Subject: [PATCH 0982/1437] brcmfmac: add support for BCM4359 SDIO chipset BCM4359 is a 2x2 802.11 abgn+ac Dual-Band HT80 combo chip and it supports Real Simultaneous Dual Band feature. Based on a similar patch by: Wright Feng Signed-off-by: Soeren Moch Acked-by: Chi-Hsien Lin Acked-by: Ulf Hansson Signed-off-by: Kalle Valo --- drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c | 2 ++ drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c | 1 + drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c | 2 ++ include/linux/mmc/sdio_ids.h | 2 ++ 4 files changed, 7 insertions(+) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c index 68baf0189305f..f4c53ab46058b 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c @@ -973,8 +973,10 @@ static const struct sdio_device_id brcmf_sdmmc_ids[] = { BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_43455), BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4354), BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4356), + BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4359), BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_CYPRESS_4373), BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_CYPRESS_43012), + BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_CYPRESS_89359), { /* end: all zeroes */ } }; MODULE_DEVICE_TABLE(sdio, brcmf_sdmmc_ids); diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c index baf72e3984fc9..282d0bc14e8e3 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c @@ -1408,6 +1408,7 @@ bool brcmf_chip_sr_capable(struct brcmf_chip *pub) addr = CORE_CC_REG(base, sr_control0); reg = chip->ops->read32(chip->ctx, addr); return (reg & CC_SR_CTL0_ENABLE_MASK) != 0; + case BRCM_CC_4359_CHIP_ID: case CY_CC_43012_CHIP_ID: addr = CORE_CC_REG(pmu->base, retention_ctl); reg = chip->ops->read32(chip->ctx, addr); diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c index 4d5b7770d4eab..f9df95bc7fa12 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c @@ -616,6 +616,7 @@ BRCMF_FW_DEF(43455, "brcmfmac43455-sdio"); BRCMF_FW_DEF(43456, "brcmfmac43456-sdio"); BRCMF_FW_DEF(4354, "brcmfmac4354-sdio"); BRCMF_FW_DEF(4356, "brcmfmac4356-sdio"); +BRCMF_FW_DEF(4359, "brcmfmac4359-sdio"); BRCMF_FW_DEF(4373, "brcmfmac4373-sdio"); BRCMF_FW_DEF(43012, "brcmfmac43012-sdio"); @@ -638,6 +639,7 @@ static const struct brcmf_firmware_mapping brcmf_sdio_fwnames[] = { BRCMF_FW_ENTRY(BRCM_CC_4345_CHIP_ID, 0xFFFFFDC0, 43455), BRCMF_FW_ENTRY(BRCM_CC_4354_CHIP_ID, 0xFFFFFFFF, 4354), BRCMF_FW_ENTRY(BRCM_CC_4356_CHIP_ID, 0xFFFFFFFF, 4356), + BRCMF_FW_ENTRY(BRCM_CC_4359_CHIP_ID, 0xFFFFFFFF, 4359), BRCMF_FW_ENTRY(CY_CC_4373_CHIP_ID, 0xFFFFFFFF, 4373), BRCMF_FW_ENTRY(CY_CC_43012_CHIP_ID, 0xFFFFFFFF, 43012) }; diff --git a/include/linux/mmc/sdio_ids.h b/include/linux/mmc/sdio_ids.h index 08b25c02b5a11..2e9a6e4634eb3 100644 --- a/include/linux/mmc/sdio_ids.h +++ b/include/linux/mmc/sdio_ids.h @@ -41,8 +41,10 @@ #define SDIO_DEVICE_ID_BROADCOM_43455 0xa9bf #define SDIO_DEVICE_ID_BROADCOM_4354 0x4354 #define SDIO_DEVICE_ID_BROADCOM_4356 0x4356 +#define SDIO_DEVICE_ID_BROADCOM_4359 0x4359 #define SDIO_DEVICE_ID_CYPRESS_4373 0x4373 #define SDIO_DEVICE_ID_CYPRESS_43012 43012 +#define SDIO_DEVICE_ID_CYPRESS_89359 0x4355 #define SDIO_VENDOR_ID_INTEL 0x0089 #define SDIO_DEVICE_ID_INTEL_IWMC3200WIMAX 0x1402 -- GitLab From 837482e69a3f0d7cbc73922020012f83635f5ddb Mon Sep 17 00:00:00 2001 From: Wright Feng Date: Thu, 12 Dec 2019 00:52:50 +0100 Subject: [PATCH 0983/1437] brcmfmac: add RSDB condition when setting interface combinations With firmware RSDB feature 1. The maximum support interface is four. 2. The maximum difference channel is two. 3. The maximum interfaces of {station/p2p client/AP} are two. 4. The maximum interface of p2p device is one. Signed-off-by: Wright Feng Signed-off-by: Soeren Moch Reviewed-by: Chi-Hsien Lin Signed-off-by: Kalle Valo --- .../broadcom/brcm80211/brcmfmac/cfg80211.c | 54 ++++++++++++++++--- 1 file changed, 46 insertions(+), 8 deletions(-) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c index 0cf13cea1dbea..9d9dc9195e9e8 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c @@ -6520,6 +6520,9 @@ brcmf_txrx_stypes[NUM_NL80211_IFTYPES] = { * #STA <= 1, #AP <= 1, channels = 1, 2 total * #AP <= 4, matching BI, channels = 1, 4 total * + * no p2p and rsdb: + * #STA <= 2, #AP <= 2, channels = 2, 4 total + * * p2p, no mchan, and mbss: * * #STA <= 1, #P2P-DEV <= 1, #{P2P-CL, P2P-GO} <= 1, channels = 1, 3 total @@ -6531,6 +6534,10 @@ brcmf_txrx_stypes[NUM_NL80211_IFTYPES] = { * #STA <= 1, #P2P-DEV <= 1, #{P2P-CL, P2P-GO} <= 1, channels = 2, 3 total * #STA <= 1, #P2P-DEV <= 1, #AP <= 1, #P2P-CL <= 1, channels = 1, 4 total * #AP <= 4, matching BI, channels = 1, 4 total + * + * p2p, rsdb, and no mbss: + * #STA <= 2, #P2P-DEV <= 1, #{P2P-CL, P2P-GO} <= 2, AP <= 2, + * channels = 2, 4 total */ static int brcmf_setup_ifmodes(struct wiphy *wiphy, struct brcmf_if *ifp) { @@ -6538,13 +6545,14 @@ static int brcmf_setup_ifmodes(struct wiphy *wiphy, struct brcmf_if *ifp) struct ieee80211_iface_limit *c0_limits = NULL; struct ieee80211_iface_limit *p2p_limits = NULL; struct ieee80211_iface_limit *mbss_limits = NULL; - bool mbss, p2p; + bool mbss, p2p, rsdb; int i, c, n_combos; mbss = brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MBSS); p2p = brcmf_feat_is_enabled(ifp, BRCMF_FEAT_P2P); + rsdb = brcmf_feat_is_enabled(ifp, BRCMF_FEAT_RSDB); - n_combos = 1 + !!p2p + !!mbss; + n_combos = 1 + !!(p2p && !rsdb) + !!mbss; combo = kcalloc(n_combos, sizeof(*combo), GFP_KERNEL); if (!combo) goto err; @@ -6555,16 +6563,36 @@ static int brcmf_setup_ifmodes(struct wiphy *wiphy, struct brcmf_if *ifp) c = 0; i = 0; - c0_limits = kcalloc(p2p ? 3 : 2, sizeof(*c0_limits), GFP_KERNEL); + if (p2p && rsdb) + c0_limits = kcalloc(4, sizeof(*c0_limits), GFP_KERNEL); + else if (p2p) + c0_limits = kcalloc(3, sizeof(*c0_limits), GFP_KERNEL); + else + c0_limits = kcalloc(2, sizeof(*c0_limits), GFP_KERNEL); if (!c0_limits) goto err; - c0_limits[i].max = 1; - c0_limits[i++].types = BIT(NL80211_IFTYPE_STATION); - if (p2p) { + if (p2p && rsdb) { + combo[c].num_different_channels = 2; + wiphy->interface_modes |= BIT(NL80211_IFTYPE_P2P_CLIENT) | + BIT(NL80211_IFTYPE_P2P_GO) | + BIT(NL80211_IFTYPE_P2P_DEVICE); + c0_limits[i].max = 2; + c0_limits[i++].types = BIT(NL80211_IFTYPE_STATION); + c0_limits[i].max = 1; + c0_limits[i++].types = BIT(NL80211_IFTYPE_P2P_DEVICE); + c0_limits[i].max = 2; + c0_limits[i++].types = BIT(NL80211_IFTYPE_P2P_CLIENT) | + BIT(NL80211_IFTYPE_P2P_GO); + c0_limits[i].max = 2; + c0_limits[i++].types = BIT(NL80211_IFTYPE_AP); + combo[c].max_interfaces = 5; + } else if (p2p) { if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MCHAN)) combo[c].num_different_channels = 2; else combo[c].num_different_channels = 1; + c0_limits[i].max = 1; + c0_limits[i++].types = BIT(NL80211_IFTYPE_STATION); wiphy->interface_modes |= BIT(NL80211_IFTYPE_P2P_CLIENT) | BIT(NL80211_IFTYPE_P2P_GO) | BIT(NL80211_IFTYPE_P2P_DEVICE); @@ -6573,16 +6601,26 @@ static int brcmf_setup_ifmodes(struct wiphy *wiphy, struct brcmf_if *ifp) c0_limits[i].max = 1; c0_limits[i++].types = BIT(NL80211_IFTYPE_P2P_CLIENT) | BIT(NL80211_IFTYPE_P2P_GO); + combo[c].max_interfaces = i; + } else if (rsdb) { + combo[c].num_different_channels = 2; + c0_limits[i].max = 2; + c0_limits[i++].types = BIT(NL80211_IFTYPE_STATION); + c0_limits[i].max = 2; + c0_limits[i++].types = BIT(NL80211_IFTYPE_AP); + combo[c].max_interfaces = 3; } else { combo[c].num_different_channels = 1; c0_limits[i].max = 1; + c0_limits[i++].types = BIT(NL80211_IFTYPE_STATION); + c0_limits[i].max = 1; c0_limits[i++].types = BIT(NL80211_IFTYPE_AP); + combo[c].max_interfaces = i; } - combo[c].max_interfaces = i; combo[c].n_limits = i; combo[c].limits = c0_limits; - if (p2p) { + if (p2p && !rsdb) { c++; i = 0; p2p_limits = kcalloc(4, sizeof(*p2p_limits), GFP_KERNEL); -- GitLab From 2635853ce4ab7654a77ab7080fb56de83408606b Mon Sep 17 00:00:00 2001 From: Wright Feng Date: Thu, 12 Dec 2019 00:52:51 +0100 Subject: [PATCH 0984/1437] brcmfmac: not set mbss in vif if firmware does not support MBSS With RSDB mode, FMAC and firmware are able to create 2 or more AP, so we should not set mbss in vif structure if firmware does not support MBSS feature. Signed-off-by: Wright Feng Signed-off-by: Soeren Moch Reviewed-by: Chi-Hsien Lin Signed-off-by: Kalle Valo --- drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c index 9d9dc9195e9e8..6eb3064c37219 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c @@ -5363,6 +5363,7 @@ struct brcmf_cfg80211_vif *brcmf_alloc_vif(struct brcmf_cfg80211_info *cfg, struct brcmf_cfg80211_vif *vif_walk; struct brcmf_cfg80211_vif *vif; bool mbss; + struct brcmf_if *ifp = brcmf_get_ifp(cfg->pub, 0); brcmf_dbg(TRACE, "allocating virtual interface (size=%zu)\n", sizeof(*vif)); @@ -5375,7 +5376,8 @@ struct brcmf_cfg80211_vif *brcmf_alloc_vif(struct brcmf_cfg80211_info *cfg, brcmf_init_prof(&vif->profile); - if (type == NL80211_IFTYPE_AP) { + if (type == NL80211_IFTYPE_AP && + brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MBSS)) { mbss = false; list_for_each_entry(vif_walk, &cfg->vif_list, list) { if (vif_walk->wdev.iftype == NL80211_IFTYPE_AP) { -- GitLab From 4282dc057d750c6a7dd92953564b15c26b54c22c Mon Sep 17 00:00:00 2001 From: Navid Emamdoost Date: Sat, 14 Dec 2019 19:51:14 -0600 Subject: [PATCH 0985/1437] brcmfmac: Fix memory leak in brcmf_usbdev_qinit In the implementation of brcmf_usbdev_qinit() the allocated memory for reqs is leaking if usb_alloc_urb() fails. Release reqs in the error handling path. Fixes: 71bb244ba2fd ("brcm80211: fmac: add USB support for bcm43235/6/8 chipsets") Signed-off-by: Navid Emamdoost Signed-off-by: Kalle Valo --- drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c index 7cdfde9b3dea9..575ed19e91951 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c @@ -430,6 +430,7 @@ fail: usb_free_urb(req->urb); list_del(q->next); } + kfree(reqs); return NULL; } -- GitLab From a32de68edab7b73ded850bcf76cdf6858e92a7e5 Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Sun, 15 Dec 2019 21:42:24 +0300 Subject: [PATCH 0986/1437] brcmfmac: Keep OOB wake-interrupt disabled when it shouldn't be enabled NVIDIA Tegra SoCs do not like when OOB wake is enabled and WiFi interface is in DOWN state during suspend. This results in a CPU hang on programming OOB wake-up state of the GPIO controller during of system's suspend. The solution is trivial: don't enable wake for the OOB interrupt when it should be disabled. This fixes hang on Tegra20 (Acer A500) and Tegra30 (Nexus 7) devices which are using BCM4329 and BCM4330 WiFi chips respectively. Signed-off-by: Dmitry Osipenko Signed-off-by: Kalle Valo --- .../net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c | 10 +++++----- .../net/wireless/broadcom/brcm80211/brcmfmac/sdio.h | 1 - 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c index f4c53ab46058b..b684a5b6d9044 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c @@ -120,7 +120,7 @@ int brcmf_sdiod_intr_register(struct brcmf_sdio_dev *sdiodev) brcmf_err("enable_irq_wake failed %d\n", ret); return ret; } - sdiodev->irq_wake = true; + disable_irq_wake(pdata->oob_irq_nr); sdio_claim_host(sdiodev->func1); @@ -179,10 +179,6 @@ void brcmf_sdiod_intr_unregister(struct brcmf_sdio_dev *sdiodev) sdio_release_host(sdiodev->func1); sdiodev->oob_irq_requested = false; - if (sdiodev->irq_wake) { - disable_irq_wake(pdata->oob_irq_nr); - sdiodev->irq_wake = false; - } free_irq(pdata->oob_irq_nr, &sdiodev->func1->dev); sdiodev->irq_en = false; sdiodev->oob_irq_requested = false; @@ -1173,6 +1169,10 @@ static int brcmf_ops_sdio_resume(struct device *dev) if (ret) brcmf_err("Failed to probe device on resume\n"); } else { + if (sdiodev->wowl_enabled && + sdiodev->settings->bus.sdio.oob_irq_supported) + disable_irq_wake(sdiodev->settings->bus.sdio.oob_irq_nr); + brcmf_sdiod_freezer_off(sdiodev); } diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.h index 0bd47c119dae0..163fd664780a0 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.h @@ -178,7 +178,6 @@ struct brcmf_sdio_dev { bool sd_irq_requested; bool irq_en; /* irq enable flags */ spinlock_t irq_en_lock; - bool irq_wake; /* irq wake enable flags */ bool sg_support; uint max_request_size; ushort max_segment_count; -- GitLab From c705f9fc6a1736dcf6ec01f8206707c108dca824 Mon Sep 17 00:00:00 2001 From: Aditya Pakki Date: Sun, 15 Dec 2019 13:58:58 -0600 Subject: [PATCH 0987/1437] orinoco: avoid assertion in case of NULL pointer In ezusb_init, if upriv is NULL, the code crashes. However, the caller in ezusb_probe can handle the error and print the failure message. The patch replaces the BUG_ON call to error return. Signed-off-by: Aditya Pakki Signed-off-by: Kalle Valo --- drivers/net/wireless/intersil/orinoco/orinoco_usb.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/intersil/orinoco/orinoco_usb.c b/drivers/net/wireless/intersil/orinoco/orinoco_usb.c index 8c79b963bcffb..e753f43e0162f 100644 --- a/drivers/net/wireless/intersil/orinoco/orinoco_usb.c +++ b/drivers/net/wireless/intersil/orinoco/orinoco_usb.c @@ -1361,7 +1361,8 @@ static int ezusb_init(struct hermes *hw) int retval; BUG_ON(in_interrupt()); - BUG_ON(!upriv); + if (!upriv) + return -EINVAL; upriv->reply_count = 0; /* Write the MAGIC number on the simulated registers to keep -- GitLab From fd6c2e4c063d64511657ad0031a1677b6a914859 Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Wed, 18 Dec 2019 12:19:41 -0700 Subject: [PATCH 0988/1437] io_uring: io_wq_submit_work() should not touch req->rw I've been chasing a weird and obscure crash that was userspace stack corruption, and finally narrowed it down to a bit flip that made a stack address invalid. io_wq_submit_work() unconditionally flips the req->rw.ki_flags IOCB_NOWAIT bit, but since it's a generic work handler, this isn't valid. Normal read/write operations own that part of the request, on other types it could be something else. Move the IOCB_NOWAIT clear to the read/write handlers where it belongs. Signed-off-by: Jens Axboe --- fs/io_uring.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/fs/io_uring.c b/fs/io_uring.c index 81e7fe6dee189..6f084e3cf8351 100644 --- a/fs/io_uring.c +++ b/fs/io_uring.c @@ -1817,6 +1817,10 @@ static int io_read(struct io_kiocb *req, struct io_kiocb **nxt, return ret; } + /* Ensure we clear previously set non-block flag */ + if (!force_nonblock) + req->rw.ki_flags &= ~IOCB_NOWAIT; + file = req->file; io_size = ret; if (req->flags & REQ_F_LINK) @@ -1906,6 +1910,10 @@ static int io_write(struct io_kiocb *req, struct io_kiocb **nxt, return ret; } + /* Ensure we clear previously set non-block flag */ + if (!force_nonblock) + req->rw.ki_flags &= ~IOCB_NOWAIT; + file = kiocb->ki_filp; io_size = ret; if (req->flags & REQ_F_LINK) @@ -3274,9 +3282,6 @@ static void io_wq_submit_work(struct io_wq_work **workptr) struct io_kiocb *nxt = NULL; int ret = 0; - /* Ensure we clear previously set non-block flag */ - req->rw.ki_flags &= ~IOCB_NOWAIT; - if (work->flags & IO_WQ_WORK_CANCEL) ret = -ECANCELED; -- GitLab From b7ac893652cafadcf669f78452329727e4e255cc Mon Sep 17 00:00:00 2001 From: Jia-Ju Bai Date: Wed, 18 Dec 2019 17:21:55 +0800 Subject: [PATCH 0989/1437] net: nfc: nci: fix a possible sleep-in-atomic-context bug in nci_uart_tty_receive() The kernel may sleep while holding a spinlock. The function call path (from bottom to top) in Linux 4.19 is: net/nfc/nci/uart.c, 349: nci_skb_alloc in nci_uart_default_recv_buf net/nfc/nci/uart.c, 255: (FUNC_PTR)nci_uart_default_recv_buf in nci_uart_tty_receive net/nfc/nci/uart.c, 254: spin_lock in nci_uart_tty_receive nci_skb_alloc(GFP_KERNEL) can sleep at runtime. (FUNC_PTR) means a function pointer is called. To fix this bug, GFP_KERNEL is replaced with GFP_ATOMIC for nci_skb_alloc(). This bug is found by a static analysis tool STCheck written by myself. Signed-off-by: Jia-Ju Bai Signed-off-by: David S. Miller --- net/nfc/nci/uart.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/nfc/nci/uart.c b/net/nfc/nci/uart.c index 78fe622eba657..11b554ce07ffc 100644 --- a/net/nfc/nci/uart.c +++ b/net/nfc/nci/uart.c @@ -346,7 +346,7 @@ static int nci_uart_default_recv_buf(struct nci_uart *nu, const u8 *data, nu->rx_packet_len = -1; nu->rx_skb = nci_skb_alloc(nu->ndev, NCI_MAX_PACKET_SIZE, - GFP_KERNEL); + GFP_ATOMIC); if (!nu->rx_skb) return -ENOMEM; } -- GitLab From 08c965430869ed423921bd9058ae59f75207feb6 Mon Sep 17 00:00:00 2001 From: Jose Abreu Date: Wed, 18 Dec 2019 11:17:35 +0100 Subject: [PATCH 0990/1437] net: stmmac: selftests: Needs to check the number of Multicast regs When running the MC and UC filter tests we setup a multicast address that its expected to be blocked. If the number of available multicast registers is zero, driver will always pass the multicast packets which will fail the test. Check if available multicast addresses is enough before running the tests. Fixes: 091810dbded9 ("net: stmmac: Introduce selftests support") Signed-off-by: Jose Abreu Signed-off-by: David S. Miller --- drivers/net/ethernet/stmicro/stmmac/stmmac_selftests.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_selftests.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_selftests.c index f3d8b9336b8e3..13227909287c0 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_selftests.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_selftests.c @@ -624,6 +624,8 @@ static int stmmac_test_mcfilt(struct stmmac_priv *priv) return -EOPNOTSUPP; if (netdev_uc_count(priv->dev) >= priv->hw->unicast_filter_entries) return -EOPNOTSUPP; + if (netdev_mc_count(priv->dev) >= priv->hw->multicast_filter_bins) + return -EOPNOTSUPP; while (--tries) { /* We only need to check the mc_addr for collisions */ @@ -666,6 +668,8 @@ static int stmmac_test_ucfilt(struct stmmac_priv *priv) if (stmmac_filter_check(priv)) return -EOPNOTSUPP; + if (netdev_uc_count(priv->dev) >= priv->hw->unicast_filter_entries) + return -EOPNOTSUPP; if (netdev_mc_count(priv->dev) >= priv->hw->multicast_filter_bins) return -EOPNOTSUPP; -- GitLab From 5d626c879e238be9585bd59a61eb606c9408178a Mon Sep 17 00:00:00 2001 From: Jose Abreu Date: Wed, 18 Dec 2019 11:17:36 +0100 Subject: [PATCH 0991/1437] net: stmmac: Determine earlier the size of RX buffer Split Header feature needs to know the size of RX buffer but current code is determining it too late. Fix this by moving the RX buffer computation to earlier stage. Changes from v2: - Do not try to align already aligned buffer size Fixes: 67afd6d1cfdf ("net: stmmac: Add Split Header support and enable it in XGMAC cores") Signed-off-by: Jose Abreu Signed-off-by: David S. Miller --- .../net/ethernet/stmicro/stmmac/stmmac_main.c | 24 +++++++++---------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index bbc65bd332a8b..eccbf5daf9ed1 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -1293,19 +1293,9 @@ static int init_dma_rx_desc_rings(struct net_device *dev, gfp_t flags) struct stmmac_priv *priv = netdev_priv(dev); u32 rx_count = priv->plat->rx_queues_to_use; int ret = -ENOMEM; - int bfsize = 0; int queue; int i; - bfsize = stmmac_set_16kib_bfsize(priv, dev->mtu); - if (bfsize < 0) - bfsize = 0; - - if (bfsize < BUF_SIZE_16KiB) - bfsize = stmmac_set_bfsize(dev->mtu, priv->dma_buf_sz); - - priv->dma_buf_sz = bfsize; - /* RX INITIALIZATION */ netif_dbg(priv, probe, priv->dev, "SKB addresses:\nskb\t\tskb data\tdma data\n"); @@ -1347,8 +1337,6 @@ static int init_dma_rx_desc_rings(struct net_device *dev, gfp_t flags) } } - buf_sz = bfsize; - return 0; err_init_rx_buffers: @@ -2658,6 +2646,7 @@ static void stmmac_hw_teardown(struct net_device *dev) static int stmmac_open(struct net_device *dev) { struct stmmac_priv *priv = netdev_priv(dev); + int bfsize = 0; u32 chan; int ret; @@ -2677,7 +2666,16 @@ static int stmmac_open(struct net_device *dev) memset(&priv->xstats, 0, sizeof(struct stmmac_extra_stats)); priv->xstats.threshold = tc; - priv->dma_buf_sz = STMMAC_ALIGN(buf_sz); + bfsize = stmmac_set_16kib_bfsize(priv, dev->mtu); + if (bfsize < 0) + bfsize = 0; + + if (bfsize < BUF_SIZE_16KiB) + bfsize = stmmac_set_bfsize(dev->mtu, priv->dma_buf_sz); + + priv->dma_buf_sz = bfsize; + buf_sz = bfsize; + priv->rx_copybreak = STMMAC_RX_COPYBREAK; ret = alloc_dma_desc_resources(priv); -- GitLab From eaf4fac478077d4ed57cbca2c044c4b58a96bd98 Mon Sep 17 00:00:00 2001 From: Jose Abreu Date: Wed, 18 Dec 2019 11:17:37 +0100 Subject: [PATCH 0992/1437] net: stmmac: Do not accept invalid MTU values The maximum MTU value is determined by the maximum size of TX FIFO so that a full packet can fit in the FIFO. Add a check for this in the MTU change callback. Also check if provided and rounded MTU does not passes the maximum limit of 16K. Changes from v2: - Align MTU before checking if its valid Fixes: 7ac6653a085b ("stmmac: Move the STMicroelectronics driver") Signed-off-by: Jose Abreu Signed-off-by: David S. Miller --- drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index eccbf5daf9ed1..8b7861909ef9c 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -3827,12 +3827,24 @@ static void stmmac_set_rx_mode(struct net_device *dev) static int stmmac_change_mtu(struct net_device *dev, int new_mtu) { struct stmmac_priv *priv = netdev_priv(dev); + int txfifosz = priv->plat->tx_fifo_size; + + if (txfifosz == 0) + txfifosz = priv->dma_cap.tx_fifo_size; + + txfifosz /= priv->plat->tx_queues_to_use; if (netif_running(dev)) { netdev_err(priv->dev, "must be stopped to change its MTU\n"); return -EBUSY; } + new_mtu = STMMAC_ALIGN(new_mtu); + + /* If condition true, FIFO is too small or MTU too large */ + if ((txfifosz < new_mtu) || (new_mtu > BUF_SIZE_16KiB)) + return -EINVAL; + dev->mtu = new_mtu; netdev_update_features(dev); -- GitLab From 93b5dce401ccd4a688fe1f0d0bf0d97e63cdf921 Mon Sep 17 00:00:00 2001 From: Jose Abreu Date: Wed, 18 Dec 2019 11:17:38 +0100 Subject: [PATCH 0993/1437] net: stmmac: Only the last buffer has the FCS field Only the last received buffer contains the FCS field. Check for end of packet before trying to strip the FCS field. Fixes: 88ebe2cf7f3f ("net: stmmac: Rework stmmac_rx()") Signed-off-by: Jose Abreu Signed-off-by: David S. Miller --- drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index 8b7861909ef9c..acb14a96243e4 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -3644,8 +3644,9 @@ read_again: * feature is always disabled and packets need to be * stripped manually. */ - if (unlikely(priv->synopsys_id >= DWMAC_CORE_4_00) || - unlikely(status != llc_snap)) { + if (likely(!(status & rx_not_ls)) && + (likely(priv->synopsys_id >= DWMAC_CORE_4_00) || + unlikely(status != llc_snap))) { if (buf2_len) buf2_len -= ETH_FCS_LEN; else -- GitLab From 11d55fd9975f8e46a0e5e19c14899544e81e1e15 Mon Sep 17 00:00:00 2001 From: Jose Abreu Date: Wed, 18 Dec 2019 11:17:39 +0100 Subject: [PATCH 0994/1437] net: stmmac: xgmac: Clear previous RX buffer size When switching between buffer sizes we need to clear the previous value. Fixes: d6ddfacd95c7 ("net: stmmac: Add DMA related callbacks for XGMAC2") Signed-off-by: Jose Abreu Signed-off-by: David S. Miller --- drivers/net/ethernet/stmicro/stmmac/dwxgmac2.h | 2 ++ drivers/net/ethernet/stmicro/stmmac/dwxgmac2_dma.c | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/dwxgmac2.h b/drivers/net/ethernet/stmicro/stmmac/dwxgmac2.h index 3b6e559aa0b99..ef8a07c68ca79 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwxgmac2.h +++ b/drivers/net/ethernet/stmicro/stmmac/dwxgmac2.h @@ -343,6 +343,8 @@ #define XGMAC_DMA_CH_RX_CONTROL(x) (0x00003108 + (0x80 * (x))) #define XGMAC_RxPBL GENMASK(21, 16) #define XGMAC_RxPBL_SHIFT 16 +#define XGMAC_RBSZ GENMASK(14, 1) +#define XGMAC_RBSZ_SHIFT 1 #define XGMAC_RXST BIT(0) #define XGMAC_DMA_CH_TxDESC_HADDR(x) (0x00003110 + (0x80 * (x))) #define XGMAC_DMA_CH_TxDESC_LADDR(x) (0x00003114 + (0x80 * (x))) diff --git a/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_dma.c b/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_dma.c index 22a7f0cc1b904..f3f08ccc379bc 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_dma.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_dma.c @@ -482,7 +482,8 @@ static void dwxgmac2_set_bfsize(void __iomem *ioaddr, int bfsize, u32 chan) u32 value; value = readl(ioaddr + XGMAC_DMA_CH_RX_CONTROL(chan)); - value |= bfsize << 1; + value &= ~XGMAC_RBSZ; + value |= bfsize << XGMAC_RBSZ_SHIFT; writel(value, ioaddr + XGMAC_DMA_CH_RX_CONTROL(chan)); } -- GitLab From 8d558f0294fe92e04af192e221d0d0f6a180ee7b Mon Sep 17 00:00:00 2001 From: Jose Abreu Date: Wed, 18 Dec 2019 11:17:40 +0100 Subject: [PATCH 0995/1437] net: stmmac: RX buffer size must be 16 byte aligned We need to align the RX buffer size to at least 16 byte so that IP doesn't mis-behave. This is required by HW. Changes from v2: - Align UP and not DOWN (David) Fixes: 7ac6653a085b ("stmmac: Move the STMicroelectronics driver") Signed-off-by: Jose Abreu Signed-off-by: David S. Miller --- drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index acb14a96243e4..f507a9bee15b8 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -46,7 +46,7 @@ #include "dwxgmac2.h" #include "hwif.h" -#define STMMAC_ALIGN(x) __ALIGN_KERNEL(x, SMP_CACHE_BYTES) +#define STMMAC_ALIGN(x) ALIGN(ALIGN(x, SMP_CACHE_BYTES), 16) #define TSO_MAX_BUFF_SIZE (SZ_16K - 1) /* Module parameters */ -- GitLab From 8605131747e7e1fd8f6c9f97a00287aae2b2c640 Mon Sep 17 00:00:00 2001 From: Jose Abreu Date: Wed, 18 Dec 2019 11:17:41 +0100 Subject: [PATCH 0996/1437] net: stmmac: 16KB buffer must be 16 byte aligned The 16KB RX Buffer must also be 16 byte aligned. Fix it. Fixes: 7ac6653a085b ("stmmac: Move the STMicroelectronics driver") Signed-off-by: Jose Abreu Signed-off-by: David S. Miller --- drivers/net/ethernet/stmicro/stmmac/common.h | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/common.h b/drivers/net/ethernet/stmicro/stmmac/common.h index b210e987a1dbd..94f94686cf7de 100644 --- a/drivers/net/ethernet/stmicro/stmmac/common.h +++ b/drivers/net/ethernet/stmicro/stmmac/common.h @@ -365,9 +365,8 @@ struct dma_features { unsigned int arpoffsel; }; -/* GMAC TX FIFO is 8K, Rx FIFO is 16K */ -#define BUF_SIZE_16KiB 16384 -/* RX Buffer size must be < 8191 and multiple of 4/8/16 bytes */ +/* RX Buffer size must be multiple of 4/8/16 bytes */ +#define BUF_SIZE_16KiB 16368 #define BUF_SIZE_8KiB 8188 #define BUF_SIZE_4KiB 4096 #define BUF_SIZE_2KiB 2048 -- GitLab From b2f3a481c4cd62f78391b836b64c0a6e72b503d2 Mon Sep 17 00:00:00 2001 From: Jose Abreu Date: Wed, 18 Dec 2019 11:17:42 +0100 Subject: [PATCH 0997/1437] net: stmmac: Enable 16KB buffer size XGMAC supports maximum MTU that can go to 16KB. Lets add this check in the calculation of RX buffer size. Fixes: 7ac6653a085b ("stmmac: Move the STMicroelectronics driver") Signed-off-by: Jose Abreu Signed-off-by: David S. Miller --- drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index f507a9bee15b8..1cb466dd5b3fc 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -1109,7 +1109,9 @@ static int stmmac_set_bfsize(int mtu, int bufsize) { int ret = bufsize; - if (mtu >= BUF_SIZE_4KiB) + if (mtu >= BUF_SIZE_8KiB) + ret = BUF_SIZE_16KiB; + else if (mtu >= BUF_SIZE_4KiB) ret = BUF_SIZE_8KiB; else if (mtu >= BUF_SIZE_2KiB) ret = BUF_SIZE_4KiB; -- GitLab From 4772f26db8d1fb568c4862c538344a1b5fb52081 Mon Sep 17 00:00:00 2001 From: Jose Abreu Date: Wed, 18 Dec 2019 11:17:43 +0100 Subject: [PATCH 0998/1437] net: stmmac: Always arm TX Timer at end of transmission start If TX Coalesce timer is enabled we should always arm it, otherwise we may hit the case where an interrupt is missed and the TX Queue will timeout. Arming the timer does not necessarly mean it will run the tx_clean() because this function is wrapped around NAPI launcher. Fixes: 9125cdd1be11 ("stmmac: add the initial tx coalesce schema") Signed-off-by: Jose Abreu Signed-off-by: David S. Miller --- drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index 1cb466dd5b3fc..6f51a265459d7 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -3053,8 +3053,6 @@ static netdev_tx_t stmmac_tso_xmit(struct sk_buff *skb, struct net_device *dev) tx_q->tx_count_frames = 0; stmmac_set_tx_ic(priv, desc); priv->xstats.tx_set_ic_bit++; - } else { - stmmac_tx_timer_arm(priv, queue); } /* We've used all descriptors we need for this skb, however, @@ -3125,6 +3123,7 @@ static netdev_tx_t stmmac_tso_xmit(struct sk_buff *skb, struct net_device *dev) tx_q->tx_tail_addr = tx_q->dma_tx_phy + (tx_q->cur_tx * sizeof(*desc)); stmmac_set_tx_tail_ptr(priv, priv->ioaddr, tx_q->tx_tail_addr, queue); + stmmac_tx_timer_arm(priv, queue); return NETDEV_TX_OK; @@ -3276,8 +3275,6 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev) tx_q->tx_count_frames = 0; stmmac_set_tx_ic(priv, desc); priv->xstats.tx_set_ic_bit++; - } else { - stmmac_tx_timer_arm(priv, queue); } /* We've used all descriptors we need for this skb, however, @@ -3366,6 +3363,7 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev) tx_q->tx_tail_addr = tx_q->dma_tx_phy + (tx_q->cur_tx * sizeof(*desc)); stmmac_set_tx_tail_ptr(priv, priv->ioaddr, tx_q->tx_tail_addr, queue); + stmmac_tx_timer_arm(priv, queue); return NETDEV_TX_OK; -- GitLab From 7d0b447a3f82f5a1a70bae20c362f332e9fa91ea Mon Sep 17 00:00:00 2001 From: Jose Abreu Date: Wed, 18 Dec 2019 11:24:43 +0100 Subject: [PATCH 0999/1437] net: stmmac: Print more information in DebugFS DMA Capabilities file DMA Capabilites have grown but the DebugFS that shows this info has not been updated. Lets add the missing information. Signed-off-by: Jose Abreu Signed-off-by: David S. Miller --- .../net/ethernet/stmicro/stmmac/stmmac_main.c | 31 ++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index da80866d03716..3299f5bb02e20 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -4238,9 +4238,38 @@ static int stmmac_dma_cap_show(struct seq_file *seq, void *v) priv->dma_cap.number_rx_channel); seq_printf(seq, "\tNumber of Additional TX channel: %d\n", priv->dma_cap.number_tx_channel); + seq_printf(seq, "\tNumber of Additional RX queues: %d\n", + priv->dma_cap.number_rx_queues); + seq_printf(seq, "\tNumber of Additional TX queues: %d\n", + priv->dma_cap.number_tx_queues); seq_printf(seq, "\tEnhanced descriptors: %s\n", (priv->dma_cap.enh_desc) ? "Y" : "N"); - + seq_printf(seq, "\tTX Fifo Size: %d\n", priv->dma_cap.tx_fifo_size); + seq_printf(seq, "\tRX Fifo Size: %d\n", priv->dma_cap.rx_fifo_size); + seq_printf(seq, "\tHash Table Size: %d\n", priv->dma_cap.hash_tb_sz); + seq_printf(seq, "\tTSO: %s\n", priv->dma_cap.tsoen ? "Y" : "N"); + seq_printf(seq, "\tNumber of PPS Outputs: %d\n", + priv->dma_cap.pps_out_num); + seq_printf(seq, "\tSafety Features: %s\n", + priv->dma_cap.asp ? "Y" : "N"); + seq_printf(seq, "\tFlexible RX Parser: %s\n", + priv->dma_cap.frpsel ? "Y" : "N"); + seq_printf(seq, "\tEnhanced Addressing: %d\n", + priv->dma_cap.addr64); + seq_printf(seq, "\tReceive Side Scaling: %s\n", + priv->dma_cap.rssen ? "Y" : "N"); + seq_printf(seq, "\tVLAN Hash Filtering: %s\n", + priv->dma_cap.vlhash ? "Y" : "N"); + seq_printf(seq, "\tSplit Header: %s\n", + priv->dma_cap.sphen ? "Y" : "N"); + seq_printf(seq, "\tVLAN TX Insertion: %s\n", + priv->dma_cap.vlins ? "Y" : "N"); + seq_printf(seq, "\tDouble VLAN: %s\n", + priv->dma_cap.dvlan ? "Y" : "N"); + seq_printf(seq, "\tNumber of L3/L4 Filters: %d\n", + priv->dma_cap.l3l4fnum); + seq_printf(seq, "\tARP Offloading: %s\n", + priv->dma_cap.arpoffsel ? "Y" : "N"); return 0; } DEFINE_SHOW_ATTRIBUTE(stmmac_dma_cap); -- GitLab From 021bd5e369709717231338a182f00611794fb588 Mon Sep 17 00:00:00 2001 From: Jose Abreu Date: Wed, 18 Dec 2019 11:24:44 +0100 Subject: [PATCH 1000/1437] net: stmmac: Let TX and RX interrupts be independently enabled/disabled By using this mechanism we can get rid of the not so nice method of scheduling TX NAPI when the RX was scheduled. No bandwidth reduction was seen with this change. Changes from v1: - Remove useless comment (Jakub) - Do not bind the TX clean to NAPI budget (Jakub) Signed-off-by: Jose Abreu Signed-off-by: David S. Miller --- .../net/ethernet/stmicro/stmmac/dwmac-sun8i.c | 24 ++++++-- .../net/ethernet/stmicro/stmmac/dwmac4_dma.h | 11 +++- .../net/ethernet/stmicro/stmmac/dwmac4_lib.c | 47 +++++++++++++--- .../net/ethernet/stmicro/stmmac/dwmac_dma.h | 6 +- .../net/ethernet/stmicro/stmmac/dwmac_lib.c | 22 ++++++-- .../net/ethernet/stmicro/stmmac/dwxgmac2.h | 2 + .../ethernet/stmicro/stmmac/dwxgmac2_dma.c | 24 ++++++-- drivers/net/ethernet/stmicro/stmmac/hwif.h | 6 +- drivers/net/ethernet/stmicro/stmmac/stmmac.h | 1 + .../net/ethernet/stmicro/stmmac/stmmac_main.c | 55 +++++++++++-------- 10 files changed, 149 insertions(+), 49 deletions(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c index 1c8d84ed84102..6f834302fda31 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c @@ -335,14 +335,30 @@ static void sun8i_dwmac_dump_mac_regs(struct mac_device_info *hw, } } -static void sun8i_dwmac_enable_dma_irq(void __iomem *ioaddr, u32 chan) +static void sun8i_dwmac_enable_dma_irq(void __iomem *ioaddr, u32 chan, + bool rx, bool tx) { - writel(EMAC_RX_INT | EMAC_TX_INT, ioaddr + EMAC_INT_EN); + u32 value = readl(ioaddr + EMAC_INT_EN); + + if (rx) + value |= EMAC_RX_INT; + if (tx) + value |= EMAC_TX_INT; + + writel(value, ioaddr + EMAC_INT_EN); } -static void sun8i_dwmac_disable_dma_irq(void __iomem *ioaddr, u32 chan) +static void sun8i_dwmac_disable_dma_irq(void __iomem *ioaddr, u32 chan, + bool rx, bool tx) { - writel(0, ioaddr + EMAC_INT_EN); + u32 value = readl(ioaddr + EMAC_INT_EN); + + if (rx) + value &= ~EMAC_RX_INT; + if (tx) + value &= ~EMAC_TX_INT; + + writel(value, ioaddr + EMAC_INT_EN); } static void sun8i_dwmac_dma_start_tx(void __iomem *ioaddr, u32 chan) diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.h b/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.h index 5899317958470..bcb6d5190f3d0 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.h +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.h @@ -168,6 +168,8 @@ /* DMA default interrupt mask for 4.00 */ #define DMA_CHAN_INTR_DEFAULT_MASK (DMA_CHAN_INTR_NORMAL | \ DMA_CHAN_INTR_ABNORMAL) +#define DMA_CHAN_INTR_DEFAULT_RX (DMA_CHAN_INTR_ENA_RIE) +#define DMA_CHAN_INTR_DEFAULT_TX (DMA_CHAN_INTR_ENA_TIE) #define DMA_CHAN_INTR_NORMAL_4_10 (DMA_CHAN_INTR_ENA_NIE_4_10 | \ DMA_CHAN_INTR_ENA_RIE | \ @@ -178,6 +180,8 @@ /* DMA default interrupt mask for 4.10a */ #define DMA_CHAN_INTR_DEFAULT_MASK_4_10 (DMA_CHAN_INTR_NORMAL_4_10 | \ DMA_CHAN_INTR_ABNORMAL_4_10) +#define DMA_CHAN_INTR_DEFAULT_RX_4_10 (DMA_CHAN_INTR_ENA_RIE) +#define DMA_CHAN_INTR_DEFAULT_TX_4_10 (DMA_CHAN_INTR_ENA_TIE) /* channel 0 specific fields */ #define DMA_CHAN0_DBG_STAT_TPS GENMASK(15, 12) @@ -186,9 +190,10 @@ #define DMA_CHAN0_DBG_STAT_RPS_SHIFT 8 int dwmac4_dma_reset(void __iomem *ioaddr); -void dwmac4_enable_dma_irq(void __iomem *ioaddr, u32 chan); -void dwmac410_enable_dma_irq(void __iomem *ioaddr, u32 chan); -void dwmac4_disable_dma_irq(void __iomem *ioaddr, u32 chan); +void dwmac4_enable_dma_irq(void __iomem *ioaddr, u32 chan, bool rx, bool tx); +void dwmac410_enable_dma_irq(void __iomem *ioaddr, u32 chan, bool rx, bool tx); +void dwmac4_disable_dma_irq(void __iomem *ioaddr, u32 chan, bool rx, bool tx); +void dwmac410_disable_dma_irq(void __iomem *ioaddr, u32 chan, bool rx, bool tx); void dwmac4_dma_start_tx(void __iomem *ioaddr, u32 chan); void dwmac4_dma_stop_tx(void __iomem *ioaddr, u32 chan); void dwmac4_dma_start_rx(void __iomem *ioaddr, u32 chan); diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4_lib.c b/drivers/net/ethernet/stmicro/stmmac/dwmac4_lib.c index f2a29a90e0854..9becca2800740 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac4_lib.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4_lib.c @@ -97,21 +97,52 @@ void dwmac4_set_rx_ring_len(void __iomem *ioaddr, u32 len, u32 chan) writel(len, ioaddr + DMA_CHAN_RX_RING_LEN(chan)); } -void dwmac4_enable_dma_irq(void __iomem *ioaddr, u32 chan) +void dwmac4_enable_dma_irq(void __iomem *ioaddr, u32 chan, bool rx, bool tx) { - writel(DMA_CHAN_INTR_DEFAULT_MASK, ioaddr + - DMA_CHAN_INTR_ENA(chan)); + u32 value = readl(ioaddr + DMA_CHAN_INTR_ENA(chan)); + + if (rx) + value |= DMA_CHAN_INTR_DEFAULT_RX; + if (tx) + value |= DMA_CHAN_INTR_DEFAULT_TX; + + writel(value, ioaddr + DMA_CHAN_INTR_ENA(chan)); } -void dwmac410_enable_dma_irq(void __iomem *ioaddr, u32 chan) +void dwmac410_enable_dma_irq(void __iomem *ioaddr, u32 chan, bool rx, bool tx) { - writel(DMA_CHAN_INTR_DEFAULT_MASK_4_10, - ioaddr + DMA_CHAN_INTR_ENA(chan)); + u32 value = readl(ioaddr + DMA_CHAN_INTR_ENA(chan)); + + if (rx) + value |= DMA_CHAN_INTR_DEFAULT_RX_4_10; + if (tx) + value |= DMA_CHAN_INTR_DEFAULT_TX_4_10; + + writel(value, ioaddr + DMA_CHAN_INTR_ENA(chan)); } -void dwmac4_disable_dma_irq(void __iomem *ioaddr, u32 chan) +void dwmac4_disable_dma_irq(void __iomem *ioaddr, u32 chan, bool rx, bool tx) { - writel(0, ioaddr + DMA_CHAN_INTR_ENA(chan)); + u32 value = readl(ioaddr + DMA_CHAN_INTR_ENA(chan)); + + if (rx) + value &= ~DMA_CHAN_INTR_DEFAULT_RX; + if (tx) + value &= ~DMA_CHAN_INTR_DEFAULT_TX; + + writel(value, ioaddr + DMA_CHAN_INTR_ENA(chan)); +} + +void dwmac410_disable_dma_irq(void __iomem *ioaddr, u32 chan, bool rx, bool tx) +{ + u32 value = readl(ioaddr + DMA_CHAN_INTR_ENA(chan)); + + if (rx) + value &= ~DMA_CHAN_INTR_DEFAULT_RX_4_10; + if (tx) + value &= ~DMA_CHAN_INTR_DEFAULT_TX_4_10; + + writel(value, ioaddr + DMA_CHAN_INTR_ENA(chan)); } int dwmac4_dma_interrupt(void __iomem *ioaddr, diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac_dma.h b/drivers/net/ethernet/stmicro/stmmac/dwmac_dma.h index 292b880f3f9fc..e5dbd0bc257e7 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac_dma.h +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac_dma.h @@ -96,6 +96,8 @@ /* DMA default interrupt mask */ #define DMA_INTR_DEFAULT_MASK (DMA_INTR_NORMAL | DMA_INTR_ABNORMAL) +#define DMA_INTR_DEFAULT_RX (DMA_INTR_ENA_RIE) +#define DMA_INTR_DEFAULT_TX (DMA_INTR_ENA_TIE) /* DMA Status register defines */ #define DMA_STATUS_GLPII 0x40000000 /* GMAC LPI interrupt */ @@ -130,8 +132,8 @@ #define NUM_DWMAC1000_DMA_REGS 23 void dwmac_enable_dma_transmission(void __iomem *ioaddr); -void dwmac_enable_dma_irq(void __iomem *ioaddr, u32 chan); -void dwmac_disable_dma_irq(void __iomem *ioaddr, u32 chan); +void dwmac_enable_dma_irq(void __iomem *ioaddr, u32 chan, bool rx, bool tx); +void dwmac_disable_dma_irq(void __iomem *ioaddr, u32 chan, bool rx, bool tx); void dwmac_dma_start_tx(void __iomem *ioaddr, u32 chan); void dwmac_dma_stop_tx(void __iomem *ioaddr, u32 chan); void dwmac_dma_start_rx(void __iomem *ioaddr, u32 chan); diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac_lib.c b/drivers/net/ethernet/stmicro/stmmac/dwmac_lib.c index 1bc25aa86dbd2..688d36095333c 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac_lib.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac_lib.c @@ -37,14 +37,28 @@ void dwmac_enable_dma_transmission(void __iomem *ioaddr) writel(1, ioaddr + DMA_XMT_POLL_DEMAND); } -void dwmac_enable_dma_irq(void __iomem *ioaddr, u32 chan) +void dwmac_enable_dma_irq(void __iomem *ioaddr, u32 chan, bool rx, bool tx) { - writel(DMA_INTR_DEFAULT_MASK, ioaddr + DMA_INTR_ENA); + u32 value = readl(ioaddr + DMA_INTR_ENA); + + if (rx) + value |= DMA_INTR_DEFAULT_RX; + if (tx) + value |= DMA_INTR_DEFAULT_TX; + + writel(value, ioaddr + DMA_INTR_ENA); } -void dwmac_disable_dma_irq(void __iomem *ioaddr, u32 chan) +void dwmac_disable_dma_irq(void __iomem *ioaddr, u32 chan, bool rx, bool tx) { - writel(0, ioaddr + DMA_INTR_ENA); + u32 value = readl(ioaddr + DMA_INTR_ENA); + + if (rx) + value &= ~DMA_INTR_DEFAULT_RX; + if (tx) + value &= ~DMA_INTR_DEFAULT_TX; + + writel(value, ioaddr + DMA_INTR_ENA); } void dwmac_dma_start_tx(void __iomem *ioaddr, u32 chan) diff --git a/drivers/net/ethernet/stmicro/stmmac/dwxgmac2.h b/drivers/net/ethernet/stmicro/stmmac/dwxgmac2.h index 3b6e559aa0b99..158cf4ad1596f 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwxgmac2.h +++ b/drivers/net/ethernet/stmicro/stmmac/dwxgmac2.h @@ -361,6 +361,8 @@ #define XGMAC_TIE BIT(0) #define XGMAC_DMA_INT_DEFAULT_EN (XGMAC_NIE | XGMAC_AIE | XGMAC_RBUE | \ XGMAC_RIE | XGMAC_TIE) +#define XGMAC_DMA_INT_DEFAULT_RX (XGMAC_RBUE | XGMAC_RIE) +#define XGMAC_DMA_INT_DEFAULT_TX (XGMAC_TIE) #define XGMAC_DMA_CH_Rx_WATCHDOG(x) (0x0000313c + (0x80 * (x))) #define XGMAC_RWT GENMASK(7, 0) #define XGMAC_DMA_CH_STATUS(x) (0x00003160 + (0x80 * (x))) diff --git a/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_dma.c b/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_dma.c index 22a7f0cc1b904..ae066f4e99a81 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_dma.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_dma.c @@ -248,14 +248,30 @@ static void dwxgmac2_dma_tx_mode(void __iomem *ioaddr, int mode, writel(value, ioaddr + XGMAC_MTL_TXQ_OPMODE(channel)); } -static void dwxgmac2_enable_dma_irq(void __iomem *ioaddr, u32 chan) +static void dwxgmac2_enable_dma_irq(void __iomem *ioaddr, u32 chan, + bool rx, bool tx) { - writel(XGMAC_DMA_INT_DEFAULT_EN, ioaddr + XGMAC_DMA_CH_INT_EN(chan)); + u32 value = readl(ioaddr + XGMAC_DMA_CH_INT_EN(chan)); + + if (rx) + value |= XGMAC_DMA_INT_DEFAULT_RX; + if (tx) + value |= XGMAC_DMA_INT_DEFAULT_TX; + + writel(value, ioaddr + XGMAC_DMA_CH_INT_EN(chan)); } -static void dwxgmac2_disable_dma_irq(void __iomem *ioaddr, u32 chan) +static void dwxgmac2_disable_dma_irq(void __iomem *ioaddr, u32 chan, + bool rx, bool tx) { - writel(0, ioaddr + XGMAC_DMA_CH_INT_EN(chan)); + u32 value = readl(ioaddr + XGMAC_DMA_CH_INT_EN(chan)); + + if (rx) + value &= ~XGMAC_DMA_INT_DEFAULT_RX; + if (tx) + value &= ~XGMAC_DMA_INT_DEFAULT_TX; + + writel(value, ioaddr + XGMAC_DMA_CH_INT_EN(chan)); } static void dwxgmac2_dma_start_tx(void __iomem *ioaddr, u32 chan) diff --git a/drivers/net/ethernet/stmicro/stmmac/hwif.h b/drivers/net/ethernet/stmicro/stmmac/hwif.h index aa5b917398fe2..098fbe7a58623 100644 --- a/drivers/net/ethernet/stmicro/stmmac/hwif.h +++ b/drivers/net/ethernet/stmicro/stmmac/hwif.h @@ -187,8 +187,10 @@ struct stmmac_dma_ops { void (*dma_diagnostic_fr) (void *data, struct stmmac_extra_stats *x, void __iomem *ioaddr); void (*enable_dma_transmission) (void __iomem *ioaddr); - void (*enable_dma_irq)(void __iomem *ioaddr, u32 chan); - void (*disable_dma_irq)(void __iomem *ioaddr, u32 chan); + void (*enable_dma_irq)(void __iomem *ioaddr, u32 chan, + bool rx, bool tx); + void (*disable_dma_irq)(void __iomem *ioaddr, u32 chan, + bool rx, bool tx); void (*start_tx)(void __iomem *ioaddr, u32 chan); void (*stop_tx)(void __iomem *ioaddr, u32 chan); void (*start_rx)(void __iomem *ioaddr, u32 chan); diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac.h b/drivers/net/ethernet/stmicro/stmmac/stmmac.h index d993fc7e82c3a..f98c5eefb3827 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac.h +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac.h @@ -88,6 +88,7 @@ struct stmmac_channel { struct napi_struct rx_napi ____cacheline_aligned_in_smp; struct napi_struct tx_napi ____cacheline_aligned_in_smp; struct stmmac_priv *priv_data; + spinlock_t lock; u32 index; }; diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index 3299f5bb02e20..d1a22c4fe67b5 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -2069,17 +2069,25 @@ static int stmmac_napi_check(struct stmmac_priv *priv, u32 chan) int status = stmmac_dma_interrupt_status(priv, priv->ioaddr, &priv->xstats, chan); struct stmmac_channel *ch = &priv->channel[chan]; + unsigned long flags; if ((status & handle_rx) && (chan < priv->plat->rx_queues_to_use)) { if (napi_schedule_prep(&ch->rx_napi)) { - stmmac_disable_dma_irq(priv, priv->ioaddr, chan); + spin_lock_irqsave(&ch->lock, flags); + stmmac_disable_dma_irq(priv, priv->ioaddr, chan, 1, 0); + spin_unlock_irqrestore(&ch->lock, flags); __napi_schedule_irqoff(&ch->rx_napi); - status |= handle_tx; } } - if ((status & handle_tx) && (chan < priv->plat->tx_queues_to_use)) - napi_schedule_irqoff(&ch->tx_napi); + if ((status & handle_tx) && (chan < priv->plat->tx_queues_to_use)) { + if (napi_schedule_prep(&ch->tx_napi)) { + spin_lock_irqsave(&ch->lock, flags); + stmmac_disable_dma_irq(priv, priv->ioaddr, chan, 0, 1); + spin_unlock_irqrestore(&ch->lock, flags); + __napi_schedule_irqoff(&ch->tx_napi); + } + } return status; } @@ -2274,14 +2282,14 @@ static void stmmac_tx_timer(struct timer_list *t) ch = &priv->channel[tx_q->queue_index]; - /* - * If NAPI is already running we can miss some events. Let's rearm - * the timer and try again. - */ - if (likely(napi_schedule_prep(&ch->tx_napi))) + if (likely(napi_schedule_prep(&ch->tx_napi))) { + unsigned long flags; + + spin_lock_irqsave(&ch->lock, flags); + stmmac_disable_dma_irq(priv, priv->ioaddr, ch->index, 0, 1); + spin_unlock_irqrestore(&ch->lock, flags); __napi_schedule(&ch->tx_napi); - else - mod_timer(&tx_q->txtimer, STMMAC_COAL_TIMER(10)); + } } /** @@ -3751,8 +3759,14 @@ static int stmmac_napi_poll_rx(struct napi_struct *napi, int budget) priv->xstats.napi_poll++; work_done = stmmac_rx(priv, budget, chan); - if (work_done < budget && napi_complete_done(napi, work_done)) - stmmac_enable_dma_irq(priv, priv->ioaddr, chan); + if (work_done < budget && napi_complete_done(napi, work_done)) { + unsigned long flags; + + spin_lock_irqsave(&ch->lock, flags); + stmmac_enable_dma_irq(priv, priv->ioaddr, chan, 1, 0); + spin_unlock_irqrestore(&ch->lock, flags); + } + return work_done; } @@ -3761,7 +3775,6 @@ static int stmmac_napi_poll_tx(struct napi_struct *napi, int budget) struct stmmac_channel *ch = container_of(napi, struct stmmac_channel, tx_napi); struct stmmac_priv *priv = ch->priv_data; - struct stmmac_tx_queue *tx_q; u32 chan = ch->index; int work_done; @@ -3770,15 +3783,12 @@ static int stmmac_napi_poll_tx(struct napi_struct *napi, int budget) work_done = stmmac_tx_clean(priv, DMA_TX_SIZE, chan); work_done = min(work_done, budget); - if (work_done < budget) - napi_complete_done(napi, work_done); + if (work_done < budget && napi_complete_done(napi, work_done)) { + unsigned long flags; - /* Force transmission restart */ - tx_q = &priv->tx_queue[chan]; - if (tx_q->cur_tx != tx_q->dirty_tx) { - stmmac_enable_dma_transmission(priv, priv->ioaddr); - stmmac_set_tx_tail_ptr(priv, priv->ioaddr, tx_q->tx_tail_addr, - chan); + spin_lock_irqsave(&ch->lock, flags); + stmmac_enable_dma_irq(priv, priv->ioaddr, chan, 0, 1); + spin_unlock_irqrestore(&ch->lock, flags); } return work_done; @@ -4714,6 +4724,7 @@ int stmmac_dvr_probe(struct device *device, for (queue = 0; queue < maxq; queue++) { struct stmmac_channel *ch = &priv->channel[queue]; + spin_lock_init(&ch->lock); ch->priv_data = priv; ch->index = queue; -- GitLab From 3755b21b04b096347605d17008c6b6a8cf3f59e4 Mon Sep 17 00:00:00 2001 From: Jose Abreu Date: Wed, 18 Dec 2019 11:24:45 +0100 Subject: [PATCH 1001/1437] net: stmmac: Always use TX coalesce timer value when rescheduling When we have pending packets we re-arm the TX timer with a magic value. This changes the re-arm of the timer from 10us to the user-defined coalesce value. As we support different speeds, having a magic value of 10us can be either too short or to large depending on the speed so we let user configure it. The default value of the timer is 1ms but it can be reconfigured by ethtool. Changes from v1: - Reword commit message (Jakub) Signed-off-by: Jose Abreu Signed-off-by: David S. Miller --- drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index d1a22c4fe67b5..ecb89c609fb20 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -1975,7 +1975,7 @@ static int stmmac_tx_clean(struct stmmac_priv *priv, int budget, u32 queue) /* We still have pending packets, let's call for a new scheduling */ if (tx_q->dirty_tx != tx_q->cur_tx) - mod_timer(&tx_q->txtimer, STMMAC_COAL_TIMER(10)); + mod_timer(&tx_q->txtimer, STMMAC_COAL_TIMER(priv->tx_coal_timer)); __netif_tx_unlock_bh(netdev_get_tx_queue(priv->dev, queue)); -- GitLab From 504723af0d85434be5fb6f2dde0b62644a7f1ead Mon Sep 17 00:00:00 2001 From: Jose Abreu Date: Wed, 18 Dec 2019 11:33:05 +0100 Subject: [PATCH 1002/1437] net: stmmac: Add basic EST support for GMAC5+ Adds the support for EST in GMAC5+ cores. This feature allows to offload scheduling of queues opening time to the IP. Signed-off-by: Jose Abreu Signed-off-by: David S. Miller --- drivers/net/ethernet/stmicro/stmmac/common.h | 4 + drivers/net/ethernet/stmicro/stmmac/dwmac4.h | 9 ++ .../net/ethernet/stmicro/stmmac/dwmac4_core.c | 2 + .../net/ethernet/stmicro/stmmac/dwmac4_dma.c | 3 + drivers/net/ethernet/stmicro/stmmac/dwmac5.c | 95 +++++++++++++++++++ drivers/net/ethernet/stmicro/stmmac/dwmac5.h | 19 ++++ drivers/net/ethernet/stmicro/stmmac/hwif.h | 5 + include/linux/stmmac.h | 13 +++ 8 files changed, 150 insertions(+) diff --git a/drivers/net/ethernet/stmicro/stmmac/common.h b/drivers/net/ethernet/stmicro/stmmac/common.h index b210e987a1dbd..2f1fc93a9b1ed 100644 --- a/drivers/net/ethernet/stmicro/stmmac/common.h +++ b/drivers/net/ethernet/stmicro/stmmac/common.h @@ -363,6 +363,10 @@ struct dma_features { unsigned int dvlan; unsigned int l3l4fnum; unsigned int arpoffsel; + /* TSN Features */ + unsigned int estwid; + unsigned int estdep; + unsigned int estsel; }; /* GMAC TX FIFO is 8K, Rx FIFO is 16K */ diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4.h b/drivers/net/ethernet/stmicro/stmmac/dwmac4.h index 2dc70d1041613..f7f5a27511471 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac4.h +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4.h @@ -176,6 +176,8 @@ enum power_event { #define GMAC_CONFIG_SARC GENMASK(30, 28) #define GMAC_CONFIG_SARC_SHIFT 28 #define GMAC_CONFIG_IPC BIT(27) +#define GMAC_CONFIG_IPG GENMASK(26, 24) +#define GMAC_CONFIG_IPG_SHIFT 24 #define GMAC_CONFIG_2K BIT(22) #define GMAC_CONFIG_ACS BIT(20) #define GMAC_CONFIG_BE BIT(18) @@ -183,6 +185,7 @@ enum power_event { #define GMAC_CONFIG_JE BIT(16) #define GMAC_CONFIG_PS BIT(15) #define GMAC_CONFIG_FES BIT(14) +#define GMAC_CONFIG_FES_SHIFT 14 #define GMAC_CONFIG_DM BIT(13) #define GMAC_CONFIG_LM BIT(12) #define GMAC_CONFIG_DCRS BIT(9) @@ -190,6 +193,9 @@ enum power_event { #define GMAC_CONFIG_RE BIT(0) /* MAC extended config */ +#define GMAC_CONFIG_EIPG GENMASK(29, 25) +#define GMAC_CONFIG_EIPG_SHIFT 25 +#define GMAC_CONFIG_EIPG_EN BIT(24) #define GMAC_CONFIG_HDSMS GENMASK(22, 20) #define GMAC_CONFIG_HDSMS_SHIFT 20 #define GMAC_CONFIG_HDSMS_256 (0x2 << GMAC_CONFIG_HDSMS_SHIFT) @@ -231,6 +237,9 @@ enum power_event { /* MAC HW features3 bitmap */ #define GMAC_HW_FEAT_ASP GENMASK(29, 28) +#define GMAC_HW_FEAT_ESTWID GENMASK(21, 20) +#define GMAC_HW_FEAT_ESTDEP GENMASK(19, 17) +#define GMAC_HW_FEAT_ESTSEL BIT(16) #define GMAC_HW_FEAT_FRPES GENMASK(14, 13) #define GMAC_HW_FEAT_FRPBS GENMASK(12, 11) #define GMAC_HW_FEAT_FRPSEL BIT(10) diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c b/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c index 40ca00e596dd7..8df7496411a02 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c @@ -984,6 +984,7 @@ const struct stmmac_ops dwmac410_ops = { .set_arp_offload = dwmac4_set_arp_offload, .config_l3_filter = dwmac4_config_l3_filter, .config_l4_filter = dwmac4_config_l4_filter, + .est_configure = dwmac5_est_configure, }; const struct stmmac_ops dwmac510_ops = { @@ -1027,6 +1028,7 @@ const struct stmmac_ops dwmac510_ops = { .set_arp_offload = dwmac4_set_arp_offload, .config_l3_filter = dwmac4_config_l3_filter, .config_l4_filter = dwmac4_config_l4_filter, + .est_configure = dwmac5_est_configure, }; int dwmac4_setup(struct stmmac_priv *priv) diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c b/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c index c154090307109..3552ce1201ffe 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c @@ -404,6 +404,9 @@ static void dwmac4_get_hw_feature(void __iomem *ioaddr, /* 5.10 Features */ dma_cap->asp = (hw_cap & GMAC_HW_FEAT_ASP) >> 28; + dma_cap->estwid = (hw_cap & GMAC_HW_FEAT_ESTWID) >> 20; + dma_cap->estdep = (hw_cap & GMAC_HW_FEAT_ESTDEP) >> 17; + dma_cap->estsel = (hw_cap & GMAC_HW_FEAT_ESTSEL) >> 16; dma_cap->frpes = (hw_cap & GMAC_HW_FEAT_FRPES) >> 13; dma_cap->frpbs = (hw_cap & GMAC_HW_FEAT_FRPBS) >> 11; dma_cap->frpsel = (hw_cap & GMAC_HW_FEAT_FRPSEL) >> 10; diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac5.c b/drivers/net/ethernet/stmicro/stmmac/dwmac5.c index e436fa160c7d6..8047f402fb3fe 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac5.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac5.c @@ -550,3 +550,98 @@ int dwmac5_flex_pps_config(void __iomem *ioaddr, int index, writel(val, ioaddr + MAC_PPS_CONTROL); return 0; } + +static int dwmac5_est_write(void __iomem *ioaddr, u32 reg, u32 val, bool gcl) +{ + u32 ctrl; + + writel(val, ioaddr + MTL_EST_GCL_DATA); + + ctrl = (reg << ADDR_SHIFT); + ctrl |= gcl ? 0 : GCRR; + + writel(ctrl, ioaddr + MTL_EST_GCL_CONTROL); + + ctrl |= SRWO; + writel(ctrl, ioaddr + MTL_EST_GCL_CONTROL); + + return readl_poll_timeout(ioaddr + MTL_EST_GCL_CONTROL, + ctrl, !(ctrl & SRWO), 100, 5000); +} + +int dwmac5_est_configure(void __iomem *ioaddr, struct stmmac_est *cfg, + unsigned int ptp_rate) +{ + u32 speed, total_offset, offset, ctrl, ctr_low; + u32 extcfg = readl(ioaddr + GMAC_EXT_CONFIG); + u32 mac_cfg = readl(ioaddr + GMAC_CONFIG); + int i, ret = 0x0; + u64 total_ctr; + + if (extcfg & GMAC_CONFIG_EIPG_EN) { + offset = (extcfg & GMAC_CONFIG_EIPG) >> GMAC_CONFIG_EIPG_SHIFT; + offset = 104 + (offset * 8); + } else { + offset = (mac_cfg & GMAC_CONFIG_IPG) >> GMAC_CONFIG_IPG_SHIFT; + offset = 96 - (offset * 8); + } + + speed = mac_cfg & (GMAC_CONFIG_PS | GMAC_CONFIG_FES); + speed = speed >> GMAC_CONFIG_FES_SHIFT; + + switch (speed) { + case 0x0: + offset = offset * 1000; /* 1G */ + break; + case 0x1: + offset = offset * 400; /* 2.5G */ + break; + case 0x2: + offset = offset * 100000; /* 10M */ + break; + case 0x3: + offset = offset * 10000; /* 100M */ + break; + default: + return -EINVAL; + } + + offset = offset / 1000; + + ret |= dwmac5_est_write(ioaddr, BTR_LOW, cfg->btr[0], false); + ret |= dwmac5_est_write(ioaddr, BTR_HIGH, cfg->btr[1], false); + ret |= dwmac5_est_write(ioaddr, TER, cfg->ter, false); + ret |= dwmac5_est_write(ioaddr, LLR, cfg->gcl_size, false); + if (ret) + return ret; + + total_offset = 0; + for (i = 0; i < cfg->gcl_size; i++) { + ret = dwmac5_est_write(ioaddr, i, cfg->gcl[i] + offset, true); + if (ret) + return ret; + + total_offset += offset; + } + + total_ctr = cfg->ctr[0] + cfg->ctr[1] * 1000000000; + total_ctr += total_offset; + + ctr_low = do_div(total_ctr, 1000000000); + + ret |= dwmac5_est_write(ioaddr, CTR_LOW, ctr_low, false); + ret |= dwmac5_est_write(ioaddr, CTR_HIGH, total_ctr, false); + if (ret) + return ret; + + ctrl = readl(ioaddr + MTL_EST_CONTROL); + ctrl &= ~PTOV; + ctrl |= ((1000000000 / ptp_rate) * 6) << PTOV_SHIFT; + if (cfg->enable) + ctrl |= EEST | SSWL; + else + ctrl &= ~EEST; + + writel(ctrl, ioaddr + MTL_EST_CONTROL); + return 0; +} diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac5.h b/drivers/net/ethernet/stmicro/stmmac/dwmac5.h index 23fecf68f781e..70e6d8837dd9d 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac5.h +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac5.h @@ -30,6 +30,23 @@ #define MAC_PPSx_INTERVAL(x) (0x00000b88 + ((x) * 0x10)) #define MAC_PPSx_WIDTH(x) (0x00000b8c + ((x) * 0x10)) +#define MTL_EST_CONTROL 0x00000c50 +#define PTOV GENMASK(31, 24) +#define PTOV_SHIFT 24 +#define SSWL BIT(1) +#define EEST BIT(0) +#define MTL_EST_GCL_CONTROL 0x00000c80 +#define BTR_LOW 0x0 +#define BTR_HIGH 0x1 +#define CTR_LOW 0x2 +#define CTR_HIGH 0x3 +#define TER 0x4 +#define LLR 0x5 +#define ADDR_SHIFT 8 +#define GCRR BIT(2) +#define SRWO BIT(0) +#define MTL_EST_GCL_DATA 0x00000c84 + #define MTL_RXP_CONTROL_STATUS 0x00000ca0 #define RXPI BIT(31) #define NPE GENMASK(23, 16) @@ -83,5 +100,7 @@ int dwmac5_rxp_config(void __iomem *ioaddr, struct stmmac_tc_entry *entries, int dwmac5_flex_pps_config(void __iomem *ioaddr, int index, struct stmmac_pps_cfg *cfg, bool enable, u32 sub_second_inc, u32 systime_flags); +int dwmac5_est_configure(void __iomem *ioaddr, struct stmmac_est *cfg, + unsigned int ptp_rate); #endif /* __DWMAC5_H__ */ diff --git a/drivers/net/ethernet/stmicro/stmmac/hwif.h b/drivers/net/ethernet/stmicro/stmmac/hwif.h index 098fbe7a58623..9fd34df6eb164 100644 --- a/drivers/net/ethernet/stmicro/stmmac/hwif.h +++ b/drivers/net/ethernet/stmicro/stmmac/hwif.h @@ -276,6 +276,7 @@ struct stmmac_safety_stats; struct stmmac_tc_entry; struct stmmac_pps_cfg; struct stmmac_rss; +struct stmmac_est; /* Helpers to program the MAC core */ struct stmmac_ops { @@ -373,6 +374,8 @@ struct stmmac_ops { bool en, bool udp, bool sa, bool inv, u32 match); void (*set_arp_offload)(struct mac_device_info *hw, bool en, u32 addr); + int (*est_configure)(void __iomem *ioaddr, struct stmmac_est *cfg, + unsigned int ptp_rate); }; #define stmmac_core_init(__priv, __args...) \ @@ -459,6 +462,8 @@ struct stmmac_ops { stmmac_do_callback(__priv, mac, config_l4_filter, __args) #define stmmac_set_arp_offload(__priv, __args...) \ stmmac_do_void_callback(__priv, mac, set_arp_offload, __args) +#define stmmac_est_configure(__priv, __args...) \ + stmmac_do_callback(__priv, mac, est_configure, __args) /* PTP and HW Timer helpers */ struct stmmac_hwtimestamp { diff --git a/include/linux/stmmac.h b/include/linux/stmmac.h index d4bcd9387136c..0531afa9b21ea 100644 --- a/include/linux/stmmac.h +++ b/include/linux/stmmac.h @@ -109,6 +109,18 @@ struct stmmac_axi { bool axi_rb; }; +#define EST_GCL 1024 +struct stmmac_est { + int enable; + u32 btr_offset[2]; + u32 btr[2]; + u32 ctr[2]; + u32 ter; + u32 gcl_unaligned[EST_GCL]; + u32 gcl[EST_GCL]; + u32 gcl_size; +}; + struct stmmac_rxq_cfg { u8 mode_to_use; u32 chan; @@ -139,6 +151,7 @@ struct plat_stmmacenet_data { struct device_node *phylink_node; struct device_node *mdio_node; struct stmmac_dma_cfg *dma_cfg; + struct stmmac_est *est; int clk_csr; int has_gmac; int enh_desc; -- GitLab From 8572aec3d0dc43045254fd1bf581fb980bfdbc4b Mon Sep 17 00:00:00 2001 From: Jose Abreu Date: Wed, 18 Dec 2019 11:33:06 +0100 Subject: [PATCH 1003/1437] net: stmmac: Add basic EST support for XGMAC Adds the support for EST in XGMAC cores. This feature allows to offload scheduling of queues opening time to the IP. Signed-off-by: Jose Abreu Signed-off-by: David S. Miller --- .../net/ethernet/stmicro/stmmac/dwxgmac2.h | 19 +++++++ .../ethernet/stmicro/stmmac/dwxgmac2_core.c | 52 +++++++++++++++++++ .../ethernet/stmicro/stmmac/dwxgmac2_dma.c | 3 ++ 3 files changed, 74 insertions(+) diff --git a/drivers/net/ethernet/stmicro/stmmac/dwxgmac2.h b/drivers/net/ethernet/stmicro/stmmac/dwxgmac2.h index 158cf4ad1596f..e1e79c90c865a 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwxgmac2.h +++ b/drivers/net/ethernet/stmicro/stmmac/dwxgmac2.h @@ -136,6 +136,9 @@ #define XGMAC_HWFEAT_TXQCNT GENMASK(9, 6) #define XGMAC_HWFEAT_RXQCNT GENMASK(3, 0) #define XGMAC_HW_FEATURE3 0x00000128 +#define XGMAC_HWFEAT_ESTWID GENMASK(24, 23) +#define XGMAC_HWFEAT_ESTDEP GENMASK(22, 20) +#define XGMAC_HWFEAT_ESTSEL BIT(19) #define XGMAC_HWFEAT_ASP GENMASK(15, 14) #define XGMAC_HWFEAT_DVLAN BIT(13) #define XGMAC_HWFEAT_FRPES GENMASK(12, 11) @@ -237,6 +240,22 @@ #define XGMAC_TC_PRTY_MAP1 0x00001044 #define XGMAC_PSTC(x) GENMASK((x) * 8 + 7, (x) * 8) #define XGMAC_PSTC_SHIFT(x) ((x) * 8) +#define XGMAC_MTL_EST_CONTROL 0x00001050 +#define XGMAC_PTOV GENMASK(31, 23) +#define XGMAC_PTOV_SHIFT 23 +#define XGMAC_SSWL BIT(1) +#define XGMAC_EEST BIT(0) +#define XGMAC_MTL_EST_GCL_CONTROL 0x00001080 +#define XGMAC_BTR_LOW 0x0 +#define XGMAC_BTR_HIGH 0x1 +#define XGMAC_CTR_LOW 0x2 +#define XGMAC_CTR_HIGH 0x3 +#define XGMAC_TER 0x4 +#define XGMAC_LLR 0x5 +#define XGMAC_ADDR_SHIFT 8 +#define XGMAC_GCRR BIT(2) +#define XGMAC_SRWO BIT(0) +#define XGMAC_MTL_EST_GCL_DATA 0x00001084 #define XGMAC_MTL_RXP_CONTROL_STATUS 0x000010a0 #define XGMAC_RXPI BIT(31) #define XGMAC_NPE GENMASK(23, 16) diff --git a/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_core.c b/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_core.c index 082f5ee9e5256..2f6e960947d9d 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_core.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_core.c @@ -1359,6 +1359,57 @@ static void dwxgmac2_set_arp_offload(struct mac_device_info *hw, bool en, writel(value, ioaddr + XGMAC_RX_CONFIG); } +static int dwxgmac3_est_write(void __iomem *ioaddr, u32 reg, u32 val, bool gcl) +{ + u32 ctrl; + + writel(val, ioaddr + XGMAC_MTL_EST_GCL_DATA); + + ctrl = (reg << XGMAC_ADDR_SHIFT); + ctrl |= gcl ? 0 : XGMAC_GCRR; + + writel(ctrl, ioaddr + XGMAC_MTL_EST_GCL_CONTROL); + + ctrl |= XGMAC_SRWO; + writel(ctrl, ioaddr + XGMAC_MTL_EST_GCL_CONTROL); + + return readl_poll_timeout_atomic(ioaddr + XGMAC_MTL_EST_GCL_CONTROL, + ctrl, !(ctrl & XGMAC_SRWO), 100, 5000); +} + +static int dwxgmac3_est_configure(void __iomem *ioaddr, struct stmmac_est *cfg, + unsigned int ptp_rate) +{ + int i, ret = 0x0; + u32 ctrl; + + ret |= dwxgmac3_est_write(ioaddr, XGMAC_BTR_LOW, cfg->btr[0], false); + ret |= dwxgmac3_est_write(ioaddr, XGMAC_BTR_HIGH, cfg->btr[1], false); + ret |= dwxgmac3_est_write(ioaddr, XGMAC_TER, cfg->ter, false); + ret |= dwxgmac3_est_write(ioaddr, XGMAC_LLR, cfg->gcl_size, false); + ret |= dwxgmac3_est_write(ioaddr, XGMAC_CTR_LOW, cfg->ctr[0], false); + ret |= dwxgmac3_est_write(ioaddr, XGMAC_CTR_HIGH, cfg->ctr[1], false); + if (ret) + return ret; + + for (i = 0; i < cfg->gcl_size; i++) { + ret = dwxgmac3_est_write(ioaddr, i, cfg->gcl[i], true); + if (ret) + return ret; + } + + ctrl = readl(ioaddr + XGMAC_MTL_EST_CONTROL); + ctrl &= ~XGMAC_PTOV; + ctrl |= ((1000000000 / ptp_rate) * 9) << XGMAC_PTOV_SHIFT; + if (cfg->enable) + ctrl |= XGMAC_EEST | XGMAC_SSWL; + else + ctrl &= ~XGMAC_EEST; + + writel(ctrl, ioaddr + XGMAC_MTL_EST_CONTROL); + return 0; +} + const struct stmmac_ops dwxgmac210_ops = { .core_init = dwxgmac2_core_init, .set_mac = dwxgmac2_set_mac, @@ -1402,6 +1453,7 @@ const struct stmmac_ops dwxgmac210_ops = { .config_l3_filter = dwxgmac2_config_l3_filter, .config_l4_filter = dwxgmac2_config_l4_filter, .set_arp_offload = dwxgmac2_set_arp_offload, + .est_configure = dwxgmac3_est_configure, }; int dwxgmac2_setup(struct stmmac_priv *priv) diff --git a/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_dma.c b/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_dma.c index ae066f4e99a81..6ff2795a51175 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_dma.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_dma.c @@ -429,6 +429,9 @@ static void dwxgmac2_get_hw_feature(void __iomem *ioaddr, /* MAC HW feature 3 */ hw_cap = readl(ioaddr + XGMAC_HW_FEATURE3); + dma_cap->estwid = (hw_cap & XGMAC_HWFEAT_ESTWID) >> 23; + dma_cap->estdep = (hw_cap & XGMAC_HWFEAT_ESTDEP) >> 20; + dma_cap->estsel = (hw_cap & XGMAC_HWFEAT_ESTSEL) >> 19; dma_cap->asp = (hw_cap & XGMAC_HWFEAT_ASP) >> 14; dma_cap->dvlan = (hw_cap & XGMAC_HWFEAT_DVLAN) >> 13; dma_cap->frpes = (hw_cap & XGMAC_HWFEAT_FRPES) >> 11; -- GitLab From b60189e0392fa06348911077ef281eb2b1047b6a Mon Sep 17 00:00:00 2001 From: Jose Abreu Date: Wed, 18 Dec 2019 11:33:07 +0100 Subject: [PATCH 1004/1437] net: stmmac: Integrate EST with TAPRIO scheduler API Now that we have the EST code for XGMAC and QoS we can use it with the TAPRIO scheduler. Integrate it into the main driver and use the API to configure the EST feature. Signed-off-by: Jose Abreu Signed-off-by: David S. Miller --- drivers/net/ethernet/stmicro/stmmac/hwif.h | 5 + .../net/ethernet/stmicro/stmmac/stmmac_main.c | 2 + .../net/ethernet/stmicro/stmmac/stmmac_tc.c | 108 ++++++++++++++++++ 3 files changed, 115 insertions(+) diff --git a/drivers/net/ethernet/stmicro/stmmac/hwif.h b/drivers/net/ethernet/stmicro/stmmac/hwif.h index 9fd34df6eb164..22f79c0a93438 100644 --- a/drivers/net/ethernet/stmicro/stmmac/hwif.h +++ b/drivers/net/ethernet/stmicro/stmmac/hwif.h @@ -521,6 +521,7 @@ struct stmmac_priv; struct tc_cls_u32_offload; struct tc_cbs_qopt_offload; struct flow_cls_offload; +struct tc_taprio_qopt_offload; struct stmmac_tc_ops { int (*init)(struct stmmac_priv *priv); @@ -530,6 +531,8 @@ struct stmmac_tc_ops { struct tc_cbs_qopt_offload *qopt); int (*setup_cls)(struct stmmac_priv *priv, struct flow_cls_offload *cls); + int (*setup_taprio)(struct stmmac_priv *priv, + struct tc_taprio_qopt_offload *qopt); }; #define stmmac_tc_init(__priv, __args...) \ @@ -540,6 +543,8 @@ struct stmmac_tc_ops { stmmac_do_callback(__priv, tc, setup_cbs, __args) #define stmmac_tc_setup_cls(__priv, __args...) \ stmmac_do_callback(__priv, tc, setup_cls, __args) +#define stmmac_tc_setup_taprio(__priv, __args...) \ + stmmac_do_callback(__priv, tc, setup_taprio, __args) struct stmmac_counters; diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index ecb89c609fb20..18a959589cbcf 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -4076,6 +4076,8 @@ static int stmmac_setup_tc(struct net_device *ndev, enum tc_setup_type type, priv, priv, true); case TC_SETUP_QDISC_CBS: return stmmac_tc_setup_cbs(priv, priv, type_data); + case TC_SETUP_QDISC_TAPRIO: + return stmmac_tc_setup_taprio(priv, priv, type_data); default: return -EOPNOTSUPP; } diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c index 7d972e0fd2b04..58d4ce094381e 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c @@ -591,9 +591,117 @@ static int tc_setup_cls(struct stmmac_priv *priv, return ret; } +static int tc_setup_taprio(struct stmmac_priv *priv, + struct tc_taprio_qopt_offload *qopt) +{ + u32 size, wid = priv->dma_cap.estwid, dep = priv->dma_cap.estdep; + struct plat_stmmacenet_data *plat = priv->plat; + struct timespec64 time; + int i, ret = 0; + + if (!priv->dma_cap.estsel) + return -EOPNOTSUPP; + + switch (wid) { + case 0x1: + wid = 16; + break; + case 0x2: + wid = 20; + break; + case 0x3: + wid = 24; + break; + default: + return -EOPNOTSUPP; + } + + switch (dep) { + case 0x1: + dep = 64; + break; + case 0x2: + dep = 128; + break; + case 0x3: + dep = 256; + break; + case 0x4: + dep = 512; + break; + case 0x5: + dep = 1024; + break; + default: + return -EOPNOTSUPP; + } + + if (!qopt->enable) + goto disable; + if (qopt->num_entries >= dep) + return -EINVAL; + if (!qopt->base_time) + return -ERANGE; + if (!qopt->cycle_time) + return -ERANGE; + + if (!plat->est) { + plat->est = devm_kzalloc(priv->device, sizeof(*plat->est), + GFP_KERNEL); + if (!plat->est) + return -ENOMEM; + } else { + memset(plat->est, 0, sizeof(*plat->est)); + } + + size = qopt->num_entries; + + priv->plat->est->gcl_size = size; + priv->plat->est->enable = qopt->enable; + + for (i = 0; i < size; i++) { + s64 delta_ns = qopt->entries[i].interval; + u32 gates = qopt->entries[i].gate_mask; + + if (delta_ns > GENMASK(wid, 0)) + return -ERANGE; + if (gates > GENMASK(31 - wid, 0)) + return -ERANGE; + if (qopt->entries[i].command != TC_TAPRIO_CMD_SET_GATES) + return -EOPNOTSUPP; + + priv->plat->est->gcl[i] = delta_ns | (gates << wid); + } + + /* Adjust for real system time */ + time = ktime_to_timespec64(qopt->base_time); + priv->plat->est->btr[0] = (u32)time.tv_nsec; + priv->plat->est->btr[1] = (u32)time.tv_sec; + + priv->plat->est->ctr[0] = (u32)(qopt->cycle_time % NSEC_PER_SEC); + priv->plat->est->ctr[1] = (u32)(qopt->cycle_time / NSEC_PER_SEC); + + ret = stmmac_est_configure(priv, priv->ioaddr, priv->plat->est, + priv->plat->clk_ptp_rate); + if (ret) { + netdev_err(priv->dev, "failed to configure EST\n"); + goto disable; + } + + netdev_info(priv->dev, "configured EST\n"); + return 0; + +disable: + priv->plat->est->enable = false; + stmmac_est_configure(priv, priv->ioaddr, priv->plat->est, + priv->plat->clk_ptp_rate); + return ret; +} + const struct stmmac_tc_ops dwmac510_tc_ops = { .init = tc_init, .setup_cls_u32 = tc_setup_cls_u32, .setup_cbs = tc_setup_cbs, .setup_cls = tc_setup_cls, + .setup_taprio = tc_setup_taprio, }; -- GitLab From 1ac14241543c0108e9fd23aca4927a148a44c815 Mon Sep 17 00:00:00 2001 From: Jose Abreu Date: Wed, 18 Dec 2019 11:33:08 +0100 Subject: [PATCH 1005/1437] net: stmmac: Add Frame Preemption support using TAPRIO API Adds the support for Frame Preemption using TAPRIO API. This works along with EST feature and allows to select if preemptable traffic shall be sent during specific queues opening time. Signed-off-by: Jose Abreu Signed-off-by: David S. Miller --- drivers/net/ethernet/stmicro/stmmac/common.h | 1 + drivers/net/ethernet/stmicro/stmmac/hwif.h | 4 +++ .../net/ethernet/stmicro/stmmac/stmmac_tc.c | 29 ++++++++++++++++++- 3 files changed, 33 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/common.h b/drivers/net/ethernet/stmicro/stmmac/common.h index 2f1fc93a9b1ed..09c72025ed3e2 100644 --- a/drivers/net/ethernet/stmicro/stmmac/common.h +++ b/drivers/net/ethernet/stmicro/stmmac/common.h @@ -367,6 +367,7 @@ struct dma_features { unsigned int estwid; unsigned int estdep; unsigned int estsel; + unsigned int fpesel; }; /* GMAC TX FIFO is 8K, Rx FIFO is 16K */ diff --git a/drivers/net/ethernet/stmicro/stmmac/hwif.h b/drivers/net/ethernet/stmicro/stmmac/hwif.h index 22f79c0a93438..905a6f0edacab 100644 --- a/drivers/net/ethernet/stmicro/stmmac/hwif.h +++ b/drivers/net/ethernet/stmicro/stmmac/hwif.h @@ -376,6 +376,8 @@ struct stmmac_ops { void (*set_arp_offload)(struct mac_device_info *hw, bool en, u32 addr); int (*est_configure)(void __iomem *ioaddr, struct stmmac_est *cfg, unsigned int ptp_rate); + void (*fpe_configure)(void __iomem *ioaddr, u32 num_txq, u32 num_rxq, + bool enable); }; #define stmmac_core_init(__priv, __args...) \ @@ -464,6 +466,8 @@ struct stmmac_ops { stmmac_do_void_callback(__priv, mac, set_arp_offload, __args) #define stmmac_est_configure(__priv, __args...) \ stmmac_do_callback(__priv, mac, est_configure, __args) +#define stmmac_fpe_configure(__priv, __args...) \ + stmmac_do_void_callback(__priv, mac, fpe_configure, __args) /* PTP and HW Timer helpers */ struct stmmac_hwtimestamp { diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c index 58d4ce094381e..8ff8f9b9bb225 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c @@ -597,6 +597,7 @@ static int tc_setup_taprio(struct stmmac_priv *priv, u32 size, wid = priv->dma_cap.estwid, dep = priv->dma_cap.estdep; struct plat_stmmacenet_data *plat = priv->plat; struct timespec64 time; + bool fpe = false; int i, ret = 0; if (!priv->dma_cap.estsel) @@ -667,8 +668,23 @@ static int tc_setup_taprio(struct stmmac_priv *priv, return -ERANGE; if (gates > GENMASK(31 - wid, 0)) return -ERANGE; - if (qopt->entries[i].command != TC_TAPRIO_CMD_SET_GATES) + + switch (qopt->entries[i].command) { + case TC_TAPRIO_CMD_SET_GATES: + if (fpe) + return -EINVAL; + break; + case TC_TAPRIO_CMD_SET_AND_HOLD: + gates |= BIT(0); + fpe = true; + break; + case TC_TAPRIO_CMD_SET_AND_RELEASE: + gates &= ~BIT(0); + fpe = true; + break; + default: return -EOPNOTSUPP; + } priv->plat->est->gcl[i] = delta_ns | (gates << wid); } @@ -681,6 +697,17 @@ static int tc_setup_taprio(struct stmmac_priv *priv, priv->plat->est->ctr[0] = (u32)(qopt->cycle_time % NSEC_PER_SEC); priv->plat->est->ctr[1] = (u32)(qopt->cycle_time / NSEC_PER_SEC); + if (fpe && !priv->dma_cap.fpesel) + return -EOPNOTSUPP; + + ret = stmmac_fpe_configure(priv, priv->ioaddr, + priv->plat->tx_queues_to_use, + priv->plat->rx_queues_to_use, fpe); + if (ret && fpe) { + netdev_err(priv->dev, "failed to enable Frame Preemption\n"); + return ret; + } + ret = stmmac_est_configure(priv, priv->ioaddr, priv->plat->est, priv->plat->clk_ptp_rate); if (ret) { -- GitLab From 7c7282746883e48aff54ff6d55e6d8674a0c414a Mon Sep 17 00:00:00 2001 From: Jose Abreu Date: Wed, 18 Dec 2019 11:33:09 +0100 Subject: [PATCH 1006/1437] net: stmmac: gmac5+: Add support for Frame Preemption Adds the HW specific support for Frame Preemption on GMAC5+ cores. Signed-off-by: Jose Abreu Signed-off-by: David S. Miller --- drivers/net/ethernet/stmicro/stmmac/dwmac4.h | 3 +++ .../net/ethernet/stmicro/stmmac/dwmac4_core.c | 2 ++ .../net/ethernet/stmicro/stmmac/dwmac4_dma.c | 1 + drivers/net/ethernet/stmicro/stmmac/dwmac5.c | 23 +++++++++++++++++++ drivers/net/ethernet/stmicro/stmmac/dwmac5.h | 5 ++++ 5 files changed, 34 insertions(+) diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4.h b/drivers/net/ethernet/stmicro/stmmac/dwmac4.h index f7f5a27511471..2e6b60a476c68 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac4.h +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4.h @@ -64,6 +64,8 @@ #define GMAC_RXQCTRL_MCBCQEN_SHIFT 20 #define GMAC_RXQCTRL_TACPQE BIT(21) #define GMAC_RXQCTRL_TACPQE_SHIFT 21 +#define GMAC_RXQCTRL_FPRQ GENMASK(26, 24) +#define GMAC_RXQCTRL_FPRQ_SHIFT 24 /* MAC Packet Filtering */ #define GMAC_PACKET_FILTER_PR BIT(0) @@ -237,6 +239,7 @@ enum power_event { /* MAC HW features3 bitmap */ #define GMAC_HW_FEAT_ASP GENMASK(29, 28) +#define GMAC_HW_FEAT_FPESEL BIT(26) #define GMAC_HW_FEAT_ESTWID GENMASK(21, 20) #define GMAC_HW_FEAT_ESTDEP GENMASK(19, 17) #define GMAC_HW_FEAT_ESTSEL BIT(16) diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c b/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c index 8df7496411a02..f0c0ea616032b 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c @@ -985,6 +985,7 @@ const struct stmmac_ops dwmac410_ops = { .config_l3_filter = dwmac4_config_l3_filter, .config_l4_filter = dwmac4_config_l4_filter, .est_configure = dwmac5_est_configure, + .fpe_configure = dwmac5_fpe_configure, }; const struct stmmac_ops dwmac510_ops = { @@ -1029,6 +1030,7 @@ const struct stmmac_ops dwmac510_ops = { .config_l3_filter = dwmac4_config_l3_filter, .config_l4_filter = dwmac4_config_l4_filter, .est_configure = dwmac5_est_configure, + .fpe_configure = dwmac5_fpe_configure, }; int dwmac4_setup(struct stmmac_priv *priv) diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c b/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c index 3552ce1201ffe..213d44482ffa1 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c @@ -404,6 +404,7 @@ static void dwmac4_get_hw_feature(void __iomem *ioaddr, /* 5.10 Features */ dma_cap->asp = (hw_cap & GMAC_HW_FEAT_ASP) >> 28; + dma_cap->fpesel = (hw_cap & GMAC_HW_FEAT_FPESEL) >> 26; dma_cap->estwid = (hw_cap & GMAC_HW_FEAT_ESTWID) >> 20; dma_cap->estdep = (hw_cap & GMAC_HW_FEAT_ESTDEP) >> 17; dma_cap->estsel = (hw_cap & GMAC_HW_FEAT_ESTSEL) >> 16; diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac5.c b/drivers/net/ethernet/stmicro/stmmac/dwmac5.c index 8047f402fb3fe..5d4a3c2458eab 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac5.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac5.c @@ -645,3 +645,26 @@ int dwmac5_est_configure(void __iomem *ioaddr, struct stmmac_est *cfg, writel(ctrl, ioaddr + MTL_EST_CONTROL); return 0; } + +void dwmac5_fpe_configure(void __iomem *ioaddr, u32 num_txq, u32 num_rxq, + bool enable) +{ + u32 value; + + if (!enable) { + value = readl(ioaddr + MAC_FPE_CTRL_STS); + + value &= ~EFPE; + + writel(value, ioaddr + MAC_FPE_CTRL_STS); + } + + value = readl(ioaddr + GMAC_RXQ_CTRL1); + value &= ~GMAC_RXQCTRL_FPRQ; + value |= (num_rxq - 1) << GMAC_RXQCTRL_FPRQ_SHIFT; + writel(value, ioaddr + GMAC_RXQ_CTRL1); + + value = readl(ioaddr + MAC_FPE_CTRL_STS); + value |= EFPE; + writel(value, ioaddr + MAC_FPE_CTRL_STS); +} diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac5.h b/drivers/net/ethernet/stmicro/stmmac/dwmac5.h index 70e6d8837dd9d..3e8faa96b4d40 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac5.h +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac5.h @@ -11,6 +11,9 @@ #define PRTYEN BIT(1) #define TMOUTEN BIT(0) +#define MAC_FPE_CTRL_STS 0x00000234 +#define EFPE BIT(0) + #define MAC_PPS_CONTROL 0x00000b70 #define PPS_MAXIDX(x) ((((x) + 1) * 8) - 1) #define PPS_MINIDX(x) ((x) * 8) @@ -102,5 +105,7 @@ int dwmac5_flex_pps_config(void __iomem *ioaddr, int index, u32 sub_second_inc, u32 systime_flags); int dwmac5_est_configure(void __iomem *ioaddr, struct stmmac_est *cfg, unsigned int ptp_rate); +void dwmac5_fpe_configure(void __iomem *ioaddr, u32 num_txq, u32 num_rxq, + bool enable); #endif /* __DWMAC5_H__ */ -- GitLab From f0e56c8d8f7d0e03e571e21c778f02d226aaeb97 Mon Sep 17 00:00:00 2001 From: Jose Abreu Date: Wed, 18 Dec 2019 11:33:10 +0100 Subject: [PATCH 1007/1437] net: stmmac: xgmac3+: Add support for Frame Preemption Adds the HW specific support for Frame Preemption on XGMAC3+ cores. Signed-off-by: Jose Abreu Signed-off-by: David S. Miller --- .../net/ethernet/stmicro/stmmac/dwxgmac2.h | 6 +++++ .../ethernet/stmicro/stmmac/dwxgmac2_core.c | 24 +++++++++++++++++++ .../ethernet/stmicro/stmmac/dwxgmac2_dma.c | 1 + 3 files changed, 31 insertions(+) diff --git a/drivers/net/ethernet/stmicro/stmmac/dwxgmac2.h b/drivers/net/ethernet/stmicro/stmmac/dwxgmac2.h index e1e79c90c865a..174b903a82114 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwxgmac2.h +++ b/drivers/net/ethernet/stmicro/stmmac/dwxgmac2.h @@ -73,6 +73,9 @@ #define XGMAC_RXQ_CTRL0 0x000000a0 #define XGMAC_RXQEN(x) GENMASK((x) * 2 + 1, (x) * 2) #define XGMAC_RXQEN_SHIFT(x) ((x) * 2) +#define XGMAC_RXQ_CTRL1 0x000000a4 +#define XGMAC_RQ GENMASK(7, 4) +#define XGMAC_RQ_SHIFT 4 #define XGMAC_RXQ_CTRL2 0x000000a8 #define XGMAC_RXQ_CTRL3 0x000000ac #define XGMAC_PSRQ(x) GENMASK((x) * 8 + 7, (x) * 8) @@ -136,6 +139,7 @@ #define XGMAC_HWFEAT_TXQCNT GENMASK(9, 6) #define XGMAC_HWFEAT_RXQCNT GENMASK(3, 0) #define XGMAC_HW_FEATURE3 0x00000128 +#define XGMAC_HWFEAT_FPESEL BIT(26) #define XGMAC_HWFEAT_ESTWID GENMASK(24, 23) #define XGMAC_HWFEAT_ESTDEP GENMASK(22, 20) #define XGMAC_HWFEAT_ESTSEL BIT(19) @@ -151,6 +155,8 @@ #define XGMAC_MDIO_ADDR 0x00000200 #define XGMAC_MDIO_DATA 0x00000204 #define XGMAC_MDIO_C22P 0x00000220 +#define XGMAC_FPE_CTRL_STS 0x00000280 +#define XGMAC_EFPE BIT(0) #define XGMAC_ADDRx_HIGH(x) (0x00000300 + (x) * 0x8) #define XGMAC_ADDR_MAX 32 #define XGMAC_AE BIT(31) diff --git a/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_core.c b/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_core.c index 2f6e960947d9d..307105e8dea03 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_core.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_core.c @@ -1410,6 +1410,29 @@ static int dwxgmac3_est_configure(void __iomem *ioaddr, struct stmmac_est *cfg, return 0; } +static void dwxgmac3_fpe_configure(void __iomem *ioaddr, u32 num_txq, + u32 num_rxq, bool enable) +{ + u32 value; + + if (!enable) { + value = readl(ioaddr + XGMAC_FPE_CTRL_STS); + + value &= ~XGMAC_EFPE; + + writel(value, ioaddr + XGMAC_FPE_CTRL_STS); + } + + value = readl(ioaddr + XGMAC_RXQ_CTRL1); + value &= ~XGMAC_RQ; + value |= (num_rxq - 1) << XGMAC_RQ_SHIFT; + writel(value, ioaddr + XGMAC_RXQ_CTRL1); + + value = readl(ioaddr + XGMAC_FPE_CTRL_STS); + value |= XGMAC_EFPE; + writel(value, ioaddr + XGMAC_FPE_CTRL_STS); +} + const struct stmmac_ops dwxgmac210_ops = { .core_init = dwxgmac2_core_init, .set_mac = dwxgmac2_set_mac, @@ -1454,6 +1477,7 @@ const struct stmmac_ops dwxgmac210_ops = { .config_l4_filter = dwxgmac2_config_l4_filter, .set_arp_offload = dwxgmac2_set_arp_offload, .est_configure = dwxgmac3_est_configure, + .fpe_configure = dwxgmac3_fpe_configure, }; int dwxgmac2_setup(struct stmmac_priv *priv) diff --git a/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_dma.c b/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_dma.c index 6ff2795a51175..c1ca73ebb0e7b 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_dma.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_dma.c @@ -429,6 +429,7 @@ static void dwxgmac2_get_hw_feature(void __iomem *ioaddr, /* MAC HW feature 3 */ hw_cap = readl(ioaddr + XGMAC_HW_FEATURE3); + dma_cap->fpesel = (hw_cap & XGMAC_HWFEAT_FPESEL) >> 26; dma_cap->estwid = (hw_cap & XGMAC_HWFEAT_ESTWID) >> 23; dma_cap->estdep = (hw_cap & XGMAC_HWFEAT_ESTDEP) >> 20; dma_cap->estsel = (hw_cap & XGMAC_HWFEAT_ESTSEL) >> 19; -- GitLab From ea77b8c813c6e56b20bb88567b63acc147cefb57 Mon Sep 17 00:00:00 2001 From: Jose Abreu Date: Wed, 18 Dec 2019 11:33:11 +0100 Subject: [PATCH 1008/1437] net: stmmac: mmc: Add Frame Preemption counters on GMAC5+ cores This can be useful for debug. Add these counters on GMAC5+ cores just like we did for XGMAC. Signed-off-by: Jose Abreu Signed-off-by: David S. Miller --- drivers/net/ethernet/stmicro/stmmac/mmc_core.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/drivers/net/ethernet/stmicro/stmmac/mmc_core.c b/drivers/net/ethernet/stmicro/stmmac/mmc_core.c index 252cf48c58166..a57b0fa815aba 100644 --- a/drivers/net/ethernet/stmicro/stmmac/mmc_core.c +++ b/drivers/net/ethernet/stmicro/stmmac/mmc_core.c @@ -119,6 +119,13 @@ #define MMC_RX_ICMP_GD_OCTETS 0x180 #define MMC_RX_ICMP_ERR_OCTETS 0x184 +#define MMC_TX_FPE_FRAG 0x1a8 +#define MMC_TX_HOLD_REQ 0x1ac +#define MMC_RX_PKT_ASSEMBLY_ERR 0x1c8 +#define MMC_RX_PKT_SMD_ERR 0x1cc +#define MMC_RX_PKT_ASSEMBLY_OK 0x1d0 +#define MMC_RX_FPE_FRAG 0x1d4 + /* XGMAC MMC Registers */ #define MMC_XGMAC_TX_OCTET_GB 0x14 #define MMC_XGMAC_TX_PKT_GB 0x1c @@ -315,6 +322,15 @@ static void dwmac_mmc_read(void __iomem *mmcaddr, struct stmmac_counters *mmc) mmc->mmc_rx_tcp_err_octets += readl(mmcaddr + MMC_RX_TCP_ERR_OCTETS); mmc->mmc_rx_icmp_gd_octets += readl(mmcaddr + MMC_RX_ICMP_GD_OCTETS); mmc->mmc_rx_icmp_err_octets += readl(mmcaddr + MMC_RX_ICMP_ERR_OCTETS); + + mmc->mmc_tx_fpe_fragment_cntr += readl(mmcaddr + MMC_TX_FPE_FRAG); + mmc->mmc_tx_hold_req_cntr += readl(mmcaddr + MMC_TX_HOLD_REQ); + mmc->mmc_rx_packet_assembly_err_cntr += + readl(mmcaddr + MMC_RX_PKT_ASSEMBLY_ERR); + mmc->mmc_rx_packet_smd_err_cntr += readl(mmcaddr + MMC_RX_PKT_SMD_ERR); + mmc->mmc_rx_packet_assembly_ok_cntr += + readl(mmcaddr + MMC_RX_PKT_ASSEMBLY_OK); + mmc->mmc_rx_fpe_fragment_cntr += readl(mmcaddr + MMC_RX_FPE_FRAG); } const struct stmmac_mmc_ops dwmac_mmc_ops = { -- GitLab From 871602b1079234b2a9279062eaeadb3aad7bc863 Mon Sep 17 00:00:00 2001 From: Julian Wiedmann Date: Wed, 18 Dec 2019 17:34:42 +0100 Subject: [PATCH 1009/1437] s390/qeth: only handle IRQs while device is online A qeth device that's offline should not be receiving any IRQs - all pending IOs have been terminated, and we avoid starting any new ones. So rather than immediately registering the IRQ handler when the device is probed, only register it while the device is online. Signed-off-by: Julian Wiedmann Signed-off-by: David S. Miller --- drivers/s390/net/qeth_core_main.c | 68 ++++++++++++++----------------- 1 file changed, 31 insertions(+), 37 deletions(-) diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c index ce7ff1abbef34..3d23748013084 100644 --- a/drivers/s390/net/qeth_core_main.c +++ b/drivers/s390/net/qeth_core_main.c @@ -972,8 +972,6 @@ static void qeth_irq(struct ccw_device *cdev, unsigned long intparm, /* while we hold the ccwdev lock, this stays valid: */ gdev = dev_get_drvdata(&cdev->dev); card = dev_get_drvdata(&gdev->dev); - if (!card) - return; QETH_CARD_TEXT(card, 5, "irq"); @@ -1198,31 +1196,6 @@ static void qeth_free_buffer_pool(struct qeth_card *card) } } -static void qeth_clean_channel(struct qeth_channel *channel) -{ - struct ccw_device *cdev = channel->ccwdev; - - QETH_DBF_TEXT(SETUP, 2, "freech"); - - spin_lock_irq(get_ccwdev_lock(cdev)); - cdev->handler = NULL; - spin_unlock_irq(get_ccwdev_lock(cdev)); -} - -static void qeth_setup_channel(struct qeth_channel *channel) -{ - struct ccw_device *cdev = channel->ccwdev; - - QETH_DBF_TEXT(SETUP, 2, "setupch"); - - channel->state = CH_STATE_DOWN; - atomic_set(&channel->irq_pending, 0); - - spin_lock_irq(get_ccwdev_lock(cdev)); - cdev->handler = qeth_irq; - spin_unlock_irq(get_ccwdev_lock(cdev)); -} - static int qeth_osa_set_output_queues(struct qeth_card *card, bool single) { unsigned int count = single ? 1 : card->dev->num_tx_queues; @@ -1395,9 +1368,6 @@ static struct qeth_card *qeth_alloc_card(struct ccwgroup_device *gdev) if (!card->read_cmd) goto out_read_cmd; - qeth_setup_channel(&card->read); - qeth_setup_channel(&card->write); - qeth_setup_channel(&card->data); card->qeth_service_level.seq_print = qeth_core_sl_print; register_service_level(&card->qeth_service_level); return card; @@ -1467,12 +1437,38 @@ int qeth_stop_channel(struct qeth_channel *channel) channel->active_cmd); channel->active_cmd = NULL; } + cdev->handler = NULL; spin_unlock_irq(get_ccwdev_lock(cdev)); return rc; } EXPORT_SYMBOL_GPL(qeth_stop_channel); +static int qeth_start_channel(struct qeth_channel *channel) +{ + struct ccw_device *cdev = channel->ccwdev; + int rc; + + channel->state = CH_STATE_DOWN; + atomic_set(&channel->irq_pending, 0); + + spin_lock_irq(get_ccwdev_lock(cdev)); + cdev->handler = qeth_irq; + spin_unlock_irq(get_ccwdev_lock(cdev)); + + rc = ccw_device_set_online(cdev); + if (rc) + goto err; + + return 0; + +err: + spin_lock_irq(get_ccwdev_lock(cdev)); + cdev->handler = NULL; + spin_unlock_irq(get_ccwdev_lock(cdev)); + return rc; +} + static int qeth_halt_channels(struct qeth_card *card) { int rc1 = 0, rc2 = 0, rc3 = 0; @@ -4706,7 +4702,7 @@ static void qeth_determine_capabilities(struct qeth_card *card) QETH_CARD_TEXT(card, 2, "detcapab"); if (!ddev->online) { ddev_offline = 1; - rc = ccw_device_set_online(ddev); + rc = qeth_start_channel(channel); if (rc) { QETH_CARD_TEXT_(card, 2, "3err%d", rc); goto out; @@ -4881,9 +4877,6 @@ out_free_nothing: static void qeth_core_free_card(struct qeth_card *card) { QETH_CARD_TEXT(card, 2, "freecrd"); - qeth_clean_channel(&card->read); - qeth_clean_channel(&card->write); - qeth_clean_channel(&card->data); qeth_put_cmd(card->read_cmd); destroy_workqueue(card->event_wq); unregister_service_level(&card->qeth_service_level); @@ -4946,13 +4939,14 @@ retry: qeth_stop_channel(&card->write); qeth_stop_channel(&card->read); qdio_free(CARD_DDEV(card)); - rc = ccw_device_set_online(CARD_RDEV(card)); + + rc = qeth_start_channel(&card->read); if (rc) goto retriable; - rc = ccw_device_set_online(CARD_WDEV(card)); + rc = qeth_start_channel(&card->write); if (rc) goto retriable; - rc = ccw_device_set_online(CARD_DDEV(card)); + rc = qeth_start_channel(&card->data); if (rc) goto retriable; retriable: -- GitLab From 7359393f3c19c9351e81345ef29f4f47229a6416 Mon Sep 17 00:00:00 2001 From: Julian Wiedmann Date: Wed, 18 Dec 2019 17:34:43 +0100 Subject: [PATCH 1010/1437] s390/qeth: wake up all waiters from qeth_irq() card->wait_q is shared by different users, for different wake-up conditions. qeth_irq() can potentially trigger multiple of these conditions: 1) A change to channel->irq_pending, which qeth_send_control_data() is waiting for. 2) A change to card->state, which qeth_clear_channel() and qeth_halt_channel() are waiting for. As qeth_irq() does only a single wake_up(), we might miss to wake up a second eligible waiter. Luckily all waiters are guarded with a timeout, so this situation should recover on its own eventually. To make things work robustly, add an additional wake_up() for changes to channel->state. And extract a helper that updates channel->irq_pending along with the needed wake_up(). Signed-off-by: Julian Wiedmann Signed-off-by: David S. Miller --- drivers/s390/net/qeth_core.h | 7 +++++++ drivers/s390/net/qeth_core_main.c | 27 +++++++++++---------------- 2 files changed, 18 insertions(+), 16 deletions(-) diff --git a/drivers/s390/net/qeth_core.h b/drivers/s390/net/qeth_core.h index a23875a4fa2bc..b54ef12840a21 100644 --- a/drivers/s390/net/qeth_core.h +++ b/drivers/s390/net/qeth_core.h @@ -862,6 +862,13 @@ static inline bool qeth_card_hw_is_reachable(struct qeth_card *card) return card->state == CARD_STATE_SOFTSETUP; } +static inline void qeth_unlock_channel(struct qeth_card *card, + struct qeth_channel *channel) +{ + atomic_set(&channel->irq_pending, 0); + wake_up(&card->wait_q); +} + struct qeth_trap_id { __u16 lparnr; char vmname[8]; diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c index 3d23748013084..38f3ed7567bc0 100644 --- a/drivers/s390/net/qeth_core_main.c +++ b/drivers/s390/net/qeth_core_main.c @@ -520,11 +520,10 @@ static int __qeth_issue_next_read(struct qeth_card *card) } else { QETH_DBF_MESSAGE(2, "error %i on device %x when starting next read ccw!\n", rc, CARD_DEVID(card)); - atomic_set(&channel->irq_pending, 0); + qeth_unlock_channel(card, channel); qeth_put_cmd(iob); card->read_or_write_problem = 1; qeth_schedule_recovery(card); - wake_up(&card->wait_q); } return rc; } @@ -1001,24 +1000,25 @@ static void qeth_irq(struct ccw_device *cdev, unsigned long intparm, } channel->active_cmd = NULL; + qeth_unlock_channel(card, channel); rc = qeth_check_irb_error(card, cdev, irb); if (rc) { /* IO was terminated, free its resources. */ if (iob) qeth_cancel_cmd(iob, rc); - atomic_set(&channel->irq_pending, 0); - wake_up(&card->wait_q); return; } - atomic_set(&channel->irq_pending, 0); - - if (irb->scsw.cmd.fctl & (SCSW_FCTL_CLEAR_FUNC)) + if (irb->scsw.cmd.fctl & SCSW_FCTL_CLEAR_FUNC) { channel->state = CH_STATE_STOPPED; + wake_up(&card->wait_q); + } - if (irb->scsw.cmd.fctl & (SCSW_FCTL_HALT_FUNC)) + if (irb->scsw.cmd.fctl & SCSW_FCTL_HALT_FUNC) { channel->state = CH_STATE_HALTED; + wake_up(&card->wait_q); + } if (iob && (irb->scsw.cmd.fctl & (SCSW_FCTL_CLEAR_FUNC | SCSW_FCTL_HALT_FUNC))) { @@ -1052,7 +1052,7 @@ static void qeth_irq(struct ccw_device *cdev, unsigned long intparm, qeth_cancel_cmd(iob, rc); qeth_clear_ipacmd_list(card); qeth_schedule_recovery(card); - goto out; + return; } } @@ -1060,16 +1060,12 @@ static void qeth_irq(struct ccw_device *cdev, unsigned long intparm, /* sanity check: */ if (irb->scsw.cmd.count > iob->length) { qeth_cancel_cmd(iob, -EIO); - goto out; + return; } if (iob->callback) iob->callback(card, iob, iob->length - irb->scsw.cmd.count); } - -out: - wake_up(&card->wait_q); - return; } static void qeth_notify_skbs(struct qeth_qdio_out_q *q, @@ -1780,8 +1776,7 @@ static int qeth_send_control_data(struct qeth_card *card, QETH_CARD_TEXT_(card, 2, " err%d", rc); qeth_dequeue_cmd(card, iob); qeth_put_cmd(iob); - atomic_set(&channel->irq_pending, 0); - wake_up(&card->wait_q); + qeth_unlock_channel(card, channel); goto out; } -- GitLab From e6b1b7da247b329bb9e62adc97ef28678199cf35 Mon Sep 17 00:00:00 2001 From: Julian Wiedmann Date: Wed, 18 Dec 2019 17:34:44 +0100 Subject: [PATCH 1011/1437] s390/qeth: overhaul L3 IP address dump code The current code that dumps the RXIP/VIPA/IPATO addresses via sysfs first checks whether the buffer still provides sufficient space to hold another formatted address. But the maximum length of an formatted IPv4 address is 15 characters, not 12. So we underestimate the max required length and if the buffer was previously filled to _just_ the right level, a formatted address can end up being truncated. Revamp these code paths to use the _actually_ required length of the formatted IP address, and while at it suppress a gratuitous newline. Also use scnprintf() to format the output. In case of a truncation, this would allow us to return the number of characters that were actually written. Signed-off-by: Julian Wiedmann Signed-off-by: David S. Miller --- drivers/s390/net/qeth_l3.h | 3 +- drivers/s390/net/qeth_l3_main.c | 20 +++------- drivers/s390/net/qeth_l3_sys.c | 66 ++++++++++++++++++--------------- 3 files changed, 44 insertions(+), 45 deletions(-) diff --git a/drivers/s390/net/qeth_l3.h b/drivers/s390/net/qeth_l3.h index 5db04fe472c01..2383ffad0a4a7 100644 --- a/drivers/s390/net/qeth_l3.h +++ b/drivers/s390/net/qeth_l3.h @@ -102,7 +102,8 @@ struct qeth_ipato_entry { extern const struct attribute_group *qeth_l3_attr_groups[]; -void qeth_l3_ipaddr_to_string(enum qeth_prot_versions, const __u8 *, char *); +int qeth_l3_ipaddr_to_string(enum qeth_prot_versions proto, const u8 *addr, + char *buf); int qeth_l3_create_device_attributes(struct device *); void qeth_l3_remove_device_attributes(struct device *); int qeth_l3_setrouting_v4(struct qeth_card *); diff --git a/drivers/s390/net/qeth_l3_main.c b/drivers/s390/net/qeth_l3_main.c index 27126330a4b00..3710761831ccd 100644 --- a/drivers/s390/net/qeth_l3_main.c +++ b/drivers/s390/net/qeth_l3_main.c @@ -44,23 +44,13 @@ static int qeth_l3_register_addr_entry(struct qeth_card *, static int qeth_l3_deregister_addr_entry(struct qeth_card *, struct qeth_ipaddr *); -static void qeth_l3_ipaddr4_to_string(const __u8 *addr, char *buf) -{ - sprintf(buf, "%pI4", addr); -} - -static void qeth_l3_ipaddr6_to_string(const __u8 *addr, char *buf) -{ - sprintf(buf, "%pI6", addr); -} - -void qeth_l3_ipaddr_to_string(enum qeth_prot_versions proto, const __u8 *addr, - char *buf) +int qeth_l3_ipaddr_to_string(enum qeth_prot_versions proto, const u8 *addr, + char *buf) { if (proto == QETH_PROT_IPV4) - qeth_l3_ipaddr4_to_string(addr, buf); - else if (proto == QETH_PROT_IPV6) - qeth_l3_ipaddr6_to_string(addr, buf); + return sprintf(buf, "%pI4", addr); + else + return sprintf(buf, "%pI6", addr); } static struct qeth_ipaddr *qeth_l3_find_addr_by_ip(struct qeth_card *card, diff --git a/drivers/s390/net/qeth_l3_sys.c b/drivers/s390/net/qeth_l3_sys.c index f9067ed6c7d32..aa9f3fc454476 100644 --- a/drivers/s390/net/qeth_l3_sys.c +++ b/drivers/s390/net/qeth_l3_sys.c @@ -386,30 +386,35 @@ static ssize_t qeth_l3_dev_ipato_add_show(char *buf, struct qeth_card *card, enum qeth_prot_versions proto) { struct qeth_ipato_entry *ipatoe; - char addr_str[40]; - int entry_len; /* length of 1 entry string, differs between v4 and v6 */ - int i = 0; + int str_len = 0; - entry_len = (proto == QETH_PROT_IPV4)? 12 : 40; - /* add strlen for "/\n" */ - entry_len += (proto == QETH_PROT_IPV4)? 5 : 6; mutex_lock(&card->ip_lock); list_for_each_entry(ipatoe, &card->ipato.entries, entry) { + char addr_str[40]; + int entry_len; + if (ipatoe->proto != proto) continue; - /* String must not be longer than PAGE_SIZE. So we check if - * string length gets near PAGE_SIZE. Then we can savely display - * the next IPv6 address (worst case, compared to IPv4) */ - if ((PAGE_SIZE - i) <= entry_len) + + entry_len = qeth_l3_ipaddr_to_string(proto, ipatoe->addr, + addr_str); + if (entry_len < 0) + continue; + + /* Append /%mask to the entry: */ + entry_len += 1 + ((proto == QETH_PROT_IPV4) ? 2 : 3); + /* Enough room to format %entry\n into null terminated page? */ + if (entry_len + 1 > PAGE_SIZE - str_len - 1) break; - qeth_l3_ipaddr_to_string(proto, ipatoe->addr, addr_str); - i += snprintf(buf + i, PAGE_SIZE - i, - "%s/%i\n", addr_str, ipatoe->mask_bits); + + entry_len = scnprintf(buf, PAGE_SIZE - str_len, + "%s/%i\n", addr_str, ipatoe->mask_bits); + str_len += entry_len; + buf += entry_len; } mutex_unlock(&card->ip_lock); - i += snprintf(buf + i, PAGE_SIZE - i, "\n"); - return i; + return str_len ? str_len : scnprintf(buf, PAGE_SIZE, "\n"); } static ssize_t qeth_l3_dev_ipato_add4_show(struct device *dev, @@ -607,31 +612,34 @@ static ssize_t qeth_l3_dev_ip_add_show(struct device *dev, char *buf, { struct qeth_card *card = dev_get_drvdata(dev); struct qeth_ipaddr *ipaddr; - char addr_str[40]; int str_len = 0; - int entry_len; /* length of 1 entry string, differs between v4 and v6 */ int i; - entry_len = (proto == QETH_PROT_IPV4)? 12 : 40; - entry_len += 2; /* \n + terminator */ mutex_lock(&card->ip_lock); hash_for_each(card->ip_htable, i, ipaddr, hnode) { + char addr_str[40]; + int entry_len; + if (ipaddr->proto != proto || ipaddr->type != type) continue; - /* String must not be longer than PAGE_SIZE. So we check if - * string length gets near PAGE_SIZE. Then we can savely display - * the next IPv6 address (worst case, compared to IPv4) */ - if ((PAGE_SIZE - str_len) <= entry_len) + + entry_len = qeth_l3_ipaddr_to_string(proto, (u8 *)&ipaddr->u, + addr_str); + if (entry_len < 0) + continue; + + /* Enough room to format %addr\n into null terminated page? */ + if (entry_len + 1 > PAGE_SIZE - str_len - 1) break; - qeth_l3_ipaddr_to_string(proto, (const u8 *)&ipaddr->u, - addr_str); - str_len += snprintf(buf + str_len, PAGE_SIZE - str_len, "%s\n", - addr_str); + + entry_len = scnprintf(buf, PAGE_SIZE - str_len, "%s\n", + addr_str); + str_len += entry_len; + buf += entry_len; } mutex_unlock(&card->ip_lock); - str_len += snprintf(buf + str_len, PAGE_SIZE - str_len, "\n"); - return str_len; + return str_len ? str_len : scnprintf(buf, PAGE_SIZE, "\n"); } static ssize_t qeth_l3_dev_vipa_add4_show(struct device *dev, -- GitLab From 2390166a6b457d3849dd4067c970f714b65bd9c3 Mon Sep 17 00:00:00 2001 From: Julian Wiedmann Date: Wed, 18 Dec 2019 17:34:45 +0100 Subject: [PATCH 1012/1437] s390/qeth: clean up L3 sysfs code Consolidate some duplicated code for adding RXIP/VIPA addresses, and move the locking to where it's actually needed. Signed-off-by: Julian Wiedmann Signed-off-by: David S. Miller --- drivers/s390/net/qeth_l3_main.c | 12 +++- drivers/s390/net/qeth_l3_sys.c | 106 +++++++------------------------- 2 files changed, 33 insertions(+), 85 deletions(-) diff --git a/drivers/s390/net/qeth_l3_main.c b/drivers/s390/net/qeth_l3_main.c index 3710761831ccd..8db5483406323 100644 --- a/drivers/s390/net/qeth_l3_main.c +++ b/drivers/s390/net/qeth_l3_main.c @@ -571,6 +571,7 @@ int qeth_l3_add_ipato_entry(struct qeth_card *card, QETH_CARD_TEXT(card, 2, "addipato"); + mutex_lock(&card->conf_mutex); mutex_lock(&card->ip_lock); list_for_each_entry(ipatoe, &card->ipato.entries, entry) { @@ -590,6 +591,7 @@ int qeth_l3_add_ipato_entry(struct qeth_card *card, } mutex_unlock(&card->ip_lock); + mutex_unlock(&card->conf_mutex); return rc; } @@ -603,6 +605,7 @@ int qeth_l3_del_ipato_entry(struct qeth_card *card, QETH_CARD_TEXT(card, 2, "delipato"); + mutex_lock(&card->conf_mutex); mutex_lock(&card->ip_lock); list_for_each_entry_safe(ipatoe, tmp, &card->ipato.entries, entry) { @@ -619,6 +622,8 @@ int qeth_l3_del_ipato_entry(struct qeth_card *card, } mutex_unlock(&card->ip_lock); + mutex_unlock(&card->conf_mutex); + return rc; } @@ -627,6 +632,7 @@ int qeth_l3_modify_rxip_vipa(struct qeth_card *card, bool add, const u8 *ip, enum qeth_prot_versions proto) { struct qeth_ipaddr addr; + int rc; qeth_l3_init_ipaddr(&addr, type, proto); if (proto == QETH_PROT_IPV4) @@ -634,7 +640,11 @@ int qeth_l3_modify_rxip_vipa(struct qeth_card *card, bool add, const u8 *ip, else memcpy(&addr.u.a6.addr, ip, 16); - return qeth_l3_modify_ip(card, &addr, add); + mutex_lock(&card->conf_mutex); + rc = qeth_l3_modify_ip(card, &addr, add); + mutex_unlock(&card->conf_mutex); + + return rc; } int qeth_l3_modify_hsuid(struct qeth_card *card, bool add) diff --git a/drivers/s390/net/qeth_l3_sys.c b/drivers/s390/net/qeth_l3_sys.c index aa9f3fc454476..96c73965eb685 100644 --- a/drivers/s390/net/qeth_l3_sys.c +++ b/drivers/s390/net/qeth_l3_sys.c @@ -460,16 +460,14 @@ static ssize_t qeth_l3_dev_ipato_add_store(const char *buf, size_t count, int mask_bits; int rc = 0; - mutex_lock(&card->conf_mutex); rc = qeth_l3_parse_ipatoe(buf, proto, addr, &mask_bits); if (rc) - goto out; + return rc; ipatoe = kzalloc(sizeof(struct qeth_ipato_entry), GFP_KERNEL); - if (!ipatoe) { - rc = -ENOMEM; - goto out; - } + if (!ipatoe) + return -ENOMEM; + ipatoe->proto = proto; memcpy(ipatoe->addr, addr, (proto == QETH_PROT_IPV4)? 4:16); ipatoe->mask_bits = mask_bits; @@ -477,8 +475,7 @@ static ssize_t qeth_l3_dev_ipato_add_store(const char *buf, size_t count, rc = qeth_l3_add_ipato_entry(card, ipatoe); if (rc) kfree(ipatoe); -out: - mutex_unlock(&card->conf_mutex); + return rc ? rc : count; } @@ -501,11 +498,9 @@ static ssize_t qeth_l3_dev_ipato_del_store(const char *buf, size_t count, int mask_bits; int rc = 0; - mutex_lock(&card->conf_mutex); rc = qeth_l3_parse_ipatoe(buf, proto, addr, &mask_bits); if (!rc) rc = qeth_l3_del_ipato_entry(card, proto, addr, mask_bits); - mutex_unlock(&card->conf_mutex); return rc ? rc : count; } @@ -650,63 +645,34 @@ static ssize_t qeth_l3_dev_vipa_add4_show(struct device *dev, QETH_IP_TYPE_VIPA); } -static int qeth_l3_parse_vipae(const char *buf, enum qeth_prot_versions proto, - u8 *addr) -{ - if (qeth_l3_string_to_ipaddr(buf, proto, addr)) { - return -EINVAL; - } - return 0; -} - -static ssize_t qeth_l3_dev_vipa_add_store(const char *buf, size_t count, - struct qeth_card *card, enum qeth_prot_versions proto) +static ssize_t qeth_l3_vipa_store(struct device *dev, const char *buf, bool add, + size_t count, enum qeth_prot_versions proto) { + struct qeth_card *card = dev_get_drvdata(dev); u8 addr[16] = {0, }; int rc; - mutex_lock(&card->conf_mutex); - rc = qeth_l3_parse_vipae(buf, proto, addr); + rc = qeth_l3_string_to_ipaddr(buf, proto, addr); if (!rc) - rc = qeth_l3_modify_rxip_vipa(card, true, addr, + rc = qeth_l3_modify_rxip_vipa(card, add, addr, QETH_IP_TYPE_VIPA, proto); - mutex_unlock(&card->conf_mutex); return rc ? rc : count; } static ssize_t qeth_l3_dev_vipa_add4_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - struct qeth_card *card = dev_get_drvdata(dev); - - return qeth_l3_dev_vipa_add_store(buf, count, card, QETH_PROT_IPV4); + return qeth_l3_vipa_store(dev, buf, true, count, QETH_PROT_IPV4); } static QETH_DEVICE_ATTR(vipa_add4, add4, 0644, qeth_l3_dev_vipa_add4_show, qeth_l3_dev_vipa_add4_store); -static ssize_t qeth_l3_dev_vipa_del_store(const char *buf, size_t count, - struct qeth_card *card, enum qeth_prot_versions proto) -{ - u8 addr[16]; - int rc; - - mutex_lock(&card->conf_mutex); - rc = qeth_l3_parse_vipae(buf, proto, addr); - if (!rc) - rc = qeth_l3_modify_rxip_vipa(card, false, addr, - QETH_IP_TYPE_VIPA, proto); - mutex_unlock(&card->conf_mutex); - return rc ? rc : count; -} - static ssize_t qeth_l3_dev_vipa_del4_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - struct qeth_card *card = dev_get_drvdata(dev); - - return qeth_l3_dev_vipa_del_store(buf, count, card, QETH_PROT_IPV4); + return qeth_l3_vipa_store(dev, buf, true, count, QETH_PROT_IPV4); } static QETH_DEVICE_ATTR(vipa_del4, del4, 0200, NULL, @@ -723,9 +689,7 @@ static ssize_t qeth_l3_dev_vipa_add6_show(struct device *dev, static ssize_t qeth_l3_dev_vipa_add6_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - struct qeth_card *card = dev_get_drvdata(dev); - - return qeth_l3_dev_vipa_add_store(buf, count, card, QETH_PROT_IPV6); + return qeth_l3_vipa_store(dev, buf, true, count, QETH_PROT_IPV6); } static QETH_DEVICE_ATTR(vipa_add6, add6, 0644, @@ -735,9 +699,7 @@ static QETH_DEVICE_ATTR(vipa_add6, add6, 0644, static ssize_t qeth_l3_dev_vipa_del6_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - struct qeth_card *card = dev_get_drvdata(dev); - - return qeth_l3_dev_vipa_del_store(buf, count, card, QETH_PROT_IPV6); + return qeth_l3_vipa_store(dev, buf, false, count, QETH_PROT_IPV6); } static QETH_DEVICE_ATTR(vipa_del6, del6, 0200, NULL, @@ -790,54 +752,34 @@ static int qeth_l3_parse_rxipe(const char *buf, enum qeth_prot_versions proto, return 0; } -static ssize_t qeth_l3_dev_rxip_add_store(const char *buf, size_t count, - struct qeth_card *card, enum qeth_prot_versions proto) +static ssize_t qeth_l3_rxip_store(struct device *dev, const char *buf, bool add, + size_t count, enum qeth_prot_versions proto) { + struct qeth_card *card = dev_get_drvdata(dev); u8 addr[16] = {0, }; int rc; - mutex_lock(&card->conf_mutex); rc = qeth_l3_parse_rxipe(buf, proto, addr); if (!rc) - rc = qeth_l3_modify_rxip_vipa(card, true, addr, + rc = qeth_l3_modify_rxip_vipa(card, add, addr, QETH_IP_TYPE_RXIP, proto); - mutex_unlock(&card->conf_mutex); return rc ? rc : count; } static ssize_t qeth_l3_dev_rxip_add4_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - struct qeth_card *card = dev_get_drvdata(dev); - - return qeth_l3_dev_rxip_add_store(buf, count, card, QETH_PROT_IPV4); + return qeth_l3_rxip_store(dev, buf, true, count, QETH_PROT_IPV4); } static QETH_DEVICE_ATTR(rxip_add4, add4, 0644, qeth_l3_dev_rxip_add4_show, qeth_l3_dev_rxip_add4_store); -static ssize_t qeth_l3_dev_rxip_del_store(const char *buf, size_t count, - struct qeth_card *card, enum qeth_prot_versions proto) -{ - u8 addr[16]; - int rc; - - mutex_lock(&card->conf_mutex); - rc = qeth_l3_parse_rxipe(buf, proto, addr); - if (!rc) - rc = qeth_l3_modify_rxip_vipa(card, false, addr, - QETH_IP_TYPE_RXIP, proto); - mutex_unlock(&card->conf_mutex); - return rc ? rc : count; -} - static ssize_t qeth_l3_dev_rxip_del4_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - struct qeth_card *card = dev_get_drvdata(dev); - - return qeth_l3_dev_rxip_del_store(buf, count, card, QETH_PROT_IPV4); + return qeth_l3_rxip_store(dev, buf, false, count, QETH_PROT_IPV4); } static QETH_DEVICE_ATTR(rxip_del4, del4, 0200, NULL, @@ -854,9 +796,7 @@ static ssize_t qeth_l3_dev_rxip_add6_show(struct device *dev, static ssize_t qeth_l3_dev_rxip_add6_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - struct qeth_card *card = dev_get_drvdata(dev); - - return qeth_l3_dev_rxip_add_store(buf, count, card, QETH_PROT_IPV6); + return qeth_l3_rxip_store(dev, buf, true, count, QETH_PROT_IPV6); } static QETH_DEVICE_ATTR(rxip_add6, add6, 0644, @@ -866,9 +806,7 @@ static QETH_DEVICE_ATTR(rxip_add6, add6, 0644, static ssize_t qeth_l3_dev_rxip_del6_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - struct qeth_card *card = dev_get_drvdata(dev); - - return qeth_l3_dev_rxip_del_store(buf, count, card, QETH_PROT_IPV6); + return qeth_l3_rxip_store(dev, buf, false, count, QETH_PROT_IPV6); } static QETH_DEVICE_ATTR(rxip_del6, del6, 0200, NULL, -- GitLab From 490df97142fe38f7af9ff8d4df8e9ece41df38ec Mon Sep 17 00:00:00 2001 From: Julian Wiedmann Date: Wed, 18 Dec 2019 17:34:46 +0100 Subject: [PATCH 1013/1437] s390/qeth: remove open-coded inet_make_mask() Use inet_make_mask() to replace some complicated bit-fiddling. Also use the right data types to replace some raw memcpy calls with proper assignments. Signed-off-by: Julian Wiedmann Signed-off-by: David S. Miller --- drivers/s390/net/qeth_core_mpc.h | 8 ++++---- drivers/s390/net/qeth_l3.h | 2 +- drivers/s390/net/qeth_l3_main.c | 34 ++++++++++++++------------------ 3 files changed, 20 insertions(+), 24 deletions(-) diff --git a/drivers/s390/net/qeth_core_mpc.h b/drivers/s390/net/qeth_core_mpc.h index 88f4dc140751c..f13225b437819 100644 --- a/drivers/s390/net/qeth_core_mpc.h +++ b/drivers/s390/net/qeth_core_mpc.h @@ -338,14 +338,14 @@ enum qeth_card_info_port_speed { /* (SET)DELIP(M) IPA stuff ***************************************************/ struct qeth_ipacmd_setdelip4 { - __u8 ip_addr[4]; - __u8 mask[4]; + __be32 addr; + __be32 mask; __u32 flags; } __attribute__ ((packed)); struct qeth_ipacmd_setdelip6 { - __u8 ip_addr[16]; - __u8 mask[16]; + struct in6_addr addr; + struct in6_addr prefix; __u32 flags; } __attribute__ ((packed)); diff --git a/drivers/s390/net/qeth_l3.h b/drivers/s390/net/qeth_l3.h index 2383ffad0a4a7..89fb91dad12e9 100644 --- a/drivers/s390/net/qeth_l3.h +++ b/drivers/s390/net/qeth_l3.h @@ -35,7 +35,7 @@ struct qeth_ipaddr { union { struct { __be32 addr; - unsigned int mask; + __be32 mask; } a4; struct { struct in6_addr addr; diff --git a/drivers/s390/net/qeth_l3_main.c b/drivers/s390/net/qeth_l3_main.c index 8db5483406323..bd2273f9ca353 100644 --- a/drivers/s390/net/qeth_l3_main.c +++ b/drivers/s390/net/qeth_l3_main.c @@ -369,17 +369,16 @@ static int qeth_l3_send_setdelmc(struct qeth_card *card, return qeth_send_ipa_cmd(card, iob, qeth_l3_setdelip_cb, NULL); } -static void qeth_l3_fill_netmask(u8 *netmask, unsigned int len) +static void qeth_l3_set_ipv6_prefix(struct in6_addr *prefix, unsigned int len) { - int i, j; - for (i = 0; i < 16; i++) { - j = (len) - (i * 8); - if (j >= 8) - netmask[i] = 0xff; - else if (j > 0) - netmask[i] = (u8)(0xFF00 >> j); - else - netmask[i] = 0; + unsigned int i = 0; + + while (len && i < 4) { + int mask_len = min_t(int, len, 32); + + prefix->s6_addr32[i] = inet_make_mask(mask_len); + len -= mask_len; + i++; } } @@ -402,7 +401,6 @@ static int qeth_l3_send_setdelip(struct qeth_card *card, { struct qeth_cmd_buffer *iob; struct qeth_ipa_cmd *cmd; - __u8 netmask[16]; u32 flags; QETH_CARD_TEXT(card, 4, "setdelip"); @@ -417,15 +415,13 @@ static int qeth_l3_send_setdelip(struct qeth_card *card, QETH_CARD_TEXT_(card, 4, "flags%02X", flags); if (addr->proto == QETH_PROT_IPV6) { - memcpy(cmd->data.setdelip6.ip_addr, &addr->u.a6.addr, - sizeof(struct in6_addr)); - qeth_l3_fill_netmask(netmask, addr->u.a6.pfxlen); - memcpy(cmd->data.setdelip6.mask, netmask, - sizeof(struct in6_addr)); + cmd->data.setdelip6.addr = addr->u.a6.addr; + qeth_l3_set_ipv6_prefix(&cmd->data.setdelip6.prefix, + addr->u.a6.pfxlen); cmd->data.setdelip6.flags = flags; } else { - memcpy(cmd->data.setdelip4.ip_addr, &addr->u.a4.addr, 4); - memcpy(cmd->data.setdelip4.mask, &addr->u.a4.mask, 4); + cmd->data.setdelip4.addr = addr->u.a4.addr; + cmd->data.setdelip4.mask = addr->u.a4.mask; cmd->data.setdelip4.flags = flags; } @@ -2436,7 +2432,7 @@ static int qeth_l3_ip_event(struct notifier_block *this, qeth_l3_init_ipaddr(&addr, QETH_IP_TYPE_NORMAL, QETH_PROT_IPV4); addr.u.a4.addr = ifa->ifa_address; - addr.u.a4.mask = be32_to_cpu(ifa->ifa_mask); + addr.u.a4.mask = ifa->ifa_mask; return qeth_l3_handle_ip_event(card, &addr, event); } -- GitLab From b6beb62a5261f4ba2e73a39889f19e5af3ce545a Mon Sep 17 00:00:00 2001 From: Julian Wiedmann Date: Wed, 18 Dec 2019 17:34:47 +0100 Subject: [PATCH 1014/1437] s390/qeth: don't raise NETDEV_REBOOT event from L3 offline path This is a leftover from back when a recovery action didn't go through dev_close(), and was meant to shoot down all remaining af_iucv sockets on the interface. Now that the offline path always calls dev_close(), the NETDEV_GOING_DOWN event from __dev_close_many() is sufficient and this hack can be removed. Signed-off-by: Julian Wiedmann Signed-off-by: David S. Miller --- drivers/s390/net/qeth_l3_main.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/drivers/s390/net/qeth_l3_main.c b/drivers/s390/net/qeth_l3_main.c index bd2273f9ca353..8bd3fc73a4fbd 100644 --- a/drivers/s390/net/qeth_l3_main.c +++ b/drivers/s390/net/qeth_l3_main.c @@ -2288,12 +2288,6 @@ static int __qeth_l3_set_offline(struct ccwgroup_device *cgdev, rtnl_unlock(); qeth_l3_stop_card(card); - if (card->options.cq == QETH_CQ_ENABLED) { - rtnl_lock(); - call_netdevice_notifiers(NETDEV_REBOOT, card->dev); - rtnl_unlock(); - } - rc = qeth_stop_channel(&card->data); rc2 = qeth_stop_channel(&card->write); rc3 = qeth_stop_channel(&card->read); -- GitLab From adee2592b6c0e96d8b079c7c9116264293e923e6 Mon Sep 17 00:00:00 2001 From: Julian Wiedmann Date: Wed, 18 Dec 2019 17:34:48 +0100 Subject: [PATCH 1015/1437] s390/qeth: stop yielding the ip_lock during IPv4 registration As commit df2a2a5225cc ("s390/qeth: convert IP table spinlock to mutex") converted the ip_lock to a mutex, we no longer have to yield it while the subsequent IO sleep-waits for completion. Signed-off-by: Julian Wiedmann Signed-off-by: David S. Miller --- drivers/s390/net/qeth_l3.h | 1 - drivers/s390/net/qeth_l3_main.c | 34 ++------------------------------- 2 files changed, 2 insertions(+), 33 deletions(-) diff --git a/drivers/s390/net/qeth_l3.h b/drivers/s390/net/qeth_l3.h index 89fb91dad12e9..6ccfe2121095a 100644 --- a/drivers/s390/net/qeth_l3.h +++ b/drivers/s390/net/qeth_l3.h @@ -23,7 +23,6 @@ struct qeth_ipaddr { struct hlist_node hnode; enum qeth_ip_types type; u8 is_multicast:1; - u8 in_progress:1; u8 disp_flag:2; u8 ipato:1; /* ucast only */ diff --git a/drivers/s390/net/qeth_l3_main.c b/drivers/s390/net/qeth_l3_main.c index 8bd3fc73a4fbd..789d3b2ba0de8 100644 --- a/drivers/s390/net/qeth_l3_main.c +++ b/drivers/s390/net/qeth_l3_main.c @@ -151,8 +151,6 @@ static int qeth_l3_delete_ip(struct qeth_card *card, addr->ref_counter--; if (addr->type == QETH_IP_TYPE_NORMAL && addr->ref_counter > 0) return rc; - if (addr->in_progress) - return -EINPROGRESS; if (qeth_card_hw_is_reachable(card)) rc = qeth_l3_deregister_addr_entry(card, addr); @@ -213,29 +211,10 @@ static int qeth_l3_add_ip(struct qeth_card *card, struct qeth_ipaddr *tmp_addr) return 0; } - /* qeth_l3_register_addr_entry can go to sleep - * if we add a IPV4 addr. It is caused by the reason - * that SETIP ipa cmd starts ARP staff for IPV4 addr. - * Thus we should unlock spinlock, and make a protection - * using in_progress variable to indicate that there is - * an hardware operation with this IPV4 address - */ - if (addr->proto == QETH_PROT_IPV4) { - addr->in_progress = 1; - mutex_unlock(&card->ip_lock); - rc = qeth_l3_register_addr_entry(card, addr); - mutex_lock(&card->ip_lock); - addr->in_progress = 0; - } else - rc = qeth_l3_register_addr_entry(card, addr); + rc = qeth_l3_register_addr_entry(card, addr); if (!rc || rc == -EADDRINUSE || rc == -ENETDOWN) { addr->disp_flag = QETH_DISP_ADDR_DO_NOTHING; - if (addr->ref_counter < 1) { - qeth_l3_deregister_addr_entry(card, addr); - hash_del(&addr->hnode); - kfree(addr); - } } else { hash_del(&addr->hnode); kfree(addr); @@ -303,19 +282,10 @@ static void qeth_l3_recover_ip(struct qeth_card *card) hash_for_each_safe(card->ip_htable, i, tmp, addr, hnode) { if (addr->disp_flag == QETH_DISP_ADDR_ADD) { - if (addr->proto == QETH_PROT_IPV4) { - addr->in_progress = 1; - mutex_unlock(&card->ip_lock); - rc = qeth_l3_register_addr_entry(card, addr); - mutex_lock(&card->ip_lock); - addr->in_progress = 0; - } else - rc = qeth_l3_register_addr_entry(card, addr); + rc = qeth_l3_register_addr_entry(card, addr); if (!rc) { addr->disp_flag = QETH_DISP_ADDR_DO_NOTHING; - if (addr->ref_counter < 1) - qeth_l3_delete_ip(card, addr); } else { hash_del(&addr->hnode); kfree(addr); -- GitLab From 52f82bf16b6e754c2a9971420ccc8209ff52081c Mon Sep 17 00:00:00 2001 From: Julian Wiedmann Date: Wed, 18 Dec 2019 17:34:49 +0100 Subject: [PATCH 1016/1437] s390/qeth: consolidate helpers for capability checking Convert the old code to use struct qeth_ipa_caps, and while at it remove all unused helper macros. Signed-off-by: Julian Wiedmann Signed-off-by: David S. Miller --- drivers/s390/net/qeth_core.h | 47 ++----------------------------- drivers/s390/net/qeth_core_main.c | 28 +++++++++--------- drivers/s390/net/qeth_core_mpc.h | 13 +++++++-- 3 files changed, 27 insertions(+), 61 deletions(-) diff --git a/drivers/s390/net/qeth_core.h b/drivers/s390/net/qeth_core.h index b54ef12840a21..6e16b19732f6a 100644 --- a/drivers/s390/net/qeth_core.h +++ b/drivers/s390/net/qeth_core.h @@ -125,12 +125,6 @@ struct qeth_routing_info { enum qeth_routing_types type; }; -/* IPA stuff */ -struct qeth_ipa_info { - __u32 supported_funcs; - __u32 enabled_funcs; -}; - /* SETBRIDGEPORT stuff */ enum qeth_sbp_roles { QETH_SBP_ROLE_NONE = 0, @@ -169,41 +163,6 @@ struct qeth_vnicc_info { bool rx_bcast_enabled; }; -static inline int qeth_is_adp_supported(struct qeth_ipa_info *ipa, - enum qeth_ipa_setadp_cmd func) -{ - return (ipa->supported_funcs & func); -} - -static inline int qeth_is_ipa_supported(struct qeth_ipa_info *ipa, - enum qeth_ipa_funcs func) -{ - return (ipa->supported_funcs & func); -} - -static inline int qeth_is_ipa_enabled(struct qeth_ipa_info *ipa, - enum qeth_ipa_funcs func) -{ - return (ipa->supported_funcs & ipa->enabled_funcs & func); -} - -#define qeth_adp_supported(c, f) \ - qeth_is_adp_supported(&c->options.adp, f) -#define qeth_is_supported(c, f) \ - qeth_is_ipa_supported(&c->options.ipa4, f) -#define qeth_is_enabled(c, f) \ - qeth_is_ipa_enabled(&c->options.ipa4, f) -#define qeth_is_supported6(c, f) \ - qeth_is_ipa_supported(&c->options.ipa6, f) -#define qeth_is_enabled6(c, f) \ - qeth_is_ipa_enabled(&c->options.ipa6, f) -#define qeth_is_ipafunc_supported(c, prot, f) \ - ((prot == QETH_PROT_IPV6) ? \ - qeth_is_supported6(c, f) : qeth_is_supported(c, f)) -#define qeth_is_ipafunc_enabled(c, prot, f) \ - ((prot == QETH_PROT_IPV6) ? \ - qeth_is_enabled6(c, f) : qeth_is_enabled(c, f)) - #define QETH_IDX_FUNC_LEVEL_OSD 0x0101 #define QETH_IDX_FUNC_LEVEL_IQD 0x4108 @@ -735,11 +694,11 @@ enum qeth_discipline_id { }; struct qeth_card_options { + struct qeth_ipa_caps ipa4; + struct qeth_ipa_caps ipa6; struct qeth_routing_info route4; - struct qeth_ipa_info ipa4; - struct qeth_ipa_info adp; /*Adapter parameters*/ struct qeth_routing_info route6; - struct qeth_ipa_info ipa6; + struct qeth_ipa_caps adp; /* Adapter parameters */ struct qeth_sbp_info sbp; /* SETBRIDGEPORT options */ struct qeth_vnicc_info vnicc; /* VNICC options */ int fake_broadcast; diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c index 38f3ed7567bc0..6b55271091cb4 100644 --- a/drivers/s390/net/qeth_core_main.c +++ b/drivers/s390/net/qeth_core_main.c @@ -2862,7 +2862,7 @@ static int qeth_query_setadapterparms_cb(struct qeth_card *card, cmd->data.setadapterparms.data.query_cmds_supp.lan_type; QETH_CARD_TEXT_(card, 2, "lnk %d", card->info.link_type); } - card->options.adp.supported_funcs = + card->options.adp.supported = cmd->data.setadapterparms.data.query_cmds_supp.supported_cmds; return 0; } @@ -2918,8 +2918,8 @@ static int qeth_query_ipassists_cb(struct qeth_card *card, case IPA_RC_NOTSUPP: case IPA_RC_L2_UNSUPPORTED_CMD: QETH_CARD_TEXT(card, 2, "ipaunsup"); - card->options.ipa4.supported_funcs |= IPA_SETADAPTERPARMS; - card->options.ipa6.supported_funcs |= IPA_SETADAPTERPARMS; + card->options.ipa4.supported |= IPA_SETADAPTERPARMS; + card->options.ipa6.supported |= IPA_SETADAPTERPARMS; return -EOPNOTSUPP; default: QETH_DBF_MESSAGE(1, "IPA_CMD_QIPASSIST on device %x: Unhandled rc=%#x\n", @@ -2927,13 +2927,11 @@ static int qeth_query_ipassists_cb(struct qeth_card *card, return -EIO; } - if (cmd->hdr.prot_version == QETH_PROT_IPV4) { - card->options.ipa4.supported_funcs = cmd->hdr.ipa_supported; - card->options.ipa4.enabled_funcs = cmd->hdr.ipa_enabled; - } else if (cmd->hdr.prot_version == QETH_PROT_IPV6) { - card->options.ipa6.supported_funcs = cmd->hdr.ipa_supported; - card->options.ipa6.enabled_funcs = cmd->hdr.ipa_enabled; - } else + if (cmd->hdr.prot_version == QETH_PROT_IPV4) + card->options.ipa4 = cmd->hdr.assists; + else if (cmd->hdr.prot_version == QETH_PROT_IPV6) + card->options.ipa6 = cmd->hdr.assists; + else QETH_DBF_MESSAGE(1, "IPA_CMD_QIPASSIST on device %x: Flawed LIC detected\n", CARD_DEVID(card)); return 0; @@ -5002,9 +5000,9 @@ retriable: *carrier_ok = true; } - card->options.ipa4.supported_funcs = 0; - card->options.ipa6.supported_funcs = 0; - card->options.adp.supported_funcs = 0; + card->options.ipa4.supported = 0; + card->options.ipa6.supported = 0; + card->options.adp.supported = 0; card->options.sbp.supported_funcs = 0; card->info.diagass_support = 0; rc = qeth_query_ipassists(card, QETH_PROT_IPV4); @@ -5421,9 +5419,9 @@ int qeth_setassparms_cb(struct qeth_card *card, cmd->hdr.return_code = cmd->data.setassparms.hdr.return_code; if (cmd->hdr.prot_version == QETH_PROT_IPV4) - card->options.ipa4.enabled_funcs = cmd->hdr.ipa_enabled; + card->options.ipa4.enabled = cmd->hdr.assists.enabled; if (cmd->hdr.prot_version == QETH_PROT_IPV6) - card->options.ipa6.enabled_funcs = cmd->hdr.ipa_enabled; + card->options.ipa6.enabled = cmd->hdr.assists.enabled; return 0; } EXPORT_SYMBOL_GPL(qeth_setassparms_cb); diff --git a/drivers/s390/net/qeth_core_mpc.h b/drivers/s390/net/qeth_core_mpc.h index f13225b437819..f4dc37e28ac7f 100644 --- a/drivers/s390/net/qeth_core_mpc.h +++ b/drivers/s390/net/qeth_core_mpc.h @@ -53,6 +53,16 @@ static inline bool qeth_ipa_caps_enabled(struct qeth_ipa_caps *caps, u32 mask) return (caps->enabled & mask) == mask; } +#define qeth_adp_supported(c, f) \ + qeth_ipa_caps_supported(&c->options.adp, f) +#define qeth_is_supported(c, f) \ + qeth_ipa_caps_supported(&c->options.ipa4, f) +#define qeth_is_supported6(c, f) \ + qeth_ipa_caps_supported(&c->options.ipa6, f) +#define qeth_is_ipafunc_supported(c, prot, f) \ + ((prot == QETH_PROT_IPV6) ? qeth_is_supported6(c, f) : \ + qeth_is_supported(c, f)) + enum qeth_card_types { QETH_CARD_TYPE_OSD = 1, QETH_CARD_TYPE_IQD = 5, @@ -766,8 +776,7 @@ struct qeth_ipacmd_hdr { __u8 prim_version_no; __u8 param_count; __u16 prot_version; - __u32 ipa_supported; - __u32 ipa_enabled; + struct qeth_ipa_caps assists; } __attribute__ ((packed)); /* The IPA command itself */ -- GitLab From 334b49de120dd6deb28c31eedb7ca81351f7464e Mon Sep 17 00:00:00 2001 From: Julian Wiedmann Date: Wed, 18 Dec 2019 17:34:50 +0100 Subject: [PATCH 1017/1437] s390/qeth: make use of napi_schedule_irqoff() qeth_qdio_start_poll() is called from the qdio layer's IRQ handler, while IRQs are masked. Signed-off-by: Julian Wiedmann Signed-off-by: David S. Miller --- drivers/s390/net/qeth_core_main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c index 6b55271091cb4..78349355c582c 100644 --- a/drivers/s390/net/qeth_core_main.c +++ b/drivers/s390/net/qeth_core_main.c @@ -3402,7 +3402,7 @@ static void qeth_qdio_start_poll(struct ccw_device *ccwdev, int queue, struct qeth_card *card = (struct qeth_card *)card_ptr; if (card->dev->flags & IFF_UP) - napi_schedule(&card->napi); + napi_schedule_irqoff(&card->napi); } int qeth_configure_cq(struct qeth_card *card, enum qeth_cq cq) -- GitLab From cbd22f172df782d6b2150044d0ff2051bad2a00f Mon Sep 17 00:00:00 2001 From: Kevin 'ldir' Darbyshire-Bryant Date: Wed, 18 Dec 2019 14:05:13 +0000 Subject: [PATCH 1018/1437] sch_cake: drop unused variable tin_quantum_prio MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Turns out tin_quantum_prio isn't used anymore and is a leftover from a previous implementation of diffserv tins. Since the variable isn't used in any calculations it can be eliminated. Drop variable and places where it was set. Rename remaining variable and consolidate naming of intermediate variables that set it. Signed-off-by: Kevin Darbyshire-Bryant Acked-by: Toke Høiland-Jørgensen Signed-off-by: David S. Miller --- net/sched/sch_cake.c | 59 ++++++++++++++------------------------------ 1 file changed, 18 insertions(+), 41 deletions(-) diff --git a/net/sched/sch_cake.c b/net/sched/sch_cake.c index e0f40400f679c..6cc3ab145513e 100644 --- a/net/sched/sch_cake.c +++ b/net/sched/sch_cake.c @@ -173,8 +173,7 @@ struct cake_tin_data { u64 tin_rate_bps; u16 tin_rate_shft; - u16 tin_quantum_prio; - u16 tin_quantum_band; + u16 tin_quantum; s32 tin_deficit; u32 tin_backlog; u32 tin_dropped; @@ -1919,7 +1918,7 @@ begin: while (b->tin_deficit < 0 || !(b->sparse_flow_count + b->bulk_flow_count)) { if (b->tin_deficit <= 0) - b->tin_deficit += b->tin_quantum_band; + b->tin_deficit += b->tin_quantum; if (b->sparse_flow_count + b->bulk_flow_count) empty = false; @@ -2241,8 +2240,7 @@ static int cake_config_besteffort(struct Qdisc *sch) cake_set_rate(b, rate, mtu, us_to_ns(q->target), us_to_ns(q->interval)); - b->tin_quantum_band = 65535; - b->tin_quantum_prio = 65535; + b->tin_quantum = 65535; return 0; } @@ -2253,8 +2251,7 @@ static int cake_config_precedence(struct Qdisc *sch) struct cake_sched_data *q = qdisc_priv(sch); u32 mtu = psched_mtu(qdisc_dev(sch)); u64 rate = q->rate_bps; - u32 quantum1 = 256; - u32 quantum2 = 256; + u32 quantum = 256; u32 i; q->tin_cnt = 8; @@ -2267,18 +2264,14 @@ static int cake_config_precedence(struct Qdisc *sch) cake_set_rate(b, rate, mtu, us_to_ns(q->target), us_to_ns(q->interval)); - b->tin_quantum_prio = max_t(u16, 1U, quantum1); - b->tin_quantum_band = max_t(u16, 1U, quantum2); + b->tin_quantum = max_t(u16, 1U, quantum); /* calculate next class's parameters */ rate *= 7; rate >>= 3; - quantum1 *= 3; - quantum1 >>= 1; - - quantum2 *= 7; - quantum2 >>= 3; + quantum *= 7; + quantum >>= 3; } return 0; @@ -2347,8 +2340,7 @@ static int cake_config_diffserv8(struct Qdisc *sch) struct cake_sched_data *q = qdisc_priv(sch); u32 mtu = psched_mtu(qdisc_dev(sch)); u64 rate = q->rate_bps; - u32 quantum1 = 256; - u32 quantum2 = 256; + u32 quantum = 256; u32 i; q->tin_cnt = 8; @@ -2364,18 +2356,14 @@ static int cake_config_diffserv8(struct Qdisc *sch) cake_set_rate(b, rate, mtu, us_to_ns(q->target), us_to_ns(q->interval)); - b->tin_quantum_prio = max_t(u16, 1U, quantum1); - b->tin_quantum_band = max_t(u16, 1U, quantum2); + b->tin_quantum = max_t(u16, 1U, quantum); /* calculate next class's parameters */ rate *= 7; rate >>= 3; - quantum1 *= 3; - quantum1 >>= 1; - - quantum2 *= 7; - quantum2 >>= 3; + quantum *= 7; + quantum >>= 3; } return 0; @@ -2414,17 +2402,11 @@ static int cake_config_diffserv4(struct Qdisc *sch) cake_set_rate(&q->tins[3], rate >> 2, mtu, us_to_ns(q->target), us_to_ns(q->interval)); - /* priority weights */ - q->tins[0].tin_quantum_prio = quantum; - q->tins[1].tin_quantum_prio = quantum >> 4; - q->tins[2].tin_quantum_prio = quantum << 2; - q->tins[3].tin_quantum_prio = quantum << 4; - /* bandwidth-sharing weights */ - q->tins[0].tin_quantum_band = quantum; - q->tins[1].tin_quantum_band = quantum >> 4; - q->tins[2].tin_quantum_band = quantum >> 1; - q->tins[3].tin_quantum_band = quantum >> 2; + q->tins[0].tin_quantum = quantum; + q->tins[1].tin_quantum = quantum >> 4; + q->tins[2].tin_quantum = quantum >> 1; + q->tins[3].tin_quantum = quantum >> 2; return 0; } @@ -2455,15 +2437,10 @@ static int cake_config_diffserv3(struct Qdisc *sch) cake_set_rate(&q->tins[2], rate >> 2, mtu, us_to_ns(q->target), us_to_ns(q->interval)); - /* priority weights */ - q->tins[0].tin_quantum_prio = quantum; - q->tins[1].tin_quantum_prio = quantum >> 4; - q->tins[2].tin_quantum_prio = quantum << 4; - /* bandwidth-sharing weights */ - q->tins[0].tin_quantum_band = quantum; - q->tins[1].tin_quantum_band = quantum >> 4; - q->tins[2].tin_quantum_band = quantum >> 2; + q->tins[0].tin_quantum = quantum; + q->tins[1].tin_quantum = quantum >> 4; + q->tins[2].tin_quantum = quantum >> 2; return 0; } -- GitLab From 9586a992fb752b14ac18fc86fe086b0e3372fff4 Mon Sep 17 00:00:00 2001 From: Petr Machata Date: Wed, 18 Dec 2019 14:55:08 +0000 Subject: [PATCH 1019/1437] net: pkt_cls: Clarify a comment The bit about negating HW backlog left me scratching my head. Clarify the comment. Signed-off-by: Petr Machata Reviewed-by: Ido Schimmel Acked-by: Jiri Pirko Signed-off-by: David S. Miller --- include/net/pkt_cls.h | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/include/net/pkt_cls.h b/include/net/pkt_cls.h index e553fc80eb230..a7c5d492bc04a 100644 --- a/include/net/pkt_cls.h +++ b/include/net/pkt_cls.h @@ -791,9 +791,8 @@ enum tc_prio_command { struct tc_prio_qopt_offload_params { int bands; u8 priomap[TC_PRIO_MAX + 1]; - /* In case that a prio qdisc is offloaded and now is changed to a - * non-offloadedable config, it needs to update the backlog & qlen - * values to negate the HW backlog & qlen values (and only them). + /* At the point of un-offloading the Qdisc, the reported backlog and + * qlen need to be reduced by the portion that is in HW. */ struct gnet_stats_queue *qstats; }; -- GitLab From 5bc146c90e9eed409210809fae3c0efebef68ae1 Mon Sep 17 00:00:00 2001 From: Petr Machata Date: Wed, 18 Dec 2019 14:55:10 +0000 Subject: [PATCH 1020/1437] mlxsw: spectrum_qdisc: Clarify a comment Expand the comment at mlxsw_sp_qdisc_prio_graft() to make the problem that this function is trying to handle clearer. Signed-off-by: Petr Machata Reviewed-by: Ido Schimmel Acked-by: Jiri Pirko Signed-off-by: David S. Miller --- .../ethernet/mellanox/mlxsw/spectrum_qdisc.c | 31 ++++++++++++++----- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_qdisc.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_qdisc.c index 68cc6737d45ce..135fef6c54b16 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_qdisc.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_qdisc.c @@ -631,10 +631,30 @@ static struct mlxsw_sp_qdisc_ops mlxsw_sp_qdisc_ops_prio = { .clean_stats = mlxsw_sp_setup_tc_qdisc_prio_clean_stats, }; -/* Grafting is not supported in mlxsw. It will result in un-offloading of the - * grafted qdisc as well as the qdisc in the qdisc new location. - * (However, if the graft is to the location where the qdisc is already at, it - * will be ignored completely and won't cause un-offloading). +/* Linux allows linking of Qdiscs to arbitrary classes (so long as the resulting + * graph is free of cycles). These operations do not change the parent handle + * though, which means it can be incomplete (if there is more than one class + * where the Qdisc in question is grafted) or outright wrong (if the Qdisc was + * linked to a different class and then removed from the original class). + * + * E.g. consider this sequence of operations: + * + * # tc qdisc add dev swp1 root handle 1: prio + * # tc qdisc add dev swp1 parent 1:3 handle 13: red limit 1000000 avpkt 10000 + * RED: set bandwidth to 10Mbit + * # tc qdisc link dev swp1 handle 13: parent 1:2 + * + * At this point, both 1:2 and 1:3 have the same RED Qdisc instance as their + * child. But RED will still only claim that 1:3 is its parent. If it's removed + * from that band, its only parent will be 1:2, but it will continue to claim + * that it is in fact 1:3. + * + * The notification for child Qdisc replace (e.g. TC_RED_REPLACE) comes before + * the notification for parent graft (e.g. TC_PRIO_GRAFT). We take the replace + * notification to offload the child Qdisc, based on its parent handle, and use + * the graft operation to validate that the class where the child is actually + * grafted corresponds to the parent handle. If the two don't match, we + * unoffload the child. */ static int mlxsw_sp_qdisc_prio_graft(struct mlxsw_sp_port *mlxsw_sp_port, @@ -644,9 +664,6 @@ mlxsw_sp_qdisc_prio_graft(struct mlxsw_sp_port *mlxsw_sp_port, int tclass_num = MLXSW_SP_PRIO_BAND_TO_TCLASS(p->band); struct mlxsw_sp_qdisc *old_qdisc; - /* Check if the grafted qdisc is already in its "new" location. If so - - * nothing needs to be done. - */ if (p->band < IEEE_8021QAZ_MAX_TCS && mlxsw_sp_port->tclass_qdiscs[tclass_num].handle == p->child_handle) return 0; -- GitLab From 9cf9b925d5387b501732dcbc8fe3c77104d423f8 Mon Sep 17 00:00:00 2001 From: Petr Machata Date: Wed, 18 Dec 2019 14:55:11 +0000 Subject: [PATCH 1021/1437] mlxsw: spectrum: Rename MLXSW_REG_QEEC_HIERARCY_* enumerators These enums want to be named MLXSW_REG_QEEC_HIERARCHY_, but due to a typo lack the second H. That is confusing and complicates searching. But actually the enumerators should be named _HR_, because that is how their enum type is called. So rename them as appropriate. Signed-off-by: Petr Machata Acked-by: Jiri Pirko Reviewed-by: Ido Schimmel Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/reg.h | 11 +++++------ .../net/ethernet/mellanox/mlxsw/spectrum.c | 19 +++++++++---------- .../ethernet/mellanox/mlxsw/spectrum_dcb.c | 8 ++++---- 3 files changed, 18 insertions(+), 20 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlxsw/reg.h b/drivers/net/ethernet/mellanox/mlxsw/reg.h index 5294a1622643e..86a2d575ae73b 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/reg.h +++ b/drivers/net/ethernet/mellanox/mlxsw/reg.h @@ -3477,10 +3477,10 @@ MLXSW_REG_DEFINE(qeec, MLXSW_REG_QEEC_ID, MLXSW_REG_QEEC_LEN); MLXSW_ITEM32(reg, qeec, local_port, 0x00, 16, 8); enum mlxsw_reg_qeec_hr { - MLXSW_REG_QEEC_HIERARCY_PORT, - MLXSW_REG_QEEC_HIERARCY_GROUP, - MLXSW_REG_QEEC_HIERARCY_SUBGROUP, - MLXSW_REG_QEEC_HIERARCY_TC, + MLXSW_REG_QEEC_HR_PORT, + MLXSW_REG_QEEC_HR_GROUP, + MLXSW_REG_QEEC_HR_SUBGROUP, + MLXSW_REG_QEEC_HR_TC, }; /* reg_qeec_element_hierarchy @@ -3618,8 +3618,7 @@ static inline void mlxsw_reg_qeec_ptps_pack(char *payload, u8 local_port, { MLXSW_REG_ZERO(qeec, payload); mlxsw_reg_qeec_local_port_set(payload, local_port); - mlxsw_reg_qeec_element_hierarchy_set(payload, - MLXSW_REG_QEEC_HIERARCY_PORT); + mlxsw_reg_qeec_element_hierarchy_set(payload, MLXSW_REG_QEEC_HR_PORT); mlxsw_reg_qeec_ptps_set(payload, ptps); } diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c index 556dca328bb51..0d8fce749248f 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c @@ -3602,26 +3602,25 @@ static int mlxsw_sp_port_ets_init(struct mlxsw_sp_port *mlxsw_sp_port) * one subgroup, which are all member in the same group. */ err = mlxsw_sp_port_ets_set(mlxsw_sp_port, - MLXSW_REG_QEEC_HIERARCY_GROUP, 0, 0, false, - 0); + MLXSW_REG_QEEC_HR_GROUP, 0, 0, false, 0); if (err) return err; for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) { err = mlxsw_sp_port_ets_set(mlxsw_sp_port, - MLXSW_REG_QEEC_HIERARCY_SUBGROUP, i, + MLXSW_REG_QEEC_HR_SUBGROUP, i, 0, false, 0); if (err) return err; } for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) { err = mlxsw_sp_port_ets_set(mlxsw_sp_port, - MLXSW_REG_QEEC_HIERARCY_TC, i, i, + MLXSW_REG_QEEC_HR_TC, i, i, false, 0); if (err) return err; err = mlxsw_sp_port_ets_set(mlxsw_sp_port, - MLXSW_REG_QEEC_HIERARCY_TC, + MLXSW_REG_QEEC_HR_TC, i + 8, i, true, 100); if (err) @@ -3633,13 +3632,13 @@ static int mlxsw_sp_port_ets_init(struct mlxsw_sp_port *mlxsw_sp_port) * for the initial configuration. */ err = mlxsw_sp_port_ets_maxrate_set(mlxsw_sp_port, - MLXSW_REG_QEEC_HIERARCY_PORT, 0, 0, + MLXSW_REG_QEEC_HR_PORT, 0, 0, MLXSW_REG_QEEC_MAS_DIS); if (err) return err; for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) { err = mlxsw_sp_port_ets_maxrate_set(mlxsw_sp_port, - MLXSW_REG_QEEC_HIERARCY_SUBGROUP, + MLXSW_REG_QEEC_HR_SUBGROUP, i, 0, MLXSW_REG_QEEC_MAS_DIS); if (err) @@ -3647,14 +3646,14 @@ static int mlxsw_sp_port_ets_init(struct mlxsw_sp_port *mlxsw_sp_port) } for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) { err = mlxsw_sp_port_ets_maxrate_set(mlxsw_sp_port, - MLXSW_REG_QEEC_HIERARCY_TC, + MLXSW_REG_QEEC_HR_TC, i, i, MLXSW_REG_QEEC_MAS_DIS); if (err) return err; err = mlxsw_sp_port_ets_maxrate_set(mlxsw_sp_port, - MLXSW_REG_QEEC_HIERARCY_TC, + MLXSW_REG_QEEC_HR_TC, i + 8, i, MLXSW_REG_QEEC_MAS_DIS); if (err) @@ -3664,7 +3663,7 @@ static int mlxsw_sp_port_ets_init(struct mlxsw_sp_port *mlxsw_sp_port) /* Configure the min shaper for multicast TCs. */ for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) { err = mlxsw_sp_port_min_bw_set(mlxsw_sp_port, - MLXSW_REG_QEEC_HIERARCY_TC, + MLXSW_REG_QEEC_HR_TC, i + 8, i, MLXSW_REG_QEEC_MIS_MIN); if (err) diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_dcb.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_dcb.c index 21296fa7f7fbf..fe3bbba906594 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_dcb.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_dcb.c @@ -160,7 +160,7 @@ static int __mlxsw_sp_dcbnl_ieee_setets(struct mlxsw_sp_port *mlxsw_sp_port, u8 weight = ets->tc_tx_bw[i]; err = mlxsw_sp_port_ets_set(mlxsw_sp_port, - MLXSW_REG_QEEC_HIERARCY_SUBGROUP, i, + MLXSW_REG_QEEC_HR_SUBGROUP, i, 0, dwrr, weight); if (err) { netdev_err(dev, "Failed to link subgroup ETS element %d to group\n", @@ -198,7 +198,7 @@ err_port_ets_set: u8 weight = my_ets->tc_tx_bw[i]; err = mlxsw_sp_port_ets_set(mlxsw_sp_port, - MLXSW_REG_QEEC_HIERARCY_SUBGROUP, i, + MLXSW_REG_QEEC_HR_SUBGROUP, i, 0, dwrr, weight); } return err; @@ -507,7 +507,7 @@ static int mlxsw_sp_dcbnl_ieee_setmaxrate(struct net_device *dev, for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) { err = mlxsw_sp_port_ets_maxrate_set(mlxsw_sp_port, - MLXSW_REG_QEEC_HIERARCY_SUBGROUP, + MLXSW_REG_QEEC_HR_SUBGROUP, i, 0, maxrate->tc_maxrate[i]); if (err) { @@ -523,7 +523,7 @@ static int mlxsw_sp_dcbnl_ieee_setmaxrate(struct net_device *dev, err_port_ets_maxrate_set: for (i--; i >= 0; i--) mlxsw_sp_port_ets_maxrate_set(mlxsw_sp_port, - MLXSW_REG_QEEC_HIERARCY_SUBGROUP, + MLXSW_REG_QEEC_HR_SUBGROUP, i, 0, my_maxrate->tc_maxrate[i]); return err; } -- GitLab From dcc68b4d8084e1ac9af0d4022d6b1aff6a139a33 Mon Sep 17 00:00:00 2001 From: Petr Machata Date: Wed, 18 Dec 2019 14:55:13 +0000 Subject: [PATCH 1022/1437] net: sch_ets: Add a new Qdisc Introduces a new Qdisc, which is based on 802.1Q-2014 wording. It is PRIO-like in how it is configured, meaning one needs to specify how many bands there are, how many are strict and how many are dwrr, quanta for the latter, and priomap. The new Qdisc operates like the PRIO / DRR combo would when configured as per the standard. The strict classes, if any, are tried for traffic first. When there's no traffic in any of the strict queues, the ETS ones (if any) are treated in the same way as in DRR. Signed-off-by: Petr Machata Acked-by: Jiri Pirko Signed-off-by: David S. Miller --- include/uapi/linux/pkt_sched.h | 17 + net/sched/Kconfig | 17 + net/sched/Makefile | 1 + net/sched/sch_ets.c | 733 +++++++++++++++++++++++++++++++++ 4 files changed, 768 insertions(+) create mode 100644 net/sched/sch_ets.c diff --git a/include/uapi/linux/pkt_sched.h b/include/uapi/linux/pkt_sched.h index 9f1a728762124..bf5a5b1dfb0b0 100644 --- a/include/uapi/linux/pkt_sched.h +++ b/include/uapi/linux/pkt_sched.h @@ -1187,4 +1187,21 @@ enum { #define TCA_TAPRIO_ATTR_MAX (__TCA_TAPRIO_ATTR_MAX - 1) +/* ETS */ + +#define TCQ_ETS_MAX_BANDS 16 + +enum { + TCA_ETS_UNSPEC, + TCA_ETS_NBANDS, /* u8 */ + TCA_ETS_NSTRICT, /* u8 */ + TCA_ETS_QUANTA, /* nested TCA_ETS_QUANTA_BAND */ + TCA_ETS_QUANTA_BAND, /* u32 */ + TCA_ETS_PRIOMAP, /* nested TCA_ETS_PRIOMAP_BAND */ + TCA_ETS_PRIOMAP_BAND, /* u8 */ + __TCA_ETS_MAX, +}; + +#define TCA_ETS_MAX (__TCA_ETS_MAX - 1) + #endif diff --git a/net/sched/Kconfig b/net/sched/Kconfig index 2985509147a22..b1e7ec7269582 100644 --- a/net/sched/Kconfig +++ b/net/sched/Kconfig @@ -409,6 +409,23 @@ config NET_SCH_PLUG To compile this code as a module, choose M here: the module will be called sch_plug. +config NET_SCH_ETS + tristate "Enhanced transmission selection scheduler (ETS)" + help + The Enhanced Transmission Selection scheduler is a classful + queuing discipline that merges functionality of PRIO and DRR + qdiscs in one scheduler. ETS makes it easy to configure a set of + strict and bandwidth-sharing bands to implement the transmission + selection described in 802.1Qaz. + + Say Y here if you want to use the ETS packet scheduling + algorithm. + + To compile this driver as a module, choose M here: the module + will be called sch_ets. + + If unsure, say N. + menuconfig NET_SCH_DEFAULT bool "Allow override default queue discipline" ---help--- diff --git a/net/sched/Makefile b/net/sched/Makefile index 415d1e1f237e1..bc8856b865ff5 100644 --- a/net/sched/Makefile +++ b/net/sched/Makefile @@ -48,6 +48,7 @@ obj-$(CONFIG_NET_SCH_ATM) += sch_atm.o obj-$(CONFIG_NET_SCH_NETEM) += sch_netem.o obj-$(CONFIG_NET_SCH_DRR) += sch_drr.o obj-$(CONFIG_NET_SCH_PLUG) += sch_plug.o +obj-$(CONFIG_NET_SCH_ETS) += sch_ets.o obj-$(CONFIG_NET_SCH_MQPRIO) += sch_mqprio.o obj-$(CONFIG_NET_SCH_SKBPRIO) += sch_skbprio.o obj-$(CONFIG_NET_SCH_CHOKE) += sch_choke.o diff --git a/net/sched/sch_ets.c b/net/sched/sch_ets.c new file mode 100644 index 0000000000000..e6194b23e9b0d --- /dev/null +++ b/net/sched/sch_ets.c @@ -0,0 +1,733 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * net/sched/sch_ets.c Enhanced Transmission Selection scheduler + * + * Description + * ----------- + * + * The Enhanced Transmission Selection scheduler is a classful queuing + * discipline that merges functionality of PRIO and DRR qdiscs in one scheduler. + * ETS makes it easy to configure a set of strict and bandwidth-sharing bands to + * implement the transmission selection described in 802.1Qaz. + * + * Although ETS is technically classful, it's not possible to add and remove + * classes at will. Instead one specifies number of classes, how many are + * PRIO-like and how many DRR-like, and quanta for the latter. + * + * Algorithm + * --------- + * + * The strict classes, if any, are tried for traffic first: first band 0, if it + * has no traffic then band 1, etc. + * + * When there is no traffic in any of the strict queues, the bandwidth-sharing + * ones are tried next. Each band is assigned a deficit counter, initialized to + * "quantum" of that band. ETS maintains a list of active bandwidth-sharing + * bands whose qdiscs are non-empty. A packet is dequeued from the band at the + * head of the list if the packet size is smaller or equal to the deficit + * counter. If the counter is too small, it is increased by "quantum" and the + * scheduler moves on to the next band in the active list. + */ + +#include +#include +#include +#include +#include +#include + +struct ets_class { + struct list_head alist; /* In struct ets_sched.active. */ + struct Qdisc *qdisc; + u32 quantum; + u32 deficit; + struct gnet_stats_basic_packed bstats; + struct gnet_stats_queue qstats; +}; + +struct ets_sched { + struct list_head active; + struct tcf_proto __rcu *filter_list; + struct tcf_block *block; + unsigned int nbands; + unsigned int nstrict; + u8 prio2band[TC_PRIO_MAX + 1]; + struct ets_class classes[TCQ_ETS_MAX_BANDS]; +}; + +static const struct nla_policy ets_policy[TCA_ETS_MAX + 1] = { + [TCA_ETS_NBANDS] = { .type = NLA_U8 }, + [TCA_ETS_NSTRICT] = { .type = NLA_U8 }, + [TCA_ETS_QUANTA] = { .type = NLA_NESTED }, + [TCA_ETS_PRIOMAP] = { .type = NLA_NESTED }, +}; + +static const struct nla_policy ets_priomap_policy[TCA_ETS_MAX + 1] = { + [TCA_ETS_PRIOMAP_BAND] = { .type = NLA_U8 }, +}; + +static const struct nla_policy ets_quanta_policy[TCA_ETS_MAX + 1] = { + [TCA_ETS_QUANTA_BAND] = { .type = NLA_U32 }, +}; + +static const struct nla_policy ets_class_policy[TCA_ETS_MAX + 1] = { + [TCA_ETS_QUANTA_BAND] = { .type = NLA_U32 }, +}; + +static int ets_quantum_parse(struct Qdisc *sch, const struct nlattr *attr, + unsigned int *quantum, + struct netlink_ext_ack *extack) +{ + *quantum = nla_get_u32(attr); + if (!*quantum) { + NL_SET_ERR_MSG(extack, "ETS quantum cannot be zero"); + return -EINVAL; + } + return 0; +} + +static struct ets_class * +ets_class_from_arg(struct Qdisc *sch, unsigned long arg) +{ + struct ets_sched *q = qdisc_priv(sch); + + return &q->classes[arg - 1]; +} + +static u32 ets_class_id(struct Qdisc *sch, const struct ets_class *cl) +{ + struct ets_sched *q = qdisc_priv(sch); + int band = cl - q->classes; + + return TC_H_MAKE(sch->handle, band + 1); +} + +static bool ets_class_is_strict(struct ets_sched *q, const struct ets_class *cl) +{ + unsigned int band = cl - q->classes; + + return band < q->nstrict; +} + +static int ets_class_change(struct Qdisc *sch, u32 classid, u32 parentid, + struct nlattr **tca, unsigned long *arg, + struct netlink_ext_ack *extack) +{ + struct ets_class *cl = ets_class_from_arg(sch, *arg); + struct ets_sched *q = qdisc_priv(sch); + struct nlattr *opt = tca[TCA_OPTIONS]; + struct nlattr *tb[TCA_ETS_MAX + 1]; + unsigned int quantum; + int err; + + /* Classes can be added and removed only through Qdisc_ops.change + * interface. + */ + if (!cl) { + NL_SET_ERR_MSG(extack, "Fine-grained class addition and removal is not supported"); + return -EOPNOTSUPP; + } + + if (!opt) { + NL_SET_ERR_MSG(extack, "ETS options are required for this operation"); + return -EINVAL; + } + + err = nla_parse_nested(tb, TCA_ETS_MAX, opt, ets_class_policy, extack); + if (err < 0) + return err; + + if (!tb[TCA_ETS_QUANTA_BAND]) + /* Nothing to configure. */ + return 0; + + if (ets_class_is_strict(q, cl)) { + NL_SET_ERR_MSG(extack, "Strict bands do not have a configurable quantum"); + return -EINVAL; + } + + err = ets_quantum_parse(sch, tb[TCA_ETS_QUANTA_BAND], &quantum, + extack); + if (err) + return err; + + sch_tree_lock(sch); + cl->quantum = quantum; + sch_tree_unlock(sch); + return 0; +} + +static int ets_class_graft(struct Qdisc *sch, unsigned long arg, + struct Qdisc *new, struct Qdisc **old, + struct netlink_ext_ack *extack) +{ + struct ets_class *cl = ets_class_from_arg(sch, arg); + + if (!new) { + new = qdisc_create_dflt(sch->dev_queue, &pfifo_qdisc_ops, + ets_class_id(sch, cl), NULL); + if (!new) + new = &noop_qdisc; + else + qdisc_hash_add(new, true); + } + + *old = qdisc_replace(sch, new, &cl->qdisc); + return 0; +} + +static struct Qdisc *ets_class_leaf(struct Qdisc *sch, unsigned long arg) +{ + struct ets_class *cl = ets_class_from_arg(sch, arg); + + return cl->qdisc; +} + +static unsigned long ets_class_find(struct Qdisc *sch, u32 classid) +{ + unsigned long band = TC_H_MIN(classid); + struct ets_sched *q = qdisc_priv(sch); + + if (band - 1 >= q->nbands) + return 0; + return band; +} + +static void ets_class_qlen_notify(struct Qdisc *sch, unsigned long arg) +{ + struct ets_class *cl = ets_class_from_arg(sch, arg); + struct ets_sched *q = qdisc_priv(sch); + + /* We get notified about zero-length child Qdiscs as well if they are + * offloaded. Those aren't on the active list though, so don't attempt + * to remove them. + */ + if (!ets_class_is_strict(q, cl) && sch->q.qlen) + list_del(&cl->alist); +} + +static int ets_class_dump(struct Qdisc *sch, unsigned long arg, + struct sk_buff *skb, struct tcmsg *tcm) +{ + struct ets_class *cl = ets_class_from_arg(sch, arg); + struct ets_sched *q = qdisc_priv(sch); + struct nlattr *nest; + + tcm->tcm_parent = TC_H_ROOT; + tcm->tcm_handle = ets_class_id(sch, cl); + tcm->tcm_info = cl->qdisc->handle; + + nest = nla_nest_start_noflag(skb, TCA_OPTIONS); + if (!nest) + goto nla_put_failure; + if (!ets_class_is_strict(q, cl)) { + if (nla_put_u32(skb, TCA_ETS_QUANTA_BAND, cl->quantum)) + goto nla_put_failure; + } + return nla_nest_end(skb, nest); + +nla_put_failure: + nla_nest_cancel(skb, nest); + return -EMSGSIZE; +} + +static int ets_class_dump_stats(struct Qdisc *sch, unsigned long arg, + struct gnet_dump *d) +{ + struct ets_class *cl = ets_class_from_arg(sch, arg); + struct Qdisc *cl_q = cl->qdisc; + + if (gnet_stats_copy_basic(qdisc_root_sleeping_running(sch), + d, NULL, &cl_q->bstats) < 0 || + qdisc_qstats_copy(d, cl_q) < 0) + return -1; + + return 0; +} + +static void ets_qdisc_walk(struct Qdisc *sch, struct qdisc_walker *arg) +{ + struct ets_sched *q = qdisc_priv(sch); + int i; + + if (arg->stop) + return; + + for (i = 0; i < q->nbands; i++) { + if (arg->count < arg->skip) { + arg->count++; + continue; + } + if (arg->fn(sch, i + 1, arg) < 0) { + arg->stop = 1; + break; + } + arg->count++; + } +} + +static struct tcf_block * +ets_qdisc_tcf_block(struct Qdisc *sch, unsigned long cl, + struct netlink_ext_ack *extack) +{ + struct ets_sched *q = qdisc_priv(sch); + + if (cl) { + NL_SET_ERR_MSG(extack, "ETS classid must be zero"); + return NULL; + } + + return q->block; +} + +static unsigned long ets_qdisc_bind_tcf(struct Qdisc *sch, unsigned long parent, + u32 classid) +{ + return ets_class_find(sch, classid); +} + +static void ets_qdisc_unbind_tcf(struct Qdisc *sch, unsigned long arg) +{ +} + +static struct ets_class *ets_classify(struct sk_buff *skb, struct Qdisc *sch, + int *qerr) +{ + struct ets_sched *q = qdisc_priv(sch); + u32 band = skb->priority; + struct tcf_result res; + struct tcf_proto *fl; + int err; + + *qerr = NET_XMIT_SUCCESS | __NET_XMIT_BYPASS; + if (TC_H_MAJ(skb->priority) != sch->handle) { + fl = rcu_dereference_bh(q->filter_list); + err = tcf_classify(skb, fl, &res, false); +#ifdef CONFIG_NET_CLS_ACT + switch (err) { + case TC_ACT_STOLEN: + case TC_ACT_QUEUED: + case TC_ACT_TRAP: + *qerr = NET_XMIT_SUCCESS | __NET_XMIT_STOLEN; + /* fall through */ + case TC_ACT_SHOT: + return NULL; + } +#endif + if (!fl || err < 0) { + if (TC_H_MAJ(band)) + band = 0; + return &q->classes[q->prio2band[band & TC_PRIO_MAX]]; + } + band = res.classid; + } + band = TC_H_MIN(band) - 1; + if (band >= q->nbands) + return &q->classes[q->prio2band[0]]; + return &q->classes[band]; +} + +static int ets_qdisc_enqueue(struct sk_buff *skb, struct Qdisc *sch, + struct sk_buff **to_free) +{ + unsigned int len = qdisc_pkt_len(skb); + struct ets_sched *q = qdisc_priv(sch); + struct ets_class *cl; + int err = 0; + bool first; + + cl = ets_classify(skb, sch, &err); + if (!cl) { + if (err & __NET_XMIT_BYPASS) + qdisc_qstats_drop(sch); + __qdisc_drop(skb, to_free); + return err; + } + + first = !cl->qdisc->q.qlen; + err = qdisc_enqueue(skb, cl->qdisc, to_free); + if (unlikely(err != NET_XMIT_SUCCESS)) { + if (net_xmit_drop_count(err)) { + cl->qstats.drops++; + qdisc_qstats_drop(sch); + } + return err; + } + + if (first && !ets_class_is_strict(q, cl)) { + list_add_tail(&cl->alist, &q->active); + cl->deficit = cl->quantum; + } + + sch->qstats.backlog += len; + sch->q.qlen++; + return err; +} + +static struct sk_buff * +ets_qdisc_dequeue_skb(struct Qdisc *sch, struct sk_buff *skb) +{ + qdisc_bstats_update(sch, skb); + qdisc_qstats_backlog_dec(sch, skb); + sch->q.qlen--; + return skb; +} + +static struct sk_buff *ets_qdisc_dequeue(struct Qdisc *sch) +{ + struct ets_sched *q = qdisc_priv(sch); + struct ets_class *cl; + struct sk_buff *skb; + unsigned int band; + unsigned int len; + + while (1) { + for (band = 0; band < q->nstrict; band++) { + cl = &q->classes[band]; + skb = qdisc_dequeue_peeked(cl->qdisc); + if (skb) + return ets_qdisc_dequeue_skb(sch, skb); + } + + if (list_empty(&q->active)) + goto out; + + cl = list_first_entry(&q->active, struct ets_class, alist); + skb = cl->qdisc->ops->peek(cl->qdisc); + if (!skb) { + qdisc_warn_nonwc(__func__, cl->qdisc); + goto out; + } + + len = qdisc_pkt_len(skb); + if (len <= cl->deficit) { + cl->deficit -= len; + skb = qdisc_dequeue_peeked(cl->qdisc); + if (unlikely(!skb)) + goto out; + if (cl->qdisc->q.qlen == 0) + list_del(&cl->alist); + return ets_qdisc_dequeue_skb(sch, skb); + } + + cl->deficit += cl->quantum; + list_move_tail(&cl->alist, &q->active); + } +out: + return NULL; +} + +static int ets_qdisc_priomap_parse(struct nlattr *priomap_attr, + unsigned int nbands, u8 *priomap, + struct netlink_ext_ack *extack) +{ + const struct nlattr *attr; + int prio = 0; + u8 band; + int rem; + int err; + + err = __nla_validate_nested(priomap_attr, TCA_ETS_MAX, + ets_priomap_policy, NL_VALIDATE_STRICT, + extack); + if (err) + return err; + + nla_for_each_nested(attr, priomap_attr, rem) { + switch (nla_type(attr)) { + case TCA_ETS_PRIOMAP_BAND: + if (prio > TC_PRIO_MAX) { + NL_SET_ERR_MSG_MOD(extack, "Too many priorities in ETS priomap"); + return -EINVAL; + } + band = nla_get_u8(attr); + if (band >= nbands) { + NL_SET_ERR_MSG_MOD(extack, "Invalid band number in ETS priomap"); + return -EINVAL; + } + priomap[prio++] = band; + break; + default: + WARN_ON_ONCE(1); /* Validate should have caught this. */ + return -EINVAL; + } + } + + return 0; +} + +static int ets_qdisc_quanta_parse(struct Qdisc *sch, struct nlattr *quanta_attr, + unsigned int nbands, unsigned int nstrict, + unsigned int *quanta, + struct netlink_ext_ack *extack) +{ + const struct nlattr *attr; + int band = nstrict; + int rem; + int err; + + err = __nla_validate_nested(quanta_attr, TCA_ETS_MAX, + ets_quanta_policy, NL_VALIDATE_STRICT, + extack); + if (err < 0) + return err; + + nla_for_each_nested(attr, quanta_attr, rem) { + switch (nla_type(attr)) { + case TCA_ETS_QUANTA_BAND: + if (band >= nbands) { + NL_SET_ERR_MSG_MOD(extack, "ETS quanta has more values than bands"); + return -EINVAL; + } + err = ets_quantum_parse(sch, attr, &quanta[band++], + extack); + if (err) + return err; + break; + default: + WARN_ON_ONCE(1); /* Validate should have caught this. */ + return -EINVAL; + } + } + + return 0; +} + +static int ets_qdisc_change(struct Qdisc *sch, struct nlattr *opt, + struct netlink_ext_ack *extack) +{ + unsigned int quanta[TCQ_ETS_MAX_BANDS] = {0}; + struct Qdisc *queues[TCQ_ETS_MAX_BANDS]; + struct ets_sched *q = qdisc_priv(sch); + struct nlattr *tb[TCA_ETS_MAX + 1]; + unsigned int oldbands = q->nbands; + u8 priomap[TC_PRIO_MAX + 1]; + unsigned int nstrict = 0; + unsigned int nbands; + unsigned int i; + int err; + + if (!opt) { + NL_SET_ERR_MSG(extack, "ETS options are required for this operation"); + return -EINVAL; + } + + err = nla_parse_nested(tb, TCA_ETS_MAX, opt, ets_policy, extack); + if (err < 0) + return err; + + if (!tb[TCA_ETS_NBANDS]) { + NL_SET_ERR_MSG_MOD(extack, "Number of bands is a required argument"); + return -EINVAL; + } + nbands = nla_get_u8(tb[TCA_ETS_NBANDS]); + if (nbands < 1 || nbands > TCQ_ETS_MAX_BANDS) { + NL_SET_ERR_MSG_MOD(extack, "Invalid number of bands"); + return -EINVAL; + } + /* Unless overridden, traffic goes to the last band. */ + memset(priomap, nbands - 1, sizeof(priomap)); + + if (tb[TCA_ETS_NSTRICT]) { + nstrict = nla_get_u8(tb[TCA_ETS_NSTRICT]); + if (nstrict > nbands) { + NL_SET_ERR_MSG_MOD(extack, "Invalid number of strict bands"); + return -EINVAL; + } + } + + if (tb[TCA_ETS_PRIOMAP]) { + err = ets_qdisc_priomap_parse(tb[TCA_ETS_PRIOMAP], + nbands, priomap, extack); + if (err) + return err; + } + + if (tb[TCA_ETS_QUANTA]) { + err = ets_qdisc_quanta_parse(sch, tb[TCA_ETS_QUANTA], + nbands, nstrict, quanta, extack); + if (err) + return err; + } + /* If there are more bands than strict + quanta provided, the remaining + * ones are ETS with quantum of MTU. Initialize the missing values here. + */ + for (i = nstrict; i < nbands; i++) { + if (!quanta[i]) + quanta[i] = psched_mtu(qdisc_dev(sch)); + } + + /* Before commit, make sure we can allocate all new qdiscs */ + for (i = oldbands; i < nbands; i++) { + queues[i] = qdisc_create_dflt(sch->dev_queue, &pfifo_qdisc_ops, + ets_class_id(sch, &q->classes[i]), + extack); + if (!queues[i]) { + while (i > oldbands) + qdisc_put(queues[--i]); + return -ENOMEM; + } + } + + sch_tree_lock(sch); + + q->nbands = nbands; + q->nstrict = nstrict; + memcpy(q->prio2band, priomap, sizeof(priomap)); + + for (i = q->nbands; i < oldbands; i++) + qdisc_tree_flush_backlog(q->classes[i].qdisc); + + for (i = 0; i < q->nbands; i++) + q->classes[i].quantum = quanta[i]; + + for (i = oldbands; i < q->nbands; i++) { + q->classes[i].qdisc = queues[i]; + if (q->classes[i].qdisc != &noop_qdisc) + qdisc_hash_add(q->classes[i].qdisc, true); + } + + sch_tree_unlock(sch); + + for (i = q->nbands; i < oldbands; i++) { + qdisc_put(q->classes[i].qdisc); + memset(&q->classes[i], 0, sizeof(q->classes[i])); + } + return 0; +} + +static int ets_qdisc_init(struct Qdisc *sch, struct nlattr *opt, + struct netlink_ext_ack *extack) +{ + struct ets_sched *q = qdisc_priv(sch); + int err; + + if (!opt) + return -EINVAL; + + err = tcf_block_get(&q->block, &q->filter_list, sch, extack); + if (err) + return err; + + INIT_LIST_HEAD(&q->active); + return ets_qdisc_change(sch, opt, extack); +} + +static void ets_qdisc_reset(struct Qdisc *sch) +{ + struct ets_sched *q = qdisc_priv(sch); + int band; + + for (band = q->nstrict; band < q->nbands; band++) { + if (q->classes[band].qdisc->q.qlen) + list_del(&q->classes[band].alist); + } + for (band = 0; band < q->nbands; band++) + qdisc_reset(q->classes[band].qdisc); + sch->qstats.backlog = 0; + sch->q.qlen = 0; +} + +static void ets_qdisc_destroy(struct Qdisc *sch) +{ + struct ets_sched *q = qdisc_priv(sch); + int band; + + tcf_block_put(q->block); + for (band = 0; band < q->nbands; band++) + qdisc_put(q->classes[band].qdisc); +} + +static int ets_qdisc_dump(struct Qdisc *sch, struct sk_buff *skb) +{ + struct ets_sched *q = qdisc_priv(sch); + struct nlattr *opts; + struct nlattr *nest; + int band; + int prio; + + opts = nla_nest_start_noflag(skb, TCA_OPTIONS); + if (!opts) + goto nla_err; + + if (nla_put_u8(skb, TCA_ETS_NBANDS, q->nbands)) + goto nla_err; + + if (q->nstrict && + nla_put_u8(skb, TCA_ETS_NSTRICT, q->nstrict)) + goto nla_err; + + if (q->nbands > q->nstrict) { + nest = nla_nest_start(skb, TCA_ETS_QUANTA); + if (!nest) + goto nla_err; + + for (band = q->nstrict; band < q->nbands; band++) { + if (nla_put_u32(skb, TCA_ETS_QUANTA_BAND, + q->classes[band].quantum)) + goto nla_err; + } + + nla_nest_end(skb, nest); + } + + nest = nla_nest_start(skb, TCA_ETS_PRIOMAP); + if (!nest) + goto nla_err; + + for (prio = 0; prio <= TC_PRIO_MAX; prio++) { + if (nla_put_u8(skb, TCA_ETS_PRIOMAP_BAND, q->prio2band[prio])) + goto nla_err; + } + + nla_nest_end(skb, nest); + + return nla_nest_end(skb, opts); + +nla_err: + nla_nest_cancel(skb, opts); + return -EMSGSIZE; +} + +static const struct Qdisc_class_ops ets_class_ops = { + .change = ets_class_change, + .graft = ets_class_graft, + .leaf = ets_class_leaf, + .find = ets_class_find, + .qlen_notify = ets_class_qlen_notify, + .dump = ets_class_dump, + .dump_stats = ets_class_dump_stats, + .walk = ets_qdisc_walk, + .tcf_block = ets_qdisc_tcf_block, + .bind_tcf = ets_qdisc_bind_tcf, + .unbind_tcf = ets_qdisc_unbind_tcf, +}; + +static struct Qdisc_ops ets_qdisc_ops __read_mostly = { + .cl_ops = &ets_class_ops, + .id = "ets", + .priv_size = sizeof(struct ets_sched), + .enqueue = ets_qdisc_enqueue, + .dequeue = ets_qdisc_dequeue, + .peek = qdisc_peek_dequeued, + .change = ets_qdisc_change, + .init = ets_qdisc_init, + .reset = ets_qdisc_reset, + .destroy = ets_qdisc_destroy, + .dump = ets_qdisc_dump, + .owner = THIS_MODULE, +}; + +static int __init ets_init(void) +{ + return register_qdisc(&ets_qdisc_ops); +} + +static void __exit ets_exit(void) +{ + unregister_qdisc(&ets_qdisc_ops); +} + +module_init(ets_init); +module_exit(ets_exit); +MODULE_LICENSE("GPL"); -- GitLab From d35eb52bd2ac7557b62bda52668f2e64dde2cf90 Mon Sep 17 00:00:00 2001 From: Petr Machata Date: Wed, 18 Dec 2019 14:55:15 +0000 Subject: [PATCH 1023/1437] net: sch_ets: Make the ETS qdisc offloadable Add hooks at appropriate points to make it possible to offload the ETS Qdisc. Signed-off-by: Petr Machata Acked-by: Jiri Pirko Reviewed-by: Ido Schimmel Signed-off-by: David S. Miller --- include/linux/netdevice.h | 1 + include/net/pkt_cls.h | 31 +++++++++++++ net/sched/sch_ets.c | 95 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 127 insertions(+) diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 30745068fb39d..7a8ed11f5d45a 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -849,6 +849,7 @@ enum tc_setup_type { TC_SETUP_QDISC_GRED, TC_SETUP_QDISC_TAPRIO, TC_SETUP_FT, + TC_SETUP_QDISC_ETS, }; /* These structures hold the attributes of bpf state that are being passed diff --git a/include/net/pkt_cls.h b/include/net/pkt_cls.h index a7c5d492bc04a..47b115e2012a3 100644 --- a/include/net/pkt_cls.h +++ b/include/net/pkt_cls.h @@ -823,4 +823,35 @@ struct tc_root_qopt_offload { bool ingress; }; +enum tc_ets_command { + TC_ETS_REPLACE, + TC_ETS_DESTROY, + TC_ETS_STATS, + TC_ETS_GRAFT, +}; + +struct tc_ets_qopt_offload_replace_params { + unsigned int bands; + u8 priomap[TC_PRIO_MAX + 1]; + unsigned int quanta[TCQ_ETS_MAX_BANDS]; /* 0 for strict bands. */ + unsigned int weights[TCQ_ETS_MAX_BANDS]; + struct gnet_stats_queue *qstats; +}; + +struct tc_ets_qopt_offload_graft_params { + u8 band; + u32 child_handle; +}; + +struct tc_ets_qopt_offload { + enum tc_ets_command command; + u32 handle; + u32 parent; + union { + struct tc_ets_qopt_offload_replace_params replace_params; + struct tc_qopt_offload_stats stats; + struct tc_ets_qopt_offload_graft_params graft_params; + }; +}; + #endif diff --git a/net/sched/sch_ets.c b/net/sched/sch_ets.c index e6194b23e9b0d..a87e9159338c4 100644 --- a/net/sched/sch_ets.c +++ b/net/sched/sch_ets.c @@ -102,6 +102,91 @@ static u32 ets_class_id(struct Qdisc *sch, const struct ets_class *cl) return TC_H_MAKE(sch->handle, band + 1); } +static void ets_offload_change(struct Qdisc *sch) +{ + struct net_device *dev = qdisc_dev(sch); + struct ets_sched *q = qdisc_priv(sch); + struct tc_ets_qopt_offload qopt; + unsigned int w_psum_prev = 0; + unsigned int q_psum = 0; + unsigned int q_sum = 0; + unsigned int quantum; + unsigned int w_psum; + unsigned int weight; + unsigned int i; + + if (!tc_can_offload(dev) || !dev->netdev_ops->ndo_setup_tc) + return; + + qopt.command = TC_ETS_REPLACE; + qopt.handle = sch->handle; + qopt.parent = sch->parent; + qopt.replace_params.bands = q->nbands; + qopt.replace_params.qstats = &sch->qstats; + memcpy(&qopt.replace_params.priomap, + q->prio2band, sizeof(q->prio2band)); + + for (i = 0; i < q->nbands; i++) + q_sum += q->classes[i].quantum; + + for (i = 0; i < q->nbands; i++) { + quantum = q->classes[i].quantum; + q_psum += quantum; + w_psum = quantum ? q_psum * 100 / q_sum : 0; + weight = w_psum - w_psum_prev; + w_psum_prev = w_psum; + + qopt.replace_params.quanta[i] = quantum; + qopt.replace_params.weights[i] = weight; + } + + dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_QDISC_ETS, &qopt); +} + +static void ets_offload_destroy(struct Qdisc *sch) +{ + struct net_device *dev = qdisc_dev(sch); + struct tc_ets_qopt_offload qopt; + + if (!tc_can_offload(dev) || !dev->netdev_ops->ndo_setup_tc) + return; + + qopt.command = TC_ETS_DESTROY; + qopt.handle = sch->handle; + qopt.parent = sch->parent; + dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_QDISC_ETS, &qopt); +} + +static void ets_offload_graft(struct Qdisc *sch, struct Qdisc *new, + struct Qdisc *old, unsigned long arg, + struct netlink_ext_ack *extack) +{ + struct net_device *dev = qdisc_dev(sch); + struct tc_ets_qopt_offload qopt; + + qopt.command = TC_ETS_GRAFT; + qopt.handle = sch->handle; + qopt.parent = sch->parent; + qopt.graft_params.band = arg - 1; + qopt.graft_params.child_handle = new->handle; + + qdisc_offload_graft_helper(dev, sch, new, old, TC_SETUP_QDISC_ETS, + &qopt, extack); +} + +static int ets_offload_dump(struct Qdisc *sch) +{ + struct tc_ets_qopt_offload qopt; + + qopt.command = TC_ETS_STATS; + qopt.handle = sch->handle; + qopt.parent = sch->parent; + qopt.stats.bstats = &sch->bstats; + qopt.stats.qstats = &sch->qstats; + + return qdisc_offload_dump_helper(sch, TC_SETUP_QDISC_ETS, &qopt); +} + static bool ets_class_is_strict(struct ets_sched *q, const struct ets_class *cl) { unsigned int band = cl - q->classes; @@ -154,6 +239,8 @@ static int ets_class_change(struct Qdisc *sch, u32 classid, u32 parentid, sch_tree_lock(sch); cl->quantum = quantum; sch_tree_unlock(sch); + + ets_offload_change(sch); return 0; } @@ -173,6 +260,7 @@ static int ets_class_graft(struct Qdisc *sch, unsigned long arg, } *old = qdisc_replace(sch, new, &cl->qdisc); + ets_offload_graft(sch, new, *old, arg, extack); return 0; } @@ -589,6 +677,7 @@ static int ets_qdisc_change(struct Qdisc *sch, struct nlattr *opt, sch_tree_unlock(sch); + ets_offload_change(sch); for (i = q->nbands; i < oldbands; i++) { qdisc_put(q->classes[i].qdisc); memset(&q->classes[i], 0, sizeof(q->classes[i])); @@ -633,6 +722,7 @@ static void ets_qdisc_destroy(struct Qdisc *sch) struct ets_sched *q = qdisc_priv(sch); int band; + ets_offload_destroy(sch); tcf_block_put(q->block); for (band = 0; band < q->nbands; band++) qdisc_put(q->classes[band].qdisc); @@ -645,6 +735,11 @@ static int ets_qdisc_dump(struct Qdisc *sch, struct sk_buff *skb) struct nlattr *nest; int band; int prio; + int err; + + err = ets_offload_dump(sch); + if (err) + return err; opts = nla_nest_start_noflag(skb, TCA_OPTIONS); if (!opts) -- GitLab From 7917f52ae188b87de627a892b57646abfe5a82bc Mon Sep 17 00:00:00 2001 From: Petr Machata Date: Wed, 18 Dec 2019 14:55:17 +0000 Subject: [PATCH 1024/1437] mlxsw: spectrum_qdisc: Generalize PRIO offload to support ETS Thanks to the similarity between PRIO and ETS it is possible to simply reuse most of the code for offloading PRIO Qdisc. Extract the common functionality into separate functions, making the current PRIO handlers thin API adapters. Extend the new functions to pass quanta for individual bands, which allows configuring a subset of bands as WRR. Invoke mlxsw_sp_port_ets_set() as appropriate to de/configure WRR-ness and weight of individual bands. Signed-off-by: Petr Machata Acked-by: Jiri Pirko Reviewed-by: Ido Schimmel Signed-off-by: David S. Miller --- .../ethernet/mellanox/mlxsw/spectrum_qdisc.c | 104 ++++++++++++++---- 1 file changed, 81 insertions(+), 23 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_qdisc.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_qdisc.c index 135fef6c54b16..d513af49c0a89 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_qdisc.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_qdisc.c @@ -471,14 +471,16 @@ int mlxsw_sp_setup_tc_red(struct mlxsw_sp_port *mlxsw_sp_port, } static int -mlxsw_sp_qdisc_prio_destroy(struct mlxsw_sp_port *mlxsw_sp_port, - struct mlxsw_sp_qdisc *mlxsw_sp_qdisc) +__mlxsw_sp_qdisc_ets_destroy(struct mlxsw_sp_port *mlxsw_sp_port) { int i; for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) { mlxsw_sp_port_prio_tc_set(mlxsw_sp_port, i, MLXSW_SP_PORT_DEFAULT_TCLASS); + mlxsw_sp_port_ets_set(mlxsw_sp_port, + MLXSW_REG_QEEC_HR_SUBGROUP, + i, 0, false, 0); mlxsw_sp_qdisc_destroy(mlxsw_sp_port, &mlxsw_sp_port->tclass_qdiscs[i]); mlxsw_sp_port->tclass_qdiscs[i].prio_bitmap = 0; @@ -488,36 +490,58 @@ mlxsw_sp_qdisc_prio_destroy(struct mlxsw_sp_port *mlxsw_sp_port, } static int -mlxsw_sp_qdisc_prio_check_params(struct mlxsw_sp_port *mlxsw_sp_port, - struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, - void *params) +mlxsw_sp_qdisc_prio_destroy(struct mlxsw_sp_port *mlxsw_sp_port, + struct mlxsw_sp_qdisc *mlxsw_sp_qdisc) { - struct tc_prio_qopt_offload_params *p = params; + return __mlxsw_sp_qdisc_ets_destroy(mlxsw_sp_port); +} - if (p->bands > IEEE_8021QAZ_MAX_TCS) +static int +__mlxsw_sp_qdisc_ets_check_params(unsigned int nbands) +{ + if (nbands > IEEE_8021QAZ_MAX_TCS) return -EOPNOTSUPP; return 0; } static int -mlxsw_sp_qdisc_prio_replace(struct mlxsw_sp_port *mlxsw_sp_port, - struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, - void *params) +mlxsw_sp_qdisc_prio_check_params(struct mlxsw_sp_port *mlxsw_sp_port, + struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, + void *params) { struct tc_prio_qopt_offload_params *p = params; + + return __mlxsw_sp_qdisc_ets_check_params(p->bands); +} + +static int +__mlxsw_sp_qdisc_ets_replace(struct mlxsw_sp_port *mlxsw_sp_port, + unsigned int nbands, + const unsigned int *quanta, + const unsigned int *weights, + const u8 *priomap) +{ struct mlxsw_sp_qdisc *child_qdisc; int tclass, i, band, backlog; u8 old_priomap; int err; - for (band = 0; band < p->bands; band++) { + for (band = 0; band < nbands; band++) { tclass = MLXSW_SP_PRIO_BAND_TO_TCLASS(band); child_qdisc = &mlxsw_sp_port->tclass_qdiscs[tclass]; old_priomap = child_qdisc->prio_bitmap; child_qdisc->prio_bitmap = 0; + + err = mlxsw_sp_port_ets_set(mlxsw_sp_port, + MLXSW_REG_QEEC_HR_SUBGROUP, + tclass, 0, !!quanta[band], + weights[band]); + if (err) + return err; + for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) { - if (p->priomap[i] == band) { + if (priomap[i] == band) { child_qdisc->prio_bitmap |= BIT(i); if (BIT(i) & old_priomap) continue; @@ -540,21 +564,46 @@ mlxsw_sp_qdisc_prio_replace(struct mlxsw_sp_port *mlxsw_sp_port, child_qdisc = &mlxsw_sp_port->tclass_qdiscs[tclass]; child_qdisc->prio_bitmap = 0; mlxsw_sp_qdisc_destroy(mlxsw_sp_port, child_qdisc); + mlxsw_sp_port_ets_set(mlxsw_sp_port, + MLXSW_REG_QEEC_HR_SUBGROUP, + tclass, 0, false, 0); } return 0; } +static int +mlxsw_sp_qdisc_prio_replace(struct mlxsw_sp_port *mlxsw_sp_port, + struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, + void *params) +{ + struct tc_prio_qopt_offload_params *p = params; + unsigned int zeroes[TCQ_ETS_MAX_BANDS] = {0}; + + return __mlxsw_sp_qdisc_ets_replace(mlxsw_sp_port, p->bands, + zeroes, zeroes, p->priomap); +} + +static void +__mlxsw_sp_qdisc_ets_unoffload(struct mlxsw_sp_port *mlxsw_sp_port, + struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, + struct gnet_stats_queue *qstats) +{ + u64 backlog; + + backlog = mlxsw_sp_cells_bytes(mlxsw_sp_port->mlxsw_sp, + mlxsw_sp_qdisc->stats_base.backlog); + qstats->backlog -= backlog; +} + static void mlxsw_sp_qdisc_prio_unoffload(struct mlxsw_sp_port *mlxsw_sp_port, struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, void *params) { struct tc_prio_qopt_offload_params *p = params; - u64 backlog; - backlog = mlxsw_sp_cells_bytes(mlxsw_sp_port->mlxsw_sp, - mlxsw_sp_qdisc->stats_base.backlog); - p->qstats->backlog -= backlog; + __mlxsw_sp_qdisc_ets_unoffload(mlxsw_sp_port, mlxsw_sp_qdisc, + p->qstats); } static int @@ -657,22 +706,22 @@ static struct mlxsw_sp_qdisc_ops mlxsw_sp_qdisc_ops_prio = { * unoffload the child. */ static int -mlxsw_sp_qdisc_prio_graft(struct mlxsw_sp_port *mlxsw_sp_port, - struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, - struct tc_prio_qopt_offload_graft_params *p) +__mlxsw_sp_qdisc_ets_graft(struct mlxsw_sp_port *mlxsw_sp_port, + struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, + u8 band, u32 child_handle) { - int tclass_num = MLXSW_SP_PRIO_BAND_TO_TCLASS(p->band); + int tclass_num = MLXSW_SP_PRIO_BAND_TO_TCLASS(band); struct mlxsw_sp_qdisc *old_qdisc; - if (p->band < IEEE_8021QAZ_MAX_TCS && - mlxsw_sp_port->tclass_qdiscs[tclass_num].handle == p->child_handle) + if (band < IEEE_8021QAZ_MAX_TCS && + mlxsw_sp_port->tclass_qdiscs[tclass_num].handle == child_handle) return 0; /* See if the grafted qdisc is already offloaded on any tclass. If so, * unoffload it. */ old_qdisc = mlxsw_sp_qdisc_find_by_handle(mlxsw_sp_port, - p->child_handle); + child_handle); if (old_qdisc) mlxsw_sp_qdisc_destroy(mlxsw_sp_port, old_qdisc); @@ -681,6 +730,15 @@ mlxsw_sp_qdisc_prio_graft(struct mlxsw_sp_port *mlxsw_sp_port, return -EOPNOTSUPP; } +static int +mlxsw_sp_qdisc_prio_graft(struct mlxsw_sp_port *mlxsw_sp_port, + struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, + struct tc_prio_qopt_offload_graft_params *p) +{ + return __mlxsw_sp_qdisc_ets_graft(mlxsw_sp_port, mlxsw_sp_qdisc, + p->band, p->child_handle); +} + int mlxsw_sp_setup_tc_prio(struct mlxsw_sp_port *mlxsw_sp_port, struct tc_prio_qopt_offload *p) { -- GitLab From 19f405b988e7bdee75e4936d27d2909c11574a7c Mon Sep 17 00:00:00 2001 From: Petr Machata Date: Wed, 18 Dec 2019 14:55:19 +0000 Subject: [PATCH 1025/1437] mlxsw: spectrum_qdisc: Support offloading of ETS Qdisc Handle TC_SETUP_QDISC_ETS, add a new ops structure for the ETS Qdisc. Invoke the extended prio handlers implemented in the previous patch. For stats ops, invoke directly the prio callbacks, which are not sensitive to differences between PRIO and ETS. Signed-off-by: Petr Machata Acked-by: Jiri Pirko Reviewed-by: Ido Schimmel Signed-off-by: David S. Miller --- .../net/ethernet/mellanox/mlxsw/spectrum.c | 2 + .../net/ethernet/mellanox/mlxsw/spectrum.h | 2 + .../ethernet/mellanox/mlxsw/spectrum_qdisc.c | 84 +++++++++++++++++++ 3 files changed, 88 insertions(+) diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c index 0d8fce749248f..ea632042e6096 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c @@ -1796,6 +1796,8 @@ static int mlxsw_sp_setup_tc(struct net_device *dev, enum tc_setup_type type, return mlxsw_sp_setup_tc_red(mlxsw_sp_port, type_data); case TC_SETUP_QDISC_PRIO: return mlxsw_sp_setup_tc_prio(mlxsw_sp_port, type_data); + case TC_SETUP_QDISC_ETS: + return mlxsw_sp_setup_tc_ets(mlxsw_sp_port, type_data); default: return -EOPNOTSUPP; } diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.h b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h index 347bec9d1ecf5..948ef4720d402 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.h +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h @@ -852,6 +852,8 @@ int mlxsw_sp_setup_tc_red(struct mlxsw_sp_port *mlxsw_sp_port, struct tc_red_qopt_offload *p); int mlxsw_sp_setup_tc_prio(struct mlxsw_sp_port *mlxsw_sp_port, struct tc_prio_qopt_offload *p); +int mlxsw_sp_setup_tc_ets(struct mlxsw_sp_port *mlxsw_sp_port, + struct tc_ets_qopt_offload *p); /* spectrum_fid.c */ bool mlxsw_sp_fid_is_dummy(struct mlxsw_sp *mlxsw_sp, u16 fid_index); diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_qdisc.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_qdisc.c index d513af49c0a89..81a2c087f534d 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_qdisc.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_qdisc.c @@ -18,6 +18,7 @@ enum mlxsw_sp_qdisc_type { MLXSW_SP_QDISC_NO_QDISC, MLXSW_SP_QDISC_RED, MLXSW_SP_QDISC_PRIO, + MLXSW_SP_QDISC_ETS, }; struct mlxsw_sp_qdisc_ops { @@ -680,6 +681,55 @@ static struct mlxsw_sp_qdisc_ops mlxsw_sp_qdisc_ops_prio = { .clean_stats = mlxsw_sp_setup_tc_qdisc_prio_clean_stats, }; +static int +mlxsw_sp_qdisc_ets_check_params(struct mlxsw_sp_port *mlxsw_sp_port, + struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, + void *params) +{ + struct tc_ets_qopt_offload_replace_params *p = params; + + return __mlxsw_sp_qdisc_ets_check_params(p->bands); +} + +static int +mlxsw_sp_qdisc_ets_replace(struct mlxsw_sp_port *mlxsw_sp_port, + struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, + void *params) +{ + struct tc_ets_qopt_offload_replace_params *p = params; + + return __mlxsw_sp_qdisc_ets_replace(mlxsw_sp_port, p->bands, + p->quanta, p->weights, p->priomap); +} + +static void +mlxsw_sp_qdisc_ets_unoffload(struct mlxsw_sp_port *mlxsw_sp_port, + struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, + void *params) +{ + struct tc_ets_qopt_offload_replace_params *p = params; + + __mlxsw_sp_qdisc_ets_unoffload(mlxsw_sp_port, mlxsw_sp_qdisc, + p->qstats); +} + +static int +mlxsw_sp_qdisc_ets_destroy(struct mlxsw_sp_port *mlxsw_sp_port, + struct mlxsw_sp_qdisc *mlxsw_sp_qdisc) +{ + return __mlxsw_sp_qdisc_ets_destroy(mlxsw_sp_port); +} + +static struct mlxsw_sp_qdisc_ops mlxsw_sp_qdisc_ops_ets = { + .type = MLXSW_SP_QDISC_ETS, + .check_params = mlxsw_sp_qdisc_ets_check_params, + .replace = mlxsw_sp_qdisc_ets_replace, + .unoffload = mlxsw_sp_qdisc_ets_unoffload, + .destroy = mlxsw_sp_qdisc_ets_destroy, + .get_stats = mlxsw_sp_qdisc_get_prio_stats, + .clean_stats = mlxsw_sp_setup_tc_qdisc_prio_clean_stats, +}; + /* Linux allows linking of Qdiscs to arbitrary classes (so long as the resulting * graph is free of cycles). These operations do not change the parent handle * though, which means it can be incomplete (if there is more than one class @@ -772,6 +822,40 @@ int mlxsw_sp_setup_tc_prio(struct mlxsw_sp_port *mlxsw_sp_port, } } +int mlxsw_sp_setup_tc_ets(struct mlxsw_sp_port *mlxsw_sp_port, + struct tc_ets_qopt_offload *p) +{ + struct mlxsw_sp_qdisc *mlxsw_sp_qdisc; + + mlxsw_sp_qdisc = mlxsw_sp_qdisc_find(mlxsw_sp_port, p->parent, true); + if (!mlxsw_sp_qdisc) + return -EOPNOTSUPP; + + if (p->command == TC_ETS_REPLACE) + return mlxsw_sp_qdisc_replace(mlxsw_sp_port, p->handle, + mlxsw_sp_qdisc, + &mlxsw_sp_qdisc_ops_ets, + &p->replace_params); + + if (!mlxsw_sp_qdisc_compare(mlxsw_sp_qdisc, p->handle, + MLXSW_SP_QDISC_ETS)) + return -EOPNOTSUPP; + + switch (p->command) { + case TC_ETS_DESTROY: + return mlxsw_sp_qdisc_destroy(mlxsw_sp_port, mlxsw_sp_qdisc); + case TC_ETS_STATS: + return mlxsw_sp_qdisc_get_stats(mlxsw_sp_port, mlxsw_sp_qdisc, + &p->stats); + case TC_ETS_GRAFT: + return __mlxsw_sp_qdisc_ets_graft(mlxsw_sp_port, mlxsw_sp_qdisc, + p->graft_params.band, + p->graft_params.child_handle); + default: + return -EOPNOTSUPP; + } +} + int mlxsw_sp_tc_qdisc_init(struct mlxsw_sp_port *mlxsw_sp_port) { struct mlxsw_sp_qdisc *mlxsw_sp_qdisc; -- GitLab From 4cf9b8f9929285b91f5f22652ad5175a2206303b Mon Sep 17 00:00:00 2001 From: Petr Machata Date: Wed, 18 Dec 2019 14:55:21 +0000 Subject: [PATCH 1026/1437] selftests: forwarding: Move start_/stop_traffic from mlxsw to lib.sh These two functions are used for starting several streams of traffic, and then stopping them later. They will be handy for the test coverage of ETS Qdisc. Move them from mlxsw-specific qos_lib.sh to the generic lib.sh. Signed-off-by: Petr Machata Reviewed-by: Ido Schimmel Acked-by: Jiri Pirko Signed-off-by: David S. Miller --- .../selftests/drivers/net/mlxsw/qos_lib.sh | 18 ------------------ tools/testing/selftests/net/forwarding/lib.sh | 18 ++++++++++++++++++ 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/tools/testing/selftests/drivers/net/mlxsw/qos_lib.sh b/tools/testing/selftests/drivers/net/mlxsw/qos_lib.sh index e80be65799adf..75a3fb3b56635 100644 --- a/tools/testing/selftests/drivers/net/mlxsw/qos_lib.sh +++ b/tools/testing/selftests/drivers/net/mlxsw/qos_lib.sh @@ -24,24 +24,6 @@ rate() echo $((8 * (t1 - t0) / interval)) } -start_traffic() -{ - local h_in=$1; shift # Where the traffic egresses the host - local sip=$1; shift - local dip=$1; shift - local dmac=$1; shift - - $MZ $h_in -p 8000 -A $sip -B $dip -c 0 \ - -a own -b $dmac -t udp -q & - sleep 1 -} - -stop_traffic() -{ - # Suppress noise from killing mausezahn. - { kill %% && wait %%; } 2>/dev/null -} - check_rate() { local rate=$1; shift diff --git a/tools/testing/selftests/net/forwarding/lib.sh b/tools/testing/selftests/net/forwarding/lib.sh index 1f64e7348f699..a0b09bb6995ed 100644 --- a/tools/testing/selftests/net/forwarding/lib.sh +++ b/tools/testing/selftests/net/forwarding/lib.sh @@ -1065,3 +1065,21 @@ flood_test() flood_unicast_test $br_port $host1_if $host2_if flood_multicast_test $br_port $host1_if $host2_if } + +start_traffic() +{ + local h_in=$1; shift # Where the traffic egresses the host + local sip=$1; shift + local dip=$1; shift + local dmac=$1; shift + + $MZ $h_in -p 8000 -A $sip -B $dip -c 0 \ + -a own -b $dmac -t udp -q & + sleep 1 +} + +stop_traffic() +{ + # Suppress noise from killing mausezahn. + { kill %% && wait %%; } 2>/dev/null +} -- GitLab From ddd3fd750ffee2c562fa88ea6ed1f90a02818303 Mon Sep 17 00:00:00 2001 From: Petr Machata Date: Wed, 18 Dec 2019 14:55:22 +0000 Subject: [PATCH 1027/1437] selftests: forwarding: sch_ets: Add test coverage for ETS Qdisc This tests the newly-added ETS Qdisc. It runs two to three streams of traffic, each with a different priority. ETS Qdisc is supposed to allocate bandwidth according to the DRR algorithm and given weights. After running the traffic for a while, counters are compared for each stream to check that the expected ratio is in fact observed. In order for the DRR process to kick in, a traffic bottleneck must exist in the first place. In slow path, such bottleneck can be implemented by wrapping the ETS Qdisc inside a TBF or other shaper. This might however make the configuration unoffloadable. Instead, on HW datapath, the bottleneck would be set up by lowering port speed and configuring shared buffer suitably. Therefore the test is structured as a core component that implements the testing, with two wrapper scripts that implement the details of slow path resp. fast path configuration. Signed-off-by: Petr Machata Reviewed-by: Ido Schimmel Acked-by: Jiri Pirko Signed-off-by: David S. Miller --- .../selftests/drivers/net/mlxsw/qos_lib.sh | 28 ++ .../selftests/drivers/net/mlxsw/sch_ets.sh | 67 ++++ .../selftests/net/forwarding/sch_ets.sh | 44 +++ .../selftests/net/forwarding/sch_ets_core.sh | 300 ++++++++++++++++++ .../selftests/net/forwarding/sch_ets_tests.sh | 227 +++++++++++++ 5 files changed, 666 insertions(+) create mode 100755 tools/testing/selftests/drivers/net/mlxsw/sch_ets.sh create mode 100755 tools/testing/selftests/net/forwarding/sch_ets.sh create mode 100644 tools/testing/selftests/net/forwarding/sch_ets_core.sh create mode 100644 tools/testing/selftests/net/forwarding/sch_ets_tests.sh diff --git a/tools/testing/selftests/drivers/net/mlxsw/qos_lib.sh b/tools/testing/selftests/drivers/net/mlxsw/qos_lib.sh index 75a3fb3b56635..a5937069ac16b 100644 --- a/tools/testing/selftests/drivers/net/mlxsw/qos_lib.sh +++ b/tools/testing/selftests/drivers/net/mlxsw/qos_lib.sh @@ -78,3 +78,31 @@ measure_rate() echo $ir $er return $ret } + +bail_on_lldpad() +{ + if systemctl is-active --quiet lldpad; then + + cat >/dev/stderr <<-EOF + WARNING: lldpad is running + + lldpad will likely configure DCB, and this test will + configure Qdiscs. mlxsw does not support both at the + same time, one of them is arbitrarily going to overwrite + the other. That will cause spurious failures (or, + unlikely, passes) of this test. + EOF + + if [[ -z $ALLOW_LLDPAD ]]; then + cat >/dev/stderr <<-EOF + + If you want to run the test anyway, please set + an environment variable ALLOW_LLDPAD to a + non-empty string. + EOF + exit 1 + else + return + fi + fi +} diff --git a/tools/testing/selftests/drivers/net/mlxsw/sch_ets.sh b/tools/testing/selftests/drivers/net/mlxsw/sch_ets.sh new file mode 100755 index 0000000000000..c9fc4d4885c17 --- /dev/null +++ b/tools/testing/selftests/drivers/net/mlxsw/sch_ets.sh @@ -0,0 +1,67 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0 + +# A driver for the ETS selftest that implements testing in offloaded datapath. +lib_dir=$(dirname $0)/../../../net/forwarding +source $lib_dir/sch_ets_core.sh +source $lib_dir/devlink_lib.sh +source qos_lib.sh + +ALL_TESTS=" + ping_ipv4 + priomap_mode + ets_test_strict + ets_test_mixed + ets_test_dwrr +" + +switch_create() +{ + ets_switch_create + + # Create a bottleneck so that the DWRR process can kick in. + ethtool -s $h2 speed 1000 autoneg off + ethtool -s $swp2 speed 1000 autoneg off + + # Set the ingress quota high and use the three egress TCs to limit the + # amount of traffic that is admitted to the shared buffers. This makes + # sure that there is always enough traffic of all types to select from + # for the DWRR process. + devlink_port_pool_th_set $swp1 0 12 + devlink_tc_bind_pool_th_set $swp1 0 ingress 0 12 + devlink_port_pool_th_set $swp2 4 12 + devlink_tc_bind_pool_th_set $swp2 7 egress 4 5 + devlink_tc_bind_pool_th_set $swp2 6 egress 4 5 + devlink_tc_bind_pool_th_set $swp2 5 egress 4 5 + + # Note: sch_ets_core.sh uses VLAN ingress-qos-map to assign packet + # priorities at $swp1 based on their 802.1p headers. ingress-qos-map is + # not offloaded by mlxsw as of this writing, but the mapping used is + # 1:1, which is the mapping currently hard-coded by the driver. +} + +switch_destroy() +{ + devlink_tc_bind_pool_th_restore $swp2 5 egress + devlink_tc_bind_pool_th_restore $swp2 6 egress + devlink_tc_bind_pool_th_restore $swp2 7 egress + devlink_port_pool_th_restore $swp2 4 + devlink_tc_bind_pool_th_restore $swp1 0 ingress + devlink_port_pool_th_restore $swp1 0 + + ethtool -s $swp2 autoneg on + ethtool -s $h2 autoneg on + + ets_switch_destroy +} + +# Callback from sch_ets_tests.sh +get_stats() +{ + local band=$1; shift + + ethtool_stats_get "$h2" rx_octets_prio_$band +} + +bail_on_lldpad +ets_run diff --git a/tools/testing/selftests/net/forwarding/sch_ets.sh b/tools/testing/selftests/net/forwarding/sch_ets.sh new file mode 100755 index 0000000000000..40e0ad1bc4f25 --- /dev/null +++ b/tools/testing/selftests/net/forwarding/sch_ets.sh @@ -0,0 +1,44 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0 + +# A driver for the ETS selftest that implements testing in slowpath. +lib_dir=. +source sch_ets_core.sh + +ALL_TESTS=" + ping_ipv4 + priomap_mode + ets_test_strict + ets_test_mixed + ets_test_dwrr + classifier_mode + ets_test_strict + ets_test_mixed + ets_test_dwrr +" + +switch_create() +{ + ets_switch_create + + # Create a bottleneck so that the DWRR process can kick in. + tc qdisc add dev $swp2 root handle 1: tbf \ + rate 1Gbit burst 1Mbit latency 100ms + PARENT="parent 1:" +} + +switch_destroy() +{ + ets_switch_destroy + tc qdisc del dev $swp2 root +} + +# Callback from sch_ets_tests.sh +get_stats() +{ + local stream=$1; shift + + link_stats_get $h2.1$stream rx bytes +} + +ets_run diff --git a/tools/testing/selftests/net/forwarding/sch_ets_core.sh b/tools/testing/selftests/net/forwarding/sch_ets_core.sh new file mode 100644 index 0000000000000..f906fcc665727 --- /dev/null +++ b/tools/testing/selftests/net/forwarding/sch_ets_core.sh @@ -0,0 +1,300 @@ +# SPDX-License-Identifier: GPL-2.0 + +# This is a template for ETS Qdisc test. +# +# This test sends from H1 several traffic streams with 802.1p-tagged packets. +# The tags are used at $swp1 to prioritize the traffic. Each stream is then +# queued at a different ETS band according to the assigned priority. After +# runnig for a while, counters at H2 are consulted to determine whether the +# traffic scheduling was according to the ETS configuration. +# +# This template is supposed to be embedded by a test driver, which implements +# statistics collection, any HW-specific stuff, and prominently configures the +# system to assure that there is overcommitment at $swp2. That is necessary so +# that the ETS traffic selection algorithm kicks in and has to schedule some +# traffic at the expense of other. +# +# A driver for veth-based testing is in sch_ets.sh, an example of a driver for +# an offloaded data path is in selftests/drivers/net/mlxsw/sch_ets.sh. +# +# +---------------------------------------------------------------------+ +# | H1 | +# | + $h1.10 + $h1.11 + $h1.12 | +# | | 192.0.2.1/28 | 192.0.2.17/28 | 192.0.2.33/28 | +# | | egress-qos-map | egress-qos-map | egress-qos-map | +# | | 0:0 | 0:1 | 0:2 | +# | \____________________ | ____________________/ | +# | \|/ | +# | + $h1 | +# +---------------------------|-----------------------------------------+ +# | +# +---------------------------|-----------------------------------------+ +# | SW + $swp1 | +# | | >1Gbps | +# | ____________________/|\____________________ | +# | / | \ | +# | +--|----------------+ +--|----------------+ +--|----------------+ | +# | | + $swp1.10 | | + $swp1.11 | | + $swp1.12 | | +# | | ingress-qos-map| | ingress-qos-map| | ingress-qos-map| | +# | | 0:0 1:1 2:2 | | 0:0 1:1 2:2 | | 0:0 1:1 2:2 | | +# | | | | | | | | +# | | BR10 | | BR11 | | BR12 | | +# | | | | | | | | +# | | + $swp2.10 | | + $swp2.11 | | + $swp2.12 | | +# | +--|----------------+ +--|----------------+ +--|----------------+ | +# | \____________________ | ____________________/ | +# | \|/ | +# | + $swp2 | +# | | 1Gbps (ethtool or HTB qdisc) | +# | | qdisc ets quanta $W0 $W1 $W2 | +# | | priomap 0 1 2 | +# +---------------------------|-----------------------------------------+ +# | +# +---------------------------|-----------------------------------------+ +# | H2 + $h2 | +# | ____________________/|\____________________ | +# | / | \ | +# | + $h2.10 + $h2.11 + $h2.12 | +# | 192.0.2.2/28 192.0.2.18/28 192.0.2.34/28 | +# +---------------------------------------------------------------------+ + +NUM_NETIFS=4 +CHECK_TC=yes +source $lib_dir/lib.sh +source $lib_dir/sch_ets_tests.sh + +PARENT=root +QDISC_DEV= + +sip() +{ + echo 192.0.2.$((16 * $1 + 1)) +} + +dip() +{ + echo 192.0.2.$((16 * $1 + 2)) +} + +# Callback from sch_ets_tests.sh +ets_start_traffic() +{ + local dst_mac=$(mac_get $h2) + local i=$1; shift + + start_traffic $h1.1$i $(sip $i) $(dip $i) $dst_mac +} + +ETS_CHANGE_QDISC= + +priomap_mode() +{ + echo "Running in priomap mode" + ets_delete_qdisc + ETS_CHANGE_QDISC=ets_change_qdisc_priomap +} + +classifier_mode() +{ + echo "Running in classifier mode" + ets_delete_qdisc + ETS_CHANGE_QDISC=ets_change_qdisc_classifier +} + +ets_change_qdisc_priomap() +{ + local dev=$1; shift + local nstrict=$1; shift + local priomap=$1; shift + local quanta=("${@}") + + local op=$(if [[ -n $QDISC_DEV ]]; then echo change; else echo add; fi) + + tc qdisc $op dev $dev $PARENT handle 10: ets \ + $(if ((nstrict)); then echo strict $nstrict; fi) \ + $(if ((${#quanta[@]})); then echo quanta ${quanta[@]}; fi) \ + priomap $priomap + QDISC_DEV=$dev +} + +ets_change_qdisc_classifier() +{ + local dev=$1; shift + local nstrict=$1; shift + local priomap=$1; shift + local quanta=("${@}") + + local op=$(if [[ -n $QDISC_DEV ]]; then echo change; else echo add; fi) + + tc qdisc $op dev $dev $PARENT handle 10: ets \ + $(if ((nstrict)); then echo strict $nstrict; fi) \ + $(if ((${#quanta[@]})); then echo quanta ${quanta[@]}; fi) + + if [[ $op == add ]]; then + local prio=0 + local band + + for band in $priomap; do + tc filter add dev $dev parent 10: basic \ + match "meta(priority eq $prio)" \ + flowid 10:$((band + 1)) + ((prio++)) + done + fi + QDISC_DEV=$dev +} + +# Callback from sch_ets_tests.sh +ets_change_qdisc() +{ + if [[ -z "$ETS_CHANGE_QDISC" ]]; then + exit 1 + fi + $ETS_CHANGE_QDISC "$@" +} + +ets_delete_qdisc() +{ + if [[ -n $QDISC_DEV ]]; then + tc qdisc del dev $QDISC_DEV $PARENT + QDISC_DEV= + fi +} + +h1_create() +{ + local i; + + simple_if_init $h1 + mtu_set $h1 9900 + for i in {0..2}; do + vlan_create $h1 1$i v$h1 $(sip $i)/28 + ip link set dev $h1.1$i type vlan egress 0:$i + done +} + +h1_destroy() +{ + local i + + for i in {0..2}; do + vlan_destroy $h1 1$i + done + mtu_restore $h1 + simple_if_fini $h1 +} + +h2_create() +{ + local i + + simple_if_init $h2 + mtu_set $h2 9900 + for i in {0..2}; do + vlan_create $h2 1$i v$h2 $(dip $i)/28 + done +} + +h2_destroy() +{ + local i + + for i in {0..2}; do + vlan_destroy $h2 1$i + done + mtu_restore $h2 + simple_if_fini $h2 +} + +ets_switch_create() +{ + local i + + ip link set dev $swp1 up + mtu_set $swp1 9900 + + ip link set dev $swp2 up + mtu_set $swp2 9900 + + for i in {0..2}; do + vlan_create $swp1 1$i + ip link set dev $swp1.1$i type vlan ingress 0:0 1:1 2:2 + + vlan_create $swp2 1$i + + ip link add dev br1$i type bridge + ip link set dev $swp1.1$i master br1$i + ip link set dev $swp2.1$i master br1$i + + ip link set dev br1$i up + ip link set dev $swp1.1$i up + ip link set dev $swp2.1$i up + done +} + +ets_switch_destroy() +{ + local i + + ets_delete_qdisc + + for i in {0..2}; do + ip link del dev br1$i + vlan_destroy $swp2 1$i + vlan_destroy $swp1 1$i + done + + mtu_restore $swp2 + ip link set dev $swp2 down + + mtu_restore $swp1 + ip link set dev $swp1 down +} + +setup_prepare() +{ + h1=${NETIFS[p1]} + swp1=${NETIFS[p2]} + + swp2=${NETIFS[p3]} + h2=${NETIFS[p4]} + + put=$swp2 + hut=$h2 + + vrf_prepare + + h1_create + h2_create + switch_create +} + +cleanup() +{ + pre_cleanup + + switch_destroy + h2_destroy + h1_destroy + + vrf_cleanup +} + +ping_ipv4() +{ + ping_test $h1.10 $(dip 0) " vlan 10" + ping_test $h1.11 $(dip 1) " vlan 11" + ping_test $h1.12 $(dip 2) " vlan 12" +} + +ets_run() +{ + trap cleanup EXIT + + setup_prepare + setup_wait + + tests_run + + exit $EXIT_STATUS +} diff --git a/tools/testing/selftests/net/forwarding/sch_ets_tests.sh b/tools/testing/selftests/net/forwarding/sch_ets_tests.sh new file mode 100644 index 0000000000000..3c3b204d47e8e --- /dev/null +++ b/tools/testing/selftests/net/forwarding/sch_ets_tests.sh @@ -0,0 +1,227 @@ +# SPDX-License-Identifier: GPL-2.0 + +# Global interface: +# $put -- port under test (e.g. $swp2) +# get_stats($band) -- A function to collect stats for band +# ets_start_traffic($band) -- Start traffic for this band +# ets_change_qdisc($op, $dev, $nstrict, $quanta...) -- Add or change qdisc + +# WS describes the Qdisc configuration. It has one value per band (so the +# number of array elements indicates the number of bands). If the value is +# 0, it is a strict band, otherwise the it's a DRR band and the value is +# that band's quantum. +declare -a WS + +qdisc_describe() +{ + local nbands=${#WS[@]} + local nstrict=0 + local i + + for ((i = 0; i < nbands; i++)); do + if ((!${WS[$i]})); then + : $((nstrict++)) + fi + done + + echo -n "ets bands $nbands" + if ((nstrict)); then + echo -n " strict $nstrict" + fi + if ((nstrict < nbands)); then + echo -n " quanta" + for ((i = nstrict; i < nbands; i++)); do + echo -n " ${WS[$i]}" + done + fi +} + +__strict_eval() +{ + local desc=$1; shift + local d=$1; shift + local total=$1; shift + local above=$1; shift + + RET=0 + + if ((! total)); then + check_err 1 "No traffic observed" + log_test "$desc" + return + fi + + local ratio=$(echo "scale=2; 100 * $d / $total" | bc -l) + if ((above)); then + test $(echo "$ratio > 95.0" | bc -l) -eq 1 + check_err $? "Not enough traffic" + log_test "$desc" + log_info "Expected ratio >95% Measured ratio $ratio" + else + test $(echo "$ratio < 5" | bc -l) -eq 1 + check_err $? "Too much traffic" + log_test "$desc" + log_info "Expected ratio <5% Measured ratio $ratio" + fi +} + +strict_eval() +{ + __strict_eval "$@" 1 +} + +notraf_eval() +{ + __strict_eval "$@" 0 +} + +__ets_dwrr_test() +{ + local -a streams=("$@") + + local low_stream=${streams[0]} + local seen_strict=0 + local -a t0 t1 d + local stream + local total + local i + + echo "Testing $(qdisc_describe), streams ${streams[@]}" + + for stream in ${streams[@]}; do + ets_start_traffic $stream + done + + sleep 10 + + t0=($(for stream in ${streams[@]}; do + get_stats $stream + done)) + + sleep 10 + + t1=($(for stream in ${streams[@]}; do + get_stats $stream + done)) + d=($(for ((i = 0; i < ${#streams[@]}; i++)); do + echo $((${t1[$i]} - ${t0[$i]})) + done)) + total=$(echo ${d[@]} | sed 's/ /+/g' | bc) + + for ((i = 0; i < ${#streams[@]}; i++)); do + local stream=${streams[$i]} + if ((seen_strict)); then + notraf_eval "band $stream" ${d[$i]} $total + elif ((${WS[$stream]} == 0)); then + strict_eval "band $stream" ${d[$i]} $total + seen_strict=1 + elif ((stream == low_stream)); then + # Low stream is used as DWRR evaluation reference. + continue + else + multipath_eval "bands $low_stream:$stream" \ + ${WS[$low_stream]} ${WS[$stream]} \ + ${d[0]} ${d[$i]} + fi + done + + for stream in ${streams[@]}; do + stop_traffic + done +} + +ets_dwrr_test_012() +{ + __ets_dwrr_test 0 1 2 +} + +ets_dwrr_test_01() +{ + __ets_dwrr_test 0 1 +} + +ets_dwrr_test_12() +{ + __ets_dwrr_test 1 2 +} + +ets_qdisc_setup() +{ + local dev=$1; shift + local nstrict=$1; shift + local -a quanta=("$@") + + local ndwrr=${#quanta[@]} + local nbands=$((nstrict + ndwrr)) + local nstreams=$(if ((nbands > 3)); then echo 3; else echo $nbands; fi) + local priomap=$(seq 0 $((nstreams - 1))) + local i + + WS=($( + for ((i = 0; i < nstrict; i++)); do + echo 0 + done + for ((i = 0; i < ndwrr; i++)); do + echo ${quanta[$i]} + done + )) + + ets_change_qdisc $dev $nstrict "$priomap" ${quanta[@]} +} + +ets_set_dwrr_uniform() +{ + ets_qdisc_setup $put 0 3300 3300 3300 +} + +ets_set_dwrr_varying() +{ + ets_qdisc_setup $put 0 5000 3500 1500 +} + +ets_set_strict() +{ + ets_qdisc_setup $put 3 +} + +ets_set_mixed() +{ + ets_qdisc_setup $put 1 5000 2500 1500 +} + +ets_change_quantum() +{ + tc class change dev $put classid 10:2 ets quantum 8000 + WS[1]=8000 +} + +ets_set_dwrr_two_bands() +{ + ets_qdisc_setup $put 0 5000 2500 +} + +ets_test_strict() +{ + ets_set_strict + ets_dwrr_test_01 + ets_dwrr_test_12 +} + +ets_test_mixed() +{ + ets_set_mixed + ets_dwrr_test_01 + ets_dwrr_test_12 +} + +ets_test_dwrr() +{ + ets_set_dwrr_uniform + ets_dwrr_test_012 + ets_set_dwrr_varying + ets_dwrr_test_012 + ets_change_quantum + ets_dwrr_test_012 + ets_set_dwrr_two_bands + ets_dwrr_test_01 +} -- GitLab From 82c664b69c8b3729dfcf33e9abfd952dad35fcca Mon Sep 17 00:00:00 2001 From: Petr Machata Date: Wed, 18 Dec 2019 14:55:24 +0000 Subject: [PATCH 1028/1437] selftests: qdiscs: Add test coverage for ETS Qdisc Add TDC coverage for the new ETS Qdisc. Signed-off-by: Petr Machata Reviewed-by: Ido Schimmel Signed-off-by: David S. Miller --- .../tc-testing/tc-tests/qdiscs/ets.json | 940 ++++++++++++++++++ 1 file changed, 940 insertions(+) create mode 100644 tools/testing/selftests/tc-testing/tc-tests/qdiscs/ets.json diff --git a/tools/testing/selftests/tc-testing/tc-tests/qdiscs/ets.json b/tools/testing/selftests/tc-testing/tc-tests/qdiscs/ets.json new file mode 100644 index 0000000000000..180593010675e --- /dev/null +++ b/tools/testing/selftests/tc-testing/tc-tests/qdiscs/ets.json @@ -0,0 +1,940 @@ +[ + { + "id": "e90e", + "name": "Add ETS qdisc using bands", + "category": [ + "qdisc", + "ets" + ], + "setup": [ + "$IP link add dev $DUMMY type dummy || /bin/true" + ], + "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root ets bands 2", + "expExitCode": "0", + "verifyCmd": "$TC qdisc show dev $DUMMY", + "matchPattern": "qdisc ets 1: root .* bands 2", + "matchCount": "1", + "teardown": [ + "$TC qdisc del dev $DUMMY handle 1: root", + "$IP link del dev $DUMMY type dummy" + ] + }, + { + "id": "b059", + "name": "Add ETS qdisc using quanta", + "category": [ + "qdisc", + "ets" + ], + "setup": [ + "$IP link add dev $DUMMY type dummy || /bin/true" + ], + "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root ets quanta 1000 900 800 700", + "expExitCode": "0", + "verifyCmd": "$TC qdisc show dev $DUMMY", + "matchPattern": "qdisc ets 1: root .*bands 4 quanta 1000 900 800 700", + "matchCount": "1", + "teardown": [ + "$TC qdisc del dev $DUMMY handle 1: root", + "$IP link del dev $DUMMY type dummy" + ] + }, + { + "id": "e8e7", + "name": "Add ETS qdisc using strict", + "category": [ + "qdisc", + "ets" + ], + "setup": [ + "$IP link add dev $DUMMY type dummy || /bin/true" + ], + "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root ets strict 3", + "expExitCode": "0", + "verifyCmd": "$TC qdisc show dev $DUMMY", + "matchPattern": "qdisc ets 1: root .*bands 3 strict 3", + "matchCount": "1", + "teardown": [ + "$TC qdisc del dev $DUMMY handle 1: root", + "$IP link del dev $DUMMY type dummy" + ] + }, + { + "id": "233c", + "name": "Add ETS qdisc using bands + quanta", + "category": [ + "qdisc", + "ets" + ], + "setup": [ + "$IP link add dev $DUMMY type dummy || /bin/true" + ], + "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root ets bands 4 quanta 1000 900 800 700", + "expExitCode": "0", + "verifyCmd": "$TC qdisc show dev $DUMMY", + "matchPattern": "qdisc ets 1: root .*bands 4 quanta 1000 900 800 700 priomap", + "matchCount": "1", + "teardown": [ + "$TC qdisc del dev $DUMMY handle 1: root", + "$IP link del dev $DUMMY type dummy" + ] + }, + { + "id": "3d35", + "name": "Add ETS qdisc using bands + strict", + "category": [ + "qdisc", + "ets" + ], + "setup": [ + "$IP link add dev $DUMMY type dummy || /bin/true" + ], + "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root ets bands 3 strict 3", + "expExitCode": "0", + "verifyCmd": "$TC qdisc show dev $DUMMY", + "matchPattern": "qdisc ets 1: root .*bands 3 strict 3 priomap", + "matchCount": "1", + "teardown": [ + "$TC qdisc del dev $DUMMY handle 1: root", + "$IP link del dev $DUMMY type dummy" + ] + }, + { + "id": "7f3b", + "name": "Add ETS qdisc using strict + quanta", + "category": [ + "qdisc", + "ets" + ], + "setup": [ + "$IP link add dev $DUMMY type dummy || /bin/true" + ], + "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root ets strict 3 quanta 1500 750", + "expExitCode": "0", + "verifyCmd": "$TC qdisc show dev $DUMMY", + "matchPattern": "qdisc ets 1: root .*bands 5 strict 3 quanta 1500 750 priomap", + "matchCount": "1", + "teardown": [ + "$TC qdisc del dev $DUMMY handle 1: root", + "$IP link del dev $DUMMY type dummy" + ] + }, + { + "id": "4593", + "name": "Add ETS qdisc using strict 0 + quanta", + "category": [ + "qdisc", + "ets" + ], + "setup": [ + "$IP link add dev $DUMMY type dummy || /bin/true" + ], + "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root ets strict 0 quanta 1500 750", + "expExitCode": "0", + "verifyCmd": "$TC qdisc show dev $DUMMY", + "matchPattern": "qdisc ets 1: root .*bands 2 quanta 1500 750 priomap", + "matchCount": "1", + "teardown": [ + "$TC qdisc del dev $DUMMY handle 1: root", + "$IP link del dev $DUMMY type dummy" + ] + }, + { + "id": "8938", + "name": "Add ETS qdisc using bands + strict + quanta", + "category": [ + "qdisc", + "ets" + ], + "setup": [ + "$IP link add dev $DUMMY type dummy || /bin/true" + ], + "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root ets bands 5 strict 3 quanta 1500 750", + "expExitCode": "0", + "verifyCmd": "$TC qdisc show dev $DUMMY", + "matchPattern": "qdisc ets 1: root .*bands 5 .*strict 3 quanta 1500 750 priomap", + "matchCount": "1", + "teardown": [ + "$TC qdisc del dev $DUMMY handle 1: root", + "$IP link del dev $DUMMY type dummy" + ] + }, + { + "id": "0782", + "name": "Add ETS qdisc with more bands than quanta", + "category": [ + "qdisc", + "ets" + ], + "setup": [ + "$IP link add dev $DUMMY type dummy || /bin/true" + ], + "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root ets bands 2 quanta 1000", + "expExitCode": "0", + "verifyCmd": "$TC qdisc show dev $DUMMY", + "matchPattern": "qdisc ets 1: root .*bands 2 .*quanta 1000 [1-9][0-9]* priomap", + "matchCount": "1", + "teardown": [ + "$TC qdisc del dev $DUMMY handle 1: root", + "$IP link del dev $DUMMY type dummy" + ] + }, + { + "id": "501b", + "name": "Add ETS qdisc with more bands than strict", + "category": [ + "qdisc", + "ets" + ], + "setup": [ + "$IP link add dev $DUMMY type dummy || /bin/true" + ], + "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root ets bands 3 strict 1", + "expExitCode": "0", + "verifyCmd": "$TC qdisc show dev $DUMMY", + "matchPattern": "qdisc ets 1: root .*bands 3 strict 1 quanta ([1-9][0-9]* ){2}priomap", + "matchCount": "1", + "teardown": [ + "$TC qdisc del dev $DUMMY handle 1: root", + "$IP link del dev $DUMMY type dummy" + ] + }, + { + "id": "671a", + "name": "Add ETS qdisc with more bands than strict + quanta", + "category": [ + "qdisc", + "ets" + ], + "setup": [ + "$IP link add dev $DUMMY type dummy || /bin/true" + ], + "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root ets bands 3 strict 1 quanta 1000", + "expExitCode": "0", + "verifyCmd": "$TC qdisc show dev $DUMMY", + "matchPattern": "qdisc ets 1: root .*bands 3 strict 1 quanta 1000 [1-9][0-9]* priomap", + "matchCount": "1", + "teardown": [ + "$TC qdisc del dev $DUMMY handle 1: root", + "$IP link del dev $DUMMY type dummy" + ] + }, + { + "id": "2a23", + "name": "Add ETS qdisc with 16 bands", + "category": [ + "qdisc", + "ets" + ], + "setup": [ + "$IP link add dev $DUMMY type dummy || /bin/true" + ], + "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root ets bands 16", + "expExitCode": "0", + "verifyCmd": "$TC qdisc show dev $DUMMY", + "matchPattern": "qdisc ets 1: root .* bands 16", + "matchCount": "1", + "teardown": [ + "$TC qdisc del dev $DUMMY handle 1: root", + "$IP link del dev $DUMMY type dummy" + ] + }, + { + "id": "8daf", + "name": "Add ETS qdisc with 17 bands", + "category": [ + "qdisc", + "ets" + ], + "setup": [ + "$IP link add dev $DUMMY type dummy || /bin/true" + ], + "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root ets bands 17", + "expExitCode": "1", + "verifyCmd": "$TC qdisc show dev $DUMMY", + "matchPattern": "qdisc ets", + "matchCount": "0", + "teardown": [ + "$IP link del dev $DUMMY type dummy" + ] + }, + { + "id": "7f95", + "name": "Add ETS qdisc with 17 strict", + "category": [ + "qdisc", + "ets" + ], + "setup": [ + "$IP link add dev $DUMMY type dummy || /bin/true" + ], + "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root ets strict 17", + "expExitCode": "1", + "verifyCmd": "$TC qdisc show dev $DUMMY", + "matchPattern": "qdisc ets", + "matchCount": "0", + "teardown": [ + "$IP link del dev $DUMMY type dummy" + ] + }, + { + "id": "837a", + "name": "Add ETS qdisc with 16 quanta", + "category": [ + "qdisc", + "ets" + ], + "setup": [ + "$IP link add dev $DUMMY type dummy || /bin/true" + ], + "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root ets quanta 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16", + "expExitCode": "0", + "verifyCmd": "$TC qdisc show dev $DUMMY", + "matchPattern": "qdisc ets 1: root .* bands 16", + "matchCount": "1", + "teardown": [ + "$TC qdisc del dev $DUMMY handle 1: root", + "$IP link del dev $DUMMY type dummy" + ] + }, + { + "id": "65b6", + "name": "Add ETS qdisc with 17 quanta", + "category": [ + "qdisc", + "ets" + ], + "setup": [ + "$IP link add dev $DUMMY type dummy || /bin/true" + ], + "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root ets quanta 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17", + "expExitCode": "2", + "verifyCmd": "$TC qdisc show dev $DUMMY", + "matchPattern": "qdisc ets", + "matchCount": "0", + "teardown": [ + "$IP link del dev $DUMMY type dummy" + ] + }, + { + "id": "b9e9", + "name": "Add ETS qdisc with 16 strict + quanta", + "category": [ + "qdisc", + "ets" + ], + "setup": [ + "$IP link add dev $DUMMY type dummy || /bin/true" + ], + "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root ets strict 8 quanta 1 2 3 4 5 6 7 8", + "expExitCode": "0", + "verifyCmd": "$TC qdisc show dev $DUMMY", + "matchPattern": "qdisc ets 1: root .* bands 16", + "matchCount": "1", + "teardown": [ + "$TC qdisc del dev $DUMMY handle 1: root", + "$IP link del dev $DUMMY type dummy" + ] + }, + { + "id": "9877", + "name": "Add ETS qdisc with 17 strict + quanta", + "category": [ + "qdisc", + "ets" + ], + "setup": [ + "$IP link add dev $DUMMY type dummy || /bin/true" + ], + "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root ets strict 9 quanta 1 2 3 4 5 6 7 8", + "expExitCode": "2", + "verifyCmd": "$TC qdisc show dev $DUMMY", + "matchPattern": "qdisc ets", + "matchCount": "0", + "teardown": [ + "$IP link del dev $DUMMY type dummy" + ] + }, + { + "id": "c696", + "name": "Add ETS qdisc with priomap", + "category": [ + "qdisc", + "ets" + ], + "setup": [ + "$IP link add dev $DUMMY type dummy || /bin/true" + ], + "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root ets bands 5 priomap 0 0 1 0 1 2 0 1 2 3 0 1 2 3 4 0", + "expExitCode": "0", + "verifyCmd": "$TC qdisc show dev $DUMMY", + "matchPattern": "qdisc ets 1: root .*priomap 0 0 1 0 1 2 0 1 2 3 0 1 2 3 4 0", + "matchCount": "1", + "teardown": [ + "$TC qdisc del dev $DUMMY handle 1: root", + "$IP link del dev $DUMMY type dummy" + ] + }, + { + "id": "30c4", + "name": "Add ETS qdisc with quanta + priomap", + "category": [ + "qdisc", + "ets" + ], + "setup": [ + "$IP link add dev $DUMMY type dummy || /bin/true" + ], + "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root ets quanta 1000 2000 3000 4000 5000 priomap 0 0 1 0 1 2 0 1 2 3 0 1 2 3 4 0", + "expExitCode": "0", + "verifyCmd": "$TC qdisc show dev $DUMMY", + "matchPattern": "qdisc ets 1: root .*quanta 1000 2000 3000 4000 5000 priomap 0 0 1 0 1 2 0 1 2 3 0 1 2 3 4 0", + "matchCount": "1", + "teardown": [ + "$TC qdisc del dev $DUMMY handle 1: root", + "$IP link del dev $DUMMY type dummy" + ] + }, + { + "id": "e8ac", + "name": "Add ETS qdisc with strict + priomap", + "category": [ + "qdisc", + "ets" + ], + "setup": [ + "$IP link add dev $DUMMY type dummy || /bin/true" + ], + "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root ets strict 5 priomap 0 0 1 0 1 2 0 1 2 3 0 1 2 3 4 0", + "expExitCode": "0", + "verifyCmd": "$TC qdisc show dev $DUMMY", + "matchPattern": "qdisc ets 1: root .*bands 5 strict 5 priomap 0 0 1 0 1 2 0 1 2 3 0 1 2 3 4 0", + "matchCount": "1", + "teardown": [ + "$TC qdisc del dev $DUMMY handle 1: root", + "$IP link del dev $DUMMY type dummy" + ] + }, + { + "id": "5a7e", + "name": "Add ETS qdisc with quanta + strict + priomap", + "category": [ + "qdisc", + "ets" + ], + "setup": [ + "$IP link add dev $DUMMY type dummy || /bin/true" + ], + "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root ets strict 2 quanta 1000 2000 3000 priomap 0 0 1 0 1 2 0 1 2 3 0 1 2 3 4 0", + "expExitCode": "0", + "verifyCmd": "$TC qdisc show dev $DUMMY", + "matchPattern": "qdisc ets 1: root .*strict 2 quanta 1000 2000 3000 priomap 0 0 1 0 1 2 0 1 2 3 0 1 2 3 4 0", + "matchCount": "1", + "teardown": [ + "$TC qdisc del dev $DUMMY handle 1: root", + "$IP link del dev $DUMMY type dummy" + ] + }, + { + "id": "cb8b", + "name": "Show ETS class :1", + "category": [ + "qdisc", + "ets" + ], + "setup": [ + "$IP link add dev $DUMMY type dummy || /bin/true" + ], + "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root ets quanta 4000 3000 2000", + "expExitCode": "0", + "verifyCmd": "$TC class show dev $DUMMY classid 1:1", + "matchPattern": "class ets 1:1 root quantum 4000", + "matchCount": "1", + "teardown": [ + "$TC qdisc del dev $DUMMY handle 1: root", + "$IP link del dev $DUMMY type dummy" + ] + }, + { + "id": "1b4e", + "name": "Show ETS class :2", + "category": [ + "qdisc", + "ets" + ], + "setup": [ + "$IP link add dev $DUMMY type dummy || /bin/true" + ], + "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root ets quanta 4000 3000 2000", + "expExitCode": "0", + "verifyCmd": "$TC class show dev $DUMMY classid 1:2", + "matchPattern": "class ets 1:2 root quantum 3000", + "matchCount": "1", + "teardown": [ + "$TC qdisc del dev $DUMMY handle 1: root", + "$IP link del dev $DUMMY type dummy" + ] + }, + { + "id": "f642", + "name": "Show ETS class :3", + "category": [ + "qdisc", + "ets" + ], + "setup": [ + "$IP link add dev $DUMMY type dummy || /bin/true" + ], + "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root ets quanta 4000 3000 2000", + "expExitCode": "0", + "verifyCmd": "$TC class show dev $DUMMY classid 1:3", + "matchPattern": "class ets 1:3 root quantum 2000", + "matchCount": "1", + "teardown": [ + "$TC qdisc del dev $DUMMY handle 1: root", + "$IP link del dev $DUMMY type dummy" + ] + }, + { + "id": "0a5f", + "name": "Show ETS strict class", + "category": [ + "qdisc", + "ets" + ], + "setup": [ + "$IP link add dev $DUMMY type dummy || /bin/true" + ], + "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root ets strict 3", + "expExitCode": "0", + "verifyCmd": "$TC class show dev $DUMMY classid 1:1", + "matchPattern": "class ets 1:1 root $", + "matchCount": "1", + "teardown": [ + "$TC qdisc del dev $DUMMY handle 1: root", + "$IP link del dev $DUMMY type dummy" + ] + }, + { + "id": "f7c8", + "name": "Add ETS qdisc with too many quanta", + "category": [ + "qdisc", + "ets" + ], + "setup": [ + "$IP link add dev $DUMMY type dummy || /bin/true" + ], + "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root ets bands 2 quanta 1000 2000 3000", + "expExitCode": "1", + "verifyCmd": "$TC qdisc show dev $DUMMY", + "matchPattern": "qdisc ets", + "matchCount": "0", + "teardown": [ + "$IP link del dev $DUMMY type dummy" + ] + }, + { + "id": "2389", + "name": "Add ETS qdisc with too many strict", + "category": [ + "qdisc", + "ets" + ], + "setup": [ + "$IP link add dev $DUMMY type dummy || /bin/true" + ], + "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root ets bands 2 strict 3", + "expExitCode": "1", + "verifyCmd": "$TC qdisc show dev $DUMMY", + "matchPattern": "qdisc ets", + "matchCount": "0", + "teardown": [ + "$IP link del dev $DUMMY type dummy" + ] + }, + { + "id": "fe3c", + "name": "Add ETS qdisc with too many strict + quanta", + "category": [ + "qdisc", + "ets" + ], + "setup": [ + "$IP link add dev $DUMMY type dummy || /bin/true" + ], + "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root ets bands 4 strict 2 quanta 1000 2000 3000", + "expExitCode": "1", + "verifyCmd": "$TC qdisc show dev $DUMMY", + "matchPattern": "qdisc ets", + "matchCount": "0", + "teardown": [ + "$IP link del dev $DUMMY type dummy" + ] + }, + { + "id": "cb04", + "name": "Add ETS qdisc with excess priomap elements", + "category": [ + "qdisc", + "ets" + ], + "setup": [ + "$IP link add dev $DUMMY type dummy || /bin/true" + ], + "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root ets bands 5 priomap 0 0 1 0 1 2 0 1 2 3 0 1 2 3 4 0 1 2", + "expExitCode": "1", + "verifyCmd": "$TC qdisc show dev $DUMMY", + "matchPattern": "qdisc ets", + "matchCount": "0", + "teardown": [ + "$IP link del dev $DUMMY type dummy" + ] + }, + { + "id": "c32e", + "name": "Add ETS qdisc with priomap above bands", + "category": [ + "qdisc", + "ets" + ], + "setup": [ + "$IP link add dev $DUMMY type dummy || /bin/true" + ], + "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root ets bands 2 priomap 0 1 2", + "expExitCode": "1", + "verifyCmd": "$TC qdisc show dev $DUMMY", + "matchPattern": "qdisc ets", + "matchCount": "0", + "teardown": [ + "$IP link del dev $DUMMY type dummy" + ] + }, + { + "id": "744c", + "name": "Add ETS qdisc with priomap above quanta", + "category": [ + "qdisc", + "ets" + ], + "setup": [ + "$IP link add dev $DUMMY type dummy || /bin/true" + ], + "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root ets quanta 1000 500 priomap 0 1 2", + "expExitCode": "1", + "verifyCmd": "$TC qdisc show dev $DUMMY", + "matchPattern": "qdisc ets", + "matchCount": "0", + "teardown": [ + "$IP link del dev $DUMMY type dummy" + ] + }, + { + "id": "7b33", + "name": "Add ETS qdisc with priomap above strict", + "category": [ + "qdisc", + "ets" + ], + "setup": [ + "$IP link add dev $DUMMY type dummy || /bin/true" + ], + "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root ets strict 2 priomap 0 1 2", + "expExitCode": "1", + "verifyCmd": "$TC qdisc show dev $DUMMY", + "matchPattern": "qdisc ets", + "matchCount": "0", + "teardown": [ + "$IP link del dev $DUMMY type dummy" + ] + }, + { + "id": "dbe6", + "name": "Add ETS qdisc with priomap above strict + quanta", + "category": [ + "qdisc", + "ets" + ], + "setup": [ + "$IP link add dev $DUMMY type dummy || /bin/true" + ], + "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root ets strict 1 quanta 1000 500 priomap 0 1 2 3", + "expExitCode": "1", + "verifyCmd": "$TC qdisc show dev $DUMMY", + "matchPattern": "qdisc ets", + "matchCount": "0", + "teardown": [ + "$IP link del dev $DUMMY type dummy" + ] + }, + { + "id": "bdb2", + "name": "Add ETS qdisc with priomap within bands with strict + quanta", + "category": [ + "qdisc", + "ets" + ], + "setup": [ + "$IP link add dev $DUMMY type dummy || /bin/true" + ], + "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root ets bands 4 strict 1 quanta 1000 500 priomap 0 1 2 3", + "expExitCode": "0", + "verifyCmd": "$TC qdisc show dev $DUMMY", + "matchPattern": "qdisc ets", + "matchCount": "1", + "teardown": [ + "$IP link del dev $DUMMY type dummy" + ] + }, + { + "id": "39a3", + "name": "Add ETS qdisc with priomap above bands with strict + quanta", + "category": [ + "qdisc", + "ets" + ], + "setup": [ + "$IP link add dev $DUMMY type dummy || /bin/true" + ], + "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root ets bands 4 strict 1 quanta 1000 500 priomap 0 1 2 3 4", + "expExitCode": "1", + "verifyCmd": "$TC qdisc show dev $DUMMY", + "matchPattern": "qdisc ets", + "matchCount": "0", + "teardown": [ + "$IP link del dev $DUMMY type dummy" + ] + }, + { + "id": "557c", + "name": "Unset priorities default to the last band", + "category": [ + "qdisc", + "ets" + ], + "setup": [ + "$IP link add dev $DUMMY type dummy || /bin/true" + ], + "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root ets bands 4 priomap 0 0 0 0", + "expExitCode": "0", + "verifyCmd": "$TC qdisc show dev $DUMMY", + "matchPattern": "qdisc ets .*priomap 0 0 0 0 3 3 3 3 3 3 3 3 3 3 3 3", + "matchCount": "1", + "teardown": [ + "$IP link del dev $DUMMY type dummy" + ] + }, + { + "id": "a347", + "name": "Unset priorities default to the last band -- no priomap", + "category": [ + "qdisc", + "ets" + ], + "setup": [ + "$IP link add dev $DUMMY type dummy || /bin/true" + ], + "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root ets bands 4", + "expExitCode": "0", + "verifyCmd": "$TC qdisc show dev $DUMMY", + "matchPattern": "qdisc ets .*priomap 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3", + "matchCount": "1", + "teardown": [ + "$IP link del dev $DUMMY type dummy" + ] + }, + { + "id": "39c4", + "name": "Add ETS qdisc with too few bands", + "category": [ + "qdisc", + "ets" + ], + "setup": [ + "$IP link add dev $DUMMY type dummy || /bin/true" + ], + "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root ets bands 0", + "expExitCode": "1", + "verifyCmd": "$TC qdisc show dev $DUMMY", + "matchPattern": "qdisc ets", + "matchCount": "0", + "teardown": [ + "$IP link del dev $DUMMY type dummy" + ] + }, + { + "id": "930b", + "name": "Add ETS qdisc with too many bands", + "category": [ + "qdisc", + "ets" + ], + "setup": [ + "$IP link add dev $DUMMY type dummy || /bin/true" + ], + "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root ets bands 17", + "expExitCode": "1", + "verifyCmd": "$TC qdisc show dev $DUMMY", + "matchPattern": "qdisc ets", + "matchCount": "0", + "teardown": [ + "$IP link del dev $DUMMY type dummy" + ] + }, + { + "id": "406a", + "name": "Add ETS qdisc without parameters", + "category": [ + "qdisc", + "ets" + ], + "setup": [ + "$IP link add dev $DUMMY type dummy || /bin/true" + ], + "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root ets", + "expExitCode": "1", + "verifyCmd": "$TC qdisc show dev $DUMMY", + "matchPattern": "qdisc ets", + "matchCount": "0", + "teardown": [ + "$IP link del dev $DUMMY type dummy" + ] + }, + { + "id": "e51a", + "name": "Zero element in quanta", + "category": [ + "qdisc", + "ets" + ], + "setup": [ + "$IP link add dev $DUMMY type dummy || /bin/true" + ], + "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root ets quanta 1000 0 800 700", + "expExitCode": "1", + "verifyCmd": "$TC qdisc show dev $DUMMY", + "matchPattern": "qdisc ets", + "matchCount": "0", + "teardown": [ + "$IP link del dev $DUMMY type dummy" + ] + }, + { + "id": "e7f2", + "name": "Sole zero element in quanta", + "category": [ + "qdisc", + "ets" + ], + "setup": [ + "$IP link add dev $DUMMY type dummy || /bin/true" + ], + "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root ets quanta 0", + "expExitCode": "1", + "verifyCmd": "$TC qdisc show dev $DUMMY", + "matchPattern": "qdisc ets", + "matchCount": "0", + "teardown": [ + "$IP link del dev $DUMMY type dummy" + ] + }, + { + "id": "d6e6", + "name": "No values after the quanta keyword", + "category": [ + "qdisc", + "ets" + ], + "setup": [ + "$IP link add dev $DUMMY type dummy || /bin/true" + ], + "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root ets quanta", + "expExitCode": "255", + "verifyCmd": "$TC qdisc show dev $DUMMY", + "matchPattern": "qdisc ets", + "matchCount": "0", + "teardown": [ + "$IP link del dev $DUMMY type dummy" + ] + }, + { + "id": "28c6", + "name": "Change ETS band quantum", + "category": [ + "qdisc", + "ets" + ], + "setup": [ + "$IP link add dev $DUMMY type dummy || /bin/true", + "$TC qdisc add dev $DUMMY handle 1: root ets quanta 1000 2000 3000" + ], + "cmdUnderTest": "$TC class change dev $DUMMY classid 1:1 ets quantum 1500", + "expExitCode": "0", + "verifyCmd": "$TC qdisc show dev $DUMMY", + "matchPattern": "qdisc ets 1: root .*quanta 1500 2000 3000 priomap ", + "matchCount": "1", + "teardown": [ + "$IP link del dev $DUMMY type dummy" + ] + }, + { + "id": "4714", + "name": "Change ETS band without quantum", + "category": [ + "qdisc", + "ets" + ], + "setup": [ + "$IP link add dev $DUMMY type dummy || /bin/true", + "$TC qdisc add dev $DUMMY handle 1: root ets quanta 1000 2000 3000" + ], + "cmdUnderTest": "$TC class change dev $DUMMY classid 1:1 ets", + "expExitCode": "0", + "verifyCmd": "$TC qdisc show dev $DUMMY", + "matchPattern": "qdisc ets 1: root .*quanta 1000 2000 3000 priomap ", + "matchCount": "1", + "teardown": [ + "$IP link del dev $DUMMY type dummy" + ] + }, + { + "id": "6979", + "name": "Change quantum of a strict ETS band", + "category": [ + "qdisc", + "ets" + ], + "setup": [ + "$IP link add dev $DUMMY type dummy || /bin/true", + "$TC qdisc add dev $DUMMY handle 1: root ets strict 5" + ], + "cmdUnderTest": "$TC class change dev $DUMMY classid 1:2 ets quantum 1500", + "expExitCode": "2", + "verifyCmd": "$TC qdisc show dev $DUMMY", + "matchPattern": "qdisc ets .*bands 5 .*strict 5", + "matchCount": "1", + "teardown": [ + "$IP link del dev $DUMMY type dummy" + ] + }, + { + "id": "9a7d", + "name": "Change ETS strict band without quantum", + "category": [ + "qdisc", + "ets" + ], + "setup": [ + "$IP link add dev $DUMMY type dummy || /bin/true", + "$TC qdisc add dev $DUMMY handle 1: root ets strict 5" + ], + "cmdUnderTest": "$TC class change dev $DUMMY classid 1:2 ets", + "expExitCode": "0", + "verifyCmd": "$TC qdisc show dev $DUMMY", + "matchPattern": "qdisc ets .*bands 5 .*strict 5", + "matchCount": "1", + "teardown": [ + "$IP link del dev $DUMMY type dummy" + ] + } +] -- GitLab From 6feeee8efc53035c3195b02068b58ae947538aa4 Mon Sep 17 00:00:00 2001 From: Sven Schnelle Date: Mon, 9 Dec 2019 09:03:12 +0100 Subject: [PATCH 1029/1437] s390/ftrace: fix endless recursion in function_graph tracer The following sequence triggers a kernel stack overflow on s390x: mount -t tracefs tracefs /sys/kernel/tracing cd /sys/kernel/tracing echo function_graph > current_tracer [crash] This is because preempt_count_{add,sub} are in the list of traced functions, which can be demonstrated by: echo preempt_count_add >set_ftrace_filter echo function_graph > current_tracer [crash] The stack overflow happens because get_tod_clock_monotonic() gets called by ftrace but itself calls preempt_{disable,enable}(), which leads to a endless recursion. Fix this by using preempt_{disable,enable}_notrace(). Fixes: 011620688a71 ("s390/time: ensure get_clock_monotonic() returns monotonic values") Signed-off-by: Sven Schnelle Reviewed-by: Vasily Gorbik Signed-off-by: Vasily Gorbik --- arch/s390/include/asm/timex.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/s390/include/asm/timex.h b/arch/s390/include/asm/timex.h index 6da8885251d65..670f14a228e55 100644 --- a/arch/s390/include/asm/timex.h +++ b/arch/s390/include/asm/timex.h @@ -194,9 +194,9 @@ static inline unsigned long long get_tod_clock_monotonic(void) { unsigned long long tod; - preempt_disable(); + preempt_disable_notrace(); tod = get_tod_clock() - *(unsigned long long *) &tod_clock_base[1]; - preempt_enable(); + preempt_enable_notrace(); return tod; } -- GitLab From cd92ac253063981c96abfc3fdb326e430bd89ea6 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Thu, 12 Dec 2019 21:53:04 +0100 Subject: [PATCH 1030/1437] s390/purgatory: Make sure we fail the build if purgatory has missing symbols Since we link purgatory with -r aka we enable "incremental linking" no checks for unresolved symbols are done while linking the purgatory. This commit adds an extra check for unresolved symbols by calling ld without -r before running objcopy to generate purgatory.ro. This will help us catch missing symbols in the purgatory sooner. Note this commit also removes --no-undefined from LDFLAGS_purgatory as that has no effect. Signed-off-by: Hans de Goede Link: https://lore.kernel.org/lkml/20191212205304.191610-1-hdegoede@redhat.com Tested-by: Philipp Rudo Signed-off-by: Christian Borntraeger Signed-off-by: Vasily Gorbik --- arch/s390/purgatory/.gitignore | 1 + arch/s390/purgatory/Makefile | 13 ++++++++++--- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/arch/s390/purgatory/.gitignore b/arch/s390/purgatory/.gitignore index 04a03433c7201..c82157f46b186 100644 --- a/arch/s390/purgatory/.gitignore +++ b/arch/s390/purgatory/.gitignore @@ -1,3 +1,4 @@ purgatory +purgatory.chk purgatory.lds purgatory.ro diff --git a/arch/s390/purgatory/Makefile b/arch/s390/purgatory/Makefile index bc0d7a0d03945..13e9a5dc0a07c 100644 --- a/arch/s390/purgatory/Makefile +++ b/arch/s390/purgatory/Makefile @@ -4,7 +4,7 @@ OBJECT_FILES_NON_STANDARD := y purgatory-y := head.o purgatory.o string.o sha256.o mem.o -targets += $(purgatory-y) purgatory.lds purgatory purgatory.ro +targets += $(purgatory-y) purgatory.lds purgatory purgatory.chk purgatory.ro PURGATORY_OBJS = $(addprefix $(obj)/,$(purgatory-y)) $(obj)/sha256.o: $(srctree)/lib/crypto/sha256.c FORCE @@ -26,15 +26,22 @@ KBUILD_CFLAGS += $(CLANG_FLAGS) KBUILD_CFLAGS += $(call cc-option,-fno-PIE) KBUILD_AFLAGS := $(filter-out -DCC_USING_EXPOLINE,$(KBUILD_AFLAGS)) -LDFLAGS_purgatory := -r --no-undefined -nostdlib -z nodefaultlib -T +# Since we link purgatory with -r unresolved symbols are not checked, so we +# also link a purgatory.chk binary without -r to check for unresolved symbols. +PURGATORY_LDFLAGS := -nostdlib -z nodefaultlib +LDFLAGS_purgatory := -r $(PURGATORY_LDFLAGS) -T +LDFLAGS_purgatory.chk := -e purgatory_start $(PURGATORY_LDFLAGS) $(obj)/purgatory: $(obj)/purgatory.lds $(PURGATORY_OBJS) FORCE $(call if_changed,ld) +$(obj)/purgatory.chk: $(obj)/purgatory FORCE + $(call if_changed,ld) + OBJCOPYFLAGS_purgatory.ro := -O elf64-s390 OBJCOPYFLAGS_purgatory.ro += --remove-section='*debug*' OBJCOPYFLAGS_purgatory.ro += --remove-section='.comment' OBJCOPYFLAGS_purgatory.ro += --remove-section='.note.*' -$(obj)/purgatory.ro: $(obj)/purgatory FORCE +$(obj)/purgatory.ro: $(obj)/purgatory $(obj)/purgatory.chk FORCE $(call if_changed,objcopy) $(obj)/kexec-purgatory.o: $(obj)/kexec-purgatory.S $(obj)/purgatory.ro FORCE -- GitLab From c23587c92f6e3260fe3b82bb75b38aa2553b9468 Mon Sep 17 00:00:00 2001 From: Christian Borntraeger Date: Wed, 18 Dec 2019 09:34:57 +0100 Subject: [PATCH 1031/1437] s390/purgatory: do not build purgatory with kcov, kasan and friends the purgatory must not rely on functions from the "old" kernel, so we must disable kasan and friends. We also need to have a separate copy of string.c as the default does not build memcmp with KASAN. Reported-by: kbuild test robot Signed-off-by: Christian Borntraeger Reviewed-by: Vasily Gorbik Signed-off-by: Vasily Gorbik --- arch/s390/purgatory/Makefile | 6 ++++-- arch/s390/purgatory/string.c | 3 +++ 2 files changed, 7 insertions(+), 2 deletions(-) create mode 100644 arch/s390/purgatory/string.c diff --git a/arch/s390/purgatory/Makefile b/arch/s390/purgatory/Makefile index 13e9a5dc0a07c..c57f8c40e9926 100644 --- a/arch/s390/purgatory/Makefile +++ b/arch/s390/purgatory/Makefile @@ -15,8 +15,10 @@ CFLAGS_sha256.o := -D__DISABLE_EXPORTS $(obj)/mem.o: $(srctree)/arch/s390/lib/mem.S FORCE $(call if_changed_rule,as_o_S) -$(obj)/string.o: $(srctree)/arch/s390/lib/string.c FORCE - $(call if_changed_rule,cc_o_c) +KCOV_INSTRUMENT := n +GCOV_PROFILE := n +UBSAN_SANITIZE := n +KASAN_SANITIZE := n KBUILD_CFLAGS := -fno-strict-aliasing -Wall -Wstrict-prototypes KBUILD_CFLAGS += -Wno-pointer-sign -Wno-sign-compare diff --git a/arch/s390/purgatory/string.c b/arch/s390/purgatory/string.c new file mode 100644 index 0000000000000..c98c22a72db71 --- /dev/null +++ b/arch/s390/purgatory/string.c @@ -0,0 +1,3 @@ +// SPDX-License-Identifier: GPL-2.0 +#define __HAVE_ARCH_MEMCMP /* arch function */ +#include "../lib/string.c" -- GitLab From eef06cbf670aaa2ccb56c9a7b84042acd657aa5d Mon Sep 17 00:00:00 2001 From: Vasily Gorbik Date: Wed, 11 Dec 2019 17:27:31 +0100 Subject: [PATCH 1032/1437] s390/unwind: stop gracefully at user mode pt_regs in irq stack Consider reaching user mode pt_regs at the bottom of irq stack graceful unwinder termination. This is the case when irq/mcck/ext interrupt arrives while in user mode. Signed-off-by: Vasily Gorbik --- arch/s390/kernel/unwind_bc.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/arch/s390/kernel/unwind_bc.c b/arch/s390/kernel/unwind_bc.c index da2d4d4c5b0e0..707fd99f6734d 100644 --- a/arch/s390/kernel/unwind_bc.c +++ b/arch/s390/kernel/unwind_bc.c @@ -36,10 +36,17 @@ static bool update_stack_info(struct unwind_state *state, unsigned long sp) return true; } -static inline bool is_task_pt_regs(struct unwind_state *state, - struct pt_regs *regs) +static inline bool is_final_pt_regs(struct unwind_state *state, + struct pt_regs *regs) { - return task_pt_regs(state->task) == regs; + /* user mode or kernel thread pt_regs at the bottom of task stack */ + if (task_pt_regs(state->task) == regs) + return true; + + /* user mode pt_regs at the bottom of irq stack */ + return state->stack_info.type == STACK_TYPE_IRQ && + state->stack_info.end - sizeof(struct pt_regs) == (unsigned long)regs && + READ_ONCE_NOCHECK(regs->psw.mask) & PSW_MASK_PSTATE; } bool unwind_next_frame(struct unwind_state *state) @@ -80,7 +87,7 @@ bool unwind_next_frame(struct unwind_state *state) if (!on_stack(info, sp, sizeof(struct pt_regs))) goto out_err; regs = (struct pt_regs *) sp; - if (is_task_pt_regs(state, regs)) + if (is_final_pt_regs(state, regs)) goto out_stop; ip = READ_ONCE_NOCHECK(regs->psw.addr); sp = READ_ONCE_NOCHECK(regs->gprs[15]); -- GitLab From b4adfe55915d8363e244e42386d69567db1719b9 Mon Sep 17 00:00:00 2001 From: Vasily Gorbik Date: Tue, 10 Dec 2019 13:50:23 +0100 Subject: [PATCH 1033/1437] s390/ftrace: save traced function caller A typical backtrace acquired from ftraced function currently looks like the following (e.g. for "path_openat"): arch_stack_walk+0x15c/0x2d8 stack_trace_save+0x50/0x68 stack_trace_call+0x15a/0x3b8 ftrace_graph_caller+0x0/0x1c 0x3e0007e3c98 <- ftraced function caller (should be do_filp_open+0x7c/0xe8) do_open_execat+0x70/0x1b8 __do_execve_file.isra.0+0x7d8/0x860 __s390x_sys_execve+0x56/0x68 system_call+0xdc/0x2d8 Note random "0x3e0007e3c98" stack value as ftraced function caller. This value causes either imprecise unwinder result or unwinding failure. That "0x3e0007e3c98" comes from r14 of ftraced function stack frame, which it haven't had a chance to initialize since the very first instruction calls ftrace code ("ftrace_caller"). (ftraced function might never save r14 as well). Nevertheless according to s390 ABI any function is called with stack frame allocated for it and r14 contains return address. "ftrace_caller" itself is called with "brasl %r0,ftrace_caller". So, to fix this issue simply always save traced function caller onto ftraced function stack frame. Reported-by: Sven Schnelle Signed-off-by: Vasily Gorbik --- arch/s390/kernel/mcount.S | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/s390/kernel/mcount.S b/arch/s390/kernel/mcount.S index 9e1660a6b9db6..c3597d2e2ae0e 100644 --- a/arch/s390/kernel/mcount.S +++ b/arch/s390/kernel/mcount.S @@ -35,6 +35,7 @@ EXPORT_SYMBOL(_mcount) ENTRY(ftrace_caller) .globl ftrace_regs_caller .set ftrace_regs_caller,ftrace_caller + stg %r14,(__SF_GPRS+8*8)(%r15) # save traced function caller lgr %r1,%r15 #if !(defined(CC_USING_HOTPATCH) || defined(CC_USING_NOP_MCOUNT)) aghi %r0,MCOUNT_RETURN_FIXUP -- GitLab From a1ec57c020201ba29608a5a3588832d07de1a518 Mon Sep 17 00:00:00 2001 From: Jose Abreu Date: Wed, 18 Dec 2019 23:55:01 +0100 Subject: [PATCH 1034/1437] net: stmmac: tc: Fix TAPRIO division operation For ARCHs that don't support 64 bits division we need to use the helpers. Fixes: b60189e0392f ("net: stmmac: Integrate EST with TAPRIO scheduler API") Signed-off-by: Jose Abreu Signed-off-by: David S. Miller --- drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c index 8ff8f9b9bb225..6c4686b775166 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c @@ -599,6 +599,7 @@ static int tc_setup_taprio(struct stmmac_priv *priv, struct timespec64 time; bool fpe = false; int i, ret = 0; + u64 ctr; if (!priv->dma_cap.estsel) return -EOPNOTSUPP; @@ -694,8 +695,9 @@ static int tc_setup_taprio(struct stmmac_priv *priv, priv->plat->est->btr[0] = (u32)time.tv_nsec; priv->plat->est->btr[1] = (u32)time.tv_sec; - priv->plat->est->ctr[0] = (u32)(qopt->cycle_time % NSEC_PER_SEC); - priv->plat->est->ctr[1] = (u32)(qopt->cycle_time / NSEC_PER_SEC); + ctr = qopt->cycle_time; + priv->plat->est->ctr[0] = do_div(ctr, NSEC_PER_SEC); + priv->plat->est->ctr[1] = (u32)ctr; if (fpe && !priv->dma_cap.fpesel) return -EOPNOTSUPP; -- GitLab From 84c92365b20a44c363b95390ea00dfbdd786f031 Mon Sep 17 00:00:00 2001 From: Chuhong Yuan Date: Mon, 18 Nov 2019 19:39:55 +0800 Subject: [PATCH 1035/1437] drm/exynos: gsc: add missed component_del The driver forgets to call component_del in remove to match component_add in probe. Add the missed call to fix it. Signed-off-by: Chuhong Yuan Signed-off-by: Inki Dae --- drivers/gpu/drm/exynos/exynos_drm_gsc.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpu/drm/exynos/exynos_drm_gsc.c b/drivers/gpu/drm/exynos/exynos_drm_gsc.c index 7ae087b0504df..88b6fcaa20be0 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_gsc.c +++ b/drivers/gpu/drm/exynos/exynos_drm_gsc.c @@ -1313,6 +1313,7 @@ static int gsc_remove(struct platform_device *pdev) { struct device *dev = &pdev->dev; + component_del(dev, &gsc_component_ops); pm_runtime_dont_use_autosuspend(dev); pm_runtime_disable(dev); -- GitLab From 1760eb689ed68c6746744aff2092bff57c78d907 Mon Sep 17 00:00:00 2001 From: Pavel Tatashin Date: Wed, 4 Dec 2019 10:40:38 -0500 Subject: [PATCH 1036/1437] tpm/tpm_ftpm_tee: add shutdown call back Add shutdown call back to close existing session with fTPM TA to support kexec scenario. Add parentheses to function names in comments as specified in kdoc. Signed-off-by: Thirupathaiah Annapureddy Signed-off-by: Pavel Tatashin Reviewed-by: Jarkko Sakkinen Tested-by: Sasha Levin Signed-off-by: Jarkko Sakkinen --- drivers/char/tpm/tpm_ftpm_tee.c | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/drivers/char/tpm/tpm_ftpm_tee.c b/drivers/char/tpm/tpm_ftpm_tee.c index 6640a14dbe48c..22bf553ccf9df 100644 --- a/drivers/char/tpm/tpm_ftpm_tee.c +++ b/drivers/char/tpm/tpm_ftpm_tee.c @@ -32,7 +32,7 @@ static const uuid_t ftpm_ta_uuid = 0x82, 0xCB, 0x34, 0x3F, 0xB7, 0xF3, 0x78, 0x96); /** - * ftpm_tee_tpm_op_recv - retrieve fTPM response. + * ftpm_tee_tpm_op_recv() - retrieve fTPM response. * @chip: the tpm_chip description as specified in driver/char/tpm/tpm.h. * @buf: the buffer to store data. * @count: the number of bytes to read. @@ -61,7 +61,7 @@ static int ftpm_tee_tpm_op_recv(struct tpm_chip *chip, u8 *buf, size_t count) } /** - * ftpm_tee_tpm_op_send - send TPM commands through the TEE shared memory. + * ftpm_tee_tpm_op_send() - send TPM commands through the TEE shared memory. * @chip: the tpm_chip description as specified in driver/char/tpm/tpm.h * @buf: the buffer to send. * @len: the number of bytes to send. @@ -208,7 +208,7 @@ static int ftpm_tee_match(struct tee_ioctl_version_data *ver, const void *data) } /** - * ftpm_tee_probe - initialize the fTPM + * ftpm_tee_probe() - initialize the fTPM * @pdev: the platform_device description. * * Return: @@ -298,7 +298,7 @@ out_tee_session: } /** - * ftpm_tee_remove - remove the TPM device + * ftpm_tee_remove() - remove the TPM device * @pdev: the platform_device description. * * Return: @@ -328,6 +328,19 @@ static int ftpm_tee_remove(struct platform_device *pdev) return 0; } +/** + * ftpm_tee_shutdown() - shutdown the TPM device + * @pdev: the platform_device description. + */ +static void ftpm_tee_shutdown(struct platform_device *pdev) +{ + struct ftpm_tee_private *pvt_data = dev_get_drvdata(&pdev->dev); + + tee_shm_free(pvt_data->shm); + tee_client_close_session(pvt_data->ctx, pvt_data->session); + tee_client_close_context(pvt_data->ctx); +} + static const struct of_device_id of_ftpm_tee_ids[] = { { .compatible = "microsoft,ftpm" }, { } @@ -341,6 +354,7 @@ static struct platform_driver ftpm_tee_driver = { }, .probe = ftpm_tee_probe, .remove = ftpm_tee_remove, + .shutdown = ftpm_tee_shutdown, }; module_platform_driver(ftpm_tee_driver); -- GitLab From 7c43e0d6a526e7734eb854fe242886f52ccd06ac Mon Sep 17 00:00:00 2001 From: Andrii Nakryiko Date: Wed, 18 Dec 2019 13:43:14 -0800 Subject: [PATCH 1037/1437] bpftool: Simplify format string to not use positional args Change format string referring to just single argument out of two available. Some versions of libc can reject such format string. Reported-by: Nikita Shirokov Signed-off-by: Andrii Nakryiko Signed-off-by: Alexei Starovoitov Acked-by: Yonghong Song Link: https://lore.kernel.org/bpf/20191218214314.2403729-1-andriin@fb.com --- tools/bpf/bpftool/gen.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/bpf/bpftool/gen.c b/tools/bpf/bpftool/gen.c index 8d93c8f90f825..851c465f99dcd 100644 --- a/tools/bpf/bpftool/gen.c +++ b/tools/bpf/bpftool/gen.c @@ -567,9 +567,9 @@ static int do_skeleton(int argc, char **argv) return -1; \n\ } \n\ \n\ - #endif /* %2$s */ \n\ + #endif /* %s */ \n\ ", - obj_name, header_guard); + header_guard); err = 0; out: bpf_object__close(obj); -- GitLab From dacce6412e09b5dce19514e2c4e2a8aab0eb217f Mon Sep 17 00:00:00 2001 From: Andrii Nakryiko Date: Wed, 18 Dec 2019 14:17:07 -0800 Subject: [PATCH 1038/1437] bpftool: Work-around rst2man conversion bug Work-around what appears to be a bug in rst2man convertion tool, used to create man pages out of reStructureText-formatted documents. If text line starts with dot, rst2man will put it in resulting man file verbatim. This seems to cause man tool to interpret it as a directive/command (e.g., `.bs`), and subsequently not render entire line because it's unrecognized one. Enclose '.xxx' words in extra formatting to work around. Fixes: cb21ac588546 ("bpftool: Add gen subcommand manpage") Reported-by: Alexei Starovoitov Signed-off-by: Andrii Nakryiko Signed-off-by: Alexei Starovoitov Acked-by: Yonghong Song Date: Wed, 18 Dec 2019 12:57:47 -0800 Subject: [PATCH 1039/1437] bpf: Allow to change skb mark in test_run allow to pass skb's mark field into bpf_prog_test_run ctx for BPF_PROG_TYPE_SCHED_CLS prog type. that would allow to test bpf programs which are doing decision based on this field Signed-off-by: Nikita V. Shirokov Signed-off-by: Alexei Starovoitov --- net/bpf/test_run.c | 10 +++++++++- tools/testing/selftests/bpf/prog_tests/skb_ctx.c | 5 +++++ tools/testing/selftests/bpf/progs/test_skb_ctx.c | 1 + 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/net/bpf/test_run.c b/net/bpf/test_run.c index 93a9c87787e07..d555c0d8657da 100644 --- a/net/bpf/test_run.c +++ b/net/bpf/test_run.c @@ -251,7 +251,13 @@ static int convert___skb_to_skb(struct sk_buff *skb, struct __sk_buff *__skb) return 0; /* make sure the fields we don't use are zeroed */ - if (!range_is_zero(__skb, 0, offsetof(struct __sk_buff, priority))) + if (!range_is_zero(__skb, 0, offsetof(struct __sk_buff, mark))) + return -EINVAL; + + /* mark is allowed */ + + if (!range_is_zero(__skb, offsetofend(struct __sk_buff, mark), + offsetof(struct __sk_buff, priority))) return -EINVAL; /* priority is allowed */ @@ -274,6 +280,7 @@ static int convert___skb_to_skb(struct sk_buff *skb, struct __sk_buff *__skb) sizeof(struct __sk_buff))) return -EINVAL; + skb->mark = __skb->mark; skb->priority = __skb->priority; skb->tstamp = __skb->tstamp; memcpy(&cb->data, __skb->cb, QDISC_CB_PRIV_LEN); @@ -301,6 +308,7 @@ static void convert_skb_to___skb(struct sk_buff *skb, struct __sk_buff *__skb) if (!__skb) return; + __skb->mark = skb->mark; __skb->priority = skb->priority; __skb->tstamp = skb->tstamp; memcpy(__skb->cb, &cb->data, QDISC_CB_PRIV_LEN); diff --git a/tools/testing/selftests/bpf/prog_tests/skb_ctx.c b/tools/testing/selftests/bpf/prog_tests/skb_ctx.c index edf5e8c7d400d..c6d6b685a9468 100644 --- a/tools/testing/selftests/bpf/prog_tests/skb_ctx.c +++ b/tools/testing/selftests/bpf/prog_tests/skb_ctx.c @@ -13,6 +13,7 @@ void test_skb_ctx(void) .tstamp = 7, .wire_len = 100, .gso_segs = 8, + .mark = 9, }; struct bpf_prog_test_run_attr tattr = { .data_in = &pkt_v4, @@ -93,4 +94,8 @@ void test_skb_ctx(void) "ctx_out_tstamp", "skb->tstamp == %lld, expected %d\n", skb.tstamp, 8); + CHECK_ATTR(skb.mark != 10, + "ctx_out_mark", + "skb->mark == %u, expected %d\n", + skb.mark, 10); } diff --git a/tools/testing/selftests/bpf/progs/test_skb_ctx.c b/tools/testing/selftests/bpf/progs/test_skb_ctx.c index 534fbf9a73445..e18da87fe84fa 100644 --- a/tools/testing/selftests/bpf/progs/test_skb_ctx.c +++ b/tools/testing/selftests/bpf/progs/test_skb_ctx.c @@ -17,6 +17,7 @@ int process(struct __sk_buff *skb) } skb->priority++; skb->tstamp++; + skb->mark++; if (skb->wire_len != 100) return 1; -- GitLab From d69587062c347314a019cf6ee27f2e4b494868e1 Mon Sep 17 00:00:00 2001 From: Andrii Nakryiko Date: Wed, 18 Dec 2019 14:50:39 -0800 Subject: [PATCH 1040/1437] libbpf: Add bpf_link__disconnect() API to preserve underlying BPF resource There are cases in which BPF resource (program, map, etc) has to outlive userspace program that "installed" it in the system in the first place. When BPF program is attached, libbpf returns bpf_link object, which is supposed to be destroyed after no longer necessary through bpf_link__destroy() API. Currently, bpf_link destruction causes both automatic detachment and frees up any resources allocated to for bpf_link in-memory representation. This is inconvenient for the case described above because of coupling of detachment and resource freeing. This patch introduces bpf_link__disconnect() API call, which marks bpf_link as disconnected from its underlying BPF resouces. This means that when bpf_link is destroyed later, all its memory resources will be freed, but BPF resource itself won't be detached. This design allows to follow strict and resource-leak-free design by default, while giving easy and straightforward way for user code to opt for keeping BPF resource attached beyond lifetime of a bpf_link. For some BPF programs (i.e., FS-based tracepoints, kprobes, raw tracepoint, etc), user has to make sure to pin BPF program to prevent kernel to automatically detach it on process exit. This should typically be achived by pinning BPF program (or map in some cases) in BPF FS. Signed-off-by: Andrii Nakryiko Signed-off-by: Alexei Starovoitov Acked-by: Martin KaFai Lau Link: https://lore.kernel.org/bpf/20191218225039.2668205-1-andriin@fb.com --- tools/lib/bpf/libbpf.c | 40 ++++++++++++++++++++++++++++++---------- tools/lib/bpf/libbpf.h | 1 + tools/lib/bpf/libbpf.map | 1 + 3 files changed, 32 insertions(+), 10 deletions(-) diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c index 906bbbf7b2e42..2a341a5b9f634 100644 --- a/tools/lib/bpf/libbpf.c +++ b/tools/lib/bpf/libbpf.c @@ -6245,17 +6245,37 @@ int bpf_prog_load_xattr(const struct bpf_prog_load_attr *attr, } struct bpf_link { + int (*detach)(struct bpf_link *link); int (*destroy)(struct bpf_link *link); + bool disconnected; }; +/* Release "ownership" of underlying BPF resource (typically, BPF program + * attached to some BPF hook, e.g., tracepoint, kprobe, etc). Disconnected + * link, when destructed through bpf_link__destroy() call won't attempt to + * detach/unregisted that BPF resource. This is useful in situations where, + * say, attached BPF program has to outlive userspace program that attached it + * in the system. Depending on type of BPF program, though, there might be + * additional steps (like pinning BPF program in BPF FS) necessary to ensure + * exit of userspace program doesn't trigger automatic detachment and clean up + * inside the kernel. + */ +void bpf_link__disconnect(struct bpf_link *link) +{ + link->disconnected = true; +} + int bpf_link__destroy(struct bpf_link *link) { - int err; + int err = 0; if (!link) return 0; - err = link->destroy(link); + if (!link->disconnected && link->detach) + err = link->detach(link); + if (link->destroy) + link->destroy(link); free(link); return err; @@ -6266,7 +6286,7 @@ struct bpf_link_fd { int fd; /* hook FD */ }; -static int bpf_link__destroy_perf_event(struct bpf_link *link) +static int bpf_link__detach_perf_event(struct bpf_link *link) { struct bpf_link_fd *l = (void *)link; int err; @@ -6298,10 +6318,10 @@ struct bpf_link *bpf_program__attach_perf_event(struct bpf_program *prog, return ERR_PTR(-EINVAL); } - link = malloc(sizeof(*link)); + link = calloc(1, sizeof(*link)); if (!link) return ERR_PTR(-ENOMEM); - link->link.destroy = &bpf_link__destroy_perf_event; + link->link.detach = &bpf_link__detach_perf_event; link->fd = pfd; if (ioctl(pfd, PERF_EVENT_IOC_SET_BPF, prog_fd) < 0) { @@ -6608,7 +6628,7 @@ out: return link; } -static int bpf_link__destroy_fd(struct bpf_link *link) +static int bpf_link__detach_fd(struct bpf_link *link) { struct bpf_link_fd *l = (void *)link; @@ -6629,10 +6649,10 @@ struct bpf_link *bpf_program__attach_raw_tracepoint(struct bpf_program *prog, return ERR_PTR(-EINVAL); } - link = malloc(sizeof(*link)); + link = calloc(1, sizeof(*link)); if (!link) return ERR_PTR(-ENOMEM); - link->link.destroy = &bpf_link__destroy_fd; + link->link.detach = &bpf_link__detach_fd; pfd = bpf_raw_tracepoint_open(tp_name, prog_fd); if (pfd < 0) { @@ -6668,10 +6688,10 @@ struct bpf_link *bpf_program__attach_trace(struct bpf_program *prog) return ERR_PTR(-EINVAL); } - link = malloc(sizeof(*link)); + link = calloc(1, sizeof(*link)); if (!link) return ERR_PTR(-ENOMEM); - link->link.destroy = &bpf_link__destroy_fd; + link->link.detach = &bpf_link__detach_fd; pfd = bpf_raw_tracepoint_open(NULL, prog_fd); if (pfd < 0) { diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h index f7084235bae95..ad8c1c1279334 100644 --- a/tools/lib/bpf/libbpf.h +++ b/tools/lib/bpf/libbpf.h @@ -215,6 +215,7 @@ LIBBPF_API void bpf_program__unload(struct bpf_program *prog); struct bpf_link; +LIBBPF_API void bpf_link__disconnect(struct bpf_link *link); LIBBPF_API int bpf_link__destroy(struct bpf_link *link); LIBBPF_API struct bpf_link * diff --git a/tools/lib/bpf/libbpf.map b/tools/lib/bpf/libbpf.map index 1376d992a703a..e3a471f38a715 100644 --- a/tools/lib/bpf/libbpf.map +++ b/tools/lib/bpf/libbpf.map @@ -212,6 +212,7 @@ LIBBPF_0.0.6 { LIBBPF_0.0.7 { global: btf_dump__emit_type_decl; + bpf_link__disconnect; bpf_object__find_program_by_name; bpf_object__attach_skeleton; bpf_object__destroy_skeleton; -- GitLab From 81bfdd087bf31a87c5ff25cc7004d5308954a35c Mon Sep 17 00:00:00 2001 From: Andrii Nakryiko Date: Wed, 18 Dec 2019 16:28:34 -0800 Subject: [PATCH 1041/1437] libbpf: Put Kconfig externs into .kconfig section Move Kconfig-provided externs into custom .kconfig section. Add __kconfig into bpf_helpers.h for user convenience. Update selftests accordingly. Suggested-by: Daniel Borkmann Signed-off-by: Andrii Nakryiko Signed-off-by: Alexei Starovoitov Link: https://lore.kernel.org/bpf/20191219002837.3074619-2-andriin@fb.com --- tools/bpf/bpftool/gen.c | 8 +-- tools/lib/bpf/bpf_helpers.h | 2 + tools/lib/bpf/libbpf.c | 58 +++++++++++-------- .../selftests/bpf/prog_tests/skeleton.c | 16 +++-- .../selftests/bpf/progs/test_core_extern.c | 20 +++---- .../selftests/bpf/progs/test_skeleton.c | 4 +- 6 files changed, 60 insertions(+), 48 deletions(-) diff --git a/tools/bpf/bpftool/gen.c b/tools/bpf/bpftool/gen.c index 851c465f99dcd..a14d8bc5d31d5 100644 --- a/tools/bpf/bpftool/gen.c +++ b/tools/bpf/bpftool/gen.c @@ -83,8 +83,8 @@ static const char *get_map_ident(const struct bpf_map *map) return "rodata"; else if (str_has_suffix(name, ".bss")) return "bss"; - else if (str_has_suffix(name, ".extern")) - return "externs"; /* extern is a C keyword */ + else if (str_has_suffix(name, ".kconfig")) + return "kconfig"; else return NULL; } @@ -112,8 +112,8 @@ static int codegen_datasec_def(struct bpf_object *obj, sec_ident = "bss"; else if (strcmp(sec_name, ".rodata") == 0) sec_ident = "rodata"; - else if (strcmp(sec_name, ".extern") == 0) - sec_ident = "externs"; /* extern is a C keyword */ + else if (strcmp(sec_name, ".kconfig") == 0) + sec_ident = "kconfig"; else return 0; diff --git a/tools/lib/bpf/bpf_helpers.h b/tools/lib/bpf/bpf_helpers.h index aa46700075e16..f69cc208778a2 100644 --- a/tools/lib/bpf/bpf_helpers.h +++ b/tools/lib/bpf/bpf_helpers.h @@ -53,4 +53,6 @@ enum libbpf_tristate { TRI_MODULE = 2, }; +#define __kconfig __attribute__((section(".kconfig"))) + #endif diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c index 2a341a5b9f634..0772eb3254e21 100644 --- a/tools/lib/bpf/libbpf.c +++ b/tools/lib/bpf/libbpf.c @@ -231,21 +231,21 @@ struct bpf_program { #define DATA_SEC ".data" #define BSS_SEC ".bss" #define RODATA_SEC ".rodata" -#define EXTERN_SEC ".extern" +#define KCONFIG_SEC ".kconfig" enum libbpf_map_type { LIBBPF_MAP_UNSPEC, LIBBPF_MAP_DATA, LIBBPF_MAP_BSS, LIBBPF_MAP_RODATA, - LIBBPF_MAP_EXTERN, + LIBBPF_MAP_KCONFIG, }; static const char * const libbpf_type_to_btf_name[] = { [LIBBPF_MAP_DATA] = DATA_SEC, [LIBBPF_MAP_BSS] = BSS_SEC, [LIBBPF_MAP_RODATA] = RODATA_SEC, - [LIBBPF_MAP_EXTERN] = EXTERN_SEC, + [LIBBPF_MAP_KCONFIG] = KCONFIG_SEC, }; struct bpf_map { @@ -305,7 +305,7 @@ struct bpf_object { char *kconfig_path; struct extern_desc *externs; int nr_extern; - int extern_map_idx; + int kconfig_map_idx; bool loaded; bool has_pseudo_calls; @@ -606,7 +606,7 @@ static struct bpf_object *bpf_object__new(const char *path, obj->efile.data_shndx = -1; obj->efile.rodata_shndx = -1; obj->efile.bss_shndx = -1; - obj->extern_map_idx = -1; + obj->kconfig_map_idx = -1; obj->kern_version = get_kernel_version(); obj->loaded = false; @@ -902,11 +902,25 @@ static size_t bpf_map_mmap_sz(const struct bpf_map *map) return map_sz; } +static char *internal_map_name(struct bpf_object *obj, + enum libbpf_map_type type) +{ + char map_name[BPF_OBJ_NAME_LEN]; + const char *sfx = libbpf_type_to_btf_name[type]; + int sfx_len = max((size_t)7, strlen(sfx)); + int pfx_len = min((size_t)BPF_OBJ_NAME_LEN - sfx_len - 1, + strlen(obj->name)); + + snprintf(map_name, sizeof(map_name), "%.*s%.*s", pfx_len, obj->name, + sfx_len, libbpf_type_to_btf_name[type]); + + return strdup(map_name); +} + static int bpf_object__init_internal_map(struct bpf_object *obj, enum libbpf_map_type type, int sec_idx, void *data, size_t data_sz) { - char map_name[BPF_OBJ_NAME_LEN]; struct bpf_map_def *def; struct bpf_map *map; int err; @@ -918,9 +932,7 @@ bpf_object__init_internal_map(struct bpf_object *obj, enum libbpf_map_type type, map->libbpf_type = type; map->sec_idx = sec_idx; map->sec_offset = 0; - snprintf(map_name, sizeof(map_name), "%.8s%.7s", obj->name, - libbpf_type_to_btf_name[type]); - map->name = strdup(map_name); + map->name = internal_map_name(obj, type); if (!map->name) { pr_warn("failed to alloc map name\n"); return -ENOMEM; @@ -931,12 +943,12 @@ bpf_object__init_internal_map(struct bpf_object *obj, enum libbpf_map_type type, def->key_size = sizeof(int); def->value_size = data_sz; def->max_entries = 1; - def->map_flags = type == LIBBPF_MAP_RODATA || type == LIBBPF_MAP_EXTERN + def->map_flags = type == LIBBPF_MAP_RODATA || type == LIBBPF_MAP_KCONFIG ? BPF_F_RDONLY_PROG : 0; def->map_flags |= BPF_F_MMAPABLE; pr_debug("map '%s' (global data): at sec_idx %d, offset %zu, flags %x.\n", - map_name, map->sec_idx, map->sec_offset, def->map_flags); + map->name, map->sec_idx, map->sec_offset, def->map_flags); map->mmaped = mmap(NULL, bpf_map_mmap_sz(map), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0); @@ -1232,7 +1244,7 @@ out: return err; } -static int bpf_object__init_extern_map(struct bpf_object *obj) +static int bpf_object__init_kconfig_map(struct bpf_object *obj) { struct extern_desc *last_ext; size_t map_sz; @@ -1244,13 +1256,13 @@ static int bpf_object__init_extern_map(struct bpf_object *obj) last_ext = &obj->externs[obj->nr_extern - 1]; map_sz = last_ext->data_off + last_ext->sz; - err = bpf_object__init_internal_map(obj, LIBBPF_MAP_EXTERN, + err = bpf_object__init_internal_map(obj, LIBBPF_MAP_KCONFIG, obj->efile.symbols_shndx, NULL, map_sz); if (err) return err; - obj->extern_map_idx = obj->nr_maps - 1; + obj->kconfig_map_idx = obj->nr_maps - 1; return 0; } @@ -1742,7 +1754,7 @@ static int bpf_object__init_maps(struct bpf_object *obj, err = bpf_object__init_user_maps(obj, strict); err = err ?: bpf_object__init_user_btf_maps(obj, strict, pin_root_path); err = err ?: bpf_object__init_global_data_maps(obj); - err = err ?: bpf_object__init_extern_map(obj); + err = err ?: bpf_object__init_kconfig_map(obj); if (err) return err; @@ -2269,9 +2281,9 @@ static int bpf_object__collect_externs(struct bpf_object *obj) i, ext->sym_idx, ext->data_off, ext->name); } - btf_id = btf__find_by_name(obj->btf, EXTERN_SEC); + btf_id = btf__find_by_name(obj->btf, KCONFIG_SEC); if (btf_id <= 0) { - pr_warn("no BTF info found for '%s' datasec\n", EXTERN_SEC); + pr_warn("no BTF info found for '%s' datasec\n", KCONFIG_SEC); return -ESRCH; } @@ -2361,7 +2373,7 @@ bpf_object__section_to_libbpf_map_type(const struct bpf_object *obj, int shndx) else if (shndx == obj->efile.rodata_shndx) return LIBBPF_MAP_RODATA; else if (shndx == obj->efile.symbols_shndx) - return LIBBPF_MAP_EXTERN; + return LIBBPF_MAP_KCONFIG; else return LIBBPF_MAP_UNSPEC; } @@ -2908,8 +2920,8 @@ bpf_object__populate_internal_map(struct bpf_object *obj, struct bpf_map *map) return err; } - /* Freeze .rodata and .extern map as read-only from syscall side. */ - if (map_type == LIBBPF_MAP_RODATA || map_type == LIBBPF_MAP_EXTERN) { + /* Freeze .rodata and .kconfig map as read-only from syscall side. */ + if (map_type == LIBBPF_MAP_RODATA || map_type == LIBBPF_MAP_KCONFIG) { err = bpf_map_freeze(map->fd); if (err) { err = -errno; @@ -4264,7 +4276,7 @@ bpf_program__relocate(struct bpf_program *prog, struct bpf_object *obj) break; case RELO_EXTERN: insn[0].src_reg = BPF_PSEUDO_MAP_VALUE; - insn[0].imm = obj->maps[obj->extern_map_idx].fd; + insn[0].imm = obj->maps[obj->kconfig_map_idx].fd; insn[1].imm = relo->sym_off; break; case RELO_CALL: @@ -4743,7 +4755,7 @@ static int bpf_object__resolve_externs(struct bpf_object *obj, if (obj->nr_extern == 0) return 0; - data = obj->maps[obj->extern_map_idx].mmaped; + data = obj->maps[obj->kconfig_map_idx].mmaped; for (i = 0; i < obj->nr_extern; i++) { ext = &obj->externs[i]; @@ -7546,7 +7558,7 @@ int bpf_object__open_skeleton(struct bpf_object_skeleton *s, } /* externs shouldn't be pre-setup from user code */ - if (mmaped && (*map)->libbpf_type != LIBBPF_MAP_EXTERN) + if (mmaped && (*map)->libbpf_type != LIBBPF_MAP_KCONFIG) *mmaped = (*map)->mmaped; } diff --git a/tools/testing/selftests/bpf/prog_tests/skeleton.c b/tools/testing/selftests/bpf/prog_tests/skeleton.c index ec6f2aec3853b..9264a27360182 100644 --- a/tools/testing/selftests/bpf/prog_tests/skeleton.c +++ b/tools/testing/selftests/bpf/prog_tests/skeleton.c @@ -15,20 +15,18 @@ void test_skeleton(void) int duration = 0, err; struct test_skeleton* skel; struct test_skeleton__bss *bss; - struct test_skeleton__externs *exts; + struct test_skeleton__kconfig *kcfg; skel = test_skeleton__open(); if (CHECK(!skel, "skel_open", "failed to open skeleton\n")) return; - printf("EXTERNS BEFORE: %p\n", skel->externs); - if (CHECK(skel->externs, "skel_externs", "externs are mmaped()!\n")) + if (CHECK(skel->kconfig, "skel_kconfig", "kconfig is mmaped()!\n")) goto cleanup; err = test_skeleton__load(skel); if (CHECK(err, "skel_load", "failed to load skeleton: %d\n", err)) goto cleanup; - printf("EXTERNS AFTER: %p\n", skel->externs); bss = skel->bss; bss->in1 = 1; @@ -37,7 +35,7 @@ void test_skeleton(void) bss->in4 = 4; bss->in5.a = 5; bss->in5.b = 6; - exts = skel->externs; + kcfg = skel->kconfig; err = test_skeleton__attach(skel); if (CHECK(err, "skel_attach", "skeleton attach failed: %d\n", err)) @@ -55,10 +53,10 @@ void test_skeleton(void) CHECK(bss->handler_out5.b != 6, "res6", "got %lld != exp %d\n", bss->handler_out5.b, 6); - CHECK(bss->bpf_syscall != exts->CONFIG_BPF_SYSCALL, "ext1", - "got %d != exp %d\n", bss->bpf_syscall, exts->CONFIG_BPF_SYSCALL); - CHECK(bss->kern_ver != exts->LINUX_KERNEL_VERSION, "ext2", - "got %d != exp %d\n", bss->kern_ver, exts->LINUX_KERNEL_VERSION); + CHECK(bss->bpf_syscall != kcfg->CONFIG_BPF_SYSCALL, "ext1", + "got %d != exp %d\n", bss->bpf_syscall, kcfg->CONFIG_BPF_SYSCALL); + CHECK(bss->kern_ver != kcfg->LINUX_KERNEL_VERSION, "ext2", + "got %d != exp %d\n", bss->kern_ver, kcfg->LINUX_KERNEL_VERSION); cleanup: test_skeleton__destroy(skel); diff --git a/tools/testing/selftests/bpf/progs/test_core_extern.c b/tools/testing/selftests/bpf/progs/test_core_extern.c index e12f09f9e881a..9bfc91d9d0044 100644 --- a/tools/testing/selftests/bpf/progs/test_core_extern.c +++ b/tools/testing/selftests/bpf/progs/test_core_extern.c @@ -10,16 +10,16 @@ /* non-existing BPF helper, to test dead code elimination */ static int (*bpf_missing_helper)(const void *arg1, int arg2) = (void *) 999; -extern int LINUX_KERNEL_VERSION; -extern bool CONFIG_BPF_SYSCALL; /* strong */ -extern enum libbpf_tristate CONFIG_TRISTATE __weak; -extern bool CONFIG_BOOL __weak; -extern char CONFIG_CHAR __weak; -extern uint16_t CONFIG_USHORT __weak; -extern int CONFIG_INT __weak; -extern uint64_t CONFIG_ULONG __weak; -extern const char CONFIG_STR[8] __weak; -extern uint64_t CONFIG_MISSING __weak; +extern int LINUX_KERNEL_VERSION __kconfig; +extern bool CONFIG_BPF_SYSCALL __kconfig; /* strong */ +extern enum libbpf_tristate CONFIG_TRISTATE __kconfig __weak; +extern bool CONFIG_BOOL __kconfig __weak; +extern char CONFIG_CHAR __kconfig __weak; +extern uint16_t CONFIG_USHORT __kconfig __weak; +extern int CONFIG_INT __kconfig __weak; +extern uint64_t CONFIG_ULONG __kconfig __weak; +extern const char CONFIG_STR[8] __kconfig __weak; +extern uint64_t CONFIG_MISSING __kconfig __weak; uint64_t kern_ver = -1; uint64_t bpf_syscall = -1; diff --git a/tools/testing/selftests/bpf/progs/test_skeleton.c b/tools/testing/selftests/bpf/progs/test_skeleton.c index 9caa44758ea26..4f69aac5635f1 100644 --- a/tools/testing/selftests/bpf/progs/test_skeleton.c +++ b/tools/testing/selftests/bpf/progs/test_skeleton.c @@ -21,8 +21,8 @@ char out3 = 0; long long out4 = 0; int out1 = 0; -extern bool CONFIG_BPF_SYSCALL; -extern int LINUX_KERNEL_VERSION; +extern bool CONFIG_BPF_SYSCALL __kconfig; +extern int LINUX_KERNEL_VERSION __kconfig; bool bpf_syscall = 0; int kern_ver = 0; -- GitLab From 8601fd422148a8f7ff5f7eaf75b6703d5166332c Mon Sep 17 00:00:00 2001 From: Andrii Nakryiko Date: Wed, 18 Dec 2019 16:28:35 -0800 Subject: [PATCH 1042/1437] libbpf: Allow to augment system Kconfig through extra optional config Instead of all or nothing approach of overriding Kconfig file location, allow to extend it with extra values and override chosen subset of values though optional user-provided extra config, passed as a string through open options' .kconfig option. If same config key is present in both user-supplied config and Kconfig, user-supplied one wins. This allows applications to more easily test various conditions despite host kernel's real configuration. If all of BPF object's __kconfig externs are satisfied from user-supplied config, system Kconfig won't be read at all. Simplify selftests by not needing to create temporary Kconfig files. Suggested-by: Alexei Starovoitov Signed-off-by: Andrii Nakryiko Signed-off-by: Alexei Starovoitov Link: https://lore.kernel.org/bpf/20191219002837.3074619-3-andriin@fb.com --- tools/lib/bpf/libbpf.c | 204 +++++++++++------- tools/lib/bpf/libbpf.h | 8 +- .../selftests/bpf/prog_tests/core_extern.c | 32 +-- 3 files changed, 132 insertions(+), 112 deletions(-) diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c index 0772eb3254e21..7b74382c7977e 100644 --- a/tools/lib/bpf/libbpf.c +++ b/tools/lib/bpf/libbpf.c @@ -302,7 +302,7 @@ struct bpf_object { size_t nr_maps; size_t maps_cap; - char *kconfig_path; + char *kconfig; struct extern_desc *externs; int nr_extern; int kconfig_map_idx; @@ -1149,98 +1149,129 @@ static int set_ext_value_num(struct extern_desc *ext, void *ext_val, return 0; } -static int bpf_object__read_kernel_config(struct bpf_object *obj, - const char *config_path, - void *data) +static int bpf_object__process_kconfig_line(struct bpf_object *obj, + char *buf, void *data) { - char buf[PATH_MAX], *sep, *value; struct extern_desc *ext; + char *sep, *value; int len, err = 0; void *ext_val; __u64 num; - gzFile file; - if (config_path) { - file = gzopen(config_path, "r"); - } else { - struct utsname uts; + if (strncmp(buf, "CONFIG_", 7)) + return 0; - uname(&uts); - len = snprintf(buf, PATH_MAX, "/boot/config-%s", uts.release); - if (len < 0) - return -EINVAL; - else if (len >= PATH_MAX) - return -ENAMETOOLONG; - /* gzopen also accepts uncompressed files. */ - file = gzopen(buf, "r"); - if (!file) - file = gzopen("/proc/config.gz", "r"); + sep = strchr(buf, '='); + if (!sep) { + pr_warn("failed to parse '%s': no separator\n", buf); + return -EINVAL; + } + + /* Trim ending '\n' */ + len = strlen(buf); + if (buf[len - 1] == '\n') + buf[len - 1] = '\0'; + /* Split on '=' and ensure that a value is present. */ + *sep = '\0'; + if (!sep[1]) { + *sep = '='; + pr_warn("failed to parse '%s': no value\n", buf); + return -EINVAL; + } + + ext = find_extern_by_name(obj, buf); + if (!ext || ext->is_set) + return 0; + + ext_val = data + ext->data_off; + value = sep + 1; + + switch (*value) { + case 'y': case 'n': case 'm': + err = set_ext_value_tri(ext, ext_val, *value); + break; + case '"': + err = set_ext_value_str(ext, ext_val, value); + break; + default: + /* assume integer */ + err = parse_u64(value, &num); + if (err) { + pr_warn("extern %s=%s should be integer\n", + ext->name, value); + return err; + } + err = set_ext_value_num(ext, ext_val, num); + break; } + if (err) + return err; + pr_debug("extern %s=%s\n", ext->name, value); + return 0; +} + +static int bpf_object__read_kconfig_file(struct bpf_object *obj, void *data) +{ + char buf[PATH_MAX]; + struct utsname uts; + int len, err = 0; + gzFile file; + + uname(&uts); + len = snprintf(buf, PATH_MAX, "/boot/config-%s", uts.release); + if (len < 0) + return -EINVAL; + else if (len >= PATH_MAX) + return -ENAMETOOLONG; + + /* gzopen also accepts uncompressed files. */ + file = gzopen(buf, "r"); + if (!file) + file = gzopen("/proc/config.gz", "r"); + if (!file) { - pr_warn("failed to read kernel config at '%s'\n", config_path); + pr_warn("failed to open system Kconfig\n"); return -ENOENT; } while (gzgets(file, buf, sizeof(buf))) { - if (strncmp(buf, "CONFIG_", 7)) - continue; - - sep = strchr(buf, '='); - if (!sep) { - err = -EINVAL; - pr_warn("failed to parse '%s': no separator\n", buf); - goto out; - } - /* Trim ending '\n' */ - len = strlen(buf); - if (buf[len - 1] == '\n') - buf[len - 1] = '\0'; - /* Split on '=' and ensure that a value is present. */ - *sep = '\0'; - if (!sep[1]) { - err = -EINVAL; - *sep = '='; - pr_warn("failed to parse '%s': no value\n", buf); + err = bpf_object__process_kconfig_line(obj, buf, data); + if (err) { + pr_warn("error parsing system Kconfig line '%s': %d\n", + buf, err); goto out; } + } - ext = find_extern_by_name(obj, buf); - if (!ext) - continue; - if (ext->is_set) { - err = -EINVAL; - pr_warn("re-defining extern '%s' not allowed\n", buf); - goto out; - } +out: + gzclose(file); + return err; +} - ext_val = data + ext->data_off; - value = sep + 1; +static int bpf_object__read_kconfig_mem(struct bpf_object *obj, + const char *config, void *data) +{ + char buf[PATH_MAX]; + int err = 0; + FILE *file; - switch (*value) { - case 'y': case 'n': case 'm': - err = set_ext_value_tri(ext, ext_val, *value); - break; - case '"': - err = set_ext_value_str(ext, ext_val, value); - break; - default: - /* assume integer */ - err = parse_u64(value, &num); - if (err) { - pr_warn("extern %s=%s should be integer\n", - ext->name, value); - goto out; - } - err = set_ext_value_num(ext, ext_val, num); + file = fmemopen((void *)config, strlen(config), "r"); + if (!file) { + err = -errno; + pr_warn("failed to open in-memory Kconfig: %d\n", err); + return err; + } + + while (fgets(buf, sizeof(buf), file)) { + err = bpf_object__process_kconfig_line(obj, buf, data); + if (err) { + pr_warn("error parsing in-memory Kconfig line '%s': %d\n", + buf, err); break; } - if (err) - goto out; - pr_debug("extern %s=%s\n", ext->name, value); } -out: - gzclose(file); + fclose(file); return err; } @@ -4567,7 +4598,7 @@ static struct bpf_object * __bpf_object__open(const char *path, const void *obj_buf, size_t obj_buf_sz, const struct bpf_object_open_opts *opts) { - const char *obj_name, *kconfig_path; + const char *obj_name, *kconfig; struct bpf_program *prog; struct bpf_object *obj; char tmp_name[64]; @@ -4599,10 +4630,10 @@ __bpf_object__open(const char *path, const void *obj_buf, size_t obj_buf_sz, return obj; obj->relaxed_core_relocs = OPTS_GET(opts, relaxed_core_relocs, false); - kconfig_path = OPTS_GET(opts, kconfig_path, NULL); - if (kconfig_path) { - obj->kconfig_path = strdup(kconfig_path); - if (!obj->kconfig_path) + kconfig = OPTS_GET(opts, kconfig, NULL); + if (kconfig) { + obj->kconfig = strdup(kconfig); + if (!obj->kconfig) return ERR_PTR(-ENOMEM); } @@ -4745,7 +4776,7 @@ static int bpf_object__sanitize_maps(struct bpf_object *obj) } static int bpf_object__resolve_externs(struct bpf_object *obj, - const char *config_path) + const char *extra_kconfig) { bool need_config = false; struct extern_desc *ext; @@ -4779,8 +4810,21 @@ static int bpf_object__resolve_externs(struct bpf_object *obj, return -EINVAL; } } + if (need_config && extra_kconfig) { + err = bpf_object__read_kconfig_mem(obj, extra_kconfig, data); + if (err) + return -EINVAL; + need_config = false; + for (i = 0; i < obj->nr_extern; i++) { + ext = &obj->externs[i]; + if (!ext->is_set) { + need_config = true; + break; + } + } + } if (need_config) { - err = bpf_object__read_kernel_config(obj, config_path, data); + err = bpf_object__read_kconfig_file(obj, data); if (err) return -EINVAL; } @@ -4818,7 +4862,7 @@ int bpf_object__load_xattr(struct bpf_object_load_attr *attr) obj->loaded = true; err = bpf_object__probe_caps(obj); - err = err ? : bpf_object__resolve_externs(obj, obj->kconfig_path); + err = err ? : bpf_object__resolve_externs(obj, obj->kconfig); err = err ? : bpf_object__sanitize_and_load_btf(obj); err = err ? : bpf_object__sanitize_maps(obj); err = err ? : bpf_object__create_maps(obj); @@ -5412,7 +5456,7 @@ void bpf_object__close(struct bpf_object *obj) zfree(&map->pin_path); } - zfree(&obj->kconfig_path); + zfree(&obj->kconfig); zfree(&obj->externs); obj->nr_extern = 0; diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h index ad8c1c1279334..fe592ef48f1bf 100644 --- a/tools/lib/bpf/libbpf.h +++ b/tools/lib/bpf/libbpf.h @@ -85,12 +85,12 @@ struct bpf_object_open_opts { */ const char *pin_root_path; __u32 attach_prog_fd; - /* kernel config file path override (for CONFIG_ externs); can point - * to either uncompressed text file or .gz file + /* Additional kernel config content that augments and overrides + * system Kconfig for CONFIG_xxx externs. */ - const char *kconfig_path; + const char *kconfig; }; -#define bpf_object_open_opts__last_field kconfig_path +#define bpf_object_open_opts__last_field kconfig LIBBPF_API struct bpf_object *bpf_object__open(const char *path); LIBBPF_API struct bpf_object * diff --git a/tools/testing/selftests/bpf/prog_tests/core_extern.c b/tools/testing/selftests/bpf/prog_tests/core_extern.c index 5f03dc1de29e6..b093787e94489 100644 --- a/tools/testing/selftests/bpf/prog_tests/core_extern.c +++ b/tools/testing/selftests/bpf/prog_tests/core_extern.c @@ -23,19 +23,13 @@ static uint32_t get_kernel_version(void) static struct test_case { const char *name; const char *cfg; - const char *cfg_path; bool fails; struct test_core_extern__data data; } test_cases[] = { - { .name = "default search path", .cfg_path = NULL, - .data = { .bpf_syscall = true } }, - { .name = "/proc/config.gz", .cfg_path = "/proc/config.gz", - .data = { .bpf_syscall = true } }, - { .name = "missing config", .fails = true, - .cfg_path = "/proc/invalid-config.gz" }, + { .name = "default search path", .data = { .bpf_syscall = true } }, { .name = "custom values", - .cfg = "CONFIG_BPF_SYSCALL=y\n" + .cfg = "CONFIG_BPF_SYSCALL=n\n" "CONFIG_TRISTATE=m\n" "CONFIG_BOOL=y\n" "CONFIG_CHAR=100\n" @@ -45,7 +39,7 @@ static struct test_case { "CONFIG_STR=\"abracad\"\n" "CONFIG_MISSING=0", .data = { - .bpf_syscall = true, + .bpf_syscall = false, .tristate_val = TRI_MODULE, .bool_val = true, .char_val = 100, @@ -133,30 +127,14 @@ void test_core_extern(void) int n = sizeof(*skel->data) / sizeof(uint64_t); for (i = 0; i < ARRAY_SIZE(test_cases); i++) { - char tmp_cfg_path[] = "/tmp/test_core_extern_cfg.XXXXXX"; struct test_case *t = &test_cases[i]; DECLARE_LIBBPF_OPTS(bpf_object_open_opts, opts, - .kconfig_path = t->cfg_path, + .kconfig = t->cfg, ); if (!test__start_subtest(t->name)) continue; - if (t->cfg) { - size_t n = strlen(t->cfg) + 1; - int fd = mkstemp(tmp_cfg_path); - int written; - - if (CHECK(fd < 0, "mkstemp", "errno: %d\n", errno)) - continue; - printf("using '%s' as config file\n", tmp_cfg_path); - written = write(fd, t->cfg, n); - close(fd); - if (CHECK_FAIL(written != n)) - goto cleanup; - opts.kconfig_path = tmp_cfg_path; - } - skel = test_core_extern__open_opts(&opts); if (CHECK(!skel, "skel_open", "skeleton open failed\n")) goto cleanup; @@ -185,8 +163,6 @@ void test_core_extern(void) j, exp[j], got[j]); } cleanup: - if (t->cfg) - unlink(tmp_cfg_path); test_core_extern__destroy(skel); skel = NULL; } -- GitLab From 630628cb7dc39780660d8fcedc66e0298a82f9da Mon Sep 17 00:00:00 2001 From: Andrii Nakryiko Date: Wed, 18 Dec 2019 16:28:36 -0800 Subject: [PATCH 1043/1437] libbpf: BTF is required when externs are present BTF is required to get type information about extern variables. Signed-off-by: Andrii Nakryiko Signed-off-by: Alexei Starovoitov Link: https://lore.kernel.org/bpf/20191219002837.3074619-4-andriin@fb.com --- tools/lib/bpf/libbpf.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c index 7b74382c7977e..6340b81b555b8 100644 --- a/tools/lib/bpf/libbpf.c +++ b/tools/lib/bpf/libbpf.c @@ -1887,7 +1887,8 @@ static void bpf_object__sanitize_btf_ext(struct bpf_object *obj) static bool bpf_object__is_btf_mandatory(const struct bpf_object *obj) { - return obj->efile.btf_maps_shndx >= 0; + return obj->efile.btf_maps_shndx >= 0 || + obj->nr_extern > 0; } static int bpf_object__init_btf(struct bpf_object *obj, -- GitLab From 8d20c39f063ed33012d7461c77fd5b89665e5163 Mon Sep 17 00:00:00 2001 From: Matthias Kaehlcke Date: Wed, 4 Dec 2019 12:04:12 -0800 Subject: [PATCH 1044/1437] clk: qcom: gcc-sc7180: Fix setting flag for votable GDSCs Commit 17269568f7267 ("clk: qcom: Add Global Clock controller (GCC) driver for SC7180") sets the VOTABLE flag in .pwrsts, but it needs to be set in .flags, fix this. Fixes: 17269568f7267 ("clk: qcom: Add Global Clock controller (GCC) driver for SC7180") Signed-off-by: Matthias Kaehlcke Link: https://lkml.kernel.org/r/20191204120341.1.I9971817e83ee890d1096c43c5a6ce6ced53d5bd3@changeid Signed-off-by: Stephen Boyd --- drivers/clk/qcom/gcc-sc7180.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/clk/qcom/gcc-sc7180.c b/drivers/clk/qcom/gcc-sc7180.c index 38424e63bcae2..7f59fb8da0337 100644 --- a/drivers/clk/qcom/gcc-sc7180.c +++ b/drivers/clk/qcom/gcc-sc7180.c @@ -2186,7 +2186,8 @@ static struct gdsc hlos1_vote_mmnoc_mmu_tbu_hf0_gdsc = { .pd = { .name = "hlos1_vote_mmnoc_mmu_tbu_hf0_gdsc", }, - .pwrsts = PWRSTS_OFF_ON | VOTABLE, + .pwrsts = PWRSTS_OFF_ON, + .flags = VOTABLE, }; static struct gdsc hlos1_vote_mmnoc_mmu_tbu_sf_gdsc = { @@ -2194,7 +2195,8 @@ static struct gdsc hlos1_vote_mmnoc_mmu_tbu_sf_gdsc = { .pd = { .name = "hlos1_vote_mmnoc_mmu_tbu_sf_gdsc", }, - .pwrsts = PWRSTS_OFF_ON | VOTABLE, + .pwrsts = PWRSTS_OFF_ON, + .flags = VOTABLE, }; static struct gdsc *gcc_sc7180_gdscs[] = { -- GitLab From 781d8cea68ac41d11a80df2a5f5babd584f86447 Mon Sep 17 00:00:00 2001 From: Jeffrey Hugo Date: Tue, 17 Dec 2019 09:19:05 -0800 Subject: [PATCH 1045/1437] clk: qcom: Avoid SMMU/cx gdsc corner cases Mark the msm8998 cpu CX gdsc as votable and use the hw control to avoid corner cases with SMMU per hardware documentation. Fixes: 3f7df5baa259 ("clk: qcom: Add MSM8998 GPU Clock Controller (GPUCC) driver") Signed-off-by: Jeffrey Hugo Link: https://lkml.kernel.org/r/20191217171905.5619-1-jeffrey.l.hugo@gmail.com Signed-off-by: Stephen Boyd --- drivers/clk/qcom/gpucc-msm8998.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/clk/qcom/gpucc-msm8998.c b/drivers/clk/qcom/gpucc-msm8998.c index e5e2492b20c5e..9b3923af02a14 100644 --- a/drivers/clk/qcom/gpucc-msm8998.c +++ b/drivers/clk/qcom/gpucc-msm8998.c @@ -242,10 +242,12 @@ static struct clk_branch gfx3d_isense_clk = { static struct gdsc gpu_cx_gdsc = { .gdscr = 0x1004, + .gds_hw_ctrl = 0x1008, .pd = { .name = "gpu_cx", }, .pwrsts = PWRSTS_OFF_ON, + .flags = VOTABLE, }; static struct gdsc gpu_gx_gdsc = { -- GitLab From f667216c5c7c967c3e568cdddefb51fe606bfe26 Mon Sep 17 00:00:00 2001 From: Yangbo Lu Date: Thu, 19 Dec 2019 11:23:35 +0800 Subject: [PATCH 1046/1437] mmc: sdhci-of-esdhc: re-implement erratum A-009204 workaround The erratum A-009204 workaround patch was reverted because of incorrect implementation. 8b6dc6b mmc: sdhci-of-esdhc: Revert "mmc: sdhci-of-esdhc: add erratum A-009204 support" This patch is to re-implement the workaround (add a 5 ms delay before setting SYSCTL[RSTD] to make sure all the DMA transfers are finished). Signed-off-by: Yangbo Lu Link: https://lore.kernel.org/r/20191219032335.26528-1-yangbo.lu@nxp.com Fixes: 5dd195522562 ("mmc: sdhci-of-esdhc: add erratum A-009204 support") Cc: stable@vger.kernel.org Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-of-esdhc.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/mmc/host/sdhci-of-esdhc.c b/drivers/mmc/host/sdhci-of-esdhc.c index 4ca640e6fd557..500f70a6ee42f 100644 --- a/drivers/mmc/host/sdhci-of-esdhc.c +++ b/drivers/mmc/host/sdhci-of-esdhc.c @@ -80,6 +80,7 @@ struct sdhci_esdhc { bool quirk_tuning_erratum_type1; bool quirk_tuning_erratum_type2; bool quirk_ignore_data_inhibit; + bool quirk_delay_before_data_reset; bool in_sw_tuning; unsigned int peripheral_clock; const struct esdhc_clk_fixup *clk_fixup; @@ -759,6 +760,11 @@ static void esdhc_reset(struct sdhci_host *host, u8 mask) struct sdhci_esdhc *esdhc = sdhci_pltfm_priv(pltfm_host); u32 val; + if (esdhc->quirk_delay_before_data_reset && + (mask & SDHCI_RESET_DATA) && + (host->flags & SDHCI_REQ_USE_DMA)) + mdelay(5); + sdhci_reset(host, mask); sdhci_writel(host, host->ier, SDHCI_INT_ENABLE); @@ -1218,6 +1224,10 @@ static void esdhc_init(struct platform_device *pdev, struct sdhci_host *host) if (match) esdhc->clk_fixup = match->data; np = pdev->dev.of_node; + + if (of_device_is_compatible(np, "fsl,p2020-esdhc")) + esdhc->quirk_delay_before_data_reset = true; + clk = of_clk_get(np, 0); if (!IS_ERR(clk)) { /* -- GitLab From f1003b787c00fbaa4b11619c6b23a885bfce8f07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20T=C3=B6pel?= Date: Mon, 16 Dec 2019 10:13:35 +0100 Subject: [PATCH 1047/1437] riscv, bpf: Fix broken BPF tail calls MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The BPF JIT incorrectly clobbered the a0 register, and did not flag usage of s5 register when BPF stack was being used. Fixes: 2353ecc6f91f ("bpf, riscv: add BPF JIT for RV64G") Signed-off-by: Björn Töpel Signed-off-by: Daniel Borkmann Link: https://lore.kernel.org/bpf/20191216091343.23260-2-bjorn.topel@gmail.com --- arch/riscv/net/bpf_jit_comp.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/arch/riscv/net/bpf_jit_comp.c b/arch/riscv/net/bpf_jit_comp.c index 5451ef3845f29..1606ebd49666a 100644 --- a/arch/riscv/net/bpf_jit_comp.c +++ b/arch/riscv/net/bpf_jit_comp.c @@ -120,6 +120,11 @@ static bool seen_reg(int reg, struct rv_jit_context *ctx) return false; } +static void mark_fp(struct rv_jit_context *ctx) +{ + __set_bit(RV_CTX_F_SEEN_S5, &ctx->flags); +} + static void mark_call(struct rv_jit_context *ctx) { __set_bit(RV_CTX_F_SEEN_CALL, &ctx->flags); @@ -596,7 +601,8 @@ static void __build_epilogue(u8 reg, struct rv_jit_context *ctx) emit(rv_addi(RV_REG_SP, RV_REG_SP, stack_adjust), ctx); /* Set return value. */ - emit(rv_addi(RV_REG_A0, RV_REG_A5, 0), ctx); + if (reg == RV_REG_RA) + emit(rv_addi(RV_REG_A0, RV_REG_A5, 0), ctx); emit(rv_jalr(RV_REG_ZERO, reg, 0), ctx); } @@ -1426,6 +1432,10 @@ static void build_prologue(struct rv_jit_context *ctx) { int stack_adjust = 0, store_offset, bpf_stack_adjust; + bpf_stack_adjust = round_up(ctx->prog->aux->stack_depth, 16); + if (bpf_stack_adjust) + mark_fp(ctx); + if (seen_reg(RV_REG_RA, ctx)) stack_adjust += 8; stack_adjust += 8; /* RV_REG_FP */ @@ -1443,7 +1453,6 @@ static void build_prologue(struct rv_jit_context *ctx) stack_adjust += 8; stack_adjust = round_up(stack_adjust, 16); - bpf_stack_adjust = round_up(ctx->prog->aux->stack_depth, 16); stack_adjust += bpf_stack_adjust; store_offset = stack_adjust - 8; -- GitLab From 7d1ef13fea2b66bb466592a7855b33181c85ec5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20T=C3=B6pel?= Date: Mon, 16 Dec 2019 10:13:36 +0100 Subject: [PATCH 1048/1437] riscv, bpf: Add support for far branching MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit adds branch relaxation to the BPF JIT, and with that support for far (offset greater than 12b) branching. The branch relaxation requires more than two passes to converge. For most programs it is three passes, but for larger programs it can be more. Signed-off-by: Björn Töpel Signed-off-by: Daniel Borkmann Reviewed-by: Luke Nelson Link: https://lore.kernel.org/bpf/20191216091343.23260-3-bjorn.topel@gmail.com --- arch/riscv/net/bpf_jit_comp.c | 352 ++++++++++++++++++---------------- 1 file changed, 188 insertions(+), 164 deletions(-) diff --git a/arch/riscv/net/bpf_jit_comp.c b/arch/riscv/net/bpf_jit_comp.c index 1606ebd49666a..e599458a9bcd2 100644 --- a/arch/riscv/net/bpf_jit_comp.c +++ b/arch/riscv/net/bpf_jit_comp.c @@ -461,6 +461,11 @@ static u32 rv_amoadd_d(u8 rd, u8 rs2, u8 rs1, u8 aq, u8 rl) return rv_amo_insn(0, aq, rl, rs2, rs1, 3, rd, 0x2f); } +static u32 rv_auipc(u8 rd, u32 imm31_12) +{ + return rv_u_insn(imm31_12, rd, 0x17); +} + static bool is_12b_int(s64 val) { return -(1 << 11) <= val && val < (1 << 11); @@ -484,7 +489,7 @@ static bool is_32b_int(s64 val) static int is_12b_check(int off, int insn) { if (!is_12b_int(off)) { - pr_err("bpf-jit: insn=%d offset=%d not supported yet!\n", + pr_err("bpf-jit: insn=%d 12b < offset=%d not supported yet!\n", insn, (int)off); return -1; } @@ -494,7 +499,7 @@ static int is_12b_check(int off, int insn) static int is_13b_check(int off, int insn) { if (!is_13b_int(off)) { - pr_err("bpf-jit: insn=%d offset=%d not supported yet!\n", + pr_err("bpf-jit: insn=%d 13b < offset=%d not supported yet!\n", insn, (int)off); return -1; } @@ -504,7 +509,7 @@ static int is_13b_check(int off, int insn) static int is_21b_check(int off, int insn) { if (!is_21b_int(off)) { - pr_err("bpf-jit: insn=%d offset=%d not supported yet!\n", + pr_err("bpf-jit: insn=%d 21b < offset=%d not supported yet!\n", insn, (int)off); return -1; } @@ -550,10 +555,13 @@ static void emit_imm(u8 rd, s64 val, struct rv_jit_context *ctx) emit(rv_addi(rd, rd, lower), ctx); } -static int rv_offset(int bpf_to, int bpf_from, struct rv_jit_context *ctx) +static int rv_offset(int insn, int off, struct rv_jit_context *ctx) { - int from = ctx->offset[bpf_from] - 1, to = ctx->offset[bpf_to]; + int from, to; + off++; /* BPF branch is from PC+1, RV is from PC */ + from = (insn > 0) ? ctx->offset[insn - 1] : 0; + to = (insn + off > 0) ? ctx->offset[insn + off - 1] : 0; return (to - from) << 2; } @@ -606,6 +614,109 @@ static void __build_epilogue(u8 reg, struct rv_jit_context *ctx) emit(rv_jalr(RV_REG_ZERO, reg, 0), ctx); } +/* return -1 or inverted cond */ +static int invert_bpf_cond(u8 cond) +{ + switch (cond) { + case BPF_JEQ: + return BPF_JNE; + case BPF_JGT: + return BPF_JLE; + case BPF_JLT: + return BPF_JGE; + case BPF_JGE: + return BPF_JLT; + case BPF_JLE: + return BPF_JGT; + case BPF_JNE: + return BPF_JEQ; + case BPF_JSGT: + return BPF_JSLE; + case BPF_JSLT: + return BPF_JSGE; + case BPF_JSGE: + return BPF_JSLT; + case BPF_JSLE: + return BPF_JSGT; + } + return -1; +} + +static void emit_bcc(u8 cond, u8 rd, u8 rs, int rvoff, + struct rv_jit_context *ctx) +{ + switch (cond) { + case BPF_JEQ: + emit(rv_beq(rd, rs, rvoff >> 1), ctx); + return; + case BPF_JGT: + emit(rv_bltu(rs, rd, rvoff >> 1), ctx); + return; + case BPF_JLT: + emit(rv_bltu(rd, rs, rvoff >> 1), ctx); + return; + case BPF_JGE: + emit(rv_bgeu(rd, rs, rvoff >> 1), ctx); + return; + case BPF_JLE: + emit(rv_bgeu(rs, rd, rvoff >> 1), ctx); + return; + case BPF_JNE: + emit(rv_bne(rd, rs, rvoff >> 1), ctx); + return; + case BPF_JSGT: + emit(rv_blt(rs, rd, rvoff >> 1), ctx); + return; + case BPF_JSLT: + emit(rv_blt(rd, rs, rvoff >> 1), ctx); + return; + case BPF_JSGE: + emit(rv_bge(rd, rs, rvoff >> 1), ctx); + return; + case BPF_JSLE: + emit(rv_bge(rs, rd, rvoff >> 1), ctx); + } +} + +static void emit_branch(u8 cond, u8 rd, u8 rs, int rvoff, + struct rv_jit_context *ctx) +{ + s64 upper, lower; + + if (is_13b_int(rvoff)) { + emit_bcc(cond, rd, rs, rvoff, ctx); + return; + } + + /* Adjust for jal */ + rvoff -= 4; + + /* Transform, e.g.: + * bne rd,rs,foo + * to + * beq rd,rs,<.L1> + * (auipc foo) + * jal(r) foo + * .L1 + */ + cond = invert_bpf_cond(cond); + if (is_21b_int(rvoff)) { + emit_bcc(cond, rd, rs, 8, ctx); + emit(rv_jal(RV_REG_ZERO, rvoff >> 1), ctx); + return; + } + + /* 32b No need for an additional rvoff adjustment, since we + * get that from the auipc at PC', where PC = PC' + 4. + */ + upper = (rvoff + (1 << 11)) >> 12; + lower = rvoff & 0xfff; + + emit_bcc(cond, rd, rs, 12, ctx); + emit(rv_auipc(RV_REG_T1, upper), ctx); + emit(rv_jalr(RV_REG_ZERO, RV_REG_T1, lower), ctx); +} + static void emit_zext_32(u8 reg, struct rv_jit_context *ctx) { emit(rv_slli(reg, reg, 32), ctx); @@ -693,13 +804,6 @@ static void init_regs(u8 *rd, u8 *rs, const struct bpf_insn *insn, *rs = bpf_to_rv_reg(insn->src_reg, ctx); } -static int rv_offset_check(int *rvoff, s16 off, int insn, - struct rv_jit_context *ctx) -{ - *rvoff = rv_offset(insn + off, insn, ctx); - return is_13b_check(*rvoff, insn); -} - static void emit_zext_32_rd_rs(u8 *rd, u8 *rs, struct rv_jit_context *ctx) { emit(rv_addi(RV_REG_T2, *rd, 0), ctx); @@ -732,13 +836,19 @@ static void emit_sext_32_rd(u8 *rd, struct rv_jit_context *ctx) *rd = RV_REG_T2; } +static bool is_signed_bpf_cond(u8 cond) +{ + return cond == BPF_JSGT || cond == BPF_JSLT || + cond == BPF_JSGE || cond == BPF_JSLE; +} + static int emit_insn(const struct bpf_insn *insn, struct rv_jit_context *ctx, bool extra_pass) { bool is64 = BPF_CLASS(insn->code) == BPF_ALU64 || BPF_CLASS(insn->code) == BPF_JMP; + int s, e, rvoff, i = insn - ctx->prog->insnsi; struct bpf_prog_aux *aux = ctx->prog->aux; - int rvoff, i = insn - ctx->prog->insnsi; u8 rd = -1, rs = -1, code = insn->code; s16 off = insn->off; s32 imm = insn->imm; @@ -1006,7 +1116,7 @@ out_be: /* JUMP off */ case BPF_JMP | BPF_JA: - rvoff = rv_offset(i + off, i, ctx); + rvoff = rv_offset(i, off, ctx); if (!is_21b_int(rvoff)) { pr_err("bpf-jit: insn=%d offset=%d not supported yet!\n", i, rvoff); @@ -1019,194 +1129,96 @@ out_be: /* IF (dst COND src) JUMP off */ case BPF_JMP | BPF_JEQ | BPF_X: case BPF_JMP32 | BPF_JEQ | BPF_X: - if (rv_offset_check(&rvoff, off, i, ctx)) - return -1; - if (!is64) - emit_zext_32_rd_rs(&rd, &rs, ctx); - emit(rv_beq(rd, rs, rvoff >> 1), ctx); - break; case BPF_JMP | BPF_JGT | BPF_X: case BPF_JMP32 | BPF_JGT | BPF_X: - if (rv_offset_check(&rvoff, off, i, ctx)) - return -1; - if (!is64) - emit_zext_32_rd_rs(&rd, &rs, ctx); - emit(rv_bltu(rs, rd, rvoff >> 1), ctx); - break; case BPF_JMP | BPF_JLT | BPF_X: case BPF_JMP32 | BPF_JLT | BPF_X: - if (rv_offset_check(&rvoff, off, i, ctx)) - return -1; - if (!is64) - emit_zext_32_rd_rs(&rd, &rs, ctx); - emit(rv_bltu(rd, rs, rvoff >> 1), ctx); - break; case BPF_JMP | BPF_JGE | BPF_X: case BPF_JMP32 | BPF_JGE | BPF_X: - if (rv_offset_check(&rvoff, off, i, ctx)) - return -1; - if (!is64) - emit_zext_32_rd_rs(&rd, &rs, ctx); - emit(rv_bgeu(rd, rs, rvoff >> 1), ctx); - break; case BPF_JMP | BPF_JLE | BPF_X: case BPF_JMP32 | BPF_JLE | BPF_X: - if (rv_offset_check(&rvoff, off, i, ctx)) - return -1; - if (!is64) - emit_zext_32_rd_rs(&rd, &rs, ctx); - emit(rv_bgeu(rs, rd, rvoff >> 1), ctx); - break; case BPF_JMP | BPF_JNE | BPF_X: case BPF_JMP32 | BPF_JNE | BPF_X: - if (rv_offset_check(&rvoff, off, i, ctx)) - return -1; - if (!is64) - emit_zext_32_rd_rs(&rd, &rs, ctx); - emit(rv_bne(rd, rs, rvoff >> 1), ctx); - break; case BPF_JMP | BPF_JSGT | BPF_X: case BPF_JMP32 | BPF_JSGT | BPF_X: - if (rv_offset_check(&rvoff, off, i, ctx)) - return -1; - if (!is64) - emit_sext_32_rd_rs(&rd, &rs, ctx); - emit(rv_blt(rs, rd, rvoff >> 1), ctx); - break; case BPF_JMP | BPF_JSLT | BPF_X: case BPF_JMP32 | BPF_JSLT | BPF_X: - if (rv_offset_check(&rvoff, off, i, ctx)) - return -1; - if (!is64) - emit_sext_32_rd_rs(&rd, &rs, ctx); - emit(rv_blt(rd, rs, rvoff >> 1), ctx); - break; case BPF_JMP | BPF_JSGE | BPF_X: case BPF_JMP32 | BPF_JSGE | BPF_X: - if (rv_offset_check(&rvoff, off, i, ctx)) - return -1; - if (!is64) - emit_sext_32_rd_rs(&rd, &rs, ctx); - emit(rv_bge(rd, rs, rvoff >> 1), ctx); - break; case BPF_JMP | BPF_JSLE | BPF_X: case BPF_JMP32 | BPF_JSLE | BPF_X: - if (rv_offset_check(&rvoff, off, i, ctx)) - return -1; - if (!is64) - emit_sext_32_rd_rs(&rd, &rs, ctx); - emit(rv_bge(rs, rd, rvoff >> 1), ctx); - break; case BPF_JMP | BPF_JSET | BPF_X: case BPF_JMP32 | BPF_JSET | BPF_X: - if (rv_offset_check(&rvoff, off, i, ctx)) - return -1; - if (!is64) - emit_zext_32_rd_rs(&rd, &rs, ctx); - emit(rv_and(RV_REG_T1, rd, rs), ctx); - emit(rv_bne(RV_REG_T1, RV_REG_ZERO, rvoff >> 1), ctx); + rvoff = rv_offset(i, off, ctx); + if (!is64) { + s = ctx->ninsns; + if (is_signed_bpf_cond(BPF_OP(code))) + emit_sext_32_rd_rs(&rd, &rs, ctx); + else + emit_zext_32_rd_rs(&rd, &rs, ctx); + e = ctx->ninsns; + + /* Adjust for extra insns */ + rvoff -= (e - s) << 2; + } + + if (BPF_OP(code) == BPF_JSET) { + /* Adjust for and */ + rvoff -= 4; + emit(rv_and(RV_REG_T1, rd, rs), ctx); + emit_branch(BPF_JNE, RV_REG_T1, RV_REG_ZERO, rvoff, + ctx); + } else { + emit_branch(BPF_OP(code), rd, rs, rvoff, ctx); + } break; /* IF (dst COND imm) JUMP off */ case BPF_JMP | BPF_JEQ | BPF_K: case BPF_JMP32 | BPF_JEQ | BPF_K: - if (rv_offset_check(&rvoff, off, i, ctx)) - return -1; - emit_imm(RV_REG_T1, imm, ctx); - if (!is64) - emit_zext_32_rd_t1(&rd, ctx); - emit(rv_beq(rd, RV_REG_T1, rvoff >> 1), ctx); - break; case BPF_JMP | BPF_JGT | BPF_K: case BPF_JMP32 | BPF_JGT | BPF_K: - if (rv_offset_check(&rvoff, off, i, ctx)) - return -1; - emit_imm(RV_REG_T1, imm, ctx); - if (!is64) - emit_zext_32_rd_t1(&rd, ctx); - emit(rv_bltu(RV_REG_T1, rd, rvoff >> 1), ctx); - break; case BPF_JMP | BPF_JLT | BPF_K: case BPF_JMP32 | BPF_JLT | BPF_K: - if (rv_offset_check(&rvoff, off, i, ctx)) - return -1; - emit_imm(RV_REG_T1, imm, ctx); - if (!is64) - emit_zext_32_rd_t1(&rd, ctx); - emit(rv_bltu(rd, RV_REG_T1, rvoff >> 1), ctx); - break; case BPF_JMP | BPF_JGE | BPF_K: case BPF_JMP32 | BPF_JGE | BPF_K: - if (rv_offset_check(&rvoff, off, i, ctx)) - return -1; - emit_imm(RV_REG_T1, imm, ctx); - if (!is64) - emit_zext_32_rd_t1(&rd, ctx); - emit(rv_bgeu(rd, RV_REG_T1, rvoff >> 1), ctx); - break; case BPF_JMP | BPF_JLE | BPF_K: case BPF_JMP32 | BPF_JLE | BPF_K: - if (rv_offset_check(&rvoff, off, i, ctx)) - return -1; - emit_imm(RV_REG_T1, imm, ctx); - if (!is64) - emit_zext_32_rd_t1(&rd, ctx); - emit(rv_bgeu(RV_REG_T1, rd, rvoff >> 1), ctx); - break; case BPF_JMP | BPF_JNE | BPF_K: case BPF_JMP32 | BPF_JNE | BPF_K: - if (rv_offset_check(&rvoff, off, i, ctx)) - return -1; - emit_imm(RV_REG_T1, imm, ctx); - if (!is64) - emit_zext_32_rd_t1(&rd, ctx); - emit(rv_bne(rd, RV_REG_T1, rvoff >> 1), ctx); - break; case BPF_JMP | BPF_JSGT | BPF_K: case BPF_JMP32 | BPF_JSGT | BPF_K: - if (rv_offset_check(&rvoff, off, i, ctx)) - return -1; - emit_imm(RV_REG_T1, imm, ctx); - if (!is64) - emit_sext_32_rd(&rd, ctx); - emit(rv_blt(RV_REG_T1, rd, rvoff >> 1), ctx); - break; case BPF_JMP | BPF_JSLT | BPF_K: case BPF_JMP32 | BPF_JSLT | BPF_K: - if (rv_offset_check(&rvoff, off, i, ctx)) - return -1; - emit_imm(RV_REG_T1, imm, ctx); - if (!is64) - emit_sext_32_rd(&rd, ctx); - emit(rv_blt(rd, RV_REG_T1, rvoff >> 1), ctx); - break; case BPF_JMP | BPF_JSGE | BPF_K: case BPF_JMP32 | BPF_JSGE | BPF_K: - if (rv_offset_check(&rvoff, off, i, ctx)) - return -1; - emit_imm(RV_REG_T1, imm, ctx); - if (!is64) - emit_sext_32_rd(&rd, ctx); - emit(rv_bge(rd, RV_REG_T1, rvoff >> 1), ctx); - break; case BPF_JMP | BPF_JSLE | BPF_K: case BPF_JMP32 | BPF_JSLE | BPF_K: - if (rv_offset_check(&rvoff, off, i, ctx)) - return -1; - emit_imm(RV_REG_T1, imm, ctx); - if (!is64) - emit_sext_32_rd(&rd, ctx); - emit(rv_bge(RV_REG_T1, rd, rvoff >> 1), ctx); - break; case BPF_JMP | BPF_JSET | BPF_K: case BPF_JMP32 | BPF_JSET | BPF_K: - if (rv_offset_check(&rvoff, off, i, ctx)) - return -1; + rvoff = rv_offset(i, off, ctx); + s = ctx->ninsns; emit_imm(RV_REG_T1, imm, ctx); - if (!is64) - emit_zext_32_rd_t1(&rd, ctx); - emit(rv_and(RV_REG_T1, rd, RV_REG_T1), ctx); - emit(rv_bne(RV_REG_T1, RV_REG_ZERO, rvoff >> 1), ctx); + if (!is64) { + if (is_signed_bpf_cond(BPF_OP(code))) + emit_sext_32_rd(&rd, ctx); + else + emit_zext_32_rd_t1(&rd, ctx); + } + e = ctx->ninsns; + + /* Adjust for extra insns */ + rvoff -= (e - s) << 2; + + if (BPF_OP(code) == BPF_JSET) { + /* Adjust for and */ + rvoff -= 4; + emit(rv_and(RV_REG_T1, rd, RV_REG_T1), ctx); + emit_branch(BPF_JNE, RV_REG_T1, RV_REG_ZERO, rvoff, + ctx); + } else { + emit_branch(BPF_OP(code), rd, RV_REG_T1, rvoff, ctx); + } break; /* function call */ @@ -1557,6 +1569,7 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog) { bool tmp_blinded = false, extra_pass = false; struct bpf_prog *tmp, *orig_prog = prog; + int pass = 0, prev_ninsns = 0, i; struct rv_jit_data *jit_data; struct rv_jit_context *ctx; unsigned int image_size; @@ -1596,15 +1609,25 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog) prog = orig_prog; goto out_offset; } + for (i = 0; i < prog->len; i++) { + prev_ninsns += 32; + ctx->offset[i] = prev_ninsns; + } - /* First pass generates the ctx->offset, but does not emit an image. */ - if (build_body(ctx, extra_pass)) { - prog = orig_prog; - goto out_offset; + for (i = 0; i < 16; i++) { + pass++; + ctx->ninsns = 0; + if (build_body(ctx, extra_pass)) { + prog = orig_prog; + goto out_offset; + } + build_prologue(ctx); + ctx->epilogue_offset = ctx->ninsns; + build_epilogue(ctx); + if (ctx->ninsns == prev_ninsns) + break; + prev_ninsns = ctx->ninsns; } - build_prologue(ctx); - ctx->epilogue_offset = ctx->ninsns; - build_epilogue(ctx); /* Allocate image, now that we know the size. */ image_size = sizeof(u32) * ctx->ninsns; @@ -1619,6 +1642,7 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog) /* Second, real pass, that acutally emits the image. */ ctx->insns = (u32 *)jit_data->image; skip_init_ctx: + pass++; ctx->ninsns = 0; build_prologue(ctx); @@ -1630,7 +1654,7 @@ skip_init_ctx: build_epilogue(ctx); if (bpf_jit_enable > 1) - bpf_jit_dump(prog->len, image_size, 2, ctx->insns); + bpf_jit_dump(prog->len, image_size, pass, ctx->insns); prog->bpf_func = (void *)ctx->insns; prog->jited = 1; -- GitLab From 29d92edd9ee8b37bd8e9a0ba7fd549f874e0d069 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20T=C3=B6pel?= Date: Mon, 16 Dec 2019 10:13:37 +0100 Subject: [PATCH 1049/1437] riscv, bpf: Add support for far branching when emitting tail call MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Start use the emit_branch() function in the tail call emitter in order to support far branching. Signed-off-by: Björn Töpel Signed-off-by: Daniel Borkmann Link: https://lore.kernel.org/bpf/20191216091343.23260-4-bjorn.topel@gmail.com --- arch/riscv/net/bpf_jit_comp.c | 22 +++------------------- 1 file changed, 3 insertions(+), 19 deletions(-) diff --git a/arch/riscv/net/bpf_jit_comp.c b/arch/riscv/net/bpf_jit_comp.c index e599458a9bcd2..c38c95df3440d 100644 --- a/arch/riscv/net/bpf_jit_comp.c +++ b/arch/riscv/net/bpf_jit_comp.c @@ -496,16 +496,6 @@ static int is_12b_check(int off, int insn) return 0; } -static int is_13b_check(int off, int insn) -{ - if (!is_13b_int(off)) { - pr_err("bpf-jit: insn=%d 13b < offset=%d not supported yet!\n", - insn, (int)off); - return -1; - } - return 0; -} - static int is_21b_check(int off, int insn) { if (!is_21b_int(off)) { @@ -744,18 +734,14 @@ static int emit_bpf_tail_call(int insn, struct rv_jit_context *ctx) return -1; emit(rv_lwu(RV_REG_T1, off, RV_REG_A1), ctx); off = (tc_ninsn - (ctx->ninsns - start_insn)) << 2; - if (is_13b_check(off, insn)) - return -1; - emit(rv_bgeu(RV_REG_A2, RV_REG_T1, off >> 1), ctx); + emit_branch(BPF_JGE, RV_REG_A2, RV_REG_T1, off, ctx); /* if (--TCC < 0) * goto out; */ emit(rv_addi(RV_REG_T1, tcc, -1), ctx); off = (tc_ninsn - (ctx->ninsns - start_insn)) << 2; - if (is_13b_check(off, insn)) - return -1; - emit(rv_blt(RV_REG_T1, RV_REG_ZERO, off >> 1), ctx); + emit_branch(BPF_JSLT, RV_REG_T1, RV_REG_ZERO, off, ctx); /* prog = array->ptrs[index]; * if (!prog) @@ -768,9 +754,7 @@ static int emit_bpf_tail_call(int insn, struct rv_jit_context *ctx) return -1; emit(rv_ld(RV_REG_T2, off, RV_REG_T2), ctx); off = (tc_ninsn - (ctx->ninsns - start_insn)) << 2; - if (is_13b_check(off, insn)) - return -1; - emit(rv_beq(RV_REG_T2, RV_REG_ZERO, off >> 1), ctx); + emit_branch(BPF_JEQ, RV_REG_T2, RV_REG_ZERO, off, ctx); /* goto *(prog->bpf_func + 4); */ off = offsetof(struct bpf_prog, bpf_func); -- GitLab From 33203c02f2f8ae8e19d7dbbabf55cfd0025efede Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20T=C3=B6pel?= Date: Mon, 16 Dec 2019 10:13:38 +0100 Subject: [PATCH 1050/1437] riscv, bpf: Add support for far jumps and exits MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit add support for far (offset > 21b) jumps and exits. Signed-off-by: Björn Töpel Signed-off-by: Daniel Borkmann Reviewed-by: Luke Nelson Link: https://lore.kernel.org/bpf/20191216091343.23260-5-bjorn.topel@gmail.com --- arch/riscv/net/bpf_jit_comp.c | 37 ++++++++++++++++------------------- 1 file changed, 17 insertions(+), 20 deletions(-) diff --git a/arch/riscv/net/bpf_jit_comp.c b/arch/riscv/net/bpf_jit_comp.c index c38c95df3440d..2fc0f24ad30fc 100644 --- a/arch/riscv/net/bpf_jit_comp.c +++ b/arch/riscv/net/bpf_jit_comp.c @@ -496,16 +496,6 @@ static int is_12b_check(int off, int insn) return 0; } -static int is_21b_check(int off, int insn) -{ - if (!is_21b_int(off)) { - pr_err("bpf-jit: insn=%d 21b < offset=%d not supported yet!\n", - insn, (int)off); - return -1; - } - return 0; -} - static void emit_imm(u8 rd, s64 val, struct rv_jit_context *ctx) { /* Note that the immediate from the add is sign-extended, @@ -820,6 +810,21 @@ static void emit_sext_32_rd(u8 *rd, struct rv_jit_context *ctx) *rd = RV_REG_T2; } +static void emit_jump_and_link(u8 rd, int rvoff, struct rv_jit_context *ctx) +{ + s64 upper, lower; + + if (is_21b_int(rvoff)) { + emit(rv_jal(rd, rvoff >> 1), ctx); + return; + } + + upper = (rvoff + (1 << 11)) >> 12; + lower = rvoff & 0xfff; + emit(rv_auipc(RV_REG_T1, upper), ctx); + emit(rv_jalr(rd, RV_REG_T1, lower), ctx); +} + static bool is_signed_bpf_cond(u8 cond) { return cond == BPF_JSGT || cond == BPF_JSLT || @@ -1101,13 +1106,7 @@ out_be: /* JUMP off */ case BPF_JMP | BPF_JA: rvoff = rv_offset(i, off, ctx); - if (!is_21b_int(rvoff)) { - pr_err("bpf-jit: insn=%d offset=%d not supported yet!\n", - i, rvoff); - return -1; - } - - emit(rv_jal(RV_REG_ZERO, rvoff >> 1), ctx); + emit_jump_and_link(RV_REG_ZERO, rvoff, ctx); break; /* IF (dst COND src) JUMP off */ @@ -1245,9 +1244,7 @@ out_be: break; rvoff = epilogue_offset(ctx); - if (is_21b_check(rvoff, i)) - return -1; - emit(rv_jal(RV_REG_ZERO, rvoff >> 1), ctx); + emit_jump_and_link(RV_REG_ZERO, rvoff, ctx); break; /* dst = imm64 */ -- GitLab From fe8322b866d560a3b3535ed9aa49df59809decbe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20T=C3=B6pel?= Date: Mon, 16 Dec 2019 10:13:39 +0100 Subject: [PATCH 1051/1437] riscv, bpf: Optimize BPF tail calls MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove one addi, and instead use the offset part of jalr. Signed-off-by: Björn Töpel Signed-off-by: Daniel Borkmann Link: https://lore.kernel.org/bpf/20191216091343.23260-6-bjorn.topel@gmail.com --- arch/riscv/net/bpf_jit_comp.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/arch/riscv/net/bpf_jit_comp.c b/arch/riscv/net/bpf_jit_comp.c index 2fc0f24ad30fc..8aa19c846881c 100644 --- a/arch/riscv/net/bpf_jit_comp.c +++ b/arch/riscv/net/bpf_jit_comp.c @@ -552,7 +552,7 @@ static int epilogue_offset(struct rv_jit_context *ctx) return (to - from) << 2; } -static void __build_epilogue(u8 reg, struct rv_jit_context *ctx) +static void __build_epilogue(bool is_tail_call, struct rv_jit_context *ctx) { int stack_adjust = ctx->stack_size, store_offset = stack_adjust - 8; @@ -589,9 +589,11 @@ static void __build_epilogue(u8 reg, struct rv_jit_context *ctx) emit(rv_addi(RV_REG_SP, RV_REG_SP, stack_adjust), ctx); /* Set return value. */ - if (reg == RV_REG_RA) + if (!is_tail_call) emit(rv_addi(RV_REG_A0, RV_REG_A5, 0), ctx); - emit(rv_jalr(RV_REG_ZERO, reg, 0), ctx); + emit(rv_jalr(RV_REG_ZERO, is_tail_call ? RV_REG_T3 : RV_REG_RA, + is_tail_call ? 4 : 0), /* skip TCC init */ + ctx); } /* return -1 or inverted cond */ @@ -751,9 +753,8 @@ static int emit_bpf_tail_call(int insn, struct rv_jit_context *ctx) if (is_12b_check(off, insn)) return -1; emit(rv_ld(RV_REG_T3, off, RV_REG_T2), ctx); - emit(rv_addi(RV_REG_T3, RV_REG_T3, 4), ctx); emit(rv_addi(RV_REG_TCC, RV_REG_T1, 0), ctx); - __build_epilogue(RV_REG_T3, ctx); + __build_epilogue(true, ctx); return 0; } @@ -1504,7 +1505,7 @@ static void build_prologue(struct rv_jit_context *ctx) static void build_epilogue(struct rv_jit_context *ctx) { - __build_epilogue(RV_REG_RA, ctx); + __build_epilogue(false, ctx); } static int build_body(struct rv_jit_context *ctx, bool extra_pass) -- GitLab From 7f3631e88ee6fb13beac333a0e9e605d2414d17f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20T=C3=B6pel?= Date: Mon, 16 Dec 2019 10:13:40 +0100 Subject: [PATCH 1052/1437] riscv, bpf: Provide RISC-V specific JIT image alloc/free MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit makes sure that the JIT images is kept close to the kernel text, so BPF calls can use relative calling with auipc/jalr or jal instead of loading the full 64-bit address and jalr. The BPF JIT image region is 128 MB before the kernel text. Signed-off-by: Björn Töpel Signed-off-by: Daniel Borkmann Link: https://lore.kernel.org/bpf/20191216091343.23260-7-bjorn.topel@gmail.com --- arch/riscv/include/asm/pgtable.h | 4 ++++ arch/riscv/net/bpf_jit_comp.c | 13 +++++++++++++ 2 files changed, 17 insertions(+) diff --git a/arch/riscv/include/asm/pgtable.h b/arch/riscv/include/asm/pgtable.h index 7ff0ed4f292e4..cc3f49415620e 100644 --- a/arch/riscv/include/asm/pgtable.h +++ b/arch/riscv/include/asm/pgtable.h @@ -404,6 +404,10 @@ static inline int ptep_clear_flush_young(struct vm_area_struct *vma, #define VMALLOC_END (PAGE_OFFSET - 1) #define VMALLOC_START (PAGE_OFFSET - VMALLOC_SIZE) +#define BPF_JIT_REGION_SIZE (SZ_128M) +#define BPF_JIT_REGION_START (PAGE_OFFSET - BPF_JIT_REGION_SIZE) +#define BPF_JIT_REGION_END (VMALLOC_END) + /* * Roughly size the vmemmap space to be large enough to fit enough * struct pages to map half the virtual address space. Then diff --git a/arch/riscv/net/bpf_jit_comp.c b/arch/riscv/net/bpf_jit_comp.c index 8aa19c846881c..46cff093f526c 100644 --- a/arch/riscv/net/bpf_jit_comp.c +++ b/arch/riscv/net/bpf_jit_comp.c @@ -1656,3 +1656,16 @@ out: tmp : orig_prog); return prog; } + +void *bpf_jit_alloc_exec(unsigned long size) +{ + return __vmalloc_node_range(size, PAGE_SIZE, BPF_JIT_REGION_START, + BPF_JIT_REGION_END, GFP_KERNEL, + PAGE_KERNEL_EXEC, 0, NUMA_NO_NODE, + __builtin_return_address(0)); +} + +void bpf_jit_free_exec(void *addr) +{ + return vfree(addr); +} -- GitLab From e368b64f8b0c8066115ad1da71f83f20f7bb43af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20T=C3=B6pel?= Date: Mon, 16 Dec 2019 10:13:41 +0100 Subject: [PATCH 1053/1437] riscv, bpf: Optimize calls MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instead of using emit_imm() and emit_jalr() which can expand to six instructions, start using jal or auipc+jalr. Signed-off-by: Björn Töpel Signed-off-by: Daniel Borkmann Link: https://lore.kernel.org/bpf/20191216091343.23260-8-bjorn.topel@gmail.com --- arch/riscv/net/bpf_jit_comp.c | 101 +++++++++++++++++++++------------- 1 file changed, 64 insertions(+), 37 deletions(-) diff --git a/arch/riscv/net/bpf_jit_comp.c b/arch/riscv/net/bpf_jit_comp.c index 46cff093f526c..8d7e3343a08c4 100644 --- a/arch/riscv/net/bpf_jit_comp.c +++ b/arch/riscv/net/bpf_jit_comp.c @@ -811,11 +811,12 @@ static void emit_sext_32_rd(u8 *rd, struct rv_jit_context *ctx) *rd = RV_REG_T2; } -static void emit_jump_and_link(u8 rd, int rvoff, struct rv_jit_context *ctx) +static void emit_jump_and_link(u8 rd, s64 rvoff, bool force_jalr, + struct rv_jit_context *ctx) { s64 upper, lower; - if (is_21b_int(rvoff)) { + if (rvoff && is_21b_int(rvoff) && !force_jalr) { emit(rv_jal(rd, rvoff >> 1), ctx); return; } @@ -832,6 +833,28 @@ static bool is_signed_bpf_cond(u8 cond) cond == BPF_JSGE || cond == BPF_JSLE; } +static int emit_call(bool fixed, u64 addr, struct rv_jit_context *ctx) +{ + s64 off = 0; + u64 ip; + u8 rd; + + if (addr && ctx->insns) { + ip = (u64)(long)(ctx->insns + ctx->ninsns); + off = addr - ip; + if (!is_32b_int(off)) { + pr_err("bpf-jit: target call addr %pK is out of range\n", + (void *)addr); + return -ERANGE; + } + } + + emit_jump_and_link(RV_REG_RA, off, !fixed, ctx); + rd = bpf_to_rv_reg(BPF_REG_0, ctx); + emit(rv_addi(rd, RV_REG_A0, 0), ctx); + return 0; +} + static int emit_insn(const struct bpf_insn *insn, struct rv_jit_context *ctx, bool extra_pass) { @@ -1107,7 +1130,7 @@ out_be: /* JUMP off */ case BPF_JMP | BPF_JA: rvoff = rv_offset(i, off, ctx); - emit_jump_and_link(RV_REG_ZERO, rvoff, ctx); + emit_jump_and_link(RV_REG_ZERO, rvoff, false, ctx); break; /* IF (dst COND src) JUMP off */ @@ -1209,7 +1232,7 @@ out_be: case BPF_JMP | BPF_CALL: { bool fixed; - int i, ret; + int ret; u64 addr; mark_call(ctx); @@ -1217,20 +1240,9 @@ out_be: &fixed); if (ret < 0) return ret; - if (fixed) { - emit_imm(RV_REG_T1, addr, ctx); - } else { - i = ctx->ninsns; - emit_imm(RV_REG_T1, addr, ctx); - for (i = ctx->ninsns - i; i < 8; i++) { - /* nop */ - emit(rv_addi(RV_REG_ZERO, RV_REG_ZERO, 0), - ctx); - } - } - emit(rv_jalr(RV_REG_RA, RV_REG_T1, 0), ctx); - rd = bpf_to_rv_reg(BPF_REG_0, ctx); - emit(rv_addi(rd, RV_REG_A0, 0), ctx); + ret = emit_call(fixed, addr, ctx); + if (ret) + return ret; break; } /* tail call */ @@ -1245,7 +1257,7 @@ out_be: break; rvoff = epilogue_offset(ctx); - emit_jump_and_link(RV_REG_ZERO, rvoff, ctx); + emit_jump_and_link(RV_REG_ZERO, rvoff, false, ctx); break; /* dst = imm64 */ @@ -1508,7 +1520,7 @@ static void build_epilogue(struct rv_jit_context *ctx) __build_epilogue(false, ctx); } -static int build_body(struct rv_jit_context *ctx, bool extra_pass) +static int build_body(struct rv_jit_context *ctx, bool extra_pass, int *offset) { const struct bpf_prog *prog = ctx->prog; int i; @@ -1520,12 +1532,12 @@ static int build_body(struct rv_jit_context *ctx, bool extra_pass) ret = emit_insn(insn, ctx, extra_pass); if (ret > 0) { i++; - if (ctx->insns == NULL) - ctx->offset[i] = ctx->ninsns; + if (offset) + offset[i] = ctx->ninsns; continue; } - if (ctx->insns == NULL) - ctx->offset[i] = ctx->ninsns; + if (offset) + offset[i] = ctx->ninsns; if (ret) return ret; } @@ -1553,8 +1565,8 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog) struct bpf_prog *tmp, *orig_prog = prog; int pass = 0, prev_ninsns = 0, i; struct rv_jit_data *jit_data; + unsigned int image_size = 0; struct rv_jit_context *ctx; - unsigned int image_size; if (!prog->jit_requested) return orig_prog; @@ -1599,36 +1611,51 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog) for (i = 0; i < 16; i++) { pass++; ctx->ninsns = 0; - if (build_body(ctx, extra_pass)) { + if (build_body(ctx, extra_pass, ctx->offset)) { prog = orig_prog; goto out_offset; } build_prologue(ctx); ctx->epilogue_offset = ctx->ninsns; build_epilogue(ctx); - if (ctx->ninsns == prev_ninsns) - break; + + if (ctx->ninsns == prev_ninsns) { + if (jit_data->header) + break; + + image_size = sizeof(u32) * ctx->ninsns; + jit_data->header = + bpf_jit_binary_alloc(image_size, + &jit_data->image, + sizeof(u32), + bpf_fill_ill_insns); + if (!jit_data->header) { + prog = orig_prog; + goto out_offset; + } + + ctx->insns = (u32 *)jit_data->image; + /* Now, when the image is allocated, the image + * can potentially shrink more (auipc/jalr -> + * jal). + */ + } prev_ninsns = ctx->ninsns; } - /* Allocate image, now that we know the size. */ - image_size = sizeof(u32) * ctx->ninsns; - jit_data->header = bpf_jit_binary_alloc(image_size, &jit_data->image, - sizeof(u32), - bpf_fill_ill_insns); - if (!jit_data->header) { + if (i == 16) { + pr_err("bpf-jit: image did not converge in <%d passes!\n", i); + bpf_jit_binary_free(jit_data->header); prog = orig_prog; goto out_offset; } - /* Second, real pass, that acutally emits the image. */ - ctx->insns = (u32 *)jit_data->image; skip_init_ctx: pass++; ctx->ninsns = 0; build_prologue(ctx); - if (build_body(ctx, extra_pass)) { + if (build_body(ctx, extra_pass, NULL)) { bpf_jit_binary_free(jit_data->header); prog = orig_prog; goto out_offset; -- GitLab From eb9928bed003dd61a443d0ba51ae066429fbe735 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20T=C3=B6pel?= Date: Mon, 16 Dec 2019 10:13:42 +0100 Subject: [PATCH 1054/1437] riscv, bpf: Add missing uapi header for BPF_PROG_TYPE_PERF_EVENT programs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add missing uapi header the BPF_PROG_TYPE_PERF_EVENT programs by exporting struct user_regs_struct instead of struct pt_regs which is in-kernel only. Signed-off-by: Björn Töpel Signed-off-by: Daniel Borkmann Link: https://lore.kernel.org/bpf/20191216091343.23260-9-bjorn.topel@gmail.com --- arch/riscv/include/uapi/asm/bpf_perf_event.h | 9 +++++++++ tools/include/uapi/asm/bpf_perf_event.h | 2 ++ 2 files changed, 11 insertions(+) create mode 100644 arch/riscv/include/uapi/asm/bpf_perf_event.h diff --git a/arch/riscv/include/uapi/asm/bpf_perf_event.h b/arch/riscv/include/uapi/asm/bpf_perf_event.h new file mode 100644 index 0000000000000..6cb1c2823288b --- /dev/null +++ b/arch/riscv/include/uapi/asm/bpf_perf_event.h @@ -0,0 +1,9 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _UAPI__ASM_BPF_PERF_EVENT_H__ +#define _UAPI__ASM_BPF_PERF_EVENT_H__ + +#include + +typedef struct user_regs_struct bpf_user_pt_regs_t; + +#endif /* _UAPI__ASM_BPF_PERF_EVENT_H__ */ diff --git a/tools/include/uapi/asm/bpf_perf_event.h b/tools/include/uapi/asm/bpf_perf_event.h index 13a58531e6fa8..39acc149d8432 100644 --- a/tools/include/uapi/asm/bpf_perf_event.h +++ b/tools/include/uapi/asm/bpf_perf_event.h @@ -2,6 +2,8 @@ #include "../../arch/arm64/include/uapi/asm/bpf_perf_event.h" #elif defined(__s390__) #include "../../arch/s390/include/uapi/asm/bpf_perf_event.h" +#elif defined(__riscv) +#include "../../arch/riscv/include/uapi/asm/bpf_perf_event.h" #else #include #endif -- GitLab From 34bfc10a6e7e573563a700239d302ab5944b3397 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20T=C3=B6pel?= Date: Mon, 16 Dec 2019 10:13:43 +0100 Subject: [PATCH 1055/1437] riscv, perf: Add arch specific perf_arch_bpf_user_pt_regs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit RISC-V was missing a proper perf_arch_bpf_user_pt_regs macro for CONFIG_PERF_EVENT builds. Signed-off-by: Björn Töpel Signed-off-by: Daniel Borkmann Link: https://lore.kernel.org/bpf/20191216091343.23260-10-bjorn.topel@gmail.com --- arch/riscv/include/asm/perf_event.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/arch/riscv/include/asm/perf_event.h b/arch/riscv/include/asm/perf_event.h index aefbfaa6a7815..0234048b12bc9 100644 --- a/arch/riscv/include/asm/perf_event.h +++ b/arch/riscv/include/asm/perf_event.h @@ -82,4 +82,8 @@ struct riscv_pmu { int irq; }; +#ifdef CONFIG_PERF_EVENTS +#define perf_arch_bpf_user_pt_regs(regs) (struct user_regs_struct *)regs +#endif + #endif /* _ASM_RISCV_PERF_EVENT_H */ -- GitLab From 12dd14b230b3c742b80272ecb8a83cdf824625ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Toke=20H=C3=B8iland-J=C3=B8rgensen?= Date: Thu, 19 Dec 2019 13:07:14 +0100 Subject: [PATCH 1056/1437] libbpf: Add missing newline in opts validation macro MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The error log output in the opts validation macro was missing a newline. Fixes: 2ce8450ef5a3 ("libbpf: add bpf_object__open_{file, mem} w/ extensible opts") Signed-off-by: Toke Høiland-Jørgensen Signed-off-by: Daniel Borkmann Link: https://lore.kernel.org/bpf/20191219120714.928380-1-toke@redhat.com --- tools/lib/bpf/libbpf_internal.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/lib/bpf/libbpf_internal.h b/tools/lib/bpf/libbpf_internal.h index 7d71621bb7e83..8c3afbd97747b 100644 --- a/tools/lib/bpf/libbpf_internal.h +++ b/tools/lib/bpf/libbpf_internal.h @@ -76,7 +76,7 @@ static inline bool libbpf_validate_opts(const char *opts, for (i = opts_sz; i < user_sz; i++) { if (opts[i]) { - pr_warn("%s has non-zero extra bytes", + pr_warn("%s has non-zero extra bytes\n", type_name); return false; } -- GitLab From 580205dd4fe800b1e95be8b6df9e2991f975a8ad Mon Sep 17 00:00:00 2001 From: Alexei Starovoitov Date: Wed, 18 Dec 2019 18:04:42 -0800 Subject: [PATCH 1057/1437] selftests/bpf: Fix test_attach_probe Fix two issues in test_attach_probe: 1. it was not able to parse /proc/self/maps beyond the first line, since %s means parse string until white space. 2. offset has to be accounted for otherwise uprobed address is incorrect. Fixes: 1e8611bbdfc9 ("selftests/bpf: add kprobe/uprobe selftests") Signed-off-by: Alexei Starovoitov Signed-off-by: Daniel Borkmann Acked-by: Yonghong Song Acked-by: Andrii Nakryiko Link: https://lore.kernel.org/bpf/20191219020442.1922617-1-ast@kernel.org --- tools/testing/selftests/bpf/prog_tests/attach_probe.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tools/testing/selftests/bpf/prog_tests/attach_probe.c b/tools/testing/selftests/bpf/prog_tests/attach_probe.c index 5ed90ede2f1d5..a0ee87c8e1ea7 100644 --- a/tools/testing/selftests/bpf/prog_tests/attach_probe.c +++ b/tools/testing/selftests/bpf/prog_tests/attach_probe.c @@ -3,7 +3,7 @@ #include "test_attach_probe.skel.h" ssize_t get_base_addr() { - size_t start; + size_t start, offset; char buf[256]; FILE *f; @@ -11,10 +11,11 @@ ssize_t get_base_addr() { if (!f) return -errno; - while (fscanf(f, "%zx-%*x %s %*s\n", &start, buf) == 2) { + while (fscanf(f, "%zx-%*x %s %zx %*[^\n]\n", + &start, buf, &offset) == 3) { if (strcmp(buf, "r-xp") == 0) { fclose(f); - return start; + return start - offset; } } -- GitLab From 06870682087b58398671e8cdc896cd62314c4399 Mon Sep 17 00:00:00 2001 From: Maxim Mikityanskiy Date: Tue, 17 Dec 2019 16:20:42 +0000 Subject: [PATCH 1058/1437] xsk: Add rcu_read_lock around the XSK wakeup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The XSK wakeup callback in drivers makes some sanity checks before triggering NAPI. However, some configuration changes may occur during this function that affect the result of those checks. For example, the interface can go down, and all the resources will be destroyed after the checks in the wakeup function, but before it attempts to use these resources. Wrap this callback in rcu_read_lock to allow driver to synchronize_rcu before actually destroying the resources. xsk_wakeup is a new function that encapsulates calling ndo_xsk_wakeup wrapped into the RCU lock. After this commit, xsk_poll starts using xsk_wakeup and checks xs->zc instead of ndo_xsk_wakeup != NULL to decide ndo_xsk_wakeup should be called. It also fixes a bug introduced with the need_wakeup feature: a non-zero-copy socket may be used with a driver supporting zero-copy, and in this case ndo_xsk_wakeup should not be called, so the xs->zc check is the correct one. Fixes: 77cd0d7b3f25 ("xsk: add support for need_wakeup flag in AF_XDP rings") Signed-off-by: Maxim Mikityanskiy Signed-off-by: Björn Töpel Signed-off-by: Daniel Borkmann Link: https://lore.kernel.org/bpf/20191217162023.16011-2-maximmi@mellanox.com --- net/xdp/xsk.c | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/net/xdp/xsk.c b/net/xdp/xsk.c index 956793893c9de..328f661b83b2e 100644 --- a/net/xdp/xsk.c +++ b/net/xdp/xsk.c @@ -334,12 +334,21 @@ out: } EXPORT_SYMBOL(xsk_umem_consume_tx); -static int xsk_zc_xmit(struct xdp_sock *xs) +static int xsk_wakeup(struct xdp_sock *xs, u8 flags) { struct net_device *dev = xs->dev; + int err; + + rcu_read_lock(); + err = dev->netdev_ops->ndo_xsk_wakeup(dev, xs->queue_id, flags); + rcu_read_unlock(); + + return err; +} - return dev->netdev_ops->ndo_xsk_wakeup(dev, xs->queue_id, - XDP_WAKEUP_TX); +static int xsk_zc_xmit(struct xdp_sock *xs) +{ + return xsk_wakeup(xs, XDP_WAKEUP_TX); } static void xsk_destruct_skb(struct sk_buff *skb) @@ -453,19 +462,16 @@ static __poll_t xsk_poll(struct file *file, struct socket *sock, __poll_t mask = datagram_poll(file, sock, wait); struct sock *sk = sock->sk; struct xdp_sock *xs = xdp_sk(sk); - struct net_device *dev; struct xdp_umem *umem; if (unlikely(!xsk_is_bound(xs))) return mask; - dev = xs->dev; umem = xs->umem; if (umem->need_wakeup) { - if (dev->netdev_ops->ndo_xsk_wakeup) - dev->netdev_ops->ndo_xsk_wakeup(dev, xs->queue_id, - umem->need_wakeup); + if (xs->zc) + xsk_wakeup(xs, umem->need_wakeup); else /* Poll needs to drive Tx also in copy mode */ __xsk_sendmsg(sk); -- GitLab From 9cf88808ad6a0f1e958e00abd9a081295fe6da0c Mon Sep 17 00:00:00 2001 From: Maxim Mikityanskiy Date: Tue, 17 Dec 2019 16:20:44 +0000 Subject: [PATCH 1059/1437] net/mlx5e: Fix concurrency issues between config flow and XSK After disabling resources necessary for XSK (the XDP program, channels, XSK queues), use synchronize_rcu to wait until the XSK wakeup function finishes, before freeing the resources. Suspend XSK wakeups during switching channels. If the XDP program is being removed, synchronize_rcu before closing the old channels to allow XSK wakeup to complete. Signed-off-by: Maxim Mikityanskiy Signed-off-by: Daniel Borkmann Link: https://lore.kernel.org/bpf/20191217162023.16011-3-maximmi@mellanox.com --- drivers/net/ethernet/mellanox/mlx5/core/en.h | 2 +- .../net/ethernet/mellanox/mlx5/core/en/xdp.h | 22 ++++++++----------- .../mellanox/mlx5/core/en/xsk/setup.c | 1 + .../ethernet/mellanox/mlx5/core/en/xsk/tx.c | 2 +- .../net/ethernet/mellanox/mlx5/core/en_main.c | 19 +--------------- 5 files changed, 13 insertions(+), 33 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en.h b/drivers/net/ethernet/mellanox/mlx5/core/en.h index 2c16add0b642f..9c8427698238f 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en.h @@ -760,7 +760,7 @@ enum { MLX5E_STATE_OPENED, MLX5E_STATE_DESTROYING, MLX5E_STATE_XDP_TX_ENABLED, - MLX5E_STATE_XDP_OPEN, + MLX5E_STATE_XDP_ACTIVE, }; struct mlx5e_rqt { diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/xdp.h b/drivers/net/ethernet/mellanox/mlx5/core/en/xdp.h index 36ac1e3816b9d..d7587f40ecaec 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/xdp.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/xdp.h @@ -75,12 +75,18 @@ int mlx5e_xdp_xmit(struct net_device *dev, int n, struct xdp_frame **frames, static inline void mlx5e_xdp_tx_enable(struct mlx5e_priv *priv) { set_bit(MLX5E_STATE_XDP_TX_ENABLED, &priv->state); + + if (priv->channels.params.xdp_prog) + set_bit(MLX5E_STATE_XDP_ACTIVE, &priv->state); } static inline void mlx5e_xdp_tx_disable(struct mlx5e_priv *priv) { + if (priv->channels.params.xdp_prog) + clear_bit(MLX5E_STATE_XDP_ACTIVE, &priv->state); + clear_bit(MLX5E_STATE_XDP_TX_ENABLED, &priv->state); - /* let other device's napi(s) see our new state */ + /* Let other device's napi(s) and XSK wakeups see our new state. */ synchronize_rcu(); } @@ -89,19 +95,9 @@ static inline bool mlx5e_xdp_tx_is_enabled(struct mlx5e_priv *priv) return test_bit(MLX5E_STATE_XDP_TX_ENABLED, &priv->state); } -static inline void mlx5e_xdp_set_open(struct mlx5e_priv *priv) -{ - set_bit(MLX5E_STATE_XDP_OPEN, &priv->state); -} - -static inline void mlx5e_xdp_set_closed(struct mlx5e_priv *priv) -{ - clear_bit(MLX5E_STATE_XDP_OPEN, &priv->state); -} - -static inline bool mlx5e_xdp_is_open(struct mlx5e_priv *priv) +static inline bool mlx5e_xdp_is_active(struct mlx5e_priv *priv) { - return test_bit(MLX5E_STATE_XDP_OPEN, &priv->state); + return test_bit(MLX5E_STATE_XDP_ACTIVE, &priv->state); } static inline void mlx5e_xmit_xdp_doorbell(struct mlx5e_xdpsq *sq) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/xsk/setup.c b/drivers/net/ethernet/mellanox/mlx5/core/en/xsk/setup.c index 631af8dee5171..c28cbae423310 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/xsk/setup.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/xsk/setup.c @@ -144,6 +144,7 @@ void mlx5e_close_xsk(struct mlx5e_channel *c) { clear_bit(MLX5E_CHANNEL_STATE_XSK, c->state); napi_synchronize(&c->napi); + synchronize_rcu(); /* Sync with the XSK wakeup. */ mlx5e_close_rq(&c->xskrq); mlx5e_close_cq(&c->xskrq.cq); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/xsk/tx.c b/drivers/net/ethernet/mellanox/mlx5/core/en/xsk/tx.c index 87827477d38c4..fe2d596cb361f 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/xsk/tx.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/xsk/tx.c @@ -14,7 +14,7 @@ int mlx5e_xsk_wakeup(struct net_device *dev, u32 qid, u32 flags) struct mlx5e_channel *c; u16 ix; - if (unlikely(!mlx5e_xdp_is_open(priv))) + if (unlikely(!mlx5e_xdp_is_active(priv))) return -ENETDOWN; if (unlikely(!mlx5e_qid_get_ch_if_in_group(params, qid, MLX5E_RQ_GROUP_XSK, &ix))) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c index 4980e80a5e85d..4997b8a51994b 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c @@ -3000,12 +3000,9 @@ void mlx5e_timestamp_init(struct mlx5e_priv *priv) int mlx5e_open_locked(struct net_device *netdev) { struct mlx5e_priv *priv = netdev_priv(netdev); - bool is_xdp = priv->channels.params.xdp_prog; int err; set_bit(MLX5E_STATE_OPENED, &priv->state); - if (is_xdp) - mlx5e_xdp_set_open(priv); err = mlx5e_open_channels(priv, &priv->channels); if (err) @@ -3020,8 +3017,6 @@ int mlx5e_open_locked(struct net_device *netdev) return 0; err_clear_state_opened_flag: - if (is_xdp) - mlx5e_xdp_set_closed(priv); clear_bit(MLX5E_STATE_OPENED, &priv->state); return err; } @@ -3053,8 +3048,6 @@ int mlx5e_close_locked(struct net_device *netdev) if (!test_bit(MLX5E_STATE_OPENED, &priv->state)) return 0; - if (priv->channels.params.xdp_prog) - mlx5e_xdp_set_closed(priv); clear_bit(MLX5E_STATE_OPENED, &priv->state); netif_carrier_off(priv->netdev); @@ -4371,16 +4364,6 @@ static int mlx5e_xdp_allowed(struct mlx5e_priv *priv, struct bpf_prog *prog) return 0; } -static int mlx5e_xdp_update_state(struct mlx5e_priv *priv) -{ - if (priv->channels.params.xdp_prog) - mlx5e_xdp_set_open(priv); - else - mlx5e_xdp_set_closed(priv); - - return 0; -} - static int mlx5e_xdp_set(struct net_device *netdev, struct bpf_prog *prog) { struct mlx5e_priv *priv = netdev_priv(netdev); @@ -4415,7 +4398,7 @@ static int mlx5e_xdp_set(struct net_device *netdev, struct bpf_prog *prog) mlx5e_set_rq_type(priv->mdev, &new_channels.params); old_prog = priv->channels.params.xdp_prog; - err = mlx5e_safe_switch_channels(priv, &new_channels, mlx5e_xdp_update_state); + err = mlx5e_safe_switch_channels(priv, &new_channels, NULL); if (err) goto unlock; } else { -- GitLab From b3873a5be757b44d51af542a50a6f2a3b6f95284 Mon Sep 17 00:00:00 2001 From: Maxim Mikityanskiy Date: Tue, 17 Dec 2019 16:20:45 +0000 Subject: [PATCH 1060/1437] net/i40e: Fix concurrency issues between config flow and XSK MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use synchronize_rcu to wait until the XSK wakeup function finishes before destroying the resources it uses: 1. i40e_down already calls synchronize_rcu. On i40e_down either __I40E_VSI_DOWN or __I40E_CONFIG_BUSY is set. Check the latter in i40e_xsk_wakeup (the former is already checked there). 2. After switching the XDP program, call synchronize_rcu to let i40e_xsk_wakeup exit before the XDP program is freed. 3. Changing the number of channels brings the interface down (see i40e_prep_for_reset and i40e_pf_quiesce_all_vsi). 4. Disabling UMEM sets __I40E_CONFIG_BUSY, too. Signed-off-by: Maxim Mikityanskiy Signed-off-by: Björn Töpel Signed-off-by: Daniel Borkmann Link: https://lore.kernel.org/bpf/20191217162023.16011-4-maximmi@mellanox.com --- drivers/net/ethernet/intel/i40e/i40e.h | 2 +- drivers/net/ethernet/intel/i40e/i40e_main.c | 10 +++++++--- drivers/net/ethernet/intel/i40e/i40e_xsk.c | 4 ++++ 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e.h b/drivers/net/ethernet/intel/i40e/i40e.h index cb6367334ca78..4833187bd2591 100644 --- a/drivers/net/ethernet/intel/i40e/i40e.h +++ b/drivers/net/ethernet/intel/i40e/i40e.h @@ -1152,7 +1152,7 @@ void i40e_set_fec_in_flags(u8 fec_cfg, u32 *flags); static inline bool i40e_enabled_xdp_vsi(struct i40e_vsi *vsi) { - return !!vsi->xdp_prog; + return !!READ_ONCE(vsi->xdp_prog); } int i40e_create_queue_channel(struct i40e_vsi *vsi, struct i40e_channel *ch); diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index 1ccabeafa44c4..2c5af6d4a6b1c 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -6823,8 +6823,8 @@ void i40e_down(struct i40e_vsi *vsi) for (i = 0; i < vsi->num_queue_pairs; i++) { i40e_clean_tx_ring(vsi->tx_rings[i]); if (i40e_enabled_xdp_vsi(vsi)) { - /* Make sure that in-progress ndo_xdp_xmit - * calls are completed. + /* Make sure that in-progress ndo_xdp_xmit and + * ndo_xsk_wakeup calls are completed. */ synchronize_rcu(); i40e_clean_tx_ring(vsi->xdp_rings[i]); @@ -12546,8 +12546,12 @@ static int i40e_xdp_setup(struct i40e_vsi *vsi, old_prog = xchg(&vsi->xdp_prog, prog); - if (need_reset) + if (need_reset) { + if (!prog) + /* Wait until ndo_xsk_wakeup completes. */ + synchronize_rcu(); i40e_reset_and_rebuild(pf, true, true); + } for (i = 0; i < vsi->num_queue_pairs; i++) WRITE_ONCE(vsi->rx_rings[i]->xdp_prog, vsi->xdp_prog); diff --git a/drivers/net/ethernet/intel/i40e/i40e_xsk.c b/drivers/net/ethernet/intel/i40e/i40e_xsk.c index d07e1a8904283..f73cd917c44f7 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_xsk.c +++ b/drivers/net/ethernet/intel/i40e/i40e_xsk.c @@ -787,8 +787,12 @@ int i40e_xsk_wakeup(struct net_device *dev, u32 queue_id, u32 flags) { struct i40e_netdev_priv *np = netdev_priv(dev); struct i40e_vsi *vsi = np->vsi; + struct i40e_pf *pf = vsi->back; struct i40e_ring *ring; + if (test_bit(__I40E_CONFIG_BUSY, pf->state)) + return -ENETDOWN; + if (test_bit(__I40E_VSI_DOWN, vsi->state)) return -ENETDOWN; -- GitLab From c0fdccfd226a1424683d3000d9e08384391210a2 Mon Sep 17 00:00:00 2001 From: Maxim Mikityanskiy Date: Tue, 17 Dec 2019 16:20:47 +0000 Subject: [PATCH 1061/1437] net/ixgbe: Fix concurrency issues between config flow and XSK MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use synchronize_rcu to wait until the XSK wakeup function finishes before destroying the resources it uses: 1. ixgbe_down already calls synchronize_rcu after setting __IXGBE_DOWN. 2. After switching the XDP program, call synchronize_rcu to let ixgbe_xsk_wakeup exit before the XDP program is freed. 3. Changing the number of channels brings the interface down. 4. Disabling UMEM sets __IXGBE_TX_DISABLED before closing hardware resources and resetting xsk_umem. Check that bit in ixgbe_xsk_wakeup to avoid using the XDP ring when it's already destroyed. synchronize_rcu is called from ixgbe_txrx_ring_disable. Signed-off-by: Maxim Mikityanskiy Signed-off-by: Björn Töpel Signed-off-by: Daniel Borkmann Link: https://lore.kernel.org/bpf/20191217162023.16011-5-maximmi@mellanox.com --- drivers/net/ethernet/intel/ixgbe/ixgbe_main.c | 7 ++++++- drivers/net/ethernet/intel/ixgbe/ixgbe_xsk.c | 8 ++++++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c index 25c097cd8100f..82a30b597cf98 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c @@ -10261,7 +10261,12 @@ static int ixgbe_xdp_setup(struct net_device *dev, struct bpf_prog *prog) /* If transitioning XDP modes reconfigure rings */ if (need_reset) { - int err = ixgbe_setup_tc(dev, adapter->hw_tcs); + int err; + + if (!prog) + /* Wait until ndo_xsk_wakeup completes. */ + synchronize_rcu(); + err = ixgbe_setup_tc(dev, adapter->hw_tcs); if (err) { rcu_assign_pointer(adapter->xdp_prog, old_prog); diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_xsk.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_xsk.c index d6feaacfbf898..b43be9f141053 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_xsk.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_xsk.c @@ -709,10 +709,14 @@ int ixgbe_xsk_wakeup(struct net_device *dev, u32 qid, u32 flags) if (qid >= adapter->num_xdp_queues) return -ENXIO; - if (!adapter->xdp_ring[qid]->xsk_umem) + ring = adapter->xdp_ring[qid]; + + if (test_bit(__IXGBE_TX_DISABLED, &ring->state)) + return -ENETDOWN; + + if (!ring->xsk_umem) return -ENXIO; - ring = adapter->xdp_ring[qid]; if (!napi_if_scheduled_mark_missed(&ring->q_vector->napi)) { u64 eics = BIT_ULL(ring->q_vector->v_idx); -- GitLab From b5c7d0d0f7d4a30ae96c1350df677cb849060b7c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Toke=20H=C3=B8iland-J=C3=B8rgensen?= Date: Thu, 19 Dec 2019 10:02:36 +0100 Subject: [PATCH 1062/1437] libbpf: Fix printing of ulimit value MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Naresh pointed out that libbpf builds fail on 32-bit architectures because rlimit.rlim_cur is defined as 'unsigned long long' on those architectures. Fix this by using %zu in printf and casting to size_t. Fixes: dc3a2d254782 ("libbpf: Print hint about ulimit when getting permission denied error") Reported-by: Naresh Kamboju Signed-off-by: Toke Høiland-Jørgensen Signed-off-by: Daniel Borkmann Link: https://lore.kernel.org/bpf/20191219090236.905059-1-toke@redhat.com --- tools/lib/bpf/libbpf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c index 6340b81b555b8..febbaac3daf4c 100644 --- a/tools/lib/bpf/libbpf.c +++ b/tools/lib/bpf/libbpf.c @@ -117,7 +117,7 @@ static void pr_perm_msg(int err) return; if (limit.rlim_cur < 1024) - snprintf(buf, sizeof(buf), "%lu bytes", limit.rlim_cur); + snprintf(buf, sizeof(buf), "%zu bytes", (size_t)limit.rlim_cur); else if (limit.rlim_cur < 1024*1024) snprintf(buf, sizeof(buf), "%.1f KiB", (double)limit.rlim_cur / 1024); else -- GitLab From 7745ff9842617323adbe24e71c495d5ebd9aa352 Mon Sep 17 00:00:00 2001 From: Andrii Nakryiko Date: Wed, 18 Dec 2019 21:21:03 -0800 Subject: [PATCH 1063/1437] libbpf: Fix another __u64 printf warning Fix yet another printf warning for %llu specifier on ppc64le. This time size_t casting won't work, so cast to verbose `unsigned long long`. Fixes: 166750bc1dd2 ("libbpf: Support libbpf-provided extern variables") Signed-off-by: Andrii Nakryiko Signed-off-by: Daniel Borkmann Link: https://lore.kernel.org/bpf/20191219052103.3515-1-andriin@fb.com --- tools/lib/bpf/libbpf.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c index febbaac3daf4c..9576a90c5a1ca 100644 --- a/tools/lib/bpf/libbpf.c +++ b/tools/lib/bpf/libbpf.c @@ -1129,12 +1129,12 @@ static int set_ext_value_num(struct extern_desc *ext, void *ext_val, { if (ext->type != EXT_INT && ext->type != EXT_CHAR) { pr_warn("extern %s=%llu should be integer\n", - ext->name, value); + ext->name, (unsigned long long)value); return -EINVAL; } if (!is_ext_value_in_range(ext, value)) { pr_warn("extern %s=%llu value doesn't fit in %d bytes\n", - ext->name, value, ext->sz); + ext->name, (unsigned long long)value, ext->sz); return -ERANGE; } switch (ext->sz) { -- GitLab From 826f7e34130a4ce756138540170cbe935c537a47 Mon Sep 17 00:00:00 2001 From: Brian Foster Date: Tue, 17 Dec 2019 13:50:26 -0800 Subject: [PATCH 1064/1437] xfs: use bitops interface for buf log item AIL flag check The xfs_log_item flags were converted to atomic bitops as of commit 22525c17ed ("xfs: log item flags are racy"). The assert check for AIL presence in xfs_buf_item_relse() still uses the old value based check. This likely went unnoticed as XFS_LI_IN_AIL evaluates to 0 and causes the assert to unconditionally pass. Fix up the check. Signed-off-by: Brian Foster Fixes: 22525c17ed ("xfs: log item flags are racy") Reviewed-by: Eric Sandeen Reviewed-by: Darrick J. Wong Signed-off-by: Darrick J. Wong --- fs/xfs/xfs_buf_item.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/xfs/xfs_buf_item.c b/fs/xfs/xfs_buf_item.c index 3458a1264a3f6..3984779e59110 100644 --- a/fs/xfs/xfs_buf_item.c +++ b/fs/xfs/xfs_buf_item.c @@ -956,7 +956,7 @@ xfs_buf_item_relse( struct xfs_buf_log_item *bip = bp->b_log_item; trace_xfs_buf_item_relse(bp, _RET_IP_); - ASSERT(!(bip->bli_item.li_flags & XFS_LI_IN_AIL)); + ASSERT(!test_bit(XFS_LI_IN_AIL, &bip->bli_item.li_flags)); bp->b_log_item = NULL; if (list_empty(&bp->b_li_list)) -- GitLab From af952aeb4a8717fe4c7a872d9699fcfa85aa9e1c Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Mon, 16 Dec 2019 11:14:09 -0800 Subject: [PATCH 1065/1437] libxfs: resync with the userspace libxfs Prepare to resync the userspace libxfs with the kernel libxfs. There were a few things I missed -- a couple of static inline directory functions that have to be exported for xfs_repair; a couple of directory naming functions that make porting much easier if they're /not/ static inline; and a u16 usage that should have been uint16_t. None of these things are bugs in their own right; this just makes porting xfsprogs easier. Signed-off-by: Darrick J. Wong Reviewed-by: Eric Sandeen --- fs/xfs/libxfs/xfs_bmap.c | 2 +- fs/xfs/libxfs/xfs_dir2.c | 21 +++++++++++++++++++++ fs/xfs/libxfs/xfs_dir2_priv.h | 29 +++++++++-------------------- fs/xfs/libxfs/xfs_dir2_sf.c | 6 +++--- 4 files changed, 34 insertions(+), 24 deletions(-) diff --git a/fs/xfs/libxfs/xfs_bmap.c b/fs/xfs/libxfs/xfs_bmap.c index 4a802b3abe77c..4c2e046fbfade 100644 --- a/fs/xfs/libxfs/xfs_bmap.c +++ b/fs/xfs/libxfs/xfs_bmap.c @@ -4561,7 +4561,7 @@ xfs_bmapi_convert_delalloc( struct xfs_mount *mp = ip->i_mount; xfs_fileoff_t offset_fsb = XFS_B_TO_FSBT(mp, offset); struct xfs_bmalloca bma = { NULL }; - u16 flags = 0; + uint16_t flags = 0; struct xfs_trans *tp; int error; diff --git a/fs/xfs/libxfs/xfs_dir2.c b/fs/xfs/libxfs/xfs_dir2.c index 0aa87cbde49e8..dd6fcaaea318a 100644 --- a/fs/xfs/libxfs/xfs_dir2.c +++ b/fs/xfs/libxfs/xfs_dir2.c @@ -724,3 +724,24 @@ xfs_dir2_namecheck( /* There shouldn't be any slashes or nulls here */ return !memchr(name, '/', length) && !memchr(name, 0, length); } + +xfs_dahash_t +xfs_dir2_hashname( + struct xfs_mount *mp, + struct xfs_name *name) +{ + if (unlikely(xfs_sb_version_hasasciici(&mp->m_sb))) + return xfs_ascii_ci_hashname(name); + return xfs_da_hashname(name->name, name->len); +} + +enum xfs_dacmp +xfs_dir2_compname( + struct xfs_da_args *args, + const unsigned char *name, + int len) +{ + if (unlikely(xfs_sb_version_hasasciici(&args->dp->i_mount->m_sb))) + return xfs_ascii_ci_compname(args, name, len); + return xfs_da_compname(args, name, len); +} diff --git a/fs/xfs/libxfs/xfs_dir2_priv.h b/fs/xfs/libxfs/xfs_dir2_priv.h index c031c53d0f0d0..01ee0b9265721 100644 --- a/fs/xfs/libxfs/xfs_dir2_priv.h +++ b/fs/xfs/libxfs/xfs_dir2_priv.h @@ -175,6 +175,12 @@ extern int xfs_dir2_sf_lookup(struct xfs_da_args *args); extern int xfs_dir2_sf_removename(struct xfs_da_args *args); extern int xfs_dir2_sf_replace(struct xfs_da_args *args); extern xfs_failaddr_t xfs_dir2_sf_verify(struct xfs_inode *ip); +int xfs_dir2_sf_entsize(struct xfs_mount *mp, + struct xfs_dir2_sf_hdr *hdr, int len); +void xfs_dir2_sf_put_ino(struct xfs_mount *mp, struct xfs_dir2_sf_hdr *hdr, + struct xfs_dir2_sf_entry *sfep, xfs_ino_t ino); +void xfs_dir2_sf_put_ftype(struct xfs_mount *mp, + struct xfs_dir2_sf_entry *sfep, uint8_t ftype); /* xfs_dir2_readdir.c */ extern int xfs_readdir(struct xfs_trans *tp, struct xfs_inode *dp, @@ -194,25 +200,8 @@ xfs_dir2_data_entsize( return round_up(len, XFS_DIR2_DATA_ALIGN); } -static inline xfs_dahash_t -xfs_dir2_hashname( - struct xfs_mount *mp, - struct xfs_name *name) -{ - if (unlikely(xfs_sb_version_hasasciici(&mp->m_sb))) - return xfs_ascii_ci_hashname(name); - return xfs_da_hashname(name->name, name->len); -} - -static inline enum xfs_dacmp -xfs_dir2_compname( - struct xfs_da_args *args, - const unsigned char *name, - int len) -{ - if (unlikely(xfs_sb_version_hasasciici(&args->dp->i_mount->m_sb))) - return xfs_ascii_ci_compname(args, name, len); - return xfs_da_compname(args, name, len); -} +xfs_dahash_t xfs_dir2_hashname(struct xfs_mount *mp, struct xfs_name *name); +enum xfs_dacmp xfs_dir2_compname(struct xfs_da_args *args, + const unsigned char *name, int len); #endif /* __XFS_DIR2_PRIV_H__ */ diff --git a/fs/xfs/libxfs/xfs_dir2_sf.c b/fs/xfs/libxfs/xfs_dir2_sf.c index 8b94d33d232f5..7b7f6fb2ea3b2 100644 --- a/fs/xfs/libxfs/xfs_dir2_sf.c +++ b/fs/xfs/libxfs/xfs_dir2_sf.c @@ -37,7 +37,7 @@ static void xfs_dir2_sf_check(xfs_da_args_t *args); static void xfs_dir2_sf_toino4(xfs_da_args_t *args); static void xfs_dir2_sf_toino8(xfs_da_args_t *args); -static int +int xfs_dir2_sf_entsize( struct xfs_mount *mp, struct xfs_dir2_sf_hdr *hdr, @@ -84,7 +84,7 @@ xfs_dir2_sf_get_ino( return get_unaligned_be64(from) & XFS_MAXINUMBER; } -static void +void xfs_dir2_sf_put_ino( struct xfs_mount *mp, struct xfs_dir2_sf_hdr *hdr, @@ -145,7 +145,7 @@ xfs_dir2_sf_get_ftype( return XFS_DIR3_FT_UNKNOWN; } -static void +void xfs_dir2_sf_put_ftype( struct xfs_mount *mp, struct xfs_dir2_sf_entry *sfep, -- GitLab From 1cac233cfe71f21e069705a4930c18e48d897be6 Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Wed, 18 Dec 2019 11:09:55 -0800 Subject: [PATCH 1066/1437] xfs: refactor agfl length computation function Refactor xfs_alloc_min_freelist to accept a NULL @pag argument, in which case it returns the largest possible minimum length. This will be used in an upcoming patch to compute the length of the AGFL at mkfs time. Signed-off-by: Darrick J. Wong Reviewed-by: Brian Foster --- fs/xfs/libxfs/xfs_alloc.c | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/fs/xfs/libxfs/xfs_alloc.c b/fs/xfs/libxfs/xfs_alloc.c index c284e10af491c..fc93fd88ec89f 100644 --- a/fs/xfs/libxfs/xfs_alloc.c +++ b/fs/xfs/libxfs/xfs_alloc.c @@ -2248,24 +2248,32 @@ xfs_alloc_longest_free_extent( return pag->pagf_flcount > 0 || pag->pagf_longest > 0; } +/* + * Compute the minimum length of the AGFL in the given AG. If @pag is NULL, + * return the largest possible minimum length. + */ unsigned int xfs_alloc_min_freelist( struct xfs_mount *mp, struct xfs_perag *pag) { + /* AG btrees have at least 1 level. */ + static const uint8_t fake_levels[XFS_BTNUM_AGF] = {1, 1, 1}; + const uint8_t *levels = pag ? pag->pagf_levels : fake_levels; unsigned int min_free; + ASSERT(mp->m_ag_maxlevels > 0); + /* space needed by-bno freespace btree */ - min_free = min_t(unsigned int, pag->pagf_levels[XFS_BTNUM_BNOi] + 1, + min_free = min_t(unsigned int, levels[XFS_BTNUM_BNOi] + 1, mp->m_ag_maxlevels); /* space needed by-size freespace btree */ - min_free += min_t(unsigned int, pag->pagf_levels[XFS_BTNUM_CNTi] + 1, + min_free += min_t(unsigned int, levels[XFS_BTNUM_CNTi] + 1, mp->m_ag_maxlevels); /* space needed reverse mapping used space btree */ if (xfs_sb_version_hasrmapbt(&mp->m_sb)) - min_free += min_t(unsigned int, - pag->pagf_levels[XFS_BTNUM_RMAPi] + 1, - mp->m_rmap_maxlevels); + min_free += min_t(unsigned int, levels[XFS_BTNUM_RMAPi] + 1, + mp->m_rmap_maxlevels); return min_free; } -- GitLab From 4f5b1b3a8fa07dc8ecedfaf539b3deed8931a73e Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Wed, 18 Dec 2019 11:13:16 -0800 Subject: [PATCH 1067/1437] xfs: split the sunit parameter update into two parts If the administrator provided a sunit= mount option, we need to validate the raw parameter, convert the mount option units (512b blocks) into the internal unit (fs blocks), and then validate that the (now cooked) parameter doesn't screw anything up on disk. The incore inode geometry computation can depend on the new sunit option, but a subsequent patch will make validating the cooked value depends on the computed inode geometry, so break the sunit update into two steps. Signed-off-by: Darrick J. Wong Reviewed-by: Brian Foster --- fs/xfs/xfs_mount.c | 123 ++++++++++++++++++++++++++------------------- 1 file changed, 72 insertions(+), 51 deletions(-) diff --git a/fs/xfs/xfs_mount.c b/fs/xfs/xfs_mount.c index fca65109cf242..d2646e1f80ba3 100644 --- a/fs/xfs/xfs_mount.c +++ b/fs/xfs/xfs_mount.c @@ -360,66 +360,76 @@ release_buf: } /* - * Update alignment values based on mount options and sb values + * If we were provided with new sunit/swidth values as mount options, make sure + * that they pass basic alignment and superblock feature checks, and convert + * them into the same units (FSB) that everything else expects. This step + * /must/ be done before computing the inode geometry. */ STATIC int -xfs_update_alignment(xfs_mount_t *mp) +xfs_validate_new_dalign( + struct xfs_mount *mp) { - xfs_sb_t *sbp = &(mp->m_sb); + if (mp->m_dalign == 0) + return 0; - if (mp->m_dalign) { + /* + * If stripe unit and stripe width are not multiples + * of the fs blocksize turn off alignment. + */ + if ((BBTOB(mp->m_dalign) & mp->m_blockmask) || + (BBTOB(mp->m_swidth) & mp->m_blockmask)) { + xfs_warn(mp, + "alignment check failed: sunit/swidth vs. blocksize(%d)", + mp->m_sb.sb_blocksize); + return -EINVAL; + } else { /* - * If stripe unit and stripe width are not multiples - * of the fs blocksize turn off alignment. + * Convert the stripe unit and width to FSBs. */ - if ((BBTOB(mp->m_dalign) & mp->m_blockmask) || - (BBTOB(mp->m_swidth) & mp->m_blockmask)) { + mp->m_dalign = XFS_BB_TO_FSBT(mp, mp->m_dalign); + if (mp->m_dalign && (mp->m_sb.sb_agblocks % mp->m_dalign)) { xfs_warn(mp, - "alignment check failed: sunit/swidth vs. blocksize(%d)", - sbp->sb_blocksize); + "alignment check failed: sunit/swidth vs. agsize(%d)", + mp->m_sb.sb_agblocks); return -EINVAL; - } else { - /* - * Convert the stripe unit and width to FSBs. - */ - mp->m_dalign = XFS_BB_TO_FSBT(mp, mp->m_dalign); - if (mp->m_dalign && (sbp->sb_agblocks % mp->m_dalign)) { - xfs_warn(mp, - "alignment check failed: sunit/swidth vs. agsize(%d)", - sbp->sb_agblocks); - return -EINVAL; - } else if (mp->m_dalign) { - mp->m_swidth = XFS_BB_TO_FSBT(mp, mp->m_swidth); - } else { - xfs_warn(mp, - "alignment check failed: sunit(%d) less than bsize(%d)", - mp->m_dalign, sbp->sb_blocksize); - return -EINVAL; - } - } - - /* - * Update superblock with new values - * and log changes - */ - if (xfs_sb_version_hasdalign(sbp)) { - if (sbp->sb_unit != mp->m_dalign) { - sbp->sb_unit = mp->m_dalign; - mp->m_update_sb = true; - } - if (sbp->sb_width != mp->m_swidth) { - sbp->sb_width = mp->m_swidth; - mp->m_update_sb = true; - } + } else if (mp->m_dalign) { + mp->m_swidth = XFS_BB_TO_FSBT(mp, mp->m_swidth); } else { xfs_warn(mp, - "cannot change alignment: superblock does not support data alignment"); + "alignment check failed: sunit(%d) less than bsize(%d)", + mp->m_dalign, mp->m_sb.sb_blocksize); return -EINVAL; } + } + + if (!xfs_sb_version_hasdalign(&mp->m_sb)) { + xfs_warn(mp, +"cannot change alignment: superblock does not support data alignment"); + return -EINVAL; + } + + return 0; +} + +/* Update alignment values based on mount options and sb values. */ +STATIC int +xfs_update_alignment( + struct xfs_mount *mp) +{ + struct xfs_sb *sbp = &mp->m_sb; + + if (mp->m_dalign) { + if (sbp->sb_unit == mp->m_dalign && + sbp->sb_width == mp->m_swidth) + return 0; + + sbp->sb_unit = mp->m_dalign; + sbp->sb_width = mp->m_swidth; + mp->m_update_sb = true; } else if ((mp->m_flags & XFS_MOUNT_NOALIGN) != XFS_MOUNT_NOALIGN && xfs_sb_version_hasdalign(&mp->m_sb)) { - mp->m_dalign = sbp->sb_unit; - mp->m_swidth = sbp->sb_width; + mp->m_dalign = sbp->sb_unit; + mp->m_swidth = sbp->sb_width; } return 0; @@ -648,12 +658,12 @@ xfs_mountfs( } /* - * Check if sb_agblocks is aligned at stripe boundary - * If sb_agblocks is NOT aligned turn off m_dalign since - * allocator alignment is within an ag, therefore ag has - * to be aligned at stripe boundary. + * If we were given new sunit/swidth options, do some basic validation + * checks and convert the incore dalign and swidth values to the + * same units (FSB) that everything else uses. This /must/ happen + * before computing the inode geometry. */ - error = xfs_update_alignment(mp); + error = xfs_validate_new_dalign(mp); if (error) goto out; @@ -664,6 +674,17 @@ xfs_mountfs( xfs_rmapbt_compute_maxlevels(mp); xfs_refcountbt_compute_maxlevels(mp); + /* + * Check if sb_agblocks is aligned at stripe boundary. If sb_agblocks + * is NOT aligned turn off m_dalign since allocator alignment is within + * an ag, therefore ag has to be aligned at stripe boundary. Note that + * we must compute the free space and rmap btree geometry before doing + * this. + */ + error = xfs_update_alignment(mp); + if (error) + goto out; + /* enable fail_at_unmount as default */ mp->m_fail_unmount = true; -- GitLab From 13eaec4b2adf2657b8167b67e27c97cc7314d923 Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Wed, 11 Dec 2019 13:19:06 -0800 Subject: [PATCH 1068/1437] xfs: don't commit sunit/swidth updates to disk if that would cause repair failures Alex Lyakas reported[1] that mounting an xfs filesystem with new sunit and swidth values could cause xfs_repair to fail loudly. The problem here is that repair calculates the where mkfs should have allocated the root inode, based on the superblock geometry. The allocation decisions depend on sunit, which means that we really can't go updating sunit if it would lead to a subsequent repair failure on an otherwise correct filesystem. Port from xfs_repair some code that computes the location of the root inode and teach mount to skip the ondisk update if it would cause problems for repair. Along the way we'll update the documentation, provide a function for computing the minimum AGFL size instead of open-coding it, and cut down some indenting in the mount code. Note that we allow the mount to proceed (and new allocations will reflect this new geometry) because we've never screened this kind of thing before. We'll have to wait for a new future incompat feature to enforce correct behavior, alas. Note that the geometry reporting always uses the superblock values, not the incore ones, so that is what xfs_info and xfs_growfs will report. [1] https://lore.kernel.org/linux-xfs/20191125130744.GA44777@bfoster/T/#m00f9594b511e076e2fcdd489d78bc30216d72a7d Reported-by: Alex Lyakas Signed-off-by: Darrick J. Wong Reviewed-by: Brian Foster --- fs/xfs/libxfs/xfs_ialloc.c | 64 ++++++++++++++++++++++++++++++++++++++ fs/xfs/libxfs/xfs_ialloc.h | 1 + fs/xfs/xfs_mount.c | 45 ++++++++++++++++++++++++++- fs/xfs/xfs_trace.h | 21 +++++++++++++ 4 files changed, 130 insertions(+), 1 deletion(-) diff --git a/fs/xfs/libxfs/xfs_ialloc.c b/fs/xfs/libxfs/xfs_ialloc.c index 988cde7744e69..5b759af4d1652 100644 --- a/fs/xfs/libxfs/xfs_ialloc.c +++ b/fs/xfs/libxfs/xfs_ialloc.c @@ -2909,3 +2909,67 @@ xfs_ialloc_setup_geometry( else igeo->ialloc_align = 0; } + +/* Compute the location of the root directory inode that is laid out by mkfs. */ +xfs_ino_t +xfs_ialloc_calc_rootino( + struct xfs_mount *mp, + int sunit) +{ + struct xfs_ino_geometry *igeo = M_IGEO(mp); + xfs_agblock_t first_bno; + + /* + * Pre-calculate the geometry of AG 0. We know what it looks like + * because libxfs knows how to create allocation groups now. + * + * first_bno is the first block in which mkfs could possibly have + * allocated the root directory inode, once we factor in the metadata + * that mkfs formats before it. Namely, the four AG headers... + */ + first_bno = howmany(4 * mp->m_sb.sb_sectsize, mp->m_sb.sb_blocksize); + + /* ...the two free space btree roots... */ + first_bno += 2; + + /* ...the inode btree root... */ + first_bno += 1; + + /* ...the initial AGFL... */ + first_bno += xfs_alloc_min_freelist(mp, NULL); + + /* ...the free inode btree root... */ + if (xfs_sb_version_hasfinobt(&mp->m_sb)) + first_bno++; + + /* ...the reverse mapping btree root... */ + if (xfs_sb_version_hasrmapbt(&mp->m_sb)) + first_bno++; + + /* ...the reference count btree... */ + if (xfs_sb_version_hasreflink(&mp->m_sb)) + first_bno++; + + /* + * ...and the log, if it is allocated in the first allocation group. + * + * This can happen with filesystems that only have a single + * allocation group, or very odd geometries created by old mkfs + * versions on very small filesystems. + */ + if (mp->m_sb.sb_logstart && + XFS_FSB_TO_AGNO(mp, mp->m_sb.sb_logstart) == 0) + first_bno += mp->m_sb.sb_logblocks; + + /* + * Now round first_bno up to whatever allocation alignment is given + * by the filesystem or was passed in. + */ + if (xfs_sb_version_hasdalign(&mp->m_sb) && igeo->ialloc_align > 0) + first_bno = roundup(first_bno, sunit); + else if (xfs_sb_version_hasalign(&mp->m_sb) && + mp->m_sb.sb_inoalignmt > 1) + first_bno = roundup(first_bno, mp->m_sb.sb_inoalignmt); + + return XFS_AGINO_TO_INO(mp, 0, XFS_AGB_TO_AGINO(mp, first_bno)); +} diff --git a/fs/xfs/libxfs/xfs_ialloc.h b/fs/xfs/libxfs/xfs_ialloc.h index 323592d563d52..72b3468b97b15 100644 --- a/fs/xfs/libxfs/xfs_ialloc.h +++ b/fs/xfs/libxfs/xfs_ialloc.h @@ -152,5 +152,6 @@ int xfs_inobt_insert_rec(struct xfs_btree_cur *cur, uint16_t holemask, int xfs_ialloc_cluster_alignment(struct xfs_mount *mp); void xfs_ialloc_setup_geometry(struct xfs_mount *mp); +xfs_ino_t xfs_ialloc_calc_rootino(struct xfs_mount *mp, int sunit); #endif /* __XFS_IALLOC_H__ */ diff --git a/fs/xfs/xfs_mount.c b/fs/xfs/xfs_mount.c index d2646e1f80ba3..56efe140c9238 100644 --- a/fs/xfs/xfs_mount.c +++ b/fs/xfs/xfs_mount.c @@ -31,7 +31,7 @@ #include "xfs_reflink.h" #include "xfs_extent_busy.h" #include "xfs_health.h" - +#include "xfs_trace.h" static DEFINE_MUTEX(xfs_uuid_table_mutex); static int xfs_uuid_table_size; @@ -359,6 +359,42 @@ release_buf: return error; } +/* + * If the sunit/swidth change would move the precomputed root inode value, we + * must reject the ondisk change because repair will stumble over that. + * However, we allow the mount to proceed because we never rejected this + * combination before. Returns true to update the sb, false otherwise. + */ +static inline int +xfs_check_new_dalign( + struct xfs_mount *mp, + int new_dalign, + bool *update_sb) +{ + struct xfs_sb *sbp = &mp->m_sb; + xfs_ino_t calc_ino; + + calc_ino = xfs_ialloc_calc_rootino(mp, new_dalign); + trace_xfs_check_new_dalign(mp, new_dalign, calc_ino); + + if (sbp->sb_rootino == calc_ino) { + *update_sb = true; + return 0; + } + + xfs_warn(mp, +"Cannot change stripe alignment; would require moving root inode."); + + /* + * XXX: Next time we add a new incompat feature, this should start + * returning -EINVAL to fail the mount. Until then, spit out a warning + * that we're ignoring the administrator's instructions. + */ + xfs_warn(mp, "Skipping superblock stripe alignment update."); + *update_sb = false; + return 0; +} + /* * If we were provided with new sunit/swidth values as mount options, make sure * that they pass basic alignment and superblock feature checks, and convert @@ -419,10 +455,17 @@ xfs_update_alignment( struct xfs_sb *sbp = &mp->m_sb; if (mp->m_dalign) { + bool update_sb; + int error; + if (sbp->sb_unit == mp->m_dalign && sbp->sb_width == mp->m_swidth) return 0; + error = xfs_check_new_dalign(mp, mp->m_dalign, &update_sb); + if (error || !update_sb) + return error; + sbp->sb_unit = mp->m_dalign; sbp->sb_width = mp->m_swidth; mp->m_update_sb = true; diff --git a/fs/xfs/xfs_trace.h b/fs/xfs/xfs_trace.h index c13bb3655e489..a86be7f807ee5 100644 --- a/fs/xfs/xfs_trace.h +++ b/fs/xfs/xfs_trace.h @@ -3573,6 +3573,27 @@ DEFINE_KMEM_EVENT(kmem_alloc_large); DEFINE_KMEM_EVENT(kmem_realloc); DEFINE_KMEM_EVENT(kmem_zone_alloc); +TRACE_EVENT(xfs_check_new_dalign, + TP_PROTO(struct xfs_mount *mp, int new_dalign, xfs_ino_t calc_rootino), + TP_ARGS(mp, new_dalign, calc_rootino), + TP_STRUCT__entry( + __field(dev_t, dev) + __field(int, new_dalign) + __field(xfs_ino_t, sb_rootino) + __field(xfs_ino_t, calc_rootino) + ), + TP_fast_assign( + __entry->dev = mp->m_super->s_dev; + __entry->new_dalign = new_dalign; + __entry->sb_rootino = mp->m_sb.sb_rootino; + __entry->calc_rootino = calc_rootino; + ), + TP_printk("dev %d:%d new_dalign %d sb_rootino %llu calc_rootino %llu", + MAJOR(__entry->dev), MINOR(__entry->dev), + __entry->new_dalign, __entry->sb_rootino, + __entry->calc_rootino) +) + #endif /* _TRACE_XFS_H */ #undef TRACE_INCLUDE_PATH -- GitLab From 7be3c2331d506a61da78cfe426a037a3c7de7c71 Mon Sep 17 00:00:00 2001 From: Ahmad Masri Date: Wed, 18 Dec 2019 20:10:04 +0200 Subject: [PATCH 1069/1437] wil6210: dump Rx status message on errors Dump all the Rx status message on different errors to allow more visibility of the case. Signed-off-by: Ahmad Masri Signed-off-by: Maya Erez Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/wil6210/txrx_edma.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/net/wireless/ath/wil6210/txrx_edma.c b/drivers/net/wireless/ath/wil6210/txrx_edma.c index 02548d40253c7..7bfe867c7509e 100644 --- a/drivers/net/wireless/ath/wil6210/txrx_edma.c +++ b/drivers/net/wireless/ath/wil6210/txrx_edma.c @@ -903,6 +903,11 @@ again: if (unlikely(!wil_val_in_range(buff_id, 1, wil->rx_buff_mgmt.size))) { wil_err(wil, "Corrupt buff_id=%d, sring->swhead=%d\n", buff_id, sring->swhead); + print_hex_dump(KERN_ERR, "RxS ", DUMP_PREFIX_OFFSET, 16, 1, + msg, wil->use_compressed_rx_status ? + sizeof(struct wil_rx_status_compressed) : + sizeof(struct wil_rx_status_extended), false); + wil_rx_status_reset_buff_id(sring); wil_sring_advance_swhead(sring); sring->invalid_buff_id_cnt++; @@ -963,6 +968,11 @@ again: if (unlikely(dmalen > sz)) { wil_err(wil, "Rx size too large: %d bytes!\n", dmalen); + print_hex_dump(KERN_ERR, "RxS ", DUMP_PREFIX_OFFSET, 16, 1, + msg, wil->use_compressed_rx_status ? + sizeof(struct wil_rx_status_compressed) : + sizeof(struct wil_rx_status_extended), false); + stats->rx_large_frame++; rxdata->skipping = true; } -- GitLab From d3214d42806676e47eec2e5a5ed041ec40b9dd35 Mon Sep 17 00:00:00 2001 From: Alexei Avshalom Lazar Date: Wed, 18 Dec 2019 20:10:07 +0200 Subject: [PATCH 1070/1437] wil6210: minimize the time that mem_lock is held mem_lock is taken for the entire wil_reset(). Optimize this by taking mem_lock just before device is being reset and release the lock after FW download. Signed-off-by: Alexei Avshalom Lazar Signed-off-by: Maya Erez Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/wil6210/main.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/ath/wil6210/main.c b/drivers/net/wireless/ath/wil6210/main.c index 06091d8a9e239..6d39547e20fd2 100644 --- a/drivers/net/wireless/ath/wil6210/main.c +++ b/drivers/net/wireless/ath/wil6210/main.c @@ -1654,6 +1654,8 @@ int wil_reset(struct wil6210_priv *wil, bool load_fw) /* Disable device led before reset*/ wmi_led_cfg(wil, false); + down_write(&wil->mem_lock); + /* prevent NAPI from being scheduled and prevent wmi commands */ mutex_lock(&wil->wmi_mutex); if (test_bit(wil_status_suspending, wil->status)) @@ -1702,6 +1704,7 @@ int wil_reset(struct wil6210_priv *wil, bool load_fw) if (wil->secured_boot) { wil_err(wil, "secured boot is not supported\n"); + up_write(&wil->mem_lock); return -ENOTSUPP; } @@ -1737,6 +1740,8 @@ int wil_reset(struct wil6210_priv *wil, bool load_fw) clear_bit(wil_status_resetting, wil->status); + up_write(&wil->mem_lock); + if (load_fw) { wil_unmask_irq(wil); @@ -1786,6 +1791,7 @@ int wil_reset(struct wil6210_priv *wil, bool load_fw) return rc; out: + up_write(&wil->mem_lock); clear_bit(wil_status_resetting, wil->status); return rc; } @@ -1811,9 +1817,7 @@ int __wil_up(struct wil6210_priv *wil) WARN_ON(!mutex_is_locked(&wil->mutex)); - down_write(&wil->mem_lock); rc = wil_reset(wil, true); - up_write(&wil->mem_lock); if (rc) return rc; @@ -1905,9 +1909,7 @@ int __wil_down(struct wil6210_priv *wil) wil_abort_scan_all_vifs(wil, false); mutex_unlock(&wil->vif_mutex); - down_write(&wil->mem_lock); rc = wil_reset(wil, false); - up_write(&wil->mem_lock); return rc; } -- GitLab From c715b38473c842db34c408ede2d5727d2b50cf1c Mon Sep 17 00:00:00 2001 From: Alexei Avshalom Lazar Date: Wed, 18 Dec 2019 20:10:10 +0200 Subject: [PATCH 1071/1437] wil6210: take mem_lock for writing in crash dump collection On some crash dump cases mem_lock is already taken, error returns and crash dump copy fails. In this case wait until mem_lock available instead of failing the operation. Also take the mem_lock for writing to prevent other threads from altering the state of the device while collecting crash dump. Signed-off-by: Alexei Avshalom Lazar Signed-off-by: Maya Erez Signed-off-by: Kalle Valo --- .../net/wireless/ath/wil6210/wil_crash_dump.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/ath/wil6210/wil_crash_dump.c b/drivers/net/wireless/ath/wil6210/wil_crash_dump.c index 1332eb8c831ff..89c12cb2aaab2 100644 --- a/drivers/net/wireless/ath/wil6210/wil_crash_dump.c +++ b/drivers/net/wireless/ath/wil6210/wil_crash_dump.c @@ -46,7 +46,7 @@ static int wil_fw_get_crash_dump_bounds(struct wil6210_priv *wil, int wil_fw_copy_crash_dump(struct wil6210_priv *wil, void *dest, u32 size) { - int i, rc; + int i; const struct fw_map *map; void *data; u32 host_min, dump_size, offset, len; @@ -62,9 +62,15 @@ int wil_fw_copy_crash_dump(struct wil6210_priv *wil, void *dest, u32 size) return -EINVAL; } - rc = wil_mem_access_lock(wil); - if (rc) - return rc; + down_write(&wil->mem_lock); + + if (test_bit(wil_status_suspending, wil->status) || + test_bit(wil_status_suspended, wil->status)) { + wil_err(wil, + "suspend/resume in progress. cannot copy crash dump\n"); + up_write(&wil->mem_lock); + return -EBUSY; + } /* copy to crash dump area */ for (i = 0; i < ARRAY_SIZE(fw_mapping); i++) { @@ -84,7 +90,8 @@ int wil_fw_copy_crash_dump(struct wil6210_priv *wil, void *dest, u32 size) wil_memcpy_fromio_32((void * __force)(dest + offset), (const void __iomem * __force)data, len); } - wil_mem_access_unlock(wil); + + up_write(&wil->mem_lock); return 0; } -- GitLab From 5c5d88a29dd4fe8154c8d262163a96d0add70917 Mon Sep 17 00:00:00 2001 From: Alexei Avshalom Lazar Date: Wed, 18 Dec 2019 20:10:13 +0200 Subject: [PATCH 1072/1437] wil6210: add verification for cid upper bound max_assoc_sta can receive values (from the user or from the FW) that are higher than WIL6210_MAX_CID. Verify that cid doesn't exceed the upper bound of WIL6210_MAX_CID. Signed-off-by: Alexei Avshalom Lazar Signed-off-by: Maya Erez Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/wil6210/wil6210.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h index 97626bfd4dac8..bf00f0693a3d7 100644 --- a/drivers/net/wireless/ath/wil6210/wil6210.h +++ b/drivers/net/wireless/ath/wil6210/wil6210.h @@ -1148,7 +1148,7 @@ static inline void wil_c(struct wil6210_priv *wil, u32 reg, u32 val) */ static inline bool wil_cid_valid(struct wil6210_priv *wil, int cid) { - return (cid >= 0 && cid < wil->max_assoc_sta); + return (cid >= 0 && cid < wil->max_assoc_sta && cid < WIL6210_MAX_CID); } void wil_get_board_file(struct wil6210_priv *wil, char *buf, size_t len); -- GitLab From c99d5f8d61784f152fb3c2e217fa17b05bbd640a Mon Sep 17 00:00:00 2001 From: Dedy Lansky Date: Wed, 18 Dec 2019 20:10:16 +0200 Subject: [PATCH 1073/1437] wil6210: reduce ucode_debug memory region ucode_debug memory region defined as 4K bytes. Fix this according to Talyn device memory map. Signed-off-by: Dedy Lansky Signed-off-by: Maya Erez Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/wil6210/wmi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/ath/wil6210/wmi.c b/drivers/net/wireless/ath/wil6210/wmi.c index 7a0d934eb2710..3aca86b3071d9 100644 --- a/drivers/net/wireless/ath/wil6210/wmi.c +++ b/drivers/net/wireless/ath/wil6210/wmi.c @@ -196,8 +196,8 @@ const struct fw_map talyn_mb_fw_mapping[] = { {0x8c0000, 0x8c0210, 0x8c0000, "dum_user_rgf", true, true}, /* DMA OFU 296b */ {0x8c2000, 0x8c2128, 0x8c2000, "dma_ofu", true, true}, - /* ucode debug 4k */ - {0x8c3000, 0x8c4000, 0x8c3000, "ucode_debug", true, true}, + /* ucode debug 256b */ + {0x8c3000, 0x8c3100, 0x8c3000, "ucode_debug", true, true}, /* upper area 1536k */ {0x900000, 0xa80000, 0x900000, "upper", true, true}, /* UCODE areas - accessible by debugfs blobs but not by -- GitLab From 18beb61d848d267371a769114d90bd1be6239eda Mon Sep 17 00:00:00 2001 From: Dedy Lansky Date: Wed, 18 Dec 2019 20:10:18 +0200 Subject: [PATCH 1074/1437] wil6210: fix MID valid bits in Rx status message Fix incorrect definitions of MAC ID bits inside Rx status message. Signed-off-by: Dedy Lansky Signed-off-by: Maya Erez Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/wil6210/txrx_edma.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/ath/wil6210/txrx_edma.h b/drivers/net/wireless/ath/wil6210/txrx_edma.h index c744c65225dac..c736f7413a35f 100644 --- a/drivers/net/wireless/ath/wil6210/txrx_edma.h +++ b/drivers/net/wireless/ath/wil6210/txrx_edma.h @@ -46,7 +46,7 @@ #define WIL_RX_EDMA_DLPF_LU_MISS_TID_POS 5 -#define WIL_RX_EDMA_MID_VALID_BIT BIT(22) +#define WIL_RX_EDMA_MID_VALID_BIT BIT(20) #define WIL_EDMA_DESC_TX_MAC_CFG_0_QID_POS 16 #define WIL_EDMA_DESC_TX_MAC_CFG_0_QID_LEN 6 @@ -244,8 +244,8 @@ struct wil_ring_tx_status { * calculated, Bit1- L4Err - TCP/UDP Checksum Error * bit 7 : Reserved:1 * bit 8..19 : Flow ID:12 - MSDU flow ID - * bit 20..21 : MID:2 - The MAC ID - * bit 22 : MID_V:1 - The MAC ID field is valid + * bit 20 : MID_V:1 - The MAC ID field is valid + * bit 21..22 : MID:2 - The MAC ID * bit 23 : L3T:1 - IP types: 0-IPv6, 1-IPv4 * bit 24 : L4T:1 - Layer 4 Type: 0-UDP, 1-TCP * bit 25 : BC:1 - The received MPDU is broadcast @@ -479,7 +479,7 @@ static inline int wil_rx_status_get_mid(void *msg) return 0; /* use the default MID */ return WIL_GET_BITS(((struct wil_rx_status_compressed *)msg)->d0, - 20, 21); + 21, 22); } static inline int wil_rx_status_get_error(void *msg) -- GitLab From 5e5f069c30ccdbc09bd548858ee75d0487e74490 Mon Sep 17 00:00:00 2001 From: Ahmad Masri Date: Wed, 18 Dec 2019 20:10:21 +0200 Subject: [PATCH 1075/1437] wil6210: support set_multicast_to_unicast cfg80211 operation Wil6210 AP has a separate ring for transmitting multicast packets, multicast packets are transmitted without an ack from the receiver side. Therefore, 802.11 spec defines some low MCS rates for multicat packets. However, there is no guarantee that these packets were really received and handled on the client side. Some applications that rely on multicast packets, may prefer to transmit these packets as a unicast to ensure reliability, and also to ensure better performance with high MCS rates. multicast to unicast is done by duplicating multicast packets to all clients and changing the DA (multicast) to the MAC address of the client. see NL80211_CMD_SET_MULTICAST_TO_UNICAST for more info. Signed-off-by: Ahmad Masri Signed-off-by: Maya Erez Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/wil6210/cfg80211.c | 16 +++++++++++ drivers/net/wireless/ath/wil6210/txrx.c | 32 ++++++++++++++++++++- drivers/net/wireless/ath/wil6210/wil6210.h | 1 + 3 files changed, 48 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/wil6210/cfg80211.c b/drivers/net/wireless/ath/wil6210/cfg80211.c index 7d6f14420855e..5a3aff1314723 100644 --- a/drivers/net/wireless/ath/wil6210/cfg80211.c +++ b/drivers/net/wireless/ath/wil6210/cfg80211.c @@ -2579,6 +2579,21 @@ wil_cfg80211_update_ft_ies(struct wiphy *wiphy, struct net_device *dev, return rc; } +static int wil_cfg80211_set_multicast_to_unicast(struct wiphy *wiphy, + struct net_device *dev, + const bool enabled) +{ + struct wil6210_priv *wil = wiphy_to_wil(wiphy); + + if (wil->multicast_to_unicast == enabled) + return 0; + + wil_info(wil, "set multicast to unicast, enabled=%d\n", enabled); + wil->multicast_to_unicast = enabled; + + return 0; +} + static const struct cfg80211_ops wil_cfg80211_ops = { .add_virtual_intf = wil_cfg80211_add_iface, .del_virtual_intf = wil_cfg80211_del_iface, @@ -2615,6 +2630,7 @@ static const struct cfg80211_ops wil_cfg80211_ops = { .sched_scan_start = wil_cfg80211_sched_scan_start, .sched_scan_stop = wil_cfg80211_sched_scan_stop, .update_ft_ies = wil_cfg80211_update_ft_ies, + .set_multicast_to_unicast = wil_cfg80211_set_multicast_to_unicast, }; static void wil_wiphy_init(struct wiphy *wiphy) diff --git a/drivers/net/wireless/ath/wil6210/txrx.c b/drivers/net/wireless/ath/wil6210/txrx.c index 8ebc6d59aa743..17118d643d7e1 100644 --- a/drivers/net/wireless/ath/wil6210/txrx.c +++ b/drivers/net/wireless/ath/wil6210/txrx.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include @@ -1529,6 +1530,35 @@ static struct wil_ring *wil_find_tx_bcast_1(struct wil6210_priv *wil, return v; } +/* apply multicast to unicast only for ARP and IP packets + * (see NL80211_CMD_SET_MULTICAST_TO_UNICAST for more info) + */ +static bool wil_check_multicast_to_unicast(struct wil6210_priv *wil, + struct sk_buff *skb) +{ + const struct ethhdr *eth = (void *)skb->data; + const struct vlan_ethhdr *ethvlan = (void *)skb->data; + __be16 ethertype; + + if (!wil->multicast_to_unicast) + return false; + + /* multicast to unicast conversion only for some payload */ + ethertype = eth->h_proto; + if (ethertype == htons(ETH_P_8021Q) && skb->len >= VLAN_ETH_HLEN) + ethertype = ethvlan->h_vlan_encapsulated_proto; + switch (ethertype) { + case htons(ETH_P_ARP): + case htons(ETH_P_IP): + case htons(ETH_P_IPV6): + break; + default: + return false; + } + + return true; +} + static void wil_set_da_for_vring(struct wil6210_priv *wil, struct sk_buff *skb, int vring_index) { @@ -2336,7 +2366,7 @@ netdev_tx_t wil_start_xmit(struct sk_buff *skb, struct net_device *ndev) /* in STA mode (ESS), all to same VRING (to AP) */ ring = wil_find_tx_ring_sta(wil, vif, skb); } else if (bcast) { - if (vif->pbss) + if (vif->pbss || wil_check_multicast_to_unicast(wil, skb)) /* in pbss, no bcast VRING - duplicate skb in * all stations VRINGs */ diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h index bf00f0693a3d7..21399ced2a5d5 100644 --- a/drivers/net/wireless/ath/wil6210/wil6210.h +++ b/drivers/net/wireless/ath/wil6210/wil6210.h @@ -1059,6 +1059,7 @@ struct wil6210_priv { u32 max_agg_wsize; u32 max_ampdu_size; + u8 multicast_to_unicast; }; #define wil_to_wiphy(i) (i->wiphy) -- GitLab From 4315a74a482f86750a1e78ac536c5baeb05ff6b1 Mon Sep 17 00:00:00 2001 From: Dedy Lansky Date: Wed, 18 Dec 2019 20:10:24 +0200 Subject: [PATCH 1076/1437] wil6210: add support for set_cqm_rssi_config set_cqm_rssi_config() is used by the kernel to configure connection quality monitor RSSI threshold. wil6210 uses WMI_SET_LINK_MONITOR_CMDID to set the RSSI threshold to FW which in turn reports RSSI threshold changes with WMI_LINK_MONITOR_EVENTID. Signed-off-by: Dedy Lansky Signed-off-by: Maya Erez Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/wil6210/cfg80211.c | 18 +++++ drivers/net/wireless/ath/wil6210/wil6210.h | 3 + drivers/net/wireless/ath/wil6210/wmi.c | 76 +++++++++++++++++++++ drivers/net/wireless/ath/wil6210/wmi.h | 33 +++++++++ 4 files changed, 130 insertions(+) diff --git a/drivers/net/wireless/ath/wil6210/cfg80211.c b/drivers/net/wireless/ath/wil6210/cfg80211.c index 5a3aff1314723..0851d2bede891 100644 --- a/drivers/net/wireless/ath/wil6210/cfg80211.c +++ b/drivers/net/wireless/ath/wil6210/cfg80211.c @@ -2594,6 +2594,23 @@ static int wil_cfg80211_set_multicast_to_unicast(struct wiphy *wiphy, return 0; } +static int wil_cfg80211_set_cqm_rssi_config(struct wiphy *wiphy, + struct net_device *dev, + s32 rssi_thold, u32 rssi_hyst) +{ + struct wil6210_priv *wil = wiphy_to_wil(wiphy); + int rc; + + wil->cqm_rssi_thold = rssi_thold; + + rc = wmi_set_cqm_rssi_config(wil, rssi_thold, rssi_hyst); + if (rc) + /* reset stored value upon failure */ + wil->cqm_rssi_thold = 0; + + return rc; +} + static const struct cfg80211_ops wil_cfg80211_ops = { .add_virtual_intf = wil_cfg80211_add_iface, .del_virtual_intf = wil_cfg80211_del_iface, @@ -2625,6 +2642,7 @@ static const struct cfg80211_ops wil_cfg80211_ops = { .start_p2p_device = wil_cfg80211_start_p2p_device, .stop_p2p_device = wil_cfg80211_stop_p2p_device, .set_power_mgmt = wil_cfg80211_set_power_mgmt, + .set_cqm_rssi_config = wil_cfg80211_set_cqm_rssi_config, .suspend = wil_cfg80211_suspend, .resume = wil_cfg80211_resume, .sched_scan_start = wil_cfg80211_sched_scan_start, diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h index 21399ced2a5d5..5dc881d3c057d 100644 --- a/drivers/net/wireless/ath/wil6210/wil6210.h +++ b/drivers/net/wireless/ath/wil6210/wil6210.h @@ -1060,6 +1060,7 @@ struct wil6210_priv { u32 max_agg_wsize; u32 max_ampdu_size; u8 multicast_to_unicast; + s32 cqm_rssi_thold; }; #define wil_to_wiphy(i) (i->wiphy) @@ -1441,4 +1442,6 @@ int wmi_addba_rx_resp_edma(struct wil6210_priv *wil, u8 mid, u8 cid, void update_supported_bands(struct wil6210_priv *wil); void wil_clear_fw_log_addr(struct wil6210_priv *wil); +int wmi_set_cqm_rssi_config(struct wil6210_priv *wil, + s32 rssi_thold, u32 rssi_hyst); #endif /* __WIL6210_H__ */ diff --git a/drivers/net/wireless/ath/wil6210/wmi.c b/drivers/net/wireless/ath/wil6210/wmi.c index 3aca86b3071d9..dcba0a4c47b43 100644 --- a/drivers/net/wireless/ath/wil6210/wmi.c +++ b/drivers/net/wireless/ath/wil6210/wmi.c @@ -476,6 +476,8 @@ static const char *cmdid2name(u16 cmdid) return "WMI_RBUFCAP_CFG_CMD"; case WMI_TEMP_SENSE_ALL_CMDID: return "WMI_TEMP_SENSE_ALL_CMDID"; + case WMI_SET_LINK_MONITOR_CMDID: + return "WMI_SET_LINK_MONITOR_CMD"; default: return "Untracked CMD"; } @@ -624,6 +626,10 @@ static const char *eventid2name(u16 eventid) return "WMI_RBUFCAP_CFG_EVENT"; case WMI_TEMP_SENSE_ALL_DONE_EVENTID: return "WMI_TEMP_SENSE_ALL_DONE_EVENTID"; + case WMI_SET_LINK_MONITOR_EVENTID: + return "WMI_SET_LINK_MONITOR_EVENT"; + case WMI_LINK_MONITOR_EVENTID: + return "WMI_LINK_MONITOR_EVENT"; default: return "Untracked EVENT"; } @@ -1836,6 +1842,32 @@ fail: wil6210_disconnect(vif, NULL, WLAN_REASON_PREV_AUTH_NOT_VALID); } +static void +wmi_evt_link_monitor(struct wil6210_vif *vif, int id, void *d, int len) +{ + struct wil6210_priv *wil = vif_to_wil(vif); + struct net_device *ndev = vif_to_ndev(vif); + struct wmi_link_monitor_event *evt = d; + enum nl80211_cqm_rssi_threshold_event event_type; + + if (len < sizeof(*evt)) { + wil_err(wil, "link monitor event too short %d\n", len); + return; + } + + wil_dbg_wmi(wil, "link monitor event, type %d rssi %d (stored %d)\n", + evt->type, evt->rssi_level, wil->cqm_rssi_thold); + + if (evt->type != WMI_LINK_MONITOR_NOTIF_RSSI_THRESHOLD_EVT) + /* ignore */ + return; + + event_type = (evt->rssi_level > wil->cqm_rssi_thold ? + NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH : + NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW); + cfg80211_cqm_rssi_notify(ndev, event_type, evt->rssi_level, GFP_KERNEL); +} + /** * Some events are ignored for purpose; and need not be interpreted as * "unhandled events" @@ -1869,6 +1901,7 @@ static const struct { {WMI_LINK_STATS_EVENTID, wmi_evt_link_stats}, {WMI_FT_AUTH_STATUS_EVENTID, wmi_evt_auth_status}, {WMI_FT_REASSOC_STATUS_EVENTID, wmi_evt_reassoc_status}, + {WMI_LINK_MONITOR_EVENTID, wmi_evt_link_monitor}, }; /* @@ -3981,3 +4014,46 @@ int wmi_link_stats_cfg(struct wil6210_vif *vif, u32 type, u8 cid, u32 interval) return 0; } + +int wmi_set_cqm_rssi_config(struct wil6210_priv *wil, + s32 rssi_thold, u32 rssi_hyst) +{ + struct net_device *ndev = wil->main_ndev; + struct wil6210_vif *vif = ndev_to_vif(ndev); + int rc; + struct { + struct wmi_set_link_monitor_cmd cmd; + s8 rssi_thold; + } __packed cmd = { + .cmd = { + .rssi_hyst = rssi_hyst, + .rssi_thresholds_list_size = 1, + }, + .rssi_thold = rssi_thold, + }; + struct { + struct wmi_cmd_hdr hdr; + struct wmi_set_link_monitor_event evt; + } __packed reply = { + .evt = {.status = WMI_FW_STATUS_FAILURE}, + }; + + if (rssi_thold > S8_MAX || rssi_thold < S8_MIN || rssi_hyst > U8_MAX) + return -EINVAL; + + rc = wmi_call(wil, WMI_SET_LINK_MONITOR_CMDID, vif->mid, &cmd, + sizeof(cmd), WMI_SET_LINK_MONITOR_EVENTID, + &reply, sizeof(reply), WIL_WMI_CALL_GENERAL_TO_MS); + if (rc) { + wil_err(wil, "WMI_SET_LINK_MONITOR_CMDID failed, rc %d\n", rc); + return rc; + } + + if (reply.evt.status != WMI_FW_STATUS_SUCCESS) { + wil_err(wil, "WMI_SET_LINK_MONITOR_CMDID failed, status %d\n", + reply.evt.status); + return -EINVAL; + } + + return 0; +} diff --git a/drivers/net/wireless/ath/wil6210/wmi.h b/drivers/net/wireless/ath/wil6210/wmi.h index 6bd4ccee28ab9..e3558136e0c4f 100644 --- a/drivers/net/wireless/ath/wil6210/wmi.h +++ b/drivers/net/wireless/ath/wil6210/wmi.h @@ -192,6 +192,7 @@ enum wmi_command_id { WMI_RCP_ADDBA_RESP_EDMA_CMDID = 0x83B, WMI_LINK_MAINTAIN_CFG_WRITE_CMDID = 0x842, WMI_LINK_MAINTAIN_CFG_READ_CMDID = 0x843, + WMI_SET_LINK_MONITOR_CMDID = 0x845, WMI_SET_SECTORS_CMDID = 0x849, WMI_MAINTAIN_PAUSE_CMDID = 0x850, WMI_MAINTAIN_RESUME_CMDID = 0x851, @@ -1973,6 +1974,7 @@ enum wmi_event_id { WMI_REPORT_STATISTICS_EVENTID = 0x100B, WMI_FT_AUTH_STATUS_EVENTID = 0x100C, WMI_FT_REASSOC_STATUS_EVENTID = 0x100D, + WMI_LINK_MONITOR_EVENTID = 0x100E, WMI_RADAR_GENERAL_CONFIG_EVENTID = 0x1100, WMI_RADAR_CONFIG_SELECT_EVENTID = 0x1101, WMI_RADAR_PARAMS_CONFIG_EVENTID = 0x1102, @@ -2024,6 +2026,7 @@ enum wmi_event_id { WMI_TX_MGMT_PACKET_EVENTID = 0x1841, WMI_LINK_MAINTAIN_CFG_WRITE_DONE_EVENTID = 0x1842, WMI_LINK_MAINTAIN_CFG_READ_DONE_EVENTID = 0x1843, + WMI_SET_LINK_MONITOR_EVENTID = 0x1845, WMI_RF_XPM_READ_RESULT_EVENTID = 0x1856, WMI_RF_XPM_WRITE_RESULT_EVENTID = 0x1857, WMI_LED_CFG_DONE_EVENTID = 0x1858, @@ -3312,6 +3315,36 @@ struct wmi_link_maintain_cfg_read_cmd { __le32 cid; } __packed; +/* WMI_SET_LINK_MONITOR_CMDID */ +struct wmi_set_link_monitor_cmd { + u8 rssi_hyst; + u8 reserved[12]; + u8 rssi_thresholds_list_size; + s8 rssi_thresholds_list[0]; +} __packed; + +/* wmi_link_monitor_event_type */ +enum wmi_link_monitor_event_type { + WMI_LINK_MONITOR_NOTIF_RSSI_THRESHOLD_EVT = 0x00, + WMI_LINK_MONITOR_NOTIF_TX_ERR_EVT = 0x01, + WMI_LINK_MONITOR_NOTIF_THERMAL_EVT = 0x02, +}; + +/* WMI_SET_LINK_MONITOR_EVENTID */ +struct wmi_set_link_monitor_event { + /* wmi_fw_status */ + u8 status; + u8 reserved[3]; +} __packed; + +/* WMI_LINK_MONITOR_EVENTID */ +struct wmi_link_monitor_event { + /* link_monitor_event_type */ + u8 type; + s8 rssi_level; + u8 reserved[2]; +} __packed; + /* WMI_LINK_MAINTAIN_CFG_WRITE_DONE_EVENTID */ struct wmi_link_maintain_cfg_write_done_event { /* requested connection ID */ -- GitLab From 77ea8b455ca1d93221bad979a59271233ecb757e Mon Sep 17 00:00:00 2001 From: John Crispin Date: Thu, 5 Dec 2019 07:26:45 +0100 Subject: [PATCH 1077/1437] ath11k: explicitly cast wmi commands to their correct struct type Three of the WMI command handlers were not casting to the right data type. Lets make the code consistent with the other handlers. Signed-off-by: John Crispin Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath11k/wmi.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/ath/ath11k/wmi.c b/drivers/net/wireless/ath/ath11k/wmi.c index 0cc58d55b3085..a9b301ceb24b0 100644 --- a/drivers/net/wireless/ath/ath11k/wmi.c +++ b/drivers/net/wireless/ath/ath11k/wmi.c @@ -2515,7 +2515,7 @@ ath11k_wmi_send_twt_enable_cmd(struct ath11k *ar, u32 pdev_id) if (!skb) return -ENOMEM; - cmd = (void *)skb->data; + cmd = (struct wmi_twt_enable_params_cmd *)skb->data; cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_TWT_ENABLE_CMD) | FIELD_PREP(WMI_TLV_LEN, len - TLV_HDR_SIZE); cmd->pdev_id = pdev_id; @@ -2566,7 +2566,7 @@ ath11k_wmi_send_twt_disable_cmd(struct ath11k *ar, u32 pdev_id) if (!skb) return -ENOMEM; - cmd = (void *)skb->data; + cmd = (struct wmi_twt_disable_params_cmd *)skb->data; cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_TWT_DISABLE_CMD) | FIELD_PREP(WMI_TLV_LEN, len - TLV_HDR_SIZE); cmd->pdev_id = pdev_id; @@ -2596,7 +2596,7 @@ ath11k_wmi_send_obss_spr_cmd(struct ath11k *ar, u32 vdev_id, if (!skb) return -ENOMEM; - cmd = (void *)skb->data; + cmd = (struct wmi_obss_spatial_reuse_params_cmd *)skb->data; cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_OBSS_SPATIAL_REUSE_SET_CMD) | FIELD_PREP(WMI_TLV_LEN, len - TLV_HDR_SIZE); -- GitLab From 1148f9adbe71415836a18a36c1b4ece999ab0973 Mon Sep 17 00:00:00 2001 From: Alexander Lobakin Date: Wed, 18 Dec 2019 12:18:21 +0300 Subject: [PATCH 1078/1437] net, sysctl: Fix compiler warning when only cBPF is present MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit proc_dointvec_minmax_bpf_restricted() has been firstly introduced in commit 2e4a30983b0f ("bpf: restrict access to core bpf sysctls") under CONFIG_HAVE_EBPF_JIT. Then, this ifdef has been removed in ede95a63b5e8 ("bpf: add bpf_jit_limit knob to restrict unpriv allocations"), because a new sysctl, bpf_jit_limit, made use of it. Finally, this parameter has become long instead of integer with fdadd04931c2 ("bpf: fix bpf_jit_limit knob for PAGE_SIZE >= 64K") and thus, a new proc_dolongvec_minmax_bpf_restricted() has been added. With this last change, we got back to that proc_dointvec_minmax_bpf_restricted() is used only under CONFIG_HAVE_EBPF_JIT, but the corresponding ifdef has not been brought back. So, in configurations like CONFIG_BPF_JIT=y && CONFIG_HAVE_EBPF_JIT=n since v4.20 we have: CC net/core/sysctl_net_core.o net/core/sysctl_net_core.c:292:1: warning: ‘proc_dointvec_minmax_bpf_restricted’ defined but not used [-Wunused-function] 292 | proc_dointvec_minmax_bpf_restricted(struct ctl_table *table, int write, | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Suppress this by guarding it with CONFIG_HAVE_EBPF_JIT again. Fixes: fdadd04931c2 ("bpf: fix bpf_jit_limit knob for PAGE_SIZE >= 64K") Signed-off-by: Alexander Lobakin Signed-off-by: Daniel Borkmann Link: https://lore.kernel.org/bpf/20191218091821.7080-1-alobakin@dlink.ru --- net/core/sysctl_net_core.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/net/core/sysctl_net_core.c b/net/core/sysctl_net_core.c index eb29e5adc84da..9f9e00ba3ad7c 100644 --- a/net/core/sysctl_net_core.c +++ b/net/core/sysctl_net_core.c @@ -288,6 +288,7 @@ static int proc_dointvec_minmax_bpf_enable(struct ctl_table *table, int write, return ret; } +# ifdef CONFIG_HAVE_EBPF_JIT static int proc_dointvec_minmax_bpf_restricted(struct ctl_table *table, int write, void __user *buffer, size_t *lenp, @@ -298,6 +299,7 @@ proc_dointvec_minmax_bpf_restricted(struct ctl_table *table, int write, return proc_dointvec_minmax(table, write, buffer, lenp, ppos); } +# endif /* CONFIG_HAVE_EBPF_JIT */ static int proc_dolongvec_minmax_bpf_restricted(struct ctl_table *table, int write, -- GitLab From ca0e477931c5a725bb887c764a49eb5d69ab7a67 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Wed, 18 Dec 2019 16:58:02 -0800 Subject: [PATCH 1079/1437] ath11k: Use sizeof_field() instead of FIELD_SIZEOF() The FIELD_SIZEOF() macro was redundant, and is being removed from the kernel. Since commit c593642c8be0 ("treewide: Use sizeof_field() macro") this is one of the last users of the old macro, so replace it. Signed-off-by: Kees Cook Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath11k/wmi.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath11k/wmi.h b/drivers/net/wireless/ath/ath11k/wmi.h index 780e72c2844a1..1fde15c762ad6 100644 --- a/drivers/net/wireless/ath/ath11k/wmi.h +++ b/drivers/net/wireless/ath/ath11k/wmi.h @@ -44,7 +44,7 @@ struct wmi_tlv { #define WMI_TLV_LEN GENMASK(15, 0) #define WMI_TLV_TAG GENMASK(31, 16) -#define TLV_HDR_SIZE FIELD_SIZEOF(struct wmi_tlv, header) +#define TLV_HDR_SIZE sizeof_field(struct wmi_tlv, header) #define WMI_CMD_HDR_CMD_ID GENMASK(23, 0) #define WMI_MAX_MEM_REQS 32 -- GitLab From c48f16b42a3bf71eeb9c6141369b001097f961e6 Mon Sep 17 00:00:00 2001 From: Russell King Date: Tue, 17 Dec 2019 13:39:00 +0000 Subject: [PATCH 1080/1437] net: phy: remove redundant .aneg_done initialisers Remove initialisers that set .aneg_done to genphy_aneg_done - this is the default for clause 22 PHYs, so the initialiser is redundant. Signed-off-by: Russell King Reviewed-by: Andrew Lunn Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/phy/mscc.c | 6 ------ drivers/net/phy/phy_device.c | 1 - 2 files changed, 7 deletions(-) diff --git a/drivers/net/phy/mscc.c b/drivers/net/phy/mscc.c index d5f8f351d9eff..50214c081164f 100644 --- a/drivers/net/phy/mscc.c +++ b/drivers/net/phy/mscc.c @@ -2404,7 +2404,6 @@ static struct phy_driver vsc85xx_driver[] = { .soft_reset = &genphy_soft_reset, .config_init = &vsc85xx_config_init, .config_aneg = &vsc85xx_config_aneg, - .aneg_done = &genphy_aneg_done, .read_status = &vsc85xx_read_status, .ack_interrupt = &vsc85xx_ack_interrupt, .config_intr = &vsc85xx_config_intr, @@ -2429,7 +2428,6 @@ static struct phy_driver vsc85xx_driver[] = { .soft_reset = &genphy_soft_reset, .config_init = &vsc85xx_config_init, .config_aneg = &vsc85xx_config_aneg, - .aneg_done = &genphy_aneg_done, .read_status = &vsc85xx_read_status, .ack_interrupt = &vsc85xx_ack_interrupt, .config_intr = &vsc85xx_config_intr, @@ -2454,7 +2452,6 @@ static struct phy_driver vsc85xx_driver[] = { .soft_reset = &genphy_soft_reset, .config_init = &vsc85xx_config_init, .config_aneg = &vsc85xx_config_aneg, - .aneg_done = &genphy_aneg_done, .read_status = &vsc85xx_read_status, .ack_interrupt = &vsc85xx_ack_interrupt, .config_intr = &vsc85xx_config_intr, @@ -2479,7 +2476,6 @@ static struct phy_driver vsc85xx_driver[] = { .soft_reset = &genphy_soft_reset, .config_init = &vsc85xx_config_init, .config_aneg = &vsc85xx_config_aneg, - .aneg_done = &genphy_aneg_done, .read_status = &vsc85xx_read_status, .ack_interrupt = &vsc85xx_ack_interrupt, .config_intr = &vsc85xx_config_intr, @@ -2504,7 +2500,6 @@ static struct phy_driver vsc85xx_driver[] = { .soft_reset = &genphy_soft_reset, .config_init = &vsc8584_config_init, .config_aneg = &vsc85xx_config_aneg, - .aneg_done = &genphy_aneg_done, .read_status = &vsc85xx_read_status, .ack_interrupt = &vsc85xx_ack_interrupt, .config_intr = &vsc85xx_config_intr, @@ -2530,7 +2525,6 @@ static struct phy_driver vsc85xx_driver[] = { .soft_reset = &genphy_soft_reset, .config_init = &vsc8584_config_init, .config_aneg = &vsc85xx_config_aneg, - .aneg_done = &genphy_aneg_done, .read_status = &vsc85xx_read_status, .ack_interrupt = &vsc85xx_ack_interrupt, .config_intr = &vsc85xx_config_intr, diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c index 0887ed2bb0500..acbdced20c11c 100644 --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c @@ -2575,7 +2575,6 @@ static struct phy_driver genphy_driver = { .name = "Generic PHY", .soft_reset = genphy_no_soft_reset, .get_features = genphy_read_abilities, - .aneg_done = genphy_aneg_done, .suspend = genphy_suspend, .resume = genphy_resume, .set_loopback = genphy_loopback, -- GitLab From af006240c6cfea9f52fc197ac26e6cade4a8623d Mon Sep 17 00:00:00 2001 From: Russell King Date: Tue, 17 Dec 2019 13:39:06 +0000 Subject: [PATCH 1081/1437] net: phy: use phy_resolve_aneg_pause() Several drivers code their own version of this, working from the LPA register, after setting the ethtool link partner advertisement bitmask. Use the generic function instead. Signed-off-by: Russell King Reviewed-by: Andrew Lunn Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/phy/lxt.c | 5 +---- drivers/net/phy/marvell.c | 5 +---- drivers/net/phy/uPD60620.c | 7 +------ 3 files changed, 3 insertions(+), 14 deletions(-) diff --git a/drivers/net/phy/lxt.c b/drivers/net/phy/lxt.c index 356bd6472f494..30172852e36e6 100644 --- a/drivers/net/phy/lxt.c +++ b/drivers/net/phy/lxt.c @@ -190,10 +190,7 @@ static int lxt973a2_read_status(struct phy_device *phydev) phydev->duplex = DUPLEX_FULL; } - if (phydev->duplex == DUPLEX_FULL) { - phydev->pause = lpa & LPA_PAUSE_CAP ? 1 : 0; - phydev->asym_pause = lpa & LPA_PAUSE_ASYM ? 1 : 0; - } + phy_resolve_aneg_pause(phydev); } else { int bmcr = phy_read(phydev, MII_BMCR); diff --git a/drivers/net/phy/marvell.c b/drivers/net/phy/marvell.c index b1fbd19373289..d807c3e5cc566 100644 --- a/drivers/net/phy/marvell.c +++ b/drivers/net/phy/marvell.c @@ -1384,10 +1384,7 @@ static int marvell_read_status_page_an(struct phy_device *phydev, mii_lpa_to_linkmode_lpa_t(phydev->lp_advertising, lpa); mii_stat1000_mod_linkmode_lpa_t(phydev->lp_advertising, lpagb); - if (phydev->duplex == DUPLEX_FULL) { - phydev->pause = lpa & LPA_PAUSE_CAP ? 1 : 0; - phydev->asym_pause = lpa & LPA_PAUSE_ASYM ? 1 : 0; - } + phy_resolve_aneg_pause(phydev); } else { /* The fiber link is only 1000M capable */ fiber_lpa_mod_linkmode_lpa_t(phydev->lp_advertising, lpa); diff --git a/drivers/net/phy/uPD60620.c b/drivers/net/phy/uPD60620.c index a32b3fd8a3705..38834347a427e 100644 --- a/drivers/net/phy/uPD60620.c +++ b/drivers/net/phy/uPD60620.c @@ -68,12 +68,7 @@ static int upd60620_read_status(struct phy_device *phydev) mii_lpa_to_linkmode_lpa_t(phydev->lp_advertising, phy_state); - if (phydev->duplex == DUPLEX_FULL) { - if (phy_state & LPA_PAUSE_CAP) - phydev->pause = 1; - if (phy_state & LPA_PAUSE_ASYM) - phydev->asym_pause = 1; - } + phy_resolve_aneg_pause(phydev); } } return 0; -- GitLab From 2a10ab043ac5a658225ee77852db7942de9ac4c5 Mon Sep 17 00:00:00 2001 From: Russell King Date: Tue, 17 Dec 2019 13:39:11 +0000 Subject: [PATCH 1082/1437] net: phy: add genphy_check_and_restart_aneg() Add a helper for restarting autonegotiation(), similar to the clause 45 variant. Use it in __genphy_config_aneg() Signed-off-by: Russell King Reviewed-by: Andrew Lunn Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/phy/phy_device.c | 48 +++++++++++++++++++++++------------- include/linux/phy.h | 1 + 2 files changed, 32 insertions(+), 17 deletions(-) diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c index acbdced20c11c..c61f2295e21e5 100644 --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c @@ -1770,6 +1770,36 @@ int genphy_restart_aneg(struct phy_device *phydev) } EXPORT_SYMBOL(genphy_restart_aneg); +/** + * genphy_check_and_restart_aneg - Enable and restart auto-negotiation + * @phydev: target phy_device struct + * @restart: whether aneg restart is requested + * + * Check, and restart auto-negotiation if needed. + */ +int genphy_check_and_restart_aneg(struct phy_device *phydev, bool restart) +{ + int ret = 0; + + if (!restart) { + /* Advertisement hasn't changed, but maybe aneg was never on to + * begin with? Or maybe phy was isolated? + */ + ret = phy_read(phydev, MII_BMCR); + if (ret < 0) + return ret; + + if (!(ret & BMCR_ANENABLE) || (ret & BMCR_ISOLATE)) + restart = true; + } + + if (restart) + ret = genphy_restart_aneg(phydev); + + return ret; +} +EXPORT_SYMBOL(genphy_check_and_restart_aneg); + /** * __genphy_config_aneg - restart auto-negotiation or write BMCR * @phydev: target phy_device struct @@ -1795,23 +1825,7 @@ int __genphy_config_aneg(struct phy_device *phydev, bool changed) else if (err) changed = true; - if (!changed) { - /* Advertisement hasn't changed, but maybe aneg was never on to - * begin with? Or maybe phy was isolated? - */ - int ctl = phy_read(phydev, MII_BMCR); - - if (ctl < 0) - return ctl; - - if (!(ctl & BMCR_ANENABLE) || (ctl & BMCR_ISOLATE)) - changed = true; /* do restart aneg */ - } - - /* Only restart aneg if we are advertising something different - * than we were before. - */ - return changed ? genphy_restart_aneg(phydev) : 0; + return genphy_check_and_restart_aneg(phydev, changed); } EXPORT_SYMBOL(__genphy_config_aneg); diff --git a/include/linux/phy.h b/include/linux/phy.h index 5032d453ac66a..1c4f97d2631d3 100644 --- a/include/linux/phy.h +++ b/include/linux/phy.h @@ -1094,6 +1094,7 @@ void phy_attached_info(struct phy_device *phydev); int genphy_read_abilities(struct phy_device *phydev); int genphy_setup_forced(struct phy_device *phydev); int genphy_restart_aneg(struct phy_device *phydev); +int genphy_check_and_restart_aneg(struct phy_device *phydev, bool restart); int genphy_config_eee_advert(struct phy_device *phydev); int __genphy_config_aneg(struct phy_device *phydev, bool changed); int genphy_aneg_done(struct phy_device *phydev); -- GitLab From 0efc286a923874f0c243e5766cce54e9429ed949 Mon Sep 17 00:00:00 2001 From: Russell King Date: Tue, 17 Dec 2019 13:39:16 +0000 Subject: [PATCH 1083/1437] net: phy: provide and use genphy_read_status_fixed() There are two drivers and generic code which contain exactly the same code to read the status of a PHY operating without autonegotiation enabled. Rather than duplicate this code, provide a helper to read this information. Signed-off-by: Russell King Reviewed-by: Andrew Lunn Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/phy/lxt.c | 19 +++----------- drivers/net/phy/marvell.c | 19 +++----------- drivers/net/phy/phy_device.c | 49 ++++++++++++++++++++++++------------ include/linux/phy.h | 1 + 4 files changed, 41 insertions(+), 47 deletions(-) diff --git a/drivers/net/phy/lxt.c b/drivers/net/phy/lxt.c index 30172852e36e6..fec58ad69e02c 100644 --- a/drivers/net/phy/lxt.c +++ b/drivers/net/phy/lxt.c @@ -192,22 +192,9 @@ static int lxt973a2_read_status(struct phy_device *phydev) phy_resolve_aneg_pause(phydev); } else { - int bmcr = phy_read(phydev, MII_BMCR); - - if (bmcr < 0) - return bmcr; - - if (bmcr & BMCR_FULLDPLX) - phydev->duplex = DUPLEX_FULL; - else - phydev->duplex = DUPLEX_HALF; - - if (bmcr & BMCR_SPEED1000) - phydev->speed = SPEED_1000; - else if (bmcr & BMCR_SPEED100) - phydev->speed = SPEED_100; - else - phydev->speed = SPEED_10; + err = genphy_read_status_fixed(phydev); + if (err < 0) + return err; phydev->pause = phydev->asym_pause = 0; linkmode_zero(phydev->lp_advertising); diff --git a/drivers/net/phy/marvell.c b/drivers/net/phy/marvell.c index d807c3e5cc566..5e49639dc8dd1 100644 --- a/drivers/net/phy/marvell.c +++ b/drivers/net/phy/marvell.c @@ -1407,22 +1407,11 @@ static int marvell_read_status_page_an(struct phy_device *phydev, static int marvell_read_status_page_fixed(struct phy_device *phydev) { - int bmcr = phy_read(phydev, MII_BMCR); - - if (bmcr < 0) - return bmcr; - - if (bmcr & BMCR_FULLDPLX) - phydev->duplex = DUPLEX_FULL; - else - phydev->duplex = DUPLEX_HALF; + int err; - if (bmcr & BMCR_SPEED1000) - phydev->speed = SPEED_1000; - else if (bmcr & BMCR_SPEED100) - phydev->speed = SPEED_100; - else - phydev->speed = SPEED_10; + err = genphy_read_status_fixed(phydev); + if (err < 0) + return err; phydev->pause = 0; phydev->asym_pause = 0; diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c index c61f2295e21e5..ec19efe7ea993 100644 --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c @@ -1992,6 +1992,36 @@ int genphy_read_lpa(struct phy_device *phydev) } EXPORT_SYMBOL(genphy_read_lpa); +/** + * genphy_read_status_fixed - read the link parameters for !aneg mode + * @phydev: target phy_device struct + * + * Read the current duplex and speed state for a PHY operating with + * autonegotiation disabled. + */ +int genphy_read_status_fixed(struct phy_device *phydev) +{ + int bmcr = phy_read(phydev, MII_BMCR); + + if (bmcr < 0) + return bmcr; + + if (bmcr & BMCR_FULLDPLX) + phydev->duplex = DUPLEX_FULL; + else + phydev->duplex = DUPLEX_HALF; + + if (bmcr & BMCR_SPEED1000) + phydev->speed = SPEED_1000; + else if (bmcr & BMCR_SPEED100) + phydev->speed = SPEED_100; + else + phydev->speed = SPEED_10; + + return 0; +} +EXPORT_SYMBOL(genphy_read_status_fixed); + /** * genphy_read_status - check the link status and update current link state * @phydev: target phy_device struct @@ -2026,22 +2056,9 @@ int genphy_read_status(struct phy_device *phydev) if (phydev->autoneg == AUTONEG_ENABLE && phydev->autoneg_complete) { phy_resolve_aneg_linkmode(phydev); } else if (phydev->autoneg == AUTONEG_DISABLE) { - int bmcr = phy_read(phydev, MII_BMCR); - - if (bmcr < 0) - return bmcr; - - if (bmcr & BMCR_FULLDPLX) - phydev->duplex = DUPLEX_FULL; - else - phydev->duplex = DUPLEX_HALF; - - if (bmcr & BMCR_SPEED1000) - phydev->speed = SPEED_1000; - else if (bmcr & BMCR_SPEED100) - phydev->speed = SPEED_100; - else - phydev->speed = SPEED_10; + err = genphy_read_status_fixed(phydev); + if (err < 0) + return err; } return 0; diff --git a/include/linux/phy.h b/include/linux/phy.h index 1c4f97d2631d3..b2105e0d72d3a 100644 --- a/include/linux/phy.h +++ b/include/linux/phy.h @@ -1100,6 +1100,7 @@ int __genphy_config_aneg(struct phy_device *phydev, bool changed); int genphy_aneg_done(struct phy_device *phydev); int genphy_update_link(struct phy_device *phydev); int genphy_read_lpa(struct phy_device *phydev); +int genphy_read_status_fixed(struct phy_device *phydev); int genphy_read_status(struct phy_device *phydev); int genphy_suspend(struct phy_device *phydev); int genphy_resume(struct phy_device *phydev); -- GitLab From fcf1f59afc67dc8c214a1b085527ed1fc513c3a2 Mon Sep 17 00:00:00 2001 From: Russell King Date: Tue, 17 Dec 2019 13:39:21 +0000 Subject: [PATCH 1084/1437] net: phy: marvell: rearrange to use genphy_read_lpa() Rearrange the Marvell PHY driver to use genphy_read_lpa() rather than open-coding this functionality. Signed-off-by: Russell King Reviewed-by: Andrew Lunn Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/phy/marvell.c | 66 +++++++++++++++++++-------------------- 1 file changed, 32 insertions(+), 34 deletions(-) diff --git a/drivers/net/phy/marvell.c b/drivers/net/phy/marvell.c index 5e49639dc8dd1..ac9c21b4e6c9f 100644 --- a/drivers/net/phy/marvell.c +++ b/drivers/net/phy/marvell.c @@ -1343,52 +1343,30 @@ static int marvell_read_status_page_an(struct phy_device *phydev, { int status; int lpa; - int lpagb; + int err; status = phy_read(phydev, MII_M1011_PHY_STATUS); if (status < 0) return status; - lpa = phy_read(phydev, MII_LPA); - if (lpa < 0) - return lpa; - - lpagb = phy_read(phydev, MII_STAT1000); - if (lpagb < 0) - return lpagb; - - if (status & MII_M1011_PHY_STATUS_FULLDUPLEX) - phydev->duplex = DUPLEX_FULL; - else - phydev->duplex = DUPLEX_HALF; - - status = status & MII_M1011_PHY_STATUS_SPD_MASK; - phydev->pause = 0; - phydev->asym_pause = 0; - - switch (status) { - case MII_M1011_PHY_STATUS_1000: - phydev->speed = SPEED_1000; - break; - - case MII_M1011_PHY_STATUS_100: - phydev->speed = SPEED_100; - break; - - default: - phydev->speed = SPEED_10; - break; - } - if (!fiber) { - mii_lpa_to_linkmode_lpa_t(phydev->lp_advertising, lpa); - mii_stat1000_mod_linkmode_lpa_t(phydev->lp_advertising, lpagb); + err = genphy_read_lpa(phydev); + if (err < 0) + return err; + phydev->pause = 0; + phydev->asym_pause = 0; phy_resolve_aneg_pause(phydev); } else { + lpa = phy_read(phydev, MII_LPA); + if (lpa < 0) + return lpa; + /* The fiber link is only 1000M capable */ fiber_lpa_mod_linkmode_lpa_t(phydev->lp_advertising, lpa); + phydev->pause = 0; + phydev->asym_pause = 0; if (phydev->duplex == DUPLEX_FULL) { if (!(lpa & LPA_PAUSE_FIBER)) { phydev->pause = 0; @@ -1402,6 +1380,26 @@ static int marvell_read_status_page_an(struct phy_device *phydev, } } } + + if (status & MII_M1011_PHY_STATUS_FULLDUPLEX) + phydev->duplex = DUPLEX_FULL; + else + phydev->duplex = DUPLEX_HALF; + + switch (status & MII_M1011_PHY_STATUS_SPD_MASK) { + case MII_M1011_PHY_STATUS_1000: + phydev->speed = SPEED_1000; + break; + + case MII_M1011_PHY_STATUS_100: + phydev->speed = SPEED_100; + break; + + default: + phydev->speed = SPEED_10; + break; + } + return 0; } -- GitLab From 98f92831c561e43e8d4dafa7165e996af4dc8ac3 Mon Sep 17 00:00:00 2001 From: Russell King Date: Tue, 17 Dec 2019 13:39:26 +0000 Subject: [PATCH 1085/1437] net: phy: marvell: initialise link partner state earlier Move the initialisation of the link partner state earlier, inside marvell_read_status_page(), so we don't have the same initialisation scattered amongst the other files. This is in a similar place to the genphy implementation, so would result in the same behaviour if a PHY read error occurs. This allows us to get rid of marvell_read_status_page_fixed(), which became a pointless wrapper around genphy_read_status_fixed(). Signed-off-by: Russell King Reviewed-by: Andrew Lunn Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/phy/marvell.c | 25 +++++-------------------- 1 file changed, 5 insertions(+), 20 deletions(-) diff --git a/drivers/net/phy/marvell.c b/drivers/net/phy/marvell.c index ac9c21b4e6c9f..b50c8a4710b4e 100644 --- a/drivers/net/phy/marvell.c +++ b/drivers/net/phy/marvell.c @@ -1354,8 +1354,6 @@ static int marvell_read_status_page_an(struct phy_device *phydev, if (err < 0) return err; - phydev->pause = 0; - phydev->asym_pause = 0; phy_resolve_aneg_pause(phydev); } else { lpa = phy_read(phydev, MII_LPA); @@ -1365,8 +1363,6 @@ static int marvell_read_status_page_an(struct phy_device *phydev, /* The fiber link is only 1000M capable */ fiber_lpa_mod_linkmode_lpa_t(phydev->lp_advertising, lpa); - phydev->pause = 0; - phydev->asym_pause = 0; if (phydev->duplex == DUPLEX_FULL) { if (!(lpa & LPA_PAUSE_FIBER)) { phydev->pause = 0; @@ -1403,21 +1399,6 @@ static int marvell_read_status_page_an(struct phy_device *phydev, return 0; } -static int marvell_read_status_page_fixed(struct phy_device *phydev) -{ - int err; - - err = genphy_read_status_fixed(phydev); - if (err < 0) - return err; - - phydev->pause = 0; - phydev->asym_pause = 0; - linkmode_zero(phydev->lp_advertising); - - return 0; -} - /* marvell_read_status_page * * Description: @@ -1443,10 +1424,14 @@ static int marvell_read_status_page(struct phy_device *phydev, int page) if (err) return err; + linkmode_zero(phydev->lp_advertising); + phydev->pause = 0; + phydev->asym_pause = 0; + if (phydev->autoneg == AUTONEG_ENABLE) err = marvell_read_status_page_an(phydev, fiber); else - err = marvell_read_status_page_fixed(phydev); + err = genphy_read_status_fixed(phydev); return err; } -- GitLab From 760fa78f3509d3d414804c4017de3d0dba995081 Mon Sep 17 00:00:00 2001 From: Russell King Date: Tue, 17 Dec 2019 13:39:31 +0000 Subject: [PATCH 1086/1437] net: phy: marvell: use positive logic for link state Rather than using negative logic: if (there is no link) set link = 0 else set link = 1 use the more natural positive logic: if (there is link) set link = 1 else set link = 0 Signed-off-by: Russell King Reviewed-by: Andrew Lunn Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/phy/marvell.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/net/phy/marvell.c b/drivers/net/phy/marvell.c index b50c8a4710b4e..5900281f4c2c1 100644 --- a/drivers/net/phy/marvell.c +++ b/drivers/net/phy/marvell.c @@ -174,7 +174,6 @@ #define ADVERTISE_PAUSE_FIBER 0x180 #define ADVERTISE_PAUSE_ASYM_FIBER 0x100 -#define REGISTER_LINK_STATUS 0x400 #define NB_FIBER_STATS 1 MODULE_DESCRIPTION("Marvell PHY driver"); @@ -1327,10 +1326,10 @@ static int marvell_update_link(struct phy_device *phydev, int fiber) if (status < 0) return status; - if ((status & REGISTER_LINK_STATUS) == 0) - phydev->link = 0; - else + if (status & MII_M1011_PHY_STATUS_LINK) phydev->link = 1; + else + phydev->link = 0; } else { return genphy_update_link(phydev); } -- GitLab From d2004e27eb352a5961cae7cc50563dbdc5386ba7 Mon Sep 17 00:00:00 2001 From: Russell King Date: Tue, 17 Dec 2019 13:39:36 +0000 Subject: [PATCH 1087/1437] net: phy: marvell: consolidate phy status reading marvell_read_status_page_an() always reads the PHY status register, but marvell_update_link() has already done this. Rather than wastefully reading the register twice in quick succession, read it once in marvell_read_status_page() and use the result for both. This makes marvell_update_link() rather pointless, so move it into marvell_read_status_page(). Signed-off-by: Russell King Signed-off-by: David S. Miller --- drivers/net/phy/marvell.c | 60 +++++++++++---------------------------- 1 file changed, 17 insertions(+), 43 deletions(-) diff --git a/drivers/net/phy/marvell.c b/drivers/net/phy/marvell.c index 5900281f4c2c1..9c26e8ebd1435 100644 --- a/drivers/net/phy/marvell.c +++ b/drivers/net/phy/marvell.c @@ -1307,47 +1307,12 @@ static void fiber_lpa_mod_linkmode_lpa_t(unsigned long *advertising, u32 lpa) advertising, lpa & LPA_FIBER_1000FULL); } -/** - * marvell_update_link - update link status in real time in @phydev - * @phydev: target phy_device struct - * - * Description: Update the value in phydev->link to reflect the - * current link value. - */ -static int marvell_update_link(struct phy_device *phydev, int fiber) -{ - int status; - - /* Use the generic register for copper link, or specific - * register for fiber case - */ - if (fiber) { - status = phy_read(phydev, MII_M1011_PHY_STATUS); - if (status < 0) - return status; - - if (status & MII_M1011_PHY_STATUS_LINK) - phydev->link = 1; - else - phydev->link = 0; - } else { - return genphy_update_link(phydev); - } - - return 0; -} - static int marvell_read_status_page_an(struct phy_device *phydev, - int fiber) + int fiber, int status) { - int status; int lpa; int err; - status = phy_read(phydev, MII_M1011_PHY_STATUS); - if (status < 0) - return status; - if (!fiber) { err = genphy_read_lpa(phydev); if (err < 0) @@ -1408,27 +1373,36 @@ static int marvell_read_status_page_an(struct phy_device *phydev, */ static int marvell_read_status_page(struct phy_device *phydev, int page) { + int status; int fiber; int err; - /* Detect and update the link, but return if there - * was an error + status = phy_read(phydev, MII_M1011_PHY_STATUS); + if (status < 0) + return status; + + /* Use the generic register for copper link status, + * and the PHY status register for fiber link status. */ + if (page == MII_MARVELL_FIBER_PAGE) { + phydev->link = !!(status & MII_M1011_PHY_STATUS_LINK); + } else { + err = genphy_update_link(phydev); + if (err) + return err; + } + if (page == MII_MARVELL_FIBER_PAGE) fiber = 1; else fiber = 0; - err = marvell_update_link(phydev, fiber); - if (err) - return err; - linkmode_zero(phydev->lp_advertising); phydev->pause = 0; phydev->asym_pause = 0; if (phydev->autoneg == AUTONEG_ENABLE) - err = marvell_read_status_page_an(phydev, fiber); + err = marvell_read_status_page_an(phydev, fiber, status); else err = genphy_read_status_fixed(phydev); -- GitLab From 20ecf424d0dbb820a337e994c8feec18c1fa77c9 Mon Sep 17 00:00:00 2001 From: Russell King Date: Tue, 17 Dec 2019 13:39:42 +0000 Subject: [PATCH 1088/1437] net: phy: marvell: use existing clause 37 definitions Use existing clause 37 advertising/link partner definitions rather than private ones for the advertisement registers. Signed-off-by: Russell King Reviewed-by: Andrew Lunn Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/phy/marvell.c | 26 ++++++++------------------ 1 file changed, 8 insertions(+), 18 deletions(-) diff --git a/drivers/net/phy/marvell.c b/drivers/net/phy/marvell.c index 9c26e8ebd1435..c6c7cde84c1c2 100644 --- a/drivers/net/phy/marvell.c +++ b/drivers/net/phy/marvell.c @@ -162,18 +162,9 @@ #define MII_88E1510_GEN_CTRL_REG_1_MODE_SGMII 0x1 /* SGMII to copper */ #define MII_88E1510_GEN_CTRL_REG_1_RESET 0x8000 /* Soft reset */ -#define LPA_FIBER_1000HALF 0x40 -#define LPA_FIBER_1000FULL 0x20 - #define LPA_PAUSE_FIBER 0x180 #define LPA_PAUSE_ASYM_FIBER 0x100 -#define ADVERTISE_FIBER_1000HALF 0x40 -#define ADVERTISE_FIBER_1000FULL 0x20 - -#define ADVERTISE_PAUSE_FIBER 0x180 -#define ADVERTISE_PAUSE_ASYM_FIBER 0x100 - #define NB_FIBER_STATS 1 MODULE_DESCRIPTION("Marvell PHY driver"); @@ -496,16 +487,15 @@ static inline u32 linkmode_adv_to_fiber_adv_t(unsigned long *advertise) u32 result = 0; if (linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseT_Half_BIT, advertise)) - result |= ADVERTISE_FIBER_1000HALF; + result |= ADVERTISE_1000XHALF; if (linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT, advertise)) - result |= ADVERTISE_FIBER_1000FULL; + result |= ADVERTISE_1000XFULL; if (linkmode_test_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, advertise) && linkmode_test_bit(ETHTOOL_LINK_MODE_Pause_BIT, advertise)) - result |= LPA_PAUSE_ASYM_FIBER; + result |= ADVERTISE_1000XPSE_ASYM; else if (linkmode_test_bit(ETHTOOL_LINK_MODE_Pause_BIT, advertise)) - result |= (ADVERTISE_PAUSE_FIBER - & (~ADVERTISE_PAUSE_ASYM_FIBER)); + result |= ADVERTISE_1000XPAUSE; return result; } @@ -538,8 +528,8 @@ static int marvell_config_aneg_fiber(struct phy_device *phydev) return adv; oldadv = adv; - adv &= ~(ADVERTISE_FIBER_1000HALF | ADVERTISE_FIBER_1000FULL - | LPA_PAUSE_FIBER); + adv &= ~(ADVERTISE_1000XHALF | ADVERTISE_1000XFULL | + ADVERTISE_1000XPAUSE | ADVERTISE_1000XPSE_ASYM); adv |= linkmode_adv_to_fiber_adv_t(phydev->advertising); if (adv != oldadv) { @@ -1301,10 +1291,10 @@ static int m88e6390_config_aneg(struct phy_device *phydev) static void fiber_lpa_mod_linkmode_lpa_t(unsigned long *advertising, u32 lpa) { linkmode_mod_bit(ETHTOOL_LINK_MODE_1000baseT_Half_BIT, - advertising, lpa & LPA_FIBER_1000HALF); + advertising, lpa & LPA_1000XHALF); linkmode_mod_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT, - advertising, lpa & LPA_FIBER_1000FULL); + advertising, lpa & LPA_1000XFULL); } static int marvell_read_status_page_an(struct phy_device *phydev, -- GitLab From 9f4bae704f93c9b83196781de3df8a4bf1855a4b Mon Sep 17 00:00:00 2001 From: Russell King Date: Tue, 17 Dec 2019 13:39:47 +0000 Subject: [PATCH 1089/1437] net: phy: marvell: use phy_modify_changed() Use phy_modify_changed() to change the fiber advertisement register rather than open coding this functionality. Signed-off-by: Russell King Reviewed-by: Andrew Lunn Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/phy/marvell.c | 26 ++++++++++---------------- 1 file changed, 10 insertions(+), 16 deletions(-) diff --git a/drivers/net/phy/marvell.c b/drivers/net/phy/marvell.c index c6c7cde84c1c2..7da64208365bd 100644 --- a/drivers/net/phy/marvell.c +++ b/drivers/net/phy/marvell.c @@ -513,7 +513,7 @@ static int marvell_config_aneg_fiber(struct phy_device *phydev) { int changed = 0; int err; - int adv, oldadv; + u16 adv; if (phydev->autoneg != AUTONEG_ENABLE) return genphy_setup_forced(phydev); @@ -522,23 +522,17 @@ static int marvell_config_aneg_fiber(struct phy_device *phydev) linkmode_and(phydev->advertising, phydev->advertising, phydev->supported); - /* Setup fiber advertisement */ - adv = phy_read(phydev, MII_ADVERTISE); - if (adv < 0) - return adv; - - oldadv = adv; - adv &= ~(ADVERTISE_1000XHALF | ADVERTISE_1000XFULL | - ADVERTISE_1000XPAUSE | ADVERTISE_1000XPSE_ASYM); - adv |= linkmode_adv_to_fiber_adv_t(phydev->advertising); - - if (adv != oldadv) { - err = phy_write(phydev, MII_ADVERTISE, adv); - if (err < 0) - return err; + adv = linkmode_adv_to_fiber_adv_t(phydev->advertising); + /* Setup fiber advertisement */ + err = phy_modify_changed(phydev, MII_ADVERTISE, + ADVERTISE_1000XHALF | ADVERTISE_1000XFULL | + ADVERTISE_1000XPAUSE | ADVERTISE_1000XPSE_ASYM, + adv); + if (err < 0) + return err; + if (err > 0) changed = 1; - } if (changed == 0) { /* Advertisement hasn't changed, but maybe aneg was never on to -- GitLab From b5abac2d2d17ce1b30c267dd72fb91c8bc21bee1 Mon Sep 17 00:00:00 2001 From: Russell King Date: Tue, 17 Dec 2019 13:39:52 +0000 Subject: [PATCH 1090/1437] net: phy: marvell: use genphy_check_and_restart_aneg() Use the helper to check and restart autonegotiation for the marvell fiber page negotiation setting. Signed-off-by: Russell King Reviewed-by: Andrew Lunn Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/phy/marvell.c | 21 +-------------------- 1 file changed, 1 insertion(+), 20 deletions(-) diff --git a/drivers/net/phy/marvell.c b/drivers/net/phy/marvell.c index 7da64208365bd..28e33ece4ce17 100644 --- a/drivers/net/phy/marvell.c +++ b/drivers/net/phy/marvell.c @@ -534,26 +534,7 @@ static int marvell_config_aneg_fiber(struct phy_device *phydev) if (err > 0) changed = 1; - if (changed == 0) { - /* Advertisement hasn't changed, but maybe aneg was never on to - * begin with? Or maybe phy was isolated? - */ - int ctl = phy_read(phydev, MII_BMCR); - - if (ctl < 0) - return ctl; - - if (!(ctl & BMCR_ANENABLE) || (ctl & BMCR_ISOLATE)) - changed = 1; /* do restart aneg */ - } - - /* Only restart aneg if we are advertising something different - * than we were before. - */ - if (changed > 0) - changed = genphy_restart_aneg(phydev); - - return changed; + return genphy_check_and_restart_aneg(phydev, changed); } static int m88e1510_config_aneg(struct phy_device *phydev) -- GitLab From 5bf2fc1f9c88397b125d5ec5f65b1ed9300ba59d Mon Sep 17 00:00:00 2001 From: Aditya Pakki Date: Thu, 19 Dec 2019 11:57:35 -0600 Subject: [PATCH 1091/1437] bpf: Remove unnecessary assertion on fp_old The two callers of bpf_prog_realloc - bpf_patch_insn_single and bpf_migrate_filter dereference the struct fp_old, before passing it to the function. Thus assertion to check fp_old is unnecessary and can be removed. Signed-off-by: Aditya Pakki Signed-off-by: Daniel Borkmann Link: https://lore.kernel.org/bpf/20191219175735.19231-1-pakki001@umn.edu --- kernel/bpf/core.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/kernel/bpf/core.c b/kernel/bpf/core.c index 2ff01a7161284..7622dfc36705d 100644 --- a/kernel/bpf/core.c +++ b/kernel/bpf/core.c @@ -222,8 +222,6 @@ struct bpf_prog *bpf_prog_realloc(struct bpf_prog *fp_old, unsigned int size, u32 pages, delta; int ret; - BUG_ON(fp_old == NULL); - size = round_up(size, PAGE_SIZE); pages = size / PAGE_SIZE; if (pages <= fp_old->pages) -- GitLab From cc52d9140aa920d8d61c7f6de3fff5fea6692ea9 Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Thu, 19 Dec 2019 22:19:50 +0100 Subject: [PATCH 1092/1437] bpf: Fix record_func_key to perform backtracking on r3 While testing Cilium with /unreleased/ Linus' tree under BPF-based NodePort implementation, I noticed a strange BPF SNAT engine behavior from time to time. In some cases it would do the correct SNAT/DNAT service translation, but at a random point in time it would just stop and perform an unexpected translation after SYN, SYN/ACK and stack would send a RST back. While initially assuming that there is some sort of a race condition in BPF code, adding trace_printk()s for debugging purposes at some point seemed to have resolved the issue auto-magically. Digging deeper on this Heisenbug and reducing the trace_printk() calls to an absolute minimum, it turns out that a single call would suffice to trigger / not trigger the seen RST issue, even though the logic of the program itself remains unchanged. Turns out the single call changed verifier pruning behavior to get everything to work. Reconstructing a minimal test case, the incorrect JIT dump looked as follows: # bpftool p d j i 11346 0xffffffffc0cba96c: [...] 21: movzbq 0x30(%rdi),%rax 26: cmp $0xd,%rax 2a: je 0x000000000000003a 2c: xor %edx,%edx 2e: movabs $0xffff89cc74e85800,%rsi 38: jmp 0x0000000000000049 3a: mov $0x2,%edx 3f: movabs $0xffff89cc74e85800,%rsi 49: mov -0x224(%rbp),%eax 4f: cmp $0x20,%eax 52: ja 0x0000000000000062 54: add $0x1,%eax 57: mov %eax,-0x224(%rbp) 5d: jmpq 0xffffffffffff6911 62: mov $0x1,%eax [...] Hence, unexpectedly, JIT emitted a direct jump even though retpoline based one would have been needed since in line 2c and 3a we have different slot keys in BPF reg r3. Verifier log of the test case reveals what happened: 0: (b7) r0 = 14 1: (73) *(u8 *)(r1 +48) = r0 2: (71) r0 = *(u8 *)(r1 +48) 3: (15) if r0 == 0xd goto pc+4 R0_w=inv(id=0,umax_value=255,var_off=(0x0; 0xff)) R1=ctx(id=0,off=0,imm=0) R10=fp0 4: (b7) r3 = 0 5: (18) r2 = 0xffff89cc74d54a00 7: (05) goto pc+3 11: (85) call bpf_tail_call#12 12: (b7) r0 = 1 13: (95) exit from 3 to 8: R0_w=inv13 R1=ctx(id=0,off=0,imm=0) R10=fp0 8: (b7) r3 = 2 9: (18) r2 = 0xffff89cc74d54a00 11: safe processed 13 insns (limit 1000000) [...] Second branch is pruned by verifier since considered safe, but issue is that record_func_key() couldn't have seen the index in line 3a and therefore decided that emitting a direct jump at this location was okay. Fix this by reusing our backtracking logic for precise scalar verification in order to prevent pruning on the slot key. This means verifier will track content of r3 all the way backwards and only prune if both scalars were unknown in state equivalence check and therefore poisoned in the first place in record_func_key(). The range is [x,x] in record_func_key() case since the slot always would have to be constant immediate. Correct verification after fix: 0: (b7) r0 = 14 1: (73) *(u8 *)(r1 +48) = r0 2: (71) r0 = *(u8 *)(r1 +48) 3: (15) if r0 == 0xd goto pc+4 R0_w=invP(id=0,umax_value=255,var_off=(0x0; 0xff)) R1=ctx(id=0,off=0,imm=0) R10=fp0 4: (b7) r3 = 0 5: (18) r2 = 0x0 7: (05) goto pc+3 11: (85) call bpf_tail_call#12 12: (b7) r0 = 1 13: (95) exit from 3 to 8: R0_w=invP13 R1=ctx(id=0,off=0,imm=0) R10=fp0 8: (b7) r3 = 2 9: (18) r2 = 0x0 11: (85) call bpf_tail_call#12 12: (b7) r0 = 1 13: (95) exit processed 15 insns (limit 1000000) [...] And correct corresponding JIT dump: # bpftool p d j i 11 0xffffffffc0dc34c4: [...] 21: movzbq 0x30(%rdi),%rax 26: cmp $0xd,%rax 2a: je 0x000000000000003a 2c: xor %edx,%edx 2e: movabs $0xffff9928b4c02200,%rsi 38: jmp 0x0000000000000049 3a: mov $0x2,%edx 3f: movabs $0xffff9928b4c02200,%rsi 49: cmp $0x4,%rdx 4d: jae 0x0000000000000093 4f: and $0x3,%edx 52: mov %edx,%edx 54: cmp %edx,0x24(%rsi) 57: jbe 0x0000000000000093 59: mov -0x224(%rbp),%eax 5f: cmp $0x20,%eax 62: ja 0x0000000000000093 64: add $0x1,%eax 67: mov %eax,-0x224(%rbp) 6d: mov 0x110(%rsi,%rdx,8),%rax 75: test %rax,%rax 78: je 0x0000000000000093 7a: mov 0x30(%rax),%rax 7e: add $0x19,%rax 82: callq 0x000000000000008e 87: pause 89: lfence 8c: jmp 0x0000000000000087 8e: mov %rax,(%rsp) 92: retq 93: mov $0x1,%eax [...] Also explicitly adding explicit env->allow_ptr_leaks to fixup_bpf_calls() since backtracking is enabled under former (direct jumps as well, but use different test). In case of only tracking different map pointers as in c93552c443eb ("bpf: properly enforce index mask to prevent out-of-bounds speculation"), pruning cannot make such short-cuts, neither if there are paths with scalar and non-scalar types as r3. mark_chain_precision() is only needed after we know that register_is_const(). If it was not the case, we already poison the key on first path and non-const key in later paths are not matching the scalar range in regsafe() either. Cilium NodePort testing passes fine as well now. Note, released kernels not affected. Fixes: d2e4c1e6c294 ("bpf: Constant map key tracking for prog array pokes") Signed-off-by: Daniel Borkmann Signed-off-by: Alexei Starovoitov Link: https://lore.kernel.org/bpf/ac43ffdeb7386c5bd688761ed266f3722bb39823.1576789878.git.daniel@iogearbox.net --- kernel/bpf/verifier.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 6ef71429d997f..4983940cbdca7 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -4134,6 +4134,7 @@ record_func_key(struct bpf_verifier_env *env, struct bpf_call_arg_meta *meta, struct bpf_map *map = meta->map_ptr; struct tnum range; u64 val; + int err; if (func_id != BPF_FUNC_tail_call) return 0; @@ -4150,6 +4151,10 @@ record_func_key(struct bpf_verifier_env *env, struct bpf_call_arg_meta *meta, return 0; } + err = mark_chain_precision(env, BPF_REG_3); + if (err) + return err; + val = reg->var_off.value; if (bpf_map_key_unseen(aux)) bpf_map_key_store(aux, val); @@ -9272,7 +9277,8 @@ static int fixup_bpf_calls(struct bpf_verifier_env *env) insn->code = BPF_JMP | BPF_TAIL_CALL; aux = &env->insn_aux_data[i + delta]; - if (prog->jit_requested && !expect_blinding && + if (env->allow_ptr_leaks && !expect_blinding && + prog->jit_requested && !bpf_map_key_poisoned(aux) && !bpf_map_ptr_poisoned(aux) && !bpf_map_ptr_unpriv(aux)) { -- GitLab From 3123d8018d4686cf193806c4e27a9853550ed895 Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Thu, 19 Dec 2019 22:19:51 +0100 Subject: [PATCH 1093/1437] bpf: Add further test_verifier cases for record_func_key Expand dummy prog generation such that we can easily check on return codes and add few more test cases to make sure we keep on tracking pruning behavior. # ./test_verifier [...] #1066/p XDP pkt read, pkt_data <= pkt_meta', bad access 1 OK #1067/p XDP pkt read, pkt_data <= pkt_meta', bad access 2 OK Summary: 1580 PASSED, 0 SKIPPED, 0 FAILED Also verified that JIT dump of added test cases looks good. Signed-off-by: Daniel Borkmann Signed-off-by: Alexei Starovoitov Link: https://lore.kernel.org/bpf/df7200b6021444fd369376d227de917357285b65.1576789878.git.daniel@iogearbox.net --- tools/testing/selftests/bpf/test_verifier.c | 43 ++--- .../selftests/bpf/verifier/ref_tracking.c | 6 +- .../selftests/bpf/verifier/runtime_jit.c | 151 ++++++++++++++++++ 3 files changed, 176 insertions(+), 24 deletions(-) diff --git a/tools/testing/selftests/bpf/test_verifier.c b/tools/testing/selftests/bpf/test_verifier.c index d27fd929abb90..87eaa49609a02 100644 --- a/tools/testing/selftests/bpf/test_verifier.c +++ b/tools/testing/selftests/bpf/test_verifier.c @@ -408,10 +408,10 @@ static void update_map(int fd, int index) assert(!bpf_map_update_elem(fd, &index, &value, 0)); } -static int create_prog_dummy1(enum bpf_prog_type prog_type) +static int create_prog_dummy_simple(enum bpf_prog_type prog_type, int ret) { struct bpf_insn prog[] = { - BPF_MOV64_IMM(BPF_REG_0, 42), + BPF_MOV64_IMM(BPF_REG_0, ret), BPF_EXIT_INSN(), }; @@ -419,14 +419,15 @@ static int create_prog_dummy1(enum bpf_prog_type prog_type) ARRAY_SIZE(prog), "GPL", 0, NULL, 0); } -static int create_prog_dummy2(enum bpf_prog_type prog_type, int mfd, int idx) +static int create_prog_dummy_loop(enum bpf_prog_type prog_type, int mfd, + int idx, int ret) { struct bpf_insn prog[] = { BPF_MOV64_IMM(BPF_REG_3, idx), BPF_LD_MAP_FD(BPF_REG_2, mfd), BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_tail_call), - BPF_MOV64_IMM(BPF_REG_0, 41), + BPF_MOV64_IMM(BPF_REG_0, ret), BPF_EXIT_INSN(), }; @@ -435,10 +436,9 @@ static int create_prog_dummy2(enum bpf_prog_type prog_type, int mfd, int idx) } static int create_prog_array(enum bpf_prog_type prog_type, uint32_t max_elem, - int p1key) + int p1key, int p2key, int p3key) { - int p2key = 1; - int mfd, p1fd, p2fd; + int mfd, p1fd, p2fd, p3fd; mfd = bpf_create_map(BPF_MAP_TYPE_PROG_ARRAY, sizeof(int), sizeof(int), max_elem, 0); @@ -449,23 +449,24 @@ static int create_prog_array(enum bpf_prog_type prog_type, uint32_t max_elem, return -1; } - p1fd = create_prog_dummy1(prog_type); - p2fd = create_prog_dummy2(prog_type, mfd, p2key); - if (p1fd < 0 || p2fd < 0) - goto out; + p1fd = create_prog_dummy_simple(prog_type, 42); + p2fd = create_prog_dummy_loop(prog_type, mfd, p2key, 41); + p3fd = create_prog_dummy_simple(prog_type, 24); + if (p1fd < 0 || p2fd < 0 || p3fd < 0) + goto err; if (bpf_map_update_elem(mfd, &p1key, &p1fd, BPF_ANY) < 0) - goto out; + goto err; if (bpf_map_update_elem(mfd, &p2key, &p2fd, BPF_ANY) < 0) - goto out; + goto err; + if (bpf_map_update_elem(mfd, &p3key, &p3fd, BPF_ANY) < 0) { +err: + close(mfd); + mfd = -1; + } + close(p3fd); close(p2fd); close(p1fd); - return mfd; -out: - close(p2fd); - close(p1fd); - close(mfd); - return -1; } static int create_map_in_map(void) @@ -684,7 +685,7 @@ static void do_test_fixup(struct bpf_test *test, enum bpf_prog_type prog_type, } if (*fixup_prog1) { - map_fds[4] = create_prog_array(prog_type, 4, 0); + map_fds[4] = create_prog_array(prog_type, 4, 0, 1, 2); do { prog[*fixup_prog1].imm = map_fds[4]; fixup_prog1++; @@ -692,7 +693,7 @@ static void do_test_fixup(struct bpf_test *test, enum bpf_prog_type prog_type, } if (*fixup_prog2) { - map_fds[5] = create_prog_array(prog_type, 8, 7); + map_fds[5] = create_prog_array(prog_type, 8, 7, 1, 2); do { prog[*fixup_prog2].imm = map_fds[5]; fixup_prog2++; diff --git a/tools/testing/selftests/bpf/verifier/ref_tracking.c b/tools/testing/selftests/bpf/verifier/ref_tracking.c index ebcbf154c4600..604b461517363 100644 --- a/tools/testing/selftests/bpf/verifier/ref_tracking.c +++ b/tools/testing/selftests/bpf/verifier/ref_tracking.c @@ -455,7 +455,7 @@ BPF_MOV64_REG(BPF_REG_1, BPF_REG_0), BPF_JMP_IMM(BPF_JNE, BPF_REG_1, 0, 7), /* bpf_tail_call() */ - BPF_MOV64_IMM(BPF_REG_3, 2), + BPF_MOV64_IMM(BPF_REG_3, 3), BPF_LD_MAP_FD(BPF_REG_2, 0), BPF_MOV64_REG(BPF_REG_1, BPF_REG_7), BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_tail_call), @@ -478,7 +478,7 @@ BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 0, 1), BPF_EMIT_CALL(BPF_FUNC_sk_release), /* bpf_tail_call() */ - BPF_MOV64_IMM(BPF_REG_3, 2), + BPF_MOV64_IMM(BPF_REG_3, 3), BPF_LD_MAP_FD(BPF_REG_2, 0), BPF_MOV64_REG(BPF_REG_1, BPF_REG_7), BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_tail_call), @@ -497,7 +497,7 @@ BPF_SK_LOOKUP(sk_lookup_tcp), /* bpf_tail_call() */ BPF_MOV64_REG(BPF_REG_6, BPF_REG_0), - BPF_MOV64_IMM(BPF_REG_3, 2), + BPF_MOV64_IMM(BPF_REG_3, 3), BPF_LD_MAP_FD(BPF_REG_2, 0), BPF_MOV64_REG(BPF_REG_1, BPF_REG_7), BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_tail_call), diff --git a/tools/testing/selftests/bpf/verifier/runtime_jit.c b/tools/testing/selftests/bpf/verifier/runtime_jit.c index a9a8f620e71cd..94c399d1facae 100644 --- a/tools/testing/selftests/bpf/verifier/runtime_jit.c +++ b/tools/testing/selftests/bpf/verifier/runtime_jit.c @@ -27,6 +27,19 @@ { "runtime/jit: tail_call within bounds, no prog", .insns = { + BPF_MOV64_IMM(BPF_REG_3, 3), + BPF_LD_MAP_FD(BPF_REG_2, 0), + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_tail_call), + BPF_MOV64_IMM(BPF_REG_0, 1), + BPF_EXIT_INSN(), + }, + .fixup_prog1 = { 1 }, + .result = ACCEPT, + .retval = 1, +}, +{ + "runtime/jit: tail_call within bounds, key 2", + .insns = { BPF_MOV64_IMM(BPF_REG_3, 2), BPF_LD_MAP_FD(BPF_REG_2, 0), BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_tail_call), @@ -35,8 +48,146 @@ }, .fixup_prog1 = { 1 }, .result = ACCEPT, + .retval = 24, +}, +{ + "runtime/jit: tail_call within bounds, key 2 / key 2, first branch", + .insns = { + BPF_MOV64_IMM(BPF_REG_0, 13), + BPF_STX_MEM(BPF_B, BPF_REG_1, BPF_REG_0, + offsetof(struct __sk_buff, cb[0])), + BPF_LDX_MEM(BPF_B, BPF_REG_0, BPF_REG_1, + offsetof(struct __sk_buff, cb[0])), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 13, 4), + BPF_MOV64_IMM(BPF_REG_3, 2), + BPF_LD_MAP_FD(BPF_REG_2, 0), + BPF_JMP_IMM(BPF_JA, 0, 0, 3), + BPF_MOV64_IMM(BPF_REG_3, 2), + BPF_LD_MAP_FD(BPF_REG_2, 0), + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_tail_call), + BPF_MOV64_IMM(BPF_REG_0, 1), + BPF_EXIT_INSN(), + }, + .fixup_prog1 = { 5, 9 }, + .result = ACCEPT, + .retval = 24, +}, +{ + "runtime/jit: tail_call within bounds, key 2 / key 2, second branch", + .insns = { + BPF_MOV64_IMM(BPF_REG_0, 14), + BPF_STX_MEM(BPF_B, BPF_REG_1, BPF_REG_0, + offsetof(struct __sk_buff, cb[0])), + BPF_LDX_MEM(BPF_B, BPF_REG_0, BPF_REG_1, + offsetof(struct __sk_buff, cb[0])), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 13, 4), + BPF_MOV64_IMM(BPF_REG_3, 2), + BPF_LD_MAP_FD(BPF_REG_2, 0), + BPF_JMP_IMM(BPF_JA, 0, 0, 3), + BPF_MOV64_IMM(BPF_REG_3, 2), + BPF_LD_MAP_FD(BPF_REG_2, 0), + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_tail_call), + BPF_MOV64_IMM(BPF_REG_0, 1), + BPF_EXIT_INSN(), + }, + .fixup_prog1 = { 5, 9 }, + .result = ACCEPT, + .retval = 24, +}, +{ + "runtime/jit: tail_call within bounds, key 0 / key 2, first branch", + .insns = { + BPF_MOV64_IMM(BPF_REG_0, 13), + BPF_STX_MEM(BPF_B, BPF_REG_1, BPF_REG_0, + offsetof(struct __sk_buff, cb[0])), + BPF_LDX_MEM(BPF_B, BPF_REG_0, BPF_REG_1, + offsetof(struct __sk_buff, cb[0])), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 13, 4), + BPF_MOV64_IMM(BPF_REG_3, 0), + BPF_LD_MAP_FD(BPF_REG_2, 0), + BPF_JMP_IMM(BPF_JA, 0, 0, 3), + BPF_MOV64_IMM(BPF_REG_3, 2), + BPF_LD_MAP_FD(BPF_REG_2, 0), + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_tail_call), + BPF_MOV64_IMM(BPF_REG_0, 1), + BPF_EXIT_INSN(), + }, + .fixup_prog1 = { 5, 9 }, + .result = ACCEPT, + .retval = 24, +}, +{ + "runtime/jit: tail_call within bounds, key 0 / key 2, second branch", + .insns = { + BPF_MOV64_IMM(BPF_REG_0, 14), + BPF_STX_MEM(BPF_B, BPF_REG_1, BPF_REG_0, + offsetof(struct __sk_buff, cb[0])), + BPF_LDX_MEM(BPF_B, BPF_REG_0, BPF_REG_1, + offsetof(struct __sk_buff, cb[0])), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 13, 4), + BPF_MOV64_IMM(BPF_REG_3, 0), + BPF_LD_MAP_FD(BPF_REG_2, 0), + BPF_JMP_IMM(BPF_JA, 0, 0, 3), + BPF_MOV64_IMM(BPF_REG_3, 2), + BPF_LD_MAP_FD(BPF_REG_2, 0), + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_tail_call), + BPF_MOV64_IMM(BPF_REG_0, 1), + BPF_EXIT_INSN(), + }, + .fixup_prog1 = { 5, 9 }, + .result = ACCEPT, + .retval = 42, +}, +{ + "runtime/jit: tail_call within bounds, different maps, first branch", + .insns = { + BPF_MOV64_IMM(BPF_REG_0, 13), + BPF_STX_MEM(BPF_B, BPF_REG_1, BPF_REG_0, + offsetof(struct __sk_buff, cb[0])), + BPF_LDX_MEM(BPF_B, BPF_REG_0, BPF_REG_1, + offsetof(struct __sk_buff, cb[0])), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 13, 4), + BPF_MOV64_IMM(BPF_REG_3, 0), + BPF_LD_MAP_FD(BPF_REG_2, 0), + BPF_JMP_IMM(BPF_JA, 0, 0, 3), + BPF_MOV64_IMM(BPF_REG_3, 0), + BPF_LD_MAP_FD(BPF_REG_2, 0), + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_tail_call), + BPF_MOV64_IMM(BPF_REG_0, 1), + BPF_EXIT_INSN(), + }, + .fixup_prog1 = { 5 }, + .fixup_prog2 = { 9 }, + .result_unpriv = REJECT, + .errstr_unpriv = "tail_call abusing map_ptr", + .result = ACCEPT, .retval = 1, }, +{ + "runtime/jit: tail_call within bounds, different maps, second branch", + .insns = { + BPF_MOV64_IMM(BPF_REG_0, 14), + BPF_STX_MEM(BPF_B, BPF_REG_1, BPF_REG_0, + offsetof(struct __sk_buff, cb[0])), + BPF_LDX_MEM(BPF_B, BPF_REG_0, BPF_REG_1, + offsetof(struct __sk_buff, cb[0])), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 13, 4), + BPF_MOV64_IMM(BPF_REG_3, 0), + BPF_LD_MAP_FD(BPF_REG_2, 0), + BPF_JMP_IMM(BPF_JA, 0, 0, 3), + BPF_MOV64_IMM(BPF_REG_3, 0), + BPF_LD_MAP_FD(BPF_REG_2, 0), + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_tail_call), + BPF_MOV64_IMM(BPF_REG_0, 1), + BPF_EXIT_INSN(), + }, + .fixup_prog1 = { 5 }, + .fixup_prog2 = { 9 }, + .result_unpriv = REJECT, + .errstr_unpriv = "tail_call abusing map_ptr", + .result = ACCEPT, + .retval = 42, +}, { "runtime/jit: tail_call out of bounds", .insns = { -- GitLab From 79e65c27f09683fbb50c33acab395d0ddf5302d2 Mon Sep 17 00:00:00 2001 From: Keita Suzuki Date: Wed, 11 Dec 2019 09:12:58 +0000 Subject: [PATCH 1094/1437] tracing: Avoid memory leak in process_system_preds() When failing in the allocation of filter_item, process_system_preds() goes to fail_mem, where the allocated filter is freed. However, this leads to memory leak of filter->filter_string and filter->prog, which is allocated before and in process_preds(). This bug has been detected by kmemleak as well. Fix this by changing kfree to __free_fiter. unreferenced object 0xffff8880658007c0 (size 32): comm "bash", pid 579, jiffies 4295096372 (age 17.752s) hex dump (first 32 bytes): 63 6f 6d 6d 6f 6e 5f 70 69 64 20 20 3e 20 31 30 common_pid > 10 00 00 00 00 00 00 00 00 65 73 00 00 00 00 00 00 ........es...... backtrace: [<0000000067441602>] kstrdup+0x2d/0x60 [<00000000141cf7b7>] apply_subsystem_event_filter+0x378/0x932 [<000000009ca32334>] subsystem_filter_write+0x5a/0x90 [<0000000072da2bee>] vfs_write+0xe1/0x240 [<000000004f14f473>] ksys_write+0xb4/0x150 [<00000000a968b4a0>] do_syscall_64+0x6d/0x1e0 [<000000001a189f40>] entry_SYSCALL_64_after_hwframe+0x44/0xa9 unreferenced object 0xffff888060c22d00 (size 64): comm "bash", pid 579, jiffies 4295096372 (age 17.752s) hex dump (first 32 bytes): 01 00 00 00 00 00 00 00 00 e8 d7 41 80 88 ff ff ...........A.... 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ backtrace: [<00000000b8c1b109>] process_preds+0x243/0x1820 [<000000003972c7f0>] apply_subsystem_event_filter+0x3be/0x932 [<000000009ca32334>] subsystem_filter_write+0x5a/0x90 [<0000000072da2bee>] vfs_write+0xe1/0x240 [<000000004f14f473>] ksys_write+0xb4/0x150 [<00000000a968b4a0>] do_syscall_64+0x6d/0x1e0 [<000000001a189f40>] entry_SYSCALL_64_after_hwframe+0x44/0xa9 unreferenced object 0xffff888041d7e800 (size 512): comm "bash", pid 579, jiffies 4295096372 (age 17.752s) hex dump (first 32 bytes): 70 bc 85 97 ff ff ff ff 0a 00 00 00 00 00 00 00 p............... 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ backtrace: [<000000001e04af34>] process_preds+0x71a/0x1820 [<000000003972c7f0>] apply_subsystem_event_filter+0x3be/0x932 [<000000009ca32334>] subsystem_filter_write+0x5a/0x90 [<0000000072da2bee>] vfs_write+0xe1/0x240 [<000000004f14f473>] ksys_write+0xb4/0x150 [<00000000a968b4a0>] do_syscall_64+0x6d/0x1e0 [<000000001a189f40>] entry_SYSCALL_64_after_hwframe+0x44/0xa9 Link: http://lkml.kernel.org/r/20191211091258.11310-1-keitasuzuki.park@sslab.ics.keio.ac.jp Cc: Ingo Molnar Cc: stable@vger.kernel.org Fixes: 404a3add43c9c ("tracing: Only add filter list when needed") Signed-off-by: Keita Suzuki Signed-off-by: Steven Rostedt (VMware) --- kernel/trace/trace_events_filter.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/trace/trace_events_filter.c b/kernel/trace/trace_events_filter.c index c9a74f82b14a2..bf44f6bbd0c36 100644 --- a/kernel/trace/trace_events_filter.c +++ b/kernel/trace/trace_events_filter.c @@ -1662,7 +1662,7 @@ static int process_system_preds(struct trace_subsystem_dir *dir, parse_error(pe, FILT_ERR_BAD_SUBSYS_FILTER, 0); return -EINVAL; fail_mem: - kfree(filter); + __free_filter(filter); /* If any call succeeded, we still need to sync */ if (!fail) tracepoint_synchronize_unregister(); -- GitLab From 106f41f5a302cb1f36c7543fae6a05de12e96fa4 Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (VMware)" Date: Wed, 11 Dec 2019 15:44:22 -0500 Subject: [PATCH 1095/1437] tracing: Have the histogram compare functions convert to u64 first The compare functions of the histogram code would be specific for the size of the value being compared (byte, short, int, long long). It would reference the value from the array via the type of the compare, but the value was stored in a 64 bit number. This is fine for little endian machines, but for big endian machines, it would end up comparing zeros or all ones (depending on the sign) for anything but 64 bit numbers. To fix this, first derference the value as a u64 then convert it to the type being compared. Link: http://lkml.kernel.org/r/20191211103557.7bed6928@gandalf.local.home Cc: stable@vger.kernel.org Fixes: 08d43a5fa063e ("tracing: Add lock-free tracing_map") Acked-by: Tom Zanussi Reported-by: Sven Schnelle Signed-off-by: Steven Rostedt (VMware) --- kernel/trace/tracing_map.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kernel/trace/tracing_map.c b/kernel/trace/tracing_map.c index 9a1c22310323d..9e31bfc818ff8 100644 --- a/kernel/trace/tracing_map.c +++ b/kernel/trace/tracing_map.c @@ -148,8 +148,8 @@ static int tracing_map_cmp_atomic64(void *val_a, void *val_b) #define DEFINE_TRACING_MAP_CMP_FN(type) \ static int tracing_map_cmp_##type(void *val_a, void *val_b) \ { \ - type a = *(type *)val_a; \ - type b = *(type *)val_b; \ + type a = (type)(*(u64 *)val_a); \ + type b = (type)(*(u64 *)val_b); \ \ return (a > b) ? 1 : ((a < b) ? -1 : 0); \ } -- GitLab From 291c2548458d3a8b326bfa8d7abbba86e036faf7 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Wed, 18 Dec 2019 08:43:01 +0000 Subject: [PATCH 1096/1437] scsi: lpfc: fix spelling mistakes of asynchronous There are spelling mistakes of asynchronous in a lpfc_printf_log message and comments. Fix these. Link: https://lore.kernel.org/r/20191218084301.627555-1-colin.king@canonical.com Signed-off-by: Colin Ian King Reviewed-by: James Smart Signed-off-by: Martin K. Petersen --- drivers/scsi/lpfc/lpfc_init.c | 2 +- drivers/scsi/lpfc/lpfc_sli.c | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/scsi/lpfc/lpfc_init.c b/drivers/scsi/lpfc/lpfc_init.c index 6298b17290989..6a04fdb3fbf21 100644 --- a/drivers/scsi/lpfc/lpfc_init.c +++ b/drivers/scsi/lpfc/lpfc_init.c @@ -5883,7 +5883,7 @@ void lpfc_sli4_async_event_proc(struct lpfc_hba *phba) break; default: lpfc_printf_log(phba, KERN_ERR, LOG_SLI, - "1804 Invalid asynchrous event code: " + "1804 Invalid asynchronous event code: " "x%x\n", bf_get(lpfc_trailer_code, &cq_event->cqe.mcqe_cmpl)); break; diff --git a/drivers/scsi/lpfc/lpfc_sli.c b/drivers/scsi/lpfc/lpfc_sli.c index c82b5792da98c..625c046ac4efa 100644 --- a/drivers/scsi/lpfc/lpfc_sli.c +++ b/drivers/scsi/lpfc/lpfc_sli.c @@ -8555,7 +8555,7 @@ lpfc_sli4_async_mbox_unblock(struct lpfc_hba *phba) psli->sli_flag &= ~LPFC_SLI_ASYNC_MBX_BLK; spin_unlock_irq(&phba->hbalock); - /* wake up worker thread to post asynchronlous mailbox command */ + /* wake up worker thread to post asynchronous mailbox command */ lpfc_worker_wake_up(phba); } @@ -8823,7 +8823,7 @@ lpfc_sli_issue_mbox_s4(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq, return rc; } - /* Now, interrupt mode asynchrous mailbox command */ + /* Now, interrupt mode asynchronous mailbox command */ rc = lpfc_mbox_cmd_check(phba, mboxq); if (rc) { lpfc_printf_log(phba, KERN_ERR, LOG_MBOX | LOG_SLI, @@ -13112,11 +13112,11 @@ lpfc_cq_event_setup(struct lpfc_hba *phba, void *entry, int size) } /** - * lpfc_sli4_sp_handle_async_event - Handle an asynchroous event + * lpfc_sli4_sp_handle_async_event - Handle an asynchronous event * @phba: Pointer to HBA context object. * @cqe: Pointer to mailbox completion queue entry. * - * This routine process a mailbox completion queue entry with asynchrous + * This routine process a mailbox completion queue entry with asynchronous * event. * * Return: true if work posted to worker thread, otherwise false. @@ -13270,7 +13270,7 @@ out_no_mqe_complete: * @cqe: Pointer to mailbox completion queue entry. * * This routine process a mailbox completion queue entry, it invokes the - * proper mailbox complete handling or asynchrous event handling routine + * proper mailbox complete handling or asynchronous event handling routine * according to the MCQE's async bit. * * Return: true if work posted to worker thread, otherwise false. -- GitLab From 71482fde704efdd8c3abe0faf34d922c61e8d76b Mon Sep 17 00:00:00 2001 From: Varun Prakash Date: Wed, 18 Dec 2019 19:15:31 +0530 Subject: [PATCH 1097/1437] scsi: libcxgbi: fix NULL pointer dereference in cxgbi_device_destroy() If cxgb4i_ddp_init() fails then cdev->cdev2ppm will be NULL, so add a check for NULL pointer before dereferencing it. Link: https://lore.kernel.org/r/1576676731-3068-1-git-send-email-varun@chelsio.com Signed-off-by: Varun Prakash Signed-off-by: Martin K. Petersen --- drivers/scsi/cxgbi/libcxgbi.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/scsi/cxgbi/libcxgbi.c b/drivers/scsi/cxgbi/libcxgbi.c index 0d044c1659609..dc9320f37ebfb 100644 --- a/drivers/scsi/cxgbi/libcxgbi.c +++ b/drivers/scsi/cxgbi/libcxgbi.c @@ -121,7 +121,8 @@ static inline void cxgbi_device_destroy(struct cxgbi_device *cdev) "cdev 0x%p, p# %u.\n", cdev, cdev->nports); cxgbi_hbas_remove(cdev); cxgbi_device_portmap_cleanup(cdev); - cxgbi_ppm_release(cdev->cdev2ppm(cdev)); + if (cdev->cdev2ppm) + cxgbi_ppm_release(cdev->cdev2ppm(cdev)); if (cdev->pmap.max_connect) cxgbi_free_big_mem(cdev->pmap.port_csk); kfree(cdev); -- GitLab From e4dc9a4c31fe10d1751c542702afc85be8a5c56a Mon Sep 17 00:00:00 2001 From: Israel Rukshin Date: Wed, 11 Dec 2019 17:36:02 +0200 Subject: [PATCH 1098/1437] scsi: target/iblock: Fix protection error with blocks greater than 512B The sector size of the block layer is 512 bytes, but integrity interval size might be different (in case of 4K block size of the media). At the initiator side the virtual start sector is the one that was originally submitted by the block layer (512 bytes) for the Reftag usage. The initiator converts the Reftag to integrity interval units and sends it to the target. So the target virtual start sector should be calculated at integrity interval units. prepare_fn() and complete_fn() don't remap correctly the Reftag when using incorrect units of the virtual start sector, which leads to the following protection error at the device: "blk_update_request: protection error, dev sdb, sector 2048 op 0x0:(READ) flags 0x10000 phys_seg 1 prio class 0" To fix that, set the seed in integrity interval units. Link: https://lore.kernel.org/r/1576078562-15240-1-git-send-email-israelr@mellanox.com Signed-off-by: Israel Rukshin Reviewed-by: Max Gurtovoy Reviewed-by: Sagi Grimberg Signed-off-by: Martin K. Petersen --- drivers/target/target_core_iblock.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/target/target_core_iblock.c b/drivers/target/target_core_iblock.c index 6949ea8bc387c..51ffd5c002dee 100644 --- a/drivers/target/target_core_iblock.c +++ b/drivers/target/target_core_iblock.c @@ -646,7 +646,9 @@ iblock_alloc_bip(struct se_cmd *cmd, struct bio *bio, } bip->bip_iter.bi_size = bio_integrity_bytes(bi, bio_sectors(bio)); - bip_set_seed(bip, bio->bi_iter.bi_sector); + /* virtual start sector must be in integrity interval units */ + bip_set_seed(bip, bio->bi_iter.bi_sector >> + (bi->interval_exp - SECTOR_SHIFT)); pr_debug("IBLOCK BIP Size: %u Sector: %llu\n", bip->bip_iter.bi_size, (unsigned long long)bip->bip_iter.bi_sector); -- GitLab From c9b3b8207bc487de02cbca968927ba2c2cb46aaf Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 10 Dec 2019 21:24:28 +0100 Subject: [PATCH 1099/1437] netfilter: nf_flow_table: fix big-endian integer overflow In some configurations, gcc reports an integer overflow: net/netfilter/nf_flow_table_offload.c: In function 'nf_flow_rule_match': net/netfilter/nf_flow_table_offload.c:80:21: error: unsigned conversion from 'int' to '__be16' {aka 'short unsigned int'} changes value from '327680' to '0' [-Werror=overflow] mask->tcp.flags = TCP_FLAG_RST | TCP_FLAG_FIN; ^~~~~~~~~~~~ From what I can tell, we want the upper 16 bits of these constants, so they need to be shifted in cpu-endian mode. Fixes: c29f74e0df7a ("netfilter: nf_flow_table: hardware offload support") Signed-off-by: Arnd Bergmann Signed-off-by: Pablo Neira Ayuso --- net/netfilter/nf_flow_table_offload.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/netfilter/nf_flow_table_offload.c b/net/netfilter/nf_flow_table_offload.c index de7a0d1e15c88..0d72e5ccb47be 100644 --- a/net/netfilter/nf_flow_table_offload.c +++ b/net/netfilter/nf_flow_table_offload.c @@ -88,7 +88,7 @@ static int nf_flow_rule_match(struct nf_flow_match *match, switch (tuple->l4proto) { case IPPROTO_TCP: key->tcp.flags = 0; - mask->tcp.flags = TCP_FLAG_RST | TCP_FLAG_FIN; + mask->tcp.flags = cpu_to_be16(be32_to_cpu(TCP_FLAG_RST | TCP_FLAG_FIN) >> 16); match->dissector.used_keys |= BIT(FLOW_DISSECTOR_KEY_TCP); break; case IPPROTO_UDP: -- GitLab From d05d5db815d56a0ce203ed297153d9794dfdcb68 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Fri, 13 Dec 2019 01:19:58 +0100 Subject: [PATCH 1100/1437] selftests: netfilter: extend flowtable test script with dnat rule NAT test currently covers snat (masquerade) only. Also add a dnat rule and then check that a connecting to the to-be-dnated address will work. Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- .../selftests/netfilter/nft_flowtable.sh | 39 ++++++++++++++++--- 1 file changed, 34 insertions(+), 5 deletions(-) diff --git a/tools/testing/selftests/netfilter/nft_flowtable.sh b/tools/testing/selftests/netfilter/nft_flowtable.sh index 16571ac1dab40..d3e0809ab3681 100755 --- a/tools/testing/selftests/netfilter/nft_flowtable.sh +++ b/tools/testing/selftests/netfilter/nft_flowtable.sh @@ -226,17 +226,19 @@ check_transfer() return 0 } -test_tcp_forwarding() +test_tcp_forwarding_ip() { local nsa=$1 local nsb=$2 + local dstip=$3 + local dstport=$4 local lret=0 ip netns exec $nsb nc -w 5 -l -p 12345 < "$ns2in" > "$ns2out" & lpid=$! sleep 1 - ip netns exec $nsa nc -w 4 10.0.2.99 12345 < "$ns1in" > "$ns1out" & + ip netns exec $nsa nc -w 4 "$dstip" "$dstport" < "$ns1in" > "$ns1out" & cpid=$! sleep 3 @@ -258,6 +260,28 @@ test_tcp_forwarding() return $lret } +test_tcp_forwarding() +{ + test_tcp_forwarding_ip "$1" "$2" 10.0.2.99 12345 + + return $? +} + +test_tcp_forwarding_nat() +{ + local lret + + test_tcp_forwarding_ip "$1" "$2" 10.0.2.99 12345 + lret=$? + + if [ $lret -eq 0 ] ; then + test_tcp_forwarding_ip "$1" "$2" 10.6.6.6 1666 + lret=$? + fi + + return $lret +} + make_file "$ns1in" "ns1" make_file "$ns2in" "ns2" @@ -283,14 +307,19 @@ ip -net ns2 route add 192.168.10.1 via 10.0.2.1 # Same, but with NAT enabled. ip netns exec nsr1 nft -f - < /dev/null ip netns exec ns2 sysctl net.ipv4.ip_no_pmtu_disc=0 > /dev/null -test_tcp_forwarding ns1 ns2 +test_tcp_forwarding_nat ns1 ns2 if [ $? -eq 0 ] ;then echo "PASS: flow offloaded for ns1/ns2 with NAT and pmtu discovery" else -- GitLab From e608f631f0ba5f1fc5ee2e260a3a35d13107cbfe Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Sun, 15 Dec 2019 03:49:25 +0100 Subject: [PATCH 1101/1437] netfilter: ebtables: compat: reject all padding in matches/watchers syzbot reported following splat: BUG: KASAN: vmalloc-out-of-bounds in size_entry_mwt net/bridge/netfilter/ebtables.c:2063 [inline] BUG: KASAN: vmalloc-out-of-bounds in compat_copy_entries+0x128b/0x1380 net/bridge/netfilter/ebtables.c:2155 Read of size 4 at addr ffffc900004461f4 by task syz-executor267/7937 CPU: 1 PID: 7937 Comm: syz-executor267 Not tainted 5.5.0-rc1-syzkaller #0 size_entry_mwt net/bridge/netfilter/ebtables.c:2063 [inline] compat_copy_entries+0x128b/0x1380 net/bridge/netfilter/ebtables.c:2155 compat_do_replace+0x344/0x720 net/bridge/netfilter/ebtables.c:2249 compat_do_ebt_set_ctl+0x22f/0x27e net/bridge/netfilter/ebtables.c:2333 [..] Because padding isn't considered during computation of ->buf_user_offset, "total" is decremented by fewer bytes than it should. Therefore, the first part of if (*total < sizeof(*entry) || entry->next_offset < sizeof(*entry)) will pass, -- it should not have. This causes oob access: entry->next_offset is past the vmalloced size. Reject padding and check that computed user offset (sum of ebt_entry structure plus all individual matches/watchers/targets) is same value that userspace gave us as the offset of the next entry. Reported-by: syzbot+f68108fed972453a0ad4@syzkaller.appspotmail.com Fixes: 81e675c227ec ("netfilter: ebtables: add CONFIG_COMPAT support") Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- net/bridge/netfilter/ebtables.c | 33 ++++++++++++++++----------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/net/bridge/netfilter/ebtables.c b/net/bridge/netfilter/ebtables.c index 4096d8a74a2bd..e1256e03a9a86 100644 --- a/net/bridge/netfilter/ebtables.c +++ b/net/bridge/netfilter/ebtables.c @@ -1867,7 +1867,7 @@ static int ebt_buf_count(struct ebt_entries_buf_state *state, unsigned int sz) } static int ebt_buf_add(struct ebt_entries_buf_state *state, - void *data, unsigned int sz) + const void *data, unsigned int sz) { if (state->buf_kern_start == NULL) goto count_only; @@ -1901,7 +1901,7 @@ enum compat_mwt { EBT_COMPAT_TARGET, }; -static int compat_mtw_from_user(struct compat_ebt_entry_mwt *mwt, +static int compat_mtw_from_user(const struct compat_ebt_entry_mwt *mwt, enum compat_mwt compat_mwt, struct ebt_entries_buf_state *state, const unsigned char *base) @@ -1979,22 +1979,23 @@ static int compat_mtw_from_user(struct compat_ebt_entry_mwt *mwt, /* return size of all matches, watchers or target, including necessary * alignment and padding. */ -static int ebt_size_mwt(struct compat_ebt_entry_mwt *match32, +static int ebt_size_mwt(const struct compat_ebt_entry_mwt *match32, unsigned int size_left, enum compat_mwt type, struct ebt_entries_buf_state *state, const void *base) { + const char *buf = (const char *)match32; int growth = 0; - char *buf; if (size_left == 0) return 0; - buf = (char *) match32; - - while (size_left >= sizeof(*match32)) { + do { struct ebt_entry_match *match_kern; int ret; + if (size_left < sizeof(*match32)) + return -EINVAL; + match_kern = (struct ebt_entry_match *) state->buf_kern_start; if (match_kern) { char *tmp; @@ -2031,22 +2032,18 @@ static int ebt_size_mwt(struct compat_ebt_entry_mwt *match32, if (match_kern) match_kern->match_size = ret; - /* rule should have no remaining data after target */ - if (type == EBT_COMPAT_TARGET && size_left) - return -EINVAL; - match32 = (struct compat_ebt_entry_mwt *) buf; - } + } while (size_left); return growth; } /* called for all ebt_entry structures. */ -static int size_entry_mwt(struct ebt_entry *entry, const unsigned char *base, +static int size_entry_mwt(const struct ebt_entry *entry, const unsigned char *base, unsigned int *total, struct ebt_entries_buf_state *state) { - unsigned int i, j, startoff, new_offset = 0; + unsigned int i, j, startoff, next_expected_off, new_offset = 0; /* stores match/watchers/targets & offset of next struct ebt_entry: */ unsigned int offsets[4]; unsigned int *offsets_update = NULL; @@ -2132,11 +2129,13 @@ static int size_entry_mwt(struct ebt_entry *entry, const unsigned char *base, return ret; } - startoff = state->buf_user_offset - startoff; + next_expected_off = state->buf_user_offset - startoff; + if (next_expected_off != entry->next_offset) + return -EINVAL; - if (WARN_ON(*total < startoff)) + if (*total < entry->next_offset) return -EINVAL; - *total -= startoff; + *total -= entry->next_offset; return 0; } -- GitLab From 8cb4ec44de42b99b92399b4d1daf3dc430ed0186 Mon Sep 17 00:00:00 2001 From: Phil Sutter Date: Wed, 18 Dec 2019 00:59:29 +0100 Subject: [PATCH 1102/1437] netfilter: nft_tproxy: Fix port selector on Big Endian MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On Big Endian architectures, u16 port value was extracted from the wrong parts of u32 sreg_port, just like commit 10596608c4d62 ("netfilter: nf_tables: fix mismatch in big-endian system") describes. Fixes: 4ed8eb6570a49 ("netfilter: nf_tables: Add native tproxy support") Signed-off-by: Phil Sutter Acked-by: Florian Westphal Acked-by: Máté Eckl Signed-off-by: Pablo Neira Ayuso --- net/netfilter/nft_tproxy.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/netfilter/nft_tproxy.c b/net/netfilter/nft_tproxy.c index f92a82c738808..95980154ef02c 100644 --- a/net/netfilter/nft_tproxy.c +++ b/net/netfilter/nft_tproxy.c @@ -50,7 +50,7 @@ static void nft_tproxy_eval_v4(const struct nft_expr *expr, taddr = nf_tproxy_laddr4(skb, taddr, iph->daddr); if (priv->sreg_port) - tport = regs->data[priv->sreg_port]; + tport = nft_reg_load16(®s->data[priv->sreg_port]); if (!tport) tport = hp->dest; @@ -117,7 +117,7 @@ static void nft_tproxy_eval_v6(const struct nft_expr *expr, taddr = *nf_tproxy_laddr6(skb, &taddr, &iph->daddr); if (priv->sreg_port) - tport = regs->data[priv->sreg_port]; + tport = nft_reg_load16(®s->data[priv->sreg_port]); if (!tport) tport = hp->dest; -- GitLab From 0dea4d039a7fdd97790a71da37c1ce67a5b52769 Mon Sep 17 00:00:00 2001 From: Russell King Date: Tue, 17 Dec 2019 13:50:29 +0000 Subject: [PATCH 1103/1437] net: sfp: report error on failure to read sfp soft status Report a rate-limited error if we fail to read the SFP soft status, and preserve the current status in that case. This avoids I2C bus errors from triggering a link flap. Signed-off-by: Russell King Signed-off-by: David S. Miller --- drivers/net/phy/sfp.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/drivers/net/phy/sfp.c b/drivers/net/phy/sfp.c index e54aef9210387..73c2969f11a4b 100644 --- a/drivers/net/phy/sfp.c +++ b/drivers/net/phy/sfp.c @@ -442,13 +442,20 @@ static unsigned int sfp_soft_get_state(struct sfp *sfp) { unsigned int state = 0; u8 status; + int ret; - if (sfp_read(sfp, true, SFP_STATUS, &status, sizeof(status)) == - sizeof(status)) { + ret = sfp_read(sfp, true, SFP_STATUS, &status, sizeof(status)); + if (ret == sizeof(status)) { if (status & SFP_STATUS_RX_LOS) state |= SFP_F_LOS; if (status & SFP_STATUS_TX_FAULT) state |= SFP_F_TX_FAULT; + } else { + dev_err_ratelimited(sfp->dev, + "failed to read SFP soft status: %d\n", + ret); + /* Preserve the current state */ + state = sfp->state; } return state & sfp->state_soft_mask; -- GitLab From 6791c102602b428b729890f6a38a319dfe225aba Mon Sep 17 00:00:00 2001 From: Russell King Date: Tue, 17 Dec 2019 15:47:36 +0000 Subject: [PATCH 1104/1437] net: mvpp2: cycle comphy to power it down Presently, at boot time, the comphys are enabled. For firmware compatibility reasons, the comphy driver does not power down the comphys at boot. Consequently, the ethernet comphys are left active until the network interfaces are brought through an up/down cycle. If the port is never used, the port wastes power needlessly. Arrange for the ethernet comphys to be cycled by the mvpp2 driver as if the interface went through an up/down cycle during driver probe, thereby powering them down. This saves: 270mW per 10G SFP+ port on the Macchiatobin Single Shot (eth0/eth1) 370mW per 10G PHY port on the Macchiatobin Double Shot (eth0/eth1) 160mW on the SFP port on either Macchiatobin flavour (eth3) Signed-off-by: Russell King Acked-by: Antoine Tenart Signed-off-by: David S. Miller --- drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c b/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c index 3d9eb5747d9ee..28eba99888c3d 100644 --- a/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c +++ b/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c @@ -5425,6 +5425,16 @@ static int mvpp2_port_probe(struct platform_device *pdev, port->phylink = NULL; } + /* Cycle the comphy to power it down, saving 270mW per port - + * don't worry about an error powering it up. When the comphy + * driver does this, we can remove this code. + */ + if (port->comphy) { + err = mvpp22_comphy_init(port); + if (err == 0) + phy_power_off(port->comphy); + } + err = register_netdev(dev); if (err < 0) { dev_err(&pdev->dev, "failed to register netdev\n"); -- GitLab From 0aa4d016c043d16a282e7e93edf6213a7b954c90 Mon Sep 17 00:00:00 2001 From: Antoine Tenart Date: Tue, 17 Dec 2019 18:07:41 +0100 Subject: [PATCH 1105/1437] of: mdio: export of_mdiobus_child_is_phy This patch exports of_mdiobus_child_is_phy, allowing to check if a child node is a network PHY. Signed-off-by: Antoine Tenart Signed-off-by: David S. Miller --- drivers/of/of_mdio.c | 3 ++- include/linux/of_mdio.h | 6 ++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/of/of_mdio.c b/drivers/of/of_mdio.c index c6b87ce2b0cc4..fc757ef6eadc5 100644 --- a/drivers/of/of_mdio.c +++ b/drivers/of/of_mdio.c @@ -162,7 +162,7 @@ static const struct of_device_id whitelist_phys[] = { * A device which is not a phy is expected to have a compatible string * indicating what sort of device it is. */ -static bool of_mdiobus_child_is_phy(struct device_node *child) +bool of_mdiobus_child_is_phy(struct device_node *child) { u32 phy_id; @@ -187,6 +187,7 @@ static bool of_mdiobus_child_is_phy(struct device_node *child) return false; } +EXPORT_SYMBOL(of_mdiobus_child_is_phy); /** * of_mdiobus_register - Register mii_bus and create PHYs from the device tree diff --git a/include/linux/of_mdio.h b/include/linux/of_mdio.h index 99cefe6f5edb9..79bc82e30c023 100644 --- a/include/linux/of_mdio.h +++ b/include/linux/of_mdio.h @@ -12,6 +12,7 @@ #include #if IS_ENABLED(CONFIG_OF_MDIO) +extern bool of_mdiobus_child_is_phy(struct device_node *child); extern int of_mdiobus_register(struct mii_bus *mdio, struct device_node *np); extern struct phy_device *of_phy_find_device(struct device_node *phy_np); extern struct phy_device *of_phy_connect(struct net_device *dev, @@ -54,6 +55,11 @@ static inline int of_mdio_parse_addr(struct device *dev, } #else /* CONFIG_OF_MDIO */ +static bool of_mdiobus_child_is_phy(struct device_node *child) +{ + return false; +} + static inline int of_mdiobus_register(struct mii_bus *mdio, struct device_node *np) { /* -- GitLab From ef8a2e27289ee1b24e743c4302a053b128e16284 Mon Sep 17 00:00:00 2001 From: Antoine Tenart Date: Tue, 17 Dec 2019 18:07:42 +0100 Subject: [PATCH 1106/1437] net: macb: fix probing of PHY not described in the dt This patch fixes the case where the PHY isn't described in the device tree. This is due to the way the MDIO bus is registered in the driver: whether the PHY is described in the device tree or not, the bus is registered through of_mdiobus_register. The function masks all the PHYs and only allow probing the ones described in the device tree. Prior to the Phylink conversion this was also done but later on in the driver the MDIO bus was manually scanned to circumvent the fact that the PHY wasn't described. This patch fixes it in a proper way, by registering the MDIO bus based on if the PHY attached to a given interface is described in the device tree or not. Fixes: 7897b071ac3b ("net: macb: convert to phylink") Signed-off-by: Antoine Tenart Signed-off-by: David S. Miller --- drivers/net/ethernet/cadence/macb_main.c | 27 ++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/cadence/macb_main.c b/drivers/net/ethernet/cadence/macb_main.c index 9c767ee252acc..c5ee363ca5dc3 100644 --- a/drivers/net/ethernet/cadence/macb_main.c +++ b/drivers/net/ethernet/cadence/macb_main.c @@ -664,9 +664,30 @@ static int macb_mii_probe(struct net_device *dev) return 0; } +static int macb_mdiobus_register(struct macb *bp) +{ + struct device_node *child, *np = bp->pdev->dev.of_node; + + /* Only create the PHY from the device tree if at least one PHY is + * described. Otherwise scan the entire MDIO bus. We do this to support + * old device tree that did not follow the best practices and did not + * describe their network PHYs. + */ + for_each_available_child_of_node(np, child) + if (of_mdiobus_child_is_phy(child)) { + /* The loop increments the child refcount, + * decrement it before returning. + */ + of_node_put(child); + + return of_mdiobus_register(bp->mii_bus, np); + } + + return mdiobus_register(bp->mii_bus); +} + static int macb_mii_init(struct macb *bp) { - struct device_node *np; int err = -ENXIO; /* Enable management port */ @@ -688,9 +709,7 @@ static int macb_mii_init(struct macb *bp) dev_set_drvdata(&bp->dev->dev, bp->mii_bus); - np = bp->pdev->dev.of_node; - - err = of_mdiobus_register(bp->mii_bus, np); + err = macb_mdiobus_register(bp); if (err) goto err_out_free_mdiobus; -- GitLab From 615f22f58029aa747b12768985e7f91cd053daa2 Mon Sep 17 00:00:00 2001 From: Aditya Pakki Date: Tue, 17 Dec 2019 14:43:00 -0600 Subject: [PATCH 1107/1437] nfc: s3fwrn5: replace the assertion with a WARN_ON In s3fwrn5_fw_recv_frame, if fw_info->rsp is not empty, the current code causes a crash via BUG_ON. However, s3fwrn5_fw_send_msg does not crash in such a scenario. The patch replaces the BUG_ON by returning the error to the callers and frees up skb. Signed-off-by: Aditya Pakki Signed-off-by: David S. Miller --- drivers/nfc/s3fwrn5/firmware.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/nfc/s3fwrn5/firmware.c b/drivers/nfc/s3fwrn5/firmware.c index be110d9cef022..de613c623a2cf 100644 --- a/drivers/nfc/s3fwrn5/firmware.c +++ b/drivers/nfc/s3fwrn5/firmware.c @@ -507,7 +507,10 @@ int s3fwrn5_fw_recv_frame(struct nci_dev *ndev, struct sk_buff *skb) struct s3fwrn5_info *info = nci_get_drvdata(ndev); struct s3fwrn5_fw_info *fw_info = &info->fw_info; - BUG_ON(fw_info->rsp); + if (WARN_ON(fw_info->rsp)) { + kfree_skb(skb); + return -EINVAL; + } fw_info->rsp = skb; -- GitLab From a886ca6fcfffd337482352f383c1002c72786b17 Mon Sep 17 00:00:00 2001 From: Aditya Pakki Date: Tue, 17 Dec 2019 15:06:19 -0600 Subject: [PATCH 1108/1437] hdlcdrv: replace unnecessary assertion in hdlcdrv_register In hdlcdrv_register, failure to register the driver causes a crash. The three callers of hdlcdrv_register all pass valid pointers and do not fail. The patch eliminates the unnecessary BUG_ON assertion. Signed-off-by: Aditya Pakki Signed-off-by: David S. Miller --- drivers/net/hamradio/hdlcdrv.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/net/hamradio/hdlcdrv.c b/drivers/net/hamradio/hdlcdrv.c index df495b5595f5f..e7413a6439292 100644 --- a/drivers/net/hamradio/hdlcdrv.c +++ b/drivers/net/hamradio/hdlcdrv.c @@ -687,8 +687,6 @@ struct net_device *hdlcdrv_register(const struct hdlcdrv_ops *ops, struct hdlcdrv_state *s; int err; - BUG_ON(ops == NULL); - if (privsize < sizeof(struct hdlcdrv_state)) privsize = sizeof(struct hdlcdrv_state); -- GitLab From 31c491e56ad1ad769e09f6971d267834bfc9bc39 Mon Sep 17 00:00:00 2001 From: John Hurley Date: Tue, 17 Dec 2019 21:57:16 +0000 Subject: [PATCH 1109/1437] nfp: flower: pass flow rule pointer directly to match functions In kernel 5.1, the flow offload API was introduced along with a helper function to extract the flow_rule from the TC offload struct. Each of the match helper functions are passed the offload struct and extract the flow rule to a local variable. Simplify the code while also removing the extra compat and local variable calls by extracting the rule once in the main match handler, and passing a reference to the rule direct to each helper. This patch does not change driver functionality. Signed-off-by: John Hurley Reviewed-by: Simon Horman Reviewed-by: Jakub Kicinski Signed-off-by: David S. Miller --- .../net/ethernet/netronome/nfp/flower/match.c | 76 +++++++------------ 1 file changed, 27 insertions(+), 49 deletions(-) diff --git a/drivers/net/ethernet/netronome/nfp/flower/match.c b/drivers/net/ethernet/netronome/nfp/flower/match.c index 9cc3ba17ff693..1079f37dbc788 100644 --- a/drivers/net/ethernet/netronome/nfp/flower/match.c +++ b/drivers/net/ethernet/netronome/nfp/flower/match.c @@ -10,9 +10,8 @@ static void nfp_flower_compile_meta_tci(struct nfp_flower_meta_tci *ext, struct nfp_flower_meta_tci *msk, - struct flow_cls_offload *flow, u8 key_type) + struct flow_rule *rule, u8 key_type) { - struct flow_rule *rule = flow_cls_offload_flow_rule(flow); u16 tmp_tci; memset(ext, 0, sizeof(struct nfp_flower_meta_tci)); @@ -77,11 +76,8 @@ nfp_flower_compile_port(struct nfp_flower_in_port *frame, u32 cmsg_port, static void nfp_flower_compile_mac(struct nfp_flower_mac_mpls *ext, - struct nfp_flower_mac_mpls *msk, - struct flow_cls_offload *flow) + struct nfp_flower_mac_mpls *msk, struct flow_rule *rule) { - struct flow_rule *rule = flow_cls_offload_flow_rule(flow); - memset(ext, 0, sizeof(struct nfp_flower_mac_mpls)); memset(msk, 0, sizeof(struct nfp_flower_mac_mpls)); @@ -130,10 +126,8 @@ nfp_flower_compile_mac(struct nfp_flower_mac_mpls *ext, static void nfp_flower_compile_tport(struct nfp_flower_tp_ports *ext, struct nfp_flower_tp_ports *msk, - struct flow_cls_offload *flow) + struct flow_rule *rule) { - struct flow_rule *rule = flow_cls_offload_flow_rule(flow); - memset(ext, 0, sizeof(struct nfp_flower_tp_ports)); memset(msk, 0, sizeof(struct nfp_flower_tp_ports)); @@ -150,11 +144,8 @@ nfp_flower_compile_tport(struct nfp_flower_tp_ports *ext, static void nfp_flower_compile_ip_ext(struct nfp_flower_ip_ext *ext, - struct nfp_flower_ip_ext *msk, - struct flow_cls_offload *flow) + struct nfp_flower_ip_ext *msk, struct flow_rule *rule) { - struct flow_rule *rule = flow_cls_offload_flow_rule(flow); - if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_BASIC)) { struct flow_match_basic match; @@ -224,10 +215,8 @@ nfp_flower_compile_ip_ext(struct nfp_flower_ip_ext *ext, static void nfp_flower_compile_ipv4(struct nfp_flower_ipv4 *ext, - struct nfp_flower_ipv4 *msk, - struct flow_cls_offload *flow) + struct nfp_flower_ipv4 *msk, struct flow_rule *rule) { - struct flow_rule *rule = flow_cls_offload_flow_rule(flow); struct flow_match_ipv4_addrs match; memset(ext, 0, sizeof(struct nfp_flower_ipv4)); @@ -241,16 +230,13 @@ nfp_flower_compile_ipv4(struct nfp_flower_ipv4 *ext, msk->ipv4_dst = match.mask->dst; } - nfp_flower_compile_ip_ext(&ext->ip_ext, &msk->ip_ext, flow); + nfp_flower_compile_ip_ext(&ext->ip_ext, &msk->ip_ext, rule); } static void nfp_flower_compile_ipv6(struct nfp_flower_ipv6 *ext, - struct nfp_flower_ipv6 *msk, - struct flow_cls_offload *flow) + struct nfp_flower_ipv6 *msk, struct flow_rule *rule) { - struct flow_rule *rule = flow_cls_offload_flow_rule(flow); - memset(ext, 0, sizeof(struct nfp_flower_ipv6)); memset(msk, 0, sizeof(struct nfp_flower_ipv6)); @@ -264,16 +250,15 @@ nfp_flower_compile_ipv6(struct nfp_flower_ipv6 *ext, msk->ipv6_dst = match.mask->dst; } - nfp_flower_compile_ip_ext(&ext->ip_ext, &msk->ip_ext, flow); + nfp_flower_compile_ip_ext(&ext->ip_ext, &msk->ip_ext, rule); } static int -nfp_flower_compile_geneve_opt(void *ext, void *msk, - struct flow_cls_offload *flow) +nfp_flower_compile_geneve_opt(void *ext, void *msk, struct flow_rule *rule) { struct flow_match_enc_opts match; - flow_rule_match_enc_opts(flow->rule, &match); + flow_rule_match_enc_opts(rule, &match); memcpy(ext, match.key->data, match.key->len); memcpy(msk, match.mask->data, match.mask->len); @@ -283,10 +268,8 @@ nfp_flower_compile_geneve_opt(void *ext, void *msk, static void nfp_flower_compile_tun_ipv4_addrs(struct nfp_flower_tun_ipv4 *ext, struct nfp_flower_tun_ipv4 *msk, - struct flow_cls_offload *flow) + struct flow_rule *rule) { - struct flow_rule *rule = flow_cls_offload_flow_rule(flow); - if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ENC_IPV4_ADDRS)) { struct flow_match_ipv4_addrs match; @@ -301,10 +284,8 @@ nfp_flower_compile_tun_ipv4_addrs(struct nfp_flower_tun_ipv4 *ext, static void nfp_flower_compile_tun_ip_ext(struct nfp_flower_tun_ip_ext *ext, struct nfp_flower_tun_ip_ext *msk, - struct flow_cls_offload *flow) + struct flow_rule *rule) { - struct flow_rule *rule = flow_cls_offload_flow_rule(flow); - if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ENC_IP)) { struct flow_match_ip match; @@ -319,10 +300,8 @@ nfp_flower_compile_tun_ip_ext(struct nfp_flower_tun_ip_ext *ext, static void nfp_flower_compile_ipv4_gre_tun(struct nfp_flower_ipv4_gre_tun *ext, struct nfp_flower_ipv4_gre_tun *msk, - struct flow_cls_offload *flow) + struct flow_rule *rule) { - struct flow_rule *rule = flow_cls_offload_flow_rule(flow); - memset(ext, 0, sizeof(struct nfp_flower_ipv4_gre_tun)); memset(msk, 0, sizeof(struct nfp_flower_ipv4_gre_tun)); @@ -341,17 +320,15 @@ nfp_flower_compile_ipv4_gre_tun(struct nfp_flower_ipv4_gre_tun *ext, msk->tun_flags = cpu_to_be16(NFP_FL_GRE_FLAG_KEY); } - nfp_flower_compile_tun_ipv4_addrs(&ext->ipv4, &msk->ipv4, flow); - nfp_flower_compile_tun_ip_ext(&ext->ip_ext, &msk->ip_ext, flow); + nfp_flower_compile_tun_ipv4_addrs(&ext->ipv4, &msk->ipv4, rule); + nfp_flower_compile_tun_ip_ext(&ext->ip_ext, &msk->ip_ext, rule); } static void nfp_flower_compile_ipv4_udp_tun(struct nfp_flower_ipv4_udp_tun *ext, struct nfp_flower_ipv4_udp_tun *msk, - struct flow_cls_offload *flow) + struct flow_rule *rule) { - struct flow_rule *rule = flow_cls_offload_flow_rule(flow); - memset(ext, 0, sizeof(struct nfp_flower_ipv4_udp_tun)); memset(msk, 0, sizeof(struct nfp_flower_ipv4_udp_tun)); @@ -366,8 +343,8 @@ nfp_flower_compile_ipv4_udp_tun(struct nfp_flower_ipv4_udp_tun *ext, msk->tun_id = cpu_to_be32(temp_vni); } - nfp_flower_compile_tun_ipv4_addrs(&ext->ipv4, &msk->ipv4, flow); - nfp_flower_compile_tun_ip_ext(&ext->ip_ext, &msk->ip_ext, flow); + nfp_flower_compile_tun_ipv4_addrs(&ext->ipv4, &msk->ipv4, rule); + nfp_flower_compile_tun_ip_ext(&ext->ip_ext, &msk->ip_ext, rule); } int nfp_flower_compile_flow_match(struct nfp_app *app, @@ -378,6 +355,7 @@ int nfp_flower_compile_flow_match(struct nfp_app *app, enum nfp_flower_tun_type tun_type, struct netlink_ext_ack *extack) { + struct flow_rule *rule = flow_cls_offload_flow_rule(flow); u32 port_id; int err; u8 *ext; @@ -393,7 +371,7 @@ int nfp_flower_compile_flow_match(struct nfp_app *app, nfp_flower_compile_meta_tci((struct nfp_flower_meta_tci *)ext, (struct nfp_flower_meta_tci *)msk, - flow, key_ls->key_layer); + rule, key_ls->key_layer); ext += sizeof(struct nfp_flower_meta_tci); msk += sizeof(struct nfp_flower_meta_tci); @@ -425,7 +403,7 @@ int nfp_flower_compile_flow_match(struct nfp_app *app, if (NFP_FLOWER_LAYER_MAC & key_ls->key_layer) { nfp_flower_compile_mac((struct nfp_flower_mac_mpls *)ext, (struct nfp_flower_mac_mpls *)msk, - flow); + rule); ext += sizeof(struct nfp_flower_mac_mpls); msk += sizeof(struct nfp_flower_mac_mpls); } @@ -433,7 +411,7 @@ int nfp_flower_compile_flow_match(struct nfp_app *app, if (NFP_FLOWER_LAYER_TP & key_ls->key_layer) { nfp_flower_compile_tport((struct nfp_flower_tp_ports *)ext, (struct nfp_flower_tp_ports *)msk, - flow); + rule); ext += sizeof(struct nfp_flower_tp_ports); msk += sizeof(struct nfp_flower_tp_ports); } @@ -441,7 +419,7 @@ int nfp_flower_compile_flow_match(struct nfp_app *app, if (NFP_FLOWER_LAYER_IPV4 & key_ls->key_layer) { nfp_flower_compile_ipv4((struct nfp_flower_ipv4 *)ext, (struct nfp_flower_ipv4 *)msk, - flow); + rule); ext += sizeof(struct nfp_flower_ipv4); msk += sizeof(struct nfp_flower_ipv4); } @@ -449,7 +427,7 @@ int nfp_flower_compile_flow_match(struct nfp_app *app, if (NFP_FLOWER_LAYER_IPV6 & key_ls->key_layer) { nfp_flower_compile_ipv6((struct nfp_flower_ipv6 *)ext, (struct nfp_flower_ipv6 *)msk, - flow); + rule); ext += sizeof(struct nfp_flower_ipv6); msk += sizeof(struct nfp_flower_ipv6); } @@ -457,7 +435,7 @@ int nfp_flower_compile_flow_match(struct nfp_app *app, if (key_ls->key_layer_two & NFP_FLOWER_LAYER2_GRE) { __be32 tun_dst; - nfp_flower_compile_ipv4_gre_tun((void *)ext, (void *)msk, flow); + nfp_flower_compile_ipv4_gre_tun((void *)ext, (void *)msk, rule); tun_dst = ((struct nfp_flower_ipv4_gre_tun *)ext)->ipv4.dst; ext += sizeof(struct nfp_flower_ipv4_gre_tun); msk += sizeof(struct nfp_flower_ipv4_gre_tun); @@ -473,7 +451,7 @@ int nfp_flower_compile_flow_match(struct nfp_app *app, key_ls->key_layer_two & NFP_FLOWER_LAYER2_GENEVE) { __be32 tun_dst; - nfp_flower_compile_ipv4_udp_tun((void *)ext, (void *)msk, flow); + nfp_flower_compile_ipv4_udp_tun((void *)ext, (void *)msk, rule); tun_dst = ((struct nfp_flower_ipv4_udp_tun *)ext)->ipv4.dst; ext += sizeof(struct nfp_flower_ipv4_udp_tun); msk += sizeof(struct nfp_flower_ipv4_udp_tun); @@ -485,7 +463,7 @@ int nfp_flower_compile_flow_match(struct nfp_app *app, nfp_tunnel_add_ipv4_off(app, tun_dst); if (key_ls->key_layer_two & NFP_FLOWER_LAYER2_GENEVE_OP) { - err = nfp_flower_compile_geneve_opt(ext, msk, flow); + err = nfp_flower_compile_geneve_opt(ext, msk, rule); if (err) return err; } -- GitLab From 77a5f73a8b337c22ccadefbed30d2a36425388d1 Mon Sep 17 00:00:00 2001 From: John Hurley Date: Tue, 17 Dec 2019 21:57:17 +0000 Subject: [PATCH 1110/1437] nfp: flower: move udp tunnel key match compilation to helper function IPv4 UDP and GRE tunnel match rule compile helpers share functions for compiling fields such as IP addresses. However, they handle fields such tunnel IDs differently. Create new helper functions for compiling GRE and UDP tunnel key data. This is in preparation for supporting IPv6 tunnels where these new functions can be reused. This patch does not change functionality. Signed-off-by: John Hurley Reviewed-by: Simon Horman Reviewed-by: Jakub Kicinski Signed-off-by: David S. Miller --- .../net/ethernet/netronome/nfp/flower/match.c | 57 ++++++++++++------- 1 file changed, 35 insertions(+), 22 deletions(-) diff --git a/drivers/net/ethernet/netronome/nfp/flower/match.c b/drivers/net/ethernet/netronome/nfp/flower/match.c index 1079f37dbc788..1cf8eaeb7ec4b 100644 --- a/drivers/net/ethernet/netronome/nfp/flower/match.c +++ b/drivers/net/ethernet/netronome/nfp/flower/match.c @@ -297,6 +297,38 @@ nfp_flower_compile_tun_ip_ext(struct nfp_flower_tun_ip_ext *ext, } } +static void +nfp_flower_compile_tun_udp_key(__be32 *key, __be32 *key_msk, + struct flow_rule *rule) +{ + if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ENC_KEYID)) { + struct flow_match_enc_keyid match; + u32 vni; + + flow_rule_match_enc_keyid(rule, &match); + vni = be32_to_cpu(match.key->keyid) << NFP_FL_TUN_VNI_OFFSET; + *key = cpu_to_be32(vni); + vni = be32_to_cpu(match.mask->keyid) << NFP_FL_TUN_VNI_OFFSET; + *key_msk = cpu_to_be32(vni); + } +} + +static void +nfp_flower_compile_tun_gre_key(__be32 *key, __be32 *key_msk, __be16 *flags, + __be16 *flags_msk, struct flow_rule *rule) +{ + if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ENC_KEYID)) { + struct flow_match_enc_keyid match; + + flow_rule_match_enc_keyid(rule, &match); + *key = match.key->keyid; + *key_msk = match.mask->keyid; + + *flags = cpu_to_be16(NFP_FL_GRE_FLAG_KEY); + *flags_msk = cpu_to_be16(NFP_FL_GRE_FLAG_KEY); + } +} + static void nfp_flower_compile_ipv4_gre_tun(struct nfp_flower_ipv4_gre_tun *ext, struct nfp_flower_ipv4_gre_tun *msk, @@ -309,19 +341,10 @@ nfp_flower_compile_ipv4_gre_tun(struct nfp_flower_ipv4_gre_tun *ext, ext->ethertype = cpu_to_be16(ETH_P_TEB); msk->ethertype = cpu_to_be16(~0); - if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ENC_KEYID)) { - struct flow_match_enc_keyid match; - - flow_rule_match_enc_keyid(rule, &match); - ext->tun_key = match.key->keyid; - msk->tun_key = match.mask->keyid; - - ext->tun_flags = cpu_to_be16(NFP_FL_GRE_FLAG_KEY); - msk->tun_flags = cpu_to_be16(NFP_FL_GRE_FLAG_KEY); - } - nfp_flower_compile_tun_ipv4_addrs(&ext->ipv4, &msk->ipv4, rule); nfp_flower_compile_tun_ip_ext(&ext->ip_ext, &msk->ip_ext, rule); + nfp_flower_compile_tun_gre_key(&ext->tun_key, &msk->tun_key, + &ext->tun_flags, &msk->tun_flags, rule); } static void @@ -332,19 +355,9 @@ nfp_flower_compile_ipv4_udp_tun(struct nfp_flower_ipv4_udp_tun *ext, memset(ext, 0, sizeof(struct nfp_flower_ipv4_udp_tun)); memset(msk, 0, sizeof(struct nfp_flower_ipv4_udp_tun)); - if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ENC_KEYID)) { - struct flow_match_enc_keyid match; - u32 temp_vni; - - flow_rule_match_enc_keyid(rule, &match); - temp_vni = be32_to_cpu(match.key->keyid) << NFP_FL_TUN_VNI_OFFSET; - ext->tun_id = cpu_to_be32(temp_vni); - temp_vni = be32_to_cpu(match.mask->keyid) << NFP_FL_TUN_VNI_OFFSET; - msk->tun_id = cpu_to_be32(temp_vni); - } - nfp_flower_compile_tun_ipv4_addrs(&ext->ipv4, &msk->ipv4, rule); nfp_flower_compile_tun_ip_ext(&ext->ip_ext, &msk->ip_ext, rule); + nfp_flower_compile_tun_udp_key(&ext->tun_id, &msk->tun_id, rule); } int nfp_flower_compile_flow_match(struct nfp_app *app, -- GitLab From 78a722af4ad9cb2d82f230f697220af5aa6723c1 Mon Sep 17 00:00:00 2001 From: John Hurley Date: Tue, 17 Dec 2019 21:57:18 +0000 Subject: [PATCH 1111/1437] nfp: flower: compile match for IPv6 tunnels IPv6 tunnel matches are now supported by firmware. Modify the NFP driver to compile these match rules. IPv6 matches are handled similar to IPv4 tunnels with the difference the address length. The type of tunnel is indicated by the same bitmap that is used in IPv4 with an extra bit signifying that the IPv6 variation should be used. Only compile IPv6 tunnel matches when the fw features symbol indicated that they are compatible with the currently loaded fw. Signed-off-by: John Hurley Reviewed-by: Simon Horman Reviewed-by: Jakub Kicinski Signed-off-by: David S. Miller --- .../net/ethernet/netronome/nfp/flower/cmsg.h | 83 +++++++++++++ .../net/ethernet/netronome/nfp/flower/main.h | 1 + .../net/ethernet/netronome/nfp/flower/match.c | 111 ++++++++++++++---- .../ethernet/netronome/nfp/flower/offload.c | 102 +++++++++++----- 4 files changed, 246 insertions(+), 51 deletions(-) diff --git a/drivers/net/ethernet/netronome/nfp/flower/cmsg.h b/drivers/net/ethernet/netronome/nfp/flower/cmsg.h index 7eb2ec8969c39..8d12805baa0d2 100644 --- a/drivers/net/ethernet/netronome/nfp/flower/cmsg.h +++ b/drivers/net/ethernet/netronome/nfp/flower/cmsg.h @@ -26,6 +26,7 @@ #define NFP_FLOWER_LAYER2_GRE BIT(0) #define NFP_FLOWER_LAYER2_GENEVE BIT(5) #define NFP_FLOWER_LAYER2_GENEVE_OP BIT(6) +#define NFP_FLOWER_LAYER2_TUN_IPV6 BIT(7) #define NFP_FLOWER_MASK_VLAN_PRIO GENMASK(15, 13) #define NFP_FLOWER_MASK_VLAN_PRESENT BIT(12) @@ -63,6 +64,7 @@ #define NFP_FL_MAX_GENEVE_OPT_ACT 32 #define NFP_FL_MAX_GENEVE_OPT_CNT 64 #define NFP_FL_MAX_GENEVE_OPT_KEY 32 +#define NFP_FL_MAX_GENEVE_OPT_KEY_V6 8 /* Action opcodes */ #define NFP_FL_ACTION_OPCODE_OUTPUT 0 @@ -387,6 +389,11 @@ struct nfp_flower_tun_ipv4 { __be32 dst; }; +struct nfp_flower_tun_ipv6 { + struct in6_addr src; + struct in6_addr dst; +}; + struct nfp_flower_tun_ip_ext { u8 tos; u8 ttl; @@ -416,6 +423,42 @@ struct nfp_flower_ipv4_udp_tun { __be32 tun_id; }; +/* Flow Frame IPv6 UDP TUNNEL --> Tunnel details (11W/44B) + * ----------------------------------------------------------------- + * 3 2 1 + * 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | ipv6_addr_src, 31 - 0 | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | ipv6_addr_src, 63 - 32 | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | ipv6_addr_src, 95 - 64 | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | ipv6_addr_src, 127 - 96 | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | ipv6_addr_dst, 31 - 0 | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | ipv6_addr_dst, 63 - 32 | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | ipv6_addr_dst, 95 - 64 | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | ipv6_addr_dst, 127 - 96 | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Reserved | tos | ttl | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Reserved | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | VNI | Reserved | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ +struct nfp_flower_ipv6_udp_tun { + struct nfp_flower_tun_ipv6 ipv6; + __be16 reserved1; + struct nfp_flower_tun_ip_ext ip_ext; + __be32 reserved2; + __be32 tun_id; +}; + /* Flow Frame GRE TUNNEL --> Tunnel details (6W/24B) * ----------------------------------------------------------------- * 3 2 1 @@ -445,6 +488,46 @@ struct nfp_flower_ipv4_gre_tun { __be32 reserved2; }; +/* Flow Frame GRE TUNNEL V6 --> Tunnel details (12W/48B) + * ----------------------------------------------------------------- + * 3 2 1 + * 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | ipv6_addr_src, 31 - 0 | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | ipv6_addr_src, 63 - 32 | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | ipv6_addr_src, 95 - 64 | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | ipv6_addr_src, 127 - 96 | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | ipv6_addr_dst, 31 - 0 | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | ipv6_addr_dst, 63 - 32 | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | ipv6_addr_dst, 95 - 64 | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | ipv6_addr_dst, 127 - 96 | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | tun_flags | tos | ttl | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Reserved | Ethertype | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Key | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Reserved | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ +struct nfp_flower_ipv6_gre_tun { + struct nfp_flower_tun_ipv6 ipv6; + __be16 tun_flags; + struct nfp_flower_tun_ip_ext ip_ext; + __be16 reserved1; + __be16 ethertype; + __be32 tun_key; + __be32 reserved2; +}; + struct nfp_flower_geneve_options { u8 data[NFP_FL_MAX_GENEVE_OPT_KEY]; }; diff --git a/drivers/net/ethernet/netronome/nfp/flower/main.h b/drivers/net/ethernet/netronome/nfp/flower/main.h index 31d94592a7c02..386d6297e0f1e 100644 --- a/drivers/net/ethernet/netronome/nfp/flower/main.h +++ b/drivers/net/ethernet/netronome/nfp/flower/main.h @@ -43,6 +43,7 @@ struct nfp_app; #define NFP_FL_FEATS_VF_RLIM BIT(4) #define NFP_FL_FEATS_FLOW_MOD BIT(5) #define NFP_FL_FEATS_PRE_TUN_RULES BIT(6) +#define NFP_FL_FEATS_IPV6_TUN BIT(7) #define NFP_FL_FEATS_FLOW_MERGE BIT(30) #define NFP_FL_FEATS_LAG BIT(31) diff --git a/drivers/net/ethernet/netronome/nfp/flower/match.c b/drivers/net/ethernet/netronome/nfp/flower/match.c index 1cf8eaeb7ec4b..2410ead05a064 100644 --- a/drivers/net/ethernet/netronome/nfp/flower/match.c +++ b/drivers/net/ethernet/netronome/nfp/flower/match.c @@ -281,6 +281,22 @@ nfp_flower_compile_tun_ipv4_addrs(struct nfp_flower_tun_ipv4 *ext, } } +static void +nfp_flower_compile_tun_ipv6_addrs(struct nfp_flower_tun_ipv6 *ext, + struct nfp_flower_tun_ipv6 *msk, + struct flow_rule *rule) +{ + if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ENC_IPV6_ADDRS)) { + struct flow_match_ipv6_addrs match; + + flow_rule_match_enc_ipv6_addrs(rule, &match); + ext->src = match.key->src; + ext->dst = match.key->dst; + msk->src = match.mask->src; + msk->dst = match.mask->dst; + } +} + static void nfp_flower_compile_tun_ip_ext(struct nfp_flower_tun_ip_ext *ext, struct nfp_flower_tun_ip_ext *msk, @@ -360,6 +376,37 @@ nfp_flower_compile_ipv4_udp_tun(struct nfp_flower_ipv4_udp_tun *ext, nfp_flower_compile_tun_udp_key(&ext->tun_id, &msk->tun_id, rule); } +static void +nfp_flower_compile_ipv6_udp_tun(struct nfp_flower_ipv6_udp_tun *ext, + struct nfp_flower_ipv6_udp_tun *msk, + struct flow_rule *rule) +{ + memset(ext, 0, sizeof(struct nfp_flower_ipv6_udp_tun)); + memset(msk, 0, sizeof(struct nfp_flower_ipv6_udp_tun)); + + nfp_flower_compile_tun_ipv6_addrs(&ext->ipv6, &msk->ipv6, rule); + nfp_flower_compile_tun_ip_ext(&ext->ip_ext, &msk->ip_ext, rule); + nfp_flower_compile_tun_udp_key(&ext->tun_id, &msk->tun_id, rule); +} + +static void +nfp_flower_compile_ipv6_gre_tun(struct nfp_flower_ipv6_gre_tun *ext, + struct nfp_flower_ipv6_gre_tun *msk, + struct flow_rule *rule) +{ + memset(ext, 0, sizeof(struct nfp_flower_ipv6_gre_tun)); + memset(msk, 0, sizeof(struct nfp_flower_ipv6_gre_tun)); + + /* NVGRE is the only supported GRE tunnel type */ + ext->ethertype = cpu_to_be16(ETH_P_TEB); + msk->ethertype = cpu_to_be16(~0); + + nfp_flower_compile_tun_ipv6_addrs(&ext->ipv6, &msk->ipv6, rule); + nfp_flower_compile_tun_ip_ext(&ext->ip_ext, &msk->ip_ext, rule); + nfp_flower_compile_tun_gre_key(&ext->tun_key, &msk->tun_key, + &ext->tun_flags, &msk->tun_flags, rule); +} + int nfp_flower_compile_flow_match(struct nfp_app *app, struct flow_cls_offload *flow, struct nfp_fl_key_ls *key_ls, @@ -446,34 +493,50 @@ int nfp_flower_compile_flow_match(struct nfp_app *app, } if (key_ls->key_layer_two & NFP_FLOWER_LAYER2_GRE) { - __be32 tun_dst; - - nfp_flower_compile_ipv4_gre_tun((void *)ext, (void *)msk, rule); - tun_dst = ((struct nfp_flower_ipv4_gre_tun *)ext)->ipv4.dst; - ext += sizeof(struct nfp_flower_ipv4_gre_tun); - msk += sizeof(struct nfp_flower_ipv4_gre_tun); - - /* Store the tunnel destination in the rule data. - * This must be present and be an exact match. - */ - nfp_flow->nfp_tun_ipv4_addr = tun_dst; - nfp_tunnel_add_ipv4_off(app, tun_dst); + if (key_ls->key_layer_two & NFP_FLOWER_LAYER2_TUN_IPV6) { + nfp_flower_compile_ipv6_gre_tun((void *)ext, + (void *)msk, rule); + ext += sizeof(struct nfp_flower_ipv6_gre_tun); + msk += sizeof(struct nfp_flower_ipv6_gre_tun); + } else { + __be32 dst; + + nfp_flower_compile_ipv4_gre_tun((void *)ext, + (void *)msk, rule); + dst = ((struct nfp_flower_ipv4_gre_tun *)ext)->ipv4.dst; + ext += sizeof(struct nfp_flower_ipv4_gre_tun); + msk += sizeof(struct nfp_flower_ipv4_gre_tun); + + /* Store the tunnel destination in the rule data. + * This must be present and be an exact match. + */ + nfp_flow->nfp_tun_ipv4_addr = dst; + nfp_tunnel_add_ipv4_off(app, dst); + } } if (key_ls->key_layer & NFP_FLOWER_LAYER_VXLAN || key_ls->key_layer_two & NFP_FLOWER_LAYER2_GENEVE) { - __be32 tun_dst; - - nfp_flower_compile_ipv4_udp_tun((void *)ext, (void *)msk, rule); - tun_dst = ((struct nfp_flower_ipv4_udp_tun *)ext)->ipv4.dst; - ext += sizeof(struct nfp_flower_ipv4_udp_tun); - msk += sizeof(struct nfp_flower_ipv4_udp_tun); - - /* Store the tunnel destination in the rule data. - * This must be present and be an exact match. - */ - nfp_flow->nfp_tun_ipv4_addr = tun_dst; - nfp_tunnel_add_ipv4_off(app, tun_dst); + if (key_ls->key_layer_two & NFP_FLOWER_LAYER2_TUN_IPV6) { + nfp_flower_compile_ipv6_udp_tun((void *)ext, + (void *)msk, rule); + ext += sizeof(struct nfp_flower_ipv6_udp_tun); + msk += sizeof(struct nfp_flower_ipv6_udp_tun); + } else { + __be32 dst; + + nfp_flower_compile_ipv4_udp_tun((void *)ext, + (void *)msk, rule); + dst = ((struct nfp_flower_ipv4_udp_tun *)ext)->ipv4.dst; + ext += sizeof(struct nfp_flower_ipv4_udp_tun); + msk += sizeof(struct nfp_flower_ipv4_udp_tun); + + /* Store the tunnel destination in the rule data. + * This must be present and be an exact match. + */ + nfp_flow->nfp_tun_ipv4_addr = dst; + nfp_tunnel_add_ipv4_off(app, dst); + } if (key_ls->key_layer_two & NFP_FLOWER_LAYER2_GENEVE_OP) { err = nfp_flower_compile_geneve_opt(ext, msk, rule); diff --git a/drivers/net/ethernet/netronome/nfp/flower/offload.c b/drivers/net/ethernet/netronome/nfp/flower/offload.c index 987ae221f6be7..099735086fb26 100644 --- a/drivers/net/ethernet/netronome/nfp/flower/offload.c +++ b/drivers/net/ethernet/netronome/nfp/flower/offload.c @@ -54,6 +54,10 @@ (BIT(FLOW_DISSECTOR_KEY_ENC_CONTROL) | \ BIT(FLOW_DISSECTOR_KEY_ENC_IPV4_ADDRS)) +#define NFP_FLOWER_WHITELIST_TUN_DISSECTOR_V6_R \ + (BIT(FLOW_DISSECTOR_KEY_ENC_CONTROL) | \ + BIT(FLOW_DISSECTOR_KEY_ENC_IPV6_ADDRS)) + #define NFP_FLOWER_MERGE_FIELDS \ (NFP_FLOWER_LAYER_PORT | \ NFP_FLOWER_LAYER_MAC | \ @@ -146,10 +150,11 @@ static bool nfp_flower_check_higher_than_l3(struct flow_cls_offload *f) static int nfp_flower_calc_opt_layer(struct flow_dissector_key_enc_opts *enc_opts, - u32 *key_layer_two, int *key_size, + u32 *key_layer_two, int *key_size, bool ipv6, struct netlink_ext_ack *extack) { - if (enc_opts->len > NFP_FL_MAX_GENEVE_OPT_KEY) { + if (enc_opts->len > NFP_FL_MAX_GENEVE_OPT_KEY || + (ipv6 && enc_opts->len > NFP_FL_MAX_GENEVE_OPT_KEY_V6)) { NL_SET_ERR_MSG_MOD(extack, "unsupported offload: geneve options exceed maximum length"); return -EOPNOTSUPP; } @@ -167,7 +172,7 @@ nfp_flower_calc_udp_tun_layer(struct flow_dissector_key_ports *enc_ports, struct flow_dissector_key_enc_opts *enc_op, u32 *key_layer_two, u8 *key_layer, int *key_size, struct nfp_flower_priv *priv, - enum nfp_flower_tun_type *tun_type, + enum nfp_flower_tun_type *tun_type, bool ipv6, struct netlink_ext_ack *extack) { int err; @@ -176,7 +181,15 @@ nfp_flower_calc_udp_tun_layer(struct flow_dissector_key_ports *enc_ports, case htons(IANA_VXLAN_UDP_PORT): *tun_type = NFP_FL_TUNNEL_VXLAN; *key_layer |= NFP_FLOWER_LAYER_VXLAN; - *key_size += sizeof(struct nfp_flower_ipv4_udp_tun); + + if (ipv6) { + *key_layer |= NFP_FLOWER_LAYER_EXT_META; + *key_size += sizeof(struct nfp_flower_ext_meta); + *key_layer_two |= NFP_FLOWER_LAYER2_TUN_IPV6; + *key_size += sizeof(struct nfp_flower_ipv6_udp_tun); + } else { + *key_size += sizeof(struct nfp_flower_ipv4_udp_tun); + } if (enc_op) { NL_SET_ERR_MSG_MOD(extack, "unsupported offload: encap options not supported on vxlan tunnels"); @@ -192,7 +205,13 @@ nfp_flower_calc_udp_tun_layer(struct flow_dissector_key_ports *enc_ports, *key_layer |= NFP_FLOWER_LAYER_EXT_META; *key_size += sizeof(struct nfp_flower_ext_meta); *key_layer_two |= NFP_FLOWER_LAYER2_GENEVE; - *key_size += sizeof(struct nfp_flower_ipv4_udp_tun); + + if (ipv6) { + *key_layer_two |= NFP_FLOWER_LAYER2_TUN_IPV6; + *key_size += sizeof(struct nfp_flower_ipv6_udp_tun); + } else { + *key_size += sizeof(struct nfp_flower_ipv4_udp_tun); + } if (!enc_op) break; @@ -200,8 +219,8 @@ nfp_flower_calc_udp_tun_layer(struct flow_dissector_key_ports *enc_ports, NL_SET_ERR_MSG_MOD(extack, "unsupported offload: loaded firmware does not support geneve option offload"); return -EOPNOTSUPP; } - err = nfp_flower_calc_opt_layer(enc_op, key_layer_two, - key_size, extack); + err = nfp_flower_calc_opt_layer(enc_op, key_layer_two, key_size, + ipv6, extack); if (err) return err; break; @@ -237,6 +256,8 @@ nfp_flower_calculate_key_layers(struct nfp_app *app, /* If any tun dissector is used then the required set must be used. */ if (dissector->used_keys & NFP_FLOWER_WHITELIST_TUN_DISSECTOR && + (dissector->used_keys & NFP_FLOWER_WHITELIST_TUN_DISSECTOR_V6_R) + != NFP_FLOWER_WHITELIST_TUN_DISSECTOR_V6_R && (dissector->used_keys & NFP_FLOWER_WHITELIST_TUN_DISSECTOR_R) != NFP_FLOWER_WHITELIST_TUN_DISSECTOR_R) { NL_SET_ERR_MSG_MOD(extack, "unsupported offload: tunnel match not supported"); @@ -268,8 +289,10 @@ nfp_flower_calculate_key_layers(struct nfp_app *app, if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ENC_CONTROL)) { struct flow_match_enc_opts enc_op = { NULL, NULL }; struct flow_match_ipv4_addrs ipv4_addrs; + struct flow_match_ipv6_addrs ipv6_addrs; struct flow_match_control enc_ctl; struct flow_match_ports enc_ports; + bool ipv6_tun = false; flow_rule_match_enc_control(rule, &enc_ctl); @@ -277,38 +300,62 @@ nfp_flower_calculate_key_layers(struct nfp_app *app, NL_SET_ERR_MSG_MOD(extack, "unsupported offload: wildcarded protocols on tunnels are not supported"); return -EOPNOTSUPP; } - if (enc_ctl.key->addr_type != FLOW_DISSECTOR_KEY_IPV4_ADDRS) { - NL_SET_ERR_MSG_MOD(extack, "unsupported offload: only IPv4 tunnels are supported"); + + ipv6_tun = enc_ctl.key->addr_type == + FLOW_DISSECTOR_KEY_IPV6_ADDRS; + if (ipv6_tun && + !(priv->flower_ext_feats & NFP_FL_FEATS_IPV6_TUN)) { + NL_SET_ERR_MSG_MOD(extack, "unsupported offload: firmware does not support IPv6 tunnels"); return -EOPNOTSUPP; } - /* These fields are already verified as used. */ - flow_rule_match_enc_ipv4_addrs(rule, &ipv4_addrs); - if (ipv4_addrs.mask->dst != cpu_to_be32(~0)) { - NL_SET_ERR_MSG_MOD(extack, "unsupported offload: only an exact match IPv4 destination address is supported"); + if (!ipv6_tun && + enc_ctl.key->addr_type != FLOW_DISSECTOR_KEY_IPV4_ADDRS) { + NL_SET_ERR_MSG_MOD(extack, "unsupported offload: tunnel address type not IPv4 or IPv6"); return -EOPNOTSUPP; } + if (ipv6_tun) { + flow_rule_match_enc_ipv6_addrs(rule, &ipv6_addrs); + if (memchr_inv(&ipv6_addrs.mask->dst, 0xff, + sizeof(ipv6_addrs.mask->dst))) { + NL_SET_ERR_MSG_MOD(extack, "unsupported offload: only an exact match IPv6 destination address is supported"); + return -EOPNOTSUPP; + } + } else { + flow_rule_match_enc_ipv4_addrs(rule, &ipv4_addrs); + if (ipv4_addrs.mask->dst != cpu_to_be32(~0)) { + NL_SET_ERR_MSG_MOD(extack, "unsupported offload: only an exact match IPv4 destination address is supported"); + return -EOPNOTSUPP; + } + } + if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ENC_OPTS)) flow_rule_match_enc_opts(rule, &enc_op); - if (!flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ENC_PORTS)) { /* check if GRE, which has no enc_ports */ - if (netif_is_gretap(netdev)) { - *tun_type = NFP_FL_TUNNEL_GRE; - key_layer |= NFP_FLOWER_LAYER_EXT_META; - key_size += sizeof(struct nfp_flower_ext_meta); - key_layer_two |= NFP_FLOWER_LAYER2_GRE; - key_size += - sizeof(struct nfp_flower_ipv4_gre_tun); + if (!netif_is_gretap(netdev)) { + NL_SET_ERR_MSG_MOD(extack, "unsupported offload: an exact match on L4 destination port is required for non-GRE tunnels"); + return -EOPNOTSUPP; + } - if (enc_op.key) { - NL_SET_ERR_MSG_MOD(extack, "unsupported offload: encap options not supported on GRE tunnels"); - return -EOPNOTSUPP; - } + *tun_type = NFP_FL_TUNNEL_GRE; + key_layer |= NFP_FLOWER_LAYER_EXT_META; + key_size += sizeof(struct nfp_flower_ext_meta); + key_layer_two |= NFP_FLOWER_LAYER2_GRE; + + if (ipv6_tun) { + key_layer_two |= NFP_FLOWER_LAYER2_TUN_IPV6; + key_size += + sizeof(struct nfp_flower_ipv6_udp_tun); } else { - NL_SET_ERR_MSG_MOD(extack, "unsupported offload: an exact match on L4 destination port is required for non-GRE tunnels"); + key_size += + sizeof(struct nfp_flower_ipv4_udp_tun); + } + + if (enc_op.key) { + NL_SET_ERR_MSG_MOD(extack, "unsupported offload: encap options not supported on GRE tunnels"); return -EOPNOTSUPP; } } else { @@ -323,7 +370,8 @@ nfp_flower_calculate_key_layers(struct nfp_app *app, &key_layer_two, &key_layer, &key_size, priv, - tun_type, extack); + tun_type, ipv6_tun, + extack); if (err) return err; -- GitLab From cfa189934231a2c7922ad9e15561b5b01ea5e82e Mon Sep 17 00:00:00 2001 From: John Hurley Date: Tue, 17 Dec 2019 21:57:19 +0000 Subject: [PATCH 1112/1437] nfp: flower: offload list of IPv6 tunnel endpoint addresses Fw requires a list of IPv6 addresses that are used as tunnel endpoints to enable correct decap of tunneled packets. Store a list of IPv6 endpoints used in rules with a ref counter to track how many times it is in use. Offload the entire list any time a new IPv6 address is added or when an address is removed (ref count is 0). Signed-off-by: John Hurley Reviewed-by: Simon Horman Reviewed-by: Jakub Kicinski Signed-off-by: David S. Miller --- .../net/ethernet/netronome/nfp/flower/cmsg.h | 1 + .../net/ethernet/netronome/nfp/flower/main.h | 21 +++++ .../net/ethernet/netronome/nfp/flower/match.c | 24 +++++ .../ethernet/netronome/nfp/flower/offload.c | 6 ++ .../netronome/nfp/flower/tunnel_conf.c | 90 ++++++++++++++++++- 5 files changed, 141 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/netronome/nfp/flower/cmsg.h b/drivers/net/ethernet/netronome/nfp/flower/cmsg.h index 8d12805baa0d2..cbb94cf1d2823 100644 --- a/drivers/net/ethernet/netronome/nfp/flower/cmsg.h +++ b/drivers/net/ethernet/netronome/nfp/flower/cmsg.h @@ -568,6 +568,7 @@ enum nfp_flower_cmsg_type_port { NFP_FLOWER_CMSG_TYPE_QOS_DEL = 19, NFP_FLOWER_CMSG_TYPE_QOS_STATS = 20, NFP_FLOWER_CMSG_TYPE_PRE_TUN_RULE = 21, + NFP_FLOWER_CMSG_TYPE_TUN_IPS_V6 = 22, NFP_FLOWER_CMSG_TYPE_MAX = 32, }; diff --git a/drivers/net/ethernet/netronome/nfp/flower/main.h b/drivers/net/ethernet/netronome/nfp/flower/main.h index 386d6297e0f1e..bd288e236db78 100644 --- a/drivers/net/ethernet/netronome/nfp/flower/main.h +++ b/drivers/net/ethernet/netronome/nfp/flower/main.h @@ -63,8 +63,10 @@ struct nfp_fl_stats_id { * struct nfp_fl_tunnel_offloads - priv data for tunnel offloads * @offloaded_macs: Hashtable of the offloaded MAC addresses * @ipv4_off_list: List of IPv4 addresses to offload + * @ipv6_off_list: List of IPv6 addresses to offload * @neigh_off_list: List of neighbour offloads * @ipv4_off_lock: Lock for the IPv4 address list + * @ipv6_off_lock: Lock for the IPv6 address list * @neigh_off_lock: Lock for the neighbour address list * @mac_off_ids: IDA to manage id assignment for offloaded MACs * @neigh_nb: Notifier to monitor neighbour state @@ -72,8 +74,10 @@ struct nfp_fl_stats_id { struct nfp_fl_tunnel_offloads { struct rhashtable offloaded_macs; struct list_head ipv4_off_list; + struct list_head ipv6_off_list; struct list_head neigh_off_list; struct mutex ipv4_off_lock; + struct mutex ipv6_off_lock; spinlock_t neigh_off_lock; struct ida mac_off_ids; struct notifier_block neigh_nb; @@ -274,12 +278,25 @@ struct nfp_fl_stats { u64 used; }; +/** + * struct nfp_ipv6_addr_entry - cached IPv6 addresses + * @ipv6_addr: IP address + * @ref_count: number of rules currently using this IP + * @list: list pointer + */ +struct nfp_ipv6_addr_entry { + struct in6_addr ipv6_addr; + int ref_count; + struct list_head list; +}; + struct nfp_fl_payload { struct nfp_fl_rule_metadata meta; unsigned long tc_flower_cookie; struct rhash_head fl_node; struct rcu_head rcu; __be32 nfp_tun_ipv4_addr; + struct nfp_ipv6_addr_entry *nfp_tun_ipv6; struct net_device *ingress_dev; char *unmasked_data; char *mask_data; @@ -397,6 +414,10 @@ int nfp_tunnel_mac_event_handler(struct nfp_app *app, unsigned long event, void *ptr); void nfp_tunnel_del_ipv4_off(struct nfp_app *app, __be32 ipv4); void nfp_tunnel_add_ipv4_off(struct nfp_app *app, __be32 ipv4); +void +nfp_tunnel_put_ipv6_off(struct nfp_app *app, struct nfp_ipv6_addr_entry *entry); +struct nfp_ipv6_addr_entry * +nfp_tunnel_add_ipv6_off(struct nfp_app *app, struct in6_addr *ipv6); void nfp_tunnel_request_route(struct nfp_app *app, struct sk_buff *skb); void nfp_tunnel_keep_alive(struct nfp_app *app, struct sk_buff *skb); void nfp_flower_lag_init(struct nfp_fl_lag *lag); diff --git a/drivers/net/ethernet/netronome/nfp/flower/match.c b/drivers/net/ethernet/netronome/nfp/flower/match.c index 2410ead05a064..546bc01d507d3 100644 --- a/drivers/net/ethernet/netronome/nfp/flower/match.c +++ b/drivers/net/ethernet/netronome/nfp/flower/match.c @@ -494,10 +494,22 @@ int nfp_flower_compile_flow_match(struct nfp_app *app, if (key_ls->key_layer_two & NFP_FLOWER_LAYER2_GRE) { if (key_ls->key_layer_two & NFP_FLOWER_LAYER2_TUN_IPV6) { + struct nfp_flower_ipv6_gre_tun *gre_match; + struct nfp_ipv6_addr_entry *entry; + struct in6_addr *dst; + nfp_flower_compile_ipv6_gre_tun((void *)ext, (void *)msk, rule); + gre_match = (struct nfp_flower_ipv6_gre_tun *)ext; + dst = &gre_match->ipv6.dst; ext += sizeof(struct nfp_flower_ipv6_gre_tun); msk += sizeof(struct nfp_flower_ipv6_gre_tun); + + entry = nfp_tunnel_add_ipv6_off(app, dst); + if (!entry) + return -EOPNOTSUPP; + + nfp_flow->nfp_tun_ipv6 = entry; } else { __be32 dst; @@ -518,10 +530,22 @@ int nfp_flower_compile_flow_match(struct nfp_app *app, if (key_ls->key_layer & NFP_FLOWER_LAYER_VXLAN || key_ls->key_layer_two & NFP_FLOWER_LAYER2_GENEVE) { if (key_ls->key_layer_two & NFP_FLOWER_LAYER2_TUN_IPV6) { + struct nfp_flower_ipv6_udp_tun *udp_match; + struct nfp_ipv6_addr_entry *entry; + struct in6_addr *dst; + nfp_flower_compile_ipv6_udp_tun((void *)ext, (void *)msk, rule); + udp_match = (struct nfp_flower_ipv6_udp_tun *)ext; + dst = &udp_match->ipv6.dst; ext += sizeof(struct nfp_flower_ipv6_udp_tun); msk += sizeof(struct nfp_flower_ipv6_udp_tun); + + entry = nfp_tunnel_add_ipv6_off(app, dst); + if (!entry) + return -EOPNOTSUPP; + + nfp_flow->nfp_tun_ipv6 = entry; } else { __be32 dst; diff --git a/drivers/net/ethernet/netronome/nfp/flower/offload.c b/drivers/net/ethernet/netronome/nfp/flower/offload.c index 099735086fb26..83ada1b6ad888 100644 --- a/drivers/net/ethernet/netronome/nfp/flower/offload.c +++ b/drivers/net/ethernet/netronome/nfp/flower/offload.c @@ -539,6 +539,7 @@ nfp_flower_allocate_new(struct nfp_fl_key_ls *key_layer) goto err_free_mask; flow_pay->nfp_tun_ipv4_addr = 0; + flow_pay->nfp_tun_ipv6 = NULL; flow_pay->meta.flags = 0; INIT_LIST_HEAD(&flow_pay->linked_flows); flow_pay->in_hw = false; @@ -1243,6 +1244,8 @@ err_remove_rhash: err_release_metadata: nfp_modify_flow_metadata(app, flow_pay); err_destroy_flow: + if (flow_pay->nfp_tun_ipv6) + nfp_tunnel_put_ipv6_off(app, flow_pay->nfp_tun_ipv6); kfree(flow_pay->action_data); kfree(flow_pay->mask_data); kfree(flow_pay->unmasked_data); @@ -1359,6 +1362,9 @@ nfp_flower_del_offload(struct nfp_app *app, struct net_device *netdev, if (nfp_flow->nfp_tun_ipv4_addr) nfp_tunnel_del_ipv4_off(app, nfp_flow->nfp_tun_ipv4_addr); + if (nfp_flow->nfp_tun_ipv6) + nfp_tunnel_put_ipv6_off(app, nfp_flow->nfp_tun_ipv6); + if (!nfp_flow->in_hw) { err = 0; goto err_free_merge_flow; diff --git a/drivers/net/ethernet/netronome/nfp/flower/tunnel_conf.c b/drivers/net/ethernet/netronome/nfp/flower/tunnel_conf.c index 2600ce476d6b2..eddb52db6dce2 100644 --- a/drivers/net/ethernet/netronome/nfp/flower/tunnel_conf.c +++ b/drivers/net/ethernet/netronome/nfp/flower/tunnel_conf.c @@ -116,6 +116,18 @@ struct nfp_ipv4_addr_entry { struct list_head list; }; +#define NFP_FL_IPV6_ADDRS_MAX 4 + +/** + * struct nfp_tun_ipv6_addr - set the IP address list on the NFP + * @count: number of IPs populated in the array + * @ipv6_addr: array of IPV6_ADDRS_MAX 128 bit IPv6 addresses + */ +struct nfp_tun_ipv6_addr { + __be32 count; + struct in6_addr ipv6_addr[NFP_FL_IPV6_ADDRS_MAX]; +}; + #define NFP_TUN_MAC_OFFLOAD_DEL_FLAG 0x2 /** @@ -502,6 +514,78 @@ void nfp_tunnel_del_ipv4_off(struct nfp_app *app, __be32 ipv4) nfp_tun_write_ipv4_list(app); } +static void nfp_tun_write_ipv6_list(struct nfp_app *app) +{ + struct nfp_flower_priv *priv = app->priv; + struct nfp_ipv6_addr_entry *entry; + struct nfp_tun_ipv6_addr payload; + int count = 0; + + memset(&payload, 0, sizeof(struct nfp_tun_ipv6_addr)); + mutex_lock(&priv->tun.ipv6_off_lock); + list_for_each_entry(entry, &priv->tun.ipv6_off_list, list) { + if (count >= NFP_FL_IPV6_ADDRS_MAX) { + nfp_flower_cmsg_warn(app, "Too many IPv6 tunnel endpoint addresses, some cannot be offloaded.\n"); + break; + } + payload.ipv6_addr[count++] = entry->ipv6_addr; + } + mutex_unlock(&priv->tun.ipv6_off_lock); + payload.count = cpu_to_be32(count); + + nfp_flower_xmit_tun_conf(app, NFP_FLOWER_CMSG_TYPE_TUN_IPS_V6, + sizeof(struct nfp_tun_ipv6_addr), + &payload, GFP_KERNEL); +} + +struct nfp_ipv6_addr_entry * +nfp_tunnel_add_ipv6_off(struct nfp_app *app, struct in6_addr *ipv6) +{ + struct nfp_flower_priv *priv = app->priv; + struct nfp_ipv6_addr_entry *entry; + + mutex_lock(&priv->tun.ipv6_off_lock); + list_for_each_entry(entry, &priv->tun.ipv6_off_list, list) + if (!memcmp(&entry->ipv6_addr, ipv6, sizeof(*ipv6))) { + entry->ref_count++; + mutex_unlock(&priv->tun.ipv6_off_lock); + return entry; + } + + entry = kmalloc(sizeof(*entry), GFP_KERNEL); + if (!entry) { + mutex_unlock(&priv->tun.ipv6_off_lock); + nfp_flower_cmsg_warn(app, "Mem error when offloading IP address.\n"); + return NULL; + } + entry->ipv6_addr = *ipv6; + entry->ref_count = 1; + list_add_tail(&entry->list, &priv->tun.ipv6_off_list); + mutex_unlock(&priv->tun.ipv6_off_lock); + + nfp_tun_write_ipv6_list(app); + + return entry; +} + +void +nfp_tunnel_put_ipv6_off(struct nfp_app *app, struct nfp_ipv6_addr_entry *entry) +{ + struct nfp_flower_priv *priv = app->priv; + bool freed = false; + + mutex_lock(&priv->tun.ipv6_off_lock); + if (!--entry->ref_count) { + list_del(&entry->list); + kfree(entry); + freed = true; + } + mutex_unlock(&priv->tun.ipv6_off_lock); + + if (freed) + nfp_tun_write_ipv6_list(app); +} + static int __nfp_tunnel_offload_mac(struct nfp_app *app, u8 *mac, u16 idx, bool del) { @@ -1013,9 +1097,11 @@ int nfp_tunnel_config_start(struct nfp_app *app) ida_init(&priv->tun.mac_off_ids); - /* Initialise priv data for IPv4 offloading. */ + /* Initialise priv data for IPv4/v6 offloading. */ mutex_init(&priv->tun.ipv4_off_lock); INIT_LIST_HEAD(&priv->tun.ipv4_off_list); + mutex_init(&priv->tun.ipv6_off_lock); + INIT_LIST_HEAD(&priv->tun.ipv6_off_list); /* Initialise priv data for neighbour offloading. */ spin_lock_init(&priv->tun.neigh_off_lock); @@ -1050,6 +1136,8 @@ void nfp_tunnel_config_stop(struct nfp_app *app) kfree(ip_entry); } + mutex_destroy(&priv->tun.ipv6_off_lock); + /* Free any memory that may be occupied by the route list. */ list_for_each_safe(ptr, storage, &priv->tun.neigh_off_list) { route_entry = list_entry(ptr, struct nfp_ipv4_route_entry, -- GitLab From 1922c9a4e273fd0934f82d9d80c69d53658a1c9d Mon Sep 17 00:00:00 2001 From: John Hurley Date: Tue, 17 Dec 2019 21:57:20 +0000 Subject: [PATCH 1113/1437] nfp: flower: modify pre-tunnel and set tunnel action for ipv6 The IPv4 set tunnel action allows the setting of tunnel metadata such as the TTL and ToS values. The pre-tunnel action includes the destination IP address and is used to calculate the next hop from from the neighbour table. Much of the IPv4 tunnel actions can be reused for IPv6 tunnels. Change the names of associated functions and structs to remove the IPv4 identifier and make minor modifcations to support IPv6 tunnel actions. Ensure the pre-tunnel action contains the IPv6 address along with an identifying flag when an IPv6 tunnel action is required. Signed-off-by: John Hurley Reviewed-by: Simon Horman Reviewed-by: Jakub Kicinski Signed-off-by: David S. Miller --- .../ethernet/netronome/nfp/flower/action.c | 65 ++++++++++++++----- .../net/ethernet/netronome/nfp/flower/cmsg.h | 19 +++--- .../ethernet/netronome/nfp/flower/offload.c | 8 +-- 3 files changed, 62 insertions(+), 30 deletions(-) diff --git a/drivers/net/ethernet/netronome/nfp/flower/action.c b/drivers/net/ethernet/netronome/nfp/flower/action.c index 1b019fdfcd97a..c06600fb47ffa 100644 --- a/drivers/net/ethernet/netronome/nfp/flower/action.c +++ b/drivers/net/ethernet/netronome/nfp/flower/action.c @@ -22,8 +22,9 @@ #define NFP_FL_TUNNEL_CSUM cpu_to_be16(0x01) #define NFP_FL_TUNNEL_KEY cpu_to_be16(0x04) #define NFP_FL_TUNNEL_GENEVE_OPT cpu_to_be16(0x0800) -#define NFP_FL_SUPPORTED_TUNNEL_INFO_FLAGS IP_TUNNEL_INFO_TX -#define NFP_FL_SUPPORTED_IPV4_UDP_TUN_FLAGS (NFP_FL_TUNNEL_CSUM | \ +#define NFP_FL_SUPPORTED_TUNNEL_INFO_FLAGS (IP_TUNNEL_INFO_TX | \ + IP_TUNNEL_INFO_IPV6) +#define NFP_FL_SUPPORTED_UDP_TUN_FLAGS (NFP_FL_TUNNEL_CSUM | \ NFP_FL_TUNNEL_KEY | \ NFP_FL_TUNNEL_GENEVE_OPT) @@ -394,19 +395,26 @@ nfp_fl_push_geneve_options(struct nfp_fl_payload *nfp_fl, int *list_len, } static int -nfp_fl_set_ipv4_tun(struct nfp_app *app, struct nfp_fl_set_ipv4_tun *set_tun, - const struct flow_action_entry *act, - struct nfp_fl_pre_tunnel *pre_tun, - enum nfp_flower_tun_type tun_type, - struct net_device *netdev, struct netlink_ext_ack *extack) +nfp_fl_set_tun(struct nfp_app *app, struct nfp_fl_set_tun *set_tun, + const struct flow_action_entry *act, + struct nfp_fl_pre_tunnel *pre_tun, + enum nfp_flower_tun_type tun_type, + struct net_device *netdev, struct netlink_ext_ack *extack) { - size_t act_size = sizeof(struct nfp_fl_set_ipv4_tun); const struct ip_tunnel_info *ip_tun = act->tunnel; + bool ipv6 = ip_tunnel_info_af(ip_tun) == AF_INET6; + size_t act_size = sizeof(struct nfp_fl_set_tun); struct nfp_flower_priv *priv = app->priv; u32 tmp_set_ip_tun_type_index = 0; /* Currently support one pre-tunnel so index is always 0. */ int pretun_idx = 0; + if (!IS_ENABLED(CONFIG_IPV6) && ipv6) + return -EOPNOTSUPP; + + if (ipv6 && !(priv->flower_ext_feats & NFP_FL_FEATS_IPV6_TUN)) + return -EOPNOTSUPP; + BUILD_BUG_ON(NFP_FL_TUNNEL_CSUM != TUNNEL_CSUM || NFP_FL_TUNNEL_KEY != TUNNEL_KEY || NFP_FL_TUNNEL_GENEVE_OPT != TUNNEL_GENEVE_OPT); @@ -417,19 +425,35 @@ nfp_fl_set_ipv4_tun(struct nfp_app *app, struct nfp_fl_set_ipv4_tun *set_tun, return -EOPNOTSUPP; } - set_tun->head.jump_id = NFP_FL_ACTION_OPCODE_SET_IPV4_TUNNEL; + set_tun->head.jump_id = NFP_FL_ACTION_OPCODE_SET_TUNNEL; set_tun->head.len_lw = act_size >> NFP_FL_LW_SIZ; /* Set tunnel type and pre-tunnel index. */ tmp_set_ip_tun_type_index |= - FIELD_PREP(NFP_FL_IPV4_TUNNEL_TYPE, tun_type) | - FIELD_PREP(NFP_FL_IPV4_PRE_TUN_INDEX, pretun_idx); + FIELD_PREP(NFP_FL_TUNNEL_TYPE, tun_type) | + FIELD_PREP(NFP_FL_PRE_TUN_INDEX, pretun_idx); set_tun->tun_type_index = cpu_to_be32(tmp_set_ip_tun_type_index); set_tun->tun_id = ip_tun->key.tun_id; if (ip_tun->key.ttl) { set_tun->ttl = ip_tun->key.ttl; +#ifdef CONFIG_IPV6 + } else if (ipv6) { + struct net *net = dev_net(netdev); + struct flowi6 flow = {}; + struct dst_entry *dst; + + flow.daddr = ip_tun->key.u.ipv6.dst; + flow.flowi4_proto = IPPROTO_UDP; + dst = ipv6_stub->ipv6_dst_lookup_flow(net, NULL, &flow, NULL); + if (!IS_ERR(dst)) { + set_tun->ttl = ip6_dst_hoplimit(dst); + dst_release(dst); + } else { + set_tun->ttl = net->ipv6.devconf_all->hop_limit; + } +#endif } else { struct net *net = dev_net(netdev); struct flowi4 flow = {}; @@ -455,7 +479,7 @@ nfp_fl_set_ipv4_tun(struct nfp_app *app, struct nfp_fl_set_ipv4_tun *set_tun, set_tun->tos = ip_tun->key.tos; if (!(ip_tun->key.tun_flags & NFP_FL_TUNNEL_KEY) || - ip_tun->key.tun_flags & ~NFP_FL_SUPPORTED_IPV4_UDP_TUN_FLAGS) { + ip_tun->key.tun_flags & ~NFP_FL_SUPPORTED_UDP_TUN_FLAGS) { NL_SET_ERR_MSG_MOD(extack, "unsupported offload: loaded firmware does not support tunnel flag offload"); return -EOPNOTSUPP; } @@ -467,7 +491,12 @@ nfp_fl_set_ipv4_tun(struct nfp_app *app, struct nfp_fl_set_ipv4_tun *set_tun, } /* Complete pre_tunnel action. */ - pre_tun->ipv4_dst = ip_tun->key.u.ipv4.dst; + if (ipv6) { + pre_tun->flags |= cpu_to_be16(NFP_FL_PRE_TUN_IPV6); + pre_tun->ipv6_dst = ip_tun->key.u.ipv6.dst; + } else { + pre_tun->ipv4_dst = ip_tun->key.u.ipv4.dst; + } return 0; } @@ -956,8 +985,8 @@ nfp_flower_loop_action(struct nfp_app *app, const struct flow_action_entry *act, struct nfp_flower_pedit_acts *set_act, bool *pkt_host, struct netlink_ext_ack *extack, int act_idx) { - struct nfp_fl_set_ipv4_tun *set_tun; struct nfp_fl_pre_tunnel *pre_tun; + struct nfp_fl_set_tun *set_tun; struct nfp_fl_push_vlan *psh_v; struct nfp_fl_push_mpls *psh_m; struct nfp_fl_pop_vlan *pop_v; @@ -1032,7 +1061,7 @@ nfp_flower_loop_action(struct nfp_app *app, const struct flow_action_entry *act, * If none, the packet falls back before applying other actions. */ if (*a_len + sizeof(struct nfp_fl_pre_tunnel) + - sizeof(struct nfp_fl_set_ipv4_tun) > NFP_FL_MAX_A_SIZ) { + sizeof(struct nfp_fl_set_tun) > NFP_FL_MAX_A_SIZ) { NL_SET_ERR_MSG_MOD(extack, "unsupported offload: maximum allowed action list size exceeded at tunnel encap"); return -EOPNOTSUPP; } @@ -1046,11 +1075,11 @@ nfp_flower_loop_action(struct nfp_app *app, const struct flow_action_entry *act, return err; set_tun = (void *)&nfp_fl->action_data[*a_len]; - err = nfp_fl_set_ipv4_tun(app, set_tun, act, pre_tun, - *tun_type, netdev, extack); + err = nfp_fl_set_tun(app, set_tun, act, pre_tun, *tun_type, + netdev, extack); if (err) return err; - *a_len += sizeof(struct nfp_fl_set_ipv4_tun); + *a_len += sizeof(struct nfp_fl_set_tun); } break; case FLOW_ACTION_TUNNEL_DECAP: diff --git a/drivers/net/ethernet/netronome/nfp/flower/cmsg.h b/drivers/net/ethernet/netronome/nfp/flower/cmsg.h index cbb94cf1d2823..7537695c23341 100644 --- a/drivers/net/ethernet/netronome/nfp/flower/cmsg.h +++ b/drivers/net/ethernet/netronome/nfp/flower/cmsg.h @@ -72,7 +72,7 @@ #define NFP_FL_ACTION_OPCODE_POP_VLAN 2 #define NFP_FL_ACTION_OPCODE_PUSH_MPLS 3 #define NFP_FL_ACTION_OPCODE_POP_MPLS 4 -#define NFP_FL_ACTION_OPCODE_SET_IPV4_TUNNEL 6 +#define NFP_FL_ACTION_OPCODE_SET_TUNNEL 6 #define NFP_FL_ACTION_OPCODE_SET_ETHERNET 7 #define NFP_FL_ACTION_OPCODE_SET_MPLS 8 #define NFP_FL_ACTION_OPCODE_SET_IPV4_ADDRS 9 @@ -101,8 +101,8 @@ /* Tunnel ports */ #define NFP_FL_PORT_TYPE_TUN 0x50000000 -#define NFP_FL_IPV4_TUNNEL_TYPE GENMASK(7, 4) -#define NFP_FL_IPV4_PRE_TUN_INDEX GENMASK(2, 0) +#define NFP_FL_TUNNEL_TYPE GENMASK(7, 4) +#define NFP_FL_PRE_TUN_INDEX GENMASK(2, 0) #define NFP_FLOWER_WORKQ_MAX_SKBS 30000 @@ -208,13 +208,16 @@ struct nfp_fl_pre_lag { struct nfp_fl_pre_tunnel { struct nfp_fl_act_head head; - __be16 reserved; - __be32 ipv4_dst; - /* reserved for use with IPv6 addresses */ - __be32 extra[3]; + __be16 flags; + union { + __be32 ipv4_dst; + struct in6_addr ipv6_dst; + }; }; -struct nfp_fl_set_ipv4_tun { +#define NFP_FL_PRE_TUN_IPV6 BIT(0) + +struct nfp_fl_set_tun { struct nfp_fl_act_head head; __be16 reserved; __be64 tun_id __packed; diff --git a/drivers/net/ethernet/netronome/nfp/flower/offload.c b/drivers/net/ethernet/netronome/nfp/flower/offload.c index 83ada1b6ad888..d512105492999 100644 --- a/drivers/net/ethernet/netronome/nfp/flower/offload.c +++ b/drivers/net/ethernet/netronome/nfp/flower/offload.c @@ -591,7 +591,7 @@ nfp_flower_update_merge_with_actions(struct nfp_fl_payload *flow, case NFP_FL_ACTION_OPCODE_POP_VLAN: merge->tci = cpu_to_be16(0); break; - case NFP_FL_ACTION_OPCODE_SET_IPV4_TUNNEL: + case NFP_FL_ACTION_OPCODE_SET_TUNNEL: /* New tunnel header means l2 to l4 can be matched. */ eth_broadcast_addr(&merge->l2.mac_dst[0]); eth_broadcast_addr(&merge->l2.mac_src[0]); @@ -814,15 +814,15 @@ nfp_fl_verify_post_tun_acts(char *acts, int len, struct nfp_fl_push_vlan **vlan) static int nfp_fl_push_vlan_after_tun(char *acts, int len, struct nfp_fl_push_vlan *vlan) { - struct nfp_fl_set_ipv4_tun *tun; + struct nfp_fl_set_tun *tun; struct nfp_fl_act_head *a; unsigned int act_off = 0; while (act_off < len) { a = (struct nfp_fl_act_head *)&acts[act_off]; - if (a->jump_id == NFP_FL_ACTION_OPCODE_SET_IPV4_TUNNEL) { - tun = (struct nfp_fl_set_ipv4_tun *)a; + if (a->jump_id == NFP_FL_ACTION_OPCODE_SET_TUNNEL) { + tun = (struct nfp_fl_set_tun *)a; tun->outer_vlan_tpid = vlan->vlan_tpid; tun->outer_vlan_tci = vlan->vlan_tci; -- GitLab From 50b1c86ab0a07509fd65c92acacf281fb72a285b Mon Sep 17 00:00:00 2001 From: John Hurley Date: Tue, 17 Dec 2019 21:57:21 +0000 Subject: [PATCH 1114/1437] nfp: flower: handle ipv6 tunnel no neigh request When fw does not know the next hop for an IPv6 tunnel, it sends a request to the driver. Handle this request by doing a route lookup on the IPv6 address and offloading the next hop to the fw neighbour table. Similar functions already exist to handle IPv4 no neighbour requests. To avoid confusion, append these functions with the _ipv4 tag. There is no change in functionality with this. Signed-off-by: John Hurley Reviewed-by: Simon Horman Reviewed-by: Jakub Kicinski Signed-off-by: David S. Miller --- .../net/ethernet/netronome/nfp/flower/cmsg.c | 8 +- .../net/ethernet/netronome/nfp/flower/cmsg.h | 2 + .../net/ethernet/netronome/nfp/flower/main.h | 3 +- .../netronome/nfp/flower/tunnel_conf.c | 111 +++++++++++++++++- 4 files changed, 116 insertions(+), 8 deletions(-) diff --git a/drivers/net/ethernet/netronome/nfp/flower/cmsg.c b/drivers/net/ethernet/netronome/nfp/flower/cmsg.c index 05981b54eaab7..00b904ea4ebc5 100644 --- a/drivers/net/ethernet/netronome/nfp/flower/cmsg.c +++ b/drivers/net/ethernet/netronome/nfp/flower/cmsg.c @@ -270,7 +270,10 @@ nfp_flower_cmsg_process_one_rx(struct nfp_app *app, struct sk_buff *skb) } goto err_default; case NFP_FLOWER_CMSG_TYPE_NO_NEIGH: - nfp_tunnel_request_route(app, skb); + nfp_tunnel_request_route_v4(app, skb); + break; + case NFP_FLOWER_CMSG_TYPE_NO_NEIGH_V6: + nfp_tunnel_request_route_v6(app, skb); break; case NFP_FLOWER_CMSG_TYPE_ACTIVE_TUNS: nfp_tunnel_keep_alive(app, skb); @@ -361,7 +364,8 @@ void nfp_flower_cmsg_rx(struct nfp_app *app, struct sk_buff *skb) nfp_flower_process_mtu_ack(app, skb)) { /* Handle MTU acks outside wq to prevent RTNL conflict. */ dev_consume_skb_any(skb); - } else if (cmsg_hdr->type == NFP_FLOWER_CMSG_TYPE_TUN_NEIGH) { + } else if (cmsg_hdr->type == NFP_FLOWER_CMSG_TYPE_TUN_NEIGH || + cmsg_hdr->type == NFP_FLOWER_CMSG_TYPE_TUN_NEIGH_V6) { /* Acks from the NFP that the route is added - ignore. */ dev_consume_skb_any(skb); } else if (cmsg_hdr->type == NFP_FLOWER_CMSG_TYPE_PORT_REIFY) { diff --git a/drivers/net/ethernet/netronome/nfp/flower/cmsg.h b/drivers/net/ethernet/netronome/nfp/flower/cmsg.h index 7537695c23341..5a8ad5814060f 100644 --- a/drivers/net/ethernet/netronome/nfp/flower/cmsg.h +++ b/drivers/net/ethernet/netronome/nfp/flower/cmsg.h @@ -572,6 +572,8 @@ enum nfp_flower_cmsg_type_port { NFP_FLOWER_CMSG_TYPE_QOS_STATS = 20, NFP_FLOWER_CMSG_TYPE_PRE_TUN_RULE = 21, NFP_FLOWER_CMSG_TYPE_TUN_IPS_V6 = 22, + NFP_FLOWER_CMSG_TYPE_NO_NEIGH_V6 = 23, + NFP_FLOWER_CMSG_TYPE_TUN_NEIGH_V6 = 24, NFP_FLOWER_CMSG_TYPE_MAX = 32, }; diff --git a/drivers/net/ethernet/netronome/nfp/flower/main.h b/drivers/net/ethernet/netronome/nfp/flower/main.h index bd288e236db78..01e1886e9d85b 100644 --- a/drivers/net/ethernet/netronome/nfp/flower/main.h +++ b/drivers/net/ethernet/netronome/nfp/flower/main.h @@ -418,7 +418,8 @@ void nfp_tunnel_put_ipv6_off(struct nfp_app *app, struct nfp_ipv6_addr_entry *entry); struct nfp_ipv6_addr_entry * nfp_tunnel_add_ipv6_off(struct nfp_app *app, struct in6_addr *ipv6); -void nfp_tunnel_request_route(struct nfp_app *app, struct sk_buff *skb); +void nfp_tunnel_request_route_v4(struct nfp_app *app, struct sk_buff *skb); +void nfp_tunnel_request_route_v6(struct nfp_app *app, struct sk_buff *skb); void nfp_tunnel_keep_alive(struct nfp_app *app, struct sk_buff *skb); void nfp_flower_lag_init(struct nfp_fl_lag *lag); void nfp_flower_lag_cleanup(struct nfp_fl_lag *lag); diff --git a/drivers/net/ethernet/netronome/nfp/flower/tunnel_conf.c b/drivers/net/ethernet/netronome/nfp/flower/tunnel_conf.c index eddb52db6dce2..09807b31b830f 100644 --- a/drivers/net/ethernet/netronome/nfp/flower/tunnel_conf.c +++ b/drivers/net/ethernet/netronome/nfp/flower/tunnel_conf.c @@ -70,6 +70,22 @@ struct nfp_tun_neigh { __be32 port_id; }; +/** + * struct nfp_tun_neigh_v6 - neighbour/route entry on the NFP + * @dst_ipv6: destination IPv6 address + * @src_ipv6: source IPv6 address + * @dst_addr: destination MAC address + * @src_addr: source MAC address + * @port_id: NFP port to output packet on - associated with source IPv6 + */ +struct nfp_tun_neigh_v6 { + struct in6_addr dst_ipv6; + struct in6_addr src_ipv6; + u8 dst_addr[ETH_ALEN]; + u8 src_addr[ETH_ALEN]; + __be32 port_id; +}; + /** * struct nfp_tun_req_route_ipv4 - NFP requests a route/neighbour lookup * @ingress_port: ingress port of packet that signalled request @@ -82,6 +98,16 @@ struct nfp_tun_req_route_ipv4 { __be32 reserved[2]; }; +/** + * struct nfp_tun_req_route_ipv6 - NFP requests an IPv6 route/neighbour lookup + * @ingress_port: ingress port of packet that signalled request + * @ipv6_addr: destination ipv6 address for route + */ +struct nfp_tun_req_route_ipv6 { + __be32 ingress_port; + struct in6_addr ipv6_addr; +}; + /** * struct nfp_ipv4_route_entry - routes that are offloaded to the NFP * @ipv4_addr: destination of route @@ -299,8 +325,8 @@ static void nfp_tun_del_route_from_cache(struct nfp_app *app, __be32 ipv4_addr) } static void -nfp_tun_write_neigh(struct net_device *netdev, struct nfp_app *app, - struct flowi4 *flow, struct neighbour *neigh, gfp_t flag) +nfp_tun_write_neigh_v4(struct net_device *netdev, struct nfp_app *app, + struct flowi4 *flow, struct neighbour *neigh, gfp_t flag) { struct nfp_tun_neigh payload; u32 port_id; @@ -334,6 +360,39 @@ send_msg: (unsigned char *)&payload, flag); } +static void +nfp_tun_write_neigh_v6(struct net_device *netdev, struct nfp_app *app, + struct flowi6 *flow, struct neighbour *neigh, gfp_t flag) +{ + struct nfp_tun_neigh_v6 payload; + u32 port_id; + + port_id = nfp_flower_get_port_id_from_netdev(app, netdev); + if (!port_id) + return; + + memset(&payload, 0, sizeof(struct nfp_tun_neigh_v6)); + payload.dst_ipv6 = flow->daddr; + + /* If entry has expired send dst IP with all other fields 0. */ + if (!(neigh->nud_state & NUD_VALID) || neigh->dead) { + /* Trigger probe to verify invalid neighbour state. */ + neigh_event_send(neigh, NULL); + goto send_msg; + } + + /* Have a valid neighbour so populate rest of entry. */ + payload.src_ipv6 = flow->saddr; + ether_addr_copy(payload.src_addr, netdev->dev_addr); + neigh_ha_snapshot(payload.dst_addr, neigh, netdev); + payload.port_id = cpu_to_be32(port_id); + +send_msg: + nfp_flower_xmit_tun_conf(app, NFP_FLOWER_CMSG_TYPE_TUN_NEIGH_V6, + sizeof(struct nfp_tun_neigh_v6), + (unsigned char *)&payload, flag); +} + static int nfp_tun_neigh_event_handler(struct notifier_block *nb, unsigned long event, void *ptr) @@ -384,12 +443,12 @@ nfp_tun_neigh_event_handler(struct notifier_block *nb, unsigned long event, #endif flow.flowi4_proto = IPPROTO_UDP; - nfp_tun_write_neigh(n->dev, app, &flow, n, GFP_ATOMIC); + nfp_tun_write_neigh_v4(n->dev, app, &flow, n, GFP_ATOMIC); return NOTIFY_OK; } -void nfp_tunnel_request_route(struct nfp_app *app, struct sk_buff *skb) +void nfp_tunnel_request_route_v4(struct nfp_app *app, struct sk_buff *skb) { struct nfp_tun_req_route_ipv4 *payload; struct net_device *netdev; @@ -423,7 +482,7 @@ void nfp_tunnel_request_route(struct nfp_app *app, struct sk_buff *skb) ip_rt_put(rt); if (!n) goto fail_rcu_unlock; - nfp_tun_write_neigh(n->dev, app, &flow, n, GFP_ATOMIC); + nfp_tun_write_neigh_v4(n->dev, app, &flow, n, GFP_ATOMIC); neigh_release(n); rcu_read_unlock(); return; @@ -433,6 +492,48 @@ fail_rcu_unlock: nfp_flower_cmsg_warn(app, "Requested route not found.\n"); } +void nfp_tunnel_request_route_v6(struct nfp_app *app, struct sk_buff *skb) +{ + struct nfp_tun_req_route_ipv6 *payload; + struct net_device *netdev; + struct flowi6 flow = {}; + struct dst_entry *dst; + struct neighbour *n; + + payload = nfp_flower_cmsg_get_data(skb); + + rcu_read_lock(); + netdev = nfp_app_dev_get(app, be32_to_cpu(payload->ingress_port), NULL); + if (!netdev) + goto fail_rcu_unlock; + + flow.daddr = payload->ipv6_addr; + flow.flowi6_proto = IPPROTO_UDP; + +#if IS_ENABLED(CONFIG_INET) && IS_ENABLED(CONFIG_IPV6) + dst = ipv6_stub->ipv6_dst_lookup_flow(dev_net(netdev), NULL, &flow, + NULL); + if (IS_ERR(dst)) + goto fail_rcu_unlock; +#else + goto fail_rcu_unlock; +#endif + + n = dst_neigh_lookup(dst, &flow.daddr); + dst_release(dst); + if (!n) + goto fail_rcu_unlock; + + nfp_tun_write_neigh_v6(n->dev, app, &flow, n, GFP_ATOMIC); + neigh_release(n); + rcu_read_unlock(); + return; + +fail_rcu_unlock: + rcu_read_unlock(); + nfp_flower_cmsg_warn(app, "Requested IPv6 route not found.\n"); +} + static void nfp_tun_write_ipv4_list(struct nfp_app *app) { struct nfp_flower_priv *priv = app->priv; -- GitLab From 6c463a059f626bc2115b17bee97f014f59dd844a Mon Sep 17 00:00:00 2001 From: John Hurley Date: Tue, 17 Dec 2019 21:57:22 +0000 Subject: [PATCH 1115/1437] nfp: flower: handle notifiers for ipv6 route changes A notifier is used to track route changes in the kernel. If a change is made to a route that is offloaded to fw then an update is sent to the NIC. The driver tracks all routes that are offloaded to determine if a kernel change is of interest. Extend the notifier to track IPv6 route changes and create a new list that stores offloaded IPv6 routes. Modify the IPv4 route helper functions to accept varying address lengths. This way, the same core functions can be used to handle IPv4 and IPv6. Signed-off-by: John Hurley Reviewed-by: Simon Horman Reviewed-by: Jakub Kicinski Signed-off-by: David S. Miller --- .../net/ethernet/netronome/nfp/flower/main.h | 12 +- .../netronome/nfp/flower/tunnel_conf.c | 237 +++++++++++++----- 2 files changed, 181 insertions(+), 68 deletions(-) diff --git a/drivers/net/ethernet/netronome/nfp/flower/main.h b/drivers/net/ethernet/netronome/nfp/flower/main.h index 01e1886e9d85b..7a46032610269 100644 --- a/drivers/net/ethernet/netronome/nfp/flower/main.h +++ b/drivers/net/ethernet/netronome/nfp/flower/main.h @@ -64,10 +64,12 @@ struct nfp_fl_stats_id { * @offloaded_macs: Hashtable of the offloaded MAC addresses * @ipv4_off_list: List of IPv4 addresses to offload * @ipv6_off_list: List of IPv6 addresses to offload - * @neigh_off_list: List of neighbour offloads + * @neigh_off_list_v4: List of IPv4 neighbour offloads + * @neigh_off_list_v6: List of IPv6 neighbour offloads * @ipv4_off_lock: Lock for the IPv4 address list * @ipv6_off_lock: Lock for the IPv6 address list - * @neigh_off_lock: Lock for the neighbour address list + * @neigh_off_lock_v4: Lock for the IPv4 neighbour address list + * @neigh_off_lock_v6: Lock for the IPv6 neighbour address list * @mac_off_ids: IDA to manage id assignment for offloaded MACs * @neigh_nb: Notifier to monitor neighbour state */ @@ -75,10 +77,12 @@ struct nfp_fl_tunnel_offloads { struct rhashtable offloaded_macs; struct list_head ipv4_off_list; struct list_head ipv6_off_list; - struct list_head neigh_off_list; + struct list_head neigh_off_list_v4; + struct list_head neigh_off_list_v6; struct mutex ipv4_off_lock; struct mutex ipv6_off_lock; - spinlock_t neigh_off_lock; + spinlock_t neigh_off_lock_v4; + spinlock_t neigh_off_lock_v6; struct ida mac_off_ids; struct notifier_block neigh_nb; }; diff --git a/drivers/net/ethernet/netronome/nfp/flower/tunnel_conf.c b/drivers/net/ethernet/netronome/nfp/flower/tunnel_conf.c index 09807b31b830f..b179b63c84563 100644 --- a/drivers/net/ethernet/netronome/nfp/flower/tunnel_conf.c +++ b/drivers/net/ethernet/netronome/nfp/flower/tunnel_conf.c @@ -109,13 +109,13 @@ struct nfp_tun_req_route_ipv6 { }; /** - * struct nfp_ipv4_route_entry - routes that are offloaded to the NFP - * @ipv4_addr: destination of route + * struct nfp_offloaded_route - routes that are offloaded to the NFP * @list: list pointer + * @ip_add: destination of route - can be IPv4 or IPv6 */ -struct nfp_ipv4_route_entry { - __be32 ipv4_addr; +struct nfp_offloaded_route { struct list_head list; + u8 ip_add[]; }; #define NFP_FL_IPV4_ADDRS_MAX 32 @@ -262,66 +262,121 @@ nfp_flower_xmit_tun_conf(struct nfp_app *app, u8 mtype, u16 plen, void *pdata, return 0; } -static bool nfp_tun_has_route(struct nfp_app *app, __be32 ipv4_addr) +static bool +__nfp_tun_has_route(struct list_head *route_list, spinlock_t *list_lock, + void *add, int add_len) { - struct nfp_flower_priv *priv = app->priv; - struct nfp_ipv4_route_entry *entry; - struct list_head *ptr, *storage; + struct nfp_offloaded_route *entry; - spin_lock_bh(&priv->tun.neigh_off_lock); - list_for_each_safe(ptr, storage, &priv->tun.neigh_off_list) { - entry = list_entry(ptr, struct nfp_ipv4_route_entry, list); - if (entry->ipv4_addr == ipv4_addr) { - spin_unlock_bh(&priv->tun.neigh_off_lock); + spin_lock_bh(list_lock); + list_for_each_entry(entry, route_list, list) + if (!memcmp(entry->ip_add, add, add_len)) { + spin_unlock_bh(list_lock); return true; } - } - spin_unlock_bh(&priv->tun.neigh_off_lock); + spin_unlock_bh(list_lock); return false; } -static void nfp_tun_add_route_to_cache(struct nfp_app *app, __be32 ipv4_addr) +static int +__nfp_tun_add_route_to_cache(struct list_head *route_list, + spinlock_t *list_lock, void *add, int add_len) { - struct nfp_flower_priv *priv = app->priv; - struct nfp_ipv4_route_entry *entry; - struct list_head *ptr, *storage; + struct nfp_offloaded_route *entry; - spin_lock_bh(&priv->tun.neigh_off_lock); - list_for_each_safe(ptr, storage, &priv->tun.neigh_off_list) { - entry = list_entry(ptr, struct nfp_ipv4_route_entry, list); - if (entry->ipv4_addr == ipv4_addr) { - spin_unlock_bh(&priv->tun.neigh_off_lock); - return; + spin_lock_bh(list_lock); + list_for_each_entry(entry, route_list, list) + if (!memcmp(entry->ip_add, add, add_len)) { + spin_unlock_bh(list_lock); + return 0; } - } - entry = kmalloc(sizeof(*entry), GFP_ATOMIC); + + entry = kmalloc(sizeof(*entry) + add_len, GFP_ATOMIC); if (!entry) { - spin_unlock_bh(&priv->tun.neigh_off_lock); - nfp_flower_cmsg_warn(app, "Mem error when storing new route.\n"); - return; + spin_unlock_bh(list_lock); + return -ENOMEM; } - entry->ipv4_addr = ipv4_addr; - list_add_tail(&entry->list, &priv->tun.neigh_off_list); - spin_unlock_bh(&priv->tun.neigh_off_lock); + memcpy(entry->ip_add, add, add_len); + list_add_tail(&entry->list, route_list); + spin_unlock_bh(list_lock); + + return 0; } -static void nfp_tun_del_route_from_cache(struct nfp_app *app, __be32 ipv4_addr) +static void +__nfp_tun_del_route_from_cache(struct list_head *route_list, + spinlock_t *list_lock, void *add, int add_len) { - struct nfp_flower_priv *priv = app->priv; - struct nfp_ipv4_route_entry *entry; - struct list_head *ptr, *storage; + struct nfp_offloaded_route *entry; - spin_lock_bh(&priv->tun.neigh_off_lock); - list_for_each_safe(ptr, storage, &priv->tun.neigh_off_list) { - entry = list_entry(ptr, struct nfp_ipv4_route_entry, list); - if (entry->ipv4_addr == ipv4_addr) { + spin_lock_bh(list_lock); + list_for_each_entry(entry, route_list, list) + if (!memcmp(entry->ip_add, add, add_len)) { list_del(&entry->list); kfree(entry); break; } - } - spin_unlock_bh(&priv->tun.neigh_off_lock); + spin_unlock_bh(list_lock); +} + +static bool nfp_tun_has_route_v4(struct nfp_app *app, __be32 *ipv4_addr) +{ + struct nfp_flower_priv *priv = app->priv; + + return __nfp_tun_has_route(&priv->tun.neigh_off_list_v4, + &priv->tun.neigh_off_lock_v4, ipv4_addr, + sizeof(*ipv4_addr)); +} + +static bool +nfp_tun_has_route_v6(struct nfp_app *app, struct in6_addr *ipv6_addr) +{ + struct nfp_flower_priv *priv = app->priv; + + return __nfp_tun_has_route(&priv->tun.neigh_off_list_v6, + &priv->tun.neigh_off_lock_v6, ipv6_addr, + sizeof(*ipv6_addr)); +} + +static void +nfp_tun_add_route_to_cache_v4(struct nfp_app *app, __be32 *ipv4_addr) +{ + struct nfp_flower_priv *priv = app->priv; + + __nfp_tun_add_route_to_cache(&priv->tun.neigh_off_list_v4, + &priv->tun.neigh_off_lock_v4, ipv4_addr, + sizeof(*ipv4_addr)); +} + +static void +nfp_tun_add_route_to_cache_v6(struct nfp_app *app, struct in6_addr *ipv6_addr) +{ + struct nfp_flower_priv *priv = app->priv; + + __nfp_tun_add_route_to_cache(&priv->tun.neigh_off_list_v6, + &priv->tun.neigh_off_lock_v6, ipv6_addr, + sizeof(*ipv6_addr)); +} + +static void +nfp_tun_del_route_from_cache_v4(struct nfp_app *app, __be32 *ipv4_addr) +{ + struct nfp_flower_priv *priv = app->priv; + + __nfp_tun_del_route_from_cache(&priv->tun.neigh_off_list_v4, + &priv->tun.neigh_off_lock_v4, ipv4_addr, + sizeof(*ipv4_addr)); +} + +static void +nfp_tun_del_route_from_cache_v6(struct nfp_app *app, struct in6_addr *ipv6_addr) +{ + struct nfp_flower_priv *priv = app->priv; + + __nfp_tun_del_route_from_cache(&priv->tun.neigh_off_list_v6, + &priv->tun.neigh_off_lock_v6, ipv6_addr, + sizeof(*ipv6_addr)); } static void @@ -340,7 +395,7 @@ nfp_tun_write_neigh_v4(struct net_device *netdev, struct nfp_app *app, /* If entry has expired send dst IP with all other fields 0. */ if (!(neigh->nud_state & NUD_VALID) || neigh->dead) { - nfp_tun_del_route_from_cache(app, payload.dst_ipv4); + nfp_tun_del_route_from_cache_v4(app, &payload.dst_ipv4); /* Trigger ARP to verify invalid neighbour state. */ neigh_event_send(neigh, NULL); goto send_msg; @@ -352,7 +407,7 @@ nfp_tun_write_neigh_v4(struct net_device *netdev, struct nfp_app *app, neigh_ha_snapshot(payload.dst_addr, neigh, netdev); payload.port_id = cpu_to_be32(port_id); /* Add destination of new route to NFP cache. */ - nfp_tun_add_route_to_cache(app, payload.dst_ipv4); + nfp_tun_add_route_to_cache_v4(app, &payload.dst_ipv4); send_msg: nfp_flower_xmit_tun_conf(app, NFP_FLOWER_CMSG_TYPE_TUN_NEIGH, @@ -376,6 +431,7 @@ nfp_tun_write_neigh_v6(struct net_device *netdev, struct nfp_app *app, /* If entry has expired send dst IP with all other fields 0. */ if (!(neigh->nud_state & NUD_VALID) || neigh->dead) { + nfp_tun_del_route_from_cache_v6(app, &payload.dst_ipv6); /* Trigger probe to verify invalid neighbour state. */ neigh_event_send(neigh, NULL); goto send_msg; @@ -386,6 +442,8 @@ nfp_tun_write_neigh_v6(struct net_device *netdev, struct nfp_app *app, ether_addr_copy(payload.src_addr, netdev->dev_addr); neigh_ha_snapshot(payload.dst_addr, neigh, netdev); payload.port_id = cpu_to_be32(port_id); + /* Add destination of new route to NFP cache. */ + nfp_tun_add_route_to_cache_v6(app, &payload.dst_ipv6); send_msg: nfp_flower_xmit_tun_conf(app, NFP_FLOWER_CMSG_TYPE_TUN_NEIGH_V6, @@ -399,10 +457,12 @@ nfp_tun_neigh_event_handler(struct notifier_block *nb, unsigned long event, { struct nfp_flower_priv *app_priv; struct netevent_redirect *redir; - struct flowi4 flow = {}; + struct flowi4 flow4 = {}; + struct flowi6 flow6 = {}; struct neighbour *n; struct nfp_app *app; struct rtable *rt; + bool ipv6 = false; int err; switch (event) { @@ -417,7 +477,13 @@ nfp_tun_neigh_event_handler(struct notifier_block *nb, unsigned long event, return NOTIFY_DONE; } - flow.daddr = *(__be32 *)n->primary_key; + if (n->tbl->family == AF_INET6) + ipv6 = true; + + if (ipv6) + flow6.daddr = *(struct in6_addr *)n->primary_key; + else + flow4.daddr = *(__be32 *)n->primary_key; app_priv = container_of(nb, struct nfp_flower_priv, tun.neigh_nb); app = app_priv->app; @@ -427,23 +493,41 @@ nfp_tun_neigh_event_handler(struct notifier_block *nb, unsigned long event, return NOTIFY_DONE; /* Only concerned with changes to routes already added to NFP. */ - if (!nfp_tun_has_route(app, flow.daddr)) + if ((ipv6 && !nfp_tun_has_route_v6(app, &flow6.daddr)) || + (!ipv6 && !nfp_tun_has_route_v4(app, &flow4.daddr))) return NOTIFY_DONE; #if IS_ENABLED(CONFIG_INET) - /* Do a route lookup to populate flow data. */ - rt = ip_route_output_key(dev_net(n->dev), &flow); - err = PTR_ERR_OR_ZERO(rt); - if (err) + if (ipv6) { +#if IS_ENABLED(CONFIG_IPV6) + struct dst_entry *dst; + + dst = ipv6_stub->ipv6_dst_lookup_flow(dev_net(n->dev), NULL, + &flow6, NULL); + if (IS_ERR(dst)) + return NOTIFY_DONE; + + dst_release(dst); + flow6.flowi6_proto = IPPROTO_UDP; + nfp_tun_write_neigh_v6(n->dev, app, &flow6, n, GFP_ATOMIC); +#else return NOTIFY_DONE; +#endif /* CONFIG_IPV6 */ + } else { + /* Do a route lookup to populate flow data. */ + rt = ip_route_output_key(dev_net(n->dev), &flow4); + err = PTR_ERR_OR_ZERO(rt); + if (err) + return NOTIFY_DONE; - ip_rt_put(rt); + ip_rt_put(rt); + + flow4.flowi4_proto = IPPROTO_UDP; + nfp_tun_write_neigh_v4(n->dev, app, &flow4, n, GFP_ATOMIC); + } #else return NOTIFY_DONE; -#endif - - flow.flowi4_proto = IPPROTO_UDP; - nfp_tun_write_neigh_v4(n->dev, app, &flow, n, GFP_ATOMIC); +#endif /* CONFIG_INET */ return NOTIFY_OK; } @@ -1205,8 +1289,10 @@ int nfp_tunnel_config_start(struct nfp_app *app) INIT_LIST_HEAD(&priv->tun.ipv6_off_list); /* Initialise priv data for neighbour offloading. */ - spin_lock_init(&priv->tun.neigh_off_lock); - INIT_LIST_HEAD(&priv->tun.neigh_off_list); + spin_lock_init(&priv->tun.neigh_off_lock_v4); + INIT_LIST_HEAD(&priv->tun.neigh_off_list_v4); + spin_lock_init(&priv->tun.neigh_off_lock_v6); + INIT_LIST_HEAD(&priv->tun.neigh_off_list_v6); priv->tun.neigh_nb.notifier_call = nfp_tun_neigh_event_handler; err = register_netevent_notifier(&priv->tun.neigh_nb); @@ -1221,9 +1307,11 @@ int nfp_tunnel_config_start(struct nfp_app *app) void nfp_tunnel_config_stop(struct nfp_app *app) { + struct nfp_offloaded_route *route_entry, *temp; struct nfp_flower_priv *priv = app->priv; - struct nfp_ipv4_route_entry *route_entry; struct nfp_ipv4_addr_entry *ip_entry; + struct nfp_tun_neigh_v6 ipv6_route; + struct nfp_tun_neigh ipv4_route; struct list_head *ptr, *storage; unregister_netevent_notifier(&priv->tun.neigh_nb); @@ -1239,12 +1327,33 @@ void nfp_tunnel_config_stop(struct nfp_app *app) mutex_destroy(&priv->tun.ipv6_off_lock); - /* Free any memory that may be occupied by the route list. */ - list_for_each_safe(ptr, storage, &priv->tun.neigh_off_list) { - route_entry = list_entry(ptr, struct nfp_ipv4_route_entry, - list); + /* Free memory in the route list and remove entries from fw cache. */ + list_for_each_entry_safe(route_entry, temp, + &priv->tun.neigh_off_list_v4, list) { + memset(&ipv4_route, 0, sizeof(ipv4_route)); + memcpy(&ipv4_route.dst_ipv4, &route_entry->ip_add, + sizeof(ipv4_route.dst_ipv4)); list_del(&route_entry->list); kfree(route_entry); + + nfp_flower_xmit_tun_conf(app, NFP_FLOWER_CMSG_TYPE_TUN_NEIGH, + sizeof(struct nfp_tun_neigh), + (unsigned char *)&ipv4_route, + GFP_KERNEL); + } + + list_for_each_entry_safe(route_entry, temp, + &priv->tun.neigh_off_list_v6, list) { + memset(&ipv6_route, 0, sizeof(ipv6_route)); + memcpy(&ipv6_route.dst_ipv6, &route_entry->ip_add, + sizeof(ipv6_route.dst_ipv6)); + list_del(&route_entry->list); + kfree(route_entry); + + nfp_flower_xmit_tun_conf(app, NFP_FLOWER_CMSG_TYPE_TUN_NEIGH_V6, + sizeof(struct nfp_tun_neigh), + (unsigned char *)&ipv6_route, + GFP_KERNEL); } /* Destroy rhash. Entries should be cleaned on netdev notifier unreg. */ -- GitLab From 9ea9bfa12240650feba7648dbb7f334dac8ba757 Mon Sep 17 00:00:00 2001 From: John Hurley Date: Tue, 17 Dec 2019 21:57:23 +0000 Subject: [PATCH 1116/1437] nfp: flower: support ipv6 tunnel keep-alive messages from fw FW sends an update of IPv6 tunnels that are active in a given period. Use this information to update the kernel table so that neighbour entries do not time out when active on the NIC. Signed-off-by: John Hurley Reviewed-by: Simon Horman Reviewed-by: Jakub Kicinski Signed-off-by: David S. Miller --- .../net/ethernet/netronome/nfp/flower/cmsg.c | 3 + .../net/ethernet/netronome/nfp/flower/cmsg.h | 1 + .../net/ethernet/netronome/nfp/flower/main.h | 1 + .../netronome/nfp/flower/tunnel_conf.c | 62 +++++++++++++++++++ 4 files changed, 67 insertions(+) diff --git a/drivers/net/ethernet/netronome/nfp/flower/cmsg.c b/drivers/net/ethernet/netronome/nfp/flower/cmsg.c index 00b904ea4ebc5..a595ddb92bff9 100644 --- a/drivers/net/ethernet/netronome/nfp/flower/cmsg.c +++ b/drivers/net/ethernet/netronome/nfp/flower/cmsg.c @@ -278,6 +278,9 @@ nfp_flower_cmsg_process_one_rx(struct nfp_app *app, struct sk_buff *skb) case NFP_FLOWER_CMSG_TYPE_ACTIVE_TUNS: nfp_tunnel_keep_alive(app, skb); break; + case NFP_FLOWER_CMSG_TYPE_ACTIVE_TUNS_V6: + nfp_tunnel_keep_alive_v6(app, skb); + break; case NFP_FLOWER_CMSG_TYPE_QOS_STATS: nfp_flower_stats_rlim_reply(app, skb); break; diff --git a/drivers/net/ethernet/netronome/nfp/flower/cmsg.h b/drivers/net/ethernet/netronome/nfp/flower/cmsg.h index 5a8ad5814060f..9b50d76bbc093 100644 --- a/drivers/net/ethernet/netronome/nfp/flower/cmsg.h +++ b/drivers/net/ethernet/netronome/nfp/flower/cmsg.h @@ -574,6 +574,7 @@ enum nfp_flower_cmsg_type_port { NFP_FLOWER_CMSG_TYPE_TUN_IPS_V6 = 22, NFP_FLOWER_CMSG_TYPE_NO_NEIGH_V6 = 23, NFP_FLOWER_CMSG_TYPE_TUN_NEIGH_V6 = 24, + NFP_FLOWER_CMSG_TYPE_ACTIVE_TUNS_V6 = 25, NFP_FLOWER_CMSG_TYPE_MAX = 32, }; diff --git a/drivers/net/ethernet/netronome/nfp/flower/main.h b/drivers/net/ethernet/netronome/nfp/flower/main.h index 7a46032610269..ddd7b7f5f5abe 100644 --- a/drivers/net/ethernet/netronome/nfp/flower/main.h +++ b/drivers/net/ethernet/netronome/nfp/flower/main.h @@ -425,6 +425,7 @@ nfp_tunnel_add_ipv6_off(struct nfp_app *app, struct in6_addr *ipv6); void nfp_tunnel_request_route_v4(struct nfp_app *app, struct sk_buff *skb); void nfp_tunnel_request_route_v6(struct nfp_app *app, struct sk_buff *skb); void nfp_tunnel_keep_alive(struct nfp_app *app, struct sk_buff *skb); +void nfp_tunnel_keep_alive_v6(struct nfp_app *app, struct sk_buff *skb); void nfp_flower_lag_init(struct nfp_fl_lag *lag); void nfp_flower_lag_cleanup(struct nfp_fl_lag *lag); int nfp_flower_lag_reset(struct nfp_fl_lag *lag); diff --git a/drivers/net/ethernet/netronome/nfp/flower/tunnel_conf.c b/drivers/net/ethernet/netronome/nfp/flower/tunnel_conf.c index b179b63c84563..2df3deedf9fd8 100644 --- a/drivers/net/ethernet/netronome/nfp/flower/tunnel_conf.c +++ b/drivers/net/ethernet/netronome/nfp/flower/tunnel_conf.c @@ -54,6 +54,25 @@ struct nfp_tun_active_tuns { } tun_info[]; }; +/** + * struct nfp_tun_active_tuns_v6 - periodic message of active IPv6 tunnels + * @seq: sequence number of the message + * @count: number of tunnels report in message + * @flags: options part of the request + * @tun_info.ipv6: dest IPv6 address of active route + * @tun_info.egress_port: port the encapsulated packet egressed + * @tun_info: tunnels that have sent traffic in reported period + */ +struct nfp_tun_active_tuns_v6 { + __be32 seq; + __be32 count; + __be32 flags; + struct route_ip_info_v6 { + struct in6_addr ipv6; + __be32 egress_port; + } tun_info[]; +}; + /** * struct nfp_tun_neigh - neighbour/route entry on the NFP * @dst_ipv4: destination IPv4 address @@ -244,6 +263,49 @@ void nfp_tunnel_keep_alive(struct nfp_app *app, struct sk_buff *skb) rcu_read_unlock(); } +void nfp_tunnel_keep_alive_v6(struct nfp_app *app, struct sk_buff *skb) +{ +#if IS_ENABLED(CONFIG_IPV6) + struct nfp_tun_active_tuns_v6 *payload; + struct net_device *netdev; + int count, i, pay_len; + struct neighbour *n; + void *ipv6_add; + u32 port; + + payload = nfp_flower_cmsg_get_data(skb); + count = be32_to_cpu(payload->count); + if (count > NFP_FL_IPV6_ADDRS_MAX) { + nfp_flower_cmsg_warn(app, "IPv6 tunnel keep-alive request exceeds max routes.\n"); + return; + } + + pay_len = nfp_flower_cmsg_get_data_len(skb); + if (pay_len != struct_size(payload, tun_info, count)) { + nfp_flower_cmsg_warn(app, "Corruption in tunnel keep-alive message.\n"); + return; + } + + rcu_read_lock(); + for (i = 0; i < count; i++) { + ipv6_add = &payload->tun_info[i].ipv6; + port = be32_to_cpu(payload->tun_info[i].egress_port); + netdev = nfp_app_dev_get(app, port, NULL); + if (!netdev) + continue; + + n = neigh_lookup(&nd_tbl, ipv6_add, netdev); + if (!n) + continue; + + /* Update the used timestamp of neighbour */ + neigh_event_send(n, NULL); + neigh_release(n); + } + rcu_read_unlock(); +#endif +} + static int nfp_flower_xmit_tun_conf(struct nfp_app *app, u8 mtype, u16 plen, void *pdata, gfp_t flag) -- GitLab From 783461604f7e3c7b4df9c653bf3db165cb9e3326 Mon Sep 17 00:00:00 2001 From: John Hurley Date: Tue, 17 Dec 2019 21:57:24 +0000 Subject: [PATCH 1117/1437] nfp: flower: update flow merge code to support IPv6 tunnels Both pre-tunnel match rules and flow merge functions parse compiled match/action fields for validation. Update these validation functions to include IPv6 match and action fields. Signed-off-by: John Hurley Reviewed-by: Simon Horman Reviewed-by: Jakub Kicinski Signed-off-by: David S. Miller --- .../ethernet/netronome/nfp/flower/offload.c | 28 +++++++++++++++---- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/drivers/net/ethernet/netronome/nfp/flower/offload.c b/drivers/net/ethernet/netronome/nfp/flower/offload.c index d512105492999..7ca5c1becfcfe 100644 --- a/drivers/net/ethernet/netronome/nfp/flower/offload.c +++ b/drivers/net/ethernet/netronome/nfp/flower/offload.c @@ -68,7 +68,8 @@ #define NFP_FLOWER_PRE_TUN_RULE_FIELDS \ (NFP_FLOWER_LAYER_PORT | \ NFP_FLOWER_LAYER_MAC | \ - NFP_FLOWER_LAYER_IPV4) + NFP_FLOWER_LAYER_IPV4 | \ + NFP_FLOWER_LAYER_IPV6) struct nfp_flower_merge_check { union { @@ -566,10 +567,12 @@ nfp_flower_update_merge_with_actions(struct nfp_fl_payload *flow, struct nfp_fl_set_ip4_addrs *ipv4_add; struct nfp_fl_set_ipv6_addr *ipv6_add; struct nfp_fl_push_vlan *push_vlan; + struct nfp_fl_pre_tunnel *pre_tun; struct nfp_fl_set_tport *tport; struct nfp_fl_set_eth *eth; struct nfp_fl_act_head *a; unsigned int act_off = 0; + bool ipv6_tun = false; u8 act_id = 0; u8 *ports; int i; @@ -597,8 +600,12 @@ nfp_flower_update_merge_with_actions(struct nfp_fl_payload *flow, eth_broadcast_addr(&merge->l2.mac_src[0]); memset(&merge->l4, 0xff, sizeof(struct nfp_flower_tp_ports)); - memset(&merge->ipv4, 0xff, - sizeof(struct nfp_flower_ipv4)); + if (ipv6_tun) + memset(&merge->ipv6, 0xff, + sizeof(struct nfp_flower_ipv6)); + else + memset(&merge->ipv4, 0xff, + sizeof(struct nfp_flower_ipv4)); break; case NFP_FL_ACTION_OPCODE_SET_ETHERNET: eth = (struct nfp_fl_set_eth *)a; @@ -646,6 +653,10 @@ nfp_flower_update_merge_with_actions(struct nfp_fl_payload *flow, ports[i] |= tport->tp_port_mask[i]; break; case NFP_FL_ACTION_OPCODE_PRE_TUNNEL: + pre_tun = (struct nfp_fl_pre_tunnel *)a; + ipv6_tun = be16_to_cpu(pre_tun->flags) & + NFP_FL_PRE_TUN_IPV6; + break; case NFP_FL_ACTION_OPCODE_PRE_LAG: case NFP_FL_ACTION_OPCODE_PUSH_GENEVE: break; @@ -1107,15 +1118,22 @@ nfp_flower_validate_pre_tun_rule(struct nfp_app *app, return -EOPNOTSUPP; } - if (key_layer & NFP_FLOWER_LAYER_IPV4) { + if (key_layer & NFP_FLOWER_LAYER_IPV4 || + key_layer & NFP_FLOWER_LAYER_IPV6) { + /* Flags and proto fields have same offset in IPv4 and IPv6. */ int ip_flags = offsetof(struct nfp_flower_ipv4, ip_ext.flags); int ip_proto = offsetof(struct nfp_flower_ipv4, ip_ext.proto); + int size; int i; + size = key_layer & NFP_FLOWER_LAYER_IPV4 ? + sizeof(struct nfp_flower_ipv4) : + sizeof(struct nfp_flower_ipv6); + mask += sizeof(struct nfp_flower_mac_mpls); /* Ensure proto and flags are the only IP layer fields. */ - for (i = 0; i < sizeof(struct nfp_flower_ipv4); i++) + for (i = 0; i < size; i++) if (mask[i] && i != ip_flags && i != ip_proto) { NL_SET_ERR_MSG_MOD(extack, "unsupported pre-tunnel rule: only flags and proto can be matched in ip header"); return -EOPNOTSUPP; -- GitLab From 572c7245182a200e3ac69bb86a0e60cc34c34dc9 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Tue, 17 Dec 2019 14:12:00 -0800 Subject: [PATCH 1118/1437] nfp: pass packet pointer to nfp_net_parse_meta() Make nfp_net_parse_meta() take a packet pointer and return a drop/no drop decision. Right now it returns the end of metadata and caller compares it to the packet pointer. Signed-off-by: Jakub Kicinski Signed-off-by: David S. Miller --- .../ethernet/netronome/nfp/nfp_net_common.c | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c index d4eeb3b3cf35d..780bd1daa601c 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c @@ -1661,9 +1661,9 @@ nfp_net_set_hash_desc(struct net_device *netdev, struct nfp_meta_parsed *meta, &rx_hash->hash); } -static void * +static bool nfp_net_parse_meta(struct net_device *netdev, struct nfp_meta_parsed *meta, - void *data, int meta_len) + void *data, void *pkt, int meta_len) { u32 meta_info; @@ -1694,13 +1694,13 @@ nfp_net_parse_meta(struct net_device *netdev, struct nfp_meta_parsed *meta, data += 4; break; default: - return NULL; + return true; } meta_info >>= NFP_NET_META_FIELD_SIZE; } - return data; + return data != pkt; } static void @@ -1885,12 +1885,10 @@ static int nfp_net_rx(struct nfp_net_rx_ring *rx_ring, int budget) nfp_net_set_hash_desc(dp->netdev, &meta, rxbuf->frag + meta_off, rxd); } else if (meta_len) { - void *end; - - end = nfp_net_parse_meta(dp->netdev, &meta, - rxbuf->frag + meta_off, - meta_len); - if (unlikely(end != rxbuf->frag + pkt_off)) { + if (unlikely(nfp_net_parse_meta(dp->netdev, &meta, + rxbuf->frag + meta_off, + rxbuf->frag + pkt_off, + meta_len))) { nn_dp_warn(dp, "invalid RX packet metadata\n"); nfp_net_rx_drop(dp, r_vec, rx_ring, rxbuf, NULL); -- GitLab From 8d5a49e9e31ba1ddd34a54b2351d068a90c78707 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Tue, 17 Dec 2019 14:12:01 -0800 Subject: [PATCH 1119/1437] net/tls: add helper for testing if socket is RX offloaded There is currently no way for driver to reliably check that the socket it has looked up is in fact RX offloaded. Add a helper. This allows drivers to catch misbehaving firmware. Signed-off-by: Jakub Kicinski Signed-off-by: David S. Miller --- include/net/tls.h | 9 +++++++++ net/tls/tls_device.c | 5 +++-- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/include/net/tls.h b/include/net/tls.h index df630f5fc723c..bf9eb48239332 100644 --- a/include/net/tls.h +++ b/include/net/tls.h @@ -641,6 +641,7 @@ int tls_sw_fallback_init(struct sock *sk, #ifdef CONFIG_TLS_DEVICE void tls_device_init(void); void tls_device_cleanup(void); +void tls_device_sk_destruct(struct sock *sk); int tls_set_device_offload(struct sock *sk, struct tls_context *ctx); void tls_device_free_resources_tx(struct sock *sk); int tls_set_device_offload_rx(struct sock *sk, struct tls_context *ctx); @@ -649,6 +650,14 @@ void tls_device_rx_resync_new_rec(struct sock *sk, u32 rcd_len, u32 seq); void tls_offload_tx_resync_request(struct sock *sk, u32 got_seq, u32 exp_seq); int tls_device_decrypted(struct sock *sk, struct tls_context *tls_ctx, struct sk_buff *skb, struct strp_msg *rxm); + +static inline bool tls_is_sk_rx_device_offloaded(struct sock *sk) +{ + if (!sk_fullsock(sk) || + smp_load_acquire(&sk->sk_destruct) != tls_device_sk_destruct) + return false; + return tls_get_ctx(sk)->rx_conf == TLS_HW; +} #else static inline void tls_device_init(void) {} static inline void tls_device_cleanup(void) {} diff --git a/net/tls/tls_device.c b/net/tls/tls_device.c index cd91ad8122915..1ba5a92832bb0 100644 --- a/net/tls/tls_device.c +++ b/net/tls/tls_device.c @@ -178,7 +178,7 @@ static void tls_icsk_clean_acked(struct sock *sk, u32 acked_seq) * socket and no in-flight SKBs associated with this * socket, so it is safe to free all the resources. */ -static void tls_device_sk_destruct(struct sock *sk) +void tls_device_sk_destruct(struct sock *sk) { struct tls_context *tls_ctx = tls_get_ctx(sk); struct tls_offload_context_tx *ctx = tls_offload_ctx_tx(tls_ctx); @@ -196,6 +196,7 @@ static void tls_device_sk_destruct(struct sock *sk) if (refcount_dec_and_test(&tls_ctx->refcount)) tls_device_queue_ctx_destruction(tls_ctx); } +EXPORT_SYMBOL_GPL(tls_device_sk_destruct); void tls_device_free_resources_tx(struct sock *sk) { @@ -903,7 +904,7 @@ static void tls_device_attach(struct tls_context *ctx, struct sock *sk, spin_unlock_irq(&tls_device_lock); ctx->sk_destruct = sk->sk_destruct; - sk->sk_destruct = tls_device_sk_destruct; + smp_store_release(&sk->sk_destruct, tls_device_sk_destruct); } } -- GitLab From 6a35ddc5445a8291ced6247a67977e110275acde Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Tue, 17 Dec 2019 14:12:02 -0800 Subject: [PATCH 1120/1437] nfp: tls: implement the stream sync RX resync The simple RX resync strategy controlled by the kernel does not guarantee as good results as if the device helps by detecting the potential record boundaries and keeping track of them. We've called this strategy stream scan in the tls-offload doc. Implement this strategy for the NFP. The device sends a request for record boundary confirmation, which is then recorded in per-TLS socket state and responded to once record is reached. Because the device keeps track of records passing after the request was sent the response is not as latency sensitive as when kernel just tries to tell the device the information about the next record. Signed-off-by: Jakub Kicinski Signed-off-by: David S. Miller --- drivers/net/ethernet/netronome/nfp/ccm.h | 1 + .../ethernet/netronome/nfp/crypto/crypto.h | 15 ++++ .../net/ethernet/netronome/nfp/crypto/fw.h | 8 ++ .../net/ethernet/netronome/nfp/crypto/tls.c | 89 +++++++++++++++++-- drivers/net/ethernet/netronome/nfp/nfp_net.h | 6 ++ .../ethernet/netronome/nfp/nfp_net_common.c | 11 ++- .../net/ethernet/netronome/nfp/nfp_net_ctrl.c | 41 +++++++-- .../net/ethernet/netronome/nfp/nfp_net_ctrl.h | 9 ++ .../ethernet/netronome/nfp/nfp_net_ethtool.c | 8 +- 9 files changed, 172 insertions(+), 16 deletions(-) diff --git a/drivers/net/ethernet/netronome/nfp/ccm.h b/drivers/net/ethernet/netronome/nfp/ccm.h index a460c75522be2..d81d450be50e4 100644 --- a/drivers/net/ethernet/netronome/nfp/ccm.h +++ b/drivers/net/ethernet/netronome/nfp/ccm.h @@ -26,6 +26,7 @@ enum nfp_ccm_type { NFP_CCM_TYPE_CRYPTO_ADD = 10, NFP_CCM_TYPE_CRYPTO_DEL = 11, NFP_CCM_TYPE_CRYPTO_UPDATE = 12, + NFP_CCM_TYPE_CRYPTO_RESYNC = 13, __NFP_CCM_TYPE_MAX, }; diff --git a/drivers/net/ethernet/netronome/nfp/crypto/crypto.h b/drivers/net/ethernet/netronome/nfp/crypto/crypto.h index 60372ddf69f0c..bffe58bb2f27f 100644 --- a/drivers/net/ethernet/netronome/nfp/crypto/crypto.h +++ b/drivers/net/ethernet/netronome/nfp/crypto/crypto.h @@ -4,6 +4,10 @@ #ifndef NFP_CRYPTO_H #define NFP_CRYPTO_H 1 +struct net_device; +struct nfp_net; +struct nfp_net_tls_resync_req; + struct nfp_net_tls_offload_ctx { __be32 fw_handle[2]; @@ -17,11 +21,22 @@ struct nfp_net_tls_offload_ctx { #ifdef CONFIG_TLS_DEVICE int nfp_net_tls_init(struct nfp_net *nn); +int nfp_net_tls_rx_resync_req(struct net_device *netdev, + struct nfp_net_tls_resync_req *req, + void *pkt, unsigned int pkt_len); #else static inline int nfp_net_tls_init(struct nfp_net *nn) { return 0; } + +static inline int +nfp_net_tls_rx_resync_req(struct net_device *netdev, + struct nfp_net_tls_resync_req *req, + void *pkt, unsigned int pkt_len) +{ + return -EOPNOTSUPP; +} #endif #endif diff --git a/drivers/net/ethernet/netronome/nfp/crypto/fw.h b/drivers/net/ethernet/netronome/nfp/crypto/fw.h index 67413d946c4a2..8d1458896bcb7 100644 --- a/drivers/net/ethernet/netronome/nfp/crypto/fw.h +++ b/drivers/net/ethernet/netronome/nfp/crypto/fw.h @@ -9,6 +9,14 @@ #define NFP_NET_CRYPTO_OP_TLS_1_2_AES_GCM_128_ENC 0 #define NFP_NET_CRYPTO_OP_TLS_1_2_AES_GCM_128_DEC 1 +struct nfp_net_tls_resync_req { + __be32 fw_handle[2]; + __be32 tcp_seq; + u8 l3_offset; + u8 l4_offset; + u8 resv[2]; +}; + struct nfp_crypto_reply_simple { struct nfp_ccm_hdr hdr; __be32 error; diff --git a/drivers/net/ethernet/netronome/nfp/crypto/tls.c b/drivers/net/ethernet/netronome/nfp/crypto/tls.c index 96a96b35c0ca6..7c50e3dfb9d52 100644 --- a/drivers/net/ethernet/netronome/nfp/crypto/tls.c +++ b/drivers/net/ethernet/netronome/nfp/crypto/tls.c @@ -5,6 +5,7 @@ #include #include #include +#include #include #include "../ccm.h" @@ -391,8 +392,9 @@ nfp_net_tls_add(struct net_device *netdev, struct sock *sk, if (direction == TLS_OFFLOAD_CTX_DIR_TX) return 0; - tls_offload_rx_resync_set_type(sk, - TLS_OFFLOAD_SYNC_TYPE_CORE_NEXT_HINT); + if (!nn->tlv_caps.tls_resync_ss) + tls_offload_rx_resync_set_type(sk, TLS_OFFLOAD_SYNC_TYPE_CORE_NEXT_HINT); + return 0; err_fw_remove: @@ -424,6 +426,7 @@ nfp_net_tls_resync(struct net_device *netdev, struct sock *sk, u32 seq, struct nfp_net *nn = netdev_priv(netdev); struct nfp_net_tls_offload_ctx *ntls; struct nfp_crypto_req_update *req; + enum nfp_ccm_type type; struct sk_buff *skb; gfp_t flags; int err; @@ -442,15 +445,18 @@ nfp_net_tls_resync(struct net_device *netdev, struct sock *sk, u32 seq, req->tcp_seq = cpu_to_be32(seq); memcpy(req->rec_no, rcd_sn, sizeof(req->rec_no)); + type = NFP_CCM_TYPE_CRYPTO_UPDATE; if (direction == TLS_OFFLOAD_CTX_DIR_TX) { - err = nfp_net_tls_communicate_simple(nn, skb, "sync", - NFP_CCM_TYPE_CRYPTO_UPDATE); + err = nfp_net_tls_communicate_simple(nn, skb, "sync", type); if (err) return err; ntls->next_seq = seq; } else { - nfp_ccm_mbox_post(nn, skb, NFP_CCM_TYPE_CRYPTO_UPDATE, + if (nn->tlv_caps.tls_resync_ss) + type = NFP_CCM_TYPE_CRYPTO_RESYNC; + nfp_ccm_mbox_post(nn, skb, type, sizeof(struct nfp_crypto_reply_simple)); + atomic_inc(&nn->ktls_rx_resync_sent); } return 0; @@ -462,6 +468,79 @@ static const struct tlsdev_ops nfp_net_tls_ops = { .tls_dev_resync = nfp_net_tls_resync, }; +int nfp_net_tls_rx_resync_req(struct net_device *netdev, + struct nfp_net_tls_resync_req *req, + void *pkt, unsigned int pkt_len) +{ + struct nfp_net *nn = netdev_priv(netdev); + struct nfp_net_tls_offload_ctx *ntls; + struct ipv6hdr *ipv6h; + struct tcphdr *th; + struct iphdr *iph; + struct sock *sk; + __be32 tcp_seq; + int err; + + iph = pkt + req->l3_offset; + ipv6h = pkt + req->l3_offset; + th = pkt + req->l4_offset; + + if ((u8 *)&th[1] > (u8 *)pkt + pkt_len) { + netdev_warn_once(netdev, "invalid TLS RX resync request (l3_off: %hhu l4_off: %hhu pkt_len: %u)\n", + req->l3_offset, req->l4_offset, pkt_len); + err = -EINVAL; + goto err_cnt_ign; + } + + switch (iph->version) { + case 4: + sk = inet_lookup_established(dev_net(netdev), &tcp_hashinfo, + iph->saddr, th->source, iph->daddr, + th->dest, netdev->ifindex); + break; +#if IS_ENABLED(CONFIG_IPV6) + case 6: + sk = __inet6_lookup_established(dev_net(netdev), &tcp_hashinfo, + &ipv6h->saddr, th->source, + &ipv6h->daddr, ntohs(th->dest), + netdev->ifindex, 0); + break; +#endif + default: + netdev_warn_once(netdev, "invalid TLS RX resync request (l3_off: %hhu l4_off: %hhu ipver: %u)\n", + req->l3_offset, req->l4_offset, iph->version); + err = -EINVAL; + goto err_cnt_ign; + } + + err = 0; + if (!sk) + goto err_cnt_ign; + if (!tls_is_sk_rx_device_offloaded(sk) || + sk->sk_shutdown & RCV_SHUTDOWN) + goto err_put_sock; + + ntls = tls_driver_ctx(sk, TLS_OFFLOAD_CTX_DIR_RX); + /* some FW versions can't report the handle and report 0s */ + if (memchr_inv(&req->fw_handle, 0, sizeof(req->fw_handle)) && + memcmp(&req->fw_handle, &ntls->fw_handle, sizeof(ntls->fw_handle))) + goto err_put_sock; + + /* copy to ensure alignment */ + memcpy(&tcp_seq, &req->tcp_seq, sizeof(tcp_seq)); + tls_offload_rx_resync_request(sk, tcp_seq); + atomic_inc(&nn->ktls_rx_resync_req); + + sock_gen_put(sk); + return 0; + +err_put_sock: + sock_gen_put(sk); +err_cnt_ign: + atomic_inc(&nn->ktls_rx_resync_ign); + return err; +} + static int nfp_net_tls_reset(struct nfp_net *nn) { struct nfp_crypto_req_reset *req; diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net.h b/drivers/net/ethernet/netronome/nfp/nfp_net.h index 250f510b1d212..ff4438478ea9c 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net.h +++ b/drivers/net/ethernet/netronome/nfp/nfp_net.h @@ -586,6 +586,9 @@ struct nfp_net_dp { * @ktls_conn_id_gen: Trivial generator for kTLS connection ids (for TX) * @ktls_no_space: Counter of firmware rejecting kTLS connection due to * lack of space + * @ktls_rx_resync_req: Counter of TLS RX resync requested + * @ktls_rx_resync_ign: Counter of TLS RX resync requests ignored + * @ktls_rx_resync_sent: Counter of TLS RX resync completed * @mbox_cmsg: Common Control Message via vNIC mailbox state * @mbox_cmsg.queue: CCM mbox queue of pending messages * @mbox_cmsg.wq: CCM mbox wait queue of waiting processes @@ -674,6 +677,9 @@ struct nfp_net { atomic64_t ktls_conn_id_gen; atomic_t ktls_no_space; + atomic_t ktls_rx_resync_req; + atomic_t ktls_rx_resync_ign; + atomic_t ktls_rx_resync_sent; struct { struct sk_buff_head queue; diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c index 780bd1daa601c..9bfb3b077bc16 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c @@ -47,6 +47,7 @@ #include "nfp_net_sriov.h" #include "nfp_port.h" #include "crypto/crypto.h" +#include "crypto/fw.h" /** * nfp_net_get_fw_version() - Read and parse the FW version @@ -1663,7 +1664,7 @@ nfp_net_set_hash_desc(struct net_device *netdev, struct nfp_meta_parsed *meta, static bool nfp_net_parse_meta(struct net_device *netdev, struct nfp_meta_parsed *meta, - void *data, void *pkt, int meta_len) + void *data, void *pkt, unsigned int pkt_len, int meta_len) { u32 meta_info; @@ -1693,6 +1694,12 @@ nfp_net_parse_meta(struct net_device *netdev, struct nfp_meta_parsed *meta, (__force __wsum)__get_unaligned_cpu32(data); data += 4; break; + case NFP_NET_META_RESYNC_INFO: + if (nfp_net_tls_rx_resync_req(netdev, data, pkt, + pkt_len)) + return NULL; + data += sizeof(struct nfp_net_tls_resync_req); + break; default: return true; } @@ -1888,7 +1895,7 @@ static int nfp_net_rx(struct nfp_net_rx_ring *rx_ring, int budget) if (unlikely(nfp_net_parse_meta(dp->netdev, &meta, rxbuf->frag + meta_off, rxbuf->frag + pkt_off, - meta_len))) { + pkt_len, meta_len))) { nn_dp_warn(dp, "invalid RX packet metadata\n"); nfp_net_rx_drop(dp, r_vec, rx_ring, rxbuf, NULL); diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_ctrl.c b/drivers/net/ethernet/netronome/nfp/nfp_net_ctrl.c index 45756648a85c4..c3a763134e79f 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_ctrl.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_ctrl.c @@ -17,6 +17,30 @@ static void nfp_net_tlv_caps_reset(struct nfp_net_tlv_caps *caps) caps->mbox_len = NFP_NET_CFG_MBOX_VAL_MAX_SZ; } +static bool +nfp_net_tls_parse_crypto_ops(struct device *dev, struct nfp_net_tlv_caps *caps, + u8 __iomem *ctrl_mem, u8 __iomem *data, + unsigned int length, unsigned int offset, + bool rx_stream_scan) +{ + /* Ignore the legacy TLV if new one was already parsed */ + if (caps->tls_resync_ss && !rx_stream_scan) + return true; + + if (length < 32) { + dev_err(dev, + "CRYPTO OPS TLV should be at least 32B, is %dB offset:%u\n", + length, offset); + return false; + } + + caps->crypto_ops = readl(data); + caps->crypto_enable_off = data - ctrl_mem + 16; + caps->tls_resync_ss = rx_stream_scan; + + return true; +} + int nfp_net_tlv_caps_parse(struct device *dev, u8 __iomem *ctrl_mem, struct nfp_net_tlv_caps *caps) { @@ -104,15 +128,10 @@ int nfp_net_tlv_caps_parse(struct device *dev, u8 __iomem *ctrl_mem, caps->mbox_cmsg_types = readl(data); break; case NFP_NET_CFG_TLV_TYPE_CRYPTO_OPS: - if (length < 32) { - dev_err(dev, - "CRYPTO OPS TLV should be at least 32B, is %dB offset:%u\n", - length, offset); + if (!nfp_net_tls_parse_crypto_ops(dev, caps, ctrl_mem, + data, length, offset, + false)) return -EINVAL; - } - - caps->crypto_ops = readl(data); - caps->crypto_enable_off = data - ctrl_mem + 16; break; case NFP_NET_CFG_TLV_TYPE_VNIC_STATS: if ((data - ctrl_mem) % 8) { @@ -123,6 +142,12 @@ int nfp_net_tlv_caps_parse(struct device *dev, u8 __iomem *ctrl_mem, caps->vnic_stats_off = data - ctrl_mem; caps->vnic_stats_cnt = length / 10; break; + case NFP_NET_CFG_TLV_TYPE_CRYPTO_OPS_RX_SCAN: + if (!nfp_net_tls_parse_crypto_ops(dev, caps, ctrl_mem, + data, length, offset, + true)) + return -EINVAL; + break; default: if (!FIELD_GET(NFP_NET_CFG_TLV_HEADER_REQUIRED, hdr)) break; diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_ctrl.h b/drivers/net/ethernet/netronome/nfp/nfp_net_ctrl.h index c38cc36a2a70e..3d61a8cb60b02 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_ctrl.h +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_ctrl.h @@ -45,6 +45,7 @@ #define NFP_NET_META_PORTID 5 #define NFP_NET_META_CSUM 6 /* checksum complete type */ #define NFP_NET_META_CONN_HANDLE 7 +#define NFP_NET_META_RESYNC_INFO 8 /* RX resync info request */ #define NFP_META_PORT_ID_CTRL ~0U @@ -479,6 +480,7 @@ * 8 words, bitmaps of supported and enabled crypto operations. * First 16B (4 words) contains a bitmap of supported crypto operations, * and next 16B contain the enabled operations. + * This capability is made obsolete by ones with better sync methods. * * %NFP_NET_CFG_TLV_TYPE_VNIC_STATS: * Variable, per-vNIC statistics, data should be 8B aligned (FW should insert @@ -490,6 +492,10 @@ * This TLV overwrites %NFP_NET_CFG_STATS_* values (statistics in this TLV * duplicate the old ones, so driver should be careful not to unnecessarily * render both). + * + * %NFP_NET_CFG_TLV_TYPE_CRYPTO_OPS_RX_SCAN: + * Same as %NFP_NET_CFG_TLV_TYPE_CRYPTO_OPS, but crypto TLS does stream scan + * RX sync, rather than kernel-assisted sync. */ #define NFP_NET_CFG_TLV_TYPE_UNKNOWN 0 #define NFP_NET_CFG_TLV_TYPE_RESERVED 1 @@ -502,6 +508,7 @@ #define NFP_NET_CFG_TLV_TYPE_MBOX_CMSG_TYPES 10 #define NFP_NET_CFG_TLV_TYPE_CRYPTO_OPS 11 /* see crypto/fw.h */ #define NFP_NET_CFG_TLV_TYPE_VNIC_STATS 12 +#define NFP_NET_CFG_TLV_TYPE_CRYPTO_OPS_RX_SCAN 13 struct device; @@ -516,6 +523,7 @@ struct device; * @crypto_enable_off: offset of crypto ops enable region * @vnic_stats_off: offset of vNIC stats area * @vnic_stats_cnt: number of vNIC stats + * @tls_resync_ss: TLS resync will be performed via stream scan */ struct nfp_net_tlv_caps { u32 me_freq_mhz; @@ -527,6 +535,7 @@ struct nfp_net_tlv_caps { unsigned int crypto_enable_off; unsigned int vnic_stats_off; unsigned int vnic_stats_cnt; + unsigned int tls_resync_ss:1; }; int nfp_net_tlv_caps_parse(struct device *dev, u8 __iomem *ctrl_mem, diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c b/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c index b386a221c5992..d648e32c0520e 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c @@ -174,7 +174,7 @@ static const char nfp_tlv_stat_names[][ETH_GSTRING_LEN] = { #define NN_ET_SWITCH_STATS_LEN 9 #define NN_RVEC_GATHER_STATS 13 #define NN_RVEC_PER_Q_STATS 3 -#define NN_CTRL_PATH_STATS 1 +#define NN_CTRL_PATH_STATS 4 #define SFP_SFF_REV_COMPLIANCE 1 @@ -476,6 +476,9 @@ static u8 *nfp_vnic_get_sw_stats_strings(struct net_device *netdev, u8 *data) data = nfp_pr_et(data, "tx_tls_drop_no_sync_data"); data = nfp_pr_et(data, "hw_tls_no_space"); + data = nfp_pr_et(data, "rx_tls_resync_req_ok"); + data = nfp_pr_et(data, "rx_tls_resync_req_ign"); + data = nfp_pr_et(data, "rx_tls_resync_sent"); return data; } @@ -524,6 +527,9 @@ static u64 *nfp_vnic_get_sw_stats(struct net_device *netdev, u64 *data) *data++ = gathered_stats[j]; *data++ = atomic_read(&nn->ktls_no_space); + *data++ = atomic_read(&nn->ktls_rx_resync_req); + *data++ = atomic_read(&nn->ktls_rx_resync_ign); + *data++ = atomic_read(&nn->ktls_rx_resync_sent); return data; } -- GitLab From 275c44aa194b7159d1191817b20e076f55f0e620 Mon Sep 17 00:00:00 2001 From: Davide Caratti Date: Wed, 18 Dec 2019 00:00:04 +0100 Subject: [PATCH 1121/1437] net/sched: cls_u32: fix refcount leak in the error path of u32_change() when users replace cls_u32 filters with new ones having wrong parameters, so that u32_change() fails to validate them, the kernel doesn't roll-back correctly, and leaves semi-configured rules. Fix this in u32_walk(), avoiding a call to the walker function on filters that don't have a match rule connected. The side effect is, these "empty" filters are not even dumped when present; but that shouldn't be a problem as long as we are restoring the original behaviour, where semi-configured filters were not even added in the error path of u32_change(). Fixes: 6676d5e416ee ("net: sched: set dedicated tcf_walker flag when tp is empty") Signed-off-by: Davide Caratti Signed-off-by: David S. Miller --- net/sched/cls_u32.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/net/sched/cls_u32.c b/net/sched/cls_u32.c index a0e6fac613de2..66c6bcec16cbc 100644 --- a/net/sched/cls_u32.c +++ b/net/sched/cls_u32.c @@ -1108,10 +1108,33 @@ erridr: return err; } +static bool u32_hnode_empty(struct tc_u_hnode *ht, bool *non_root_ht) +{ + int i; + + if (!ht) + return true; + if (!ht->is_root) { + *non_root_ht = true; + return false; + } + if (*non_root_ht) + return false; + if (ht->refcnt < 2) + return true; + + for (i = 0; i <= ht->divisor; i++) { + if (rtnl_dereference(ht->ht[i])) + return false; + } + return true; +} + static void u32_walk(struct tcf_proto *tp, struct tcf_walker *arg, bool rtnl_held) { struct tc_u_common *tp_c = tp->data; + bool non_root_ht = false; struct tc_u_hnode *ht; struct tc_u_knode *n; unsigned int h; @@ -1124,6 +1147,8 @@ static void u32_walk(struct tcf_proto *tp, struct tcf_walker *arg, ht = rtnl_dereference(ht->next)) { if (ht->prio != tp->prio) continue; + if (u32_hnode_empty(ht, &non_root_ht)) + return; if (arg->count >= arg->skip) { if (arg->fn(tp, ht, arg) < 0) { arg->stop = 1; -- GitLab From 6649a3f3374720e000ea6d67b79b4df28a7662ba Mon Sep 17 00:00:00 2001 From: Davide Caratti Date: Wed, 18 Dec 2019 00:00:05 +0100 Subject: [PATCH 1122/1437] tc-testing: initial tdc selftests for cls_u32 - move test "e9a3 - Add u32 with source match" to u32.json, and change the match pattern to catch all hnodes - add testcases for relevant error paths of cls_u32 module Signed-off-by: Davide Caratti Signed-off-by: David S. Miller --- .../tc-testing/tc-tests/filters/tests.json | 22 -- .../tc-testing/tc-tests/filters/u32.json | 205 ++++++++++++++++++ 2 files changed, 205 insertions(+), 22 deletions(-) create mode 100644 tools/testing/selftests/tc-testing/tc-tests/filters/u32.json diff --git a/tools/testing/selftests/tc-testing/tc-tests/filters/tests.json b/tools/testing/selftests/tc-testing/tc-tests/filters/tests.json index 0f89cd50a94b6..8877f7b2b809d 100644 --- a/tools/testing/selftests/tc-testing/tc-tests/filters/tests.json +++ b/tools/testing/selftests/tc-testing/tc-tests/filters/tests.json @@ -1,26 +1,4 @@ [ - { - "id": "e9a3", - "name": "Add u32 with source match", - "category": [ - "filter", - "u32" - ], - "plugins": { - "requires": "nsPlugin" - }, - "setup": [ - "$TC qdisc add dev $DEV1 ingress" - ], - "cmdUnderTest": "$TC filter add dev $DEV1 parent ffff: protocol ip prio 1 u32 match ip src 127.0.0.1/32 flowid 1:1 action ok", - "expExitCode": "0", - "verifyCmd": "$TC filter show dev $DEV1 parent ffff:", - "matchPattern": "match 7f000001/ffffffff at 12", - "matchCount": "1", - "teardown": [ - "$TC qdisc del dev $DEV1 ingress" - ] - }, { "id": "2638", "name": "Add matchall and try to get it", diff --git a/tools/testing/selftests/tc-testing/tc-tests/filters/u32.json b/tools/testing/selftests/tc-testing/tc-tests/filters/u32.json new file mode 100644 index 0000000000000..e09d3c0e307f6 --- /dev/null +++ b/tools/testing/selftests/tc-testing/tc-tests/filters/u32.json @@ -0,0 +1,205 @@ +[ + { + "id": "afa9", + "name": "Add u32 with source match", + "category": [ + "filter", + "u32" + ], + "plugins": { + "requires": "nsPlugin" + }, + "setup": [ + "$TC qdisc add dev $DEV1 ingress" + ], + "cmdUnderTest": "$TC filter add dev $DEV1 ingress protocol ip prio 1 u32 match ip src 127.0.0.1/32 flowid 1:1 action ok", + "expExitCode": "0", + "verifyCmd": "$TC filter show dev $DEV1 ingress", + "matchPattern": "filter protocol ip pref 1 u32 chain (0[ ]+$|0 fh 800: ht divisor 1|0 fh 800::800 order 2048 key ht 800 bkt 0 flowid 1:1.*match 7f000001/ffffffff at 12)", + "matchCount": "3", + "teardown": [ + "$TC qdisc del dev $DEV1 ingress" + ] + }, + { + "id": "6aa7", + "name": "Add/Replace u32 with source match and invalid indev", + "category": [ + "filter", + "u32" + ], + "plugins": { + "requires": "nsPlugin" + }, + "setup": [ + "$TC qdisc add dev $DEV1 ingress" + ], + "cmdUnderTest": "$TC filter replace dev $DEV1 ingress protocol ip prio 1 u32 match ip src 127.0.0.1/32 indev notexist20 flowid 1:1 action ok", + "expExitCode": "2", + "verifyCmd": "$TC filter show dev $DEV1 ingress", + "matchPattern": "filter protocol ip pref 1 u32 chain 0", + "matchCount": "0", + "teardown": [ + "$TC qdisc del dev $DEV1 ingress" + ] + }, + { + "id": "bc4d", + "name": "Replace valid u32 with source match and invalid indev", + "category": [ + "filter", + "u32" + ], + "plugins": { + "requires": "nsPlugin" + }, + "setup": [ + "$TC qdisc add dev $DEV1 ingress", + "$TC filter add dev $DEV1 ingress protocol ip prio 1 u32 match ip src 127.0.0.3/32 flowid 1:3 action ok" + ], + "cmdUnderTest": "$TC filter replace dev $DEV1 ingress protocol ip prio 1 u32 match ip src 127.0.0.2/32 indev notexist20 flowid 1:2 action ok", + "expExitCode": "2", + "verifyCmd": "$TC filter show dev $DEV1 ingress", + "matchPattern": "filter protocol ip pref 1 u32 chain (0[ ]+$|0 fh 800: ht divisor 1|0 fh 800::800 order 2048 key ht 800 bkt 0 flowid 1:3.*match 7f000003/ffffffff at 12)", + "matchCount": "3", + "teardown": [ + "$TC qdisc del dev $DEV1 ingress" + ] + }, + { + "id": "648b", + "name": "Add u32 with custom hash table", + "category": [ + "filter", + "u32" + ], + "plugins": { + "requires": "nsPlugin" + }, + "setup": [ + "$TC qdisc add dev $DEV1 ingress" + ], + "cmdUnderTest": "$TC filter add dev $DEV1 ingress prio 99 handle 42: u32 divisor 256", + "expExitCode": "0", + "verifyCmd": "$TC filter show dev $DEV1 ingress", + "matchPattern": "pref 99 u32 chain (0[ ]+$|0 fh 42: ht divisor 256|0 fh 800: ht divisor 1)", + "matchCount": "3", + "teardown": [ + "$TC qdisc del dev $DEV1 ingress" + ] + }, + { + "id": "6658", + "name": "Add/Replace u32 with custom hash table and invalid handle", + "category": [ + "filter", + "u32" + ], + "plugins": { + "requires": "nsPlugin" + }, + "setup": [ + "$TC qdisc add dev $DEV1 ingress" + ], + "cmdUnderTest": "$TC filter replace dev $DEV1 ingress prio 99 handle 42:42 u32 divisor 256", + "expExitCode": "2", + "verifyCmd": "$TC filter show dev $DEV1 ingress", + "matchPattern": "pref 99 u32 chain 0", + "matchCount": "0", + "teardown": [ + "$TC qdisc del dev $DEV1 ingress" + ] + }, + { + "id": "9d0a", + "name": "Replace valid u32 with custom hash table and invalid handle", + "category": [ + "filter", + "u32" + ], + "plugins": { + "requires": "nsPlugin" + }, + "setup": [ + "$TC qdisc add dev $DEV1 ingress", + "$TC filter add dev $DEV1 ingress prio 99 handle 42: u32 divisor 256" + ], + "cmdUnderTest": "$TC filter replace dev $DEV1 ingress prio 99 handle 42:42 u32 divisor 128", + "expExitCode": "2", + "verifyCmd": "$TC filter show dev $DEV1 ingress", + "matchPattern": "pref 99 u32 chain (0[ ]+$|0 fh 42: ht divisor 256|0 fh 800: ht divisor 1)", + "matchCount": "3", + "teardown": [ + "$TC qdisc del dev $DEV1 ingress" + ] + }, + { + "id": "1644", + "name": "Add u32 filter that links to a custom hash table", + "category": [ + "filter", + "u32" + ], + "plugins": { + "requires": "nsPlugin" + }, + "setup": [ + "$TC qdisc add dev $DEV1 ingress", + "$TC filter add dev $DEV1 ingress prio 99 handle 43: u32 divisor 256" + ], + "cmdUnderTest": "$TC filter add dev $DEV1 ingress protocol ip prio 98 u32 link 43: hashkey mask 0x0000ff00 at 12 match ip src 192.168.0.0/16", + "expExitCode": "0", + "verifyCmd": "$TC filter show dev $DEV1 ingress", + "matchPattern": "filter protocol ip pref 98 u32 chain (0[ ]+$|0 fh 801: ht divisor 1|0 fh 801::800 order 2048 key ht 801 bkt 0 link 43:.*match c0a80000/ffff0000 at 12.*hash mask 0000ff00 at 12)", + "matchCount": "3", + "teardown": [ + "$TC qdisc del dev $DEV1 ingress" + ] + }, + { + "id": "74c2", + "name": "Add/Replace u32 filter with invalid hash table id", + "category": [ + "filter", + "u32" + ], + "plugins": { + "requires": "nsPlugin" + }, + "setup": [ + "$TC qdisc add dev $DEV1 ingress" + ], + "cmdUnderTest": "$TC filter replace dev $DEV1 ingress protocol ip prio 20 u32 ht 47:47 action drop", + "expExitCode": "2", + "verifyCmd": "$TC filter show dev $DEV1 ingress", + "matchPattern": "filter protocol ip pref 20 u32 chain 0", + "matchCount": "0", + "teardown": [ + "$TC qdisc del dev $DEV1 ingress" + ] + }, + { + "id": "1fe6", + "name": "Replace valid u32 filter with invalid hash table id", + "category": [ + "filter", + "u32" + ], + "plugins": { + "requires": "nsPlugin" + }, + "setup": [ + "$TC qdisc add dev $DEV1 ingress", + "$TC filter add dev $DEV1 ingress protocol ip prio 99 handle 43: u32 divisor 1", + "$TC filter add dev $DEV1 ingress protocol ip prio 98 u32 ht 43: match tcp src 22 FFFF classid 1:3" + ], + "cmdUnderTest": "$TC filter replace dev $DEV1 ingress protocol ip prio 98 u32 ht 43:1 match tcp src 23 FFFF classid 1:4", + "expExitCode": "2", + "verifyCmd": "$TC filter show dev $DEV1 ingress", + "matchPattern": "filter protocol ip pref 99 u32 chain (0[ ]+$|0 fh (43|800): ht divisor 1|0 fh 43::800 order 2048 key ht 43 bkt 0 flowid 1:3.*match 00160000/ffff0000 at nexthdr\\+0)", + "matchCount": "4", + "teardown": [ + "$TC qdisc del dev $DEV1 ingress" + ] + } +] -- GitLab From d63b542c625c4af8f78f227b2749b87ca6acdb37 Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Tue, 17 Dec 2019 16:29:50 -0800 Subject: [PATCH 1123/1437] net: systemport: Set correct DMA mask SYSTEMPORT is capabable of doing up to 40-bit of physical addresses, set an appropriate DMA mask to permit that. Signed-off-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bcmsysport.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/net/ethernet/broadcom/bcmsysport.c b/drivers/net/ethernet/broadcom/bcmsysport.c index 8e3152779a614..1907e47fd0af1 100644 --- a/drivers/net/ethernet/broadcom/bcmsysport.c +++ b/drivers/net/ethernet/broadcom/bcmsysport.c @@ -2427,6 +2427,14 @@ static int bcm_sysport_probe(struct platform_device *pdev) if (!of_id || !of_id->data) return -EINVAL; + ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(40)); + if (ret) + ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); + if (ret) { + dev_err(&pdev->dev, "unable to set DMA mask: %d\n", ret); + return ret; + } + /* Fairly quickly we need to know the type of adapter we have */ params = of_id->data; -- GitLab From 99d55638d4b053c549c924959747c58392b2dbea Mon Sep 17 00:00:00 2001 From: Doug Berger Date: Tue, 17 Dec 2019 16:51:08 -0800 Subject: [PATCH 1124/1437] net: bcmgenet: enable NETIF_F_HIGHDMA flag This commit configures the DMA masks for the GENET driver and sets the NETIF_F_HIGHDMA flag to report support of the feature. Signed-off-by: Doug Berger Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/genet/bcmgenet.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/broadcom/genet/bcmgenet.c b/drivers/net/ethernet/broadcom/genet/bcmgenet.c index 32f1245a69e2d..d9defb8b1e5f2 100644 --- a/drivers/net/ethernet/broadcom/genet/bcmgenet.c +++ b/drivers/net/ethernet/broadcom/genet/bcmgenet.c @@ -2,7 +2,7 @@ /* * Broadcom GENET (Gigabit Ethernet) controller driver * - * Copyright (c) 2014-2017 Broadcom + * Copyright (c) 2014-2019 Broadcom */ #define pr_fmt(fmt) "bcmgenet: " fmt @@ -3537,7 +3537,7 @@ static int bcmgenet_probe(struct platform_device *pdev) /* Set hardware features */ dev->hw_features |= NETIF_F_SG | NETIF_F_IP_CSUM | - NETIF_F_IPV6_CSUM | NETIF_F_RXCSUM; + NETIF_F_IPV6_CSUM | NETIF_F_HIGHDMA | NETIF_F_RXCSUM; /* Request the WOL interrupt and advertise suspend if available */ priv->wol_irq_disabled = true; @@ -3574,6 +3574,14 @@ static int bcmgenet_probe(struct platform_device *pdev) bcmgenet_set_hw_params(priv); + err = -EIO; + if (priv->hw_params->flags & GENET_HAS_40BITS) + err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(40)); + if (err) + err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); + if (err) + goto err; + /* Mii wait queue */ init_waitqueue_head(&priv->wq); /* Always use RX_BUF_LENGTH (2KB) buffer for all chips */ -- GitLab From dd8e911b7f2af2315184a8e551ca77ff226a4ec5 Mon Sep 17 00:00:00 2001 From: Doug Berger Date: Tue, 17 Dec 2019 16:51:09 -0800 Subject: [PATCH 1125/1437] net: bcmgenet: enable NETIF_F_HW_CSUM feature The GENET hardware should be capable of generating IP checksums using the NETIF_F_HW_CSUM feature, so switch to using that feature instead of the depricated NETIF_F_IP_CSUM and NETIF_F_IPV6_CSUM. Signed-off-by: Doug Berger Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller --- .../net/ethernet/broadcom/genet/bcmgenet.c | 29 ++++++++----------- 1 file changed, 12 insertions(+), 17 deletions(-) diff --git a/drivers/net/ethernet/broadcom/genet/bcmgenet.c b/drivers/net/ethernet/broadcom/genet/bcmgenet.c index d9defb8b1e5f2..cd07b3ad1d534 100644 --- a/drivers/net/ethernet/broadcom/genet/bcmgenet.c +++ b/drivers/net/ethernet/broadcom/genet/bcmgenet.c @@ -549,7 +549,7 @@ static int bcmgenet_set_tx_csum(struct net_device *dev, tbuf_ctrl = bcmgenet_tbuf_ctrl_get(priv); rbuf_ctrl = bcmgenet_rbuf_readl(priv, RBUF_CTRL); - desc_64b_en = !!(wanted & (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM)); + desc_64b_en = !!(wanted & NETIF_F_HW_CSUM); /* enable 64 bytes descriptor in both directions (RBUF and TBUF) */ if (desc_64b_en) { @@ -574,7 +574,7 @@ static int bcmgenet_set_features(struct net_device *dev, netdev_features_t wanted = dev->wanted_features; int ret = 0; - if (changed & (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM)) + if (changed & NETIF_F_HW_CSUM) ret = bcmgenet_set_tx_csum(dev, wanted); if (changed & (NETIF_F_RXCSUM)) ret = bcmgenet_set_rx_csum(dev, wanted); @@ -1516,24 +1516,19 @@ static struct sk_buff *bcmgenet_put_tx_csum(struct net_device *dev, ip_proto = ipv6_hdr(skb)->nexthdr; break; default: - return skb; + /* don't use UDP flag */ + ip_proto = 0; + break; } offset = skb_checksum_start_offset(skb) - sizeof(*status); tx_csum_info = (offset << STATUS_TX_CSUM_START_SHIFT) | - (offset + skb->csum_offset); + (offset + skb->csum_offset) | + STATUS_TX_CSUM_LV; - /* Set the length valid bit for TCP and UDP and just set - * the special UDP flag for IPv4, else just set to 0. - */ - if (ip_proto == IPPROTO_TCP || ip_proto == IPPROTO_UDP) { - tx_csum_info |= STATUS_TX_CSUM_LV; - if (ip_proto == IPPROTO_UDP && - ip_ver == htons(ETH_P_IP)) - tx_csum_info |= STATUS_TX_CSUM_PROTO_UDP; - } else { - tx_csum_info = 0; - } + /* Set the special UDP flag for UDP */ + if (ip_proto == IPPROTO_UDP) + tx_csum_info |= STATUS_TX_CSUM_PROTO_UDP; status->tx_csum_info = tx_csum_info; } @@ -3536,8 +3531,8 @@ static int bcmgenet_probe(struct platform_device *pdev) priv->msg_enable = netif_msg_init(-1, GENET_MSG_DEFAULT); /* Set hardware features */ - dev->hw_features |= NETIF_F_SG | NETIF_F_IP_CSUM | - NETIF_F_IPV6_CSUM | NETIF_F_HIGHDMA | NETIF_F_RXCSUM; + dev->hw_features |= NETIF_F_SG | NETIF_F_HIGHDMA | NETIF_F_HW_CSUM | + NETIF_F_RXCSUM; /* Request the WOL interrupt and advertise suspend if available */ priv->wol_irq_disabled = true; -- GitLab From 810155397890ecdea23868ef0ed10cb80134997f Mon Sep 17 00:00:00 2001 From: Doug Berger Date: Tue, 17 Dec 2019 16:51:10 -0800 Subject: [PATCH 1126/1437] net: bcmgenet: use CHECKSUM_COMPLETE for NETIF_F_RXCSUM This commit updates the Rx checksum offload behavior of the driver to use the more generic CHECKSUM_COMPLETE method that supports all protocols over the CHECKSUM_UNNECESSARY method that only applies to some protocols known by the hardware. This behavior is perceived to be superior. Signed-off-by: Doug Berger Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller --- .../net/ethernet/broadcom/genet/bcmgenet.c | 19 +++++++------------ .../net/ethernet/broadcom/genet/bcmgenet.h | 2 +- 2 files changed, 8 insertions(+), 13 deletions(-) diff --git a/drivers/net/ethernet/broadcom/genet/bcmgenet.c b/drivers/net/ethernet/broadcom/genet/bcmgenet.c index cd07b3ad1d534..13cbe5828adbc 100644 --- a/drivers/net/ethernet/broadcom/genet/bcmgenet.c +++ b/drivers/net/ethernet/broadcom/genet/bcmgenet.c @@ -521,7 +521,7 @@ static int bcmgenet_set_rx_csum(struct net_device *dev, /* enable rx checksumming */ if (rx_csum_en) - rbuf_chk_ctrl |= RBUF_RXCHK_EN; + rbuf_chk_ctrl |= RBUF_RXCHK_EN | RBUF_L3_PARSE_DIS; else rbuf_chk_ctrl &= ~RBUF_RXCHK_EN; priv->desc_rxchk_en = rx_csum_en; @@ -1739,7 +1739,6 @@ static unsigned int bcmgenet_desc_rx(struct bcmgenet_rx_ring *ring, unsigned int bytes_processed = 0; unsigned int p_index, mask; unsigned int discards; - unsigned int chksum_ok = 0; /* Clear status before servicing to reduce spurious interrupts */ if (ring->index == DESC_INDEX) { @@ -1790,9 +1789,15 @@ static unsigned int bcmgenet_desc_rx(struct bcmgenet_rx_ring *ring, dmadesc_get_length_status(priv, cb->bd_addr); } else { struct status_64 *status; + __be16 rx_csum; status = (struct status_64 *)skb->data; dma_length_status = status->length_status; + rx_csum = (__force __be16)(status->rx_csum & 0xffff); + if (priv->desc_rxchk_en) { + skb->csum = (__force __wsum)ntohs(rx_csum); + skb->ip_summed = CHECKSUM_COMPLETE; + } } /* DMA flags and length are still valid no matter how @@ -1835,18 +1840,12 @@ static unsigned int bcmgenet_desc_rx(struct bcmgenet_rx_ring *ring, goto next; } /* error packet */ - chksum_ok = (dma_flag & priv->dma_rx_chk_bit) && - priv->desc_rxchk_en; - skb_put(skb, len); if (priv->desc_64b_en) { skb_pull(skb, 64); len -= 64; } - if (likely(chksum_ok)) - skb->ip_summed = CHECKSUM_UNNECESSARY; - /* remove hardware 2bytes added for IP alignment */ skb_pull(skb, 2); len -= 2; @@ -3322,19 +3321,15 @@ static void bcmgenet_set_hw_params(struct bcmgenet_priv *priv) if (GENET_IS_V5(priv) || GENET_IS_V4(priv)) { bcmgenet_dma_regs = bcmgenet_dma_regs_v3plus; genet_dma_ring_regs = genet_dma_ring_regs_v4; - priv->dma_rx_chk_bit = DMA_RX_CHK_V3PLUS; } else if (GENET_IS_V3(priv)) { bcmgenet_dma_regs = bcmgenet_dma_regs_v3plus; genet_dma_ring_regs = genet_dma_ring_regs_v123; - priv->dma_rx_chk_bit = DMA_RX_CHK_V3PLUS; } else if (GENET_IS_V2(priv)) { bcmgenet_dma_regs = bcmgenet_dma_regs_v2; genet_dma_ring_regs = genet_dma_ring_regs_v123; - priv->dma_rx_chk_bit = DMA_RX_CHK_V12; } else if (GENET_IS_V1(priv)) { bcmgenet_dma_regs = bcmgenet_dma_regs_v1; genet_dma_ring_regs = genet_dma_ring_regs_v123; - priv->dma_rx_chk_bit = DMA_RX_CHK_V12; } /* enum genet_version starts at 1 */ diff --git a/drivers/net/ethernet/broadcom/genet/bcmgenet.h b/drivers/net/ethernet/broadcom/genet/bcmgenet.h index a5659197598f5..d33c0d093f828 100644 --- a/drivers/net/ethernet/broadcom/genet/bcmgenet.h +++ b/drivers/net/ethernet/broadcom/genet/bcmgenet.h @@ -251,6 +251,7 @@ struct bcmgenet_mib_counters { #define RBUF_CHK_CTRL 0x14 #define RBUF_RXCHK_EN (1 << 0) #define RBUF_SKIP_FCS (1 << 4) +#define RBUF_L3_PARSE_DIS (1 << 5) #define RBUF_ENERGY_CTRL 0x9c #define RBUF_EEE_EN (1 << 0) @@ -663,7 +664,6 @@ struct bcmgenet_priv { bool desc_rxchk_en; bool crc_fwd_en; - unsigned int dma_rx_chk_bit; u32 dma_max_burst_length; u32 msg_enable; -- GitLab From f63db4efdd50a2b67b97336fde67661af8af112b Mon Sep 17 00:00:00 2001 From: Doug Berger Date: Tue, 17 Dec 2019 16:51:11 -0800 Subject: [PATCH 1127/1437] net: bcmgenet: Refactor bcmgenet_set_features() In preparation for unconditionally enabling TX and RX checksum offloads, refactor bcmgenet_set_features() a bit such that __netdev_update_features() during register_netdev() can make sure that features are correctly programmed during network device registration. Since we can now be called during register_netdev() with clocks gated, we need to temporarily turn them on/off in order to have a successful register programming. We also move the CRC forward setting read into bcmgenet_set_features() since priv->crc_fwd_en matters while turning on RX checksum offload, that way we are guaranteed they are in sync in case we ever add support for NETIF_F_RXFCS at some point in the future. Signed-off-by: Doug Berger Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller --- .../net/ethernet/broadcom/genet/bcmgenet.c | 38 +++++++++---------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/drivers/net/ethernet/broadcom/genet/bcmgenet.c b/drivers/net/ethernet/broadcom/genet/bcmgenet.c index 13cbe5828adbc..811713e3d2304 100644 --- a/drivers/net/ethernet/broadcom/genet/bcmgenet.c +++ b/drivers/net/ethernet/broadcom/genet/bcmgenet.c @@ -508,8 +508,8 @@ static int bcmgenet_set_link_ksettings(struct net_device *dev, return phy_ethtool_ksettings_set(dev->phydev, cmd); } -static int bcmgenet_set_rx_csum(struct net_device *dev, - netdev_features_t wanted) +static void bcmgenet_set_rx_csum(struct net_device *dev, + netdev_features_t wanted) { struct bcmgenet_priv *priv = netdev_priv(dev); u32 rbuf_chk_ctrl; @@ -535,12 +535,10 @@ static int bcmgenet_set_rx_csum(struct net_device *dev, rbuf_chk_ctrl &= ~RBUF_SKIP_FCS; bcmgenet_rbuf_writel(priv, rbuf_chk_ctrl, RBUF_CHK_CTRL); - - return 0; } -static int bcmgenet_set_tx_csum(struct net_device *dev, - netdev_features_t wanted) +static void bcmgenet_set_tx_csum(struct net_device *dev, + netdev_features_t wanted) { struct bcmgenet_priv *priv = netdev_priv(dev); bool desc_64b_en; @@ -563,21 +561,27 @@ static int bcmgenet_set_tx_csum(struct net_device *dev, bcmgenet_tbuf_ctrl_set(priv, tbuf_ctrl); bcmgenet_rbuf_writel(priv, rbuf_ctrl, RBUF_CTRL); - - return 0; } static int bcmgenet_set_features(struct net_device *dev, netdev_features_t features) { - netdev_features_t changed = features ^ dev->features; - netdev_features_t wanted = dev->wanted_features; - int ret = 0; + struct bcmgenet_priv *priv = netdev_priv(dev); + u32 reg; + int ret; + + ret = clk_prepare_enable(priv->clk); + if (ret) + return ret; + + /* Make sure we reflect the value of CRC_CMD_FWD */ + reg = bcmgenet_umac_readl(priv, UMAC_CMD); + priv->crc_fwd_en = !!(reg & CMD_CRC_FWD); - if (changed & NETIF_F_HW_CSUM) - ret = bcmgenet_set_tx_csum(dev, wanted); - if (changed & (NETIF_F_RXCSUM)) - ret = bcmgenet_set_rx_csum(dev, wanted); + bcmgenet_set_tx_csum(dev, features); + bcmgenet_set_rx_csum(dev, features); + + clk_disable_unprepare(priv->clk); return ret; } @@ -2880,10 +2884,6 @@ static int bcmgenet_open(struct net_device *dev) init_umac(priv); - /* Make sure we reflect the value of CRC_CMD_FWD */ - reg = bcmgenet_umac_readl(priv, UMAC_CMD); - priv->crc_fwd_en = !!(reg & CMD_CRC_FWD); - bcmgenet_set_hw_addr(priv, dev->dev_addr); if (priv->internal_phy) { -- GitLab From 206f54b66cbf6f71e9e86f50f60ffdf7f565c3b7 Mon Sep 17 00:00:00 2001 From: Doug Berger Date: Tue, 17 Dec 2019 16:51:12 -0800 Subject: [PATCH 1128/1437] net: bcmgenet: Utilize bcmgenet_set_features() during resume/open During driver resume and open, the HW may have lost its context/state, utilize bcmgenet_set_features() to make sure we do restore the correct set of features that were previously configured. Signed-off-by: Doug Berger Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/genet/bcmgenet.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/net/ethernet/broadcom/genet/bcmgenet.c b/drivers/net/ethernet/broadcom/genet/bcmgenet.c index 811713e3d2304..0e57effd5b191 100644 --- a/drivers/net/ethernet/broadcom/genet/bcmgenet.c +++ b/drivers/net/ethernet/broadcom/genet/bcmgenet.c @@ -2884,6 +2884,11 @@ static int bcmgenet_open(struct net_device *dev) init_umac(priv); + /* Apply features again in case we changed them while interface was + * down + */ + bcmgenet_set_features(dev, dev->features); + bcmgenet_set_hw_addr(priv, dev->dev_addr); if (priv->internal_phy) { @@ -3687,6 +3692,9 @@ static int bcmgenet_resume(struct device *d) genphy_config_aneg(dev->phydev); bcmgenet_mii_config(priv->dev, false); + /* Restore enabled features */ + bcmgenet_set_features(dev, dev->features); + bcmgenet_set_hw_addr(priv, dev->dev_addr); if (priv->internal_phy) { -- GitLab From ae895c49905cd99aca24d23361b72ed559b30f4f Mon Sep 17 00:00:00 2001 From: Doug Berger Date: Tue, 17 Dec 2019 16:51:13 -0800 Subject: [PATCH 1129/1437] net: bcmgenet: Turn on offloads by default We can turn on the RX/TX checksum offloads and the scatter/gather features by default and make sure that those are properly reflected back to e.g: stacked devices such as VLAN. Signed-off-by: Doug Berger Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/genet/bcmgenet.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/broadcom/genet/bcmgenet.c b/drivers/net/ethernet/broadcom/genet/bcmgenet.c index 0e57effd5b191..13e9154db2536 100644 --- a/drivers/net/ethernet/broadcom/genet/bcmgenet.c +++ b/drivers/net/ethernet/broadcom/genet/bcmgenet.c @@ -3530,9 +3530,11 @@ static int bcmgenet_probe(struct platform_device *pdev) priv->msg_enable = netif_msg_init(-1, GENET_MSG_DEFAULT); - /* Set hardware features */ - dev->hw_features |= NETIF_F_SG | NETIF_F_HIGHDMA | NETIF_F_HW_CSUM | - NETIF_F_RXCSUM; + /* Set default features */ + dev->features |= NETIF_F_SG | NETIF_F_HIGHDMA | NETIF_F_HW_CSUM | + NETIF_F_RXCSUM; + dev->hw_features |= dev->features; + dev->vlan_features |= dev->features; /* Request the WOL interrupt and advertise suspend if available */ priv->wol_irq_disabled = true; -- GitLab From e3fa85883d1566d788960c0f31866a26c898c070 Mon Sep 17 00:00:00 2001 From: Doug Berger Date: Tue, 17 Dec 2019 16:51:14 -0800 Subject: [PATCH 1130/1437] net: bcmgenet: Be drop monitor friendly while re-allocating headroom During bcmgenet_put_tx_csum() make sure we differentiate a SKB headroom re-allocation failure from the normal swap and replace path. Signed-off-by: Doug Berger Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/genet/bcmgenet.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/broadcom/genet/bcmgenet.c b/drivers/net/ethernet/broadcom/genet/bcmgenet.c index 13e9154db2536..e2bca19bf10b8 100644 --- a/drivers/net/ethernet/broadcom/genet/bcmgenet.c +++ b/drivers/net/ethernet/broadcom/genet/bcmgenet.c @@ -1499,11 +1499,12 @@ static struct sk_buff *bcmgenet_put_tx_csum(struct net_device *dev, * enough headroom for us to insert 64B status block. */ new_skb = skb_realloc_headroom(skb, sizeof(*status)); - dev_kfree_skb(skb); if (!new_skb) { + dev_kfree_skb_any(skb); dev->stats.tx_dropped++; return NULL; } + dev_consume_skb_any(skb); skb = new_skb; } -- GitLab From f1af17c0b7a51357699bba7c8cba1276c9486907 Mon Sep 17 00:00:00 2001 From: Doug Berger Date: Tue, 17 Dec 2019 16:51:15 -0800 Subject: [PATCH 1131/1437] net: bcmgenet: Add software counters to track reallocations When inserting the TSB, keep track of how many times we had to do it and if there was a failure in doing so, this helps profile the driver for possibly incorrect headroom settings. Signed-off-by: Doug Berger Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/genet/bcmgenet.c | 6 ++++++ drivers/net/ethernet/broadcom/genet/bcmgenet.h | 2 ++ 2 files changed, 8 insertions(+) diff --git a/drivers/net/ethernet/broadcom/genet/bcmgenet.c b/drivers/net/ethernet/broadcom/genet/bcmgenet.c index e2bca19bf10b8..3ee7917e3fc0c 100644 --- a/drivers/net/ethernet/broadcom/genet/bcmgenet.c +++ b/drivers/net/ethernet/broadcom/genet/bcmgenet.c @@ -861,6 +861,9 @@ static const struct bcmgenet_stats bcmgenet_gstrings_stats[] = { STAT_GENET_SOFT_MIB("alloc_rx_buff_failed", mib.alloc_rx_buff_failed), STAT_GENET_SOFT_MIB("rx_dma_failed", mib.rx_dma_failed), STAT_GENET_SOFT_MIB("tx_dma_failed", mib.tx_dma_failed), + STAT_GENET_SOFT_MIB("tx_realloc_tsb", mib.tx_realloc_tsb), + STAT_GENET_SOFT_MIB("tx_realloc_tsb_failed", + mib.tx_realloc_tsb_failed), /* Per TX queues */ STAT_GENET_Q(0), STAT_GENET_Q(1), @@ -1487,6 +1490,7 @@ static void bcmgenet_tx_reclaim_all(struct net_device *dev) static struct sk_buff *bcmgenet_put_tx_csum(struct net_device *dev, struct sk_buff *skb) { + struct bcmgenet_priv *priv = netdev_priv(dev); struct status_64 *status = NULL; struct sk_buff *new_skb; u16 offset; @@ -1501,11 +1505,13 @@ static struct sk_buff *bcmgenet_put_tx_csum(struct net_device *dev, new_skb = skb_realloc_headroom(skb, sizeof(*status)); if (!new_skb) { dev_kfree_skb_any(skb); + priv->mib.tx_realloc_tsb_failed++; dev->stats.tx_dropped++; return NULL; } dev_consume_skb_any(skb); skb = new_skb; + priv->mib.tx_realloc_tsb++; } skb_push(skb, sizeof(*status)); diff --git a/drivers/net/ethernet/broadcom/genet/bcmgenet.h b/drivers/net/ethernet/broadcom/genet/bcmgenet.h index d33c0d093f828..61a6fe9f4cecc 100644 --- a/drivers/net/ethernet/broadcom/genet/bcmgenet.h +++ b/drivers/net/ethernet/broadcom/genet/bcmgenet.h @@ -144,6 +144,8 @@ struct bcmgenet_mib_counters { u32 alloc_rx_buff_failed; u32 rx_dma_failed; u32 tx_dma_failed; + u32 tx_realloc_tsb; + u32 tx_realloc_tsb_failed; }; #define UMAC_HD_BKP_CTRL 0x004 -- GitLab From 5080832627b65e3772a35d1dced68c64e2b24442 Mon Sep 17 00:00:00 2001 From: Nathan Chancellor Date: Tue, 17 Dec 2019 18:21:52 -0700 Subject: [PATCH 1132/1437] NFC: pn544: Adjust indentation in pn544_hci_check_presence Clang warns ../drivers/nfc/pn544/pn544.c:696:4: warning: misleading indentation; statement is not part of the previous 'if' [-Wmisleading-indentation] return nfc_hci_send_cmd(hdev, NFC_HCI_RF_READER_A_GATE, ^ ../drivers/nfc/pn544/pn544.c:692:3: note: previous statement is here if (target->nfcid1_len != 4 && target->nfcid1_len != 7 && ^ 1 warning generated. This warning occurs because there is a space after the tab on this line. Remove it so that the indentation is consistent with the Linux kernel coding style and clang no longer warns. Fixes: da052850b911 ("NFC: Add pn544 presence check for different targets") Link: https://github.com/ClangBuiltLinux/linux/issues/814 Signed-off-by: Nathan Chancellor Signed-off-by: David S. Miller --- drivers/nfc/pn544/pn544.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/nfc/pn544/pn544.c b/drivers/nfc/pn544/pn544.c index cda996f6954e0..2b83156efe3ff 100644 --- a/drivers/nfc/pn544/pn544.c +++ b/drivers/nfc/pn544/pn544.c @@ -693,7 +693,7 @@ static int pn544_hci_check_presence(struct nfc_hci_dev *hdev, target->nfcid1_len != 10) return -EOPNOTSUPP; - return nfc_hci_send_cmd(hdev, NFC_HCI_RF_READER_A_GATE, + return nfc_hci_send_cmd(hdev, NFC_HCI_RF_READER_A_GATE, PN544_RF_READER_CMD_ACTIVATE_NEXT, target->nfcid1, target->nfcid1_len, NULL); } else if (target->supported_protocols & (NFC_PROTO_JEWEL_MASK | -- GitLab From 0536b85239b8440735cdd910aae0eb076ebbb439 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20T=C3=B6pel?= Date: Thu, 19 Dec 2019 07:09:59 +0100 Subject: [PATCH 1133/1437] xdp: Simplify devmap cleanup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit After the RCU flavor consolidation [1], call_rcu() and synchronize_rcu() waits for preempt-disable regions (NAPI) in addition to the read-side critical sections. As a result of this, the cleanup code in devmap can be simplified * There is no longer a need to flush in __dev_map_entry_free, since we know that this has been done when the call_rcu() callback is triggered. * When freeing the map, there is no need to explicitly wait for a flush. It's guaranteed to be done after the synchronize_rcu() call in dev_map_free(). The rcu_barrier() is still needed, so that the map is not freed prior the elements. [1] https://lwn.net/Articles/777036/ Signed-off-by: Björn Töpel Signed-off-by: Alexei Starovoitov Acked-by: Toke Høiland-Jørgensen Link: https://lore.kernel.org/bpf/20191219061006.21980-2-bjorn.topel@gmail.com --- kernel/bpf/devmap.c | 43 +++++-------------------------------------- 1 file changed, 5 insertions(+), 38 deletions(-) diff --git a/kernel/bpf/devmap.c b/kernel/bpf/devmap.c index 3d3d61b5985b0..b7595de6a91af 100644 --- a/kernel/bpf/devmap.c +++ b/kernel/bpf/devmap.c @@ -201,7 +201,7 @@ static struct bpf_map *dev_map_alloc(union bpf_attr *attr) static void dev_map_free(struct bpf_map *map) { struct bpf_dtab *dtab = container_of(map, struct bpf_dtab, map); - int i, cpu; + int i; /* At this point bpf_prog->aux->refcnt == 0 and this map->refcnt == 0, * so the programs (can be more than one that used this map) were @@ -221,18 +221,6 @@ static void dev_map_free(struct bpf_map *map) /* Make sure prior __dev_map_entry_free() have completed. */ rcu_barrier(); - /* To ensure all pending flush operations have completed wait for flush - * list to empty on _all_ cpus. - * Because the above synchronize_rcu() ensures the map is disconnected - * from the program we can assume no new items will be added. - */ - for_each_online_cpu(cpu) { - struct list_head *flush_list = per_cpu_ptr(dtab->flush_list, cpu); - - while (!list_empty(flush_list)) - cond_resched(); - } - if (dtab->map.map_type == BPF_MAP_TYPE_DEVMAP_HASH) { for (i = 0; i < dtab->n_buckets; i++) { struct bpf_dtab_netdev *dev; @@ -345,8 +333,7 @@ static int dev_map_hash_get_next_key(struct bpf_map *map, void *key, return -ENOENT; } -static int bq_xmit_all(struct xdp_bulk_queue *bq, u32 flags, - bool in_napi_ctx) +static int bq_xmit_all(struct xdp_bulk_queue *bq, u32 flags) { struct bpf_dtab_netdev *obj = bq->obj; struct net_device *dev = obj->dev; @@ -384,11 +371,7 @@ error: for (i = 0; i < bq->count; i++) { struct xdp_frame *xdpf = bq->q[i]; - /* RX path under NAPI protection, can return frames faster */ - if (likely(in_napi_ctx)) - xdp_return_frame_rx_napi(xdpf); - else - xdp_return_frame(xdpf); + xdp_return_frame_rx_napi(xdpf); drops++; } goto out; @@ -409,7 +392,7 @@ void __dev_map_flush(struct bpf_map *map) rcu_read_lock(); list_for_each_entry_safe(bq, tmp, flush_list, flush_node) - bq_xmit_all(bq, XDP_XMIT_FLUSH, true); + bq_xmit_all(bq, XDP_XMIT_FLUSH); rcu_read_unlock(); } @@ -440,7 +423,7 @@ static int bq_enqueue(struct bpf_dtab_netdev *obj, struct xdp_frame *xdpf, struct xdp_bulk_queue *bq = this_cpu_ptr(obj->bulkq); if (unlikely(bq->count == DEV_MAP_BULK_SIZE)) - bq_xmit_all(bq, 0, true); + bq_xmit_all(bq, 0); /* Ingress dev_rx will be the same for all xdp_frame's in * bulk_queue, because bq stored per-CPU and must be flushed @@ -509,27 +492,11 @@ static void *dev_map_hash_lookup_elem(struct bpf_map *map, void *key) return dev ? &dev->ifindex : NULL; } -static void dev_map_flush_old(struct bpf_dtab_netdev *dev) -{ - if (dev->dev->netdev_ops->ndo_xdp_xmit) { - struct xdp_bulk_queue *bq; - int cpu; - - rcu_read_lock(); - for_each_online_cpu(cpu) { - bq = per_cpu_ptr(dev->bulkq, cpu); - bq_xmit_all(bq, XDP_XMIT_FLUSH, false); - } - rcu_read_unlock(); - } -} - static void __dev_map_entry_free(struct rcu_head *rcu) { struct bpf_dtab_netdev *dev; dev = container_of(rcu, struct bpf_dtab_netdev, rcu); - dev_map_flush_old(dev); free_percpu(dev->bulkq); dev_put(dev->dev); kfree(dev); -- GitLab From 4bc188c7f23a5a308d7f15dda1b6a286d74e8954 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20T=C3=B6pel?= Date: Thu, 19 Dec 2019 07:10:00 +0100 Subject: [PATCH 1134/1437] xdp: Simplify cpumap cleanup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit After the RCU flavor consolidation [1], call_rcu() and synchronize_rcu() waits for preempt-disable regions (NAPI) in addition to the read-side critical sections. As a result of this, the cleanup code in cpumap can be simplified * There is no longer a need to flush in __cpu_map_entry_free, since we know that this has been done when the call_rcu() callback is triggered. * When freeing the map, there is no need to explicitly wait for a flush. It's guaranteed to be done after the synchronize_rcu() call in cpu_map_free(). [1] https://lwn.net/Articles/777036/ Signed-off-by: Björn Töpel Signed-off-by: Alexei Starovoitov Acked-by: Toke Høiland-Jørgensen Link: https://lore.kernel.org/bpf/20191219061006.21980-3-bjorn.topel@gmail.com --- kernel/bpf/cpumap.c | 34 +++++----------------------------- 1 file changed, 5 insertions(+), 29 deletions(-) diff --git a/kernel/bpf/cpumap.c b/kernel/bpf/cpumap.c index ef49e17ae47cb..04c8eb11cd90b 100644 --- a/kernel/bpf/cpumap.c +++ b/kernel/bpf/cpumap.c @@ -75,7 +75,7 @@ struct bpf_cpu_map { struct list_head __percpu *flush_list; }; -static int bq_flush_to_queue(struct xdp_bulk_queue *bq, bool in_napi_ctx); +static int bq_flush_to_queue(struct xdp_bulk_queue *bq); static struct bpf_map *cpu_map_alloc(union bpf_attr *attr) { @@ -399,7 +399,6 @@ free_rcu: static void __cpu_map_entry_free(struct rcu_head *rcu) { struct bpf_cpu_map_entry *rcpu; - int cpu; /* This cpu_map_entry have been disconnected from map and one * RCU graze-period have elapsed. Thus, XDP cannot queue any @@ -408,13 +407,6 @@ static void __cpu_map_entry_free(struct rcu_head *rcu) */ rcpu = container_of(rcu, struct bpf_cpu_map_entry, rcu); - /* Flush remaining packets in percpu bulkq */ - for_each_online_cpu(cpu) { - struct xdp_bulk_queue *bq = per_cpu_ptr(rcpu->bulkq, cpu); - - /* No concurrent bq_enqueue can run at this point */ - bq_flush_to_queue(bq, false); - } free_percpu(rcpu->bulkq); /* Cannot kthread_stop() here, last put free rcpu resources */ put_cpu_map_entry(rcpu); @@ -507,7 +499,6 @@ static int cpu_map_update_elem(struct bpf_map *map, void *key, void *value, static void cpu_map_free(struct bpf_map *map) { struct bpf_cpu_map *cmap = container_of(map, struct bpf_cpu_map, map); - int cpu; u32 i; /* At this point bpf_prog->aux->refcnt == 0 and this map->refcnt == 0, @@ -522,18 +513,6 @@ static void cpu_map_free(struct bpf_map *map) bpf_clear_redirect_map(map); synchronize_rcu(); - /* To ensure all pending flush operations have completed wait for flush - * list be empty on _all_ cpus. Because the above synchronize_rcu() - * ensures the map is disconnected from the program we can assume no new - * items will be added to the list. - */ - for_each_online_cpu(cpu) { - struct list_head *flush_list = per_cpu_ptr(cmap->flush_list, cpu); - - while (!list_empty(flush_list)) - cond_resched(); - } - /* For cpu_map the remote CPUs can still be using the entries * (struct bpf_cpu_map_entry). */ @@ -599,7 +578,7 @@ const struct bpf_map_ops cpu_map_ops = { .map_check_btf = map_check_no_btf, }; -static int bq_flush_to_queue(struct xdp_bulk_queue *bq, bool in_napi_ctx) +static int bq_flush_to_queue(struct xdp_bulk_queue *bq) { struct bpf_cpu_map_entry *rcpu = bq->obj; unsigned int processed = 0, drops = 0; @@ -620,10 +599,7 @@ static int bq_flush_to_queue(struct xdp_bulk_queue *bq, bool in_napi_ctx) err = __ptr_ring_produce(q, xdpf); if (err) { drops++; - if (likely(in_napi_ctx)) - xdp_return_frame_rx_napi(xdpf); - else - xdp_return_frame(xdpf); + xdp_return_frame_rx_napi(xdpf); } processed++; } @@ -646,7 +622,7 @@ static int bq_enqueue(struct bpf_cpu_map_entry *rcpu, struct xdp_frame *xdpf) struct xdp_bulk_queue *bq = this_cpu_ptr(rcpu->bulkq); if (unlikely(bq->count == CPU_MAP_BULK_SIZE)) - bq_flush_to_queue(bq, true); + bq_flush_to_queue(bq); /* Notice, xdp_buff/page MUST be queued here, long enough for * driver to code invoking us to finished, due to driver @@ -688,7 +664,7 @@ void __cpu_map_flush(struct bpf_map *map) struct xdp_bulk_queue *bq, *tmp; list_for_each_entry_safe(bq, tmp, flush_list, flush_node) { - bq_flush_to_queue(bq, true); + bq_flush_to_queue(bq); /* If already running, costs spin_lock_irqsave + smb_mb */ wake_up_process(bq->obj->kthread); -- GitLab From fb5aacdf3603ccbafe1da74eecd132eb4a31e53f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20T=C3=B6pel?= Date: Thu, 19 Dec 2019 07:10:01 +0100 Subject: [PATCH 1135/1437] xdp: Fix graze->grace type-o in cpumap comments MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Simple spelling fix. Signed-off-by: Björn Töpel Signed-off-by: Alexei Starovoitov Acked-by: Toke Høiland-Jørgensen Link: https://lore.kernel.org/bpf/20191219061006.21980-4-bjorn.topel@gmail.com --- kernel/bpf/cpumap.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/kernel/bpf/cpumap.c b/kernel/bpf/cpumap.c index 04c8eb11cd90b..f9deed659798e 100644 --- a/kernel/bpf/cpumap.c +++ b/kernel/bpf/cpumap.c @@ -401,7 +401,7 @@ static void __cpu_map_entry_free(struct rcu_head *rcu) struct bpf_cpu_map_entry *rcpu; /* This cpu_map_entry have been disconnected from map and one - * RCU graze-period have elapsed. Thus, XDP cannot queue any + * RCU grace-period have elapsed. Thus, XDP cannot queue any * new packets and cannot change/set flush_needed that can * find this entry. */ @@ -428,7 +428,7 @@ static void __cpu_map_entry_free(struct rcu_head *rcu) * percpu bulkq to queue. Due to caller map_delete_elem() disable * preemption, cannot call kthread_stop() to make sure queue is empty. * Instead a work_queue is started for stopping kthread, - * cpu_map_kthread_stop, which waits for an RCU graze period before + * cpu_map_kthread_stop, which waits for an RCU grace period before * stopping kthread, emptying the queue. */ static void __cpu_map_entry_replace(struct bpf_cpu_map *cmap, @@ -523,7 +523,7 @@ static void cpu_map_free(struct bpf_map *map) if (!rcpu) continue; - /* bq flush and cleanup happens after RCU graze-period */ + /* bq flush and cleanup happens after RCU grace-period */ __cpu_map_entry_replace(cmap, i, NULL); /* call_rcu */ } free_percpu(cmap->flush_list); -- GitLab From e312b9e706ed6d94f6cc9088fcd9fbd81de4525c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20T=C3=B6pel?= Date: Thu, 19 Dec 2019 07:10:02 +0100 Subject: [PATCH 1136/1437] xsk: Make xskmap flush_list common for all map instances MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The xskmap flush list is used to track entries that need to flushed from via the xdp_do_flush_map() function. This list used to be per-map, but there is really no reason for that. Instead make the flush list global for all xskmaps, which simplifies __xsk_map_flush() and xsk_map_alloc(). Signed-off-by: Björn Töpel Signed-off-by: Alexei Starovoitov Acked-by: Toke Høiland-Jørgensen Link: https://lore.kernel.org/bpf/20191219061006.21980-5-bjorn.topel@gmail.com --- include/net/xdp_sock.h | 11 ++++------- kernel/bpf/xskmap.c | 18 +++--------------- net/core/filter.c | 9 ++++----- net/xdp/xsk.c | 17 +++++++++-------- 4 files changed, 20 insertions(+), 35 deletions(-) diff --git a/include/net/xdp_sock.h b/include/net/xdp_sock.h index e3780e4b74e10..48594740d67c5 100644 --- a/include/net/xdp_sock.h +++ b/include/net/xdp_sock.h @@ -72,7 +72,6 @@ struct xdp_umem { struct xsk_map { struct bpf_map map; - struct list_head __percpu *flush_list; spinlock_t lock; /* Synchronize map updates */ struct xdp_sock *xsk_map[]; }; @@ -139,9 +138,8 @@ void xsk_map_try_sock_delete(struct xsk_map *map, struct xdp_sock *xs, struct xdp_sock **map_entry); int xsk_map_inc(struct xsk_map *map); void xsk_map_put(struct xsk_map *map); -int __xsk_map_redirect(struct bpf_map *map, struct xdp_buff *xdp, - struct xdp_sock *xs); -void __xsk_map_flush(struct bpf_map *map); +int __xsk_map_redirect(struct xdp_sock *xs, struct xdp_buff *xdp); +void __xsk_map_flush(void); static inline struct xdp_sock *__xsk_map_lookup_elem(struct bpf_map *map, u32 key) @@ -369,13 +367,12 @@ static inline u64 xsk_umem_adjust_offset(struct xdp_umem *umem, u64 handle, return 0; } -static inline int __xsk_map_redirect(struct bpf_map *map, struct xdp_buff *xdp, - struct xdp_sock *xs) +static inline int __xsk_map_redirect(struct xdp_sock *xs, struct xdp_buff *xdp) { return -EOPNOTSUPP; } -static inline void __xsk_map_flush(struct bpf_map *map) +static inline void __xsk_map_flush(void) { } diff --git a/kernel/bpf/xskmap.c b/kernel/bpf/xskmap.c index 90c4fce1c9814..2cc5c8f4c8007 100644 --- a/kernel/bpf/xskmap.c +++ b/kernel/bpf/xskmap.c @@ -72,9 +72,9 @@ static void xsk_map_sock_delete(struct xdp_sock *xs, static struct bpf_map *xsk_map_alloc(union bpf_attr *attr) { struct bpf_map_memory mem; - int cpu, err, numa_node; + int err, numa_node; struct xsk_map *m; - u64 cost, size; + u64 size; if (!capable(CAP_NET_ADMIN)) return ERR_PTR(-EPERM); @@ -86,9 +86,8 @@ static struct bpf_map *xsk_map_alloc(union bpf_attr *attr) numa_node = bpf_map_attr_numa_node(attr); size = struct_size(m, xsk_map, attr->max_entries); - cost = size + array_size(sizeof(*m->flush_list), num_possible_cpus()); - err = bpf_map_charge_init(&mem, cost); + err = bpf_map_charge_init(&mem, size); if (err < 0) return ERR_PTR(err); @@ -102,16 +101,6 @@ static struct bpf_map *xsk_map_alloc(union bpf_attr *attr) bpf_map_charge_move(&m->map.memory, &mem); spin_lock_init(&m->lock); - m->flush_list = alloc_percpu(struct list_head); - if (!m->flush_list) { - bpf_map_charge_finish(&m->map.memory); - bpf_map_area_free(m); - return ERR_PTR(-ENOMEM); - } - - for_each_possible_cpu(cpu) - INIT_LIST_HEAD(per_cpu_ptr(m->flush_list, cpu)); - return &m->map; } @@ -121,7 +110,6 @@ static void xsk_map_free(struct bpf_map *map) bpf_clear_redirect_map(map); synchronize_net(); - free_percpu(m->flush_list); bpf_map_area_free(m); } diff --git a/net/core/filter.c b/net/core/filter.c index a411f7835deef..c51678c473c51 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -3511,8 +3511,7 @@ err: static int __bpf_tx_xdp_map(struct net_device *dev_rx, void *fwd, struct bpf_map *map, - struct xdp_buff *xdp, - u32 index) + struct xdp_buff *xdp) { int err; @@ -3537,7 +3536,7 @@ static int __bpf_tx_xdp_map(struct net_device *dev_rx, void *fwd, case BPF_MAP_TYPE_XSKMAP: { struct xdp_sock *xs = fwd; - err = __xsk_map_redirect(map, xdp, xs); + err = __xsk_map_redirect(xs, xdp); return err; } default: @@ -3562,7 +3561,7 @@ void xdp_do_flush_map(void) __cpu_map_flush(map); break; case BPF_MAP_TYPE_XSKMAP: - __xsk_map_flush(map); + __xsk_map_flush(); break; default: break; @@ -3619,7 +3618,7 @@ static int xdp_do_redirect_map(struct net_device *dev, struct xdp_buff *xdp, if (ri->map_to_flush && unlikely(ri->map_to_flush != map)) xdp_do_flush_map(); - err = __bpf_tx_xdp_map(dev, fwd, map, xdp, index); + err = __bpf_tx_xdp_map(dev, fwd, map, xdp); if (unlikely(err)) goto err; diff --git a/net/xdp/xsk.c b/net/xdp/xsk.c index 956793893c9de..e45c27f5cfca0 100644 --- a/net/xdp/xsk.c +++ b/net/xdp/xsk.c @@ -31,6 +31,8 @@ #define TX_BATCH_SIZE 16 +static DEFINE_PER_CPU(struct list_head, xskmap_flush_list); + bool xsk_is_setup_for_bpf_map(struct xdp_sock *xs) { return READ_ONCE(xs->rx) && READ_ONCE(xs->umem) && @@ -264,11 +266,9 @@ out_unlock: return err; } -int __xsk_map_redirect(struct bpf_map *map, struct xdp_buff *xdp, - struct xdp_sock *xs) +int __xsk_map_redirect(struct xdp_sock *xs, struct xdp_buff *xdp) { - struct xsk_map *m = container_of(map, struct xsk_map, map); - struct list_head *flush_list = this_cpu_ptr(m->flush_list); + struct list_head *flush_list = this_cpu_ptr(&xskmap_flush_list); int err; err = xsk_rcv(xs, xdp); @@ -281,10 +281,9 @@ int __xsk_map_redirect(struct bpf_map *map, struct xdp_buff *xdp, return 0; } -void __xsk_map_flush(struct bpf_map *map) +void __xsk_map_flush(void) { - struct xsk_map *m = container_of(map, struct xsk_map, map); - struct list_head *flush_list = this_cpu_ptr(m->flush_list); + struct list_head *flush_list = this_cpu_ptr(&xskmap_flush_list); struct xdp_sock *xs, *tmp; list_for_each_entry_safe(xs, tmp, flush_list, flush_node) { @@ -1177,7 +1176,7 @@ static struct pernet_operations xsk_net_ops = { static int __init xsk_init(void) { - int err; + int err, cpu; err = proto_register(&xsk_proto, 0 /* no slab */); if (err) @@ -1195,6 +1194,8 @@ static int __init xsk_init(void) if (err) goto out_pernet; + for_each_possible_cpu(cpu) + INIT_LIST_HEAD(&per_cpu(xskmap_flush_list, cpu)); return 0; out_pernet: -- GitLab From 96360004b8628541f5d05a845ea213267db0b1a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20T=C3=B6pel?= Date: Thu, 19 Dec 2019 07:10:03 +0100 Subject: [PATCH 1137/1437] xdp: Make devmap flush_list common for all map instances MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The devmap flush list is used to track entries that need to flushed from via the xdp_do_flush_map() function. This list used to be per-map, but there is really no reason for that. Instead make the flush list global for all devmaps, which simplifies __dev_map_flush() and dev_map_init_map(). Signed-off-by: Björn Töpel Signed-off-by: Alexei Starovoitov Acked-by: Toke Høiland-Jørgensen Link: https://lore.kernel.org/bpf/20191219061006.21980-6-bjorn.topel@gmail.com --- include/linux/bpf.h | 4 ++-- kernel/bpf/devmap.c | 35 +++++++++++++---------------------- net/core/filter.c | 2 +- 3 files changed, 16 insertions(+), 25 deletions(-) diff --git a/include/linux/bpf.h b/include/linux/bpf.h index d467983e61bb2..31191804ca098 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -959,7 +959,7 @@ struct sk_buff; struct bpf_dtab_netdev *__dev_map_lookup_elem(struct bpf_map *map, u32 key); struct bpf_dtab_netdev *__dev_map_hash_lookup_elem(struct bpf_map *map, u32 key); -void __dev_map_flush(struct bpf_map *map); +void __dev_map_flush(void); int dev_map_enqueue(struct bpf_dtab_netdev *dst, struct xdp_buff *xdp, struct net_device *dev_rx); int dev_map_generic_redirect(struct bpf_dtab_netdev *dst, struct sk_buff *skb, @@ -1068,7 +1068,7 @@ static inline struct net_device *__dev_map_hash_lookup_elem(struct bpf_map *map return NULL; } -static inline void __dev_map_flush(struct bpf_map *map) +static inline void __dev_map_flush(void) { } diff --git a/kernel/bpf/devmap.c b/kernel/bpf/devmap.c index b7595de6a91af..da9c832fc5c84 100644 --- a/kernel/bpf/devmap.c +++ b/kernel/bpf/devmap.c @@ -75,7 +75,6 @@ struct bpf_dtab_netdev { struct bpf_dtab { struct bpf_map map; struct bpf_dtab_netdev **netdev_map; /* DEVMAP type only */ - struct list_head __percpu *flush_list; struct list_head list; /* these are only used for DEVMAP_HASH type maps */ @@ -85,6 +84,7 @@ struct bpf_dtab { u32 n_buckets; }; +static DEFINE_PER_CPU(struct list_head, dev_map_flush_list); static DEFINE_SPINLOCK(dev_map_lock); static LIST_HEAD(dev_map_list); @@ -109,8 +109,8 @@ static inline struct hlist_head *dev_map_index_hash(struct bpf_dtab *dtab, static int dev_map_init_map(struct bpf_dtab *dtab, union bpf_attr *attr) { - int err, cpu; - u64 cost; + u64 cost = 0; + int err; /* check sanity of attributes */ if (attr->max_entries == 0 || attr->key_size != 4 || @@ -125,9 +125,6 @@ static int dev_map_init_map(struct bpf_dtab *dtab, union bpf_attr *attr) bpf_map_init_from_attr(&dtab->map, attr); - /* make sure page count doesn't overflow */ - cost = (u64) sizeof(struct list_head) * num_possible_cpus(); - if (attr->map_type == BPF_MAP_TYPE_DEVMAP_HASH) { dtab->n_buckets = roundup_pow_of_two(dtab->map.max_entries); @@ -143,17 +140,10 @@ static int dev_map_init_map(struct bpf_dtab *dtab, union bpf_attr *attr) if (err) return -EINVAL; - dtab->flush_list = alloc_percpu(struct list_head); - if (!dtab->flush_list) - goto free_charge; - - for_each_possible_cpu(cpu) - INIT_LIST_HEAD(per_cpu_ptr(dtab->flush_list, cpu)); - if (attr->map_type == BPF_MAP_TYPE_DEVMAP_HASH) { dtab->dev_index_head = dev_map_create_hash(dtab->n_buckets); if (!dtab->dev_index_head) - goto free_percpu; + goto free_charge; spin_lock_init(&dtab->index_lock); } else { @@ -161,13 +151,11 @@ static int dev_map_init_map(struct bpf_dtab *dtab, union bpf_attr *attr) sizeof(struct bpf_dtab_netdev *), dtab->map.numa_node); if (!dtab->netdev_map) - goto free_percpu; + goto free_charge; } return 0; -free_percpu: - free_percpu(dtab->flush_list); free_charge: bpf_map_charge_finish(&dtab->map.memory); return -ENOMEM; @@ -254,7 +242,6 @@ static void dev_map_free(struct bpf_map *map) bpf_map_area_free(dtab->netdev_map); } - free_percpu(dtab->flush_list); kfree(dtab); } @@ -384,10 +371,9 @@ error: * net device can be torn down. On devmap tear down we ensure the flush list * is empty before completing to ensure all flush operations have completed. */ -void __dev_map_flush(struct bpf_map *map) +void __dev_map_flush(void) { - struct bpf_dtab *dtab = container_of(map, struct bpf_dtab, map); - struct list_head *flush_list = this_cpu_ptr(dtab->flush_list); + struct list_head *flush_list = this_cpu_ptr(&dev_map_flush_list); struct xdp_bulk_queue *bq, *tmp; rcu_read_lock(); @@ -419,7 +405,7 @@ static int bq_enqueue(struct bpf_dtab_netdev *obj, struct xdp_frame *xdpf, struct net_device *dev_rx) { - struct list_head *flush_list = this_cpu_ptr(obj->dtab->flush_list); + struct list_head *flush_list = this_cpu_ptr(&dev_map_flush_list); struct xdp_bulk_queue *bq = this_cpu_ptr(obj->bulkq); if (unlikely(bq->count == DEV_MAP_BULK_SIZE)) @@ -777,10 +763,15 @@ static struct notifier_block dev_map_notifier = { static int __init dev_map_init(void) { + int cpu; + /* Assure tracepoint shadow struct _bpf_dtab_netdev is in sync */ BUILD_BUG_ON(offsetof(struct bpf_dtab_netdev, dev) != offsetof(struct _bpf_dtab_netdev, dev)); register_netdevice_notifier(&dev_map_notifier); + + for_each_possible_cpu(cpu) + INIT_LIST_HEAD(&per_cpu(dev_map_flush_list, cpu)); return 0; } diff --git a/net/core/filter.c b/net/core/filter.c index c51678c473c51..b7570cb849025 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -3555,7 +3555,7 @@ void xdp_do_flush_map(void) switch (map->map_type) { case BPF_MAP_TYPE_DEVMAP: case BPF_MAP_TYPE_DEVMAP_HASH: - __dev_map_flush(map); + __dev_map_flush(); break; case BPF_MAP_TYPE_CPUMAP: __cpu_map_flush(map); -- GitLab From cdfafe98cabefeedbbc65af5c191c59745c03298 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20T=C3=B6pel?= Date: Thu, 19 Dec 2019 07:10:04 +0100 Subject: [PATCH 1138/1437] xdp: Make cpumap flush_list common for all map instances MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The cpumap flush list is used to track entries that need to flushed from via the xdp_do_flush_map() function. This list used to be per-map, but there is really no reason for that. Instead make the flush list global for all devmaps, which simplifies __cpu_map_flush() and cpu_map_alloc(). Signed-off-by: Björn Töpel Signed-off-by: Alexei Starovoitov Acked-by: Toke Høiland-Jørgensen Link: https://lore.kernel.org/bpf/20191219061006.21980-7-bjorn.topel@gmail.com --- include/linux/bpf.h | 4 ++-- kernel/bpf/cpumap.c | 36 ++++++++++++++++++------------------ net/core/filter.c | 2 +- 3 files changed, 21 insertions(+), 21 deletions(-) diff --git a/include/linux/bpf.h b/include/linux/bpf.h index 31191804ca098..8f3e00c84f39f 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -966,7 +966,7 @@ int dev_map_generic_redirect(struct bpf_dtab_netdev *dst, struct sk_buff *skb, struct bpf_prog *xdp_prog); struct bpf_cpu_map_entry *__cpu_map_lookup_elem(struct bpf_map *map, u32 key); -void __cpu_map_flush(struct bpf_map *map); +void __cpu_map_flush(void); int cpu_map_enqueue(struct bpf_cpu_map_entry *rcpu, struct xdp_buff *xdp, struct net_device *dev_rx); @@ -1097,7 +1097,7 @@ struct bpf_cpu_map_entry *__cpu_map_lookup_elem(struct bpf_map *map, u32 key) return NULL; } -static inline void __cpu_map_flush(struct bpf_map *map) +static inline void __cpu_map_flush(void) { } diff --git a/kernel/bpf/cpumap.c b/kernel/bpf/cpumap.c index f9deed659798e..70f71b154fa57 100644 --- a/kernel/bpf/cpumap.c +++ b/kernel/bpf/cpumap.c @@ -72,17 +72,18 @@ struct bpf_cpu_map { struct bpf_map map; /* Below members specific for map type */ struct bpf_cpu_map_entry **cpu_map; - struct list_head __percpu *flush_list; }; +static DEFINE_PER_CPU(struct list_head, cpu_map_flush_list); + static int bq_flush_to_queue(struct xdp_bulk_queue *bq); static struct bpf_map *cpu_map_alloc(union bpf_attr *attr) { struct bpf_cpu_map *cmap; int err = -ENOMEM; - int ret, cpu; u64 cost; + int ret; if (!capable(CAP_SYS_ADMIN)) return ERR_PTR(-EPERM); @@ -106,7 +107,6 @@ static struct bpf_map *cpu_map_alloc(union bpf_attr *attr) /* make sure page count doesn't overflow */ cost = (u64) cmap->map.max_entries * sizeof(struct bpf_cpu_map_entry *); - cost += sizeof(struct list_head) * num_possible_cpus(); /* Notice returns -EPERM on if map size is larger than memlock limit */ ret = bpf_map_charge_init(&cmap->map.memory, cost); @@ -115,23 +115,14 @@ static struct bpf_map *cpu_map_alloc(union bpf_attr *attr) goto free_cmap; } - cmap->flush_list = alloc_percpu(struct list_head); - if (!cmap->flush_list) - goto free_charge; - - for_each_possible_cpu(cpu) - INIT_LIST_HEAD(per_cpu_ptr(cmap->flush_list, cpu)); - /* Alloc array for possible remote "destination" CPUs */ cmap->cpu_map = bpf_map_area_alloc(cmap->map.max_entries * sizeof(struct bpf_cpu_map_entry *), cmap->map.numa_node); if (!cmap->cpu_map) - goto free_percpu; + goto free_charge; return &cmap->map; -free_percpu: - free_percpu(cmap->flush_list); free_charge: bpf_map_charge_finish(&cmap->map.memory); free_cmap: @@ -526,7 +517,6 @@ static void cpu_map_free(struct bpf_map *map) /* bq flush and cleanup happens after RCU grace-period */ __cpu_map_entry_replace(cmap, i, NULL); /* call_rcu */ } - free_percpu(cmap->flush_list); bpf_map_area_free(cmap->cpu_map); kfree(cmap); } @@ -618,7 +608,7 @@ static int bq_flush_to_queue(struct xdp_bulk_queue *bq) */ static int bq_enqueue(struct bpf_cpu_map_entry *rcpu, struct xdp_frame *xdpf) { - struct list_head *flush_list = this_cpu_ptr(rcpu->cmap->flush_list); + struct list_head *flush_list = this_cpu_ptr(&cpu_map_flush_list); struct xdp_bulk_queue *bq = this_cpu_ptr(rcpu->bulkq); if (unlikely(bq->count == CPU_MAP_BULK_SIZE)) @@ -657,10 +647,9 @@ int cpu_map_enqueue(struct bpf_cpu_map_entry *rcpu, struct xdp_buff *xdp, return 0; } -void __cpu_map_flush(struct bpf_map *map) +void __cpu_map_flush(void) { - struct bpf_cpu_map *cmap = container_of(map, struct bpf_cpu_map, map); - struct list_head *flush_list = this_cpu_ptr(cmap->flush_list); + struct list_head *flush_list = this_cpu_ptr(&cpu_map_flush_list); struct xdp_bulk_queue *bq, *tmp; list_for_each_entry_safe(bq, tmp, flush_list, flush_node) { @@ -670,3 +659,14 @@ void __cpu_map_flush(struct bpf_map *map) wake_up_process(bq->obj->kthread); } } + +static int __init cpu_map_init(void) +{ + int cpu; + + for_each_possible_cpu(cpu) + INIT_LIST_HEAD(&per_cpu(cpu_map_flush_list, cpu)); + return 0; +} + +subsys_initcall(cpu_map_init); diff --git a/net/core/filter.c b/net/core/filter.c index b7570cb849025..c706325b3e667 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -3558,7 +3558,7 @@ void xdp_do_flush_map(void) __dev_map_flush(); break; case BPF_MAP_TYPE_CPUMAP: - __cpu_map_flush(map); + __cpu_map_flush(); break; case BPF_MAP_TYPE_XSKMAP: __xsk_map_flush(); -- GitLab From 332f22a60e4c3492d4953cd6f7aaa4e8bd0bba97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20T=C3=B6pel?= Date: Thu, 19 Dec 2019 07:10:05 +0100 Subject: [PATCH 1139/1437] xdp: Remove map_to_flush and map swap detection MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now that all XDP maps that can be used with bpf_redirect_map() tracks entries to be flushed in a global fashion, there is not need to track that the map has changed and flush from xdp_do_generic_map() anymore. All entries will be flushed in xdp_do_flush_map(). This means that the map_to_flush can be removed, and the corresponding checks. Moving the flush logic to one place, xdp_do_flush_map(), give a bulking behavior and performance boost. Signed-off-by: Björn Töpel Signed-off-by: Alexei Starovoitov Acked-by: Toke Høiland-Jørgensen Link: https://lore.kernel.org/bpf/20191219061006.21980-8-bjorn.topel@gmail.com --- include/linux/filter.h | 1 - net/core/filter.c | 27 +++------------------------ 2 files changed, 3 insertions(+), 25 deletions(-) diff --git a/include/linux/filter.h b/include/linux/filter.h index 37ac7025031dc..69d6706fc8895 100644 --- a/include/linux/filter.h +++ b/include/linux/filter.h @@ -592,7 +592,6 @@ struct bpf_redirect_info { u32 tgt_index; void *tgt_value; struct bpf_map *map; - struct bpf_map *map_to_flush; u32 kern_flags; }; diff --git a/net/core/filter.c b/net/core/filter.c index c706325b3e667..d9caa3e57ea1f 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -3547,26 +3547,9 @@ static int __bpf_tx_xdp_map(struct net_device *dev_rx, void *fwd, void xdp_do_flush_map(void) { - struct bpf_redirect_info *ri = this_cpu_ptr(&bpf_redirect_info); - struct bpf_map *map = ri->map_to_flush; - - ri->map_to_flush = NULL; - if (map) { - switch (map->map_type) { - case BPF_MAP_TYPE_DEVMAP: - case BPF_MAP_TYPE_DEVMAP_HASH: - __dev_map_flush(); - break; - case BPF_MAP_TYPE_CPUMAP: - __cpu_map_flush(); - break; - case BPF_MAP_TYPE_XSKMAP: - __xsk_map_flush(); - break; - default: - break; - } - } + __dev_map_flush(); + __cpu_map_flush(); + __xsk_map_flush(); } EXPORT_SYMBOL_GPL(xdp_do_flush_map); @@ -3615,14 +3598,10 @@ static int xdp_do_redirect_map(struct net_device *dev, struct xdp_buff *xdp, ri->tgt_value = NULL; WRITE_ONCE(ri->map, NULL); - if (ri->map_to_flush && unlikely(ri->map_to_flush != map)) - xdp_do_flush_map(); - err = __bpf_tx_xdp_map(dev, fwd, map, xdp); if (unlikely(err)) goto err; - ri->map_to_flush = map; _trace_xdp_redirect_map(dev, xdp_prog, fwd, map, index); return 0; err: -- GitLab From 1170beaa3fa3c3381fd820e9d05b84d168fe1dab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20T=C3=B6pel?= Date: Thu, 19 Dec 2019 07:10:06 +0100 Subject: [PATCH 1140/1437] xdp: Simplify __bpf_tx_xdp_map() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The explicit error checking is not needed. Simply return the error instead. Signed-off-by: Björn Töpel Signed-off-by: Alexei Starovoitov Acked-by: Toke Høiland-Jørgensen Link: https://lore.kernel.org/bpf/20191219061006.21980-9-bjorn.topel@gmail.com --- net/core/filter.c | 33 +++++++-------------------------- 1 file changed, 7 insertions(+), 26 deletions(-) diff --git a/net/core/filter.c b/net/core/filter.c index d9caa3e57ea1f..217af9974c864 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -3510,35 +3510,16 @@ err: } static int __bpf_tx_xdp_map(struct net_device *dev_rx, void *fwd, - struct bpf_map *map, - struct xdp_buff *xdp) + struct bpf_map *map, struct xdp_buff *xdp) { - int err; - switch (map->map_type) { case BPF_MAP_TYPE_DEVMAP: - case BPF_MAP_TYPE_DEVMAP_HASH: { - struct bpf_dtab_netdev *dst = fwd; - - err = dev_map_enqueue(dst, xdp, dev_rx); - if (unlikely(err)) - return err; - break; - } - case BPF_MAP_TYPE_CPUMAP: { - struct bpf_cpu_map_entry *rcpu = fwd; - - err = cpu_map_enqueue(rcpu, xdp, dev_rx); - if (unlikely(err)) - return err; - break; - } - case BPF_MAP_TYPE_XSKMAP: { - struct xdp_sock *xs = fwd; - - err = __xsk_map_redirect(xs, xdp); - return err; - } + case BPF_MAP_TYPE_DEVMAP_HASH: + return dev_map_enqueue(fwd, xdp, dev_rx); + case BPF_MAP_TYPE_CPUMAP: + return cpu_map_enqueue(fwd, xdp, dev_rx); + case BPF_MAP_TYPE_XSKMAP: + return __xsk_map_redirect(fwd, xdp); default: break; } -- GitLab From 1020c1f24a946e7d5d8a67db741b20efcd2cefc5 Mon Sep 17 00:00:00 2001 From: Andrey Ignatov Date: Wed, 18 Dec 2019 23:44:33 -0800 Subject: [PATCH 1141/1437] bpf: Simplify __cgroup_bpf_attach __cgroup_bpf_attach has a lot of identical code to handle two scenarios: BPF_F_ALLOW_MULTI is set and unset. Simplify it by splitting the two main steps: * First, the decision is made whether a new bpf_prog_list entry should be allocated or existing entry should be reused for the new program. This decision is saved in replace_pl pointer; * Next, replace_pl pointer is used to handle both possible states of BPF_F_ALLOW_MULTI flag (set / unset) instead of doing similar work for them separately. This splitting, in turn, allows to make further simplifications: * The check for attaching same program twice in BPF_F_ALLOW_MULTI mode can be done before allocating cgroup storage, so that if user tries to attach same program twice no alloc/free happens as it was before; * pl_was_allocated becomes redundant so it's removed. Signed-off-by: Andrey Ignatov Signed-off-by: Alexei Starovoitov Acked-by: Martin KaFai Lau Acked-by: Andrii Nakryiko Link: https://lore.kernel.org/bpf/c6193db6fe630797110b0d3ff06c125d093b834c.1576741281.git.rdna@fb.com --- kernel/bpf/cgroup.c | 62 +++++++++++++++++---------------------------- 1 file changed, 23 insertions(+), 39 deletions(-) diff --git a/kernel/bpf/cgroup.c b/kernel/bpf/cgroup.c index 9f90d3c92bdac..e8cbdd1be687b 100644 --- a/kernel/bpf/cgroup.c +++ b/kernel/bpf/cgroup.c @@ -295,9 +295,8 @@ int __cgroup_bpf_attach(struct cgroup *cgrp, struct bpf_prog *prog, struct bpf_prog *old_prog = NULL; struct bpf_cgroup_storage *storage[MAX_BPF_CGROUP_STORAGE_TYPE], *old_storage[MAX_BPF_CGROUP_STORAGE_TYPE] = {NULL}; + struct bpf_prog_list *pl, *replace_pl = NULL; enum bpf_cgroup_storage_type stype; - struct bpf_prog_list *pl; - bool pl_was_allocated; int err; if ((flags & BPF_F_ALLOW_OVERRIDE) && (flags & BPF_F_ALLOW_MULTI)) @@ -317,6 +316,16 @@ int __cgroup_bpf_attach(struct cgroup *cgrp, struct bpf_prog *prog, if (prog_list_length(progs) >= BPF_CGROUP_MAX_PROGS) return -E2BIG; + if (flags & BPF_F_ALLOW_MULTI) { + list_for_each_entry(pl, progs, node) { + if (pl->prog == prog) + /* disallow attaching the same prog twice */ + return -EINVAL; + } + } else if (!list_empty(progs)) { + replace_pl = list_first_entry(progs, typeof(*pl), node); + } + for_each_cgroup_storage_type(stype) { storage[stype] = bpf_cgroup_storage_alloc(prog, stype); if (IS_ERR(storage[stype])) { @@ -327,52 +336,27 @@ int __cgroup_bpf_attach(struct cgroup *cgrp, struct bpf_prog *prog, } } - if (flags & BPF_F_ALLOW_MULTI) { - list_for_each_entry(pl, progs, node) { - if (pl->prog == prog) { - /* disallow attaching the same prog twice */ - for_each_cgroup_storage_type(stype) - bpf_cgroup_storage_free(storage[stype]); - return -EINVAL; - } + if (replace_pl) { + pl = replace_pl; + old_prog = pl->prog; + for_each_cgroup_storage_type(stype) { + old_storage[stype] = pl->storage[stype]; + bpf_cgroup_storage_unlink(old_storage[stype]); } - + } else { pl = kmalloc(sizeof(*pl), GFP_KERNEL); if (!pl) { for_each_cgroup_storage_type(stype) bpf_cgroup_storage_free(storage[stype]); return -ENOMEM; } - - pl_was_allocated = true; - pl->prog = prog; - for_each_cgroup_storage_type(stype) - pl->storage[stype] = storage[stype]; list_add_tail(&pl->node, progs); - } else { - if (list_empty(progs)) { - pl = kmalloc(sizeof(*pl), GFP_KERNEL); - if (!pl) { - for_each_cgroup_storage_type(stype) - bpf_cgroup_storage_free(storage[stype]); - return -ENOMEM; - } - pl_was_allocated = true; - list_add_tail(&pl->node, progs); - } else { - pl = list_first_entry(progs, typeof(*pl), node); - old_prog = pl->prog; - for_each_cgroup_storage_type(stype) { - old_storage[stype] = pl->storage[stype]; - bpf_cgroup_storage_unlink(old_storage[stype]); - } - pl_was_allocated = false; - } - pl->prog = prog; - for_each_cgroup_storage_type(stype) - pl->storage[stype] = storage[stype]; } + pl->prog = prog; + for_each_cgroup_storage_type(stype) + pl->storage[stype] = storage[stype]; + cgrp->bpf.flags[type] = flags; err = update_effective_progs(cgrp, type); @@ -401,7 +385,7 @@ cleanup: pl->storage[stype] = old_storage[stype]; bpf_cgroup_storage_link(old_storage[stype], cgrp, type); } - if (pl_was_allocated) { + if (!replace_pl) { list_del(&pl->node); kfree(pl); } -- GitLab From 9fab329d6a04c0a52a84d207b5e0d83aeb660aa0 Mon Sep 17 00:00:00 2001 From: Andrey Ignatov Date: Wed, 18 Dec 2019 23:44:34 -0800 Subject: [PATCH 1142/1437] bpf: Remove unused new_flags in hierarchy_allows_attach() new_flags is unused, remove it. Signed-off-by: Andrey Ignatov Signed-off-by: Alexei Starovoitov Acked-by: Martin KaFai Lau Link: https://lore.kernel.org/bpf/2c49b30ab750f93cfef04a1e40b097d70c3a39a1.1576741281.git.rdna@fb.com --- kernel/bpf/cgroup.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/kernel/bpf/cgroup.c b/kernel/bpf/cgroup.c index e8cbdd1be687b..283efe3ce052a 100644 --- a/kernel/bpf/cgroup.c +++ b/kernel/bpf/cgroup.c @@ -103,8 +103,7 @@ static u32 prog_list_length(struct list_head *head) * if parent has overridable or multi-prog, allow attaching */ static bool hierarchy_allows_attach(struct cgroup *cgrp, - enum bpf_attach_type type, - u32 new_flags) + enum bpf_attach_type type) { struct cgroup *p; @@ -303,7 +302,7 @@ int __cgroup_bpf_attach(struct cgroup *cgrp, struct bpf_prog *prog, /* invalid combination */ return -EINVAL; - if (!hierarchy_allows_attach(cgrp, type, flags)) + if (!hierarchy_allows_attach(cgrp, type)) return -EPERM; if (!list_empty(progs) && cgrp->bpf.flags[type] != flags) -- GitLab From 7dd68b3279f1792103d12e69933db3128c6d416e Mon Sep 17 00:00:00 2001 From: Andrey Ignatov Date: Wed, 18 Dec 2019 23:44:35 -0800 Subject: [PATCH 1143/1437] bpf: Support replacing cgroup-bpf program in MULTI mode The common use-case in production is to have multiple cgroup-bpf programs per attach type that cover multiple use-cases. Such programs are attached with BPF_F_ALLOW_MULTI and can be maintained by different people. Order of programs usually matters, for example imagine two egress programs: the first one drops packets and the second one counts packets. If they're swapped the result of counting program will be different. It brings operational challenges with updating cgroup-bpf program(s) attached with BPF_F_ALLOW_MULTI since there is no way to replace a program: * One way to update is to detach all programs first and then attach the new version(s) again in the right order. This introduces an interruption in the work a program is doing and may not be acceptable (e.g. if it's egress firewall); * Another way is attach the new version of a program first and only then detach the old version. This introduces the time interval when two versions of same program are working, what may not be acceptable if a program is not idempotent. It also imposes additional burden on program developers to make sure that two versions of their program can co-exist. Solve the problem by introducing a "replace" mode in BPF_PROG_ATTACH command for cgroup-bpf programs being attached with BPF_F_ALLOW_MULTI flag. This mode is enabled by newly introduced BPF_F_REPLACE attach flag and bpf_attr.replace_bpf_fd attribute to pass fd of the old program to replace That way user can replace any program among those attached with BPF_F_ALLOW_MULTI flag without the problems described above. Details of the new API: * If BPF_F_REPLACE is set but replace_bpf_fd doesn't have valid descriptor of BPF program, BPF_PROG_ATTACH will return corresponding error (EINVAL or EBADF). * If replace_bpf_fd has valid descriptor of BPF program but such a program is not attached to specified cgroup, BPF_PROG_ATTACH will return ENOENT. BPF_F_REPLACE is introduced to make the user intent clear, since replace_bpf_fd alone can't be used for this (its default value, 0, is a valid fd). BPF_F_REPLACE also makes it possible to extend the API in the future (e.g. add BPF_F_BEFORE and BPF_F_AFTER if needed). Signed-off-by: Andrey Ignatov Signed-off-by: Alexei Starovoitov Acked-by: Martin KaFai Lau Acked-by: Andrii Narkyiko Link: https://lore.kernel.org/bpf/30cd850044a0057bdfcaaf154b7d2f39850ba813.1576741281.git.rdna@fb.com --- include/linux/bpf-cgroup.h | 4 +++- include/uapi/linux/bpf.h | 10 ++++++++++ kernel/bpf/cgroup.c | 30 ++++++++++++++++++++++++++---- kernel/bpf/syscall.c | 4 ++-- kernel/cgroup/cgroup.c | 5 +++-- tools/include/uapi/linux/bpf.h | 10 ++++++++++ 6 files changed, 54 insertions(+), 9 deletions(-) diff --git a/include/linux/bpf-cgroup.h b/include/linux/bpf-cgroup.h index 169fd25f6bc2d..18f6a6da7c3c8 100644 --- a/include/linux/bpf-cgroup.h +++ b/include/linux/bpf-cgroup.h @@ -85,6 +85,7 @@ int cgroup_bpf_inherit(struct cgroup *cgrp); void cgroup_bpf_offline(struct cgroup *cgrp); int __cgroup_bpf_attach(struct cgroup *cgrp, struct bpf_prog *prog, + struct bpf_prog *replace_prog, enum bpf_attach_type type, u32 flags); int __cgroup_bpf_detach(struct cgroup *cgrp, struct bpf_prog *prog, enum bpf_attach_type type); @@ -93,7 +94,8 @@ int __cgroup_bpf_query(struct cgroup *cgrp, const union bpf_attr *attr, /* Wrapper for __cgroup_bpf_*() protected by cgroup_mutex */ int cgroup_bpf_attach(struct cgroup *cgrp, struct bpf_prog *prog, - enum bpf_attach_type type, u32 flags); + struct bpf_prog *replace_prog, enum bpf_attach_type type, + u32 flags); int cgroup_bpf_detach(struct cgroup *cgrp, struct bpf_prog *prog, enum bpf_attach_type type, u32 flags); int cgroup_bpf_query(struct cgroup *cgrp, const union bpf_attr *attr, diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h index dbbcf0b02970b..7df436da542d7 100644 --- a/include/uapi/linux/bpf.h +++ b/include/uapi/linux/bpf.h @@ -231,6 +231,11 @@ enum bpf_attach_type { * When children program makes decision (like picking TCP CA or sock bind) * parent program has a chance to override it. * + * With BPF_F_ALLOW_MULTI a new program is added to the end of the list of + * programs for a cgroup. Though it's possible to replace an old program at + * any position by also specifying BPF_F_REPLACE flag and position itself in + * replace_bpf_fd attribute. Old program at this position will be released. + * * A cgroup with MULTI or OVERRIDE flag allows any attach flags in sub-cgroups. * A cgroup with NONE doesn't allow any programs in sub-cgroups. * Ex1: @@ -249,6 +254,7 @@ enum bpf_attach_type { */ #define BPF_F_ALLOW_OVERRIDE (1U << 0) #define BPF_F_ALLOW_MULTI (1U << 1) +#define BPF_F_REPLACE (1U << 2) /* If BPF_F_STRICT_ALIGNMENT is used in BPF_PROG_LOAD command, the * verifier will perform strict alignment checking as if the kernel @@ -442,6 +448,10 @@ union bpf_attr { __u32 attach_bpf_fd; /* eBPF program to attach */ __u32 attach_type; __u32 attach_flags; + __u32 replace_bpf_fd; /* previously attached eBPF + * program to replace if + * BPF_F_REPLACE is used + */ }; struct { /* anonymous struct used by BPF_PROG_TEST_RUN command */ diff --git a/kernel/bpf/cgroup.c b/kernel/bpf/cgroup.c index 283efe3ce052a..45346c79613a9 100644 --- a/kernel/bpf/cgroup.c +++ b/kernel/bpf/cgroup.c @@ -282,14 +282,17 @@ cleanup: * propagate the change to descendants * @cgrp: The cgroup which descendants to traverse * @prog: A program to attach + * @replace_prog: Previously attached program to replace if BPF_F_REPLACE is set * @type: Type of attach operation * @flags: Option flags * * Must be called with cgroup_mutex held. */ int __cgroup_bpf_attach(struct cgroup *cgrp, struct bpf_prog *prog, + struct bpf_prog *replace_prog, enum bpf_attach_type type, u32 flags) { + u32 saved_flags = (flags & (BPF_F_ALLOW_OVERRIDE | BPF_F_ALLOW_MULTI)); struct list_head *progs = &cgrp->bpf.progs[type]; struct bpf_prog *old_prog = NULL; struct bpf_cgroup_storage *storage[MAX_BPF_CGROUP_STORAGE_TYPE], @@ -298,14 +301,15 @@ int __cgroup_bpf_attach(struct cgroup *cgrp, struct bpf_prog *prog, enum bpf_cgroup_storage_type stype; int err; - if ((flags & BPF_F_ALLOW_OVERRIDE) && (flags & BPF_F_ALLOW_MULTI)) + if (((flags & BPF_F_ALLOW_OVERRIDE) && (flags & BPF_F_ALLOW_MULTI)) || + ((flags & BPF_F_REPLACE) && !(flags & BPF_F_ALLOW_MULTI))) /* invalid combination */ return -EINVAL; if (!hierarchy_allows_attach(cgrp, type)) return -EPERM; - if (!list_empty(progs) && cgrp->bpf.flags[type] != flags) + if (!list_empty(progs) && cgrp->bpf.flags[type] != saved_flags) /* Disallow attaching non-overridable on top * of existing overridable in this cgroup. * Disallow attaching multi-prog if overridable or none @@ -320,7 +324,12 @@ int __cgroup_bpf_attach(struct cgroup *cgrp, struct bpf_prog *prog, if (pl->prog == prog) /* disallow attaching the same prog twice */ return -EINVAL; + if (pl->prog == replace_prog) + replace_pl = pl; } + if ((flags & BPF_F_REPLACE) && !replace_pl) + /* prog to replace not found for cgroup */ + return -ENOENT; } else if (!list_empty(progs)) { replace_pl = list_first_entry(progs, typeof(*pl), node); } @@ -356,7 +365,7 @@ int __cgroup_bpf_attach(struct cgroup *cgrp, struct bpf_prog *prog, for_each_cgroup_storage_type(stype) pl->storage[stype] = storage[stype]; - cgrp->bpf.flags[type] = flags; + cgrp->bpf.flags[type] = saved_flags; err = update_effective_progs(cgrp, type); if (err) @@ -522,6 +531,7 @@ int __cgroup_bpf_query(struct cgroup *cgrp, const union bpf_attr *attr, int cgroup_bpf_prog_attach(const union bpf_attr *attr, enum bpf_prog_type ptype, struct bpf_prog *prog) { + struct bpf_prog *replace_prog = NULL; struct cgroup *cgrp; int ret; @@ -529,8 +539,20 @@ int cgroup_bpf_prog_attach(const union bpf_attr *attr, if (IS_ERR(cgrp)) return PTR_ERR(cgrp); - ret = cgroup_bpf_attach(cgrp, prog, attr->attach_type, + if ((attr->attach_flags & BPF_F_ALLOW_MULTI) && + (attr->attach_flags & BPF_F_REPLACE)) { + replace_prog = bpf_prog_get_type(attr->replace_bpf_fd, ptype); + if (IS_ERR(replace_prog)) { + cgroup_put(cgrp); + return PTR_ERR(replace_prog); + } + } + + ret = cgroup_bpf_attach(cgrp, prog, replace_prog, attr->attach_type, attr->attach_flags); + + if (replace_prog) + bpf_prog_put(replace_prog); cgroup_put(cgrp); return ret; } diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c index b08c362f4e020..81ee8595dfee9 100644 --- a/kernel/bpf/syscall.c +++ b/kernel/bpf/syscall.c @@ -2073,10 +2073,10 @@ static int bpf_prog_attach_check_attach_type(const struct bpf_prog *prog, } } -#define BPF_PROG_ATTACH_LAST_FIELD attach_flags +#define BPF_PROG_ATTACH_LAST_FIELD replace_bpf_fd #define BPF_F_ATTACH_MASK \ - (BPF_F_ALLOW_OVERRIDE | BPF_F_ALLOW_MULTI) + (BPF_F_ALLOW_OVERRIDE | BPF_F_ALLOW_MULTI | BPF_F_REPLACE) static int bpf_prog_attach(const union bpf_attr *attr) { diff --git a/kernel/cgroup/cgroup.c b/kernel/cgroup/cgroup.c index 735af8f15f954..725365df066d7 100644 --- a/kernel/cgroup/cgroup.c +++ b/kernel/cgroup/cgroup.c @@ -6288,12 +6288,13 @@ void cgroup_sk_free(struct sock_cgroup_data *skcd) #ifdef CONFIG_CGROUP_BPF int cgroup_bpf_attach(struct cgroup *cgrp, struct bpf_prog *prog, - enum bpf_attach_type type, u32 flags) + struct bpf_prog *replace_prog, enum bpf_attach_type type, + u32 flags) { int ret; mutex_lock(&cgroup_mutex); - ret = __cgroup_bpf_attach(cgrp, prog, type, flags); + ret = __cgroup_bpf_attach(cgrp, prog, replace_prog, type, flags); mutex_unlock(&cgroup_mutex); return ret; } diff --git a/tools/include/uapi/linux/bpf.h b/tools/include/uapi/linux/bpf.h index dbbcf0b02970b..7df436da542d7 100644 --- a/tools/include/uapi/linux/bpf.h +++ b/tools/include/uapi/linux/bpf.h @@ -231,6 +231,11 @@ enum bpf_attach_type { * When children program makes decision (like picking TCP CA or sock bind) * parent program has a chance to override it. * + * With BPF_F_ALLOW_MULTI a new program is added to the end of the list of + * programs for a cgroup. Though it's possible to replace an old program at + * any position by also specifying BPF_F_REPLACE flag and position itself in + * replace_bpf_fd attribute. Old program at this position will be released. + * * A cgroup with MULTI or OVERRIDE flag allows any attach flags in sub-cgroups. * A cgroup with NONE doesn't allow any programs in sub-cgroups. * Ex1: @@ -249,6 +254,7 @@ enum bpf_attach_type { */ #define BPF_F_ALLOW_OVERRIDE (1U << 0) #define BPF_F_ALLOW_MULTI (1U << 1) +#define BPF_F_REPLACE (1U << 2) /* If BPF_F_STRICT_ALIGNMENT is used in BPF_PROG_LOAD command, the * verifier will perform strict alignment checking as if the kernel @@ -442,6 +448,10 @@ union bpf_attr { __u32 attach_bpf_fd; /* eBPF program to attach */ __u32 attach_type; __u32 attach_flags; + __u32 replace_bpf_fd; /* previously attached eBPF + * program to replace if + * BPF_F_REPLACE is used + */ }; struct { /* anonymous struct used by BPF_PROG_TEST_RUN command */ -- GitLab From cdbee3839cd91a4577425c43cf064b9523926159 Mon Sep 17 00:00:00 2001 From: Andrey Ignatov Date: Wed, 18 Dec 2019 23:44:36 -0800 Subject: [PATCH 1144/1437] libbpf: Introduce bpf_prog_attach_xattr Introduce a new bpf_prog_attach_xattr function that, in addition to program fd, target fd and attach type, accepts an extendable struct bpf_prog_attach_opts. bpf_prog_attach_opts relies on DECLARE_LIBBPF_OPTS macro to maintain backward and forward compatibility and has the following "optional" attach attributes: * existing attach_flags, since it's not required when attaching in NONE mode. Even though it's quite often used in MULTI and OVERRIDE mode it seems to be a good idea to reduce number of arguments to bpf_prog_attach_xattr; * newly introduced attribute of BPF_PROG_ATTACH command: replace_prog_fd that is fd of previously attached cgroup-bpf program to replace if BPF_F_REPLACE flag is used. The new function is named to be consistent with other xattr-functions (bpf_prog_test_run_xattr, bpf_create_map_xattr, bpf_load_program_xattr). The struct bpf_prog_attach_opts is supposed to be used with DECLARE_LIBBPF_OPTS macro. Signed-off-by: Andrey Ignatov Signed-off-by: Alexei Starovoitov Acked-by: Andrii Nakryiko Link: https://lore.kernel.org/bpf/bd6e0732303eb14e4b79cb128268d9e9ad6db208.1576741281.git.rdna@fb.com --- tools/lib/bpf/bpf.c | 17 ++++++++++++++++- tools/lib/bpf/bpf.h | 11 +++++++++++ tools/lib/bpf/libbpf.map | 1 + 3 files changed, 28 insertions(+), 1 deletion(-) diff --git a/tools/lib/bpf/bpf.c b/tools/lib/bpf/bpf.c index 98596e15390fb..a787d53699c81 100644 --- a/tools/lib/bpf/bpf.c +++ b/tools/lib/bpf/bpf.c @@ -466,14 +466,29 @@ int bpf_obj_get(const char *pathname) int bpf_prog_attach(int prog_fd, int target_fd, enum bpf_attach_type type, unsigned int flags) +{ + DECLARE_LIBBPF_OPTS(bpf_prog_attach_opts, opts, + .flags = flags, + ); + + return bpf_prog_attach_xattr(prog_fd, target_fd, type, &opts); +} + +int bpf_prog_attach_xattr(int prog_fd, int target_fd, + enum bpf_attach_type type, + const struct bpf_prog_attach_opts *opts) { union bpf_attr attr; + if (!OPTS_VALID(opts, bpf_prog_attach_opts)) + return -EINVAL; + memset(&attr, 0, sizeof(attr)); attr.target_fd = target_fd; attr.attach_bpf_fd = prog_fd; attr.attach_type = type; - attr.attach_flags = flags; + attr.attach_flags = OPTS_GET(opts, flags, 0); + attr.replace_bpf_fd = OPTS_GET(opts, replace_prog_fd, 0); return sys_bpf(BPF_PROG_ATTACH, &attr, sizeof(attr)); } diff --git a/tools/lib/bpf/bpf.h b/tools/lib/bpf/bpf.h index 269807ce9ef52..f0ab8519986ec 100644 --- a/tools/lib/bpf/bpf.h +++ b/tools/lib/bpf/bpf.h @@ -126,8 +126,19 @@ LIBBPF_API int bpf_map_get_next_key(int fd, const void *key, void *next_key); LIBBPF_API int bpf_map_freeze(int fd); LIBBPF_API int bpf_obj_pin(int fd, const char *pathname); LIBBPF_API int bpf_obj_get(const char *pathname); + +struct bpf_prog_attach_opts { + size_t sz; /* size of this struct for forward/backward compatibility */ + unsigned int flags; + int replace_prog_fd; +}; +#define bpf_prog_attach_opts__last_field replace_prog_fd + LIBBPF_API int bpf_prog_attach(int prog_fd, int attachable_fd, enum bpf_attach_type type, unsigned int flags); +LIBBPF_API int bpf_prog_attach_xattr(int prog_fd, int attachable_fd, + enum bpf_attach_type type, + const struct bpf_prog_attach_opts *opts); LIBBPF_API int bpf_prog_detach(int attachable_fd, enum bpf_attach_type type); LIBBPF_API int bpf_prog_detach2(int prog_fd, int attachable_fd, enum bpf_attach_type type); diff --git a/tools/lib/bpf/libbpf.map b/tools/lib/bpf/libbpf.map index e3a471f38a715..e9713a5742432 100644 --- a/tools/lib/bpf/libbpf.map +++ b/tools/lib/bpf/libbpf.map @@ -219,6 +219,7 @@ LIBBPF_0.0.7 { bpf_object__detach_skeleton; bpf_object__load_skeleton; bpf_object__open_skeleton; + bpf_prog_attach_xattr; bpf_program__attach; bpf_program__name; btf__align_of; -- GitLab From 257c88559f360ef40d251942f1f9f0c55f5f91ca Mon Sep 17 00:00:00 2001 From: Andrey Ignatov Date: Wed, 18 Dec 2019 23:44:37 -0800 Subject: [PATCH 1145/1437] selftests/bpf: Convert test_cgroup_attach to prog_tests Convert test_cgroup_attach to prog_tests. This change does a lot of things but in many cases it's pretty expensive to separate them, so they go in one commit. Nevertheless the logic is ketp as is and changes made are just moving things around, simplifying them (w/o changing the meaning of the tests) and making prog_tests compatible: * split the 3 tests in the file into 3 separate files in prog_tests/; * rename the test functions to test_; * remove unused includes, constants, variables and functions from every test; * replace `if`-s with or `if (CHECK())` where additional context should be logged and with `if (CHECK_FAIL())` where line number is enough; * switch from `log_err()` to logging via `CHECK()`; * replace `assert`-s with `CHECK_FAIL()` to avoid crashing the whole test_progs if one assertion fails; * replace cgroup_helpers with test__join_cgroup() in cgroup_attach_override only, other tests need more fine-grained control for cgroup creation/deletion so cgroup_helpers are still used there; * simplify cgroup_attach_autodetach by switching to easiest possible program since this test doesn't really need such a complicated program as cgroup_attach_multi does; * remove test_cgroup_attach.c itself. Signed-off-by: Andrey Ignatov Signed-off-by: Alexei Starovoitov Acked-by: Andrii Nakryiko Link: https://lore.kernel.org/bpf/0ff19cc64d2dc5cf404349f07131119480e10e32.1576741281.git.rdna@fb.com --- tools/testing/selftests/bpf/.gitignore | 1 - tools/testing/selftests/bpf/Makefile | 3 +- .../bpf/prog_tests/cgroup_attach_autodetach.c | 111 ++++ .../bpf/prog_tests/cgroup_attach_multi.c | 238 ++++++++ .../bpf/prog_tests/cgroup_attach_override.c | 148 +++++ .../selftests/bpf/test_cgroup_attach.c | 571 ------------------ 6 files changed, 498 insertions(+), 574 deletions(-) create mode 100644 tools/testing/selftests/bpf/prog_tests/cgroup_attach_autodetach.c create mode 100644 tools/testing/selftests/bpf/prog_tests/cgroup_attach_multi.c create mode 100644 tools/testing/selftests/bpf/prog_tests/cgroup_attach_override.c delete mode 100644 tools/testing/selftests/bpf/test_cgroup_attach.c diff --git a/tools/testing/selftests/bpf/.gitignore b/tools/testing/selftests/bpf/.gitignore index ce5af95ede422..b139b3d75ebb0 100644 --- a/tools/testing/selftests/bpf/.gitignore +++ b/tools/testing/selftests/bpf/.gitignore @@ -21,7 +21,6 @@ test_lirc_mode2_user get_cgroup_id_user test_skb_cgroup_id_user test_socket_cookie -test_cgroup_attach test_cgroup_storage test_select_reuseport test_flow_dissector diff --git a/tools/testing/selftests/bpf/Makefile b/tools/testing/selftests/bpf/Makefile index c652bd84ef0e6..866fc1cadd7c2 100644 --- a/tools/testing/selftests/bpf/Makefile +++ b/tools/testing/selftests/bpf/Makefile @@ -32,7 +32,7 @@ TEST_GEN_PROGS = test_verifier test_tag test_maps test_lru_map test_lpm_map test test_sock test_btf test_sockmap get_cgroup_id_user test_socket_cookie \ test_cgroup_storage \ test_netcnt test_tcpnotify_user test_sock_fields test_sysctl test_hashmap \ - test_cgroup_attach test_progs-no_alu32 + test_progs-no_alu32 # Also test bpf-gcc, if present ifneq ($(BPF_GCC),) @@ -136,7 +136,6 @@ $(OUTPUT)/test_cgroup_storage: cgroup_helpers.c $(OUTPUT)/test_netcnt: cgroup_helpers.c $(OUTPUT)/test_sock_fields: cgroup_helpers.c $(OUTPUT)/test_sysctl: cgroup_helpers.c -$(OUTPUT)/test_cgroup_attach: cgroup_helpers.c .PHONY: force diff --git a/tools/testing/selftests/bpf/prog_tests/cgroup_attach_autodetach.c b/tools/testing/selftests/bpf/prog_tests/cgroup_attach_autodetach.c new file mode 100644 index 0000000000000..5b13f2c6c4024 --- /dev/null +++ b/tools/testing/selftests/bpf/prog_tests/cgroup_attach_autodetach.c @@ -0,0 +1,111 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include + +#include "cgroup_helpers.h" + +#define PING_CMD "ping -q -c1 -w1 127.0.0.1 > /dev/null" + +char bpf_log_buf[BPF_LOG_BUF_SIZE]; + +static int prog_load(void) +{ + struct bpf_insn prog[] = { + BPF_MOV64_IMM(BPF_REG_0, 1), /* r0 = 1 */ + BPF_EXIT_INSN(), + }; + size_t insns_cnt = sizeof(prog) / sizeof(struct bpf_insn); + + return bpf_load_program(BPF_PROG_TYPE_CGROUP_SKB, + prog, insns_cnt, "GPL", 0, + bpf_log_buf, BPF_LOG_BUF_SIZE); +} + +void test_cgroup_attach_autodetach(void) +{ + __u32 duration = 0, prog_cnt = 4, attach_flags; + int allow_prog[2] = {-1}; + __u32 prog_ids[2] = {0}; + void *ptr = NULL; + int cg = 0, i; + int attempts; + + for (i = 0; i < ARRAY_SIZE(allow_prog); i++) { + allow_prog[i] = prog_load(); + if (CHECK(allow_prog[i] < 0, "prog_load", + "verifier output:\n%s\n-------\n", bpf_log_buf)) + goto err; + } + + if (CHECK_FAIL(setup_cgroup_environment())) + goto err; + + /* create a cgroup, attach two programs and remember their ids */ + cg = create_and_get_cgroup("/cg_autodetach"); + if (CHECK_FAIL(cg < 0)) + goto err; + + if (CHECK_FAIL(join_cgroup("/cg_autodetach"))) + goto err; + + for (i = 0; i < ARRAY_SIZE(allow_prog); i++) + if (CHECK(bpf_prog_attach(allow_prog[i], cg, + BPF_CGROUP_INET_EGRESS, + BPF_F_ALLOW_MULTI), + "prog_attach", "prog[%d], errno=%d\n", i, errno)) + goto err; + + /* make sure that programs are attached and run some traffic */ + if (CHECK(bpf_prog_query(cg, BPF_CGROUP_INET_EGRESS, 0, &attach_flags, + prog_ids, &prog_cnt), + "prog_query", "errno=%d\n", errno)) + goto err; + if (CHECK_FAIL(system(PING_CMD))) + goto err; + + /* allocate some memory (4Mb) to pin the original cgroup */ + ptr = malloc(4 * (1 << 20)); + if (CHECK_FAIL(!ptr)) + goto err; + + /* close programs and cgroup fd */ + for (i = 0; i < ARRAY_SIZE(allow_prog); i++) { + close(allow_prog[i]); + allow_prog[i] = -1; + } + + close(cg); + cg = 0; + + /* leave the cgroup and remove it. don't detach programs */ + cleanup_cgroup_environment(); + + /* wait for the asynchronous auto-detachment. + * wait for no more than 5 sec and give up. + */ + for (i = 0; i < ARRAY_SIZE(prog_ids); i++) { + for (attempts = 5; attempts >= 0; attempts--) { + int fd = bpf_prog_get_fd_by_id(prog_ids[i]); + + if (fd < 0) + break; + + /* don't leave the fd open */ + close(fd); + + if (CHECK_FAIL(!attempts)) + goto err; + + sleep(1); + } + } + +err: + for (i = 0; i < ARRAY_SIZE(allow_prog); i++) + if (allow_prog[i] >= 0) + close(allow_prog[i]); + if (cg) + close(cg); + free(ptr); + cleanup_cgroup_environment(); +} diff --git a/tools/testing/selftests/bpf/prog_tests/cgroup_attach_multi.c b/tools/testing/selftests/bpf/prog_tests/cgroup_attach_multi.c new file mode 100644 index 0000000000000..4eaab74350440 --- /dev/null +++ b/tools/testing/selftests/bpf/prog_tests/cgroup_attach_multi.c @@ -0,0 +1,238 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include + +#include "cgroup_helpers.h" + +#define PING_CMD "ping -q -c1 -w1 127.0.0.1 > /dev/null" + +char bpf_log_buf[BPF_LOG_BUF_SIZE]; + +static int map_fd = -1; + +static int prog_load_cnt(int verdict, int val) +{ + int cgroup_storage_fd, percpu_cgroup_storage_fd; + + if (map_fd < 0) + map_fd = bpf_create_map(BPF_MAP_TYPE_ARRAY, 4, 8, 1, 0); + if (map_fd < 0) { + printf("failed to create map '%s'\n", strerror(errno)); + return -1; + } + + cgroup_storage_fd = bpf_create_map(BPF_MAP_TYPE_CGROUP_STORAGE, + sizeof(struct bpf_cgroup_storage_key), 8, 0, 0); + if (cgroup_storage_fd < 0) { + printf("failed to create map '%s'\n", strerror(errno)); + return -1; + } + + percpu_cgroup_storage_fd = bpf_create_map( + BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE, + sizeof(struct bpf_cgroup_storage_key), 8, 0, 0); + if (percpu_cgroup_storage_fd < 0) { + printf("failed to create map '%s'\n", strerror(errno)); + return -1; + } + + struct bpf_insn prog[] = { + BPF_MOV32_IMM(BPF_REG_0, 0), + BPF_STX_MEM(BPF_W, BPF_REG_10, BPF_REG_0, -4), /* *(u32 *)(fp - 4) = r0 */ + BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -4), /* r2 = fp - 4 */ + BPF_LD_MAP_FD(BPF_REG_1, map_fd), + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 2), + BPF_MOV64_IMM(BPF_REG_1, val), /* r1 = 1 */ + BPF_RAW_INSN(BPF_STX | BPF_XADD | BPF_DW, BPF_REG_0, BPF_REG_1, 0, 0), /* xadd r0 += r1 */ + + BPF_LD_MAP_FD(BPF_REG_1, cgroup_storage_fd), + BPF_MOV64_IMM(BPF_REG_2, 0), + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_get_local_storage), + BPF_MOV64_IMM(BPF_REG_1, val), + BPF_RAW_INSN(BPF_STX | BPF_XADD | BPF_W, BPF_REG_0, BPF_REG_1, 0, 0), + + BPF_LD_MAP_FD(BPF_REG_1, percpu_cgroup_storage_fd), + BPF_MOV64_IMM(BPF_REG_2, 0), + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_get_local_storage), + BPF_LDX_MEM(BPF_W, BPF_REG_3, BPF_REG_0, 0), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_3, 0x1), + BPF_STX_MEM(BPF_W, BPF_REG_0, BPF_REG_3, 0), + + BPF_MOV64_IMM(BPF_REG_0, verdict), /* r0 = verdict */ + BPF_EXIT_INSN(), + }; + size_t insns_cnt = sizeof(prog) / sizeof(struct bpf_insn); + int ret; + + ret = bpf_load_program(BPF_PROG_TYPE_CGROUP_SKB, + prog, insns_cnt, "GPL", 0, + bpf_log_buf, BPF_LOG_BUF_SIZE); + + close(cgroup_storage_fd); + return ret; +} + +void test_cgroup_attach_multi(void) +{ + __u32 prog_ids[4], prog_cnt = 0, attach_flags, saved_prog_id; + int cg1 = 0, cg2 = 0, cg3 = 0, cg4 = 0, cg5 = 0, key = 0; + int allow_prog[6] = {-1}; + unsigned long long value; + __u32 duration = 0; + int i = 0; + + for (i = 0; i < ARRAY_SIZE(allow_prog); i++) { + allow_prog[i] = prog_load_cnt(1, 1 << i); + if (CHECK(allow_prog[i] < 0, "prog_load", + "verifier output:\n%s\n-------\n", bpf_log_buf)) + goto err; + } + + if (CHECK_FAIL(setup_cgroup_environment())) + goto err; + + cg1 = create_and_get_cgroup("/cg1"); + if (CHECK_FAIL(cg1 < 0)) + goto err; + cg2 = create_and_get_cgroup("/cg1/cg2"); + if (CHECK_FAIL(cg2 < 0)) + goto err; + cg3 = create_and_get_cgroup("/cg1/cg2/cg3"); + if (CHECK_FAIL(cg3 < 0)) + goto err; + cg4 = create_and_get_cgroup("/cg1/cg2/cg3/cg4"); + if (CHECK_FAIL(cg4 < 0)) + goto err; + cg5 = create_and_get_cgroup("/cg1/cg2/cg3/cg4/cg5"); + if (CHECK_FAIL(cg5 < 0)) + goto err; + + if (CHECK_FAIL(join_cgroup("/cg1/cg2/cg3/cg4/cg5"))) + goto err; + + if (CHECK(bpf_prog_attach(allow_prog[0], cg1, BPF_CGROUP_INET_EGRESS, + BPF_F_ALLOW_MULTI), + "prog0_attach_to_cg1_multi", "errno=%d\n", errno)) + goto err; + + if (CHECK(!bpf_prog_attach(allow_prog[0], cg1, BPF_CGROUP_INET_EGRESS, + BPF_F_ALLOW_MULTI), + "fail_same_prog_attach_to_cg1", "unexpected success\n")) + goto err; + + if (CHECK(bpf_prog_attach(allow_prog[1], cg1, BPF_CGROUP_INET_EGRESS, + BPF_F_ALLOW_MULTI), + "prog1_attach_to_cg1_multi", "errno=%d\n", errno)) + goto err; + + if (CHECK(bpf_prog_attach(allow_prog[2], cg2, BPF_CGROUP_INET_EGRESS, + BPF_F_ALLOW_OVERRIDE), + "prog2_attach_to_cg2_override", "errno=%d\n", errno)) + goto err; + + if (CHECK(bpf_prog_attach(allow_prog[3], cg3, BPF_CGROUP_INET_EGRESS, + BPF_F_ALLOW_MULTI), + "prog3_attach_to_cg3_multi", "errno=%d\n", errno)) + goto err; + + if (CHECK(bpf_prog_attach(allow_prog[4], cg4, BPF_CGROUP_INET_EGRESS, + BPF_F_ALLOW_OVERRIDE), + "prog4_attach_to_cg4_override", "errno=%d\n", errno)) + goto err; + + if (CHECK(bpf_prog_attach(allow_prog[5], cg5, BPF_CGROUP_INET_EGRESS, 0), + "prog5_attach_to_cg5_none", "errno=%d\n", errno)) + goto err; + + CHECK_FAIL(system(PING_CMD)); + CHECK_FAIL(bpf_map_lookup_elem(map_fd, &key, &value)); + CHECK_FAIL(value != 1 + 2 + 8 + 32); + + /* query the number of effective progs in cg5 */ + CHECK_FAIL(bpf_prog_query(cg5, BPF_CGROUP_INET_EGRESS, + BPF_F_QUERY_EFFECTIVE, NULL, NULL, &prog_cnt)); + CHECK_FAIL(prog_cnt != 4); + /* retrieve prog_ids of effective progs in cg5 */ + CHECK_FAIL(bpf_prog_query(cg5, BPF_CGROUP_INET_EGRESS, + BPF_F_QUERY_EFFECTIVE, &attach_flags, + prog_ids, &prog_cnt)); + CHECK_FAIL(prog_cnt != 4); + CHECK_FAIL(attach_flags != 0); + saved_prog_id = prog_ids[0]; + /* check enospc handling */ + prog_ids[0] = 0; + prog_cnt = 2; + CHECK_FAIL(bpf_prog_query(cg5, BPF_CGROUP_INET_EGRESS, + BPF_F_QUERY_EFFECTIVE, &attach_flags, + prog_ids, &prog_cnt) != -1); + CHECK_FAIL(errno != ENOSPC); + CHECK_FAIL(prog_cnt != 4); + /* check that prog_ids are returned even when buffer is too small */ + CHECK_FAIL(prog_ids[0] != saved_prog_id); + /* retrieve prog_id of single attached prog in cg5 */ + prog_ids[0] = 0; + CHECK_FAIL(bpf_prog_query(cg5, BPF_CGROUP_INET_EGRESS, 0, NULL, + prog_ids, &prog_cnt)); + CHECK_FAIL(prog_cnt != 1); + CHECK_FAIL(prog_ids[0] != saved_prog_id); + + /* detach bottom program and ping again */ + if (CHECK(bpf_prog_detach2(-1, cg5, BPF_CGROUP_INET_EGRESS), + "prog_detach_from_cg5", "errno=%d\n", errno)) + goto err; + + value = 0; + CHECK_FAIL(bpf_map_update_elem(map_fd, &key, &value, 0)); + CHECK_FAIL(system(PING_CMD)); + CHECK_FAIL(bpf_map_lookup_elem(map_fd, &key, &value)); + CHECK_FAIL(value != 1 + 2 + 8 + 16); + + /* detach 3rd from bottom program and ping again */ + if (CHECK(!bpf_prog_detach2(0, cg3, BPF_CGROUP_INET_EGRESS), + "fail_prog_detach_from_cg3", "unexpected success\n")) + goto err; + + if (CHECK(bpf_prog_detach2(allow_prog[3], cg3, BPF_CGROUP_INET_EGRESS), + "prog3_detach_from_cg3", "errno=%d\n", errno)) + goto err; + + value = 0; + CHECK_FAIL(bpf_map_update_elem(map_fd, &key, &value, 0)); + CHECK_FAIL(system(PING_CMD)); + CHECK_FAIL(bpf_map_lookup_elem(map_fd, &key, &value)); + CHECK_FAIL(value != 1 + 2 + 16); + + /* detach 2nd from bottom program and ping again */ + if (CHECK(bpf_prog_detach2(-1, cg4, BPF_CGROUP_INET_EGRESS), + "prog_detach_from_cg4", "errno=%d\n", errno)) + goto err; + + value = 0; + CHECK_FAIL(bpf_map_update_elem(map_fd, &key, &value, 0)); + CHECK_FAIL(system(PING_CMD)); + CHECK_FAIL(bpf_map_lookup_elem(map_fd, &key, &value)); + CHECK_FAIL(value != 1 + 2 + 4); + + prog_cnt = 4; + CHECK_FAIL(bpf_prog_query(cg5, BPF_CGROUP_INET_EGRESS, + BPF_F_QUERY_EFFECTIVE, &attach_flags, + prog_ids, &prog_cnt)); + CHECK_FAIL(prog_cnt != 3); + CHECK_FAIL(attach_flags != 0); + CHECK_FAIL(bpf_prog_query(cg5, BPF_CGROUP_INET_EGRESS, 0, NULL, + prog_ids, &prog_cnt)); + CHECK_FAIL(prog_cnt != 0); + +err: + for (i = 0; i < ARRAY_SIZE(allow_prog); i++) + if (allow_prog[i] >= 0) + close(allow_prog[i]); + close(cg1); + close(cg2); + close(cg3); + close(cg4); + close(cg5); + cleanup_cgroup_environment(); +} diff --git a/tools/testing/selftests/bpf/prog_tests/cgroup_attach_override.c b/tools/testing/selftests/bpf/prog_tests/cgroup_attach_override.c new file mode 100644 index 0000000000000..9d8cb48b99dee --- /dev/null +++ b/tools/testing/selftests/bpf/prog_tests/cgroup_attach_override.c @@ -0,0 +1,148 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include + +#include "cgroup_helpers.h" + +#define FOO "/foo" +#define BAR "/foo/bar/" +#define PING_CMD "ping -q -c1 -w1 127.0.0.1 > /dev/null" + +char bpf_log_buf[BPF_LOG_BUF_SIZE]; + +static int prog_load(int verdict) +{ + struct bpf_insn prog[] = { + BPF_MOV64_IMM(BPF_REG_0, verdict), /* r0 = verdict */ + BPF_EXIT_INSN(), + }; + size_t insns_cnt = sizeof(prog) / sizeof(struct bpf_insn); + + return bpf_load_program(BPF_PROG_TYPE_CGROUP_SKB, + prog, insns_cnt, "GPL", 0, + bpf_log_buf, BPF_LOG_BUF_SIZE); +} + +void test_cgroup_attach_override(void) +{ + int drop_prog = -1, allow_prog = -1, foo = -1, bar = -1; + __u32 duration = 0; + + allow_prog = prog_load(1); + if (CHECK(allow_prog < 0, "prog_load_allow", + "verifier output:\n%s\n-------\n", bpf_log_buf)) + goto err; + + drop_prog = prog_load(0); + if (CHECK(drop_prog < 0, "prog_load_drop", + "verifier output:\n%s\n-------\n", bpf_log_buf)) + goto err; + + foo = test__join_cgroup(FOO); + if (CHECK(foo < 0, "cgroup_join_foo", "cgroup setup failed\n")) + goto err; + + if (CHECK(bpf_prog_attach(drop_prog, foo, BPF_CGROUP_INET_EGRESS, + BPF_F_ALLOW_OVERRIDE), + "prog_attach_drop_foo_override", + "attach prog to %s failed, errno=%d\n", FOO, errno)) + goto err; + + if (CHECK(!system(PING_CMD), "ping_fail", + "ping unexpectedly succeeded\n")) + goto err; + + bar = test__join_cgroup(BAR); + if (CHECK(bar < 0, "cgroup_join_bar", "cgroup setup failed\n")) + goto err; + + if (CHECK(!system(PING_CMD), "ping_fail", + "ping unexpectedly succeeded\n")) + goto err; + + if (CHECK(bpf_prog_attach(allow_prog, bar, BPF_CGROUP_INET_EGRESS, + BPF_F_ALLOW_OVERRIDE), + "prog_attach_allow_bar_override", + "attach prog to %s failed, errno=%d\n", BAR, errno)) + goto err; + + if (CHECK(system(PING_CMD), "ping_ok", "ping failed\n")) + goto err; + + if (CHECK(bpf_prog_detach(bar, BPF_CGROUP_INET_EGRESS), + "prog_detach_bar", + "detach prog from %s failed, errno=%d\n", BAR, errno)) + goto err; + + if (CHECK(!system(PING_CMD), "ping_fail", + "ping unexpectedly succeeded\n")) + goto err; + + if (CHECK(bpf_prog_attach(allow_prog, bar, BPF_CGROUP_INET_EGRESS, + BPF_F_ALLOW_OVERRIDE), + "prog_attach_allow_bar_override", + "attach prog to %s failed, errno=%d\n", BAR, errno)) + goto err; + + if (CHECK(bpf_prog_detach(foo, BPF_CGROUP_INET_EGRESS), + "prog_detach_foo", + "detach prog from %s failed, errno=%d\n", FOO, errno)) + goto err; + + if (CHECK(system(PING_CMD), "ping_ok", "ping failed\n")) + goto err; + + if (CHECK(bpf_prog_attach(allow_prog, bar, BPF_CGROUP_INET_EGRESS, + BPF_F_ALLOW_OVERRIDE), + "prog_attach_allow_bar_override", + "attach prog to %s failed, errno=%d\n", BAR, errno)) + goto err; + + if (CHECK(!bpf_prog_attach(allow_prog, bar, BPF_CGROUP_INET_EGRESS, 0), + "fail_prog_attach_allow_bar_none", + "attach prog to %s unexpectedly succeeded\n", BAR)) + goto err; + + if (CHECK(bpf_prog_detach(bar, BPF_CGROUP_INET_EGRESS), + "prog_detach_bar", + "detach prog from %s failed, errno=%d\n", BAR, errno)) + goto err; + + if (CHECK(!bpf_prog_detach(foo, BPF_CGROUP_INET_EGRESS), + "fail_prog_detach_foo", + "double detach from %s unexpectedly succeeded\n", FOO)) + goto err; + + if (CHECK(bpf_prog_attach(allow_prog, foo, BPF_CGROUP_INET_EGRESS, 0), + "prog_attach_allow_foo_none", + "attach prog to %s failed, errno=%d\n", FOO, errno)) + goto err; + + if (CHECK(!bpf_prog_attach(allow_prog, bar, BPF_CGROUP_INET_EGRESS, 0), + "fail_prog_attach_allow_bar_none", + "attach prog to %s unexpectedly succeeded\n", BAR)) + goto err; + + if (CHECK(!bpf_prog_attach(allow_prog, bar, BPF_CGROUP_INET_EGRESS, + BPF_F_ALLOW_OVERRIDE), + "fail_prog_attach_allow_bar_override", + "attach prog to %s unexpectedly succeeded\n", BAR)) + goto err; + + if (CHECK(!bpf_prog_attach(allow_prog, foo, BPF_CGROUP_INET_EGRESS, + BPF_F_ALLOW_OVERRIDE), + "fail_prog_attach_allow_foo_override", + "attach prog to %s unexpectedly succeeded\n", FOO)) + goto err; + + if (CHECK(bpf_prog_attach(drop_prog, foo, BPF_CGROUP_INET_EGRESS, 0), + "prog_attach_drop_foo_none", + "attach prog to %s failed, errno=%d\n", FOO, errno)) + goto err; + +err: + close(foo); + close(bar); + close(allow_prog); + close(drop_prog); +} diff --git a/tools/testing/selftests/bpf/test_cgroup_attach.c b/tools/testing/selftests/bpf/test_cgroup_attach.c deleted file mode 100644 index 7671909ee1cbd..0000000000000 --- a/tools/testing/selftests/bpf/test_cgroup_attach.c +++ /dev/null @@ -1,571 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 - -/* eBPF example program: - * - * - Creates arraymap in kernel with 4 bytes keys and 8 byte values - * - * - Loads eBPF program - * - * The eBPF program accesses the map passed in to store two pieces of - * information. The number of invocations of the program, which maps - * to the number of packets received, is stored to key 0. Key 1 is - * incremented on each iteration by the number of bytes stored in - * the skb. The program also stores the number of received bytes - * in the cgroup storage. - * - * - Attaches the new program to a cgroup using BPF_PROG_ATTACH - * - * - Every second, reads map[0] and map[1] to see how many bytes and - * packets were seen on any socket of tasks in the given cgroup. - */ - -#define _GNU_SOURCE - -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include "bpf_util.h" -#include "bpf_rlimit.h" -#include "cgroup_helpers.h" - -#define FOO "/foo" -#define BAR "/foo/bar/" -#define PING_CMD "ping -q -c1 -w1 127.0.0.1 > /dev/null" - -char bpf_log_buf[BPF_LOG_BUF_SIZE]; - -#ifdef DEBUG -#define debug(args...) printf(args) -#else -#define debug(args...) -#endif - -static int prog_load(int verdict) -{ - int ret; - struct bpf_insn prog[] = { - BPF_MOV64_IMM(BPF_REG_0, verdict), /* r0 = verdict */ - BPF_EXIT_INSN(), - }; - size_t insns_cnt = sizeof(prog) / sizeof(struct bpf_insn); - - ret = bpf_load_program(BPF_PROG_TYPE_CGROUP_SKB, - prog, insns_cnt, "GPL", 0, - bpf_log_buf, BPF_LOG_BUF_SIZE); - - if (ret < 0) { - log_err("Loading program"); - printf("Output from verifier:\n%s\n-------\n", bpf_log_buf); - return 0; - } - return ret; -} - -static int test_foo_bar(void) -{ - int drop_prog, allow_prog, foo = 0, bar = 0, rc = 0; - - allow_prog = prog_load(1); - if (!allow_prog) - goto err; - - drop_prog = prog_load(0); - if (!drop_prog) - goto err; - - if (setup_cgroup_environment()) - goto err; - - /* Create cgroup /foo, get fd, and join it */ - foo = create_and_get_cgroup(FOO); - if (foo < 0) - goto err; - - if (join_cgroup(FOO)) - goto err; - - if (bpf_prog_attach(drop_prog, foo, BPF_CGROUP_INET_EGRESS, - BPF_F_ALLOW_OVERRIDE)) { - log_err("Attaching prog to /foo"); - goto err; - } - - debug("Attached DROP prog. This ping in cgroup /foo should fail...\n"); - assert(system(PING_CMD) != 0); - - /* Create cgroup /foo/bar, get fd, and join it */ - bar = create_and_get_cgroup(BAR); - if (bar < 0) - goto err; - - if (join_cgroup(BAR)) - goto err; - - debug("Attached DROP prog. This ping in cgroup /foo/bar should fail...\n"); - assert(system(PING_CMD) != 0); - - if (bpf_prog_attach(allow_prog, bar, BPF_CGROUP_INET_EGRESS, - BPF_F_ALLOW_OVERRIDE)) { - log_err("Attaching prog to /foo/bar"); - goto err; - } - - debug("Attached PASS prog. This ping in cgroup /foo/bar should pass...\n"); - assert(system(PING_CMD) == 0); - - if (bpf_prog_detach(bar, BPF_CGROUP_INET_EGRESS)) { - log_err("Detaching program from /foo/bar"); - goto err; - } - - debug("Detached PASS from /foo/bar while DROP is attached to /foo.\n" - "This ping in cgroup /foo/bar should fail...\n"); - assert(system(PING_CMD) != 0); - - if (bpf_prog_attach(allow_prog, bar, BPF_CGROUP_INET_EGRESS, - BPF_F_ALLOW_OVERRIDE)) { - log_err("Attaching prog to /foo/bar"); - goto err; - } - - if (bpf_prog_detach(foo, BPF_CGROUP_INET_EGRESS)) { - log_err("Detaching program from /foo"); - goto err; - } - - debug("Attached PASS from /foo/bar and detached DROP from /foo.\n" - "This ping in cgroup /foo/bar should pass...\n"); - assert(system(PING_CMD) == 0); - - if (bpf_prog_attach(allow_prog, bar, BPF_CGROUP_INET_EGRESS, - BPF_F_ALLOW_OVERRIDE)) { - log_err("Attaching prog to /foo/bar"); - goto err; - } - - if (!bpf_prog_attach(allow_prog, bar, BPF_CGROUP_INET_EGRESS, 0)) { - errno = 0; - log_err("Unexpected success attaching prog to /foo/bar"); - goto err; - } - - if (bpf_prog_detach(bar, BPF_CGROUP_INET_EGRESS)) { - log_err("Detaching program from /foo/bar"); - goto err; - } - - if (!bpf_prog_detach(foo, BPF_CGROUP_INET_EGRESS)) { - errno = 0; - log_err("Unexpected success in double detach from /foo"); - goto err; - } - - if (bpf_prog_attach(allow_prog, foo, BPF_CGROUP_INET_EGRESS, 0)) { - log_err("Attaching non-overridable prog to /foo"); - goto err; - } - - if (!bpf_prog_attach(allow_prog, bar, BPF_CGROUP_INET_EGRESS, 0)) { - errno = 0; - log_err("Unexpected success attaching non-overridable prog to /foo/bar"); - goto err; - } - - if (!bpf_prog_attach(allow_prog, bar, BPF_CGROUP_INET_EGRESS, - BPF_F_ALLOW_OVERRIDE)) { - errno = 0; - log_err("Unexpected success attaching overridable prog to /foo/bar"); - goto err; - } - - if (!bpf_prog_attach(allow_prog, foo, BPF_CGROUP_INET_EGRESS, - BPF_F_ALLOW_OVERRIDE)) { - errno = 0; - log_err("Unexpected success attaching overridable prog to /foo"); - goto err; - } - - if (bpf_prog_attach(drop_prog, foo, BPF_CGROUP_INET_EGRESS, 0)) { - log_err("Attaching different non-overridable prog to /foo"); - goto err; - } - - goto out; - -err: - rc = 1; - -out: - close(foo); - close(bar); - cleanup_cgroup_environment(); - if (!rc) - printf("#override:PASS\n"); - else - printf("#override:FAIL\n"); - return rc; -} - -static int map_fd = -1; - -static int prog_load_cnt(int verdict, int val) -{ - int cgroup_storage_fd, percpu_cgroup_storage_fd; - - if (map_fd < 0) - map_fd = bpf_create_map(BPF_MAP_TYPE_ARRAY, 4, 8, 1, 0); - if (map_fd < 0) { - printf("failed to create map '%s'\n", strerror(errno)); - return -1; - } - - cgroup_storage_fd = bpf_create_map(BPF_MAP_TYPE_CGROUP_STORAGE, - sizeof(struct bpf_cgroup_storage_key), 8, 0, 0); - if (cgroup_storage_fd < 0) { - printf("failed to create map '%s'\n", strerror(errno)); - return -1; - } - - percpu_cgroup_storage_fd = bpf_create_map( - BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE, - sizeof(struct bpf_cgroup_storage_key), 8, 0, 0); - if (percpu_cgroup_storage_fd < 0) { - printf("failed to create map '%s'\n", strerror(errno)); - return -1; - } - - struct bpf_insn prog[] = { - BPF_MOV32_IMM(BPF_REG_0, 0), - BPF_STX_MEM(BPF_W, BPF_REG_10, BPF_REG_0, -4), /* *(u32 *)(fp - 4) = r0 */ - BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), - BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -4), /* r2 = fp - 4 */ - BPF_LD_MAP_FD(BPF_REG_1, map_fd), - BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem), - BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 2), - BPF_MOV64_IMM(BPF_REG_1, val), /* r1 = 1 */ - BPF_RAW_INSN(BPF_STX | BPF_XADD | BPF_DW, BPF_REG_0, BPF_REG_1, 0, 0), /* xadd r0 += r1 */ - - BPF_LD_MAP_FD(BPF_REG_1, cgroup_storage_fd), - BPF_MOV64_IMM(BPF_REG_2, 0), - BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_get_local_storage), - BPF_MOV64_IMM(BPF_REG_1, val), - BPF_RAW_INSN(BPF_STX | BPF_XADD | BPF_W, BPF_REG_0, BPF_REG_1, 0, 0), - - BPF_LD_MAP_FD(BPF_REG_1, percpu_cgroup_storage_fd), - BPF_MOV64_IMM(BPF_REG_2, 0), - BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_get_local_storage), - BPF_LDX_MEM(BPF_W, BPF_REG_3, BPF_REG_0, 0), - BPF_ALU64_IMM(BPF_ADD, BPF_REG_3, 0x1), - BPF_STX_MEM(BPF_W, BPF_REG_0, BPF_REG_3, 0), - - BPF_MOV64_IMM(BPF_REG_0, verdict), /* r0 = verdict */ - BPF_EXIT_INSN(), - }; - size_t insns_cnt = sizeof(prog) / sizeof(struct bpf_insn); - int ret; - - ret = bpf_load_program(BPF_PROG_TYPE_CGROUP_SKB, - prog, insns_cnt, "GPL", 0, - bpf_log_buf, BPF_LOG_BUF_SIZE); - - if (ret < 0) { - log_err("Loading program"); - printf("Output from verifier:\n%s\n-------\n", bpf_log_buf); - return 0; - } - close(cgroup_storage_fd); - return ret; -} - - -static int test_multiprog(void) -{ - __u32 prog_ids[4], prog_cnt = 0, attach_flags, saved_prog_id; - int cg1 = 0, cg2 = 0, cg3 = 0, cg4 = 0, cg5 = 0, key = 0; - int drop_prog, allow_prog[6] = {}, rc = 0; - unsigned long long value; - int i = 0; - - for (i = 0; i < 6; i++) { - allow_prog[i] = prog_load_cnt(1, 1 << i); - if (!allow_prog[i]) - goto err; - } - drop_prog = prog_load_cnt(0, 1); - if (!drop_prog) - goto err; - - if (setup_cgroup_environment()) - goto err; - - cg1 = create_and_get_cgroup("/cg1"); - if (cg1 < 0) - goto err; - cg2 = create_and_get_cgroup("/cg1/cg2"); - if (cg2 < 0) - goto err; - cg3 = create_and_get_cgroup("/cg1/cg2/cg3"); - if (cg3 < 0) - goto err; - cg4 = create_and_get_cgroup("/cg1/cg2/cg3/cg4"); - if (cg4 < 0) - goto err; - cg5 = create_and_get_cgroup("/cg1/cg2/cg3/cg4/cg5"); - if (cg5 < 0) - goto err; - - if (join_cgroup("/cg1/cg2/cg3/cg4/cg5")) - goto err; - - if (bpf_prog_attach(allow_prog[0], cg1, BPF_CGROUP_INET_EGRESS, - BPF_F_ALLOW_MULTI)) { - log_err("Attaching prog to cg1"); - goto err; - } - if (!bpf_prog_attach(allow_prog[0], cg1, BPF_CGROUP_INET_EGRESS, - BPF_F_ALLOW_MULTI)) { - log_err("Unexpected success attaching the same prog to cg1"); - goto err; - } - if (bpf_prog_attach(allow_prog[1], cg1, BPF_CGROUP_INET_EGRESS, - BPF_F_ALLOW_MULTI)) { - log_err("Attaching prog2 to cg1"); - goto err; - } - if (bpf_prog_attach(allow_prog[2], cg2, BPF_CGROUP_INET_EGRESS, - BPF_F_ALLOW_OVERRIDE)) { - log_err("Attaching prog to cg2"); - goto err; - } - if (bpf_prog_attach(allow_prog[3], cg3, BPF_CGROUP_INET_EGRESS, - BPF_F_ALLOW_MULTI)) { - log_err("Attaching prog to cg3"); - goto err; - } - if (bpf_prog_attach(allow_prog[4], cg4, BPF_CGROUP_INET_EGRESS, - BPF_F_ALLOW_OVERRIDE)) { - log_err("Attaching prog to cg4"); - goto err; - } - if (bpf_prog_attach(allow_prog[5], cg5, BPF_CGROUP_INET_EGRESS, 0)) { - log_err("Attaching prog to cg5"); - goto err; - } - assert(system(PING_CMD) == 0); - assert(bpf_map_lookup_elem(map_fd, &key, &value) == 0); - assert(value == 1 + 2 + 8 + 32); - - /* query the number of effective progs in cg5 */ - assert(bpf_prog_query(cg5, BPF_CGROUP_INET_EGRESS, BPF_F_QUERY_EFFECTIVE, - NULL, NULL, &prog_cnt) == 0); - assert(prog_cnt == 4); - /* retrieve prog_ids of effective progs in cg5 */ - assert(bpf_prog_query(cg5, BPF_CGROUP_INET_EGRESS, BPF_F_QUERY_EFFECTIVE, - &attach_flags, prog_ids, &prog_cnt) == 0); - assert(prog_cnt == 4); - assert(attach_flags == 0); - saved_prog_id = prog_ids[0]; - /* check enospc handling */ - prog_ids[0] = 0; - prog_cnt = 2; - assert(bpf_prog_query(cg5, BPF_CGROUP_INET_EGRESS, BPF_F_QUERY_EFFECTIVE, - &attach_flags, prog_ids, &prog_cnt) == -1 && - errno == ENOSPC); - assert(prog_cnt == 4); - /* check that prog_ids are returned even when buffer is too small */ - assert(prog_ids[0] == saved_prog_id); - /* retrieve prog_id of single attached prog in cg5 */ - prog_ids[0] = 0; - assert(bpf_prog_query(cg5, BPF_CGROUP_INET_EGRESS, 0, - NULL, prog_ids, &prog_cnt) == 0); - assert(prog_cnt == 1); - assert(prog_ids[0] == saved_prog_id); - - /* detach bottom program and ping again */ - if (bpf_prog_detach2(-1, cg5, BPF_CGROUP_INET_EGRESS)) { - log_err("Detaching prog from cg5"); - goto err; - } - value = 0; - assert(bpf_map_update_elem(map_fd, &key, &value, 0) == 0); - assert(system(PING_CMD) == 0); - assert(bpf_map_lookup_elem(map_fd, &key, &value) == 0); - assert(value == 1 + 2 + 8 + 16); - - /* detach 3rd from bottom program and ping again */ - errno = 0; - if (!bpf_prog_detach2(0, cg3, BPF_CGROUP_INET_EGRESS)) { - log_err("Unexpected success on detach from cg3"); - goto err; - } - if (bpf_prog_detach2(allow_prog[3], cg3, BPF_CGROUP_INET_EGRESS)) { - log_err("Detaching from cg3"); - goto err; - } - value = 0; - assert(bpf_map_update_elem(map_fd, &key, &value, 0) == 0); - assert(system(PING_CMD) == 0); - assert(bpf_map_lookup_elem(map_fd, &key, &value) == 0); - assert(value == 1 + 2 + 16); - - /* detach 2nd from bottom program and ping again */ - if (bpf_prog_detach2(-1, cg4, BPF_CGROUP_INET_EGRESS)) { - log_err("Detaching prog from cg4"); - goto err; - } - value = 0; - assert(bpf_map_update_elem(map_fd, &key, &value, 0) == 0); - assert(system(PING_CMD) == 0); - assert(bpf_map_lookup_elem(map_fd, &key, &value) == 0); - assert(value == 1 + 2 + 4); - - prog_cnt = 4; - assert(bpf_prog_query(cg5, BPF_CGROUP_INET_EGRESS, BPF_F_QUERY_EFFECTIVE, - &attach_flags, prog_ids, &prog_cnt) == 0); - assert(prog_cnt == 3); - assert(attach_flags == 0); - assert(bpf_prog_query(cg5, BPF_CGROUP_INET_EGRESS, 0, - NULL, prog_ids, &prog_cnt) == 0); - assert(prog_cnt == 0); - goto out; -err: - rc = 1; - -out: - for (i = 0; i < 6; i++) - if (allow_prog[i] > 0) - close(allow_prog[i]); - close(cg1); - close(cg2); - close(cg3); - close(cg4); - close(cg5); - cleanup_cgroup_environment(); - if (!rc) - printf("#multi:PASS\n"); - else - printf("#multi:FAIL\n"); - return rc; -} - -static int test_autodetach(void) -{ - __u32 prog_cnt = 4, attach_flags; - int allow_prog[2] = {0}; - __u32 prog_ids[2] = {0}; - int cg = 0, i, rc = -1; - void *ptr = NULL; - int attempts; - - for (i = 0; i < ARRAY_SIZE(allow_prog); i++) { - allow_prog[i] = prog_load_cnt(1, 1 << i); - if (!allow_prog[i]) - goto err; - } - - if (setup_cgroup_environment()) - goto err; - - /* create a cgroup, attach two programs and remember their ids */ - cg = create_and_get_cgroup("/cg_autodetach"); - if (cg < 0) - goto err; - - if (join_cgroup("/cg_autodetach")) - goto err; - - for (i = 0; i < ARRAY_SIZE(allow_prog); i++) { - if (bpf_prog_attach(allow_prog[i], cg, BPF_CGROUP_INET_EGRESS, - BPF_F_ALLOW_MULTI)) { - log_err("Attaching prog[%d] to cg:egress", i); - goto err; - } - } - - /* make sure that programs are attached and run some traffic */ - assert(bpf_prog_query(cg, BPF_CGROUP_INET_EGRESS, 0, &attach_flags, - prog_ids, &prog_cnt) == 0); - assert(system(PING_CMD) == 0); - - /* allocate some memory (4Mb) to pin the original cgroup */ - ptr = malloc(4 * (1 << 20)); - if (!ptr) - goto err; - - /* close programs and cgroup fd */ - for (i = 0; i < ARRAY_SIZE(allow_prog); i++) { - close(allow_prog[i]); - allow_prog[i] = 0; - } - - close(cg); - cg = 0; - - /* leave the cgroup and remove it. don't detach programs */ - cleanup_cgroup_environment(); - - /* wait for the asynchronous auto-detachment. - * wait for no more than 5 sec and give up. - */ - for (i = 0; i < ARRAY_SIZE(prog_ids); i++) { - for (attempts = 5; attempts >= 0; attempts--) { - int fd = bpf_prog_get_fd_by_id(prog_ids[i]); - - if (fd < 0) - break; - - /* don't leave the fd open */ - close(fd); - - if (!attempts) - goto err; - - sleep(1); - } - } - - rc = 0; -err: - for (i = 0; i < ARRAY_SIZE(allow_prog); i++) - if (allow_prog[i] > 0) - close(allow_prog[i]); - if (cg) - close(cg); - free(ptr); - cleanup_cgroup_environment(); - if (!rc) - printf("#autodetach:PASS\n"); - else - printf("#autodetach:FAIL\n"); - return rc; -} - -int main(void) -{ - int (*tests[])(void) = { - test_foo_bar, - test_multiprog, - test_autodetach, - }; - int errors = 0; - int i; - - for (i = 0; i < ARRAY_SIZE(tests); i++) - if (tests[i]()) - errors++; - - if (errors) - printf("test_cgroup_attach:FAIL\n"); - else - printf("test_cgroup_attach:PASS\n"); - - return errors ? EXIT_FAILURE : EXIT_SUCCESS; -} -- GitLab From 06ac0186bd242d44f5703ce64ee0077b317c86bb Mon Sep 17 00:00:00 2001 From: Andrey Ignatov Date: Wed, 18 Dec 2019 23:44:38 -0800 Subject: [PATCH 1146/1437] selftests/bpf: Test BPF_F_REPLACE in cgroup_attach_multi Test replacing a cgroup-bpf program attached with BPF_F_ALLOW_MULTI and possible failure modes: invalid combination of flags, invalid replace_bpf_fd, replacing a non-attachd to specified cgroup program. Example of program replacing: # gdb -q --args ./test_progs --name=cgroup_attach_multi ... Breakpoint 1, test_cgroup_attach_multi () at cgroup_attach_multi.c:227 (gdb) [1]+ Stopped gdb -q --args ./test_progs --name=cgroup_attach_multi # bpftool c s /mnt/cgroup2/cgroup-test-work-dir/cg1 ID AttachType AttachFlags Name 2133 egress multi 2134 egress multi # fg gdb -q --args ./test_progs --name=cgroup_attach_multi (gdb) c Continuing. Breakpoint 2, test_cgroup_attach_multi () at cgroup_attach_multi.c:233 (gdb) [1]+ Stopped gdb -q --args ./test_progs --name=cgroup_attach_multi # bpftool c s /mnt/cgroup2/cgroup-test-work-dir/cg1 ID AttachType AttachFlags Name 2139 egress multi 2134 egress multi Signed-off-by: Andrey Ignatov Signed-off-by: Alexei Starovoitov Acked-by: Andrii Nakryiko Link: https://lore.kernel.org/bpf/7b9b83e8d5fb82e15b034341bd40b6fb2431eeba.1576741281.git.rdna@fb.com --- .../bpf/prog_tests/cgroup_attach_multi.c | 53 +++++++++++++++++-- 1 file changed, 50 insertions(+), 3 deletions(-) diff --git a/tools/testing/selftests/bpf/prog_tests/cgroup_attach_multi.c b/tools/testing/selftests/bpf/prog_tests/cgroup_attach_multi.c index 4eaab74350440..2ff21dbce179c 100644 --- a/tools/testing/selftests/bpf/prog_tests/cgroup_attach_multi.c +++ b/tools/testing/selftests/bpf/prog_tests/cgroup_attach_multi.c @@ -78,7 +78,8 @@ void test_cgroup_attach_multi(void) { __u32 prog_ids[4], prog_cnt = 0, attach_flags, saved_prog_id; int cg1 = 0, cg2 = 0, cg3 = 0, cg4 = 0, cg5 = 0, key = 0; - int allow_prog[6] = {-1}; + DECLARE_LIBBPF_OPTS(bpf_prog_attach_opts, attach_opts); + int allow_prog[7] = {-1}; unsigned long long value; __u32 duration = 0; int i = 0; @@ -189,6 +190,52 @@ void test_cgroup_attach_multi(void) CHECK_FAIL(bpf_map_lookup_elem(map_fd, &key, &value)); CHECK_FAIL(value != 1 + 2 + 8 + 16); + /* test replace */ + + attach_opts.flags = BPF_F_ALLOW_OVERRIDE | BPF_F_REPLACE; + attach_opts.replace_prog_fd = allow_prog[0]; + if (CHECK(!bpf_prog_attach_xattr(allow_prog[6], cg1, + BPF_CGROUP_INET_EGRESS, &attach_opts), + "fail_prog_replace_override", "unexpected success\n")) + goto err; + CHECK_FAIL(errno != EINVAL); + + attach_opts.flags = BPF_F_REPLACE; + if (CHECK(!bpf_prog_attach_xattr(allow_prog[6], cg1, + BPF_CGROUP_INET_EGRESS, &attach_opts), + "fail_prog_replace_no_multi", "unexpected success\n")) + goto err; + CHECK_FAIL(errno != EINVAL); + + attach_opts.flags = BPF_F_ALLOW_MULTI | BPF_F_REPLACE; + attach_opts.replace_prog_fd = -1; + if (CHECK(!bpf_prog_attach_xattr(allow_prog[6], cg1, + BPF_CGROUP_INET_EGRESS, &attach_opts), + "fail_prog_replace_bad_fd", "unexpected success\n")) + goto err; + CHECK_FAIL(errno != EBADF); + + /* replacing a program that is not attached to cgroup should fail */ + attach_opts.replace_prog_fd = allow_prog[3]; + if (CHECK(!bpf_prog_attach_xattr(allow_prog[6], cg1, + BPF_CGROUP_INET_EGRESS, &attach_opts), + "fail_prog_replace_no_ent", "unexpected success\n")) + goto err; + CHECK_FAIL(errno != ENOENT); + + /* replace 1st from the top program */ + attach_opts.replace_prog_fd = allow_prog[0]; + if (CHECK(bpf_prog_attach_xattr(allow_prog[6], cg1, + BPF_CGROUP_INET_EGRESS, &attach_opts), + "prog_replace", "errno=%d\n", errno)) + goto err; + + value = 0; + CHECK_FAIL(bpf_map_update_elem(map_fd, &key, &value, 0)); + CHECK_FAIL(system(PING_CMD)); + CHECK_FAIL(bpf_map_lookup_elem(map_fd, &key, &value)); + CHECK_FAIL(value != 64 + 2 + 8 + 16); + /* detach 3rd from bottom program and ping again */ if (CHECK(!bpf_prog_detach2(0, cg3, BPF_CGROUP_INET_EGRESS), "fail_prog_detach_from_cg3", "unexpected success\n")) @@ -202,7 +249,7 @@ void test_cgroup_attach_multi(void) CHECK_FAIL(bpf_map_update_elem(map_fd, &key, &value, 0)); CHECK_FAIL(system(PING_CMD)); CHECK_FAIL(bpf_map_lookup_elem(map_fd, &key, &value)); - CHECK_FAIL(value != 1 + 2 + 16); + CHECK_FAIL(value != 64 + 2 + 16); /* detach 2nd from bottom program and ping again */ if (CHECK(bpf_prog_detach2(-1, cg4, BPF_CGROUP_INET_EGRESS), @@ -213,7 +260,7 @@ void test_cgroup_attach_multi(void) CHECK_FAIL(bpf_map_update_elem(map_fd, &key, &value, 0)); CHECK_FAIL(system(PING_CMD)); CHECK_FAIL(bpf_map_lookup_elem(map_fd, &key, &value)); - CHECK_FAIL(value != 1 + 2 + 4); + CHECK_FAIL(value != 64 + 2 + 4); prog_cnt = 4; CHECK_FAIL(bpf_prog_query(cg5, BPF_CGROUP_INET_EGRESS, -- GitLab From 0312a3d4b43c0045869379affc0e228e36411c78 Mon Sep 17 00:00:00 2001 From: Andreas Schwab Date: Tue, 17 Dec 2019 12:15:16 +0100 Subject: [PATCH 1147/1437] riscv: Fix use of undefined config option CONFIG_CONFIG_MMU In Kconfig files, config options are written without the CONFIG_ prefix. Fixes: 6bd33e1ece52 ("riscv: add nommu support") Signed-off-by: Andreas Schwab Reviewed-by: Anup Patel Signed-off-by: Paul Walmsley --- arch/riscv/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig index 759ffb00267cf..d8efbaa78d676 100644 --- a/arch/riscv/Kconfig +++ b/arch/riscv/Kconfig @@ -154,7 +154,7 @@ config GENERIC_HWEIGHT def_bool y config FIX_EARLYCON_MEM - def_bool CONFIG_MMU + def_bool MMU config PGTABLE_LEVELS int -- GitLab From d411cf02ed0260dacc4b2fd61dd5040fc2aa97e7 Mon Sep 17 00:00:00 2001 From: Greentime Hu Date: Thu, 19 Dec 2019 14:44:59 +0800 Subject: [PATCH 1148/1437] riscv: fix scratch register clearing in M-mode. This patch fixes that the sscratch register clearing in M-mode. It cleared sscratch register in M-mode, but it should clear mscratch register. That will cause kernel trap if the CPU core doesn't support S-mode when trying to access sscratch. Fixes: 9e80635619b5 ("riscv: clear the instruction cache and all registers when booting") Signed-off-by: Greentime Hu Reviewed-by: Anup Patel Signed-off-by: Paul Walmsley --- arch/riscv/kernel/head.S | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/riscv/kernel/head.S b/arch/riscv/kernel/head.S index 84a6f0a4b120b..797802c73dee2 100644 --- a/arch/riscv/kernel/head.S +++ b/arch/riscv/kernel/head.S @@ -246,7 +246,7 @@ ENTRY(reset_regs) li t4, 0 li t5, 0 li t6, 0 - csrw sscratch, 0 + csrw CSR_SCRATCH, 0 #ifdef CONFIG_FPU csrr t0, CSR_MISA -- GitLab From 01f52e16b868ce22069425c69f2c8e3ef4077b5c Mon Sep 17 00:00:00 2001 From: David Abdurachmanov Date: Wed, 18 Dec 2019 10:28:11 +0200 Subject: [PATCH 1149/1437] riscv: define vmemmap before pfn_to_page calls pfn_to_page & page_to_pfn depend on vmemmap being available before the calls if kernel is configured with CONFIG_SPARSEMEM_VMEMMAP=y. This was caused by NOMMU changes which moved vmemmap definition bellow functions definitions calling pfn_to_page & page_to_pfn. Noticed while compiled 5.5-rc2 kernel for Fedora/RISCV. v2: - Add a comment for vmemmap in source Signed-off-by: David Abdurachmanov Fixes: 6bd33e1ece52 ("riscv: add nommu support") Reviewed-by: Anup Patel Signed-off-by: Paul Walmsley --- arch/riscv/include/asm/pgtable.h | 38 ++++++++++++++++++-------------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/arch/riscv/include/asm/pgtable.h b/arch/riscv/include/asm/pgtable.h index 7ff0ed4f292e4..36ae017613527 100644 --- a/arch/riscv/include/asm/pgtable.h +++ b/arch/riscv/include/asm/pgtable.h @@ -90,6 +90,27 @@ extern pgd_t swapper_pg_dir[]; #define __S110 PAGE_SHARED_EXEC #define __S111 PAGE_SHARED_EXEC +#define VMALLOC_SIZE (KERN_VIRT_SIZE >> 1) +#define VMALLOC_END (PAGE_OFFSET - 1) +#define VMALLOC_START (PAGE_OFFSET - VMALLOC_SIZE) + +/* + * Roughly size the vmemmap space to be large enough to fit enough + * struct pages to map half the virtual address space. Then + * position vmemmap directly below the VMALLOC region. + */ +#define VMEMMAP_SHIFT \ + (CONFIG_VA_BITS - PAGE_SHIFT - 1 + STRUCT_PAGE_MAX_SHIFT) +#define VMEMMAP_SIZE BIT(VMEMMAP_SHIFT) +#define VMEMMAP_END (VMALLOC_START - 1) +#define VMEMMAP_START (VMALLOC_START - VMEMMAP_SIZE) + +/* + * Define vmemmap for pfn_to_page & page_to_pfn calls. Needed if kernel + * is configured with CONFIG_SPARSEMEM_VMEMMAP enabled. + */ +#define vmemmap ((struct page *)VMEMMAP_START) + static inline int pmd_present(pmd_t pmd) { return (pmd_val(pmd) & (_PAGE_PRESENT | _PAGE_PROT_NONE)); @@ -400,23 +421,6 @@ static inline int ptep_clear_flush_young(struct vm_area_struct *vma, #define __pte_to_swp_entry(pte) ((swp_entry_t) { pte_val(pte) }) #define __swp_entry_to_pte(x) ((pte_t) { (x).val }) -#define VMALLOC_SIZE (KERN_VIRT_SIZE >> 1) -#define VMALLOC_END (PAGE_OFFSET - 1) -#define VMALLOC_START (PAGE_OFFSET - VMALLOC_SIZE) - -/* - * Roughly size the vmemmap space to be large enough to fit enough - * struct pages to map half the virtual address space. Then - * position vmemmap directly below the VMALLOC region. - */ -#define VMEMMAP_SHIFT \ - (CONFIG_VA_BITS - PAGE_SHIFT - 1 + STRUCT_PAGE_MAX_SHIFT) -#define VMEMMAP_SIZE BIT(VMEMMAP_SHIFT) -#define VMEMMAP_END (VMALLOC_START - 1) -#define VMEMMAP_START (VMALLOC_START - VMEMMAP_SIZE) - -#define vmemmap ((struct page *)VMEMMAP_START) - #define PCI_IO_SIZE SZ_16M #define PCI_IO_END VMEMMAP_START #define PCI_IO_START (PCI_IO_END - PCI_IO_SIZE) -- GitLab From 9209fb51896fe0eef8dfac85afe1f357e9265c0d Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Thu, 7 Nov 2019 10:20:39 +0100 Subject: [PATCH 1150/1437] riscv: move sifive_l2_cache.c to drivers/soc The sifive_l2_cache.c is in no way related to RISC-V architecture memory management. It is a little stub driver working around the fact that the EDAC maintainers prefer their drivers to be structured in a certain way that doesn't fit the SiFive SOCs. Move the file to drivers/soc and add a Kconfig option for it, as well as the whole drivers/soc boilerplate for CONFIG_SOC_SIFIVE. Fixes: a967a289f169 ("RISC-V: sifive_l2_cache: Add L2 cache controller driver for SiFive SoCs") Signed-off-by: Christoph Hellwig Reviewed-by: Borislav Petkov [paul.walmsley@sifive.com: keep the MAINTAINERS change specific to the L2$ controller code] Signed-off-by: Paul Walmsley --- MAINTAINERS | 1 + arch/riscv/mm/Makefile | 1 - drivers/edac/Kconfig | 2 +- drivers/soc/Kconfig | 1 + drivers/soc/Makefile | 1 + drivers/soc/sifive/Kconfig | 10 ++++++++++ drivers/soc/sifive/Makefile | 3 +++ .../riscv/mm => drivers/soc/sifive}/sifive_l2_cache.c | 0 8 files changed, 17 insertions(+), 2 deletions(-) create mode 100644 drivers/soc/sifive/Kconfig create mode 100644 drivers/soc/sifive/Makefile rename {arch/riscv/mm => drivers/soc/sifive}/sifive_l2_cache.c (100%) diff --git a/MAINTAINERS b/MAINTAINERS index a049abccaa269..4bc8405e632a8 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -6027,6 +6027,7 @@ M: Yash Shah L: linux-edac@vger.kernel.org S: Supported F: drivers/edac/sifive_edac.c +F: drivers/soc/sifive_l2_cache.c EDAC-SKYLAKE M: Tony Luck diff --git a/arch/riscv/mm/Makefile b/arch/riscv/mm/Makefile index 3c8b332584579..a1bd95c8047a1 100644 --- a/arch/riscv/mm/Makefile +++ b/arch/riscv/mm/Makefile @@ -10,7 +10,6 @@ obj-y += extable.o obj-$(CONFIG_MMU) += fault.o obj-y += cacheflush.o obj-y += context.o -obj-y += sifive_l2_cache.o ifeq ($(CONFIG_MMU),y) obj-$(CONFIG_SMP) += tlbflush.o diff --git a/drivers/edac/Kconfig b/drivers/edac/Kconfig index 417dad6355268..5c8272329a65d 100644 --- a/drivers/edac/Kconfig +++ b/drivers/edac/Kconfig @@ -462,7 +462,7 @@ config EDAC_ALTERA_SDMMC config EDAC_SIFIVE bool "Sifive platform EDAC driver" - depends on EDAC=y && RISCV + depends on EDAC=y && SIFIVE_L2 help Support for error detection and correction on the SiFive SoCs. diff --git a/drivers/soc/Kconfig b/drivers/soc/Kconfig index 833e04a7835c5..1778f8c62861b 100644 --- a/drivers/soc/Kconfig +++ b/drivers/soc/Kconfig @@ -14,6 +14,7 @@ source "drivers/soc/qcom/Kconfig" source "drivers/soc/renesas/Kconfig" source "drivers/soc/rockchip/Kconfig" source "drivers/soc/samsung/Kconfig" +source "drivers/soc/sifive/Kconfig" source "drivers/soc/sunxi/Kconfig" source "drivers/soc/tegra/Kconfig" source "drivers/soc/ti/Kconfig" diff --git a/drivers/soc/Makefile b/drivers/soc/Makefile index 2ec3550035243..8b49d782a1ab7 100644 --- a/drivers/soc/Makefile +++ b/drivers/soc/Makefile @@ -20,6 +20,7 @@ obj-y += qcom/ obj-y += renesas/ obj-$(CONFIG_ARCH_ROCKCHIP) += rockchip/ obj-$(CONFIG_SOC_SAMSUNG) += samsung/ +obj-$(CONFIG_SOC_SIFIVE) += sifive/ obj-y += sunxi/ obj-$(CONFIG_ARCH_TEGRA) += tegra/ obj-y += ti/ diff --git a/drivers/soc/sifive/Kconfig b/drivers/soc/sifive/Kconfig new file mode 100644 index 0000000000000..58cf8c40d08d5 --- /dev/null +++ b/drivers/soc/sifive/Kconfig @@ -0,0 +1,10 @@ +# SPDX-License-Identifier: GPL-2.0 + +if SOC_SIFIVE + +config SIFIVE_L2 + bool "Sifive L2 Cache controller" + help + Support for the L2 cache controller on SiFive platforms. + +endif diff --git a/drivers/soc/sifive/Makefile b/drivers/soc/sifive/Makefile new file mode 100644 index 0000000000000..b5caff77938f6 --- /dev/null +++ b/drivers/soc/sifive/Makefile @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0 + +obj-$(CONFIG_SIFIVE_L2) += sifive_l2_cache.o diff --git a/arch/riscv/mm/sifive_l2_cache.c b/drivers/soc/sifive/sifive_l2_cache.c similarity index 100% rename from arch/riscv/mm/sifive_l2_cache.c rename to drivers/soc/sifive/sifive_l2_cache.c -- GitLab From 589b72894f53124a39d1bb3c0cecaf9dcabac417 Mon Sep 17 00:00:00 2001 From: Nathan Chancellor Date: Mon, 9 Dec 2019 13:14:44 -0700 Subject: [PATCH 1151/1437] xen/blkfront: Adjust indentation in xlvbd_alloc_gendisk MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Clang warns: ../drivers/block/xen-blkfront.c:1117:4: warning: misleading indentation; statement is not part of the previous 'if' [-Wmisleading-indentation] nr_parts = PARTS_PER_DISK; ^ ../drivers/block/xen-blkfront.c:1115:3: note: previous statement is here if (err) ^ This is because there is a space at the beginning of this line; remove it so that the indentation is consistent according to the Linux kernel coding style and clang no longer warns. While we are here, the previous line has some trailing whitespace; clean that up as well. Fixes: c80a420995e7 ("xen-blkfront: handle Xen major numbers other than XENVBD") Link: https://github.com/ClangBuiltLinux/linux/issues/791 Signed-off-by: Nathan Chancellor Reviewed-by: Juergen Gross Acked-by: Roger Pau Monné Signed-off-by: Juergen Gross --- drivers/block/xen-blkfront.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/block/xen-blkfront.c b/drivers/block/xen-blkfront.c index a74d03913822d..c02be06c52995 100644 --- a/drivers/block/xen-blkfront.c +++ b/drivers/block/xen-blkfront.c @@ -1113,8 +1113,8 @@ static int xlvbd_alloc_gendisk(blkif_sector_t capacity, if (!VDEV_IS_EXTENDED(info->vdevice)) { err = xen_translate_vdev(info->vdevice, &minor, &offset); if (err) - return err; - nr_parts = PARTS_PER_DISK; + return err; + nr_parts = PARTS_PER_DISK; } else { minor = BLKIF_MINOR_EXT(info->vdevice); nr_parts = PARTS_PER_EXT_DISK; -- GitLab From c534374ecf044384ddd24474b91fd0d31e720464 Mon Sep 17 00:00:00 2001 From: Paul Durrant Date: Wed, 11 Dec 2019 15:29:53 +0000 Subject: [PATCH 1152/1437] xenbus: move xenbus_dev_shutdown() into frontend code... ...and make it static xenbus_dev_shutdown() is seemingly intended to cause clean shutdown of PV frontends when a guest is rebooted. Indeed the function waits for a conpletion which is only set by a call to xenbus_frontend_closed(). This patch removes the shutdown() method from backends and moves xenbus_dev_shutdown() from xenbus_probe.c into xenbus_probe_frontend.c, renaming it appropriately and making it static. NOTE: In the case where the backend is running in a driver domain, the toolstack should have already terminated any frontends that may be using it (since Xen does not support re-startable PV driver domains) so xenbus_dev_shutdown() should never be called. Signed-off-by: Paul Durrant Reviewed-by: Juergen Gross Signed-off-by: Juergen Gross --- drivers/xen/xenbus/xenbus.h | 2 -- drivers/xen/xenbus/xenbus_probe.c | 23 --------------------- drivers/xen/xenbus/xenbus_probe_backend.c | 1 - drivers/xen/xenbus/xenbus_probe_frontend.c | 24 +++++++++++++++++++++- 4 files changed, 23 insertions(+), 27 deletions(-) diff --git a/drivers/xen/xenbus/xenbus.h b/drivers/xen/xenbus/xenbus.h index d75a2385b37c7..5f5b8a7d5b80b 100644 --- a/drivers/xen/xenbus/xenbus.h +++ b/drivers/xen/xenbus/xenbus.h @@ -116,8 +116,6 @@ int xenbus_probe_devices(struct xen_bus_type *bus); void xenbus_dev_changed(const char *node, struct xen_bus_type *bus); -void xenbus_dev_shutdown(struct device *_dev); - int xenbus_dev_suspend(struct device *dev); int xenbus_dev_resume(struct device *dev); int xenbus_dev_cancel(struct device *dev); diff --git a/drivers/xen/xenbus/xenbus_probe.c b/drivers/xen/xenbus/xenbus_probe.c index c21be6e9d38a6..5aa29396c9e39 100644 --- a/drivers/xen/xenbus/xenbus_probe.c +++ b/drivers/xen/xenbus/xenbus_probe.c @@ -281,29 +281,6 @@ int xenbus_dev_remove(struct device *_dev) } EXPORT_SYMBOL_GPL(xenbus_dev_remove); -void xenbus_dev_shutdown(struct device *_dev) -{ - struct xenbus_device *dev = to_xenbus_device(_dev); - unsigned long timeout = 5*HZ; - - DPRINTK("%s", dev->nodename); - - get_device(&dev->dev); - if (dev->state != XenbusStateConnected) { - pr_info("%s: %s: %s != Connected, skipping\n", - __func__, dev->nodename, xenbus_strstate(dev->state)); - goto out; - } - xenbus_switch_state(dev, XenbusStateClosing); - timeout = wait_for_completion_timeout(&dev->down, timeout); - if (!timeout) - pr_info("%s: %s timeout closing device\n", - __func__, dev->nodename); - out: - put_device(&dev->dev); -} -EXPORT_SYMBOL_GPL(xenbus_dev_shutdown); - int xenbus_register_driver_common(struct xenbus_driver *drv, struct xen_bus_type *bus, struct module *owner, const char *mod_name) diff --git a/drivers/xen/xenbus/xenbus_probe_backend.c b/drivers/xen/xenbus/xenbus_probe_backend.c index b0bed4faf44cc..14876faff3b03 100644 --- a/drivers/xen/xenbus/xenbus_probe_backend.c +++ b/drivers/xen/xenbus/xenbus_probe_backend.c @@ -198,7 +198,6 @@ static struct xen_bus_type xenbus_backend = { .uevent = xenbus_uevent_backend, .probe = xenbus_dev_probe, .remove = xenbus_dev_remove, - .shutdown = xenbus_dev_shutdown, .dev_groups = xenbus_dev_groups, }, }; diff --git a/drivers/xen/xenbus/xenbus_probe_frontend.c b/drivers/xen/xenbus/xenbus_probe_frontend.c index a7d90a719cea6..8a1650bbe18ff 100644 --- a/drivers/xen/xenbus/xenbus_probe_frontend.c +++ b/drivers/xen/xenbus/xenbus_probe_frontend.c @@ -126,6 +126,28 @@ static int xenbus_frontend_dev_probe(struct device *dev) return xenbus_dev_probe(dev); } +static void xenbus_frontend_dev_shutdown(struct device *_dev) +{ + struct xenbus_device *dev = to_xenbus_device(_dev); + unsigned long timeout = 5*HZ; + + DPRINTK("%s", dev->nodename); + + get_device(&dev->dev); + if (dev->state != XenbusStateConnected) { + pr_info("%s: %s: %s != Connected, skipping\n", + __func__, dev->nodename, xenbus_strstate(dev->state)); + goto out; + } + xenbus_switch_state(dev, XenbusStateClosing); + timeout = wait_for_completion_timeout(&dev->down, timeout); + if (!timeout) + pr_info("%s: %s timeout closing device\n", + __func__, dev->nodename); + out: + put_device(&dev->dev); +} + static const struct dev_pm_ops xenbus_pm_ops = { .suspend = xenbus_dev_suspend, .resume = xenbus_frontend_dev_resume, @@ -146,7 +168,7 @@ static struct xen_bus_type xenbus_frontend = { .uevent = xenbus_uevent_frontend, .probe = xenbus_frontend_dev_probe, .remove = xenbus_dev_remove, - .shutdown = xenbus_dev_shutdown, + .shutdown = xenbus_frontend_dev_shutdown, .dev_groups = xenbus_dev_groups, .pm = &xenbus_pm_ops, -- GitLab From 672b7763cb2c723fd6f92a3879cc60d2fb11e56a Mon Sep 17 00:00:00 2001 From: Paul Durrant Date: Wed, 11 Dec 2019 15:29:54 +0000 Subject: [PATCH 1153/1437] xenbus: limit when state is forced to closed If a driver probe() fails then leave the xenstore state alone. There is no reason to modify it as the failure may be due to transient resource allocation issues and hence a subsequent probe() may succeed. If the driver supports re-binding then only force state to closed during remove() only in the case when the toolstack may need to clean up. This can be detected by checking whether the state in xenstore has been set to closing prior to device removal. NOTE: Re-bind support is indicated by new boolean in struct xenbus_driver, which defaults to false. Subsequent patches will add support to some backend drivers. Signed-off-by: Paul Durrant Reviewed-by: Juergen Gross Signed-off-by: Juergen Gross --- drivers/xen/xenbus/xenbus_probe.c | 12 ++++++++++-- include/xen/xenbus.h | 1 + 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/drivers/xen/xenbus/xenbus_probe.c b/drivers/xen/xenbus/xenbus_probe.c index 5aa29396c9e39..378486b79f96a 100644 --- a/drivers/xen/xenbus/xenbus_probe.c +++ b/drivers/xen/xenbus/xenbus_probe.c @@ -255,7 +255,6 @@ fail_put: module_put(drv->driver.owner); fail: xenbus_dev_error(dev, err, "xenbus_dev_probe on %s", dev->nodename); - xenbus_switch_state(dev, XenbusStateClosed); return err; } EXPORT_SYMBOL_GPL(xenbus_dev_probe); @@ -276,7 +275,16 @@ int xenbus_dev_remove(struct device *_dev) free_otherend_details(dev); - xenbus_switch_state(dev, XenbusStateClosed); + /* + * If the toolstack has forced the device state to closing then set + * the state to closed now to allow it to be cleaned up. + * Similarly, if the driver does not support re-bind, set the + * closed. + */ + if (!drv->allow_rebind || + xenbus_read_driver_state(dev->nodename) == XenbusStateClosing) + xenbus_switch_state(dev, XenbusStateClosed); + return 0; } EXPORT_SYMBOL_GPL(xenbus_dev_remove); diff --git a/include/xen/xenbus.h b/include/xen/xenbus.h index 869c816d5f8c3..24228a102141e 100644 --- a/include/xen/xenbus.h +++ b/include/xen/xenbus.h @@ -93,6 +93,7 @@ struct xenbus_device_id struct xenbus_driver { const char *name; /* defaults to ids[0].devicetype */ const struct xenbus_device_id *ids; + bool allow_rebind; /* avoid setting xenstore closed during remove */ int (*probe)(struct xenbus_device *dev, const struct xenbus_device_id *id); void (*otherend_changed)(struct xenbus_device *dev, -- GitLab From 1ee54195a305fae3955642af8528bdf67496d353 Mon Sep 17 00:00:00 2001 From: Paul Durrant Date: Wed, 11 Dec 2019 15:29:55 +0000 Subject: [PATCH 1154/1437] xen/interface: re-define FRONT/BACK_RING_ATTACH() Currently these macros are defined to re-initialize a front/back ring (respectively) to values read from the shared ring in such a way that any requests/responses that are added to the shared ring whilst the front/back is detached will be skipped over. This, in general, is not a desirable semantic since most frontend implementations will eventually block waiting for a response which would either never appear or never be processed. Since the macros are currently unused, take this opportunity to re-define them to re-initialize a front/back ring using specified values. This also allows FRONT/BACK_RING_INIT() to be re-defined in terms of FRONT/BACK_RING_ATTACH() using a specified value of 0. NOTE: BACK_RING_ATTACH() will be used directly in a subsequent patch. Signed-off-by: Paul Durrant Reviewed-by: Juergen Gross Signed-off-by: Juergen Gross --- include/xen/interface/io/ring.h | 29 +++++++++-------------------- 1 file changed, 9 insertions(+), 20 deletions(-) diff --git a/include/xen/interface/io/ring.h b/include/xen/interface/io/ring.h index 3f40501fc60b1..2af7a1cd66589 100644 --- a/include/xen/interface/io/ring.h +++ b/include/xen/interface/io/ring.h @@ -125,35 +125,24 @@ struct __name##_back_ring { \ memset((_s)->pad, 0, sizeof((_s)->pad)); \ } while(0) -#define FRONT_RING_INIT(_r, _s, __size) do { \ - (_r)->req_prod_pvt = 0; \ - (_r)->rsp_cons = 0; \ +#define FRONT_RING_ATTACH(_r, _s, _i, __size) do { \ + (_r)->req_prod_pvt = (_i); \ + (_r)->rsp_cons = (_i); \ (_r)->nr_ents = __RING_SIZE(_s, __size); \ (_r)->sring = (_s); \ } while (0) -#define BACK_RING_INIT(_r, _s, __size) do { \ - (_r)->rsp_prod_pvt = 0; \ - (_r)->req_cons = 0; \ - (_r)->nr_ents = __RING_SIZE(_s, __size); \ - (_r)->sring = (_s); \ -} while (0) +#define FRONT_RING_INIT(_r, _s, __size) FRONT_RING_ATTACH(_r, _s, 0, __size) -/* Initialize to existing shared indexes -- for recovery */ -#define FRONT_RING_ATTACH(_r, _s, __size) do { \ - (_r)->sring = (_s); \ - (_r)->req_prod_pvt = (_s)->req_prod; \ - (_r)->rsp_cons = (_s)->rsp_prod; \ +#define BACK_RING_ATTACH(_r, _s, _i, __size) do { \ + (_r)->rsp_prod_pvt = (_i); \ + (_r)->req_cons = (_i); \ (_r)->nr_ents = __RING_SIZE(_s, __size); \ -} while (0) - -#define BACK_RING_ATTACH(_r, _s, __size) do { \ (_r)->sring = (_s); \ - (_r)->rsp_prod_pvt = (_s)->rsp_prod; \ - (_r)->req_cons = (_s)->req_prod; \ - (_r)->nr_ents = __RING_SIZE(_s, __size); \ } while (0) +#define BACK_RING_INIT(_r, _s, __size) BACK_RING_ATTACH(_r, _s, 0, __size) + /* How big is this ring? */ #define RING_SIZE(_r) \ ((_r)->nr_ents) -- GitLab From f4eef1b652eeb850a0f44e8f985cc4153a0c0265 Mon Sep 17 00:00:00 2001 From: Paul Durrant Date: Wed, 11 Dec 2019 15:29:56 +0000 Subject: [PATCH 1155/1437] xen-blkback: support dynamic unbind/bind MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit By simply re-attaching to shared rings during connect_ring() rather than assuming they are freshly allocated (i.e assuming the counters are zero) it is possible for vbd instances to be unbound and re-bound from and to (respectively) a running guest. This has been tested by running: while true; do fio --name=randwrite --ioengine=libaio --iodepth=16 \ --rw=randwrite --bs=4k --direct=1 --size=1G --verify=crc32; done in a PV guest whilst running: while true; do echo vbd-$DOMID-$VBD >unbind; echo unbound; sleep 5; echo vbd-$DOMID-$VBD >bind; echo bound; sleep 3; done in dom0 from /sys/bus/xen-backend/drivers/vbd to continuously unbind and re-bind its system disk image. This is a highly useful feature for a backend module as it allows it to be unloaded and re-loaded (i.e. updated) without requiring domUs to be halted. This was also tested by running: while true; do echo vbd-$DOMID-$VBD >unbind; echo unbound; sleep 5; rmmod xen-blkback; echo unloaded; sleep 1; modprobe xen-blkback; echo bound; cd $(pwd); sleep 3; done in dom0 whilst running the same loop as above in the (single) PV guest. Some (less stressful) testing has also been done using a Windows HVM guest with the latest 9.0 PV drivers installed. Signed-off-by: Paul Durrant Reviewed-by: Juergen Gross Reviewed-by: Roger Pau Monné Signed-off-by: Juergen Gross --- drivers/block/xen-blkback/xenbus.c | 56 ++++++++++++++++++++---------- 1 file changed, 38 insertions(+), 18 deletions(-) diff --git a/drivers/block/xen-blkback/xenbus.c b/drivers/block/xen-blkback/xenbus.c index d6a6adfd5159d..4c5d99f878136 100644 --- a/drivers/block/xen-blkback/xenbus.c +++ b/drivers/block/xen-blkback/xenbus.c @@ -190,6 +190,9 @@ static int xen_blkif_map(struct xen_blkif_ring *ring, grant_ref_t *gref, { int err; struct xen_blkif *blkif = ring->blkif; + const struct blkif_common_sring *sring_common; + RING_IDX rsp_prod, req_prod; + unsigned int size; /* Already connected through? */ if (ring->irq) @@ -200,46 +203,62 @@ static int xen_blkif_map(struct xen_blkif_ring *ring, grant_ref_t *gref, if (err < 0) return err; + sring_common = (struct blkif_common_sring *)ring->blk_ring; + rsp_prod = READ_ONCE(sring_common->rsp_prod); + req_prod = READ_ONCE(sring_common->req_prod); + switch (blkif->blk_protocol) { case BLKIF_PROTOCOL_NATIVE: { - struct blkif_sring *sring; - sring = (struct blkif_sring *)ring->blk_ring; - BACK_RING_INIT(&ring->blk_rings.native, sring, - XEN_PAGE_SIZE * nr_grefs); + struct blkif_sring *sring_native = + (struct blkif_sring *)ring->blk_ring; + + BACK_RING_ATTACH(&ring->blk_rings.native, sring_native, + rsp_prod, XEN_PAGE_SIZE * nr_grefs); + size = __RING_SIZE(sring_native, XEN_PAGE_SIZE * nr_grefs); break; } case BLKIF_PROTOCOL_X86_32: { - struct blkif_x86_32_sring *sring_x86_32; - sring_x86_32 = (struct blkif_x86_32_sring *)ring->blk_ring; - BACK_RING_INIT(&ring->blk_rings.x86_32, sring_x86_32, - XEN_PAGE_SIZE * nr_grefs); + struct blkif_x86_32_sring *sring_x86_32 = + (struct blkif_x86_32_sring *)ring->blk_ring; + + BACK_RING_ATTACH(&ring->blk_rings.x86_32, sring_x86_32, + rsp_prod, XEN_PAGE_SIZE * nr_grefs); + size = __RING_SIZE(sring_x86_32, XEN_PAGE_SIZE * nr_grefs); break; } case BLKIF_PROTOCOL_X86_64: { - struct blkif_x86_64_sring *sring_x86_64; - sring_x86_64 = (struct blkif_x86_64_sring *)ring->blk_ring; - BACK_RING_INIT(&ring->blk_rings.x86_64, sring_x86_64, - XEN_PAGE_SIZE * nr_grefs); + struct blkif_x86_64_sring *sring_x86_64 = + (struct blkif_x86_64_sring *)ring->blk_ring; + + BACK_RING_ATTACH(&ring->blk_rings.x86_64, sring_x86_64, + rsp_prod, XEN_PAGE_SIZE * nr_grefs); + size = __RING_SIZE(sring_x86_64, XEN_PAGE_SIZE * nr_grefs); break; } default: BUG(); } + err = -EIO; + if (req_prod - rsp_prod > size) + goto fail; + err = bind_interdomain_evtchn_to_irqhandler(blkif->domid, evtchn, xen_blkif_be_int, 0, "blkif-backend", ring); - if (err < 0) { - xenbus_unmap_ring_vfree(blkif->be->dev, ring->blk_ring); - ring->blk_rings.common.sring = NULL; - return err; - } + if (err < 0) + goto fail; ring->irq = err; return 0; + +fail: + xenbus_unmap_ring_vfree(blkif->be->dev, ring->blk_ring); + ring->blk_rings.common.sring = NULL; + return err; } static int xen_blkif_disconnect(struct xen_blkif *blkif) @@ -1131,7 +1150,8 @@ static struct xenbus_driver xen_blkbk_driver = { .ids = xen_blkbk_ids, .probe = xen_blkbk_probe, .remove = xen_blkbk_remove, - .otherend_changed = frontend_changed + .otherend_changed = frontend_changed, + .allow_rebind = true, }; int xen_blkif_xenbus_init(void) -- GitLab From d6bd6cf9feb81737f9f64d2c1acf98fdaacebad1 Mon Sep 17 00:00:00 2001 From: Aditya Pakki Date: Tue, 17 Dec 2019 14:53:56 -0600 Subject: [PATCH 1156/1437] xen/grant-table: remove multiple BUG_ON on gnttab_interface gnttab_request_version() always sets the gnttab_interface variable and the assertions to check for empty gnttab_interface is unnecessary. The patch eliminates multiple such assertions. Signed-off-by: Aditya Pakki Reviewed-by: Juergen Gross Signed-off-by: Juergen Gross --- drivers/xen/grant-table.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/xen/grant-table.c b/drivers/xen/grant-table.c index 49b381e104efa..7b36b51cdb9f9 100644 --- a/drivers/xen/grant-table.c +++ b/drivers/xen/grant-table.c @@ -664,7 +664,6 @@ static int grow_gnttab_list(unsigned int more_frames) unsigned int nr_glist_frames, new_nr_glist_frames; unsigned int grefs_per_frame; - BUG_ON(gnttab_interface == NULL); grefs_per_frame = gnttab_interface->grefs_per_grant_frame; new_nr_grant_frames = nr_grant_frames + more_frames; @@ -1160,7 +1159,6 @@ EXPORT_SYMBOL_GPL(gnttab_unmap_refs_sync); static unsigned int nr_status_frames(unsigned int nr_grant_frames) { - BUG_ON(gnttab_interface == NULL); return gnttab_frames(nr_grant_frames, SPP); } @@ -1388,7 +1386,6 @@ static int gnttab_expand(unsigned int req_entries) int rc; unsigned int cur, extra; - BUG_ON(gnttab_interface == NULL); cur = nr_grant_frames; extra = ((req_entries + gnttab_interface->grefs_per_grant_frame - 1) / gnttab_interface->grefs_per_grant_frame); @@ -1423,7 +1420,6 @@ int gnttab_init(void) /* Determine the maximum number of frames required for the * grant reference free list on the current hypervisor. */ - BUG_ON(gnttab_interface == NULL); max_nr_glist_frames = (max_nr_grant_frames * gnttab_interface->grefs_per_grant_frame / RPP); -- GitLab From d55e5f5b70dd6214ef81fb2313121b72a7dd2200 Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Wed, 11 Dec 2019 16:12:15 -0700 Subject: [PATCH 1157/1437] io_uring: use u64_to_user_ptr() consistently We use it in some spots, but not consistently. Convert the rest over, makes it easier to read as well. No functional changes in this patch. Signed-off-by: Jens Axboe --- fs/io_uring.c | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/fs/io_uring.c b/fs/io_uring.c index 6f084e3cf8351..7a23d2351be23 100644 --- a/fs/io_uring.c +++ b/fs/io_uring.c @@ -2157,7 +2157,7 @@ static int io_sendmsg_prep(struct io_kiocb *req, struct io_async_ctx *io) unsigned flags; flags = READ_ONCE(sqe->msg_flags); - msg = (struct user_msghdr __user *)(unsigned long) READ_ONCE(sqe->addr); + msg = u64_to_user_ptr(READ_ONCE(sqe->addr)); io->msg.iov = io->msg.fast_iov; return sendmsg_copy_msghdr(&io->msg.msg, msg, flags, &io->msg.iov); #else @@ -2239,7 +2239,7 @@ static int io_recvmsg_prep(struct io_kiocb *req, struct io_async_ctx *io) unsigned flags; flags = READ_ONCE(sqe->msg_flags); - msg = (struct user_msghdr __user *)(unsigned long) READ_ONCE(sqe->addr); + msg = u64_to_user_ptr(READ_ONCE(sqe->addr)); io->msg.iov = io->msg.fast_iov; return recvmsg_copy_msghdr(&io->msg.msg, msg, flags, &io->msg.uaddr, &io->msg.iov); @@ -2273,8 +2273,7 @@ static int io_recvmsg(struct io_kiocb *req, struct io_kiocb **nxt, else if (force_nonblock) flags |= MSG_DONTWAIT; - msg = (struct user_msghdr __user *) (unsigned long) - READ_ONCE(sqe->addr); + msg = u64_to_user_ptr(READ_ONCE(sqe->addr)); if (req->io) { kmsg = &req->io->msg; kmsg->msg.msg_name = &addr; @@ -2331,9 +2330,8 @@ static int io_accept_prep(struct io_kiocb *req) if (sqe->ioprio || sqe->len || sqe->buf_index) return -EINVAL; - accept->addr = (struct sockaddr __user *) - (unsigned long) READ_ONCE(sqe->addr); - accept->addr_len = (int __user *) (unsigned long) READ_ONCE(sqe->addr2); + accept->addr = u64_to_user_ptr(READ_ONCE(sqe->addr)); + accept->addr_len = u64_to_user_ptr(READ_ONCE(sqe->addr2)); accept->flags = READ_ONCE(sqe->accept_flags); req->flags |= REQ_F_PREPPED; return 0; @@ -2407,7 +2405,7 @@ static int io_connect_prep(struct io_kiocb *req, struct io_async_ctx *io) struct sockaddr __user *addr; int addr_len; - addr = (struct sockaddr __user *) (unsigned long) READ_ONCE(sqe->addr); + addr = u64_to_user_ptr(READ_ONCE(sqe->addr)); addr_len = READ_ONCE(sqe->addr2); return move_addr_to_kernel(addr, addr_len, &io->connect.address); #else @@ -4702,7 +4700,7 @@ static int io_copy_iov(struct io_ring_ctx *ctx, struct iovec *dst, if (copy_from_user(&ciov, &ciovs[index], sizeof(ciov))) return -EFAULT; - dst->iov_base = (void __user *) (unsigned long) ciov.iov_base; + dst->iov_base = u64_to_user_ptr((u64)ciov.iov_base); dst->iov_len = ciov.iov_len; return 0; } -- GitLab From 5084bf6b2006fcd46f1e44e3c51b687507b362e2 Mon Sep 17 00:00:00 2001 From: Chen Wandun Date: Fri, 20 Dec 2019 08:07:31 -0800 Subject: [PATCH 1158/1437] xfs: Make the symbol 'xfs_rtalloc_log_count' static Fix the following sparse warning: fs/xfs/libxfs/xfs_trans_resv.c:206:1: warning: symbol 'xfs_rtalloc_log_count' was not declared. Should it be static? Fixes: b1de6fc7520f ("xfs: fix log reservation overflows when allocating large rt extents") Signed-off-by: Chen Wandun Reviewed-by: Darrick J. Wong Signed-off-by: Darrick J. Wong --- fs/xfs/libxfs/xfs_trans_resv.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/xfs/libxfs/xfs_trans_resv.c b/fs/xfs/libxfs/xfs_trans_resv.c index 824073a839acb..7a9c04920505a 100644 --- a/fs/xfs/libxfs/xfs_trans_resv.c +++ b/fs/xfs/libxfs/xfs_trans_resv.c @@ -202,7 +202,7 @@ xfs_calc_inode_chunk_res( * blocks as needed to mark inuse MAXEXTLEN blocks' worth of realtime extents, * as well as the realtime summary block. */ -unsigned int +static unsigned int xfs_rtalloc_log_count( struct xfs_mount *mp, unsigned int num_ops) -- GitLab From f33121cbe91973a08e68e4bde8c3f7e6e4e351c1 Mon Sep 17 00:00:00 2001 From: David Howells Date: Wed, 18 Dec 2019 16:38:49 +0000 Subject: [PATCH 1159/1437] rxrpc: Unlock new call in rxrpc_new_incoming_call() rather than the caller Move the unlock and the ping transmission for a new incoming call into rxrpc_new_incoming_call() rather than doing it in the caller. This makes it clearer to see what's going on. Suggested-by: Peter Zijlstra Signed-off-by: David Howells Acked-by: Peter Zijlstra (Intel) cc: Ingo Molnar cc: Will Deacon cc: Davidlohr Bueso --- net/rxrpc/call_accept.c | 36 ++++++++++++++++++++++++++++-------- net/rxrpc/input.c | 18 ------------------ 2 files changed, 28 insertions(+), 26 deletions(-) diff --git a/net/rxrpc/call_accept.c b/net/rxrpc/call_accept.c index 135bf5cd8dd51..3685b1732f65f 100644 --- a/net/rxrpc/call_accept.c +++ b/net/rxrpc/call_accept.c @@ -239,6 +239,22 @@ void rxrpc_discard_prealloc(struct rxrpc_sock *rx) kfree(b); } +/* + * Ping the other end to fill our RTT cache and to retrieve the rwind + * and MTU parameters. + */ +static void rxrpc_send_ping(struct rxrpc_call *call, struct sk_buff *skb) +{ + struct rxrpc_skb_priv *sp = rxrpc_skb(skb); + ktime_t now = skb->tstamp; + + if (call->peer->rtt_usage < 3 || + ktime_before(ktime_add_ms(call->peer->rtt_last_req, 1000), now)) + rxrpc_propose_ACK(call, RXRPC_ACK_PING, sp->hdr.serial, + true, true, + rxrpc_propose_ack_ping_for_params); +} + /* * Allocate a new incoming call from the prealloc pool, along with a connection * and a peer as necessary. @@ -346,9 +362,7 @@ struct rxrpc_call *rxrpc_new_incoming_call(struct rxrpc_local *local, sp->hdr.seq, RX_INVALID_OPERATION, ESHUTDOWN); skb->mark = RXRPC_SKB_MARK_REJECT_ABORT; skb->priority = RX_INVALID_OPERATION; - _leave(" = NULL [close]"); - call = NULL; - goto out; + goto no_call; } /* The peer, connection and call may all have sprung into existence due @@ -361,9 +375,7 @@ struct rxrpc_call *rxrpc_new_incoming_call(struct rxrpc_local *local, call = rxrpc_alloc_incoming_call(rx, local, peer, conn, skb); if (!call) { skb->mark = RXRPC_SKB_MARK_REJECT_BUSY; - _leave(" = NULL [busy]"); - call = NULL; - goto out; + goto no_call; } trace_rxrpc_receive(call, rxrpc_receive_incoming, @@ -432,10 +444,18 @@ struct rxrpc_call *rxrpc_new_incoming_call(struct rxrpc_local *local, */ rxrpc_put_call(call, rxrpc_call_put); - _leave(" = %p{%d}", call, call->debug_id); -out: spin_unlock(&rx->incoming_lock); + + rxrpc_send_ping(call, skb); + mutex_unlock(&call->user_mutex); + + _leave(" = %p{%d}", call, call->debug_id); return call; + +no_call: + spin_unlock(&rx->incoming_lock); + _leave(" = NULL [%u]", skb->mark); + return NULL; } /* diff --git a/net/rxrpc/input.c b/net/rxrpc/input.c index 157be1ff8697b..86bd133b4fa0a 100644 --- a/net/rxrpc/input.c +++ b/net/rxrpc/input.c @@ -192,22 +192,6 @@ send_extra_data: goto out_no_clear_ca; } -/* - * Ping the other end to fill our RTT cache and to retrieve the rwind - * and MTU parameters. - */ -static void rxrpc_send_ping(struct rxrpc_call *call, struct sk_buff *skb) -{ - struct rxrpc_skb_priv *sp = rxrpc_skb(skb); - ktime_t now = skb->tstamp; - - if (call->peer->rtt_usage < 3 || - ktime_before(ktime_add_ms(call->peer->rtt_last_req, 1000), now)) - rxrpc_propose_ACK(call, RXRPC_ACK_PING, sp->hdr.serial, - true, true, - rxrpc_propose_ack_ping_for_params); -} - /* * Apply a hard ACK by advancing the Tx window. */ @@ -1396,8 +1380,6 @@ int rxrpc_input_packet(struct sock *udp_sk, struct sk_buff *skb) call = rxrpc_new_incoming_call(local, rx, skb); if (!call) goto reject_packet; - rxrpc_send_ping(call, skb); - mutex_unlock(&call->user_mutex); } /* Process a call packet; this either discards or passes on the ref -- GitLab From 13b7955a0252e15265386b229b814152f109b234 Mon Sep 17 00:00:00 2001 From: David Howells Date: Fri, 20 Dec 2019 16:20:56 +0000 Subject: [PATCH 1160/1437] rxrpc: Don't take call->user_mutex in rxrpc_new_incoming_call() Standard kernel mutexes cannot be used in any way from interrupt or softirq context, so the user_mutex which manages access to a call cannot be a mutex since on a new call the mutex must start off locked and be unlocked within the softirq handler to prevent userspace interfering with a call we're setting up. Commit a0855d24fc22d49cdc25664fb224caee16998683 ("locking/mutex: Complain upon mutex API misuse in IRQ contexts") causes big warnings to be splashed in dmesg for each a new call that comes in from the server. Whilst it *seems* like it should be okay, since the accept path uses trylock, there are issues with PI boosting and marking the wrong task as the owner. Fix this by not taking the mutex in the softirq path at all. It's not obvious that there should be any need for it as the state is set before the first notification is generated for the new call. There's also no particular reason why the link-assessing ping should be triggered inside the mutex. It's not actually transmitted there anyway, but rather it has to be deferred to a workqueue. Further, I don't think that there's any particular reason that the socket notification needs to be done from within rx->incoming_lock, so the amount of time that lock is held can be shortened too and the ping prepared before the new call notification is sent. Fixes: 540b1c48c37a ("rxrpc: Fix deadlock between call creation and sendmsg/recvmsg") Signed-off-by: David Howells cc: Peter Zijlstra (Intel) cc: Ingo Molnar cc: Will Deacon cc: Davidlohr Bueso --- net/rxrpc/call_accept.c | 20 +++----------------- 1 file changed, 3 insertions(+), 17 deletions(-) diff --git a/net/rxrpc/call_accept.c b/net/rxrpc/call_accept.c index 3685b1732f65f..44fa22b020ef9 100644 --- a/net/rxrpc/call_accept.c +++ b/net/rxrpc/call_accept.c @@ -381,18 +381,6 @@ struct rxrpc_call *rxrpc_new_incoming_call(struct rxrpc_local *local, trace_rxrpc_receive(call, rxrpc_receive_incoming, sp->hdr.serial, sp->hdr.seq); - /* Lock the call to prevent rxrpc_kernel_send/recv_data() and - * sendmsg()/recvmsg() inconveniently stealing the mutex once the - * notification is generated. - * - * The BUG should never happen because the kernel should be well - * behaved enough not to access the call before the first notification - * event and userspace is prevented from doing so until the state is - * appropriate. - */ - if (!mutex_trylock(&call->user_mutex)) - BUG(); - /* Make the call live. */ rxrpc_incoming_call(rx, call, skb); conn = call->conn; @@ -433,6 +421,9 @@ struct rxrpc_call *rxrpc_new_incoming_call(struct rxrpc_local *local, BUG(); } spin_unlock(&conn->state_lock); + spin_unlock(&rx->incoming_lock); + + rxrpc_send_ping(call, skb); if (call->state == RXRPC_CALL_SERVER_ACCEPTING) rxrpc_notify_socket(call); @@ -444,11 +435,6 @@ struct rxrpc_call *rxrpc_new_incoming_call(struct rxrpc_local *local, */ rxrpc_put_call(call, rxrpc_call_put); - spin_unlock(&rx->incoming_lock); - - rxrpc_send_ping(call, skb); - mutex_unlock(&call->user_mutex); - _leave(" = %p{%d}", call, call->debug_id); return call; -- GitLab From 063c60d39180cec7c9317f5acfc3071f8fecd705 Mon Sep 17 00:00:00 2001 From: David Howells Date: Fri, 20 Dec 2019 16:17:16 +0000 Subject: [PATCH 1161/1437] rxrpc: Fix missing security check on incoming calls Fix rxrpc_new_incoming_call() to check that we have a suitable service key available for the combination of service ID and security class of a new incoming call - and to reject calls for which we don't. This causes an assertion like the following to appear: rxrpc: Assertion failed - 6(0x6) == 12(0xc) is false kernel BUG at net/rxrpc/call_object.c:456! Where call->state is RXRPC_CALL_SERVER_SECURING (6) rather than RXRPC_CALL_COMPLETE (12). Fixes: 248f219cb8bc ("rxrpc: Rewrite the data and ack handling code") Reported-by: Marc Dionne Signed-off-by: David Howells --- net/rxrpc/ar-internal.h | 10 ++++-- net/rxrpc/call_accept.c | 14 ++++++-- net/rxrpc/conn_event.c | 16 +-------- net/rxrpc/conn_service.c | 4 +++ net/rxrpc/rxkad.c | 5 +-- net/rxrpc/security.c | 70 +++++++++++++++++++--------------------- 6 files changed, 59 insertions(+), 60 deletions(-) diff --git a/net/rxrpc/ar-internal.h b/net/rxrpc/ar-internal.h index 7c7d10f2e0c18..5e99df80e80a7 100644 --- a/net/rxrpc/ar-internal.h +++ b/net/rxrpc/ar-internal.h @@ -209,6 +209,7 @@ struct rxrpc_skb_priv { struct rxrpc_security { const char *name; /* name of this service */ u8 security_index; /* security type provided */ + u32 no_key_abort; /* Abort code indicating no key */ /* Initialise a security service */ int (*init)(void); @@ -977,8 +978,9 @@ static inline void rxrpc_reduce_conn_timer(struct rxrpc_connection *conn, struct rxrpc_connection *rxrpc_find_service_conn_rcu(struct rxrpc_peer *, struct sk_buff *); struct rxrpc_connection *rxrpc_prealloc_service_connection(struct rxrpc_net *, gfp_t); -void rxrpc_new_incoming_connection(struct rxrpc_sock *, - struct rxrpc_connection *, struct sk_buff *); +void rxrpc_new_incoming_connection(struct rxrpc_sock *, struct rxrpc_connection *, + const struct rxrpc_security *, struct key *, + struct sk_buff *); void rxrpc_unpublish_service_conn(struct rxrpc_connection *); /* @@ -1103,7 +1105,9 @@ extern const struct rxrpc_security rxkad; int __init rxrpc_init_security(void); void rxrpc_exit_security(void); int rxrpc_init_client_conn_security(struct rxrpc_connection *); -int rxrpc_init_server_conn_security(struct rxrpc_connection *); +bool rxrpc_look_up_server_security(struct rxrpc_local *, struct rxrpc_sock *, + const struct rxrpc_security **, struct key **, + struct sk_buff *); /* * sendmsg.c diff --git a/net/rxrpc/call_accept.c b/net/rxrpc/call_accept.c index 44fa22b020ef9..70e44abf106c8 100644 --- a/net/rxrpc/call_accept.c +++ b/net/rxrpc/call_accept.c @@ -263,6 +263,8 @@ static struct rxrpc_call *rxrpc_alloc_incoming_call(struct rxrpc_sock *rx, struct rxrpc_local *local, struct rxrpc_peer *peer, struct rxrpc_connection *conn, + const struct rxrpc_security *sec, + struct key *key, struct sk_buff *skb) { struct rxrpc_backlog *b = rx->backlog; @@ -310,7 +312,7 @@ static struct rxrpc_call *rxrpc_alloc_incoming_call(struct rxrpc_sock *rx, conn->params.local = rxrpc_get_local(local); conn->params.peer = peer; rxrpc_see_connection(conn); - rxrpc_new_incoming_connection(rx, conn, skb); + rxrpc_new_incoming_connection(rx, conn, sec, key, skb); } else { rxrpc_get_connection(conn); } @@ -349,9 +351,11 @@ struct rxrpc_call *rxrpc_new_incoming_call(struct rxrpc_local *local, struct sk_buff *skb) { struct rxrpc_skb_priv *sp = rxrpc_skb(skb); + const struct rxrpc_security *sec = NULL; struct rxrpc_connection *conn; struct rxrpc_peer *peer = NULL; - struct rxrpc_call *call; + struct rxrpc_call *call = NULL; + struct key *key = NULL; _enter(""); @@ -372,7 +376,11 @@ struct rxrpc_call *rxrpc_new_incoming_call(struct rxrpc_local *local, */ conn = rxrpc_find_connection_rcu(local, skb, &peer); - call = rxrpc_alloc_incoming_call(rx, local, peer, conn, skb); + if (!conn && !rxrpc_look_up_server_security(local, rx, &sec, &key, skb)) + goto no_call; + + call = rxrpc_alloc_incoming_call(rx, local, peer, conn, sec, key, skb); + key_put(key); if (!call) { skb->mark = RXRPC_SKB_MARK_REJECT_BUSY; goto no_call; diff --git a/net/rxrpc/conn_event.c b/net/rxrpc/conn_event.c index a1ceef4f5cd07..808a4723f8684 100644 --- a/net/rxrpc/conn_event.c +++ b/net/rxrpc/conn_event.c @@ -376,21 +376,7 @@ static void rxrpc_secure_connection(struct rxrpc_connection *conn) _enter("{%d}", conn->debug_id); ASSERT(conn->security_ix != 0); - - if (!conn->params.key) { - _debug("set up security"); - ret = rxrpc_init_server_conn_security(conn); - switch (ret) { - case 0: - break; - case -ENOENT: - abort_code = RX_CALL_DEAD; - goto abort; - default: - abort_code = RXKADNOAUTH; - goto abort; - } - } + ASSERT(conn->server_key); if (conn->security->issue_challenge(conn) < 0) { abort_code = RX_CALL_DEAD; diff --git a/net/rxrpc/conn_service.c b/net/rxrpc/conn_service.c index 123d6ceab15cb..21da48e3d2e51 100644 --- a/net/rxrpc/conn_service.c +++ b/net/rxrpc/conn_service.c @@ -148,6 +148,8 @@ struct rxrpc_connection *rxrpc_prealloc_service_connection(struct rxrpc_net *rxn */ void rxrpc_new_incoming_connection(struct rxrpc_sock *rx, struct rxrpc_connection *conn, + const struct rxrpc_security *sec, + struct key *key, struct sk_buff *skb) { struct rxrpc_skb_priv *sp = rxrpc_skb(skb); @@ -160,6 +162,8 @@ void rxrpc_new_incoming_connection(struct rxrpc_sock *rx, conn->service_id = sp->hdr.serviceId; conn->security_ix = sp->hdr.securityIndex; conn->out_clientflag = 0; + conn->security = sec; + conn->server_key = key_get(key); if (conn->security_ix) conn->state = RXRPC_CONN_SERVICE_UNSECURED; else diff --git a/net/rxrpc/rxkad.c b/net/rxrpc/rxkad.c index 8d8aa3c230b55..098f1f9ec53ba 100644 --- a/net/rxrpc/rxkad.c +++ b/net/rxrpc/rxkad.c @@ -648,9 +648,9 @@ static int rxkad_issue_challenge(struct rxrpc_connection *conn) u32 serial; int ret; - _enter("{%d,%x}", conn->debug_id, key_serial(conn->params.key)); + _enter("{%d,%x}", conn->debug_id, key_serial(conn->server_key)); - ret = key_validate(conn->params.key); + ret = key_validate(conn->server_key); if (ret < 0) return ret; @@ -1293,6 +1293,7 @@ static void rxkad_exit(void) const struct rxrpc_security rxkad = { .name = "rxkad", .security_index = RXRPC_SECURITY_RXKAD, + .no_key_abort = RXKADUNKNOWNKEY, .init = rxkad_init, .exit = rxkad_exit, .init_connection_security = rxkad_init_connection_security, diff --git a/net/rxrpc/security.c b/net/rxrpc/security.c index a4c47d2b70547..9b1fb9ed07177 100644 --- a/net/rxrpc/security.c +++ b/net/rxrpc/security.c @@ -101,62 +101,58 @@ int rxrpc_init_client_conn_security(struct rxrpc_connection *conn) } /* - * initialise the security on a server connection + * Find the security key for a server connection. */ -int rxrpc_init_server_conn_security(struct rxrpc_connection *conn) +bool rxrpc_look_up_server_security(struct rxrpc_local *local, struct rxrpc_sock *rx, + const struct rxrpc_security **_sec, + struct key **_key, + struct sk_buff *skb) { const struct rxrpc_security *sec; - struct rxrpc_local *local = conn->params.local; - struct rxrpc_sock *rx; - struct key *key; - key_ref_t kref; + struct rxrpc_skb_priv *sp = rxrpc_skb(skb); + key_ref_t kref = NULL; char kdesc[5 + 1 + 3 + 1]; _enter(""); - sprintf(kdesc, "%u:%u", conn->service_id, conn->security_ix); + sprintf(kdesc, "%u:%u", sp->hdr.serviceId, sp->hdr.securityIndex); - sec = rxrpc_security_lookup(conn->security_ix); + sec = rxrpc_security_lookup(sp->hdr.securityIndex); if (!sec) { - _leave(" = -ENOKEY [lookup]"); - return -ENOKEY; + trace_rxrpc_abort(0, "SVS", + sp->hdr.cid, sp->hdr.callNumber, sp->hdr.seq, + RX_INVALID_OPERATION, EKEYREJECTED); + skb->mark = RXRPC_SKB_MARK_REJECT_ABORT; + skb->priority = RX_INVALID_OPERATION; + return false; } - /* find the service */ - read_lock(&local->services_lock); - rx = rcu_dereference_protected(local->service, - lockdep_is_held(&local->services_lock)); - if (rx && (rx->srx.srx_service == conn->service_id || - rx->second_service == conn->service_id)) - goto found_service; + if (sp->hdr.securityIndex == RXRPC_SECURITY_NONE) + goto out; - /* the service appears to have died */ - read_unlock(&local->services_lock); - _leave(" = -ENOENT"); - return -ENOENT; - -found_service: if (!rx->securities) { - read_unlock(&local->services_lock); - _leave(" = -ENOKEY"); - return -ENOKEY; + trace_rxrpc_abort(0, "SVR", + sp->hdr.cid, sp->hdr.callNumber, sp->hdr.seq, + RX_INVALID_OPERATION, EKEYREJECTED); + skb->mark = RXRPC_SKB_MARK_REJECT_ABORT; + skb->priority = RX_INVALID_OPERATION; + return false; } /* look through the service's keyring */ kref = keyring_search(make_key_ref(rx->securities, 1UL), &key_type_rxrpc_s, kdesc, true); if (IS_ERR(kref)) { - read_unlock(&local->services_lock); - _leave(" = %ld [search]", PTR_ERR(kref)); - return PTR_ERR(kref); + trace_rxrpc_abort(0, "SVK", + sp->hdr.cid, sp->hdr.callNumber, sp->hdr.seq, + sec->no_key_abort, EKEYREJECTED); + skb->mark = RXRPC_SKB_MARK_REJECT_ABORT; + skb->priority = sec->no_key_abort; + return false; } - key = key_ref_to_ptr(kref); - read_unlock(&local->services_lock); - - conn->server_key = key; - conn->security = sec; - - _leave(" = 0"); - return 0; +out: + *_sec = sec; + *_key = key_ref_to_ptr(kref); + return true; } -- GitLab From 9adbd45d6d32ffc1a03f3c51d72cfc69ebfc2ddb Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Fri, 20 Dec 2019 08:45:55 -0700 Subject: [PATCH 1162/1437] io_uring: add and use struct io_rw for read/writes Put the kiocb in struct io_rw, and add the addr/len for the request as well. Use the kiocb->private field for the buffer index for fixed reads and writes. Any use of kiocb->ki_filp is flipped to req->file. It's the same thing, and less confusing. Signed-off-by: Jens Axboe --- fs/io_uring.c | 96 +++++++++++++++++++++++++++------------------------ 1 file changed, 50 insertions(+), 46 deletions(-) diff --git a/fs/io_uring.c b/fs/io_uring.c index 7a23d2351be23..b5f91d21fd04a 100644 --- a/fs/io_uring.c +++ b/fs/io_uring.c @@ -332,6 +332,13 @@ struct io_timeout { int flags; }; +struct io_rw { + /* NOTE: kiocb has the file as the first member, so don't do it here */ + struct kiocb kiocb; + u64 addr; + u64 len; +}; + struct io_async_connect { struct sockaddr_storage address; }; @@ -369,7 +376,7 @@ struct io_async_ctx { struct io_kiocb { union { struct file *file; - struct kiocb rw; + struct io_rw rw; struct io_poll_iocb poll; struct io_accept accept; struct io_sync sync; @@ -1180,7 +1187,7 @@ static int io_do_iopoll(struct io_ring_ctx *ctx, unsigned int *nr_events, ret = 0; list_for_each_entry_safe(req, tmp, &ctx->poll_list, list) { - struct kiocb *kiocb = &req->rw; + struct kiocb *kiocb = &req->rw.kiocb; /* * Move completed entries to our local list. If we find a @@ -1335,7 +1342,7 @@ static inline void req_set_fail_links(struct io_kiocb *req) static void io_complete_rw_common(struct kiocb *kiocb, long res) { - struct io_kiocb *req = container_of(kiocb, struct io_kiocb, rw); + struct io_kiocb *req = container_of(kiocb, struct io_kiocb, rw.kiocb); if (kiocb->ki_flags & IOCB_WRITE) kiocb_end_write(req); @@ -1347,7 +1354,7 @@ static void io_complete_rw_common(struct kiocb *kiocb, long res) static void io_complete_rw(struct kiocb *kiocb, long res, long res2) { - struct io_kiocb *req = container_of(kiocb, struct io_kiocb, rw); + struct io_kiocb *req = container_of(kiocb, struct io_kiocb, rw.kiocb); io_complete_rw_common(kiocb, res); io_put_req(req); @@ -1355,7 +1362,7 @@ static void io_complete_rw(struct kiocb *kiocb, long res, long res2) static struct io_kiocb *__io_complete_rw(struct kiocb *kiocb, long res) { - struct io_kiocb *req = container_of(kiocb, struct io_kiocb, rw); + struct io_kiocb *req = container_of(kiocb, struct io_kiocb, rw.kiocb); struct io_kiocb *nxt = NULL; io_complete_rw_common(kiocb, res); @@ -1366,7 +1373,7 @@ static struct io_kiocb *__io_complete_rw(struct kiocb *kiocb, long res) static void io_complete_rw_iopoll(struct kiocb *kiocb, long res, long res2) { - struct io_kiocb *req = container_of(kiocb, struct io_kiocb, rw); + struct io_kiocb *req = container_of(kiocb, struct io_kiocb, rw.kiocb); if (kiocb->ki_flags & IOCB_WRITE) kiocb_end_write(req); @@ -1400,7 +1407,7 @@ static void io_iopoll_req_issued(struct io_kiocb *req) list_req = list_first_entry(&ctx->poll_list, struct io_kiocb, list); - if (list_req->rw.ki_filp != req->rw.ki_filp) + if (list_req->file != req->file) ctx->poll_multi_file = true; } @@ -1475,7 +1482,7 @@ static int io_prep_rw(struct io_kiocb *req, bool force_nonblock) { const struct io_uring_sqe *sqe = req->sqe; struct io_ring_ctx *ctx = req->ctx; - struct kiocb *kiocb = &req->rw; + struct kiocb *kiocb = &req->rw.kiocb; unsigned ioprio; int ret; @@ -1524,6 +1531,12 @@ static int io_prep_rw(struct io_kiocb *req, bool force_nonblock) return -EINVAL; kiocb->ki_complete = io_complete_rw; } + + req->rw.addr = READ_ONCE(req->sqe->addr); + req->rw.len = READ_ONCE(req->sqe->len); + /* we own ->private, reuse it for the buffer index */ + req->rw.kiocb.private = (void *) (unsigned long) + READ_ONCE(req->sqe->buf_index); return 0; } @@ -1557,11 +1570,11 @@ static void kiocb_done(struct kiocb *kiocb, ssize_t ret, struct io_kiocb **nxt, io_rw_done(kiocb, ret); } -static ssize_t io_import_fixed(struct io_ring_ctx *ctx, int rw, - const struct io_uring_sqe *sqe, +static ssize_t io_import_fixed(struct io_kiocb *req, int rw, struct iov_iter *iter) { - size_t len = READ_ONCE(sqe->len); + struct io_ring_ctx *ctx = req->ctx; + size_t len = req->rw.len; struct io_mapped_ubuf *imu; unsigned index, buf_index; size_t offset; @@ -1571,13 +1584,13 @@ static ssize_t io_import_fixed(struct io_ring_ctx *ctx, int rw, if (unlikely(!ctx->user_bufs)) return -EFAULT; - buf_index = READ_ONCE(sqe->buf_index); + buf_index = (unsigned long) req->rw.kiocb.private; if (unlikely(buf_index >= ctx->nr_user_bufs)) return -EFAULT; index = array_index_nospec(buf_index, ctx->nr_user_bufs); imu = &ctx->user_bufs[index]; - buf_addr = READ_ONCE(sqe->addr); + buf_addr = req->rw.addr; /* overflow */ if (buf_addr + len < buf_addr) @@ -1634,25 +1647,20 @@ static ssize_t io_import_fixed(struct io_ring_ctx *ctx, int rw, static ssize_t io_import_iovec(int rw, struct io_kiocb *req, struct iovec **iovec, struct iov_iter *iter) { - const struct io_uring_sqe *sqe = req->sqe; - void __user *buf = u64_to_user_ptr(READ_ONCE(sqe->addr)); - size_t sqe_len = READ_ONCE(sqe->len); + void __user *buf = u64_to_user_ptr(req->rw.addr); + size_t sqe_len = req->rw.len; u8 opcode; - /* - * We're reading ->opcode for the second time, but the first read - * doesn't care whether it's _FIXED or not, so it doesn't matter - * whether ->opcode changes concurrently. The first read does care - * about whether it is a READ or a WRITE, so we don't trust this read - * for that purpose and instead let the caller pass in the read/write - * flag. - */ opcode = req->opcode; if (opcode == IORING_OP_READ_FIXED || opcode == IORING_OP_WRITE_FIXED) { *iovec = NULL; - return io_import_fixed(req->ctx, rw, sqe, iter); + return io_import_fixed(req, rw, iter); } + /* buffer index only valid with fixed read/write */ + if (req->rw.kiocb.private) + return -EINVAL; + if (req->io) { struct io_async_rw *iorw = &req->io->rw; @@ -1801,9 +1809,8 @@ static int io_read(struct io_kiocb *req, struct io_kiocb **nxt, bool force_nonblock) { struct iovec inline_vecs[UIO_FASTIOV], *iovec = inline_vecs; - struct kiocb *kiocb = &req->rw; + struct kiocb *kiocb = &req->rw.kiocb; struct iov_iter iter; - struct file *file; size_t iov_count; ssize_t io_size, ret; @@ -1819,9 +1826,8 @@ static int io_read(struct io_kiocb *req, struct io_kiocb **nxt, /* Ensure we clear previously set non-block flag */ if (!force_nonblock) - req->rw.ki_flags &= ~IOCB_NOWAIT; + req->rw.kiocb.ki_flags &= ~IOCB_NOWAIT; - file = req->file; io_size = ret; if (req->flags & REQ_F_LINK) req->result = io_size; @@ -1830,20 +1836,20 @@ static int io_read(struct io_kiocb *req, struct io_kiocb **nxt, * If the file doesn't support async, mark it as REQ_F_MUST_PUNT so * we know to async punt it even if it was opened O_NONBLOCK */ - if (force_nonblock && !io_file_supports_async(file)) { + if (force_nonblock && !io_file_supports_async(req->file)) { req->flags |= REQ_F_MUST_PUNT; goto copy_iov; } iov_count = iov_iter_count(&iter); - ret = rw_verify_area(READ, file, &kiocb->ki_pos, iov_count); + ret = rw_verify_area(READ, req->file, &kiocb->ki_pos, iov_count); if (!ret) { ssize_t ret2; - if (file->f_op->read_iter) - ret2 = call_read_iter(file, kiocb, &iter); + if (req->file->f_op->read_iter) + ret2 = call_read_iter(req->file, kiocb, &iter); else - ret2 = loop_rw_iter(READ, file, kiocb, &iter); + ret2 = loop_rw_iter(READ, req->file, kiocb, &iter); /* * In case of a short read, punt to async. This can happen @@ -1894,9 +1900,8 @@ static int io_write(struct io_kiocb *req, struct io_kiocb **nxt, bool force_nonblock) { struct iovec inline_vecs[UIO_FASTIOV], *iovec = inline_vecs; - struct kiocb *kiocb = &req->rw; + struct kiocb *kiocb = &req->rw.kiocb; struct iov_iter iter; - struct file *file; size_t iov_count; ssize_t ret, io_size; @@ -1912,9 +1917,8 @@ static int io_write(struct io_kiocb *req, struct io_kiocb **nxt, /* Ensure we clear previously set non-block flag */ if (!force_nonblock) - req->rw.ki_flags &= ~IOCB_NOWAIT; + req->rw.kiocb.ki_flags &= ~IOCB_NOWAIT; - file = kiocb->ki_filp; io_size = ret; if (req->flags & REQ_F_LINK) req->result = io_size; @@ -1934,7 +1938,7 @@ static int io_write(struct io_kiocb *req, struct io_kiocb **nxt, goto copy_iov; iov_count = iov_iter_count(&iter); - ret = rw_verify_area(WRITE, file, &kiocb->ki_pos, iov_count); + ret = rw_verify_area(WRITE, req->file, &kiocb->ki_pos, iov_count); if (!ret) { ssize_t ret2; @@ -1946,17 +1950,17 @@ static int io_write(struct io_kiocb *req, struct io_kiocb **nxt, * we return to userspace. */ if (req->flags & REQ_F_ISREG) { - __sb_start_write(file_inode(file)->i_sb, + __sb_start_write(file_inode(req->file)->i_sb, SB_FREEZE_WRITE, true); - __sb_writers_release(file_inode(file)->i_sb, + __sb_writers_release(file_inode(req->file)->i_sb, SB_FREEZE_WRITE); } kiocb->ki_flags |= IOCB_WRITE; - if (file->f_op->write_iter) - ret2 = call_write_iter(file, kiocb, &iter); + if (req->file->f_op->write_iter) + ret2 = call_write_iter(req->file, kiocb, &iter); else - ret2 = loop_rw_iter(WRITE, file, kiocb, &iter); + ret2 = loop_rw_iter(WRITE, req->file, kiocb, &iter); if (!force_nonblock || ret2 != -EAGAIN) { kiocb_done(kiocb, ret2, nxt, req->in_async); } else { @@ -2036,7 +2040,7 @@ static void io_fsync_finish(struct io_wq_work **workptr) if (io_req_cancelled(req)) return; - ret = vfs_fsync_range(req->rw.ki_filp, req->sync.off, + ret = vfs_fsync_range(req->file, req->sync.off, end > 0 ? end : LLONG_MAX, req->sync.flags & IORING_FSYNC_DATASYNC); if (ret < 0) @@ -2102,7 +2106,7 @@ static void io_sync_file_range_finish(struct io_wq_work **workptr) if (io_req_cancelled(req)) return; - ret = sync_file_range(req->rw.ki_filp, req->sync.off, req->sync.len, + ret = sync_file_range(req->file, req->sync.off, req->sync.len, req->sync.flags); if (ret < 0) req_set_fail_links(req); -- GitLab From 3fbb51c18f5c15a23db74c4da79d3d035176c480 Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Fri, 20 Dec 2019 08:51:52 -0700 Subject: [PATCH 1163/1437] io_uring: move all prep state for IORING_OP_CONNECT to prep handler Add struct io_connect in our io_kiocb per-command union, and ensure that io_connect_prep() has grabbed what it needs from the SQE. Signed-off-by: Jens Axboe --- fs/io_uring.c | 40 ++++++++++++++++++++++------------------ 1 file changed, 22 insertions(+), 18 deletions(-) diff --git a/fs/io_uring.c b/fs/io_uring.c index b5f91d21fd04a..2a173f54ec8ea 100644 --- a/fs/io_uring.c +++ b/fs/io_uring.c @@ -339,6 +339,12 @@ struct io_rw { u64 len; }; +struct io_connect { + struct file *file; + struct sockaddr __user *addr; + int addr_len; +}; + struct io_async_connect { struct sockaddr_storage address; }; @@ -382,6 +388,7 @@ struct io_kiocb { struct io_sync sync; struct io_cancel cancel; struct io_timeout timeout; + struct io_connect connect; }; const struct io_uring_sqe *sqe; @@ -2406,14 +2413,18 @@ static int io_connect_prep(struct io_kiocb *req, struct io_async_ctx *io) { #if defined(CONFIG_NET) const struct io_uring_sqe *sqe = req->sqe; - struct sockaddr __user *addr; - int addr_len; - addr = u64_to_user_ptr(READ_ONCE(sqe->addr)); - addr_len = READ_ONCE(sqe->addr2); - return move_addr_to_kernel(addr, addr_len, &io->connect.address); + if (unlikely(req->ctx->flags & (IORING_SETUP_IOPOLL|IORING_SETUP_SQPOLL))) + return -EINVAL; + if (sqe->ioprio || sqe->len || sqe->buf_index || sqe->rw_flags) + return -EINVAL; + + req->connect.addr = u64_to_user_ptr(READ_ONCE(sqe->addr)); + req->connect.addr_len = READ_ONCE(sqe->addr2); + return move_addr_to_kernel(req->connect.addr, req->connect.addr_len, + &io->connect.address); #else - return 0; + return -EOPNOTSUPP; #endif } @@ -2421,18 +2432,9 @@ static int io_connect(struct io_kiocb *req, struct io_kiocb **nxt, bool force_nonblock) { #if defined(CONFIG_NET) - const struct io_uring_sqe *sqe = req->sqe; struct io_async_ctx __io, *io; unsigned file_flags; - int addr_len, ret; - - if (unlikely(req->ctx->flags & (IORING_SETUP_IOPOLL|IORING_SETUP_SQPOLL))) - return -EINVAL; - if (sqe->ioprio || sqe->len || sqe->buf_index || sqe->rw_flags) - return -EINVAL; - - addr_len = READ_ONCE(sqe->addr2); - file_flags = force_nonblock ? O_NONBLOCK : 0; + int ret; if (req->io) { io = req->io; @@ -2443,8 +2445,10 @@ static int io_connect(struct io_kiocb *req, struct io_kiocb **nxt, io = &__io; } - ret = __sys_connect_file(req->file, &io->connect.address, addr_len, - file_flags); + file_flags = force_nonblock ? O_NONBLOCK : 0; + + ret = __sys_connect_file(req->file, &io->connect.address, + req->connect.addr_len, file_flags); if ((ret == -EAGAIN || ret == -EINPROGRESS) && force_nonblock) { if (req->io) return -EAGAIN; -- GitLab From e47293fdf98998292a89d516c8f7b8b9eb5c5213 Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Fri, 20 Dec 2019 08:58:21 -0700 Subject: [PATCH 1164/1437] io_uring: move all prep state for IORING_OP_{SEND,RECV}_MGS to prep handler Add struct io_sr_msg in our io_kiocb per-command union, and ensure that the send/recvmsg prep handlers have grabbed what they need from the SQE by the time prep is done. Signed-off-by: Jens Axboe --- fs/io_uring.c | 64 ++++++++++++++++++++++++++------------------------- 1 file changed, 33 insertions(+), 31 deletions(-) diff --git a/fs/io_uring.c b/fs/io_uring.c index 2a173f54ec8ea..89e5b19044cc7 100644 --- a/fs/io_uring.c +++ b/fs/io_uring.c @@ -345,6 +345,12 @@ struct io_connect { int addr_len; }; +struct io_sr_msg { + struct file *file; + struct user_msghdr __user *msg; + int msg_flags; +}; + struct io_async_connect { struct sockaddr_storage address; }; @@ -389,6 +395,7 @@ struct io_kiocb { struct io_cancel cancel; struct io_timeout timeout; struct io_connect connect; + struct io_sr_msg sr_msg; }; const struct io_uring_sqe *sqe; @@ -2164,15 +2171,15 @@ static int io_sendmsg_prep(struct io_kiocb *req, struct io_async_ctx *io) { #if defined(CONFIG_NET) const struct io_uring_sqe *sqe = req->sqe; - struct user_msghdr __user *msg; - unsigned flags; + struct io_sr_msg *sr = &req->sr_msg; - flags = READ_ONCE(sqe->msg_flags); - msg = u64_to_user_ptr(READ_ONCE(sqe->addr)); + sr->msg_flags = READ_ONCE(sqe->msg_flags); + sr->msg = u64_to_user_ptr(READ_ONCE(sqe->addr)); io->msg.iov = io->msg.fast_iov; - return sendmsg_copy_msghdr(&io->msg.msg, msg, flags, &io->msg.iov); + return sendmsg_copy_msghdr(&io->msg.msg, sr->msg, sr->msg_flags, + &io->msg.iov); #else - return 0; + return -EOPNOTSUPP; #endif } @@ -2180,7 +2187,6 @@ static int io_sendmsg(struct io_kiocb *req, struct io_kiocb **nxt, bool force_nonblock) { #if defined(CONFIG_NET) - const struct io_uring_sqe *sqe = req->sqe; struct io_async_msghdr *kmsg = NULL; struct socket *sock; int ret; @@ -2194,12 +2200,6 @@ static int io_sendmsg(struct io_kiocb *req, struct io_kiocb **nxt, struct sockaddr_storage addr; unsigned flags; - flags = READ_ONCE(sqe->msg_flags); - if (flags & MSG_DONTWAIT) - req->flags |= REQ_F_NOWAIT; - else if (force_nonblock) - flags |= MSG_DONTWAIT; - if (req->io) { kmsg = &req->io->msg; kmsg->msg.msg_name = &addr; @@ -2215,6 +2215,12 @@ static int io_sendmsg(struct io_kiocb *req, struct io_kiocb **nxt, goto out; } + flags = req->sr_msg.msg_flags; + if (flags & MSG_DONTWAIT) + req->flags |= REQ_F_NOWAIT; + else if (force_nonblock) + flags |= MSG_DONTWAIT; + ret = __sys_sendmsg_sock(sock, &kmsg->msg, flags); if (force_nonblock && ret == -EAGAIN) { if (req->io) @@ -2245,17 +2251,15 @@ out: static int io_recvmsg_prep(struct io_kiocb *req, struct io_async_ctx *io) { #if defined(CONFIG_NET) - const struct io_uring_sqe *sqe = req->sqe; - struct user_msghdr __user *msg; - unsigned flags; + struct io_sr_msg *sr = &req->sr_msg; - flags = READ_ONCE(sqe->msg_flags); - msg = u64_to_user_ptr(READ_ONCE(sqe->addr)); + sr->msg_flags = READ_ONCE(req->sqe->msg_flags); + sr->msg = u64_to_user_ptr(READ_ONCE(req->sqe->addr)); io->msg.iov = io->msg.fast_iov; - return recvmsg_copy_msghdr(&io->msg.msg, msg, flags, &io->msg.uaddr, - &io->msg.iov); + return recvmsg_copy_msghdr(&io->msg.msg, sr->msg, sr->msg_flags, + &io->msg.uaddr, &io->msg.iov); #else - return 0; + return -EOPNOTSUPP; #endif } @@ -2263,7 +2267,6 @@ static int io_recvmsg(struct io_kiocb *req, struct io_kiocb **nxt, bool force_nonblock) { #if defined(CONFIG_NET) - const struct io_uring_sqe *sqe = req->sqe; struct io_async_msghdr *kmsg = NULL; struct socket *sock; int ret; @@ -2273,18 +2276,10 @@ static int io_recvmsg(struct io_kiocb *req, struct io_kiocb **nxt, sock = sock_from_file(req->file, &ret); if (sock) { - struct user_msghdr __user *msg; struct io_async_ctx io; struct sockaddr_storage addr; unsigned flags; - flags = READ_ONCE(sqe->msg_flags); - if (flags & MSG_DONTWAIT) - req->flags |= REQ_F_NOWAIT; - else if (force_nonblock) - flags |= MSG_DONTWAIT; - - msg = u64_to_user_ptr(READ_ONCE(sqe->addr)); if (req->io) { kmsg = &req->io->msg; kmsg->msg.msg_name = &addr; @@ -2300,7 +2295,14 @@ static int io_recvmsg(struct io_kiocb *req, struct io_kiocb **nxt, goto out; } - ret = __sys_recvmsg_sock(sock, &kmsg->msg, msg, kmsg->uaddr, flags); + flags = req->sr_msg.msg_flags; + if (flags & MSG_DONTWAIT) + req->flags |= REQ_F_NOWAIT; + else if (force_nonblock) + flags |= MSG_DONTWAIT; + + ret = __sys_recvmsg_sock(sock, &kmsg->msg, req->sr_msg.msg, + kmsg->uaddr, flags); if (force_nonblock && ret == -EAGAIN) { if (req->io) return -EAGAIN; -- GitLab From 26a61679f10c6f041726411964b172565021c2eb Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Fri, 20 Dec 2019 09:02:01 -0700 Subject: [PATCH 1165/1437] io_uring: read 'count' for IORING_OP_TIMEOUT in prep handler Add the count field to struct io_timeout, and ensure the prep handler has read it. Timeout also needs an async context always, set it up in the prep handler if we don't have one. Signed-off-by: Jens Axboe --- fs/io_uring.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/fs/io_uring.c b/fs/io_uring.c index 89e5b19044cc7..7e8d287500532 100644 --- a/fs/io_uring.c +++ b/fs/io_uring.c @@ -330,6 +330,7 @@ struct io_timeout { struct file *file; u64 addr; int flags; + unsigned count; }; struct io_rw { @@ -2902,7 +2903,12 @@ static int io_timeout_prep(struct io_kiocb *req, struct io_async_ctx *io, if (flags & ~IORING_TIMEOUT_ABS) return -EINVAL; - data = &io->timeout; + req->timeout.count = READ_ONCE(sqe->off); + + if (!io && io_alloc_async_ctx(req)) + return -ENOMEM; + + data = &req->io->timeout; data->req = req; req->flags |= REQ_F_TIMEOUT; @@ -2920,7 +2926,6 @@ static int io_timeout_prep(struct io_kiocb *req, struct io_async_ctx *io, static int io_timeout(struct io_kiocb *req) { - const struct io_uring_sqe *sqe = req->sqe; unsigned count; struct io_ring_ctx *ctx = req->ctx; struct io_timeout_data *data; @@ -2942,7 +2947,7 @@ static int io_timeout(struct io_kiocb *req) * timeout event to be satisfied. If it isn't set, then this is * a pure timeout request, sequence isn't used. */ - count = READ_ONCE(sqe->off); + count = req->timeout.count; if (!count) { req->flags |= REQ_F_TIMEOUT_NOSEQ; spin_lock_irq(&ctx->completion_lock); -- GitLab From 77dcc95e2048765b616b5308863ab1d4d5a8f720 Mon Sep 17 00:00:00 2001 From: Liming Sun Date: Wed, 18 Dec 2019 13:35:27 -0500 Subject: [PATCH 1166/1437] platform/mellanox: fix the mlx-bootctl sysfs This is a follow-up commit for the sysfs attributes to change from DRIVER_ATTR to DEVICE_ATTR according to some initial comments. In such case, it's better to point the sysfs path to the device itself instead of the driver. The ABI document is also updated. Fixes: 79e29cb8fbc5 ("platform/mellanox: Add bootctl driver for Mellanox BlueField Soc") Signed-off-by: Liming Sun Signed-off-by: Andy Shevchenko --- .../ABI/testing/sysfs-platform-mellanox-bootctl | 10 +++++----- drivers/platform/mellanox/mlxbf-bootctl.c | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Documentation/ABI/testing/sysfs-platform-mellanox-bootctl b/Documentation/ABI/testing/sysfs-platform-mellanox-bootctl index c65a805748692..401d202f478b2 100644 --- a/Documentation/ABI/testing/sysfs-platform-mellanox-bootctl +++ b/Documentation/ABI/testing/sysfs-platform-mellanox-bootctl @@ -1,4 +1,4 @@ -What: /sys/bus/platform/devices/MLNXBF04:00/driver/lifecycle_state +What: /sys/bus/platform/devices/MLNXBF04:00/lifecycle_state Date: Oct 2019 KernelVersion: 5.5 Contact: "Liming Sun " @@ -10,7 +10,7 @@ Description: GA Non-Secured - Non-Secure chip and not able to change state RMA - Return Merchandise Authorization -What: /sys/bus/platform/devices/MLNXBF04:00/driver/post_reset_wdog +What: /sys/bus/platform/devices/MLNXBF04:00/post_reset_wdog Date: Oct 2019 KernelVersion: 5.5 Contact: "Liming Sun " @@ -19,7 +19,7 @@ Description: to reboot the chip and recover it to the old state if the new boot partition fails. -What: /sys/bus/platform/devices/MLNXBF04:00/driver/reset_action +What: /sys/bus/platform/devices/MLNXBF04:00/reset_action Date: Oct 2019 KernelVersion: 5.5 Contact: "Liming Sun " @@ -30,7 +30,7 @@ Description: emmc - boot from the onchip eMMC emmc_legacy - boot from the onchip eMMC in legacy (slow) mode -What: /sys/bus/platform/devices/MLNXBF04:00/driver/second_reset_action +What: /sys/bus/platform/devices/MLNXBF04:00/second_reset_action Date: Oct 2019 KernelVersion: 5.5 Contact: "Liming Sun " @@ -44,7 +44,7 @@ Description: swap_emmc - swap the primary / secondary boot partition none - cancel the action -What: /sys/bus/platform/devices/MLNXBF04:00/driver/secure_boot_fuse_state +What: /sys/bus/platform/devices/MLNXBF04:00/secure_boot_fuse_state Date: Oct 2019 KernelVersion: 5.5 Contact: "Liming Sun " diff --git a/drivers/platform/mellanox/mlxbf-bootctl.c b/drivers/platform/mellanox/mlxbf-bootctl.c index 61753b648506b..5d21c6adf1ab6 100644 --- a/drivers/platform/mellanox/mlxbf-bootctl.c +++ b/drivers/platform/mellanox/mlxbf-bootctl.c @@ -309,7 +309,7 @@ static struct platform_driver mlxbf_bootctl_driver = { .probe = mlxbf_bootctl_probe, .driver = { .name = "mlxbf-bootctl", - .groups = mlxbf_bootctl_groups, + .dev_groups = mlxbf_bootctl_groups, .acpi_match_table = mlxbf_bootctl_acpi_ids, } }; -- GitLab From 133b2acee3871ae6bf123b8fe34be14464aa3d2c Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 17 Dec 2019 20:06:04 +0100 Subject: [PATCH 1167/1437] platform/x86: hp-wmi: Make buffer for HPWMI_FEATURE2_QUERY 128 bytes At least on the HP Envy x360 15-cp0xxx model the WMI interface for HPWMI_FEATURE2_QUERY requires an outsize of at least 128 bytes, otherwise it fails with an error code 5 (HPWMI_RET_INVALID_PARAMETERS): Dec 06 00:59:38 kernel: hp_wmi: query 0xd returned error 0x5 We do not care about the contents of the buffer, we just want to know if the HPWMI_FEATURE2_QUERY command is supported. This commits bumps the buffer size, fixing the error. Fixes: 8a1513b4932 ("hp-wmi: limit hotkey enable") Cc: stable@vger.kernel.org BugLink: https://bugzilla.redhat.com/show_bug.cgi?id=1520703 Signed-off-by: Hans de Goede Signed-off-by: Andy Shevchenko --- drivers/platform/x86/hp-wmi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/platform/x86/hp-wmi.c b/drivers/platform/x86/hp-wmi.c index 9579a706fc082..a881b709af256 100644 --- a/drivers/platform/x86/hp-wmi.c +++ b/drivers/platform/x86/hp-wmi.c @@ -300,7 +300,7 @@ static int __init hp_wmi_bios_2008_later(void) static int __init hp_wmi_bios_2009_later(void) { - int state = 0; + u8 state[128]; int ret = hp_wmi_perform_query(HPWMI_FEATURE2_QUERY, HPWMI_READ, &state, sizeof(state), sizeof(state)); if (!ret) -- GitLab From e8796c6c69d129420ee94a1906b18d86b84644d4 Mon Sep 17 00:00:00 2001 From: Michael Haener Date: Fri, 29 Nov 2019 10:16:49 +0100 Subject: [PATCH 1168/1437] platform/x86: pmc_atom: Add Siemens CONNECT X300 to critclk_systems DMI table The CONNECT X300 uses the PMC clock for on-board components and gets stuck during boot if the clock is disabled. Therefore, add this device to the critical systems list. Tested on CONNECT X300. Fixes: 648e921888ad ("clk: x86: Stop marking clocks as CLK_IS_CRITICAL") Signed-off-by: Michael Haener Signed-off-by: Andy Shevchenko --- drivers/platform/x86/pmc_atom.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/platform/x86/pmc_atom.c b/drivers/platform/x86/pmc_atom.c index 07d1b911e72f8..52ef1419b6715 100644 --- a/drivers/platform/x86/pmc_atom.c +++ b/drivers/platform/x86/pmc_atom.c @@ -429,6 +429,14 @@ static const struct dmi_system_id critclk_systems[] = { DMI_MATCH(DMI_PRODUCT_VERSION, "6AV7882-0"), }, }, + { + .ident = "CONNECT X300", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "SIEMENS AG"), + DMI_MATCH(DMI_PRODUCT_VERSION, "A5E45074588"), + }, + }, + { /*sentinel*/ } }; -- GitLab From d4ac8f83dafec205c5db9b86b21587fba43bc017 Mon Sep 17 00:00:00 2001 From: "Enrico Weigelt, metux IT consult" Date: Thu, 12 Dec 2019 12:23:48 +0100 Subject: [PATCH 1169/1437] platform/x86: pcengines-apuv2: fix simswap GPIO assignment The mapping entry has to hold the GPIO line index instead of controller's register number. Fixes: 5037d4ddda31 ("platform/x86: pcengines-apuv2: wire up simswitch gpio as led") Signed-off-by: Enrico Weigelt, metux IT consult Signed-off-by: Andy Shevchenko --- drivers/platform/x86/pcengines-apuv2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/platform/x86/pcengines-apuv2.c b/drivers/platform/x86/pcengines-apuv2.c index 48b112b4f0b07..c32daf087640a 100644 --- a/drivers/platform/x86/pcengines-apuv2.c +++ b/drivers/platform/x86/pcengines-apuv2.c @@ -95,7 +95,7 @@ static struct gpiod_lookup_table gpios_led_table = { NULL, 1, GPIO_ACTIVE_LOW), GPIO_LOOKUP_IDX(AMD_FCH_GPIO_DRIVER_NAME, APU2_GPIO_LINE_LED3, NULL, 2, GPIO_ACTIVE_LOW), - GPIO_LOOKUP_IDX(AMD_FCH_GPIO_DRIVER_NAME, APU2_GPIO_REG_SIMSWAP, + GPIO_LOOKUP_IDX(AMD_FCH_GPIO_DRIVER_NAME, APU2_GPIO_LINE_SIMSWAP, NULL, 3, GPIO_ACTIVE_LOW), } }; -- GitLab From 3d00da1de3ea36ba44f4a7ba76c8c8b16f98204b Mon Sep 17 00:00:00 2001 From: "Enrico Weigelt, metux IT consult" Date: Thu, 12 Dec 2019 14:27:56 +0100 Subject: [PATCH 1170/1437] platform/x86: pcengines-apuv2: detect apuv4 board GPIO stuff on APUv4 seems to be the same as on APUv2, so we just need to match on DMI data. Signed-off-by: Enrico Weigelt, metux IT consult Signed-off-by: Andy Shevchenko --- drivers/platform/x86/pcengines-apuv2.c | 27 ++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/drivers/platform/x86/pcengines-apuv2.c b/drivers/platform/x86/pcengines-apuv2.c index c32daf087640a..5f3c40162a7e7 100644 --- a/drivers/platform/x86/pcengines-apuv2.c +++ b/drivers/platform/x86/pcengines-apuv2.c @@ -189,6 +189,33 @@ static const struct dmi_system_id apu_gpio_dmi_table[] __initconst = { }, .driver_data = (void *)&board_apu2, }, + /* APU4 w/ legacy bios < 4.0.8 */ + { + .ident = "apu4", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "PC Engines"), + DMI_MATCH(DMI_BOARD_NAME, "APU4") + }, + .driver_data = (void *)&board_apu2, + }, + /* APU4 w/ legacy bios >= 4.0.8 */ + { + .ident = "apu4", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "PC Engines"), + DMI_MATCH(DMI_BOARD_NAME, "apu4") + }, + .driver_data = (void *)&board_apu2, + }, + /* APU4 w/ mainline bios */ + { + .ident = "apu4", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "PC Engines"), + DMI_MATCH(DMI_BOARD_NAME, "PC Engines apu4") + }, + .driver_data = (void *)&board_apu2, + }, {} }; -- GitLab From 02abbda105f25fb634207e7f23a8a4b51fe67ad4 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 18 Dec 2019 14:48:07 +0200 Subject: [PATCH 1171/1437] platform/x86: pcengines-apuv2: Spelling fixes in the driver Mainly does: - capitalize gpio and bios to GPIO and BIOS - capitalize beginning of comments - add periods in multi-line comments Signed-off-by: Andy Shevchenko --- drivers/platform/x86/pcengines-apuv2.c | 40 +++++++++++++------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/drivers/platform/x86/pcengines-apuv2.c b/drivers/platform/x86/pcengines-apuv2.c index 5f3c40162a7e7..9b11ef1a401f2 100644 --- a/drivers/platform/x86/pcengines-apuv2.c +++ b/drivers/platform/x86/pcengines-apuv2.c @@ -2,7 +2,7 @@ /* * PC-Engines APUv2/APUv3 board platform driver - * for gpio buttons and LEDs + * for GPIO buttons and LEDs * * Copyright (C) 2018 metux IT consult * Author: Enrico Weigelt @@ -23,10 +23,10 @@ /* * NOTE: this driver only supports APUv2/3 - not APUv1, as this one - * has completely different register layouts + * has completely different register layouts. */ -/* register mappings */ +/* Register mappings */ #define APU2_GPIO_REG_LED1 AMD_FCH_GPIO_REG_GPIO57 #define APU2_GPIO_REG_LED2 AMD_FCH_GPIO_REG_GPIO58 #define APU2_GPIO_REG_LED3 AMD_FCH_GPIO_REG_GPIO59_DEVSLP1 @@ -35,7 +35,7 @@ #define APU2_GPIO_REG_MPCIE2 AMD_FCH_GPIO_REG_GPIO59_DEVSLP0 #define APU2_GPIO_REG_MPCIE3 AMD_FCH_GPIO_REG_GPIO51 -/* order in which the gpio lines are defined in the register list */ +/* Order in which the GPIO lines are defined in the register list */ #define APU2_GPIO_LINE_LED1 0 #define APU2_GPIO_LINE_LED2 1 #define APU2_GPIO_LINE_LED3 2 @@ -44,7 +44,7 @@ #define APU2_GPIO_LINE_MPCIE2 5 #define APU2_GPIO_LINE_MPCIE3 6 -/* gpio device */ +/* GPIO device */ static int apu2_gpio_regs[] = { [APU2_GPIO_LINE_LED1] = APU2_GPIO_REG_LED1, @@ -72,7 +72,7 @@ static const struct amd_fch_gpio_pdata board_apu2 = { .gpio_names = apu2_gpio_names, }; -/* gpio leds device */ +/* GPIO LEDs device */ static const struct gpio_led apu2_leds[] = { { .name = "apu:green:1" }, @@ -100,7 +100,7 @@ static struct gpiod_lookup_table gpios_led_table = { } }; -/* gpio keyboard device */ +/* GPIO keyboard device */ static struct gpio_keys_button apu2_keys_buttons[] = { { @@ -129,12 +129,12 @@ static struct gpiod_lookup_table gpios_key_table = { } }; -/* board setup */ +/* Board setup */ -/* note: matching works on string prefix, so "apu2" must come before "apu" */ +/* Note: matching works on string prefix, so "apu2" must come before "apu" */ static const struct dmi_system_id apu_gpio_dmi_table[] __initconst = { - /* APU2 w/ legacy bios < 4.0.8 */ + /* APU2 w/ legacy BIOS < 4.0.8 */ { .ident = "apu2", .matches = { @@ -143,7 +143,7 @@ static const struct dmi_system_id apu_gpio_dmi_table[] __initconst = { }, .driver_data = (void *)&board_apu2, }, - /* APU2 w/ legacy bios >= 4.0.8 */ + /* APU2 w/ legacy BIOS >= 4.0.8 */ { .ident = "apu2", .matches = { @@ -152,7 +152,7 @@ static const struct dmi_system_id apu_gpio_dmi_table[] __initconst = { }, .driver_data = (void *)&board_apu2, }, - /* APU2 w/ maainline bios */ + /* APU2 w/ mainline BIOS */ { .ident = "apu2", .matches = { @@ -162,7 +162,7 @@ static const struct dmi_system_id apu_gpio_dmi_table[] __initconst = { .driver_data = (void *)&board_apu2, }, - /* APU3 w/ legacy bios < 4.0.8 */ + /* APU3 w/ legacy BIOS < 4.0.8 */ { .ident = "apu3", .matches = { @@ -171,7 +171,7 @@ static const struct dmi_system_id apu_gpio_dmi_table[] __initconst = { }, .driver_data = (void *)&board_apu2, }, - /* APU3 w/ legacy bios >= 4.0.8 */ + /* APU3 w/ legacy BIOS >= 4.0.8 */ { .ident = "apu3", .matches = { @@ -180,7 +180,7 @@ static const struct dmi_system_id apu_gpio_dmi_table[] __initconst = { }, .driver_data = (void *)&board_apu2, }, - /* APU3 w/ mainline bios */ + /* APU3 w/ mainline BIOS */ { .ident = "apu3", .matches = { @@ -189,7 +189,7 @@ static const struct dmi_system_id apu_gpio_dmi_table[] __initconst = { }, .driver_data = (void *)&board_apu2, }, - /* APU4 w/ legacy bios < 4.0.8 */ + /* APU4 w/ legacy BIOS < 4.0.8 */ { .ident = "apu4", .matches = { @@ -198,7 +198,7 @@ static const struct dmi_system_id apu_gpio_dmi_table[] __initconst = { }, .driver_data = (void *)&board_apu2, }, - /* APU4 w/ legacy bios >= 4.0.8 */ + /* APU4 w/ legacy BIOS >= 4.0.8 */ { .ident = "apu4", .matches = { @@ -207,7 +207,7 @@ static const struct dmi_system_id apu_gpio_dmi_table[] __initconst = { }, .driver_data = (void *)&board_apu2, }, - /* APU4 w/ mainline bios */ + /* APU4 w/ mainline BIOS */ { .ident = "apu4", .matches = { @@ -250,7 +250,7 @@ static int __init apu_board_init(void) id = dmi_first_match(apu_gpio_dmi_table); if (!id) { - pr_err("failed to detect apu board via dmi\n"); + pr_err("failed to detect APU board via DMI\n"); return -ENODEV; } @@ -289,7 +289,7 @@ module_init(apu_board_init); module_exit(apu_board_exit); MODULE_AUTHOR("Enrico Weigelt, metux IT consult "); -MODULE_DESCRIPTION("PC Engines APUv2/APUv3 board GPIO/LED/keys driver"); +MODULE_DESCRIPTION("PC Engines APUv2/APUv3 board GPIO/LEDs/keys driver"); MODULE_LICENSE("GPL"); MODULE_DEVICE_TABLE(dmi, apu_gpio_dmi_table); MODULE_ALIAS("platform:pcengines-apuv2"); -- GitLab From 06b76d44ba25e52711dc7cc4fc75b50907bc6b8e Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Thu, 19 Dec 2019 14:44:26 -0700 Subject: [PATCH 1172/1437] io_uring: standardize the prep methods We currently have a mix of use cases. Most of the newer ones are pretty uniform, but we have some older ones that use different calling calling conventions. This is confusing. For the opcodes that currently rely on the req->io->sqe copy saving them from reuse, add a request type struct in the io_kiocb command union to store the data they need. Prepare for all opcodes having a standard prep method, so we can call it in a uniform fashion and outside of the opcode handler. This is in preparation for passing in the 'sqe' pointer, rather than storing it in the io_kiocb. Once we have uniform prep handlers, we can leave all the prep work to that part, and not even pass in the sqe to the opcode handler. This ensures that we don't reuse sqe data inadvertently. Signed-off-by: Jens Axboe --- fs/io_uring.c | 128 +++++++++++++++++++++++++------------------------- 1 file changed, 63 insertions(+), 65 deletions(-) diff --git a/fs/io_uring.c b/fs/io_uring.c index 7e8d287500532..2cdfbb451fe21 100644 --- a/fs/io_uring.c +++ b/fs/io_uring.c @@ -371,7 +371,6 @@ struct io_async_rw { }; struct io_async_ctx { - struct io_uring_sqe sqe; union { struct io_async_rw rw; struct io_async_msghdr msg; @@ -433,7 +432,6 @@ struct io_kiocb { #define REQ_F_INFLIGHT 16384 /* on inflight list */ #define REQ_F_COMP_LOCKED 32768 /* completion under lock */ #define REQ_F_HARDLINK 65536 /* doesn't sever on completion < 0 */ -#define REQ_F_PREPPED 131072 /* request already opcode prepared */ u64 user_data; u32 result; u32 sequence; @@ -1501,6 +1499,8 @@ static int io_prep_rw(struct io_kiocb *req, bool force_nonblock) unsigned ioprio; int ret; + if (!sqe) + return 0; if (!req->file) return -EBADF; @@ -1552,6 +1552,7 @@ static int io_prep_rw(struct io_kiocb *req, bool force_nonblock) /* we own ->private, reuse it for the buffer index */ req->rw.kiocb.private = (void *) (unsigned long) READ_ONCE(req->sqe->buf_index); + req->sqe = NULL; return 0; } @@ -1773,13 +1774,7 @@ static void io_req_map_rw(struct io_kiocb *req, ssize_t io_size, static int io_alloc_async_ctx(struct io_kiocb *req) { req->io = kmalloc(sizeof(*req->io), GFP_KERNEL); - if (req->io) { - memcpy(&req->io->sqe, req->sqe, sizeof(req->io->sqe)); - req->sqe = &req->io->sqe; - return 0; - } - - return 1; + return req->io == NULL; } static void io_rw_async(struct io_wq_work **workptr) @@ -1810,12 +1805,14 @@ static int io_read_prep(struct io_kiocb *req, struct iovec **iovec, { ssize_t ret; - ret = io_prep_rw(req, force_nonblock); - if (ret) - return ret; + if (req->sqe) { + ret = io_prep_rw(req, force_nonblock); + if (ret) + return ret; - if (unlikely(!(req->file->f_mode & FMODE_READ))) - return -EBADF; + if (unlikely(!(req->file->f_mode & FMODE_READ))) + return -EBADF; + } return io_import_iovec(READ, req, iovec, iter); } @@ -1829,15 +1826,9 @@ static int io_read(struct io_kiocb *req, struct io_kiocb **nxt, size_t iov_count; ssize_t io_size, ret; - if (!req->io) { - ret = io_read_prep(req, &iovec, &iter, force_nonblock); - if (ret < 0) - return ret; - } else { - ret = io_import_iovec(READ, req, &iovec, &iter); - if (ret < 0) - return ret; - } + ret = io_read_prep(req, &iovec, &iter, force_nonblock); + if (ret < 0) + return ret; /* Ensure we clear previously set non-block flag */ if (!force_nonblock) @@ -1901,12 +1892,14 @@ static int io_write_prep(struct io_kiocb *req, struct iovec **iovec, { ssize_t ret; - ret = io_prep_rw(req, force_nonblock); - if (ret) - return ret; + if (req->sqe) { + ret = io_prep_rw(req, force_nonblock); + if (ret) + return ret; - if (unlikely(!(req->file->f_mode & FMODE_WRITE))) - return -EBADF; + if (unlikely(!(req->file->f_mode & FMODE_WRITE))) + return -EBADF; + } return io_import_iovec(WRITE, req, iovec, iter); } @@ -1920,15 +1913,9 @@ static int io_write(struct io_kiocb *req, struct io_kiocb **nxt, size_t iov_count; ssize_t ret, io_size; - if (!req->io) { - ret = io_write_prep(req, &iovec, &iter, force_nonblock); - if (ret < 0) - return ret; - } else { - ret = io_import_iovec(WRITE, req, &iovec, &iter); - if (ret < 0) - return ret; - } + ret = io_write_prep(req, &iovec, &iter, force_nonblock); + if (ret < 0) + return ret; /* Ensure we clear previously set non-block flag */ if (!force_nonblock) @@ -2013,7 +2000,7 @@ static int io_prep_fsync(struct io_kiocb *req) const struct io_uring_sqe *sqe = req->sqe; struct io_ring_ctx *ctx = req->ctx; - if (req->flags & REQ_F_PREPPED) + if (!req->sqe) return 0; if (!req->file) return -EBADF; @@ -2029,7 +2016,7 @@ static int io_prep_fsync(struct io_kiocb *req) req->sync.off = READ_ONCE(sqe->off); req->sync.len = READ_ONCE(sqe->len); - req->flags |= REQ_F_PREPPED; + req->sqe = NULL; return 0; } @@ -2095,7 +2082,7 @@ static int io_prep_sfr(struct io_kiocb *req) const struct io_uring_sqe *sqe = req->sqe; struct io_ring_ctx *ctx = req->ctx; - if (req->flags & REQ_F_PREPPED) + if (!sqe) return 0; if (!req->file) return -EBADF; @@ -2108,7 +2095,7 @@ static int io_prep_sfr(struct io_kiocb *req) req->sync.off = READ_ONCE(sqe->off); req->sync.len = READ_ONCE(sqe->len); req->sync.flags = READ_ONCE(sqe->sync_range_flags); - req->flags |= REQ_F_PREPPED; + req->sqe = NULL; return 0; } @@ -2173,12 +2160,17 @@ static int io_sendmsg_prep(struct io_kiocb *req, struct io_async_ctx *io) #if defined(CONFIG_NET) const struct io_uring_sqe *sqe = req->sqe; struct io_sr_msg *sr = &req->sr_msg; + int ret; + if (!sqe) + return 0; sr->msg_flags = READ_ONCE(sqe->msg_flags); sr->msg = u64_to_user_ptr(READ_ONCE(sqe->addr)); io->msg.iov = io->msg.fast_iov; - return sendmsg_copy_msghdr(&io->msg.msg, sr->msg, sr->msg_flags, + ret = sendmsg_copy_msghdr(&io->msg.msg, sr->msg, sr->msg_flags, &io->msg.iov); + req->sqe = NULL; + return ret; #else return -EOPNOTSUPP; #endif @@ -2253,12 +2245,18 @@ static int io_recvmsg_prep(struct io_kiocb *req, struct io_async_ctx *io) { #if defined(CONFIG_NET) struct io_sr_msg *sr = &req->sr_msg; + int ret; + + if (!req->sqe) + return 0; sr->msg_flags = READ_ONCE(req->sqe->msg_flags); sr->msg = u64_to_user_ptr(READ_ONCE(req->sqe->addr)); io->msg.iov = io->msg.fast_iov; - return recvmsg_copy_msghdr(&io->msg.msg, sr->msg, sr->msg_flags, + ret = recvmsg_copy_msghdr(&io->msg.msg, sr->msg, sr->msg_flags, &io->msg.uaddr, &io->msg.iov); + req->sqe = NULL; + return ret; #else return -EOPNOTSUPP; #endif @@ -2336,7 +2334,7 @@ static int io_accept_prep(struct io_kiocb *req) const struct io_uring_sqe *sqe = req->sqe; struct io_accept *accept = &req->accept; - if (req->flags & REQ_F_PREPPED) + if (!req->sqe) return 0; if (unlikely(req->ctx->flags & (IORING_SETUP_IOPOLL|IORING_SETUP_SQPOLL))) @@ -2347,7 +2345,7 @@ static int io_accept_prep(struct io_kiocb *req) accept->addr = u64_to_user_ptr(READ_ONCE(sqe->addr)); accept->addr_len = u64_to_user_ptr(READ_ONCE(sqe->addr2)); accept->flags = READ_ONCE(sqe->accept_flags); - req->flags |= REQ_F_PREPPED; + req->sqe = NULL; return 0; #else return -EOPNOTSUPP; @@ -2416,7 +2414,10 @@ static int io_connect_prep(struct io_kiocb *req, struct io_async_ctx *io) { #if defined(CONFIG_NET) const struct io_uring_sqe *sqe = req->sqe; + int ret; + if (!sqe) + return 0; if (unlikely(req->ctx->flags & (IORING_SETUP_IOPOLL|IORING_SETUP_SQPOLL))) return -EINVAL; if (sqe->ioprio || sqe->len || sqe->buf_index || sqe->rw_flags) @@ -2424,8 +2425,10 @@ static int io_connect_prep(struct io_kiocb *req, struct io_async_ctx *io) req->connect.addr = u64_to_user_ptr(READ_ONCE(sqe->addr)); req->connect.addr_len = READ_ONCE(sqe->addr2); - return move_addr_to_kernel(req->connect.addr, req->connect.addr_len, + ret = move_addr_to_kernel(req->connect.addr, req->connect.addr_len, &io->connect.address); + req->sqe = NULL; + return ret; #else return -EOPNOTSUPP; #endif @@ -2526,7 +2529,7 @@ static int io_poll_remove_prep(struct io_kiocb *req) { const struct io_uring_sqe *sqe = req->sqe; - if (req->flags & REQ_F_PREPPED) + if (!sqe) return 0; if (unlikely(req->ctx->flags & IORING_SETUP_IOPOLL)) return -EINVAL; @@ -2535,7 +2538,7 @@ static int io_poll_remove_prep(struct io_kiocb *req) return -EINVAL; req->poll.addr = READ_ONCE(sqe->addr); - req->flags |= REQ_F_PREPPED; + req->sqe = NULL; return 0; } @@ -2696,7 +2699,7 @@ static int io_poll_add_prep(struct io_kiocb *req) struct io_poll_iocb *poll = &req->poll; u16 events; - if (req->flags & REQ_F_PREPPED) + if (!sqe) return 0; if (unlikely(req->ctx->flags & IORING_SETUP_IOPOLL)) return -EINVAL; @@ -2705,9 +2708,9 @@ static int io_poll_add_prep(struct io_kiocb *req) if (!poll->file) return -EBADF; - req->flags |= REQ_F_PREPPED; events = READ_ONCE(sqe->poll_events); poll->events = demangle_poll(events) | EPOLLERR | EPOLLHUP; + req->sqe = NULL; return 0; } @@ -2845,7 +2848,7 @@ static int io_timeout_remove_prep(struct io_kiocb *req) { const struct io_uring_sqe *sqe = req->sqe; - if (req->flags & REQ_F_PREPPED) + if (!sqe) return 0; if (unlikely(req->ctx->flags & IORING_SETUP_IOPOLL)) return -EINVAL; @@ -2857,7 +2860,7 @@ static int io_timeout_remove_prep(struct io_kiocb *req) if (req->timeout.flags) return -EINVAL; - req->flags |= REQ_F_PREPPED; + req->sqe = NULL; return 0; } @@ -2893,6 +2896,8 @@ static int io_timeout_prep(struct io_kiocb *req, struct io_async_ctx *io, struct io_timeout_data *data; unsigned flags; + if (!sqe) + return 0; if (unlikely(req->ctx->flags & IORING_SETUP_IOPOLL)) return -EINVAL; if (sqe->ioprio || sqe->buf_index || sqe->len != 1) @@ -2921,6 +2926,7 @@ static int io_timeout_prep(struct io_kiocb *req, struct io_async_ctx *io, data->mode = HRTIMER_MODE_REL; hrtimer_init(&data->timer, CLOCK_MONOTONIC, data->mode); + req->sqe = NULL; return 0; } @@ -2933,13 +2939,9 @@ static int io_timeout(struct io_kiocb *req) unsigned span = 0; int ret; - if (!req->io) { - if (io_alloc_async_ctx(req)) - return -ENOMEM; - ret = io_timeout_prep(req, req->io, false); - if (ret) - return ret; - } + ret = io_timeout_prep(req, req->io, false); + if (ret) + return ret; data = &req->io->timeout; /* @@ -3069,7 +3071,7 @@ static int io_async_cancel_prep(struct io_kiocb *req) { const struct io_uring_sqe *sqe = req->sqe; - if (req->flags & REQ_F_PREPPED) + if (!sqe) return 0; if (unlikely(req->ctx->flags & IORING_SETUP_IOPOLL)) return -EINVAL; @@ -3077,8 +3079,8 @@ static int io_async_cancel_prep(struct io_kiocb *req) sqe->cancel_flags) return -EINVAL; - req->flags |= REQ_F_PREPPED; req->cancel.addr = READ_ONCE(sqe->addr); + req->sqe = NULL; return 0; } @@ -3213,13 +3215,9 @@ static int io_issue_sqe(struct io_kiocb *req, struct io_kiocb **nxt, ret = io_nop(req); break; case IORING_OP_READV: - if (unlikely(req->sqe->buf_index)) - return -EINVAL; ret = io_read(req, nxt, force_nonblock); break; case IORING_OP_WRITEV: - if (unlikely(req->sqe->buf_index)) - return -EINVAL; ret = io_write(req, nxt, force_nonblock); break; case IORING_OP_READ_FIXED: -- GitLab From 3529d8c2b353e6e446277ae96a36e7471cb070fc Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Thu, 19 Dec 2019 18:24:38 -0700 Subject: [PATCH 1173/1437] io_uring: pass in 'sqe' to the prep handlers This moves the prep handlers outside of the opcode handlers, and allows us to pass in the sqe directly. If the sqe is non-NULL, it means that the request should be prepared for the first time. With the opcode handlers not having access to the sqe at all, we are guaranteed that the prep handler has setup the request fully by the time we get there. As before, for opcodes that need to copy in more data then the io_kiocb allows for, the io_async_ctx holds that info. If a prep handler is invoked with req->io set, it must use that to retain information for later. Finally, we can remove io_kiocb->sqe as well. Signed-off-by: Jens Axboe --- fs/io_uring.c | 493 +++++++++++++++++++++++++------------------------- 1 file changed, 251 insertions(+), 242 deletions(-) diff --git a/fs/io_uring.c b/fs/io_uring.c index 2cdfbb451fe21..562e3a1a1bf95 100644 --- a/fs/io_uring.c +++ b/fs/io_uring.c @@ -398,7 +398,6 @@ struct io_kiocb { struct io_sr_msg sr_msg; }; - const struct io_uring_sqe *sqe; struct io_async_ctx *io; struct file *ring_file; int ring_fd; @@ -629,33 +628,31 @@ static inline bool io_prep_async_work(struct io_kiocb *req, { bool do_hashed = false; - if (req->sqe) { - switch (req->opcode) { - case IORING_OP_WRITEV: - case IORING_OP_WRITE_FIXED: - /* only regular files should be hashed for writes */ - if (req->flags & REQ_F_ISREG) - do_hashed = true; - /* fall-through */ - case IORING_OP_READV: - case IORING_OP_READ_FIXED: - case IORING_OP_SENDMSG: - case IORING_OP_RECVMSG: - case IORING_OP_ACCEPT: - case IORING_OP_POLL_ADD: - case IORING_OP_CONNECT: - /* - * We know REQ_F_ISREG is not set on some of these - * opcodes, but this enables us to keep the check in - * just one place. - */ - if (!(req->flags & REQ_F_ISREG)) - req->work.flags |= IO_WQ_WORK_UNBOUND; - break; - } - if (io_req_needs_user(req)) - req->work.flags |= IO_WQ_WORK_NEEDS_USER; + switch (req->opcode) { + case IORING_OP_WRITEV: + case IORING_OP_WRITE_FIXED: + /* only regular files should be hashed for writes */ + if (req->flags & REQ_F_ISREG) + do_hashed = true; + /* fall-through */ + case IORING_OP_READV: + case IORING_OP_READ_FIXED: + case IORING_OP_SENDMSG: + case IORING_OP_RECVMSG: + case IORING_OP_ACCEPT: + case IORING_OP_POLL_ADD: + case IORING_OP_CONNECT: + /* + * We know REQ_F_ISREG is not set on some of these + * opcodes, but this enables us to keep the check in + * just one place. + */ + if (!(req->flags & REQ_F_ISREG)) + req->work.flags |= IO_WQ_WORK_UNBOUND; + break; } + if (io_req_needs_user(req)) + req->work.flags |= IO_WQ_WORK_NEEDS_USER; *link = io_prep_linked_timeout(req); return do_hashed; @@ -1491,16 +1488,14 @@ static bool io_file_supports_async(struct file *file) return false; } -static int io_prep_rw(struct io_kiocb *req, bool force_nonblock) +static int io_prep_rw(struct io_kiocb *req, const struct io_uring_sqe *sqe, + bool force_nonblock) { - const struct io_uring_sqe *sqe = req->sqe; struct io_ring_ctx *ctx = req->ctx; struct kiocb *kiocb = &req->rw.kiocb; unsigned ioprio; int ret; - if (!sqe) - return 0; if (!req->file) return -EBADF; @@ -1547,12 +1542,11 @@ static int io_prep_rw(struct io_kiocb *req, bool force_nonblock) kiocb->ki_complete = io_complete_rw; } - req->rw.addr = READ_ONCE(req->sqe->addr); - req->rw.len = READ_ONCE(req->sqe->len); + req->rw.addr = READ_ONCE(sqe->addr); + req->rw.len = READ_ONCE(sqe->len); /* we own ->private, reuse it for the buffer index */ req->rw.kiocb.private = (void *) (unsigned long) - READ_ONCE(req->sqe->buf_index); - req->sqe = NULL; + READ_ONCE(sqe->buf_index); return 0; } @@ -1800,21 +1794,33 @@ static int io_setup_async_rw(struct io_kiocb *req, ssize_t io_size, return 0; } -static int io_read_prep(struct io_kiocb *req, struct iovec **iovec, - struct iov_iter *iter, bool force_nonblock) +static int io_read_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe, + bool force_nonblock) { + struct io_async_ctx *io; + struct iov_iter iter; ssize_t ret; - if (req->sqe) { - ret = io_prep_rw(req, force_nonblock); - if (ret) - return ret; + ret = io_prep_rw(req, sqe, force_nonblock); + if (ret) + return ret; - if (unlikely(!(req->file->f_mode & FMODE_READ))) - return -EBADF; - } + if (unlikely(!(req->file->f_mode & FMODE_READ))) + return -EBADF; - return io_import_iovec(READ, req, iovec, iter); + if (!req->io) + return 0; + + io = req->io; + io->rw.iov = io->rw.fast_iov; + req->io = NULL; + ret = io_import_iovec(READ, req, &io->rw.iov, &iter); + req->io = io; + if (ret < 0) + return ret; + + io_req_map_rw(req, ret, io->rw.iov, io->rw.fast_iov, &iter); + return 0; } static int io_read(struct io_kiocb *req, struct io_kiocb **nxt, @@ -1826,7 +1832,7 @@ static int io_read(struct io_kiocb *req, struct io_kiocb **nxt, size_t iov_count; ssize_t io_size, ret; - ret = io_read_prep(req, &iovec, &iter, force_nonblock); + ret = io_import_iovec(READ, req, &iovec, &iter); if (ret < 0) return ret; @@ -1887,21 +1893,33 @@ out_free: return ret; } -static int io_write_prep(struct io_kiocb *req, struct iovec **iovec, - struct iov_iter *iter, bool force_nonblock) +static int io_write_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe, + bool force_nonblock) { + struct io_async_ctx *io; + struct iov_iter iter; ssize_t ret; - if (req->sqe) { - ret = io_prep_rw(req, force_nonblock); - if (ret) - return ret; + ret = io_prep_rw(req, sqe, force_nonblock); + if (ret) + return ret; - if (unlikely(!(req->file->f_mode & FMODE_WRITE))) - return -EBADF; - } + if (unlikely(!(req->file->f_mode & FMODE_WRITE))) + return -EBADF; - return io_import_iovec(WRITE, req, iovec, iter); + if (!req->io) + return 0; + + io = req->io; + io->rw.iov = io->rw.fast_iov; + req->io = NULL; + ret = io_import_iovec(WRITE, req, &io->rw.iov, &iter); + req->io = io; + if (ret < 0) + return ret; + + io_req_map_rw(req, ret, io->rw.iov, io->rw.fast_iov, &iter); + return 0; } static int io_write(struct io_kiocb *req, struct io_kiocb **nxt, @@ -1913,7 +1931,7 @@ static int io_write(struct io_kiocb *req, struct io_kiocb **nxt, size_t iov_count; ssize_t ret, io_size; - ret = io_write_prep(req, &iovec, &iter, force_nonblock); + ret = io_import_iovec(WRITE, req, &iovec, &iter); if (ret < 0) return ret; @@ -1995,13 +2013,10 @@ static int io_nop(struct io_kiocb *req) return 0; } -static int io_prep_fsync(struct io_kiocb *req) +static int io_prep_fsync(struct io_kiocb *req, const struct io_uring_sqe *sqe) { - const struct io_uring_sqe *sqe = req->sqe; struct io_ring_ctx *ctx = req->ctx; - if (!req->sqe) - return 0; if (!req->file) return -EBADF; @@ -2016,7 +2031,6 @@ static int io_prep_fsync(struct io_kiocb *req) req->sync.off = READ_ONCE(sqe->off); req->sync.len = READ_ONCE(sqe->len); - req->sqe = NULL; return 0; } @@ -2057,11 +2071,6 @@ static int io_fsync(struct io_kiocb *req, struct io_kiocb **nxt, bool force_nonblock) { struct io_wq_work *work, *old_work; - int ret; - - ret = io_prep_fsync(req); - if (ret) - return ret; /* fsync always requires a blocking context */ if (force_nonblock) { @@ -2077,13 +2086,10 @@ static int io_fsync(struct io_kiocb *req, struct io_kiocb **nxt, return 0; } -static int io_prep_sfr(struct io_kiocb *req) +static int io_prep_sfr(struct io_kiocb *req, const struct io_uring_sqe *sqe) { - const struct io_uring_sqe *sqe = req->sqe; struct io_ring_ctx *ctx = req->ctx; - if (!sqe) - return 0; if (!req->file) return -EBADF; @@ -2095,7 +2101,6 @@ static int io_prep_sfr(struct io_kiocb *req) req->sync.off = READ_ONCE(sqe->off); req->sync.len = READ_ONCE(sqe->len); req->sync.flags = READ_ONCE(sqe->sync_range_flags); - req->sqe = NULL; return 0; } @@ -2122,11 +2127,6 @@ static int io_sync_file_range(struct io_kiocb *req, struct io_kiocb **nxt, bool force_nonblock) { struct io_wq_work *work, *old_work; - int ret; - - ret = io_prep_sfr(req); - if (ret) - return ret; /* sync_file_range always requires a blocking context */ if (force_nonblock) { @@ -2155,22 +2155,21 @@ static void io_sendrecv_async(struct io_wq_work **workptr) } #endif -static int io_sendmsg_prep(struct io_kiocb *req, struct io_async_ctx *io) +static int io_sendmsg_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) { #if defined(CONFIG_NET) - const struct io_uring_sqe *sqe = req->sqe; struct io_sr_msg *sr = &req->sr_msg; - int ret; + struct io_async_ctx *io = req->io; - if (!sqe) - return 0; sr->msg_flags = READ_ONCE(sqe->msg_flags); sr->msg = u64_to_user_ptr(READ_ONCE(sqe->addr)); + + if (!io) + return 0; + io->msg.iov = io->msg.fast_iov; - ret = sendmsg_copy_msghdr(&io->msg.msg, sr->msg, sr->msg_flags, + return sendmsg_copy_msghdr(&io->msg.msg, sr->msg, sr->msg_flags, &io->msg.iov); - req->sqe = NULL; - return ret; #else return -EOPNOTSUPP; #endif @@ -2201,11 +2200,16 @@ static int io_sendmsg(struct io_kiocb *req, struct io_kiocb **nxt, kmsg->iov = kmsg->fast_iov; kmsg->msg.msg_iter.iov = kmsg->iov; } else { + struct io_sr_msg *sr = &req->sr_msg; + kmsg = &io.msg; kmsg->msg.msg_name = &addr; - ret = io_sendmsg_prep(req, &io); + + io.msg.iov = io.msg.fast_iov; + ret = sendmsg_copy_msghdr(&io.msg.msg, sr->msg, + sr->msg_flags, &io.msg.iov); if (ret) - goto out; + return ret; } flags = req->sr_msg.msg_flags; @@ -2228,7 +2232,6 @@ static int io_sendmsg(struct io_kiocb *req, struct io_kiocb **nxt, ret = -EINTR; } -out: if (!io_wq_current_is_worker() && kmsg && kmsg->iov != kmsg->fast_iov) kfree(kmsg->iov); io_cqring_add_event(req, ret); @@ -2241,22 +2244,22 @@ out: #endif } -static int io_recvmsg_prep(struct io_kiocb *req, struct io_async_ctx *io) +static int io_recvmsg_prep(struct io_kiocb *req, + const struct io_uring_sqe *sqe) { #if defined(CONFIG_NET) struct io_sr_msg *sr = &req->sr_msg; - int ret; + struct io_async_ctx *io = req->io; + + sr->msg_flags = READ_ONCE(sqe->msg_flags); + sr->msg = u64_to_user_ptr(READ_ONCE(sqe->addr)); - if (!req->sqe) + if (!io) return 0; - sr->msg_flags = READ_ONCE(req->sqe->msg_flags); - sr->msg = u64_to_user_ptr(READ_ONCE(req->sqe->addr)); io->msg.iov = io->msg.fast_iov; - ret = recvmsg_copy_msghdr(&io->msg.msg, sr->msg, sr->msg_flags, + return recvmsg_copy_msghdr(&io->msg.msg, sr->msg, sr->msg_flags, &io->msg.uaddr, &io->msg.iov); - req->sqe = NULL; - return ret; #else return -EOPNOTSUPP; #endif @@ -2287,11 +2290,17 @@ static int io_recvmsg(struct io_kiocb *req, struct io_kiocb **nxt, kmsg->iov = kmsg->fast_iov; kmsg->msg.msg_iter.iov = kmsg->iov; } else { + struct io_sr_msg *sr = &req->sr_msg; + kmsg = &io.msg; kmsg->msg.msg_name = &addr; - ret = io_recvmsg_prep(req, &io); + + io.msg.iov = io.msg.fast_iov; + ret = recvmsg_copy_msghdr(&io.msg.msg, sr->msg, + sr->msg_flags, &io.msg.uaddr, + &io.msg.iov); if (ret) - goto out; + return ret; } flags = req->sr_msg.msg_flags; @@ -2315,7 +2324,6 @@ static int io_recvmsg(struct io_kiocb *req, struct io_kiocb **nxt, ret = -EINTR; } -out: if (!io_wq_current_is_worker() && kmsg && kmsg->iov != kmsg->fast_iov) kfree(kmsg->iov); io_cqring_add_event(req, ret); @@ -2328,15 +2336,11 @@ out: #endif } -static int io_accept_prep(struct io_kiocb *req) +static int io_accept_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) { #if defined(CONFIG_NET) - const struct io_uring_sqe *sqe = req->sqe; struct io_accept *accept = &req->accept; - if (!req->sqe) - return 0; - if (unlikely(req->ctx->flags & (IORING_SETUP_IOPOLL|IORING_SETUP_SQPOLL))) return -EINVAL; if (sqe->ioprio || sqe->len || sqe->buf_index) @@ -2345,7 +2349,6 @@ static int io_accept_prep(struct io_kiocb *req) accept->addr = u64_to_user_ptr(READ_ONCE(sqe->addr)); accept->addr_len = u64_to_user_ptr(READ_ONCE(sqe->addr2)); accept->flags = READ_ONCE(sqe->accept_flags); - req->sqe = NULL; return 0; #else return -EOPNOTSUPP; @@ -2393,10 +2396,6 @@ static int io_accept(struct io_kiocb *req, struct io_kiocb **nxt, #if defined(CONFIG_NET) int ret; - ret = io_accept_prep(req); - if (ret) - return ret; - ret = __io_accept(req, nxt, force_nonblock); if (ret == -EAGAIN && force_nonblock) { req->work.func = io_accept_finish; @@ -2410,25 +2409,25 @@ static int io_accept(struct io_kiocb *req, struct io_kiocb **nxt, #endif } -static int io_connect_prep(struct io_kiocb *req, struct io_async_ctx *io) +static int io_connect_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) { #if defined(CONFIG_NET) - const struct io_uring_sqe *sqe = req->sqe; - int ret; + struct io_connect *conn = &req->connect; + struct io_async_ctx *io = req->io; - if (!sqe) - return 0; if (unlikely(req->ctx->flags & (IORING_SETUP_IOPOLL|IORING_SETUP_SQPOLL))) return -EINVAL; if (sqe->ioprio || sqe->len || sqe->buf_index || sqe->rw_flags) return -EINVAL; - req->connect.addr = u64_to_user_ptr(READ_ONCE(sqe->addr)); - req->connect.addr_len = READ_ONCE(sqe->addr2); - ret = move_addr_to_kernel(req->connect.addr, req->connect.addr_len, + conn->addr = u64_to_user_ptr(READ_ONCE(sqe->addr)); + conn->addr_len = READ_ONCE(sqe->addr2); + + if (!io) + return 0; + + return move_addr_to_kernel(conn->addr, conn->addr_len, &io->connect.address); - req->sqe = NULL; - return ret; #else return -EOPNOTSUPP; #endif @@ -2445,7 +2444,9 @@ static int io_connect(struct io_kiocb *req, struct io_kiocb **nxt, if (req->io) { io = req->io; } else { - ret = io_connect_prep(req, &__io); + ret = move_addr_to_kernel(req->connect.addr, + req->connect.addr_len, + &__io.connect.address); if (ret) goto out; io = &__io; @@ -2525,12 +2526,9 @@ static int io_poll_cancel(struct io_ring_ctx *ctx, __u64 sqe_addr) return -ENOENT; } -static int io_poll_remove_prep(struct io_kiocb *req) +static int io_poll_remove_prep(struct io_kiocb *req, + const struct io_uring_sqe *sqe) { - const struct io_uring_sqe *sqe = req->sqe; - - if (!sqe) - return 0; if (unlikely(req->ctx->flags & IORING_SETUP_IOPOLL)) return -EINVAL; if (sqe->ioprio || sqe->off || sqe->len || sqe->buf_index || @@ -2538,7 +2536,6 @@ static int io_poll_remove_prep(struct io_kiocb *req) return -EINVAL; req->poll.addr = READ_ONCE(sqe->addr); - req->sqe = NULL; return 0; } @@ -2552,10 +2549,6 @@ static int io_poll_remove(struct io_kiocb *req) u64 addr; int ret; - ret = io_poll_remove_prep(req); - if (ret) - return ret; - addr = req->poll.addr; spin_lock_irq(&ctx->completion_lock); ret = io_poll_cancel(ctx, addr); @@ -2693,14 +2686,11 @@ static void io_poll_req_insert(struct io_kiocb *req) hlist_add_head(&req->hash_node, list); } -static int io_poll_add_prep(struct io_kiocb *req) +static int io_poll_add_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) { - const struct io_uring_sqe *sqe = req->sqe; struct io_poll_iocb *poll = &req->poll; u16 events; - if (!sqe) - return 0; if (unlikely(req->ctx->flags & IORING_SETUP_IOPOLL)) return -EINVAL; if (sqe->addr || sqe->ioprio || sqe->off || sqe->len || sqe->buf_index) @@ -2710,7 +2700,6 @@ static int io_poll_add_prep(struct io_kiocb *req) events = READ_ONCE(sqe->poll_events); poll->events = demangle_poll(events) | EPOLLERR | EPOLLHUP; - req->sqe = NULL; return 0; } @@ -2721,11 +2710,6 @@ static int io_poll_add(struct io_kiocb *req, struct io_kiocb **nxt) struct io_poll_table ipt; bool cancel = false; __poll_t mask; - int ret; - - ret = io_poll_add_prep(req); - if (ret) - return ret; INIT_IO_WORK(&req->work, io_poll_complete_work); INIT_HLIST_NODE(&req->hash_node); @@ -2844,12 +2828,9 @@ static int io_timeout_cancel(struct io_ring_ctx *ctx, __u64 user_data) return 0; } -static int io_timeout_remove_prep(struct io_kiocb *req) +static int io_timeout_remove_prep(struct io_kiocb *req, + const struct io_uring_sqe *sqe) { - const struct io_uring_sqe *sqe = req->sqe; - - if (!sqe) - return 0; if (unlikely(req->ctx->flags & IORING_SETUP_IOPOLL)) return -EINVAL; if (sqe->flags || sqe->ioprio || sqe->buf_index || sqe->len) @@ -2860,7 +2841,6 @@ static int io_timeout_remove_prep(struct io_kiocb *req) if (req->timeout.flags) return -EINVAL; - req->sqe = NULL; return 0; } @@ -2872,10 +2852,6 @@ static int io_timeout_remove(struct io_kiocb *req) struct io_ring_ctx *ctx = req->ctx; int ret; - ret = io_timeout_remove_prep(req); - if (ret) - return ret; - spin_lock_irq(&ctx->completion_lock); ret = io_timeout_cancel(ctx, req->timeout.addr); @@ -2889,15 +2865,12 @@ static int io_timeout_remove(struct io_kiocb *req) return 0; } -static int io_timeout_prep(struct io_kiocb *req, struct io_async_ctx *io, +static int io_timeout_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe, bool is_timeout_link) { - const struct io_uring_sqe *sqe = req->sqe; struct io_timeout_data *data; unsigned flags; - if (!sqe) - return 0; if (unlikely(req->ctx->flags & IORING_SETUP_IOPOLL)) return -EINVAL; if (sqe->ioprio || sqe->buf_index || sqe->len != 1) @@ -2910,7 +2883,7 @@ static int io_timeout_prep(struct io_kiocb *req, struct io_async_ctx *io, req->timeout.count = READ_ONCE(sqe->off); - if (!io && io_alloc_async_ctx(req)) + if (!req->io && io_alloc_async_ctx(req)) return -ENOMEM; data = &req->io->timeout; @@ -2926,7 +2899,6 @@ static int io_timeout_prep(struct io_kiocb *req, struct io_async_ctx *io, data->mode = HRTIMER_MODE_REL; hrtimer_init(&data->timer, CLOCK_MONOTONIC, data->mode); - req->sqe = NULL; return 0; } @@ -2937,11 +2909,7 @@ static int io_timeout(struct io_kiocb *req) struct io_timeout_data *data; struct list_head *entry; unsigned span = 0; - int ret; - ret = io_timeout_prep(req, req->io, false); - if (ret) - return ret; data = &req->io->timeout; /* @@ -3067,12 +3035,9 @@ done: io_put_req_find_next(req, nxt); } -static int io_async_cancel_prep(struct io_kiocb *req) +static int io_async_cancel_prep(struct io_kiocb *req, + const struct io_uring_sqe *sqe) { - const struct io_uring_sqe *sqe = req->sqe; - - if (!sqe) - return 0; if (unlikely(req->ctx->flags & IORING_SETUP_IOPOLL)) return -EINVAL; if (sqe->flags || sqe->ioprio || sqe->off || sqe->len || @@ -3080,28 +3045,20 @@ static int io_async_cancel_prep(struct io_kiocb *req) return -EINVAL; req->cancel.addr = READ_ONCE(sqe->addr); - req->sqe = NULL; return 0; } static int io_async_cancel(struct io_kiocb *req, struct io_kiocb **nxt) { struct io_ring_ctx *ctx = req->ctx; - int ret; - - ret = io_async_cancel_prep(req); - if (ret) - return ret; io_async_find_and_cancel(ctx, req, req->cancel.addr, nxt, 0); return 0; } -static int io_req_defer_prep(struct io_kiocb *req) +static int io_req_defer_prep(struct io_kiocb *req, + const struct io_uring_sqe *sqe) { - struct iovec inline_vecs[UIO_FASTIOV], *iovec = inline_vecs; - struct io_async_ctx *io = req->io; - struct iov_iter iter; ssize_t ret = 0; switch (req->opcode) { @@ -3109,61 +3066,47 @@ static int io_req_defer_prep(struct io_kiocb *req) break; case IORING_OP_READV: case IORING_OP_READ_FIXED: - /* ensure prep does right import */ - req->io = NULL; - ret = io_read_prep(req, &iovec, &iter, true); - req->io = io; - if (ret < 0) - break; - io_req_map_rw(req, ret, iovec, inline_vecs, &iter); - ret = 0; + ret = io_read_prep(req, sqe, true); break; case IORING_OP_WRITEV: case IORING_OP_WRITE_FIXED: - /* ensure prep does right import */ - req->io = NULL; - ret = io_write_prep(req, &iovec, &iter, true); - req->io = io; - if (ret < 0) - break; - io_req_map_rw(req, ret, iovec, inline_vecs, &iter); - ret = 0; + ret = io_write_prep(req, sqe, true); break; case IORING_OP_POLL_ADD: - ret = io_poll_add_prep(req); + ret = io_poll_add_prep(req, sqe); break; case IORING_OP_POLL_REMOVE: - ret = io_poll_remove_prep(req); + ret = io_poll_remove_prep(req, sqe); break; case IORING_OP_FSYNC: - ret = io_prep_fsync(req); + ret = io_prep_fsync(req, sqe); break; case IORING_OP_SYNC_FILE_RANGE: - ret = io_prep_sfr(req); + ret = io_prep_sfr(req, sqe); break; case IORING_OP_SENDMSG: - ret = io_sendmsg_prep(req, io); + ret = io_sendmsg_prep(req, sqe); break; case IORING_OP_RECVMSG: - ret = io_recvmsg_prep(req, io); + ret = io_recvmsg_prep(req, sqe); break; case IORING_OP_CONNECT: - ret = io_connect_prep(req, io); + ret = io_connect_prep(req, sqe); break; case IORING_OP_TIMEOUT: - ret = io_timeout_prep(req, io, false); + ret = io_timeout_prep(req, sqe, false); break; case IORING_OP_TIMEOUT_REMOVE: - ret = io_timeout_remove_prep(req); + ret = io_timeout_remove_prep(req, sqe); break; case IORING_OP_ASYNC_CANCEL: - ret = io_async_cancel_prep(req); + ret = io_async_cancel_prep(req, sqe); break; case IORING_OP_LINK_TIMEOUT: - ret = io_timeout_prep(req, io, true); + ret = io_timeout_prep(req, sqe, true); break; case IORING_OP_ACCEPT: - ret = io_accept_prep(req); + ret = io_accept_prep(req, sqe); break; default: printk_once(KERN_WARNING "io_uring: unhandled opcode %d\n", @@ -3175,7 +3118,7 @@ static int io_req_defer_prep(struct io_kiocb *req) return ret; } -static int io_req_defer(struct io_kiocb *req) +static int io_req_defer(struct io_kiocb *req, const struct io_uring_sqe *sqe) { struct io_ring_ctx *ctx = req->ctx; int ret; @@ -3184,10 +3127,10 @@ static int io_req_defer(struct io_kiocb *req) if (!req_need_defer(req) && list_empty(&ctx->defer_list)) return 0; - if (io_alloc_async_ctx(req)) + if (!req->io && io_alloc_async_ctx(req)) return -EAGAIN; - ret = io_req_defer_prep(req); + ret = io_req_defer_prep(req, sqe); if (ret < 0) return ret; @@ -3203,9 +3146,8 @@ static int io_req_defer(struct io_kiocb *req) return -EIOCBQUEUED; } -__attribute__((nonnull)) -static int io_issue_sqe(struct io_kiocb *req, struct io_kiocb **nxt, - bool force_nonblock) +static int io_issue_sqe(struct io_kiocb *req, const struct io_uring_sqe *sqe, + struct io_kiocb **nxt, bool force_nonblock) { struct io_ring_ctx *ctx = req->ctx; int ret; @@ -3215,48 +3157,109 @@ static int io_issue_sqe(struct io_kiocb *req, struct io_kiocb **nxt, ret = io_nop(req); break; case IORING_OP_READV: - ret = io_read(req, nxt, force_nonblock); - break; - case IORING_OP_WRITEV: - ret = io_write(req, nxt, force_nonblock); - break; case IORING_OP_READ_FIXED: + if (sqe) { + ret = io_read_prep(req, sqe, force_nonblock); + if (ret < 0) + break; + } ret = io_read(req, nxt, force_nonblock); break; + case IORING_OP_WRITEV: case IORING_OP_WRITE_FIXED: + if (sqe) { + ret = io_write_prep(req, sqe, force_nonblock); + if (ret < 0) + break; + } ret = io_write(req, nxt, force_nonblock); break; case IORING_OP_FSYNC: + if (sqe) { + ret = io_prep_fsync(req, sqe); + if (ret < 0) + break; + } ret = io_fsync(req, nxt, force_nonblock); break; case IORING_OP_POLL_ADD: + if (sqe) { + ret = io_poll_add_prep(req, sqe); + if (ret) + break; + } ret = io_poll_add(req, nxt); break; case IORING_OP_POLL_REMOVE: + if (sqe) { + ret = io_poll_remove_prep(req, sqe); + if (ret < 0) + break; + } ret = io_poll_remove(req); break; case IORING_OP_SYNC_FILE_RANGE: + if (sqe) { + ret = io_prep_sfr(req, sqe); + if (ret < 0) + break; + } ret = io_sync_file_range(req, nxt, force_nonblock); break; case IORING_OP_SENDMSG: + if (sqe) { + ret = io_sendmsg_prep(req, sqe); + if (ret < 0) + break; + } ret = io_sendmsg(req, nxt, force_nonblock); break; case IORING_OP_RECVMSG: + if (sqe) { + ret = io_recvmsg_prep(req, sqe); + if (ret) + break; + } ret = io_recvmsg(req, nxt, force_nonblock); break; case IORING_OP_TIMEOUT: + if (sqe) { + ret = io_timeout_prep(req, sqe, false); + if (ret) + break; + } ret = io_timeout(req); break; case IORING_OP_TIMEOUT_REMOVE: + if (sqe) { + ret = io_timeout_remove_prep(req, sqe); + if (ret) + break; + } ret = io_timeout_remove(req); break; case IORING_OP_ACCEPT: + if (sqe) { + ret = io_accept_prep(req, sqe); + if (ret) + break; + } ret = io_accept(req, nxt, force_nonblock); break; case IORING_OP_CONNECT: + if (sqe) { + ret = io_connect_prep(req, sqe); + if (ret) + break; + } ret = io_connect(req, nxt, force_nonblock); break; case IORING_OP_ASYNC_CANCEL: + if (sqe) { + ret = io_async_cancel_prep(req, sqe); + if (ret) + break; + } ret = io_async_cancel(req, nxt); break; default: @@ -3300,7 +3303,7 @@ static void io_wq_submit_work(struct io_wq_work **workptr) req->has_user = (work->flags & IO_WQ_WORK_HAS_MM) != 0; req->in_async = true; do { - ret = io_issue_sqe(req, &nxt, false); + ret = io_issue_sqe(req, NULL, &nxt, false); /* * We can get EAGAIN for polled IO even though we're * forcing a sync submission from here, since we can't @@ -3366,14 +3369,15 @@ static inline struct file *io_file_from_index(struct io_ring_ctx *ctx, return table->files[index & IORING_FILE_TABLE_MASK]; } -static int io_req_set_file(struct io_submit_state *state, struct io_kiocb *req) +static int io_req_set_file(struct io_submit_state *state, struct io_kiocb *req, + const struct io_uring_sqe *sqe) { struct io_ring_ctx *ctx = req->ctx; unsigned flags; int fd, ret; - flags = READ_ONCE(req->sqe->flags); - fd = READ_ONCE(req->sqe->fd); + flags = READ_ONCE(sqe->flags); + fd = READ_ONCE(sqe->fd); if (flags & IOSQE_IO_DRAIN) req->flags |= REQ_F_IO_DRAIN; @@ -3505,7 +3509,7 @@ static struct io_kiocb *io_prep_linked_timeout(struct io_kiocb *req) return nxt; } -static void __io_queue_sqe(struct io_kiocb *req) +static void __io_queue_sqe(struct io_kiocb *req, const struct io_uring_sqe *sqe) { struct io_kiocb *linked_timeout; struct io_kiocb *nxt = NULL; @@ -3514,7 +3518,7 @@ static void __io_queue_sqe(struct io_kiocb *req) again: linked_timeout = io_prep_linked_timeout(req); - ret = io_issue_sqe(req, &nxt, true); + ret = io_issue_sqe(req, sqe, &nxt, true); /* * We async punt it if the file wasn't marked NOWAIT, or if the file @@ -3561,7 +3565,7 @@ done_req: } } -static void io_queue_sqe(struct io_kiocb *req) +static void io_queue_sqe(struct io_kiocb *req, const struct io_uring_sqe *sqe) { int ret; @@ -3571,7 +3575,7 @@ static void io_queue_sqe(struct io_kiocb *req) } req->ctx->drain_next = (req->flags & REQ_F_DRAIN_LINK); - ret = io_req_defer(req); + ret = io_req_defer(req, sqe); if (ret) { if (ret != -EIOCBQUEUED) { io_cqring_add_event(req, ret); @@ -3579,7 +3583,7 @@ static void io_queue_sqe(struct io_kiocb *req) io_double_put_req(req); } } else - __io_queue_sqe(req); + __io_queue_sqe(req, sqe); } static inline void io_queue_link_head(struct io_kiocb *req) @@ -3588,25 +3592,25 @@ static inline void io_queue_link_head(struct io_kiocb *req) io_cqring_add_event(req, -ECANCELED); io_double_put_req(req); } else - io_queue_sqe(req); + io_queue_sqe(req, NULL); } #define SQE_VALID_FLAGS (IOSQE_FIXED_FILE|IOSQE_IO_DRAIN|IOSQE_IO_LINK| \ IOSQE_IO_HARDLINK) -static bool io_submit_sqe(struct io_kiocb *req, struct io_submit_state *state, - struct io_kiocb **link) +static bool io_submit_sqe(struct io_kiocb *req, const struct io_uring_sqe *sqe, + struct io_submit_state *state, struct io_kiocb **link) { struct io_ring_ctx *ctx = req->ctx; int ret; /* enforce forwards compatibility on users */ - if (unlikely(req->sqe->flags & ~SQE_VALID_FLAGS)) { + if (unlikely(sqe->flags & ~SQE_VALID_FLAGS)) { ret = -EINVAL; goto err_req; } - ret = io_req_set_file(state, req); + ret = io_req_set_file(state, req, sqe); if (unlikely(ret)) { err_req: io_cqring_add_event(req, ret); @@ -3624,10 +3628,10 @@ err_req: if (*link) { struct io_kiocb *prev = *link; - if (req->sqe->flags & IOSQE_IO_DRAIN) + if (sqe->flags & IOSQE_IO_DRAIN) (*link)->flags |= REQ_F_DRAIN_LINK | REQ_F_IO_DRAIN; - if (req->sqe->flags & IOSQE_IO_HARDLINK) + if (sqe->flags & IOSQE_IO_HARDLINK) req->flags |= REQ_F_HARDLINK; if (io_alloc_async_ctx(req)) { @@ -3635,7 +3639,7 @@ err_req: goto err_req; } - ret = io_req_defer_prep(req); + ret = io_req_defer_prep(req, sqe); if (ret) { /* fail even hard links since we don't submit */ prev->flags |= REQ_F_FAIL_LINK; @@ -3643,15 +3647,18 @@ err_req: } trace_io_uring_link(ctx, req, prev); list_add_tail(&req->link_list, &prev->link_list); - } else if (req->sqe->flags & (IOSQE_IO_LINK|IOSQE_IO_HARDLINK)) { + } else if (sqe->flags & (IOSQE_IO_LINK|IOSQE_IO_HARDLINK)) { req->flags |= REQ_F_LINK; - if (req->sqe->flags & IOSQE_IO_HARDLINK) + if (sqe->flags & IOSQE_IO_HARDLINK) req->flags |= REQ_F_HARDLINK; INIT_LIST_HEAD(&req->link_list); + ret = io_req_defer_prep(req, sqe); + if (ret) + req->flags |= REQ_F_FAIL_LINK; *link = req; } else { - io_queue_sqe(req); + io_queue_sqe(req, sqe); } return true; @@ -3696,14 +3703,15 @@ static void io_commit_sqring(struct io_ring_ctx *ctx) } /* - * Fetch an sqe, if one is available. Note that req->sqe will point to memory + * Fetch an sqe, if one is available. Note that sqe_ptr will point to memory * that is mapped by userspace. This means that care needs to be taken to * ensure that reads are stable, as we cannot rely on userspace always * being a good citizen. If members of the sqe are validated and then later * used, it's important that those reads are done through READ_ONCE() to * prevent a re-load down the line. */ -static bool io_get_sqring(struct io_ring_ctx *ctx, struct io_kiocb *req) +static bool io_get_sqring(struct io_ring_ctx *ctx, struct io_kiocb *req, + const struct io_uring_sqe **sqe_ptr) { struct io_rings *rings = ctx->rings; u32 *sq_array = ctx->sq_array; @@ -3730,9 +3738,9 @@ static bool io_get_sqring(struct io_ring_ctx *ctx, struct io_kiocb *req) * link list. */ req->sequence = ctx->cached_sq_head; - req->sqe = &ctx->sq_sqes[head]; - req->opcode = READ_ONCE(req->sqe->opcode); - req->user_data = READ_ONCE(req->sqe->user_data); + *sqe_ptr = &ctx->sq_sqes[head]; + req->opcode = READ_ONCE((*sqe_ptr)->opcode); + req->user_data = READ_ONCE((*sqe_ptr)->user_data); ctx->cached_sq_head++; return true; } @@ -3764,6 +3772,7 @@ static int io_submit_sqes(struct io_ring_ctx *ctx, unsigned int nr, } for (i = 0; i < nr; i++) { + const struct io_uring_sqe *sqe; struct io_kiocb *req; unsigned int sqe_flags; @@ -3773,7 +3782,7 @@ static int io_submit_sqes(struct io_ring_ctx *ctx, unsigned int nr, submitted = -EAGAIN; break; } - if (!io_get_sqring(ctx, req)) { + if (!io_get_sqring(ctx, req, &sqe)) { __io_free_req(req); break; } @@ -3787,7 +3796,7 @@ static int io_submit_sqes(struct io_ring_ctx *ctx, unsigned int nr, } submitted++; - sqe_flags = req->sqe->flags; + sqe_flags = sqe->flags; req->ring_file = ring_file; req->ring_fd = ring_fd; @@ -3795,7 +3804,7 @@ static int io_submit_sqes(struct io_ring_ctx *ctx, unsigned int nr, req->in_async = async; req->needs_fixed_file = async; trace_io_uring_submit_sqe(ctx, req->user_data, true, async); - if (!io_submit_sqe(req, statep, &link)) + if (!io_submit_sqe(req, sqe, statep, &link)) break; /* * If previous wasn't linked and we have a linked command, -- GitLab From aa638cfe3e7358122a15cb1d295b622aae69e006 Mon Sep 17 00:00:00 2001 From: Wei Li Date: Fri, 20 Dec 2019 17:17:10 +0800 Subject: [PATCH 1174/1437] arm64: cpu_errata: Add Hisilicon TSV110 to spectre-v2 safe list HiSilicon Taishan v110 CPUs didn't implement CSV2 field of the ID_AA64PFR0_EL1, but spectre-v2 is mitigated by hardware, so whitelist the MIDR in the safe list. Signed-off-by: Wei Li [hanjun: re-write the commit log] Signed-off-by: Hanjun Guo Signed-off-by: Catalin Marinas --- arch/arm64/kernel/cpu_errata.c | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm64/kernel/cpu_errata.c b/arch/arm64/kernel/cpu_errata.c index 6a09ca7644ea1..85f4bec22f6d4 100644 --- a/arch/arm64/kernel/cpu_errata.c +++ b/arch/arm64/kernel/cpu_errata.c @@ -547,6 +547,7 @@ static const struct midr_range spectre_v2_safe_list[] = { MIDR_ALL_VERSIONS(MIDR_CORTEX_A53), MIDR_ALL_VERSIONS(MIDR_CORTEX_A55), MIDR_ALL_VERSIONS(MIDR_BRAHMA_B53), + MIDR_ALL_VERSIONS(MIDR_HISI_TSV110), { /* sentinel */ } }; -- GitLab From 36257d5580419b28bd42de47d20400b04fc1dd2c Mon Sep 17 00:00:00 2001 From: Helge Deller Date: Fri, 20 Dec 2019 19:46:17 +0100 Subject: [PATCH 1175/1437] parisc: soft_offline_page() now takes the pfn Switch page deallocation table (pdt) driver to use pfn instead of a page pointer in soft_offline_page(). Fixes: feec24a6139d ("mm, soft-offline: convert parameter to pfn") Signed-off-by: Helge Deller --- arch/parisc/kernel/pdt.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/arch/parisc/kernel/pdt.c b/arch/parisc/kernel/pdt.c index 36434d4da381a..749c4579db0d5 100644 --- a/arch/parisc/kernel/pdt.c +++ b/arch/parisc/kernel/pdt.c @@ -327,8 +327,7 @@ static int pdt_mainloop(void *unused) ((pde & PDT_ADDR_SINGLE_ERR) == 0)) memory_failure(pde >> PAGE_SHIFT, 0); else - soft_offline_page( - pfn_to_page(pde >> PAGE_SHIFT), 0); + soft_offline_page(pde >> PAGE_SHIFT, 0); #else pr_crit("PDT: memory error at 0x%lx ignored.\n" "Rebuild kernel with CONFIG_MEMORY_FAILURE=y " -- GitLab From c44a4edb20938c85b64a256661443039f5bffdea Mon Sep 17 00:00:00 2001 From: Bart Van Assche Date: Tue, 17 Dec 2019 16:23:29 -0800 Subject: [PATCH 1176/1437] block: Fix the type of 'sts' in bsg_queue_rq() This patch fixes the following sparse warnings: block/bsg-lib.c:269:19: warning: incorrect type in initializer (different base types) block/bsg-lib.c:269:19: expected int sts block/bsg-lib.c:269:19: got restricted blk_status_t [usertype] block/bsg-lib.c:286:16: warning: incorrect type in return expression (different base types) block/bsg-lib.c:286:16: expected restricted blk_status_t block/bsg-lib.c:286:16: got int [assigned] sts Cc: Martin Wilck Fixes: d46fe2cb2dce ("block: drop device references in bsg_queue_rq()") Signed-off-by: Bart Van Assche Signed-off-by: Jens Axboe --- block/bsg-lib.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/block/bsg-lib.c b/block/bsg-lib.c index 347dda16c2f46..6cbb7926534cd 100644 --- a/block/bsg-lib.c +++ b/block/bsg-lib.c @@ -266,7 +266,7 @@ static blk_status_t bsg_queue_rq(struct blk_mq_hw_ctx *hctx, struct request *req = bd->rq; struct bsg_set *bset = container_of(q->tag_set, struct bsg_set, tag_set); - int sts = BLK_STS_IOERR; + blk_status_t sts = BLK_STS_IOERR; int ret; blk_mq_start_request(req); -- GitLab From b3c6a59975415bde29cfd76ff1ab008edbf614a9 Mon Sep 17 00:00:00 2001 From: Bart Van Assche Date: Tue, 17 Dec 2019 16:24:35 -0800 Subject: [PATCH 1177/1437] block: Fix a lockdep complaint triggered by request queue flushing Avoid that running test nvme/012 from the blktests suite triggers the following false positive lockdep complaint: ============================================ WARNING: possible recursive locking detected 5.0.0-rc3-xfstests-00015-g1236f7d60242 #841 Not tainted -------------------------------------------- ksoftirqd/1/16 is trying to acquire lock: 000000000282032e (&(&fq->mq_flush_lock)->rlock){..-.}, at: flush_end_io+0x4e/0x1d0 but task is already holding lock: 00000000cbadcbc2 (&(&fq->mq_flush_lock)->rlock){..-.}, at: flush_end_io+0x4e/0x1d0 other info that might help us debug this: Possible unsafe locking scenario: CPU0 ---- lock(&(&fq->mq_flush_lock)->rlock); lock(&(&fq->mq_flush_lock)->rlock); *** DEADLOCK *** May be due to missing lock nesting notation 1 lock held by ksoftirqd/1/16: #0: 00000000cbadcbc2 (&(&fq->mq_flush_lock)->rlock){..-.}, at: flush_end_io+0x4e/0x1d0 stack backtrace: CPU: 1 PID: 16 Comm: ksoftirqd/1 Not tainted 5.0.0-rc3-xfstests-00015-g1236f7d60242 #841 Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 01/01/2011 Call Trace: dump_stack+0x67/0x90 __lock_acquire.cold.45+0x2b4/0x313 lock_acquire+0x98/0x160 _raw_spin_lock_irqsave+0x3b/0x80 flush_end_io+0x4e/0x1d0 blk_mq_complete_request+0x76/0x110 nvmet_req_complete+0x15/0x110 [nvmet] nvmet_bio_done+0x27/0x50 [nvmet] blk_update_request+0xd7/0x2d0 blk_mq_end_request+0x1a/0x100 blk_flush_complete_seq+0xe5/0x350 flush_end_io+0x12f/0x1d0 blk_done_softirq+0x9f/0xd0 __do_softirq+0xca/0x440 run_ksoftirqd+0x24/0x50 smpboot_thread_fn+0x113/0x1e0 kthread+0x121/0x140 ret_from_fork+0x3a/0x50 Cc: Christoph Hellwig Cc: Ming Lei Cc: Hannes Reinecke Signed-off-by: Bart Van Assche Signed-off-by: Jens Axboe --- block/blk-flush.c | 5 +++++ block/blk.h | 1 + 2 files changed, 6 insertions(+) diff --git a/block/blk-flush.c b/block/blk-flush.c index 1777346baf06f..3f977c517960e 100644 --- a/block/blk-flush.c +++ b/block/blk-flush.c @@ -69,6 +69,7 @@ #include #include #include +#include #include "blk.h" #include "blk-mq.h" @@ -505,6 +506,9 @@ struct blk_flush_queue *blk_alloc_flush_queue(struct request_queue *q, INIT_LIST_HEAD(&fq->flush_queue[1]); INIT_LIST_HEAD(&fq->flush_data_in_flight); + lockdep_register_key(&fq->key); + lockdep_set_class(&fq->mq_flush_lock, &fq->key); + return fq; fail_rq: @@ -519,6 +523,7 @@ void blk_free_flush_queue(struct blk_flush_queue *fq) if (!fq) return; + lockdep_unregister_key(&fq->key); kfree(fq->flush_rq); kfree(fq); } diff --git a/block/blk.h b/block/blk.h index 6842f28c033e7..0b8884353f6bf 100644 --- a/block/blk.h +++ b/block/blk.h @@ -30,6 +30,7 @@ struct blk_flush_queue { * at the same time */ struct request *orig_rq; + struct lock_class_key key; spinlock_t mq_flush_lock; }; -- GitLab From dd4b3c83b9efac10d48a94c61372119fc555a077 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20H=C3=B6ppner?= Date: Thu, 19 Dec 2019 09:43:50 +0100 Subject: [PATCH 1178/1437] s390/dasd/cio: Interpret ccw_device_get_mdc return value correctly MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The max data count (mdc) is an unsigned 16-bit integer value as per AR documentation and is received via ccw_device_get_mdc() for a specific path mask from the CIO layer. The function itself also always returns a positive mdc value or 0 in case mdc isn't supported or couldn't be determined. Though, the comment for this function describes a negative return value to indicate failures. As a result, the DASD device driver interprets the return value of ccw_device_get_mdc() incorrectly. The error case is essentially a dead code path. To fix this behaviour, check explicitly for a return value of 0 and change the comment for ccw_device_get_mdc() accordingly. This fix merely enables the error code path in the DASD functions get_fcx_max_data() and verify_fcx_max_data(). The actual functionality stays the same and is still correct. Reviewed-by: Cornelia Huck Signed-off-by: Jan Höppner Acked-by: Peter Oberparleiter Reviewed-by: Stefan Haberland Signed-off-by: Stefan Haberland Signed-off-by: Jens Axboe --- drivers/s390/block/dasd_eckd.c | 9 +++++---- drivers/s390/cio/device_ops.c | 2 +- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/drivers/s390/block/dasd_eckd.c b/drivers/s390/block/dasd_eckd.c index c94184d080f84..f5622f4a2ecfc 100644 --- a/drivers/s390/block/dasd_eckd.c +++ b/drivers/s390/block/dasd_eckd.c @@ -1128,7 +1128,8 @@ static u32 get_fcx_max_data(struct dasd_device *device) { struct dasd_eckd_private *private = device->private; int fcx_in_css, fcx_in_gneq, fcx_in_features; - int tpm, mdc; + unsigned int mdc; + int tpm; if (dasd_nofcx) return 0; @@ -1142,7 +1143,7 @@ static u32 get_fcx_max_data(struct dasd_device *device) return 0; mdc = ccw_device_get_mdc(device->cdev, 0); - if (mdc < 0) { + if (mdc == 0) { dev_warn(&device->cdev->dev, "Detecting the maximum supported data size for zHPF requests failed\n"); return 0; } else { @@ -1153,12 +1154,12 @@ static u32 get_fcx_max_data(struct dasd_device *device) static int verify_fcx_max_data(struct dasd_device *device, __u8 lpm) { struct dasd_eckd_private *private = device->private; - int mdc; + unsigned int mdc; u32 fcx_max_data; if (private->fcx_max_data) { mdc = ccw_device_get_mdc(device->cdev, lpm); - if ((mdc < 0)) { + if (mdc == 0) { dev_warn(&device->cdev->dev, "Detecting the maximum data size for zHPF " "requests failed (rc=%d) for a new path %x\n", diff --git a/drivers/s390/cio/device_ops.c b/drivers/s390/cio/device_ops.c index 65841af157480..ccecf6b9504e8 100644 --- a/drivers/s390/cio/device_ops.c +++ b/drivers/s390/cio/device_ops.c @@ -635,7 +635,7 @@ EXPORT_SYMBOL(ccw_device_tm_start_timeout); * @mask: mask of paths to use * * Return the number of 64K-bytes blocks all paths at least support - * for a transport command. Return values <= 0 indicate failures. + * for a transport command. Return value 0 indicates failure. */ int ccw_device_get_mdc(struct ccw_device *cdev, u8 mask) { -- GitLab From 00b39f698a4f1ee897227cace2e3937fc4412270 Mon Sep 17 00:00:00 2001 From: Stefan Haberland Date: Thu, 19 Dec 2019 09:43:51 +0100 Subject: [PATCH 1179/1437] s390/dasd: fix memleak in path handling error case If for whatever reason the dasd_eckd_check_characteristics() function exits after at least some paths have their configuration data allocated those data is never freed again. In the error case the device->private pointer is set to NULL and dasd_eckd_uncheck_device() will exit without freeing the path data because of this NULL pointer. Fix by calling dasd_eckd_clear_conf_data() for error cases. Also use dasd_eckd_clear_conf_data() in dasd_eckd_uncheck_device() to avoid code duplication. Reported-by: Qian Cai Reviewed-by: Jan Hoeppner Signed-off-by: Stefan Haberland Signed-off-by: Jens Axboe --- drivers/s390/block/dasd_eckd.c | 19 ++----------------- 1 file changed, 2 insertions(+), 17 deletions(-) diff --git a/drivers/s390/block/dasd_eckd.c b/drivers/s390/block/dasd_eckd.c index f5622f4a2ecfc..a28b9ff823780 100644 --- a/drivers/s390/block/dasd_eckd.c +++ b/drivers/s390/block/dasd_eckd.c @@ -2074,7 +2074,7 @@ out_err2: dasd_free_block(device->block); device->block = NULL; out_err1: - kfree(private->conf_data); + dasd_eckd_clear_conf_data(device); kfree(device->private); device->private = NULL; return rc; @@ -2083,7 +2083,6 @@ out_err1: static void dasd_eckd_uncheck_device(struct dasd_device *device) { struct dasd_eckd_private *private = device->private; - int i; if (!private) return; @@ -2093,21 +2092,7 @@ static void dasd_eckd_uncheck_device(struct dasd_device *device) private->sneq = NULL; private->vdsneq = NULL; private->gneq = NULL; - private->conf_len = 0; - for (i = 0; i < 8; i++) { - kfree(device->path[i].conf_data); - if ((__u8 *)device->path[i].conf_data == - private->conf_data) { - private->conf_data = NULL; - private->conf_len = 0; - } - device->path[i].conf_data = NULL; - device->path[i].cssid = 0; - device->path[i].ssid = 0; - device->path[i].chpid = 0; - } - kfree(private->conf_data); - private->conf_data = NULL; + dasd_eckd_clear_conf_data(device); } static struct dasd_ccw_req * -- GitLab From daa400f5a1e581acc1f9a97100574e82a4590e87 Mon Sep 17 00:00:00 2001 From: Stefan Haberland Date: Thu, 19 Dec 2019 09:43:52 +0100 Subject: [PATCH 1180/1437] s390/dasd: fix typo in copyright statement coypright -> copyright Reported-by: Kate Stewart Signed-off-by: Stefan Haberland Signed-off-by: Jens Axboe --- drivers/s390/block/dasd_fba.h | 2 +- drivers/s390/block/dasd_proc.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/s390/block/dasd_fba.h b/drivers/s390/block/dasd_fba.h index 8f75df06e893c..45ddabec40176 100644 --- a/drivers/s390/block/dasd_fba.h +++ b/drivers/s390/block/dasd_fba.h @@ -2,7 +2,7 @@ /* * Author(s)......: Holger Smolinski * Bugreports.to..: - * Coypright IBM Corp. 1999, 2000 + * Copyright IBM Corp. 1999, 2000 * */ diff --git a/drivers/s390/block/dasd_proc.c b/drivers/s390/block/dasd_proc.c index 1770b99f607e6..8d4d69ea5bafd 100644 --- a/drivers/s390/block/dasd_proc.c +++ b/drivers/s390/block/dasd_proc.c @@ -5,7 +5,7 @@ * Carsten Otte * Martin Schwidefsky * Bugreports.to..: - * Coypright IBM Corp. 1999, 2002 + * Copyright IBM Corp. 1999, 2002 * * /proc interface for the dasd driver. * -- GitLab From 3b7995a98ad76da5597b488fa84aa5a56d43b608 Mon Sep 17 00:00:00 2001 From: Yang Yingliang Date: Wed, 18 Dec 2019 16:44:04 +0800 Subject: [PATCH 1181/1437] block: fix memleak when __blk_rq_map_user_iov() is failed When I doing fuzzy test, get the memleak report: BUG: memory leak unreferenced object 0xffff88837af80000 (size 4096): comm "memleak", pid 3557, jiffies 4294817681 (age 112.499s) hex dump (first 32 bytes): 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 20 00 00 00 10 01 00 00 00 00 00 00 01 00 00 00 ............... backtrace: [<000000001c894df8>] bio_alloc_bioset+0x393/0x590 [<000000008b139a3c>] bio_copy_user_iov+0x300/0xcd0 [<00000000a998bd8c>] blk_rq_map_user_iov+0x2f1/0x5f0 [<000000005ceb7f05>] blk_rq_map_user+0xf2/0x160 [<000000006454da92>] sg_common_write.isra.21+0x1094/0x1870 [<00000000064bb208>] sg_write.part.25+0x5d9/0x950 [<000000004fc670f6>] sg_write+0x5f/0x8c [<00000000b0d05c7b>] __vfs_write+0x7c/0x100 [<000000008e177714>] vfs_write+0x1c3/0x500 [<0000000087d23f34>] ksys_write+0xf9/0x200 [<000000002c8dbc9d>] do_syscall_64+0x9f/0x4f0 [<00000000678d8e9a>] entry_SYSCALL_64_after_hwframe+0x49/0xbe If __blk_rq_map_user_iov() is failed in blk_rq_map_user_iov(), the bio(s) which is allocated before this failing will leak. The refcount of the bio(s) is init to 1 and increased to 2 by calling bio_get(), but __blk_rq_unmap_user() only decrease it to 1, so the bio cannot be freed. Fix it by calling blk_rq_unmap_user(). Reviewed-by: Bob Liu Reported-by: Hulk Robot Signed-off-by: Yang Yingliang Signed-off-by: Jens Axboe --- block/blk-map.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/block/blk-map.c b/block/blk-map.c index 3a62e471d81bd..b0790268ed9d9 100644 --- a/block/blk-map.c +++ b/block/blk-map.c @@ -151,7 +151,7 @@ int blk_rq_map_user_iov(struct request_queue *q, struct request *rq, return 0; unmap_rq: - __blk_rq_unmap_user(bio); + blk_rq_unmap_user(bio); fail: rq->bio = NULL; return ret; -- GitLab From 75cf9797006a3a9f29a3a25c1febd6842a4a9eb2 Mon Sep 17 00:00:00 2001 From: Helge Deller Date: Fri, 20 Dec 2019 21:00:19 +0100 Subject: [PATCH 1182/1437] parisc: Fix compiler warnings in debug_core.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix this compiler warning: kernel/debug/debug_core.c: In function ‘kgdb_cpu_enter’: arch/parisc/include/asm/cmpxchg.h:48:3: warning: value computed is not used [-Wunused-value] 48 | ((__typeof__(*(ptr)))__xchg((unsigned long)(x), (ptr), sizeof(*(ptr)))) arch/parisc/include/asm/atomic.h:78:30: note: in expansion of macro ‘xchg’ 78 | #define atomic_xchg(v, new) (xchg(&((v)->counter), new)) | ^~~~ kernel/debug/debug_core.c:596:4: note: in expansion of macro ‘atomic_xchg’ 596 | atomic_xchg(&kgdb_active, cpu); | ^~~~~~~~~~~ Signed-off-by: Helge Deller --- arch/parisc/include/asm/cmpxchg.h | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/arch/parisc/include/asm/cmpxchg.h b/arch/parisc/include/asm/cmpxchg.h index f627c37dad9c9..ab5c215cf46c3 100644 --- a/arch/parisc/include/asm/cmpxchg.h +++ b/arch/parisc/include/asm/cmpxchg.h @@ -44,8 +44,14 @@ __xchg(unsigned long x, __volatile__ void *ptr, int size) ** if (((unsigned long)p & 0xf) == 0) ** return __ldcw(p); */ -#define xchg(ptr, x) \ - ((__typeof__(*(ptr)))__xchg((unsigned long)(x), (ptr), sizeof(*(ptr)))) +#define xchg(ptr, x) \ +({ \ + __typeof__(*(ptr)) __ret; \ + __typeof__(*(ptr)) _x_ = (x); \ + __ret = (__typeof__(*(ptr))) \ + __xchg((unsigned long)_x_, (ptr), sizeof(*(ptr))); \ + __ret; \ +}) /* bug catcher for when unsupported size is used - won't link */ extern void __cmpxchg_called_with_bad_pointer(void); -- GitLab From ea8608d401def015e83c2c32ed6c32996abb3ed2 Mon Sep 17 00:00:00 2001 From: Rahul Lakkireddy Date: Wed, 18 Dec 2019 09:19:29 +0530 Subject: [PATCH 1183/1437] cxgb4: fix refcount init for TC-MQPRIO offload Properly initialize refcount to 1 when hardware queue arrays for TC-MQPRIO offload have been freshly allocated. Otherwise, following warning is observed. Also fix up error path to only free hardware queue arrays when refcount reaches 0. [ 130.075342] ------------[ cut here ]------------ [ 130.075343] refcount_t: addition on 0; use-after-free. [ 130.075355] WARNING: CPU: 0 PID: 10870 at lib/refcount.c:25 refcount_warn_saturate+0xe1/0x100 [ 130.075356] Modules linked in: sch_mqprio iptable_nat ib_iser libiscsi scsi_transport_iscsi ib_ipoib rdma_ucm ib_umad iw_cxgb4 libcxgb ib_uverbs x86_pkg_temp_thermal cxgb4 igb [ 130.075361] CPU: 0 PID: 10870 Comm: tc Kdump: loaded Not tainted 5.5.0-rc1+ #11 [ 130.075362] Hardware name: Supermicro X9SRE/X9SRE-3F/X9SRi/X9SRi-3F/X9SRE/X9SRE-3F/X9SRi/X9SRi-3F, BIOS 3.2 01/16/2015 [ 130.075363] RIP: 0010:refcount_warn_saturate+0xe1/0x100 [ 130.075364] Code: e8 14 41 c1 ff 0f 0b c3 80 3d 44 f4 10 01 00 0f 85 63 ff ff ff 48 c7 c7 38 9f 83 8c 31 c0 c6 05 2e f4 10 01 01 e8 ef 40 c1 ff <0f> 0b c3 48 c7 c7 10 9f 83 8c 31 c0 c6 05 17 f4 10 01 01 e8 d7 40 [ 130.075365] RSP: 0018:ffffa48d00c0b768 EFLAGS: 00010286 [ 130.075366] RAX: 0000000000000000 RBX: 0000000000000008 RCX: 0000000000000001 [ 130.075366] RDX: 0000000000000001 RSI: 0000000000000096 RDI: ffff8a2e9fa187d0 [ 130.075367] RBP: ffff8a2e93890000 R08: 0000000000000398 R09: 000000000000003c [ 130.075367] R10: 00000000000142a0 R11: 0000000000000397 R12: ffffa48d00c0b848 [ 130.075368] R13: ffff8a2e94746498 R14: ffff8a2e966f7000 R15: 0000000000000031 [ 130.075368] FS: 00007f689015f840(0000) GS:ffff8a2e9fa00000(0000) knlGS:0000000000000000 [ 130.075369] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 [ 130.075369] CR2: 00000000006762a0 CR3: 00000007cf164005 CR4: 00000000001606f0 [ 130.075370] Call Trace: [ 130.075377] cxgb4_setup_tc_mqprio+0xbee/0xc30 [cxgb4] [ 130.075382] ? cxgb4_ethofld_restart+0x50/0x50 [cxgb4] [ 130.075384] ? pfifo_fast_init+0x7e/0xf0 [ 130.075386] mqprio_init+0x5f4/0x630 [sch_mqprio] [ 130.075389] qdisc_create+0x1bf/0x4a0 [ 130.075390] tc_modify_qdisc+0x1ff/0x770 [ 130.075392] rtnetlink_rcv_msg+0x28b/0x350 [ 130.075394] ? rtnl_calcit.isra.32+0x110/0x110 [ 130.075395] netlink_rcv_skb+0xc6/0x100 [ 130.075396] netlink_unicast+0x1db/0x330 [ 130.075397] netlink_sendmsg+0x2f5/0x460 [ 130.075399] ? _copy_from_user+0x2e/0x60 [ 130.075400] sock_sendmsg+0x59/0x70 [ 130.075401] ____sys_sendmsg+0x1f0/0x230 [ 130.075402] ? copy_msghdr_from_user+0xd7/0x140 [ 130.075403] ___sys_sendmsg+0x77/0xb0 [ 130.075404] ? ___sys_recvmsg+0x84/0xb0 [ 130.075406] ? __handle_mm_fault+0x377/0xaf0 [ 130.075407] __sys_sendmsg+0x53/0xa0 [ 130.075409] do_syscall_64+0x44/0x130 [ 130.075412] entry_SYSCALL_64_after_hwframe+0x44/0xa9 [ 130.075413] RIP: 0033:0x7f688f13af10 [ 130.075414] Code: c3 48 8b 05 82 6f 2c 00 f7 db 64 89 18 48 83 cb ff eb dd 0f 1f 80 00 00 00 00 83 3d 8d d0 2c 00 00 75 10 b8 2e 00 00 00 0f 05 <48> 3d 01 f0 ff ff 73 31 c3 48 83 ec 08 e8 ae cc 00 00 48 89 04 24 [ 130.075414] RSP: 002b:00007ffe6c7d9988 EFLAGS: 00000246 ORIG_RAX: 000000000000002e [ 130.075415] RAX: ffffffffffffffda RBX: 00000000006703a0 RCX: 00007f688f13af10 [ 130.075415] RDX: 0000000000000000 RSI: 00007ffe6c7d99f0 RDI: 0000000000000003 [ 130.075416] RBP: 000000005df38312 R08: 0000000000000002 R09: 0000000000008000 [ 130.075416] R10: 00007ffe6c7d93e0 R11: 0000000000000246 R12: 0000000000000000 [ 130.075417] R13: 00007ffe6c7e9c50 R14: 0000000000000001 R15: 000000000067c600 [ 130.075418] ---[ end trace 8fbb3bf36a8671db ]--- v2: - Move the refcount_set() closer to where the hardware queue arrays are being allocated. - Fix up error path to only free hardware queue arrays when refcount reaches 0. Fixes: 2d0cb84dd973 ("cxgb4: add ETHOFLD hardware queue support") Signed-off-by: Rahul Lakkireddy Signed-off-by: David S. Miller --- drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_mqprio.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_mqprio.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_mqprio.c index 477973d2e3412..8971dddcdb7a8 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_mqprio.c +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_mqprio.c @@ -145,6 +145,10 @@ static int cxgb4_mqprio_alloc_hw_resources(struct net_device *dev) kfree(adap->sge.eohw_rxq); return -ENOMEM; } + + refcount_set(&adap->tc_mqprio->refcnt, 1); + } else { + refcount_inc(&adap->tc_mqprio->refcnt); } if (!(adap->flags & CXGB4_USING_MSIX)) @@ -205,7 +209,6 @@ static int cxgb4_mqprio_alloc_hw_resources(struct net_device *dev) cxgb4_enable_rx(adap, &eorxq->rspq); } - refcount_inc(&adap->tc_mqprio->refcnt); return 0; out_free_msix: @@ -234,9 +237,10 @@ out_free_queues: t4_sge_free_ethofld_txq(adap, eotxq); } - kfree(adap->sge.eohw_txq); - kfree(adap->sge.eohw_rxq); - + if (refcount_dec_and_test(&adap->tc_mqprio->refcnt)) { + kfree(adap->sge.eohw_txq); + kfree(adap->sge.eohw_rxq); + } return ret; } -- GitLab From df034c93f15ee71df231ff9fe311d27ff08a2a52 Mon Sep 17 00:00:00 2001 From: David Jeffery Date: Tue, 17 Dec 2019 11:00:24 -0500 Subject: [PATCH 1184/1437] sbitmap: only queue kyber's wait callback if not already active Under heavy loads where the kyber I/O scheduler hits the token limits for its scheduling domains, kyber can become stuck. When active requests complete, kyber may not be woken up leaving the I/O requests in kyber stuck. This stuck state is due to a race condition with kyber and the sbitmap functions it uses to run a callback when enough requests have completed. The running of a sbt_wait callback can race with the attempt to insert the sbt_wait. Since sbitmap_del_wait_queue removes the sbt_wait from the list first then sets the sbq field to NULL, kyber can see the item as not on a list but the call to sbitmap_add_wait_queue will see sbq as non-NULL. This results in the sbt_wait being inserted onto the wait list but ws_active doesn't get incremented. So the sbitmap queue does not know there is a waiter on a wait list. Since sbitmap doesn't think there is a waiter, kyber may never be informed that there are domain tokens available and the I/O never advances. With the sbt_wait on a wait list, kyber believes it has an active waiter so cannot insert a new waiter when reaching the domain's full state. This race can be fixed by only adding the sbt_wait to the queue if the sbq field is NULL. If sbq is not NULL, there is already an action active which will trigger the re-running of kyber. Let it run and add the sbt_wait to the wait list if still needing to wait. Reviewed-by: Omar Sandoval Signed-off-by: David Jeffery Reported-by: John Pittman Tested-by: John Pittman Signed-off-by: Jens Axboe --- lib/sbitmap.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/sbitmap.c b/lib/sbitmap.c index 33feec8989f14..af88d1346dd74 100644 --- a/lib/sbitmap.c +++ b/lib/sbitmap.c @@ -650,8 +650,8 @@ void sbitmap_add_wait_queue(struct sbitmap_queue *sbq, if (!sbq_wait->sbq) { sbq_wait->sbq = sbq; atomic_inc(&sbq->ws_active); + add_wait_queue(&ws->wait, &sbq_wait->wait); } - add_wait_queue(&ws->wait, &sbq_wait->wait); } EXPORT_SYMBOL_GPL(sbitmap_add_wait_queue); -- GitLab From 484b165306e18fd9a0d960e6ed2d6eff31665522 Mon Sep 17 00:00:00 2001 From: Magnus Karlsson Date: Thu, 19 Dec 2019 13:39:20 +0100 Subject: [PATCH 1185/1437] xsk: Eliminate the lazy update threshold The lazy update threshold was introduced to keep the producer and consumer some distance apart in the completion ring. This was important in the beginning of the development of AF_XDP as the ring format as that point in time was very sensitive to the producer and consumer being on the same cache line. This is not the case anymore as the current ring format does not degrade in any noticeable way when this happens. Moreover, this threshold makes it impossible to run the system with rings that have less than 128 entries. So let us remove this threshold and just get one entry from the ring as in all other functions. This will enable us to remove this function in a later commit. Note that xskq_produce_addr_lazy followed by xskq_produce_flush_addr_n are still not the same function as xskq_produce_addr() as it operates on another cached pointer. Signed-off-by: Magnus Karlsson Signed-off-by: Alexei Starovoitov Link: https://lore.kernel.org/bpf/1576759171-28550-2-git-send-email-magnus.karlsson@intel.com --- net/xdp/xsk_queue.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/net/xdp/xsk_queue.h b/net/xdp/xsk_queue.h index eddae46888629..a2f0ba65559f2 100644 --- a/net/xdp/xsk_queue.h +++ b/net/xdp/xsk_queue.h @@ -11,7 +11,6 @@ #include #define RX_BATCH_SIZE 16 -#define LAZY_UPDATE_THRESHOLD 128 struct xdp_ring { u32 producer ____cacheline_aligned_in_smp; @@ -239,7 +238,7 @@ static inline int xskq_produce_addr_lazy(struct xsk_queue *q, u64 addr) { struct xdp_umem_ring *ring = (struct xdp_umem_ring *)q->ring; - if (xskq_nb_free(q, q->prod_head, LAZY_UPDATE_THRESHOLD) == 0) + if (xskq_nb_free(q, q->prod_head, 1) == 0) return -ENOSPC; /* A, matches D */ -- GitLab From 11cc2d21499cabe7e7964389634ed1de3ee91d33 Mon Sep 17 00:00:00 2001 From: Magnus Karlsson Date: Thu, 19 Dec 2019 13:39:21 +0100 Subject: [PATCH 1186/1437] xsk: Simplify detection of empty and full rings In order to set the correct return flags for poll, the xsk code has to check if the Rx queue is empty and if the Tx queue is full. This code was unnecessarily large and complex as it used the functions that are used to update the local state from the global state (xskq_nb_free and xskq_nb_avail). Since we are not doing this nor updating any data dependent on this state, we can simplify the functions. Another benefit from this is that we can also simplify the xskq_nb_free and xskq_nb_avail functions in a later commit. Signed-off-by: Magnus Karlsson Signed-off-by: Alexei Starovoitov Link: https://lore.kernel.org/bpf/1576759171-28550-3-git-send-email-magnus.karlsson@intel.com --- net/xdp/xsk_queue.h | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/net/xdp/xsk_queue.h b/net/xdp/xsk_queue.h index a2f0ba65559f2..b28f9da7b317a 100644 --- a/net/xdp/xsk_queue.h +++ b/net/xdp/xsk_queue.h @@ -362,12 +362,15 @@ static inline void xskq_produce_flush_desc(struct xsk_queue *q) static inline bool xskq_full_desc(struct xsk_queue *q) { - return xskq_nb_avail(q, q->nentries) == q->nentries; + /* No barriers needed since data is not accessed */ + return READ_ONCE(q->ring->producer) - READ_ONCE(q->ring->consumer) == + q->nentries; } static inline bool xskq_empty_desc(struct xsk_queue *q) { - return xskq_nb_free(q, q->prod_tail, q->nentries) == q->nentries; + /* No barriers needed since data is not accessed */ + return READ_ONCE(q->ring->consumer) == READ_ONCE(q->ring->producer); } void xskq_set_umem(struct xsk_queue *q, u64 size, u64 chunk_mask); -- GitLab From d7012f05e3ca5aba92770c370895092d4882e8c2 Mon Sep 17 00:00:00 2001 From: Magnus Karlsson Date: Thu, 19 Dec 2019 13:39:22 +0100 Subject: [PATCH 1187/1437] xsk: Consolidate to one single cached producer pointer Currently, the xsk ring code has two cached producer pointers: prod_head and prod_tail. This patch consolidates these two into a single one called cached_prod to make the code simpler and easier to maintain. This will be in line with the user space part of the the code found in libbpf, that only uses a single cached pointer. The Rx path only uses the two top level functions xskq_produce_batch_desc and xskq_produce_flush_desc and they both use prod_head and never prod_tail. So just move them over to cached_prod. The Tx XDP_DRV path uses xskq_produce_addr_lazy and xskq_produce_flush_addr_n and unnecessarily operates on both prod_tail and prod_head, so move them over to just use cached_prod by skipping the intermediate step of updating prod_tail. The Tx path in XDP_SKB mode uses xskq_reserve_addr and xskq_produce_addr. They currently use both cached pointers, but we can operate on the global producer pointer in xskq_produce_addr since it has to be updated anyway, thus eliminating the use of both cached pointers. We can also remove the xskq_nb_free in xskq_produce_addr since it is already called in xskq_reserve_addr. No need to do it twice. When there is only one cached producer pointer, we can also simplify xskq_nb_free by removing one argument. Signed-off-by: Magnus Karlsson Signed-off-by: Alexei Starovoitov Link: https://lore.kernel.org/bpf/1576759171-28550-4-git-send-email-magnus.karlsson@intel.com --- net/xdp/xsk_queue.h | 47 ++++++++++++++++++++------------------------- 1 file changed, 21 insertions(+), 26 deletions(-) diff --git a/net/xdp/xsk_queue.h b/net/xdp/xsk_queue.h index b28f9da7b317a..7ad80748f209b 100644 --- a/net/xdp/xsk_queue.h +++ b/net/xdp/xsk_queue.h @@ -35,8 +35,7 @@ struct xsk_queue { u64 size; u32 ring_mask; u32 nentries; - u32 prod_head; - u32 prod_tail; + u32 cached_prod; u32 cons_head; u32 cons_tail; struct xdp_ring *ring; @@ -94,39 +93,39 @@ static inline u64 xskq_nb_invalid_descs(struct xsk_queue *q) static inline u32 xskq_nb_avail(struct xsk_queue *q, u32 dcnt) { - u32 entries = q->prod_tail - q->cons_tail; + u32 entries = q->cached_prod - q->cons_tail; if (entries == 0) { /* Refresh the local pointer */ - q->prod_tail = READ_ONCE(q->ring->producer); - entries = q->prod_tail - q->cons_tail; + q->cached_prod = READ_ONCE(q->ring->producer); + entries = q->cached_prod - q->cons_tail; } return (entries > dcnt) ? dcnt : entries; } -static inline u32 xskq_nb_free(struct xsk_queue *q, u32 producer, u32 dcnt) +static inline u32 xskq_nb_free(struct xsk_queue *q, u32 dcnt) { - u32 free_entries = q->nentries - (producer - q->cons_tail); + u32 free_entries = q->nentries - (q->cached_prod - q->cons_tail); if (free_entries >= dcnt) return free_entries; /* Refresh the local tail pointer */ q->cons_tail = READ_ONCE(q->ring->consumer); - return q->nentries - (producer - q->cons_tail); + return q->nentries - (q->cached_prod - q->cons_tail); } static inline bool xskq_has_addrs(struct xsk_queue *q, u32 cnt) { - u32 entries = q->prod_tail - q->cons_tail; + u32 entries = q->cached_prod - q->cons_tail; if (entries >= cnt) return true; /* Refresh the local pointer. */ - q->prod_tail = READ_ONCE(q->ring->producer); - entries = q->prod_tail - q->cons_tail; + q->cached_prod = READ_ONCE(q->ring->producer); + entries = q->cached_prod - q->cons_tail; return entries >= cnt; } @@ -220,17 +219,15 @@ static inline void xskq_discard_addr(struct xsk_queue *q) static inline int xskq_produce_addr(struct xsk_queue *q, u64 addr) { struct xdp_umem_ring *ring = (struct xdp_umem_ring *)q->ring; - - if (xskq_nb_free(q, q->prod_tail, 1) == 0) - return -ENOSPC; + unsigned int idx = q->ring->producer; /* A, matches D */ - ring->desc[q->prod_tail++ & q->ring_mask] = addr; + ring->desc[idx++ & q->ring_mask] = addr; /* Order producer and data */ smp_wmb(); /* B, matches C */ - WRITE_ONCE(q->ring->producer, q->prod_tail); + WRITE_ONCE(q->ring->producer, idx); return 0; } @@ -238,11 +235,11 @@ static inline int xskq_produce_addr_lazy(struct xsk_queue *q, u64 addr) { struct xdp_umem_ring *ring = (struct xdp_umem_ring *)q->ring; - if (xskq_nb_free(q, q->prod_head, 1) == 0) + if (xskq_nb_free(q, 1) == 0) return -ENOSPC; /* A, matches D */ - ring->desc[q->prod_head++ & q->ring_mask] = addr; + ring->desc[q->cached_prod++ & q->ring_mask] = addr; return 0; } @@ -252,17 +249,16 @@ static inline void xskq_produce_flush_addr_n(struct xsk_queue *q, /* Order producer and data */ smp_wmb(); /* B, matches C */ - q->prod_tail += nb_entries; - WRITE_ONCE(q->ring->producer, q->prod_tail); + WRITE_ONCE(q->ring->producer, q->ring->producer + nb_entries); } static inline int xskq_reserve_addr(struct xsk_queue *q) { - if (xskq_nb_free(q, q->prod_head, 1) == 0) + if (xskq_nb_free(q, 1) == 0) return -ENOSPC; /* A, matches D */ - q->prod_head++; + q->cached_prod++; return 0; } @@ -340,11 +336,11 @@ static inline int xskq_produce_batch_desc(struct xsk_queue *q, struct xdp_rxtx_ring *ring = (struct xdp_rxtx_ring *)q->ring; unsigned int idx; - if (xskq_nb_free(q, q->prod_head, 1) == 0) + if (xskq_nb_free(q, 1) == 0) return -ENOSPC; /* A, matches D */ - idx = (q->prod_head++) & q->ring_mask; + idx = q->cached_prod++ & q->ring_mask; ring->desc[idx].addr = addr; ring->desc[idx].len = len; @@ -356,8 +352,7 @@ static inline void xskq_produce_flush_desc(struct xsk_queue *q) /* Order producer and data */ smp_wmb(); /* B, matches C */ - q->prod_tail = q->prod_head; - WRITE_ONCE(q->ring->producer, q->prod_tail); + WRITE_ONCE(q->ring->producer, q->cached_prod); } static inline bool xskq_full_desc(struct xsk_queue *q) -- GitLab From 59e35e552529b858f35b30bc5a803ea532ca17f1 Mon Sep 17 00:00:00 2001 From: Magnus Karlsson Date: Thu, 19 Dec 2019 13:39:23 +0100 Subject: [PATCH 1188/1437] xsk: Standardize naming of producer ring access functions Adopt the naming of the producer ring access functions to have a similar naming convention as the functions in libbpf, but adapted to the kernel. You first reserve a number of entries that you later submit to the global state of the ring. This is much clearer, IMO, than the one that was in the kernel part. Once renamed, we also discover that two functions are actually the same, so remove one of them. Some of the primitive ring submission operations are also the same so break these out into __xskq_prod_submit that the upper level ring access functions can use. Signed-off-by: Magnus Karlsson Signed-off-by: Alexei Starovoitov Link: https://lore.kernel.org/bpf/1576759171-28550-5-git-send-email-magnus.karlsson@intel.com --- net/xdp/xsk.c | 20 ++++++++-------- net/xdp/xsk_queue.h | 58 +++++++++++++++++++++------------------------ 2 files changed, 37 insertions(+), 41 deletions(-) diff --git a/net/xdp/xsk.c b/net/xdp/xsk.c index e45c27f5cfca0..5f2123fe630c9 100644 --- a/net/xdp/xsk.c +++ b/net/xdp/xsk.c @@ -167,7 +167,7 @@ static int __xsk_rcv(struct xdp_sock *xs, struct xdp_buff *xdp, u32 len) offset += metalen; addr = xsk_umem_adjust_offset(xs->umem, addr, offset); - err = xskq_produce_batch_desc(xs->rx, addr, len); + err = xskq_prod_reserve_desc(xs->rx, addr, len); if (!err) { xskq_discard_addr(xs->umem->fq); xdp_return_buff(xdp); @@ -180,7 +180,7 @@ static int __xsk_rcv(struct xdp_sock *xs, struct xdp_buff *xdp, u32 len) static int __xsk_rcv_zc(struct xdp_sock *xs, struct xdp_buff *xdp, u32 len) { - int err = xskq_produce_batch_desc(xs->rx, (u64)xdp->handle, len); + int err = xskq_prod_reserve_desc(xs->rx, xdp->handle, len); if (err) xs->rx_dropped++; @@ -216,7 +216,7 @@ static int xsk_rcv(struct xdp_sock *xs, struct xdp_buff *xdp) static void xsk_flush(struct xdp_sock *xs) { - xskq_produce_flush_desc(xs->rx); + xskq_prod_submit(xs->rx); xs->sk.sk_data_ready(&xs->sk); } @@ -247,12 +247,12 @@ int xsk_generic_rcv(struct xdp_sock *xs, struct xdp_buff *xdp) memcpy(buffer, xdp->data_meta, len + metalen); addr = xsk_umem_adjust_offset(xs->umem, addr, metalen); - err = xskq_produce_batch_desc(xs->rx, addr, len); + err = xskq_prod_reserve_desc(xs->rx, addr, len); if (err) goto out_drop; xskq_discard_addr(xs->umem->fq); - xskq_produce_flush_desc(xs->rx); + xskq_prod_submit(xs->rx); spin_unlock_bh(&xs->rx_lock); @@ -294,7 +294,7 @@ void __xsk_map_flush(void) void xsk_umem_complete_tx(struct xdp_umem *umem, u32 nb_entries) { - xskq_produce_flush_addr_n(umem->cq, nb_entries); + xskq_prod_submit_n(umem->cq, nb_entries); } EXPORT_SYMBOL(xsk_umem_complete_tx); @@ -319,7 +319,7 @@ bool xsk_umem_consume_tx(struct xdp_umem *umem, struct xdp_desc *desc) if (!xskq_peek_desc(xs->tx, desc, umem)) continue; - if (xskq_produce_addr_lazy(umem->cq, desc->addr)) + if (xskq_prod_reserve_addr(umem->cq, desc->addr)) goto out; xskq_discard_desc(xs->tx); @@ -348,7 +348,7 @@ static void xsk_destruct_skb(struct sk_buff *skb) unsigned long flags; spin_lock_irqsave(&xs->tx_completion_lock, flags); - WARN_ON_ONCE(xskq_produce_addr(xs->umem->cq, addr)); + xskq_prod_submit_addr(xs->umem->cq, addr); spin_unlock_irqrestore(&xs->tx_completion_lock, flags); sock_wfree(skb); @@ -389,7 +389,7 @@ static int xsk_generic_xmit(struct sock *sk) addr = desc.addr; buffer = xdp_umem_get_data(xs->umem, addr); err = skb_store_bits(skb, 0, buffer, len); - if (unlikely(err) || xskq_reserve_addr(xs->umem->cq)) { + if (unlikely(err) || xskq_prod_reserve(xs->umem->cq)) { kfree_skb(skb); goto out; } @@ -470,7 +470,7 @@ static __poll_t xsk_poll(struct file *file, struct socket *sock, __xsk_sendmsg(sk); } - if (xs->rx && !xskq_empty_desc(xs->rx)) + if (xs->rx && !xskq_prod_is_empty(xs->rx)) mask |= EPOLLIN | EPOLLRDNORM; if (xs->tx && !xskq_full_desc(xs->tx)) mask |= EPOLLOUT | EPOLLWRNORM; diff --git a/net/xdp/xsk_queue.h b/net/xdp/xsk_queue.h index 7ad80748f209b..1b9a350f2e663 100644 --- a/net/xdp/xsk_queue.h +++ b/net/xdp/xsk_queue.h @@ -216,22 +216,17 @@ static inline void xskq_discard_addr(struct xsk_queue *q) q->cons_tail++; } -static inline int xskq_produce_addr(struct xsk_queue *q, u64 addr) +static inline int xskq_prod_reserve(struct xsk_queue *q) { - struct xdp_umem_ring *ring = (struct xdp_umem_ring *)q->ring; - unsigned int idx = q->ring->producer; + if (xskq_nb_free(q, 1) == 0) + return -ENOSPC; /* A, matches D */ - ring->desc[idx++ & q->ring_mask] = addr; - - /* Order producer and data */ - smp_wmb(); /* B, matches C */ - - WRITE_ONCE(q->ring->producer, idx); + q->cached_prod++; return 0; } -static inline int xskq_produce_addr_lazy(struct xsk_queue *q, u64 addr) +static inline int xskq_prod_reserve_addr(struct xsk_queue *q, u64 addr) { struct xdp_umem_ring *ring = (struct xdp_umem_ring *)q->ring; @@ -243,23 +238,32 @@ static inline int xskq_produce_addr_lazy(struct xsk_queue *q, u64 addr) return 0; } -static inline void xskq_produce_flush_addr_n(struct xsk_queue *q, - u32 nb_entries) +static inline void __xskq_prod_submit(struct xsk_queue *q, u32 idx) { /* Order producer and data */ smp_wmb(); /* B, matches C */ - WRITE_ONCE(q->ring->producer, q->ring->producer + nb_entries); + WRITE_ONCE(q->ring->producer, idx); } -static inline int xskq_reserve_addr(struct xsk_queue *q) +static inline void xskq_prod_submit(struct xsk_queue *q) { - if (xskq_nb_free(q, 1) == 0) - return -ENOSPC; + __xskq_prod_submit(q, q->cached_prod); +} - /* A, matches D */ - q->cached_prod++; - return 0; +static inline void xskq_prod_submit_addr(struct xsk_queue *q, u64 addr) +{ + struct xdp_umem_ring *ring = (struct xdp_umem_ring *)q->ring; + u32 idx = q->ring->producer; + + ring->desc[idx++ & q->ring_mask] = addr; + + __xskq_prod_submit(q, idx); +} + +static inline void xskq_prod_submit_n(struct xsk_queue *q, u32 nb_entries) +{ + __xskq_prod_submit(q, q->ring->producer + nb_entries); } /* Rx/Tx queue */ @@ -330,11 +334,11 @@ static inline void xskq_discard_desc(struct xsk_queue *q) q->cons_tail++; } -static inline int xskq_produce_batch_desc(struct xsk_queue *q, - u64 addr, u32 len) +static inline int xskq_prod_reserve_desc(struct xsk_queue *q, + u64 addr, u32 len) { struct xdp_rxtx_ring *ring = (struct xdp_rxtx_ring *)q->ring; - unsigned int idx; + u32 idx; if (xskq_nb_free(q, 1) == 0) return -ENOSPC; @@ -347,14 +351,6 @@ static inline int xskq_produce_batch_desc(struct xsk_queue *q, return 0; } -static inline void xskq_produce_flush_desc(struct xsk_queue *q) -{ - /* Order producer and data */ - smp_wmb(); /* B, matches C */ - - WRITE_ONCE(q->ring->producer, q->cached_prod); -} - static inline bool xskq_full_desc(struct xsk_queue *q) { /* No barriers needed since data is not accessed */ @@ -362,7 +358,7 @@ static inline bool xskq_full_desc(struct xsk_queue *q) q->nentries; } -static inline bool xskq_empty_desc(struct xsk_queue *q) +static inline bool xskq_prod_is_empty(struct xsk_queue *q) { /* No barriers needed since data is not accessed */ return READ_ONCE(q->ring->consumer) == READ_ONCE(q->ring->producer); -- GitLab From 4b638f13bab4dbfe8569c84e374e8201a427115c Mon Sep 17 00:00:00 2001 From: Magnus Karlsson Date: Thu, 19 Dec 2019 13:39:24 +0100 Subject: [PATCH 1189/1437] xsk: Eliminate the RX batch size In the xsk consumer ring code there is a variable called RX_BATCH_SIZE that dictates the minimum number of entries that we try to grab from the fill and Tx rings. In fact, the code always try to grab the maximum amount of entries from these rings. The only thing this variable does is to throw an error if there is less than 16 (as it is defined) entries on the ring. There is no reason to do this and it will just lead to weird behavior from user space's point of view. So eliminate this variable. With this change, we will be able to simplify the xskq_nb_free and xskq_nb_avail code in the next commit. Signed-off-by: Magnus Karlsson Signed-off-by: Alexei Starovoitov Link: https://lore.kernel.org/bpf/1576759171-28550-6-git-send-email-magnus.karlsson@intel.com --- net/xdp/xsk_queue.h | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/net/xdp/xsk_queue.h b/net/xdp/xsk_queue.h index 1b9a350f2e663..6ca5fed87852f 100644 --- a/net/xdp/xsk_queue.h +++ b/net/xdp/xsk_queue.h @@ -10,8 +10,6 @@ #include #include -#define RX_BATCH_SIZE 16 - struct xdp_ring { u32 producer ____cacheline_aligned_in_smp; u32 consumer ____cacheline_aligned_in_smp; @@ -202,7 +200,7 @@ static inline u64 *xskq_peek_addr(struct xsk_queue *q, u64 *addr, if (q->cons_tail == q->cons_head) { smp_mb(); /* D, matches A */ WRITE_ONCE(q->ring->consumer, q->cons_tail); - q->cons_head = q->cons_tail + xskq_nb_avail(q, RX_BATCH_SIZE); + q->cons_head = q->cons_tail + xskq_nb_avail(q, 1); /* Order consumer and data */ smp_rmb(); @@ -320,7 +318,7 @@ static inline struct xdp_desc *xskq_peek_desc(struct xsk_queue *q, if (q->cons_tail == q->cons_head) { smp_mb(); /* D, matches A */ WRITE_ONCE(q->ring->consumer, q->cons_tail); - q->cons_head = q->cons_tail + xskq_nb_avail(q, RX_BATCH_SIZE); + q->cons_head = q->cons_tail + xskq_nb_avail(q, 1); /* Order consumer and data */ smp_rmb(); /* C, matches B */ -- GitLab From df0ae6f78a45e5696427779fc3379c5d75f5d4a5 Mon Sep 17 00:00:00 2001 From: Magnus Karlsson Date: Thu, 19 Dec 2019 13:39:25 +0100 Subject: [PATCH 1190/1437] xsk: Simplify xskq_nb_avail and xskq_nb_free At this point, there are no users of the functions xskq_nb_avail and xskq_nb_free that take any other number of entries argument than 1, so let us get rid of the second argument that takes the number of entries. Signed-off-by: Magnus Karlsson Signed-off-by: Alexei Starovoitov Link: https://lore.kernel.org/bpf/1576759171-28550-7-git-send-email-magnus.karlsson@intel.com --- net/xdp/xsk_queue.h | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/net/xdp/xsk_queue.h b/net/xdp/xsk_queue.h index 6ca5fed87852f..8bfa2ee6864c3 100644 --- a/net/xdp/xsk_queue.h +++ b/net/xdp/xsk_queue.h @@ -89,7 +89,7 @@ static inline u64 xskq_nb_invalid_descs(struct xsk_queue *q) return q ? q->invalid_descs : 0; } -static inline u32 xskq_nb_avail(struct xsk_queue *q, u32 dcnt) +static inline u32 xskq_nb_avail(struct xsk_queue *q) { u32 entries = q->cached_prod - q->cons_tail; @@ -99,19 +99,21 @@ static inline u32 xskq_nb_avail(struct xsk_queue *q, u32 dcnt) entries = q->cached_prod - q->cons_tail; } - return (entries > dcnt) ? dcnt : entries; + return entries; } -static inline u32 xskq_nb_free(struct xsk_queue *q, u32 dcnt) +static inline bool xskq_prod_is_full(struct xsk_queue *q) { u32 free_entries = q->nentries - (q->cached_prod - q->cons_tail); - if (free_entries >= dcnt) - return free_entries; + if (free_entries) + return false; /* Refresh the local tail pointer */ q->cons_tail = READ_ONCE(q->ring->consumer); - return q->nentries - (q->cached_prod - q->cons_tail); + free_entries = q->nentries - (q->cached_prod - q->cons_tail); + + return !free_entries; } static inline bool xskq_has_addrs(struct xsk_queue *q, u32 cnt) @@ -200,7 +202,7 @@ static inline u64 *xskq_peek_addr(struct xsk_queue *q, u64 *addr, if (q->cons_tail == q->cons_head) { smp_mb(); /* D, matches A */ WRITE_ONCE(q->ring->consumer, q->cons_tail); - q->cons_head = q->cons_tail + xskq_nb_avail(q, 1); + q->cons_head = q->cons_tail + xskq_nb_avail(q); /* Order consumer and data */ smp_rmb(); @@ -216,7 +218,7 @@ static inline void xskq_discard_addr(struct xsk_queue *q) static inline int xskq_prod_reserve(struct xsk_queue *q) { - if (xskq_nb_free(q, 1) == 0) + if (xskq_prod_is_full(q)) return -ENOSPC; /* A, matches D */ @@ -228,7 +230,7 @@ static inline int xskq_prod_reserve_addr(struct xsk_queue *q, u64 addr) { struct xdp_umem_ring *ring = (struct xdp_umem_ring *)q->ring; - if (xskq_nb_free(q, 1) == 0) + if (xskq_prod_is_full(q)) return -ENOSPC; /* A, matches D */ @@ -318,7 +320,7 @@ static inline struct xdp_desc *xskq_peek_desc(struct xsk_queue *q, if (q->cons_tail == q->cons_head) { smp_mb(); /* D, matches A */ WRITE_ONCE(q->ring->consumer, q->cons_tail); - q->cons_head = q->cons_tail + xskq_nb_avail(q, 1); + q->cons_head = q->cons_tail + xskq_nb_avail(q); /* Order consumer and data */ smp_rmb(); /* C, matches B */ @@ -338,7 +340,7 @@ static inline int xskq_prod_reserve_desc(struct xsk_queue *q, struct xdp_rxtx_ring *ring = (struct xdp_rxtx_ring *)q->ring; u32 idx; - if (xskq_nb_free(q, 1) == 0) + if (xskq_prod_is_full(q)) return -ENOSPC; /* A, matches D */ -- GitLab From c5ed924b54c892ee637d2e6889ef83341835a560 Mon Sep 17 00:00:00 2001 From: Magnus Karlsson Date: Thu, 19 Dec 2019 13:39:26 +0100 Subject: [PATCH 1191/1437] xsk: Simplify the consumer ring access functions Simplify and refactor consumer ring functions. The consumer first "peeks" to find descriptors or addresses that are available to read from the ring, then reads them and finally "releases" these descriptors once it is done. The two local variables cons_tail and cons_head are turned into one single variable called cached_cons. cached_tail referred to the cached value of the global consumer pointer and will be stored in cached_cons. For cached_head, we just use cached_prod instead as it was not used for a consumer queue before. It also better reflects what it really is now: a cached copy of the producer pointer. The names of the functions are also renamed in the same manner as the producer functions. The new functions are called xskq_cons_ followed by what it does. Signed-off-by: Magnus Karlsson Signed-off-by: Alexei Starovoitov Link: https://lore.kernel.org/bpf/1576759171-28550-8-git-send-email-magnus.karlsson@intel.com --- net/xdp/xsk.c | 24 +++++------ net/xdp/xsk_queue.h | 102 ++++++++++++++++++++------------------------ 2 files changed, 58 insertions(+), 68 deletions(-) diff --git a/net/xdp/xsk.c b/net/xdp/xsk.c index 5f2123fe630c9..cd84b9d0f1e1d 100644 --- a/net/xdp/xsk.c +++ b/net/xdp/xsk.c @@ -41,19 +41,19 @@ bool xsk_is_setup_for_bpf_map(struct xdp_sock *xs) bool xsk_umem_has_addrs(struct xdp_umem *umem, u32 cnt) { - return xskq_has_addrs(umem->fq, cnt); + return xskq_cons_has_entries(umem->fq, cnt); } EXPORT_SYMBOL(xsk_umem_has_addrs); u64 *xsk_umem_peek_addr(struct xdp_umem *umem, u64 *addr) { - return xskq_peek_addr(umem->fq, addr, umem); + return xskq_cons_peek_addr(umem->fq, addr, umem); } EXPORT_SYMBOL(xsk_umem_peek_addr); void xsk_umem_discard_addr(struct xdp_umem *umem) { - xskq_discard_addr(umem->fq); + xskq_cons_release(umem->fq); } EXPORT_SYMBOL(xsk_umem_discard_addr); @@ -148,7 +148,7 @@ static int __xsk_rcv(struct xdp_sock *xs, struct xdp_buff *xdp, u32 len) u32 metalen; int err; - if (!xskq_peek_addr(xs->umem->fq, &addr, xs->umem) || + if (!xskq_cons_peek_addr(xs->umem->fq, &addr, xs->umem) || len > xs->umem->chunk_size_nohr - XDP_PACKET_HEADROOM) { xs->rx_dropped++; return -ENOSPC; @@ -169,7 +169,7 @@ static int __xsk_rcv(struct xdp_sock *xs, struct xdp_buff *xdp, u32 len) addr = xsk_umem_adjust_offset(xs->umem, addr, offset); err = xskq_prod_reserve_desc(xs->rx, addr, len); if (!err) { - xskq_discard_addr(xs->umem->fq); + xskq_cons_release(xs->umem->fq); xdp_return_buff(xdp); return 0; } @@ -236,7 +236,7 @@ int xsk_generic_rcv(struct xdp_sock *xs, struct xdp_buff *xdp) goto out_unlock; } - if (!xskq_peek_addr(xs->umem->fq, &addr, xs->umem) || + if (!xskq_cons_peek_addr(xs->umem->fq, &addr, xs->umem) || len > xs->umem->chunk_size_nohr - XDP_PACKET_HEADROOM) { err = -ENOSPC; goto out_drop; @@ -251,7 +251,7 @@ int xsk_generic_rcv(struct xdp_sock *xs, struct xdp_buff *xdp) if (err) goto out_drop; - xskq_discard_addr(xs->umem->fq); + xskq_cons_release(xs->umem->fq); xskq_prod_submit(xs->rx); spin_unlock_bh(&xs->rx_lock); @@ -316,13 +316,13 @@ bool xsk_umem_consume_tx(struct xdp_umem *umem, struct xdp_desc *desc) rcu_read_lock(); list_for_each_entry_rcu(xs, &umem->xsk_list, list) { - if (!xskq_peek_desc(xs->tx, desc, umem)) + if (!xskq_cons_peek_desc(xs->tx, desc, umem)) continue; if (xskq_prod_reserve_addr(umem->cq, desc->addr)) goto out; - xskq_discard_desc(xs->tx); + xskq_cons_release(xs->tx); rcu_read_unlock(); return true; } @@ -368,7 +368,7 @@ static int xsk_generic_xmit(struct sock *sk) if (xs->queue_id >= xs->dev->real_num_tx_queues) goto out; - while (xskq_peek_desc(xs->tx, &desc, xs->umem)) { + while (xskq_cons_peek_desc(xs->tx, &desc, xs->umem)) { char *buffer; u64 addr; u32 len; @@ -401,7 +401,7 @@ static int xsk_generic_xmit(struct sock *sk) skb->destructor = xsk_destruct_skb; err = dev_direct_xmit(skb, xs->queue_id); - xskq_discard_desc(xs->tx); + xskq_cons_release(xs->tx); /* Ignore NET_XMIT_CN as packet might have been sent */ if (err == NET_XMIT_DROP || err == NETDEV_TX_BUSY) { /* SKB completed but not sent */ @@ -472,7 +472,7 @@ static __poll_t xsk_poll(struct file *file, struct socket *sock, if (xs->rx && !xskq_prod_is_empty(xs->rx)) mask |= EPOLLIN | EPOLLRDNORM; - if (xs->tx && !xskq_full_desc(xs->tx)) + if (xs->tx && !xskq_cons_is_full(xs->tx)) mask |= EPOLLOUT | EPOLLWRNORM; return mask; diff --git a/net/xdp/xsk_queue.h b/net/xdp/xsk_queue.h index 8bfa2ee6864c3..1436116767ea6 100644 --- a/net/xdp/xsk_queue.h +++ b/net/xdp/xsk_queue.h @@ -34,8 +34,7 @@ struct xsk_queue { u32 ring_mask; u32 nentries; u32 cached_prod; - u32 cons_head; - u32 cons_tail; + u32 cached_cons; struct xdp_ring *ring; u64 invalid_descs; }; @@ -89,43 +88,48 @@ static inline u64 xskq_nb_invalid_descs(struct xsk_queue *q) return q ? q->invalid_descs : 0; } -static inline u32 xskq_nb_avail(struct xsk_queue *q) +static inline void __xskq_cons_release(struct xsk_queue *q) { - u32 entries = q->cached_prod - q->cons_tail; + smp_mb(); /* D, matches A */ + WRITE_ONCE(q->ring->consumer, q->cached_cons); +} - if (entries == 0) { - /* Refresh the local pointer */ - q->cached_prod = READ_ONCE(q->ring->producer); - entries = q->cached_prod - q->cons_tail; - } +static inline void __xskq_cons_peek(struct xsk_queue *q) +{ + /* Refresh the local pointer */ + q->cached_prod = READ_ONCE(q->ring->producer); + smp_rmb(); /* C, matches B */ +} - return entries; +static inline void xskq_cons_get_entries(struct xsk_queue *q) +{ + __xskq_cons_release(q); + __xskq_cons_peek(q); } static inline bool xskq_prod_is_full(struct xsk_queue *q) { - u32 free_entries = q->nentries - (q->cached_prod - q->cons_tail); + u32 free_entries = q->nentries - (q->cached_prod - q->cached_cons); if (free_entries) return false; /* Refresh the local tail pointer */ - q->cons_tail = READ_ONCE(q->ring->consumer); - free_entries = q->nentries - (q->cached_prod - q->cons_tail); + q->cached_cons = READ_ONCE(q->ring->consumer); + free_entries = q->nentries - (q->cached_prod - q->cached_cons); return !free_entries; } -static inline bool xskq_has_addrs(struct xsk_queue *q, u32 cnt) +static inline bool xskq_cons_has_entries(struct xsk_queue *q, u32 cnt) { - u32 entries = q->cached_prod - q->cons_tail; + u32 entries = q->cached_prod - q->cached_cons; if (entries >= cnt) return true; - /* Refresh the local pointer. */ - q->cached_prod = READ_ONCE(q->ring->producer); - entries = q->cached_prod - q->cons_tail; + __xskq_cons_peek(q); + entries = q->cached_prod - q->cached_cons; return entries >= cnt; } @@ -172,9 +176,10 @@ static inline bool xskq_is_valid_addr_unaligned(struct xsk_queue *q, u64 addr, static inline u64 *xskq_validate_addr(struct xsk_queue *q, u64 *addr, struct xdp_umem *umem) { - while (q->cons_tail != q->cons_head) { - struct xdp_umem_ring *ring = (struct xdp_umem_ring *)q->ring; - unsigned int idx = q->cons_tail & q->ring_mask; + struct xdp_umem_ring *ring = (struct xdp_umem_ring *)q->ring; + + while (q->cached_cons != q->cached_prod) { + u32 idx = q->cached_cons & q->ring_mask; *addr = READ_ONCE(ring->desc[idx]) & q->chunk_mask; @@ -190,30 +195,27 @@ static inline u64 *xskq_validate_addr(struct xsk_queue *q, u64 *addr, return addr; out: - q->cons_tail++; + q->cached_cons++; } return NULL; } -static inline u64 *xskq_peek_addr(struct xsk_queue *q, u64 *addr, - struct xdp_umem *umem) +static inline u64 *xskq_cons_peek_addr(struct xsk_queue *q, u64 *addr, + struct xdp_umem *umem) { - if (q->cons_tail == q->cons_head) { - smp_mb(); /* D, matches A */ - WRITE_ONCE(q->ring->consumer, q->cons_tail); - q->cons_head = q->cons_tail + xskq_nb_avail(q); - - /* Order consumer and data */ - smp_rmb(); - } - + if (q->cached_prod == q->cached_cons) + xskq_cons_get_entries(q); return xskq_validate_addr(q, addr, umem); } -static inline void xskq_discard_addr(struct xsk_queue *q) +static inline void xskq_cons_release(struct xsk_queue *q) { - q->cons_tail++; + /* To improve performance, only update local state here. + * Do the actual release operation when we get new entries + * from the ring in xskq_cons_get_entries() instead. + */ + q->cached_cons++; } static inline int xskq_prod_reserve(struct xsk_queue *q) @@ -299,41 +301,29 @@ static inline struct xdp_desc *xskq_validate_desc(struct xsk_queue *q, struct xdp_desc *desc, struct xdp_umem *umem) { - while (q->cons_tail != q->cons_head) { + while (q->cached_cons != q->cached_prod) { struct xdp_rxtx_ring *ring = (struct xdp_rxtx_ring *)q->ring; - unsigned int idx = q->cons_tail & q->ring_mask; + u32 idx = q->cached_cons & q->ring_mask; *desc = READ_ONCE(ring->desc[idx]); if (xskq_is_valid_desc(q, desc, umem)) return desc; - q->cons_tail++; + q->cached_cons++; } return NULL; } -static inline struct xdp_desc *xskq_peek_desc(struct xsk_queue *q, - struct xdp_desc *desc, - struct xdp_umem *umem) +static inline struct xdp_desc *xskq_cons_peek_desc(struct xsk_queue *q, + struct xdp_desc *desc, + struct xdp_umem *umem) { - if (q->cons_tail == q->cons_head) { - smp_mb(); /* D, matches A */ - WRITE_ONCE(q->ring->consumer, q->cons_tail); - q->cons_head = q->cons_tail + xskq_nb_avail(q); - - /* Order consumer and data */ - smp_rmb(); /* C, matches B */ - } - + if (q->cached_prod == q->cached_cons) + xskq_cons_get_entries(q); return xskq_validate_desc(q, desc, umem); } -static inline void xskq_discard_desc(struct xsk_queue *q) -{ - q->cons_tail++; -} - static inline int xskq_prod_reserve_desc(struct xsk_queue *q, u64 addr, u32 len) { @@ -351,7 +341,7 @@ static inline int xskq_prod_reserve_desc(struct xsk_queue *q, return 0; } -static inline bool xskq_full_desc(struct xsk_queue *q) +static inline bool xskq_cons_is_full(struct xsk_queue *q) { /* No barriers needed since data is not accessed */ return READ_ONCE(q->ring->producer) - READ_ONCE(q->ring->consumer) == -- GitLab From 03896ef1f0cb23d2742ddf486c531c700a2da7d6 Mon Sep 17 00:00:00 2001 From: Magnus Karlsson Date: Thu, 19 Dec 2019 13:39:27 +0100 Subject: [PATCH 1192/1437] xsk: Change names of validation functions Change the names of the validation functions to better reflect what they are doing. The uppermost ones are reading entries from the rings and only the bottom ones validate entries. So xskq_cons_read_ is a better prefix name. Also change the xskq_cons_read_ functions to return a bool as the the descriptor or address is already returned by reference in the parameters. Everyone is using the return value as a bool anyway. Signed-off-by: Magnus Karlsson Signed-off-by: Alexei Starovoitov Link: https://lore.kernel.org/bpf/1576759171-28550-9-git-send-email-magnus.karlsson@intel.com --- include/net/xdp_sock.h | 4 +-- net/xdp/xsk.c | 4 +-- net/xdp/xsk_queue.h | 59 ++++++++++++++++++++++-------------------- 3 files changed, 35 insertions(+), 32 deletions(-) diff --git a/include/net/xdp_sock.h b/include/net/xdp_sock.h index 48594740d67c5..63f005830866a 100644 --- a/include/net/xdp_sock.h +++ b/include/net/xdp_sock.h @@ -118,7 +118,7 @@ int xsk_generic_rcv(struct xdp_sock *xs, struct xdp_buff *xdp); bool xsk_is_setup_for_bpf_map(struct xdp_sock *xs); /* Used from netdev driver */ bool xsk_umem_has_addrs(struct xdp_umem *umem, u32 cnt); -u64 *xsk_umem_peek_addr(struct xdp_umem *umem, u64 *addr); +bool xsk_umem_peek_addr(struct xdp_umem *umem, u64 *addr); void xsk_umem_discard_addr(struct xdp_umem *umem); void xsk_umem_complete_tx(struct xdp_umem *umem, u32 nb_entries); bool xsk_umem_consume_tx(struct xdp_umem *umem, struct xdp_desc *desc); @@ -197,7 +197,7 @@ static inline bool xsk_umem_has_addrs_rq(struct xdp_umem *umem, u32 cnt) return xsk_umem_has_addrs(umem, cnt - rq->length); } -static inline u64 *xsk_umem_peek_addr_rq(struct xdp_umem *umem, u64 *addr) +static inline bool xsk_umem_peek_addr_rq(struct xdp_umem *umem, u64 *addr) { struct xdp_umem_fq_reuse *rq = umem->fq_reuse; diff --git a/net/xdp/xsk.c b/net/xdp/xsk.c index cd84b9d0f1e1d..4e932a930b3a1 100644 --- a/net/xdp/xsk.c +++ b/net/xdp/xsk.c @@ -45,7 +45,7 @@ bool xsk_umem_has_addrs(struct xdp_umem *umem, u32 cnt) } EXPORT_SYMBOL(xsk_umem_has_addrs); -u64 *xsk_umem_peek_addr(struct xdp_umem *umem, u64 *addr) +bool xsk_umem_peek_addr(struct xdp_umem *umem, u64 *addr) { return xskq_cons_peek_addr(umem->fq, addr, umem); } @@ -126,7 +126,7 @@ static void __xsk_rcv_memcpy(struct xdp_umem *umem, u64 addr, void *from_buf, void *to_buf = xdp_umem_get_data(umem, addr); addr = xsk_umem_add_offset_to_addr(addr); - if (xskq_crosses_non_contig_pg(umem, addr, len + metalen)) { + if (xskq_cons_crosses_non_contig_pg(umem, addr, len + metalen)) { void *next_pg_addr = umem->pages[(addr >> PAGE_SHIFT) + 1].addr; u64 page_start = addr & ~(PAGE_SIZE - 1); u64 first_len = PAGE_SIZE - (addr - page_start); diff --git a/net/xdp/xsk_queue.h b/net/xdp/xsk_queue.h index 1436116767ea6..6d04a96dd092f 100644 --- a/net/xdp/xsk_queue.h +++ b/net/xdp/xsk_queue.h @@ -136,8 +136,9 @@ static inline bool xskq_cons_has_entries(struct xsk_queue *q, u32 cnt) /* UMEM queue */ -static inline bool xskq_crosses_non_contig_pg(struct xdp_umem *umem, u64 addr, - u64 length) +static inline bool xskq_cons_crosses_non_contig_pg(struct xdp_umem *umem, + u64 addr, + u64 length) { bool cross_pg = (addr & (PAGE_SIZE - 1)) + length > PAGE_SIZE; bool next_pg_contig = @@ -147,7 +148,7 @@ static inline bool xskq_crosses_non_contig_pg(struct xdp_umem *umem, u64 addr, return cross_pg && !next_pg_contig; } -static inline bool xskq_is_valid_addr(struct xsk_queue *q, u64 addr) +static inline bool xskq_cons_is_valid_addr(struct xsk_queue *q, u64 addr) { if (addr >= q->size) { q->invalid_descs++; @@ -157,7 +158,8 @@ static inline bool xskq_is_valid_addr(struct xsk_queue *q, u64 addr) return true; } -static inline bool xskq_is_valid_addr_unaligned(struct xsk_queue *q, u64 addr, +static inline bool xskq_cons_is_valid_unaligned(struct xsk_queue *q, + u64 addr, u64 length, struct xdp_umem *umem) { @@ -165,7 +167,7 @@ static inline bool xskq_is_valid_addr_unaligned(struct xsk_queue *q, u64 addr, addr = xsk_umem_add_offset_to_addr(addr); if (base_addr >= q->size || addr >= q->size || - xskq_crosses_non_contig_pg(umem, addr, length)) { + xskq_cons_crosses_non_contig_pg(umem, addr, length)) { q->invalid_descs++; return false; } @@ -173,8 +175,8 @@ static inline bool xskq_is_valid_addr_unaligned(struct xsk_queue *q, u64 addr, return true; } -static inline u64 *xskq_validate_addr(struct xsk_queue *q, u64 *addr, - struct xdp_umem *umem) +static inline bool xskq_cons_read_addr(struct xsk_queue *q, u64 *addr, + struct xdp_umem *umem) { struct xdp_umem_ring *ring = (struct xdp_umem_ring *)q->ring; @@ -184,29 +186,29 @@ static inline u64 *xskq_validate_addr(struct xsk_queue *q, u64 *addr, *addr = READ_ONCE(ring->desc[idx]) & q->chunk_mask; if (umem->flags & XDP_UMEM_UNALIGNED_CHUNK_FLAG) { - if (xskq_is_valid_addr_unaligned(q, *addr, + if (xskq_cons_is_valid_unaligned(q, *addr, umem->chunk_size_nohr, umem)) - return addr; + return true; goto out; } - if (xskq_is_valid_addr(q, *addr)) - return addr; + if (xskq_cons_is_valid_addr(q, *addr)) + return true; out: q->cached_cons++; } - return NULL; + return false; } -static inline u64 *xskq_cons_peek_addr(struct xsk_queue *q, u64 *addr, +static inline bool xskq_cons_peek_addr(struct xsk_queue *q, u64 *addr, struct xdp_umem *umem) { if (q->cached_prod == q->cached_cons) xskq_cons_get_entries(q); - return xskq_validate_addr(q, addr, umem); + return xskq_cons_read_addr(q, addr, umem); } static inline void xskq_cons_release(struct xsk_queue *q) @@ -270,11 +272,12 @@ static inline void xskq_prod_submit_n(struct xsk_queue *q, u32 nb_entries) /* Rx/Tx queue */ -static inline bool xskq_is_valid_desc(struct xsk_queue *q, struct xdp_desc *d, - struct xdp_umem *umem) +static inline bool xskq_cons_is_valid_desc(struct xsk_queue *q, + struct xdp_desc *d, + struct xdp_umem *umem) { if (umem->flags & XDP_UMEM_UNALIGNED_CHUNK_FLAG) { - if (!xskq_is_valid_addr_unaligned(q, d->addr, d->len, umem)) + if (!xskq_cons_is_valid_unaligned(q, d->addr, d->len, umem)) return false; if (d->len > umem->chunk_size_nohr || d->options) { @@ -285,7 +288,7 @@ static inline bool xskq_is_valid_desc(struct xsk_queue *q, struct xdp_desc *d, return true; } - if (!xskq_is_valid_addr(q, d->addr)) + if (!xskq_cons_is_valid_addr(q, d->addr)) return false; if (((d->addr + d->len) & q->chunk_mask) != (d->addr & q->chunk_mask) || @@ -297,31 +300,31 @@ static inline bool xskq_is_valid_desc(struct xsk_queue *q, struct xdp_desc *d, return true; } -static inline struct xdp_desc *xskq_validate_desc(struct xsk_queue *q, - struct xdp_desc *desc, - struct xdp_umem *umem) +static inline bool xskq_cons_read_desc(struct xsk_queue *q, + struct xdp_desc *desc, + struct xdp_umem *umem) { while (q->cached_cons != q->cached_prod) { struct xdp_rxtx_ring *ring = (struct xdp_rxtx_ring *)q->ring; u32 idx = q->cached_cons & q->ring_mask; *desc = READ_ONCE(ring->desc[idx]); - if (xskq_is_valid_desc(q, desc, umem)) - return desc; + if (xskq_cons_is_valid_desc(q, desc, umem)) + return true; q->cached_cons++; } - return NULL; + return false; } -static inline struct xdp_desc *xskq_cons_peek_desc(struct xsk_queue *q, - struct xdp_desc *desc, - struct xdp_umem *umem) +static inline bool xskq_cons_peek_desc(struct xsk_queue *q, + struct xdp_desc *desc, + struct xdp_umem *umem) { if (q->cached_prod == q->cached_cons) xskq_cons_get_entries(q); - return xskq_validate_desc(q, desc, umem); + return xskq_cons_read_desc(q, desc, umem); } static inline int xskq_prod_reserve_desc(struct xsk_queue *q, -- GitLab From f8509aa078de0842ec1817e8026e58620cd05d3b Mon Sep 17 00:00:00 2001 From: Magnus Karlsson Date: Thu, 19 Dec 2019 13:39:28 +0100 Subject: [PATCH 1193/1437] xsk: ixgbe: i40e: ice: mlx5: Xsk_umem_discard_addr to xsk_umem_release_addr Change the name of xsk_umem_discard_addr to xsk_umem_release_addr to better reflect the new naming of the AF_XDP queue manipulation functions. As this functions is used by drivers implementing support for AF_XDP zero-copy, it requires a name change to these drivers. The function xsk_umem_release_addr_rq has also changed name in the same fashion. Signed-off-by: Magnus Karlsson Signed-off-by: Alexei Starovoitov Link: https://lore.kernel.org/bpf/1576759171-28550-10-git-send-email-magnus.karlsson@intel.com --- drivers/net/ethernet/intel/i40e/i40e_xsk.c | 4 ++-- drivers/net/ethernet/intel/ice/ice_xsk.c | 4 ++-- drivers/net/ethernet/intel/ixgbe/ixgbe_xsk.c | 4 ++-- drivers/net/ethernet/mellanox/mlx5/core/en/xsk/rx.c | 2 +- include/net/xdp_sock.h | 10 +++++----- net/xdp/xsk.c | 4 ++-- 6 files changed, 14 insertions(+), 14 deletions(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e_xsk.c b/drivers/net/ethernet/intel/i40e/i40e_xsk.c index d07e1a8904283..20d6f11a3468e 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_xsk.c +++ b/drivers/net/ethernet/intel/i40e/i40e_xsk.c @@ -269,7 +269,7 @@ static bool i40e_alloc_buffer_zc(struct i40e_ring *rx_ring, bi->handle = xsk_umem_adjust_offset(umem, handle, umem->headroom); - xsk_umem_discard_addr(umem); + xsk_umem_release_addr(umem); return true; } @@ -306,7 +306,7 @@ static bool i40e_alloc_buffer_slow_zc(struct i40e_ring *rx_ring, bi->handle = xsk_umem_adjust_offset(umem, handle, umem->headroom); - xsk_umem_discard_addr_rq(umem); + xsk_umem_release_addr_rq(umem); return true; } diff --git a/drivers/net/ethernet/intel/ice/ice_xsk.c b/drivers/net/ethernet/intel/ice/ice_xsk.c index cf9b8b22d24f4..0af1f0b951c0e 100644 --- a/drivers/net/ethernet/intel/ice/ice_xsk.c +++ b/drivers/net/ethernet/intel/ice/ice_xsk.c @@ -555,7 +555,7 @@ ice_alloc_buf_fast_zc(struct ice_ring *rx_ring, struct ice_rx_buf *rx_buf) rx_buf->handle = handle + umem->headroom; - xsk_umem_discard_addr(umem); + xsk_umem_release_addr(umem); return true; } @@ -591,7 +591,7 @@ ice_alloc_buf_slow_zc(struct ice_ring *rx_ring, struct ice_rx_buf *rx_buf) rx_buf->handle = handle + umem->headroom; - xsk_umem_discard_addr_rq(umem); + xsk_umem_release_addr_rq(umem); return true; } diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_xsk.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_xsk.c index d6feaacfbf898..1501d0a220784 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_xsk.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_xsk.c @@ -277,7 +277,7 @@ static bool ixgbe_alloc_buffer_zc(struct ixgbe_ring *rx_ring, bi->handle = xsk_umem_adjust_offset(umem, handle, umem->headroom); - xsk_umem_discard_addr(umem); + xsk_umem_release_addr(umem); return true; } @@ -304,7 +304,7 @@ static bool ixgbe_alloc_buffer_slow_zc(struct ixgbe_ring *rx_ring, bi->handle = xsk_umem_adjust_offset(umem, handle, umem->headroom); - xsk_umem_discard_addr_rq(umem); + xsk_umem_release_addr_rq(umem); return true; } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/xsk/rx.c b/drivers/net/ethernet/mellanox/mlx5/core/en/xsk/rx.c index 475b6bd5d29be..62fc8a128a8df 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/xsk/rx.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/xsk/rx.c @@ -35,7 +35,7 @@ int mlx5e_xsk_page_alloc_umem(struct mlx5e_rq *rq, */ dma_info->addr = xdp_umem_get_dma(umem, handle); - xsk_umem_discard_addr_rq(umem); + xsk_umem_release_addr_rq(umem); dma_sync_single_for_device(rq->pdev, dma_info->addr, PAGE_SIZE, DMA_BIDIRECTIONAL); diff --git a/include/net/xdp_sock.h b/include/net/xdp_sock.h index 63f005830866a..e86ec48ef6274 100644 --- a/include/net/xdp_sock.h +++ b/include/net/xdp_sock.h @@ -119,7 +119,7 @@ bool xsk_is_setup_for_bpf_map(struct xdp_sock *xs); /* Used from netdev driver */ bool xsk_umem_has_addrs(struct xdp_umem *umem, u32 cnt); bool xsk_umem_peek_addr(struct xdp_umem *umem, u64 *addr); -void xsk_umem_discard_addr(struct xdp_umem *umem); +void xsk_umem_release_addr(struct xdp_umem *umem); void xsk_umem_complete_tx(struct xdp_umem *umem, u32 nb_entries); bool xsk_umem_consume_tx(struct xdp_umem *umem, struct xdp_desc *desc); void xsk_umem_consume_tx_done(struct xdp_umem *umem); @@ -208,12 +208,12 @@ static inline bool xsk_umem_peek_addr_rq(struct xdp_umem *umem, u64 *addr) return addr; } -static inline void xsk_umem_discard_addr_rq(struct xdp_umem *umem) +static inline void xsk_umem_release_addr_rq(struct xdp_umem *umem) { struct xdp_umem_fq_reuse *rq = umem->fq_reuse; if (!rq->length) - xsk_umem_discard_addr(umem); + xsk_umem_release_addr(umem); else rq->length--; } @@ -258,7 +258,7 @@ static inline u64 *xsk_umem_peek_addr(struct xdp_umem *umem, u64 *addr) return NULL; } -static inline void xsk_umem_discard_addr(struct xdp_umem *umem) +static inline void xsk_umem_release_addr(struct xdp_umem *umem) { } @@ -332,7 +332,7 @@ static inline u64 *xsk_umem_peek_addr_rq(struct xdp_umem *umem, u64 *addr) return NULL; } -static inline void xsk_umem_discard_addr_rq(struct xdp_umem *umem) +static inline void xsk_umem_release_addr_rq(struct xdp_umem *umem) { } diff --git a/net/xdp/xsk.c b/net/xdp/xsk.c index 4e932a930b3a1..55092feeaf36e 100644 --- a/net/xdp/xsk.c +++ b/net/xdp/xsk.c @@ -51,11 +51,11 @@ bool xsk_umem_peek_addr(struct xdp_umem *umem, u64 *addr) } EXPORT_SYMBOL(xsk_umem_peek_addr); -void xsk_umem_discard_addr(struct xdp_umem *umem) +void xsk_umem_release_addr(struct xdp_umem *umem) { xskq_cons_release(umem->fq); } -EXPORT_SYMBOL(xsk_umem_discard_addr); +EXPORT_SYMBOL(xsk_umem_release_addr); void xsk_set_rx_need_wakeup(struct xdp_umem *umem) { -- GitLab From c34787fcc90f0732ff00754057f11780007002e4 Mon Sep 17 00:00:00 2001 From: Magnus Karlsson Date: Thu, 19 Dec 2019 13:39:29 +0100 Subject: [PATCH 1194/1437] xsk: Remove unnecessary READ_ONCE of data There are two unnecessary READ_ONCE of descriptor data. These are not needed since the data is written by the producer before it signals that the data is available by incrementing the producer pointer. As the access to this producer pointer is serialized and the consumer always reads the descriptor after it has read and synchronized with the producer counter, the write of the descriptor will have fully completed and it does not matter if the consumer has any read tearing. Signed-off-by: Magnus Karlsson Signed-off-by: Alexei Starovoitov Link: https://lore.kernel.org/bpf/1576759171-28550-11-git-send-email-magnus.karlsson@intel.com --- net/xdp/xsk_queue.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/xdp/xsk_queue.h b/net/xdp/xsk_queue.h index 6d04a96dd092f..4c049cacc6932 100644 --- a/net/xdp/xsk_queue.h +++ b/net/xdp/xsk_queue.h @@ -183,7 +183,7 @@ static inline bool xskq_cons_read_addr(struct xsk_queue *q, u64 *addr, while (q->cached_cons != q->cached_prod) { u32 idx = q->cached_cons & q->ring_mask; - *addr = READ_ONCE(ring->desc[idx]) & q->chunk_mask; + *addr = ring->desc[idx] & q->chunk_mask; if (umem->flags & XDP_UMEM_UNALIGNED_CHUNK_FLAG) { if (xskq_cons_is_valid_unaligned(q, *addr, @@ -308,7 +308,7 @@ static inline bool xskq_cons_read_desc(struct xsk_queue *q, struct xdp_rxtx_ring *ring = (struct xdp_rxtx_ring *)q->ring; u32 idx = q->cached_cons & q->ring_mask; - *desc = READ_ONCE(ring->desc[idx]); + *desc = ring->desc[idx]; if (xskq_cons_is_valid_desc(q, desc, umem)) return true; -- GitLab From 15d8c9162ced1789d25261859a7b37db8426e409 Mon Sep 17 00:00:00 2001 From: Magnus Karlsson Date: Thu, 19 Dec 2019 13:39:30 +0100 Subject: [PATCH 1195/1437] xsk: Add function naming comments and reorder functions Add comments on how the ring access functions are named and how they are supposed to be used for producers and consumers. The functions are also reordered so that the consumer functions are in the beginning and the producer functions in the end, for easier reference. Put this in a separate patch as the diff might look a little odd, but no functionality has changed in this patch. Signed-off-by: Magnus Karlsson Signed-off-by: Alexei Starovoitov Link: https://lore.kernel.org/bpf/1576759171-28550-12-git-send-email-magnus.karlsson@intel.com --- net/xdp/xsk.c | 10 ++ net/xdp/xsk_queue.h | 293 ++++++++++++++++++++++++-------------------- 2 files changed, 167 insertions(+), 136 deletions(-) diff --git a/net/xdp/xsk.c b/net/xdp/xsk.c index 55092feeaf36e..5560d60c1ff59 100644 --- a/net/xdp/xsk.c +++ b/net/xdp/xsk.c @@ -319,6 +319,11 @@ bool xsk_umem_consume_tx(struct xdp_umem *umem, struct xdp_desc *desc) if (!xskq_cons_peek_desc(xs->tx, desc, umem)) continue; + /* This is the backpreassure mechanism for the Tx path. + * Reserve space in the completion queue and only proceed + * if there is space in it. This avoids having to implement + * any buffering in the Tx path. + */ if (xskq_prod_reserve_addr(umem->cq, desc->addr)) goto out; @@ -389,6 +394,11 @@ static int xsk_generic_xmit(struct sock *sk) addr = desc.addr; buffer = xdp_umem_get_data(xs->umem, addr); err = skb_store_bits(skb, 0, buffer, len); + /* This is the backpreassure mechanism for the Tx path. + * Reserve space in the completion queue and only proceed + * if there is space in it. This avoids having to implement + * any buffering in the Tx path. + */ if (unlikely(err) || xskq_prod_reserve(xs->umem->cq)) { kfree_skb(skb); goto out; diff --git a/net/xdp/xsk_queue.h b/net/xdp/xsk_queue.h index 4c049cacc6932..bec2af11853ad 100644 --- a/net/xdp/xsk_queue.h +++ b/net/xdp/xsk_queue.h @@ -81,60 +81,27 @@ struct xsk_queue { * now and again after circling through the ring. */ -/* Common functions operating for both RXTX and umem queues */ - -static inline u64 xskq_nb_invalid_descs(struct xsk_queue *q) -{ - return q ? q->invalid_descs : 0; -} - -static inline void __xskq_cons_release(struct xsk_queue *q) -{ - smp_mb(); /* D, matches A */ - WRITE_ONCE(q->ring->consumer, q->cached_cons); -} - -static inline void __xskq_cons_peek(struct xsk_queue *q) -{ - /* Refresh the local pointer */ - q->cached_prod = READ_ONCE(q->ring->producer); - smp_rmb(); /* C, matches B */ -} - -static inline void xskq_cons_get_entries(struct xsk_queue *q) -{ - __xskq_cons_release(q); - __xskq_cons_peek(q); -} - -static inline bool xskq_prod_is_full(struct xsk_queue *q) -{ - u32 free_entries = q->nentries - (q->cached_prod - q->cached_cons); - - if (free_entries) - return false; - - /* Refresh the local tail pointer */ - q->cached_cons = READ_ONCE(q->ring->consumer); - free_entries = q->nentries - (q->cached_prod - q->cached_cons); - - return !free_entries; -} - -static inline bool xskq_cons_has_entries(struct xsk_queue *q, u32 cnt) -{ - u32 entries = q->cached_prod - q->cached_cons; - - if (entries >= cnt) - return true; - - __xskq_cons_peek(q); - entries = q->cached_prod - q->cached_cons; - - return entries >= cnt; -} +/* The operations on the rings are the following: + * + * producer consumer + * + * RESERVE entries PEEK in the ring for entries + * WRITE data into the ring READ data from the ring + * SUBMIT entries RELEASE entries + * + * The producer reserves one or more entries in the ring. It can then + * fill in these entries and finally submit them so that they can be + * seen and read by the consumer. + * + * The consumer peeks into the ring to see if the producer has written + * any new entries. If so, the producer can then read these entries + * and when it is done reading them release them back to the producer + * so that the producer can use these slots to fill in new entries. + * + * The function names below reflect these operations. + */ -/* UMEM queue */ +/* Functions that read and validate content from consumer rings. */ static inline bool xskq_cons_crosses_non_contig_pg(struct xdp_umem *umem, u64 addr, @@ -148,16 +115,6 @@ static inline bool xskq_cons_crosses_non_contig_pg(struct xdp_umem *umem, return cross_pg && !next_pg_contig; } -static inline bool xskq_cons_is_valid_addr(struct xsk_queue *q, u64 addr) -{ - if (addr >= q->size) { - q->invalid_descs++; - return false; - } - - return true; -} - static inline bool xskq_cons_is_valid_unaligned(struct xsk_queue *q, u64 addr, u64 length, @@ -175,6 +132,16 @@ static inline bool xskq_cons_is_valid_unaligned(struct xsk_queue *q, return true; } +static inline bool xskq_cons_is_valid_addr(struct xsk_queue *q, u64 addr) +{ + if (addr >= q->size) { + q->invalid_descs++; + return false; + } + + return true; +} + static inline bool xskq_cons_read_addr(struct xsk_queue *q, u64 *addr, struct xdp_umem *umem) { @@ -203,75 +170,6 @@ out: return false; } -static inline bool xskq_cons_peek_addr(struct xsk_queue *q, u64 *addr, - struct xdp_umem *umem) -{ - if (q->cached_prod == q->cached_cons) - xskq_cons_get_entries(q); - return xskq_cons_read_addr(q, addr, umem); -} - -static inline void xskq_cons_release(struct xsk_queue *q) -{ - /* To improve performance, only update local state here. - * Do the actual release operation when we get new entries - * from the ring in xskq_cons_get_entries() instead. - */ - q->cached_cons++; -} - -static inline int xskq_prod_reserve(struct xsk_queue *q) -{ - if (xskq_prod_is_full(q)) - return -ENOSPC; - - /* A, matches D */ - q->cached_prod++; - return 0; -} - -static inline int xskq_prod_reserve_addr(struct xsk_queue *q, u64 addr) -{ - struct xdp_umem_ring *ring = (struct xdp_umem_ring *)q->ring; - - if (xskq_prod_is_full(q)) - return -ENOSPC; - - /* A, matches D */ - ring->desc[q->cached_prod++ & q->ring_mask] = addr; - return 0; -} - -static inline void __xskq_prod_submit(struct xsk_queue *q, u32 idx) -{ - /* Order producer and data */ - smp_wmb(); /* B, matches C */ - - WRITE_ONCE(q->ring->producer, idx); -} - -static inline void xskq_prod_submit(struct xsk_queue *q) -{ - __xskq_prod_submit(q, q->cached_prod); -} - -static inline void xskq_prod_submit_addr(struct xsk_queue *q, u64 addr) -{ - struct xdp_umem_ring *ring = (struct xdp_umem_ring *)q->ring; - u32 idx = q->ring->producer; - - ring->desc[idx++ & q->ring_mask] = addr; - - __xskq_prod_submit(q, idx); -} - -static inline void xskq_prod_submit_n(struct xsk_queue *q, u32 nb_entries) -{ - __xskq_prod_submit(q, q->ring->producer + nb_entries); -} - -/* Rx/Tx queue */ - static inline bool xskq_cons_is_valid_desc(struct xsk_queue *q, struct xdp_desc *d, struct xdp_umem *umem) @@ -318,6 +216,48 @@ static inline bool xskq_cons_read_desc(struct xsk_queue *q, return false; } +/* Functions for consumers */ + +static inline void __xskq_cons_release(struct xsk_queue *q) +{ + smp_mb(); /* D, matches A */ + WRITE_ONCE(q->ring->consumer, q->cached_cons); +} + +static inline void __xskq_cons_peek(struct xsk_queue *q) +{ + /* Refresh the local pointer */ + q->cached_prod = READ_ONCE(q->ring->producer); + smp_rmb(); /* C, matches B */ +} + +static inline void xskq_cons_get_entries(struct xsk_queue *q) +{ + __xskq_cons_release(q); + __xskq_cons_peek(q); +} + +static inline bool xskq_cons_has_entries(struct xsk_queue *q, u32 cnt) +{ + u32 entries = q->cached_prod - q->cached_cons; + + if (entries >= cnt) + return true; + + __xskq_cons_peek(q); + entries = q->cached_prod - q->cached_cons; + + return entries >= cnt; +} + +static inline bool xskq_cons_peek_addr(struct xsk_queue *q, u64 *addr, + struct xdp_umem *umem) +{ + if (q->cached_prod == q->cached_cons) + xskq_cons_get_entries(q); + return xskq_cons_read_addr(q, addr, umem); +} + static inline bool xskq_cons_peek_desc(struct xsk_queue *q, struct xdp_desc *desc, struct xdp_umem *umem) @@ -327,6 +267,60 @@ static inline bool xskq_cons_peek_desc(struct xsk_queue *q, return xskq_cons_read_desc(q, desc, umem); } +static inline void xskq_cons_release(struct xsk_queue *q) +{ + /* To improve performance, only update local state here. + * Reflect this to global state when we get new entries + * from the ring in xskq_cons_get_entries(). + */ + q->cached_cons++; +} + +static inline bool xskq_cons_is_full(struct xsk_queue *q) +{ + /* No barriers needed since data is not accessed */ + return READ_ONCE(q->ring->producer) - READ_ONCE(q->ring->consumer) == + q->nentries; +} + +/* Functions for producers */ + +static inline bool xskq_prod_is_full(struct xsk_queue *q) +{ + u32 free_entries = q->nentries - (q->cached_prod - q->cached_cons); + + if (free_entries) + return false; + + /* Refresh the local tail pointer */ + q->cached_cons = READ_ONCE(q->ring->consumer); + free_entries = q->nentries - (q->cached_prod - q->cached_cons); + + return !free_entries; +} + +static inline int xskq_prod_reserve(struct xsk_queue *q) +{ + if (xskq_prod_is_full(q)) + return -ENOSPC; + + /* A, matches D */ + q->cached_prod++; + return 0; +} + +static inline int xskq_prod_reserve_addr(struct xsk_queue *q, u64 addr) +{ + struct xdp_umem_ring *ring = (struct xdp_umem_ring *)q->ring; + + if (xskq_prod_is_full(q)) + return -ENOSPC; + + /* A, matches D */ + ring->desc[q->cached_prod++ & q->ring_mask] = addr; + return 0; +} + static inline int xskq_prod_reserve_desc(struct xsk_queue *q, u64 addr, u32 len) { @@ -344,11 +338,31 @@ static inline int xskq_prod_reserve_desc(struct xsk_queue *q, return 0; } -static inline bool xskq_cons_is_full(struct xsk_queue *q) +static inline void __xskq_prod_submit(struct xsk_queue *q, u32 idx) { - /* No barriers needed since data is not accessed */ - return READ_ONCE(q->ring->producer) - READ_ONCE(q->ring->consumer) == - q->nentries; + smp_wmb(); /* B, matches C */ + + WRITE_ONCE(q->ring->producer, idx); +} + +static inline void xskq_prod_submit(struct xsk_queue *q) +{ + __xskq_prod_submit(q, q->cached_prod); +} + +static inline void xskq_prod_submit_addr(struct xsk_queue *q, u64 addr) +{ + struct xdp_umem_ring *ring = (struct xdp_umem_ring *)q->ring; + u32 idx = q->ring->producer; + + ring->desc[idx++ & q->ring_mask] = addr; + + __xskq_prod_submit(q, idx); +} + +static inline void xskq_prod_submit_n(struct xsk_queue *q, u32 nb_entries) +{ + __xskq_prod_submit(q, q->ring->producer + nb_entries); } static inline bool xskq_prod_is_empty(struct xsk_queue *q) @@ -357,6 +371,13 @@ static inline bool xskq_prod_is_empty(struct xsk_queue *q) return READ_ONCE(q->ring->consumer) == READ_ONCE(q->ring->producer); } +/* For both producers and consumers */ + +static inline u64 xskq_nb_invalid_descs(struct xsk_queue *q) +{ + return q ? q->invalid_descs : 0; +} + void xskq_set_umem(struct xsk_queue *q, u64 size, u64 chunk_mask); struct xsk_queue *xskq_create(u32 nentries, bool umem_queue); void xskq_destroy(struct xsk_queue *q_ops); -- GitLab From 1d9cb1f381860b529edec57cf7a08133f40366eb Mon Sep 17 00:00:00 2001 From: Magnus Karlsson Date: Thu, 19 Dec 2019 13:39:31 +0100 Subject: [PATCH 1196/1437] xsk: Use struct_size() helper Improve readability and maintainability by using the struct_size() helper when allocating the AF_XDP rings. Signed-off-by: Magnus Karlsson Signed-off-by: Alexei Starovoitov Link: https://lore.kernel.org/bpf/1576759171-28550-13-git-send-email-magnus.karlsson@intel.com --- net/xdp/xsk_queue.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/net/xdp/xsk_queue.c b/net/xdp/xsk_queue.c index b66504592d9bd..c90e9c1e3c63e 100644 --- a/net/xdp/xsk_queue.c +++ b/net/xdp/xsk_queue.c @@ -18,14 +18,14 @@ void xskq_set_umem(struct xsk_queue *q, u64 size, u64 chunk_mask) q->chunk_mask = chunk_mask; } -static u32 xskq_umem_get_ring_size(struct xsk_queue *q) +static size_t xskq_get_ring_size(struct xsk_queue *q, bool umem_queue) { - return sizeof(struct xdp_umem_ring) + q->nentries * sizeof(u64); -} + struct xdp_umem_ring *umem_ring; + struct xdp_rxtx_ring *rxtx_ring; -static u32 xskq_rxtx_get_ring_size(struct xsk_queue *q) -{ - return sizeof(struct xdp_ring) + q->nentries * sizeof(struct xdp_desc); + if (umem_queue) + return struct_size(umem_ring, desc, q->nentries); + return struct_size(rxtx_ring, desc, q->nentries); } struct xsk_queue *xskq_create(u32 nentries, bool umem_queue) @@ -43,8 +43,7 @@ struct xsk_queue *xskq_create(u32 nentries, bool umem_queue) gfp_flags = GFP_KERNEL | __GFP_ZERO | __GFP_NOWARN | __GFP_COMP | __GFP_NORETRY; - size = umem_queue ? xskq_umem_get_ring_size(q) : - xskq_rxtx_get_ring_size(q); + size = xskq_get_ring_size(q, umem_queue); q->ring = (struct xdp_ring *)__get_free_pages(gfp_flags, get_order(size)); -- GitLab From 478bee0df0ec9067c12e7d058d78721a7e7a1b29 Mon Sep 17 00:00:00 2001 From: Andrey Ignatov Date: Thu, 19 Dec 2019 16:05:11 -0800 Subject: [PATCH 1197/1437] selftests/bpf: Preserve errno in test_progs CHECK macros It's follow-up for discussion [1] CHECK and CHECK_FAIL macros in test_progs.h can affect errno in some circumstances, e.g. if some code accidentally closes stdout. It makes checking errno in patterns like this unreliable: if (CHECK(!bpf_prog_attach_xattr(...), "tag", "msg")) goto err; CHECK_FAIL(errno != ENOENT); , since by CHECK_FAIL time errno could be affected not only by bpf_prog_attach_xattr but by CHECK as well. Fix it by saving and restoring errno in the macros. There is no "Fixes" tag since no problems were discovered yet and it's rather precaution. test_progs was run with this change and no difference was identified. [1] https://lore.kernel.org/bpf/20191219210907.GD16266@rdna-mbp.dhcp.thefacebook.com/ Signed-off-by: Andrey Ignatov Signed-off-by: Alexei Starovoitov Acked-by: Andrii Nakryiko Link: https://lore.kernel.org/bpf/20191220000511.1684853-1-rdna@fb.com --- tools/testing/selftests/bpf/test_progs.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tools/testing/selftests/bpf/test_progs.h b/tools/testing/selftests/bpf/test_progs.h index 8477df8359793..de1fdaa4e7b43 100644 --- a/tools/testing/selftests/bpf/test_progs.h +++ b/tools/testing/selftests/bpf/test_progs.h @@ -100,6 +100,7 @@ extern struct ipv6_packet pkt_v6; #define _CHECK(condition, tag, duration, format...) ({ \ int __ret = !!(condition); \ + int __save_errno = errno; \ if (__ret) { \ test__fail(); \ printf("%s:FAIL:%s ", __func__, tag); \ @@ -108,15 +109,18 @@ extern struct ipv6_packet pkt_v6; printf("%s:PASS:%s %d nsec\n", \ __func__, tag, duration); \ } \ + errno = __save_errno; \ __ret; \ }) #define CHECK_FAIL(condition) ({ \ int __ret = !!(condition); \ + int __save_errno = errno; \ if (__ret) { \ test__fail(); \ printf("%s:FAIL:%d\n", __func__, __LINE__); \ } \ + errno = __save_errno; \ __ret; \ }) -- GitLab From d3f11b018f6ce278e683008e9c225fe87afc532d Mon Sep 17 00:00:00 2001 From: Jay Jayatheerthan Date: Fri, 20 Dec 2019 14:25:25 +0530 Subject: [PATCH 1198/1437] samples/bpf: xdpsock: Add duration option to specify how long to run The application now supports '-d' or '--duration' option to specify number of seconds to run. This is used in tx, rx and l2fwd features. If this option is not provided, the application runs forever. Signed-off-by: Jay Jayatheerthan Signed-off-by: Alexei Starovoitov Link: https://lore.kernel.org/bpf/20191220085530.4980-2-jay.jayatheerthan@intel.com --- samples/bpf/xdpsock_user.c | 37 +++++++++++++++++++++++++++++++++++-- 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/samples/bpf/xdpsock_user.c b/samples/bpf/xdpsock_user.c index e7829e5baaffd..e188a79a9c31f 100644 --- a/samples/bpf/xdpsock_user.c +++ b/samples/bpf/xdpsock_user.c @@ -65,6 +65,9 @@ static u32 opt_xdp_flags = XDP_FLAGS_UPDATE_IF_NOEXIST; static const char *opt_if = ""; static int opt_ifindex; static int opt_queue; +static unsigned long opt_duration; +static unsigned long start_time; +static bool benchmark_done; static int opt_poll; static int opt_interval = 1; static u32 opt_xdp_bind_flags = XDP_USE_NEED_WAKEUP; @@ -167,10 +170,21 @@ static void dump_stats(void) } } +static bool is_benchmark_done(void) +{ + if (opt_duration > 0) { + unsigned long dt = (get_nsecs() - start_time); + + if (dt >= opt_duration) + benchmark_done = true; + } + return benchmark_done; +} + static void *poller(void *arg) { (void)arg; - for (;;) { + while (!is_benchmark_done()) { sleep(opt_interval); dump_stats(); } @@ -375,6 +389,7 @@ static struct option long_options[] = { {"unaligned", no_argument, 0, 'u'}, {"shared-umem", no_argument, 0, 'M'}, {"force", no_argument, 0, 'F'}, + {"duration", required_argument, 0, 'd'}, {0, 0, 0, 0} }; @@ -399,6 +414,8 @@ static void usage(const char *prog) " -u, --unaligned Enable unaligned chunk placement\n" " -M, --shared-umem Enable XDP_SHARED_UMEM\n" " -F, --force Force loading the XDP prog\n" + " -d, --duration=n Duration in secs to run command.\n" + " Default: forever.\n" "\n"; fprintf(stderr, str, prog, XSK_UMEM__DEFAULT_FRAME_SIZE); exit(EXIT_FAILURE); @@ -411,7 +428,7 @@ static void parse_command_line(int argc, char **argv) opterr = 0; for (;;) { - c = getopt_long(argc, argv, "Frtli:q:psSNn:czf:muM", + c = getopt_long(argc, argv, "Frtli:q:psSNn:czf:muMd:", long_options, &option_index); if (c == -1) break; @@ -469,6 +486,10 @@ static void parse_command_line(int argc, char **argv) case 'M': opt_num_xsks = MAX_SOCKS; break; + case 'd': + opt_duration = atoi(optarg); + opt_duration *= 1000000000; + break; default: usage(basename(argv[0])); } @@ -622,6 +643,9 @@ static void rx_drop_all(void) for (i = 0; i < num_socks; i++) rx_drop(xsks[i], fds); + + if (benchmark_done) + break; } } @@ -671,6 +695,9 @@ static void tx_only_all(void) for (i = 0; i < num_socks; i++) tx_only(xsks[i], frame_nb[i]); + + if (benchmark_done) + break; } } @@ -739,6 +766,9 @@ static void l2fwd_all(void) for (i = 0; i < num_socks; i++) l2fwd(xsks[i], fds); + + if (benchmark_done) + break; } } @@ -852,6 +882,7 @@ int main(int argc, char **argv) exit_with_error(ret); prev_time = get_nsecs(); + start_time = prev_time; if (opt_bench == BENCH_RXDROP) rx_drop_all(); @@ -860,5 +891,7 @@ int main(int argc, char **argv) else l2fwd_all(); + pthread_join(pt, NULL); + return 0; } -- GitLab From 695255882bdf63da240db33d0f2aa9ccca1cbe67 Mon Sep 17 00:00:00 2001 From: Jay Jayatheerthan Date: Fri, 20 Dec 2019 14:25:26 +0530 Subject: [PATCH 1199/1437] samples/bpf: xdpsock: Use common code to handle signal and main exit Add code to do cleanup for signals and application completion in a unified fashion. The signal handler sets benckmark_done flag terminating the threads. The cleanup is called before returning from main() function. Signed-off-by: Jay Jayatheerthan Signed-off-by: Alexei Starovoitov Link: https://lore.kernel.org/bpf/20191220085530.4980-3-jay.jayatheerthan@intel.com --- samples/bpf/xdpsock_user.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/samples/bpf/xdpsock_user.c b/samples/bpf/xdpsock_user.c index e188a79a9c31f..7febc3d519a14 100644 --- a/samples/bpf/xdpsock_user.c +++ b/samples/bpf/xdpsock_user.c @@ -209,6 +209,11 @@ static void remove_xdp_program(void) } static void int_exit(int sig) +{ + benchmark_done = true; +} + +static void xdpsock_cleanup(void) { struct xsk_umem *umem = xsks[0]->umem->umem; int i; @@ -218,8 +223,6 @@ static void int_exit(int sig) xsk_socket__delete(xsks[i]->xsk); (void)xsk_umem__delete(umem); remove_xdp_program(); - - exit(EXIT_SUCCESS); } static void __exit_with_error(int error, const char *file, const char *func, @@ -893,5 +896,7 @@ int main(int argc, char **argv) pthread_join(pt, NULL); + xdpsock_cleanup(); + return 0; } -- GitLab From cd9e72b6f21044b36a096833003811c2b2038455 Mon Sep 17 00:00:00 2001 From: Jay Jayatheerthan Date: Fri, 20 Dec 2019 14:25:27 +0530 Subject: [PATCH 1200/1437] samples/bpf: xdpsock: Add option to specify batch size New option to specify batch size for tx, rx and l2fwd has been added. This allows fine tuning to maximize performance. It is specified using '-b' or '--batch_size' options. When not specified default is 64. Signed-off-by: Jay Jayatheerthan Signed-off-by: Alexei Starovoitov Link: https://lore.kernel.org/bpf/20191220085530.4980-4-jay.jayatheerthan@intel.com --- samples/bpf/xdpsock_user.c | 52 +++++++++++++++++++++----------------- 1 file changed, 29 insertions(+), 23 deletions(-) diff --git a/samples/bpf/xdpsock_user.c b/samples/bpf/xdpsock_user.c index 7febc3d519a14..1ba3e7142f39f 100644 --- a/samples/bpf/xdpsock_user.c +++ b/samples/bpf/xdpsock_user.c @@ -45,7 +45,6 @@ #endif #define NUM_FRAMES (4 * 1024) -#define BATCH_SIZE 64 #define DEBUG_HEXDUMP 0 @@ -68,6 +67,7 @@ static int opt_queue; static unsigned long opt_duration; static unsigned long start_time; static bool benchmark_done; +static u32 opt_batch_size = 64; static int opt_poll; static int opt_interval = 1; static u32 opt_xdp_bind_flags = XDP_USE_NEED_WAKEUP; @@ -237,7 +237,6 @@ static void __exit_with_error(int error, const char *file, const char *func, #define exit_with_error(error) __exit_with_error(error, __FILE__, __func__, \ __LINE__) - static const char pkt_data[] = "\x3c\xfd\xfe\x9e\x7f\x71\xec\xb1\xd7\x98\x3a\xc0\x08\x00\x45\x00" "\x00\x2e\x00\x00\x00\x00\x40\x11\x88\x97\x05\x08\x07\x08\xc8\x14" @@ -291,11 +290,10 @@ static void hex_dump(void *pkt, size_t length, u64 addr) printf("\n"); } -static size_t gen_eth_frame(struct xsk_umem_info *umem, u64 addr) +static void gen_eth_frame(struct xsk_umem_info *umem, u64 addr) { memcpy(xsk_umem__get_data(umem->buffer, addr), pkt_data, sizeof(pkt_data) - 1); - return sizeof(pkt_data) - 1; } static struct xsk_umem_info *xsk_configure_umem(void *buffer, u64 size) @@ -393,6 +391,7 @@ static struct option long_options[] = { {"shared-umem", no_argument, 0, 'M'}, {"force", no_argument, 0, 'F'}, {"duration", required_argument, 0, 'd'}, + {"batch-size", required_argument, 0, 'b'}, {0, 0, 0, 0} }; @@ -419,8 +418,11 @@ static void usage(const char *prog) " -F, --force Force loading the XDP prog\n" " -d, --duration=n Duration in secs to run command.\n" " Default: forever.\n" + " -b, --batch-size=n Batch size for sending or receiving\n" + " packets. Default: %d\n" "\n"; - fprintf(stderr, str, prog, XSK_UMEM__DEFAULT_FRAME_SIZE); + fprintf(stderr, str, prog, XSK_UMEM__DEFAULT_FRAME_SIZE, + opt_batch_size); exit(EXIT_FAILURE); } @@ -431,7 +433,7 @@ static void parse_command_line(int argc, char **argv) opterr = 0; for (;;) { - c = getopt_long(argc, argv, "Frtli:q:psSNn:czf:muMd:", + c = getopt_long(argc, argv, "Frtli:q:pSNn:czf:muMd:b:", long_options, &option_index); if (c == -1) break; @@ -493,6 +495,9 @@ static void parse_command_line(int argc, char **argv) opt_duration = atoi(optarg); opt_duration *= 1000000000; break; + case 'b': + opt_batch_size = atoi(optarg); + break; default: usage(basename(argv[0])); } @@ -540,7 +545,7 @@ static inline void complete_tx_l2fwd(struct xsk_socket_info *xsk, if (!opt_need_wakeup || xsk_ring_prod__needs_wakeup(&xsk->tx)) kick_tx(xsk); - ndescs = (xsk->outstanding_tx > BATCH_SIZE) ? BATCH_SIZE : + ndescs = (xsk->outstanding_tx > opt_batch_size) ? opt_batch_size : xsk->outstanding_tx; /* re-add completed Tx buffers */ @@ -580,7 +585,7 @@ static inline void complete_tx_only(struct xsk_socket_info *xsk) if (!opt_need_wakeup || xsk_ring_prod__needs_wakeup(&xsk->tx)) kick_tx(xsk); - rcvd = xsk_ring_cons__peek(&xsk->umem->cq, BATCH_SIZE, &idx); + rcvd = xsk_ring_cons__peek(&xsk->umem->cq, opt_batch_size, &idx); if (rcvd > 0) { xsk_ring_cons__release(&xsk->umem->cq, rcvd); xsk->outstanding_tx -= rcvd; @@ -594,7 +599,7 @@ static void rx_drop(struct xsk_socket_info *xsk, struct pollfd *fds) u32 idx_rx = 0, idx_fq = 0; int ret; - rcvd = xsk_ring_cons__peek(&xsk->rx, BATCH_SIZE, &idx_rx); + rcvd = xsk_ring_cons__peek(&xsk->rx, opt_batch_size, &idx_rx); if (!rcvd) { if (xsk_ring_prod__needs_wakeup(&xsk->umem->fq)) ret = poll(fds, num_socks, opt_timeout); @@ -655,23 +660,24 @@ static void rx_drop_all(void) static void tx_only(struct xsk_socket_info *xsk, u32 frame_nb) { u32 idx; + unsigned int i; - if (xsk_ring_prod__reserve(&xsk->tx, BATCH_SIZE, &idx) == BATCH_SIZE) { - unsigned int i; - - for (i = 0; i < BATCH_SIZE; i++) { - xsk_ring_prod__tx_desc(&xsk->tx, idx + i)->addr = - (frame_nb + i) << XSK_UMEM__DEFAULT_FRAME_SHIFT; - xsk_ring_prod__tx_desc(&xsk->tx, idx + i)->len = - sizeof(pkt_data) - 1; - } + while (xsk_ring_prod__reserve(&xsk->tx, opt_batch_size, &idx) < + opt_batch_size) { + complete_tx_only(xsk); + } - xsk_ring_prod__submit(&xsk->tx, BATCH_SIZE); - xsk->outstanding_tx += BATCH_SIZE; - frame_nb += BATCH_SIZE; - frame_nb %= NUM_FRAMES; + for (i = 0; i < opt_batch_size; i++) { + struct xdp_desc *tx_desc = xsk_ring_prod__tx_desc(&xsk->tx, + idx + i); + tx_desc->addr = (frame_nb + i) << XSK_UMEM__DEFAULT_FRAME_SHIFT; + tx_desc->len = sizeof(pkt_data) - 1; } + xsk_ring_prod__submit(&xsk->tx, opt_batch_size); + xsk->outstanding_tx += opt_batch_size; + frame_nb += opt_batch_size; + frame_nb %= NUM_FRAMES; complete_tx_only(xsk); } @@ -712,7 +718,7 @@ static void l2fwd(struct xsk_socket_info *xsk, struct pollfd *fds) complete_tx_l2fwd(xsk, fds); - rcvd = xsk_ring_cons__peek(&xsk->rx, BATCH_SIZE, &idx_rx); + rcvd = xsk_ring_cons__peek(&xsk->rx, opt_batch_size, &idx_rx); if (!rcvd) { if (xsk_ring_prod__needs_wakeup(&xsk->umem->fq)) ret = poll(fds, num_socks, opt_timeout); -- GitLab From ece6e9694751a4f0b99372724a0705a0217132b3 Mon Sep 17 00:00:00 2001 From: Jay Jayatheerthan Date: Fri, 20 Dec 2019 14:25:28 +0530 Subject: [PATCH 1201/1437] samples/bpf: xdpsock: Add option to specify number of packets to send Use '-C' or '--tx-pkt-count' to specify number of packets to send. If it is not specified, the application sends packets forever. If packet count is not a multiple of batch size, last batch sent is less than the batch size. Signed-off-by: Jay Jayatheerthan Signed-off-by: Alexei Starovoitov Link: https://lore.kernel.org/bpf/20191220085530.4980-5-jay.jayatheerthan@intel.com --- samples/bpf/xdpsock_user.c | 73 ++++++++++++++++++++++++++++++-------- 1 file changed, 59 insertions(+), 14 deletions(-) diff --git a/samples/bpf/xdpsock_user.c b/samples/bpf/xdpsock_user.c index 1ba3e7142f39f..f96ce3055d46a 100644 --- a/samples/bpf/xdpsock_user.c +++ b/samples/bpf/xdpsock_user.c @@ -68,6 +68,7 @@ static unsigned long opt_duration; static unsigned long start_time; static bool benchmark_done; static u32 opt_batch_size = 64; +static int opt_pkt_count; static int opt_poll; static int opt_interval = 1; static u32 opt_xdp_bind_flags = XDP_USE_NEED_WAKEUP; @@ -392,6 +393,7 @@ static struct option long_options[] = { {"force", no_argument, 0, 'F'}, {"duration", required_argument, 0, 'd'}, {"batch-size", required_argument, 0, 'b'}, + {"tx-pkt-count", required_argument, 0, 'C'}, {0, 0, 0, 0} }; @@ -420,6 +422,8 @@ static void usage(const char *prog) " Default: forever.\n" " -b, --batch-size=n Batch size for sending or receiving\n" " packets. Default: %d\n" + " -C, --tx-pkt-count=n Number of packets to send.\n" + " Default: Continuous packets.\n" "\n"; fprintf(stderr, str, prog, XSK_UMEM__DEFAULT_FRAME_SIZE, opt_batch_size); @@ -433,7 +437,7 @@ static void parse_command_line(int argc, char **argv) opterr = 0; for (;;) { - c = getopt_long(argc, argv, "Frtli:q:pSNn:czf:muMd:b:", + c = getopt_long(argc, argv, "Frtli:q:pSNn:czf:muMd:b:C:", long_options, &option_index); if (c == -1) break; @@ -498,6 +502,9 @@ static void parse_command_line(int argc, char **argv) case 'b': opt_batch_size = atoi(optarg); break; + case 'C': + opt_pkt_count = atoi(optarg); + break; default: usage(basename(argv[0])); } @@ -574,7 +581,8 @@ static inline void complete_tx_l2fwd(struct xsk_socket_info *xsk, } } -static inline void complete_tx_only(struct xsk_socket_info *xsk) +static inline void complete_tx_only(struct xsk_socket_info *xsk, + int batch_size) { unsigned int rcvd; u32 idx; @@ -585,7 +593,7 @@ static inline void complete_tx_only(struct xsk_socket_info *xsk) if (!opt_need_wakeup || xsk_ring_prod__needs_wakeup(&xsk->tx)) kick_tx(xsk); - rcvd = xsk_ring_cons__peek(&xsk->umem->cq, opt_batch_size, &idx); + rcvd = xsk_ring_cons__peek(&xsk->umem->cq, batch_size, &idx); if (rcvd > 0) { xsk_ring_cons__release(&xsk->umem->cq, rcvd); xsk->outstanding_tx -= rcvd; @@ -657,34 +665,62 @@ static void rx_drop_all(void) } } -static void tx_only(struct xsk_socket_info *xsk, u32 frame_nb) +static void tx_only(struct xsk_socket_info *xsk, u32 frame_nb, int batch_size) { u32 idx; unsigned int i; - while (xsk_ring_prod__reserve(&xsk->tx, opt_batch_size, &idx) < - opt_batch_size) { - complete_tx_only(xsk); + while (xsk_ring_prod__reserve(&xsk->tx, batch_size, &idx) < + batch_size) { + complete_tx_only(xsk, batch_size); } - for (i = 0; i < opt_batch_size; i++) { + for (i = 0; i < batch_size; i++) { struct xdp_desc *tx_desc = xsk_ring_prod__tx_desc(&xsk->tx, idx + i); tx_desc->addr = (frame_nb + i) << XSK_UMEM__DEFAULT_FRAME_SHIFT; tx_desc->len = sizeof(pkt_data) - 1; } - xsk_ring_prod__submit(&xsk->tx, opt_batch_size); - xsk->outstanding_tx += opt_batch_size; - frame_nb += opt_batch_size; + xsk_ring_prod__submit(&xsk->tx, batch_size); + xsk->outstanding_tx += batch_size; + frame_nb += batch_size; frame_nb %= NUM_FRAMES; - complete_tx_only(xsk); + complete_tx_only(xsk, batch_size); +} + +static inline int get_batch_size(int pkt_cnt) +{ + if (!opt_pkt_count) + return opt_batch_size; + + if (pkt_cnt + opt_batch_size <= opt_pkt_count) + return opt_batch_size; + + return opt_pkt_count - pkt_cnt; +} + +static void complete_tx_only_all(void) +{ + bool pending; + int i; + + do { + pending = false; + for (i = 0; i < num_socks; i++) { + if (xsks[i]->outstanding_tx) { + complete_tx_only(xsks[i], opt_batch_size); + pending = !!xsks[i]->outstanding_tx; + } + } + } while (pending); } static void tx_only_all(void) { struct pollfd fds[MAX_SOCKS] = {}; u32 frame_nb[MAX_SOCKS] = {}; + int pkt_cnt = 0; int i, ret; for (i = 0; i < num_socks; i++) { @@ -692,7 +728,9 @@ static void tx_only_all(void) fds[0].events = POLLOUT; } - for (;;) { + while ((opt_pkt_count && pkt_cnt < opt_pkt_count) || !opt_pkt_count) { + int batch_size = get_batch_size(pkt_cnt); + if (opt_poll) { ret = poll(fds, num_socks, opt_timeout); if (ret <= 0) @@ -703,11 +741,16 @@ static void tx_only_all(void) } for (i = 0; i < num_socks; i++) - tx_only(xsks[i], frame_nb[i]); + tx_only(xsks[i], frame_nb[i], batch_size); + + pkt_cnt += batch_size; if (benchmark_done) break; } + + if (opt_pkt_count) + complete_tx_only_all(); } static void l2fwd(struct xsk_socket_info *xsk, struct pollfd *fds) @@ -900,6 +943,8 @@ int main(int argc, char **argv) else l2fwd_all(); + benchmark_done = true; + pthread_join(pt, NULL); xdpsock_cleanup(); -- GitLab From 4a3c23ae3acc6185a9d418830f22672a52ff986a Mon Sep 17 00:00:00 2001 From: Jay Jayatheerthan Date: Fri, 20 Dec 2019 14:25:29 +0530 Subject: [PATCH 1202/1437] samples/bpf: xdpsock: Add option to specify tx packet size New option '-s' or '--tx-pkt-size' has been added to specify the transmit packet size. The packet size ranges from 47 to 4096 bytes. When this option is not provided, it defaults to 64 byte packet. The code uses struct ethhdr, struct iphdr and struct udphdr to form the transmit packet. The MAC address, IP address and UDP ports are set to default values. The code calculates IP and UDP checksums before sending the packet. Checksum calculation code in Linux kernel is used for this purpose. The Ethernet FCS is not filled by the code. Signed-off-by: Jay Jayatheerthan Signed-off-by: Alexei Starovoitov Link: https://lore.kernel.org/bpf/20191220085530.4980-6-jay.jayatheerthan@intel.com --- samples/bpf/xdpsock_user.c | 276 +++++++++++++++++++++++++++++++++++-- 1 file changed, 265 insertions(+), 11 deletions(-) diff --git a/samples/bpf/xdpsock_user.c b/samples/bpf/xdpsock_user.c index f96ce3055d46a..2297158a32bd7 100644 --- a/samples/bpf/xdpsock_user.c +++ b/samples/bpf/xdpsock_user.c @@ -10,6 +10,9 @@ #include #include #include +#include +#include +#include #include #include #include @@ -45,11 +48,14 @@ #endif #define NUM_FRAMES (4 * 1024) +#define MIN_PKT_SIZE 64 #define DEBUG_HEXDUMP 0 typedef __u64 u64; typedef __u32 u32; +typedef __u16 u16; +typedef __u8 u8; static unsigned long prev_time; @@ -69,6 +75,8 @@ static unsigned long start_time; static bool benchmark_done; static u32 opt_batch_size = 64; static int opt_pkt_count; +static u16 opt_pkt_size = MIN_PKT_SIZE; +static u32 pkt_fill_pattern = 0x12345678; static int opt_poll; static int opt_interval = 1; static u32 opt_xdp_bind_flags = XDP_USE_NEED_WAKEUP; @@ -238,12 +246,6 @@ static void __exit_with_error(int error, const char *file, const char *func, #define exit_with_error(error) __exit_with_error(error, __FILE__, __func__, \ __LINE__) -static const char pkt_data[] = - "\x3c\xfd\xfe\x9e\x7f\x71\xec\xb1\xd7\x98\x3a\xc0\x08\x00\x45\x00" - "\x00\x2e\x00\x00\x00\x00\x40\x11\x88\x97\x05\x08\x07\x08\xc8\x14" - "\x1e\x04\x10\x92\x10\x92\x00\x1a\x6d\xa3\x34\x33\x1f\x69\x40\x6b" - "\x54\x59\xb6\x14\x2d\x11\x44\xbf\xaf\xd9\xbe\xaa"; - static void swap_mac_addresses(void *data) { struct ether_header *eth = (struct ether_header *)data; @@ -291,10 +293,243 @@ static void hex_dump(void *pkt, size_t length, u64 addr) printf("\n"); } +static void *memset32_htonl(void *dest, u32 val, u32 size) +{ + u32 *ptr = (u32 *)dest; + int i; + + val = htonl(val); + + for (i = 0; i < (size & (~0x3)); i += 4) + ptr[i >> 2] = val; + + for (; i < size; i++) + ((char *)dest)[i] = ((char *)&val)[i & 3]; + + return dest; +} + +/* + * This function code has been taken from + * Linux kernel lib/checksum.c + */ +static inline unsigned short from32to16(unsigned int x) +{ + /* add up 16-bit and 16-bit for 16+c bit */ + x = (x & 0xffff) + (x >> 16); + /* add up carry.. */ + x = (x & 0xffff) + (x >> 16); + return x; +} + +/* + * This function code has been taken from + * Linux kernel lib/checksum.c + */ +static unsigned int do_csum(const unsigned char *buff, int len) +{ + unsigned int result = 0; + int odd; + + if (len <= 0) + goto out; + odd = 1 & (unsigned long)buff; + if (odd) { +#ifdef __LITTLE_ENDIAN + result += (*buff << 8); +#else + result = *buff; +#endif + len--; + buff++; + } + if (len >= 2) { + if (2 & (unsigned long)buff) { + result += *(unsigned short *)buff; + len -= 2; + buff += 2; + } + if (len >= 4) { + const unsigned char *end = buff + + ((unsigned int)len & ~3); + unsigned int carry = 0; + + do { + unsigned int w = *(unsigned int *)buff; + + buff += 4; + result += carry; + result += w; + carry = (w > result); + } while (buff < end); + result += carry; + result = (result & 0xffff) + (result >> 16); + } + if (len & 2) { + result += *(unsigned short *)buff; + buff += 2; + } + } + if (len & 1) +#ifdef __LITTLE_ENDIAN + result += *buff; +#else + result += (*buff << 8); +#endif + result = from32to16(result); + if (odd) + result = ((result >> 8) & 0xff) | ((result & 0xff) << 8); +out: + return result; +} + +__sum16 ip_fast_csum(const void *iph, unsigned int ihl); + +/* + * This is a version of ip_compute_csum() optimized for IP headers, + * which always checksum on 4 octet boundaries. + * This function code has been taken from + * Linux kernel lib/checksum.c + */ +__sum16 ip_fast_csum(const void *iph, unsigned int ihl) +{ + return (__force __sum16)~do_csum(iph, ihl * 4); +} + +/* + * Fold a partial checksum + * This function code has been taken from + * Linux kernel include/asm-generic/checksum.h + */ +static inline __sum16 csum_fold(__wsum csum) +{ + u32 sum = (__force u32)csum; + + sum = (sum & 0xffff) + (sum >> 16); + sum = (sum & 0xffff) + (sum >> 16); + return (__force __sum16)~sum; +} + +/* + * This function code has been taken from + * Linux kernel lib/checksum.c + */ +static inline u32 from64to32(u64 x) +{ + /* add up 32-bit and 32-bit for 32+c bit */ + x = (x & 0xffffffff) + (x >> 32); + /* add up carry.. */ + x = (x & 0xffffffff) + (x >> 32); + return (u32)x; +} + +__wsum csum_tcpudp_nofold(__be32 saddr, __be32 daddr, + __u32 len, __u8 proto, __wsum sum); + +/* + * This function code has been taken from + * Linux kernel lib/checksum.c + */ +__wsum csum_tcpudp_nofold(__be32 saddr, __be32 daddr, + __u32 len, __u8 proto, __wsum sum) +{ + unsigned long long s = (__force u32)sum; + + s += (__force u32)saddr; + s += (__force u32)daddr; +#ifdef __BIG_ENDIAN__ + s += proto + len; +#else + s += (proto + len) << 8; +#endif + return (__force __wsum)from64to32(s); +} + +/* + * This function has been taken from + * Linux kernel include/asm-generic/checksum.h + */ +static inline __sum16 +csum_tcpudp_magic(__be32 saddr, __be32 daddr, __u32 len, + __u8 proto, __wsum sum) +{ + return csum_fold(csum_tcpudp_nofold(saddr, daddr, len, proto, sum)); +} + +static inline u16 udp_csum(u32 saddr, u32 daddr, u32 len, + u8 proto, u16 *udp_pkt) +{ + u32 csum = 0; + u32 cnt = 0; + + /* udp hdr and data */ + for (; cnt < len; cnt += 2) + csum += udp_pkt[cnt >> 1]; + + return csum_tcpudp_magic(saddr, daddr, len, proto, csum); +} + +#define ETH_FCS_SIZE 4 + +#define PKT_HDR_SIZE (sizeof(struct ethhdr) + sizeof(struct iphdr) + \ + sizeof(struct udphdr)) + +#define PKT_SIZE (opt_pkt_size - ETH_FCS_SIZE) +#define IP_PKT_SIZE (PKT_SIZE - sizeof(struct ethhdr)) +#define UDP_PKT_SIZE (IP_PKT_SIZE - sizeof(struct iphdr)) +#define UDP_PKT_DATA_SIZE (UDP_PKT_SIZE - sizeof(struct udphdr)) + +static u8 pkt_data[XSK_UMEM__DEFAULT_FRAME_SIZE]; + +static void gen_eth_hdr_data(void) +{ + struct udphdr *udp_hdr = (struct udphdr *)(pkt_data + + sizeof(struct ethhdr) + + sizeof(struct iphdr)); + struct iphdr *ip_hdr = (struct iphdr *)(pkt_data + + sizeof(struct ethhdr)); + struct ethhdr *eth_hdr = (struct ethhdr *)pkt_data; + + /* ethernet header */ + memcpy(eth_hdr->h_dest, "\x3c\xfd\xfe\x9e\x7f\x71", ETH_ALEN); + memcpy(eth_hdr->h_source, "\xec\xb1\xd7\x98\x3a\xc0", ETH_ALEN); + eth_hdr->h_proto = htons(ETH_P_IP); + + /* IP header */ + ip_hdr->version = IPVERSION; + ip_hdr->ihl = 0x5; /* 20 byte header */ + ip_hdr->tos = 0x0; + ip_hdr->tot_len = htons(IP_PKT_SIZE); + ip_hdr->id = 0; + ip_hdr->frag_off = 0; + ip_hdr->ttl = IPDEFTTL; + ip_hdr->protocol = IPPROTO_UDP; + ip_hdr->saddr = htonl(0x0a0a0a10); + ip_hdr->daddr = htonl(0x0a0a0a20); + + /* IP header checksum */ + ip_hdr->check = 0; + ip_hdr->check = ip_fast_csum((const void *)ip_hdr, ip_hdr->ihl); + + /* UDP header */ + udp_hdr->source = htons(0x1000); + udp_hdr->dest = htons(0x1000); + udp_hdr->len = htons(UDP_PKT_SIZE); + + /* UDP data */ + memset32_htonl(pkt_data + PKT_HDR_SIZE, pkt_fill_pattern, + UDP_PKT_DATA_SIZE); + + /* UDP header checksum */ + udp_hdr->check = 0; + udp_hdr->check = udp_csum(ip_hdr->saddr, ip_hdr->daddr, UDP_PKT_SIZE, + IPPROTO_UDP, (u16 *)udp_hdr); +} + static void gen_eth_frame(struct xsk_umem_info *umem, u64 addr) { memcpy(xsk_umem__get_data(umem->buffer, addr), pkt_data, - sizeof(pkt_data) - 1); + PKT_SIZE); } static struct xsk_umem_info *xsk_configure_umem(void *buffer, u64 size) @@ -394,6 +629,7 @@ static struct option long_options[] = { {"duration", required_argument, 0, 'd'}, {"batch-size", required_argument, 0, 'b'}, {"tx-pkt-count", required_argument, 0, 'C'}, + {"tx-pkt-size", required_argument, 0, 's'}, {0, 0, 0, 0} }; @@ -424,9 +660,14 @@ static void usage(const char *prog) " packets. Default: %d\n" " -C, --tx-pkt-count=n Number of packets to send.\n" " Default: Continuous packets.\n" + " -s, --tx-pkt-size=n Transmit packet size.\n" + " (Default: %d bytes)\n" + " Min size: %d, Max size %d.\n" "\n"; fprintf(stderr, str, prog, XSK_UMEM__DEFAULT_FRAME_SIZE, - opt_batch_size); + opt_batch_size, MIN_PKT_SIZE, MIN_PKT_SIZE, + XSK_UMEM__DEFAULT_FRAME_SIZE); + exit(EXIT_FAILURE); } @@ -437,7 +678,7 @@ static void parse_command_line(int argc, char **argv) opterr = 0; for (;;) { - c = getopt_long(argc, argv, "Frtli:q:pSNn:czf:muMd:b:C:", + c = getopt_long(argc, argv, "Frtli:q:pSNn:czf:muMd:b:C:s:", long_options, &option_index); if (c == -1) break; @@ -505,6 +746,16 @@ static void parse_command_line(int argc, char **argv) case 'C': opt_pkt_count = atoi(optarg); break; + case 's': + opt_pkt_size = atoi(optarg); + if (opt_pkt_size > (XSK_UMEM__DEFAULT_FRAME_SIZE) || + opt_pkt_size < MIN_PKT_SIZE) { + fprintf(stderr, + "ERROR: Invalid frame size %d\n", + opt_pkt_size); + usage(basename(argv[0])); + } + break; default: usage(basename(argv[0])); } @@ -679,7 +930,7 @@ static void tx_only(struct xsk_socket_info *xsk, u32 frame_nb, int batch_size) struct xdp_desc *tx_desc = xsk_ring_prod__tx_desc(&xsk->tx, idx + i); tx_desc->addr = (frame_nb + i) << XSK_UMEM__DEFAULT_FRAME_SHIFT; - tx_desc->len = sizeof(pkt_data) - 1; + tx_desc->len = PKT_SIZE; } xsk_ring_prod__submit(&xsk->tx, batch_size); @@ -916,9 +1167,12 @@ int main(int argc, char **argv) for (i = 0; i < opt_num_xsks; i++) xsks[num_socks++] = xsk_configure_socket(umem, rx, tx); - if (opt_bench == BENCH_TXONLY) + if (opt_bench == BENCH_TXONLY) { + gen_eth_hdr_data(); + for (i = 0; i < NUM_FRAMES; i++) gen_eth_frame(umem, i * opt_xsk_frame_size); + } if (opt_num_xsks > 1 && opt_bench != BENCH_TXONLY) enter_xsks_into_map(obj); -- GitLab From 46e3268eaaca9f8a0f145872b96fe6d54a232890 Mon Sep 17 00:00:00 2001 From: Jay Jayatheerthan Date: Fri, 20 Dec 2019 14:25:30 +0530 Subject: [PATCH 1203/1437] samples/bpf: xdpsock: Add option to specify transmit fill pattern The UDP payload fill pattern can be specified using '-P' or '--tx-pkt-pattern' option. It is an unsigned 32 bit field and defaulted to 0x12345678. The IP and UDP checksum is calculated by the code as per the content of the packet before transmission. Signed-off-by: Jay Jayatheerthan Signed-off-by: Alexei Starovoitov Link: https://lore.kernel.org/bpf/20191220085530.4980-7-jay.jayatheerthan@intel.com --- samples/bpf/xdpsock_user.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/samples/bpf/xdpsock_user.c b/samples/bpf/xdpsock_user.c index 2297158a32bd7..d74c4c83fc939 100644 --- a/samples/bpf/xdpsock_user.c +++ b/samples/bpf/xdpsock_user.c @@ -76,7 +76,7 @@ static bool benchmark_done; static u32 opt_batch_size = 64; static int opt_pkt_count; static u16 opt_pkt_size = MIN_PKT_SIZE; -static u32 pkt_fill_pattern = 0x12345678; +static u32 opt_pkt_fill_pattern = 0x12345678; static int opt_poll; static int opt_interval = 1; static u32 opt_xdp_bind_flags = XDP_USE_NEED_WAKEUP; @@ -517,7 +517,7 @@ static void gen_eth_hdr_data(void) udp_hdr->len = htons(UDP_PKT_SIZE); /* UDP data */ - memset32_htonl(pkt_data + PKT_HDR_SIZE, pkt_fill_pattern, + memset32_htonl(pkt_data + PKT_HDR_SIZE, opt_pkt_fill_pattern, UDP_PKT_DATA_SIZE); /* UDP header checksum */ @@ -630,6 +630,7 @@ static struct option long_options[] = { {"batch-size", required_argument, 0, 'b'}, {"tx-pkt-count", required_argument, 0, 'C'}, {"tx-pkt-size", required_argument, 0, 's'}, + {"tx-pkt-pattern", required_argument, 0, 'P'}, {0, 0, 0, 0} }; @@ -663,10 +664,11 @@ static void usage(const char *prog) " -s, --tx-pkt-size=n Transmit packet size.\n" " (Default: %d bytes)\n" " Min size: %d, Max size %d.\n" + " -P, --tx-pkt-pattern=nPacket fill pattern. Default: 0x%x\n" "\n"; fprintf(stderr, str, prog, XSK_UMEM__DEFAULT_FRAME_SIZE, opt_batch_size, MIN_PKT_SIZE, MIN_PKT_SIZE, - XSK_UMEM__DEFAULT_FRAME_SIZE); + XSK_UMEM__DEFAULT_FRAME_SIZE, opt_pkt_fill_pattern); exit(EXIT_FAILURE); } @@ -678,7 +680,7 @@ static void parse_command_line(int argc, char **argv) opterr = 0; for (;;) { - c = getopt_long(argc, argv, "Frtli:q:pSNn:czf:muMd:b:C:s:", + c = getopt_long(argc, argv, "Frtli:q:pSNn:czf:muMd:b:C:s:P:", long_options, &option_index); if (c == -1) break; @@ -756,6 +758,9 @@ static void parse_command_line(int argc, char **argv) usage(basename(argv[0])); } break; + case 'P': + opt_pkt_fill_pattern = strtol(optarg, NULL, 16); + break; default: usage(basename(argv[0])); } -- GitLab From f9e6bfdbaf0cf304d72c70a05d81acac01a04f48 Mon Sep 17 00:00:00 2001 From: Jesper Dangaard Brouer Date: Fri, 20 Dec 2019 17:19:36 +0100 Subject: [PATCH 1204/1437] samples/bpf: Xdp_redirect_cpu fix missing tracepoint attach When sample xdp_redirect_cpu was converted to use libbpf, the tracepoints used by this sample were not getting attached automatically like with bpf_load.c. The BPF-maps was still getting loaded, thus nobody notice that the tracepoints were not updating these maps. This fix doesn't use the new skeleton code, as this bug was introduced in v5.1 and stable might want to backport this. E.g. Red Hat QA uses this sample as part of their testing. Fixes: bbaf6029c49c ("samples/bpf: Convert XDP samples to libbpf usage") Signed-off-by: Jesper Dangaard Brouer Signed-off-by: Alexei Starovoitov Acked-by: Andrii Nakryiko Link: https://lore.kernel.org/bpf/157685877642.26195.2798780195186786841.stgit@firesoul --- samples/bpf/xdp_redirect_cpu_user.c | 59 +++++++++++++++++++++++++++-- 1 file changed, 55 insertions(+), 4 deletions(-) diff --git a/samples/bpf/xdp_redirect_cpu_user.c b/samples/bpf/xdp_redirect_cpu_user.c index 72af628529b51..79a2fb7d16cb7 100644 --- a/samples/bpf/xdp_redirect_cpu_user.c +++ b/samples/bpf/xdp_redirect_cpu_user.c @@ -16,6 +16,10 @@ static const char *__doc__ = #include #include #include +#include + +#define __must_check +#include #include #include @@ -46,6 +50,10 @@ static int cpus_count_map_fd; static int cpus_iterator_map_fd; static int exception_cnt_map_fd; +#define NUM_TP 5 +struct bpf_link *tp_links[NUM_TP] = { 0 }; +static int tp_cnt = 0; + /* Exit return codes */ #define EXIT_OK 0 #define EXIT_FAIL 1 @@ -88,6 +96,10 @@ static void int_exit(int sig) printf("program on interface changed, not removing\n"); } } + /* Detach tracepoints */ + while (tp_cnt) + bpf_link__destroy(tp_links[--tp_cnt]); + exit(EXIT_OK); } @@ -588,23 +600,61 @@ static void stats_poll(int interval, bool use_separators, char *prog_name, free_stats_record(prev); } +static struct bpf_link * attach_tp(struct bpf_object *obj, + const char *tp_category, + const char* tp_name) +{ + struct bpf_program *prog; + struct bpf_link *link; + char sec_name[PATH_MAX]; + int len; + + len = snprintf(sec_name, PATH_MAX, "tracepoint/%s/%s", + tp_category, tp_name); + if (len < 0) + exit(EXIT_FAIL); + + prog = bpf_object__find_program_by_title(obj, sec_name); + if (!prog) { + fprintf(stderr, "ERR: finding progsec: %s\n", sec_name); + exit(EXIT_FAIL_BPF); + } + + link = bpf_program__attach_tracepoint(prog, tp_category, tp_name); + if (IS_ERR(link)) + exit(EXIT_FAIL_BPF); + + return link; +} + +static void init_tracepoints(struct bpf_object *obj) { + tp_links[tp_cnt++] = attach_tp(obj, "xdp", "xdp_redirect_err"); + tp_links[tp_cnt++] = attach_tp(obj, "xdp", "xdp_redirect_map_err"); + tp_links[tp_cnt++] = attach_tp(obj, "xdp", "xdp_exception"); + tp_links[tp_cnt++] = attach_tp(obj, "xdp", "xdp_cpumap_enqueue"); + tp_links[tp_cnt++] = attach_tp(obj, "xdp", "xdp_cpumap_kthread"); +} + static int init_map_fds(struct bpf_object *obj) { - cpu_map_fd = bpf_object__find_map_fd_by_name(obj, "cpu_map"); - rx_cnt_map_fd = bpf_object__find_map_fd_by_name(obj, "rx_cnt"); + /* Maps updated by tracepoints */ redirect_err_cnt_map_fd = bpf_object__find_map_fd_by_name(obj, "redirect_err_cnt"); + exception_cnt_map_fd = + bpf_object__find_map_fd_by_name(obj, "exception_cnt"); cpumap_enqueue_cnt_map_fd = bpf_object__find_map_fd_by_name(obj, "cpumap_enqueue_cnt"); cpumap_kthread_cnt_map_fd = bpf_object__find_map_fd_by_name(obj, "cpumap_kthread_cnt"); + + /* Maps used by XDP */ + rx_cnt_map_fd = bpf_object__find_map_fd_by_name(obj, "rx_cnt"); + cpu_map_fd = bpf_object__find_map_fd_by_name(obj, "cpu_map"); cpus_available_map_fd = bpf_object__find_map_fd_by_name(obj, "cpus_available"); cpus_count_map_fd = bpf_object__find_map_fd_by_name(obj, "cpus_count"); cpus_iterator_map_fd = bpf_object__find_map_fd_by_name(obj, "cpus_iterator"); - exception_cnt_map_fd = - bpf_object__find_map_fd_by_name(obj, "exception_cnt"); if (cpu_map_fd < 0 || rx_cnt_map_fd < 0 || redirect_err_cnt_map_fd < 0 || cpumap_enqueue_cnt_map_fd < 0 || @@ -662,6 +712,7 @@ int main(int argc, char **argv) strerror(errno)); return EXIT_FAIL; } + init_tracepoints(obj); if (init_map_fds(obj) < 0) { fprintf(stderr, "bpf_object__find_map_fd_by_name failed\n"); return EXIT_FAIL; -- GitLab From dba261f35d16a8e4b70962d0f8391290510a8201 Mon Sep 17 00:00:00 2001 From: Oleksij Rempel Date: Wed, 18 Dec 2019 09:02:12 +0100 Subject: [PATCH 1205/1437] dt-bindings: net: dsa: qca, ar9331 switch documentation Atheros AR9331 has built-in 5 port switch. The switch can be configured to use all 5 or 4 ports. One of built-in PHYs can be used by first built-in ethernet controller or to be used directly by the switch over second ethernet controller. Signed-off-by: Oleksij Rempel Signed-off-by: David S. Miller --- .../devicetree/bindings/net/dsa/ar9331.txt | 148 ++++++++++++++++++ 1 file changed, 148 insertions(+) create mode 100644 Documentation/devicetree/bindings/net/dsa/ar9331.txt diff --git a/Documentation/devicetree/bindings/net/dsa/ar9331.txt b/Documentation/devicetree/bindings/net/dsa/ar9331.txt new file mode 100644 index 0000000000000..320607cbbb176 --- /dev/null +++ b/Documentation/devicetree/bindings/net/dsa/ar9331.txt @@ -0,0 +1,148 @@ +Atheros AR9331 built-in switch +============================= + +It is a switch built-in to Atheros AR9331 WiSoC and addressable over internal +MDIO bus. All PHYs are built-in as well. + +Required properties: + + - compatible: should be: "qca,ar9331-switch" + - reg: Address on the MII bus for the switch. + - resets : Must contain an entry for each entry in reset-names. + - reset-names : Must include the following entries: "switch" + - interrupt-parent: Phandle to the parent interrupt controller + - interrupts: IRQ line for the switch + - interrupt-controller: Indicates the switch is itself an interrupt + controller. This is used for the PHY interrupts. + - #interrupt-cells: must be 1 + - mdio: Container of PHY and devices on the switches MDIO bus. + +See Documentation/devicetree/bindings/net/dsa/dsa.txt for a list of additional +required and optional properties. +Examples: + +eth0: ethernet@19000000 { + compatible = "qca,ar9330-eth"; + reg = <0x19000000 0x200>; + interrupts = <4>; + + resets = <&rst 9>, <&rst 22>; + reset-names = "mac", "mdio"; + clocks = <&pll ATH79_CLK_AHB>, <&pll ATH79_CLK_AHB>; + clock-names = "eth", "mdio"; + + phy-mode = "mii"; + phy-handle = <&phy_port4>; +}; + +eth1: ethernet@1a000000 { + compatible = "qca,ar9330-eth"; + reg = <0x1a000000 0x200>; + interrupts = <5>; + resets = <&rst 13>, <&rst 23>; + reset-names = "mac", "mdio"; + clocks = <&pll ATH79_CLK_AHB>, <&pll ATH79_CLK_AHB>; + clock-names = "eth", "mdio"; + + phy-mode = "gmii"; + + fixed-link { + speed = <1000>; + full-duplex; + }; + + mdio { + #address-cells = <1>; + #size-cells = <0>; + + switch10: switch@10 { + #address-cells = <1>; + #size-cells = <0>; + + compatible = "qca,ar9331-switch"; + reg = <0x10>; + resets = <&rst 8>; + reset-names = "switch"; + + interrupt-parent = <&miscintc>; + interrupts = <12>; + + interrupt-controller; + #interrupt-cells = <1>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + switch_port0: port@0 { + reg = <0x0>; + label = "cpu"; + ethernet = <ð1>; + + phy-mode = "gmii"; + + fixed-link { + speed = <1000>; + full-duplex; + }; + }; + + switch_port1: port@1 { + reg = <0x1>; + phy-handle = <&phy_port0>; + phy-mode = "internal"; + }; + + switch_port2: port@2 { + reg = <0x2>; + phy-handle = <&phy_port1>; + phy-mode = "internal"; + }; + + switch_port3: port@3 { + reg = <0x3>; + phy-handle = <&phy_port2>; + phy-mode = "internal"; + }; + + switch_port4: port@4 { + reg = <0x4>; + phy-handle = <&phy_port3>; + phy-mode = "internal"; + }; + }; + + mdio { + #address-cells = <1>; + #size-cells = <0>; + + interrupt-parent = <&switch10>; + + phy_port0: phy@0 { + reg = <0x0>; + interrupts = <0>; + }; + + phy_port1: phy@1 { + reg = <0x1>; + interrupts = <0>; + }; + + phy_port2: phy@2 { + reg = <0x2>; + interrupts = <0>; + }; + + phy_port3: phy@3 { + reg = <0x3>; + interrupts = <0>; + }; + + phy_port4: phy@4 { + reg = <0x4>; + interrupts = <0>; + }; + }; + }; + }; +}; -- GitLab From 4eb7ae7a301d3586c3351e81d5c3cfe2304a1a6a Mon Sep 17 00:00:00 2001 From: Oleksij Rempel Date: Wed, 18 Dec 2019 09:02:13 +0100 Subject: [PATCH 1206/1437] MIPS: ath79: ar9331: add ar9331-switch node Add switch node supported by dsa ar9331 driver. Signed-off-by: Oleksij Rempel Signed-off-by: David S. Miller --- arch/mips/boot/dts/qca/ar9331.dtsi | 119 ++++++++++++++++++- arch/mips/boot/dts/qca/ar9331_dpt_module.dts | 13 ++ 2 files changed, 131 insertions(+), 1 deletion(-) diff --git a/arch/mips/boot/dts/qca/ar9331.dtsi b/arch/mips/boot/dts/qca/ar9331.dtsi index 5cfc9d347826a..8f5aed760abb2 100644 --- a/arch/mips/boot/dts/qca/ar9331.dtsi +++ b/arch/mips/boot/dts/qca/ar9331.dtsi @@ -126,6 +126,9 @@ clocks = <&pll ATH79_CLK_AHB>, <&pll ATH79_CLK_AHB>; clock-names = "eth", "mdio"; + phy-mode = "mii"; + phy-handle = <&phy_port4>; + status = "disabled"; }; @@ -133,13 +136,127 @@ compatible = "qca,ar9330-eth"; reg = <0x1a000000 0x200>; interrupts = <5>; - resets = <&rst 13>, <&rst 23>; reset-names = "mac", "mdio"; clocks = <&pll ATH79_CLK_AHB>, <&pll ATH79_CLK_AHB>; clock-names = "eth", "mdio"; + phy-mode = "gmii"; + status = "disabled"; + + fixed-link { + speed = <1000>; + full-duplex; + }; + + mdio { + #address-cells = <1>; + #size-cells = <0>; + + switch10: switch@10 { + #address-cells = <1>; + #size-cells = <0>; + + compatible = "qca,ar9331-switch"; + reg = <0x10>; + resets = <&rst 8>; + reset-names = "switch"; + + interrupt-parent = <&miscintc>; + interrupts = <12>; + + interrupt-controller; + #interrupt-cells = <1>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + switch_port0: port@0 { + reg = <0x0>; + label = "cpu"; + ethernet = <ð1>; + + phy-mode = "gmii"; + + fixed-link { + speed = <1000>; + full-duplex; + }; + }; + + switch_port1: port@1 { + reg = <0x1>; + phy-handle = <&phy_port0>; + phy-mode = "internal"; + + status = "disabled"; + }; + + switch_port2: port@2 { + reg = <0x2>; + phy-handle = <&phy_port1>; + phy-mode = "internal"; + + status = "disabled"; + }; + + switch_port3: port@3 { + reg = <0x3>; + phy-handle = <&phy_port2>; + phy-mode = "internal"; + + status = "disabled"; + }; + + switch_port4: port@4 { + reg = <0x4>; + phy-handle = <&phy_port3>; + phy-mode = "internal"; + + status = "disabled"; + }; + }; + + mdio { + #address-cells = <1>; + #size-cells = <0>; + + interrupt-parent = <&switch10>; + + phy_port0: phy@0 { + reg = <0x0>; + interrupts = <0>; + status = "disabled"; + }; + + phy_port1: phy@1 { + reg = <0x1>; + interrupts = <0>; + status = "disabled"; + }; + + phy_port2: phy@2 { + reg = <0x2>; + interrupts = <0>; + status = "disabled"; + }; + + phy_port3: phy@3 { + reg = <0x3>; + interrupts = <0>; + status = "disabled"; + }; + + phy_port4: phy@4 { + reg = <0x4>; + interrupts = <0>; + status = "disabled"; + }; + }; + }; + }; }; usb: usb@1b000100 { diff --git a/arch/mips/boot/dts/qca/ar9331_dpt_module.dts b/arch/mips/boot/dts/qca/ar9331_dpt_module.dts index 77bab823eb3b0..0f2b200448340 100644 --- a/arch/mips/boot/dts/qca/ar9331_dpt_module.dts +++ b/arch/mips/boot/dts/qca/ar9331_dpt_module.dts @@ -84,3 +84,16 @@ ð1 { status = "okay"; }; + +&switch_port1 { + label = "lan0"; + status = "okay"; +}; + +&phy_port0 { + status = "okay"; +}; + +&phy_port4 { + status = "okay"; +}; -- GitLab From 48fda74f0a9377ce2145ac5f06b6db0274880826 Mon Sep 17 00:00:00 2001 From: Oleksij Rempel Date: Wed, 18 Dec 2019 09:02:14 +0100 Subject: [PATCH 1207/1437] net: dsa: add support for Atheros AR9331 TAG format Add support for tag format used in Atheros AR9331 built-in switch. Reviewed-by: Vivien Didelot Reviewed-by: Andrew Lunn Signed-off-by: Oleksij Rempel Signed-off-by: David S. Miller --- include/net/dsa.h | 2 + net/dsa/Kconfig | 6 +++ net/dsa/Makefile | 1 + net/dsa/tag_ar9331.c | 96 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 105 insertions(+) create mode 100644 net/dsa/tag_ar9331.c diff --git a/include/net/dsa.h b/include/net/dsa.h index 6767dc3f66c00..da5578db228eb 100644 --- a/include/net/dsa.h +++ b/include/net/dsa.h @@ -43,6 +43,7 @@ struct phylink_link_state; #define DSA_TAG_PROTO_SJA1105_VALUE 13 #define DSA_TAG_PROTO_KSZ8795_VALUE 14 #define DSA_TAG_PROTO_OCELOT_VALUE 15 +#define DSA_TAG_PROTO_AR9331_VALUE 16 enum dsa_tag_protocol { DSA_TAG_PROTO_NONE = DSA_TAG_PROTO_NONE_VALUE, @@ -61,6 +62,7 @@ enum dsa_tag_protocol { DSA_TAG_PROTO_SJA1105 = DSA_TAG_PROTO_SJA1105_VALUE, DSA_TAG_PROTO_KSZ8795 = DSA_TAG_PROTO_KSZ8795_VALUE, DSA_TAG_PROTO_OCELOT = DSA_TAG_PROTO_OCELOT_VALUE, + DSA_TAG_PROTO_AR9331 = DSA_TAG_PROTO_AR9331_VALUE, }; struct packet_type; diff --git a/net/dsa/Kconfig b/net/dsa/Kconfig index 1e6c3cac11e61..92663dcb3aa2c 100644 --- a/net/dsa/Kconfig +++ b/net/dsa/Kconfig @@ -29,6 +29,12 @@ config NET_DSA_TAG_8021Q Drivers which use these helpers should select this as dependency. +config NET_DSA_TAG_AR9331 + tristate "Tag driver for Atheros AR9331 SoC with built-in switch" + help + Say Y or M if you want to enable support for tagging frames for + the Atheros AR9331 SoC with built-in switch. + config NET_DSA_TAG_BRCM_COMMON tristate default n diff --git a/net/dsa/Makefile b/net/dsa/Makefile index 9a482c38bdb14..108486cfdeefe 100644 --- a/net/dsa/Makefile +++ b/net/dsa/Makefile @@ -5,6 +5,7 @@ dsa_core-y += dsa.o dsa2.o master.o port.o slave.o switch.o # tagging formats obj-$(CONFIG_NET_DSA_TAG_8021Q) += tag_8021q.o +obj-$(CONFIG_NET_DSA_TAG_AR9331) += tag_ar9331.o obj-$(CONFIG_NET_DSA_TAG_BRCM_COMMON) += tag_brcm.o obj-$(CONFIG_NET_DSA_TAG_DSA) += tag_dsa.o obj-$(CONFIG_NET_DSA_TAG_EDSA) += tag_edsa.o diff --git a/net/dsa/tag_ar9331.c b/net/dsa/tag_ar9331.c new file mode 100644 index 0000000000000..466ffa92a4746 --- /dev/null +++ b/net/dsa/tag_ar9331.c @@ -0,0 +1,96 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2019 Pengutronix, Oleksij Rempel + */ + + +#include +#include + +#include "dsa_priv.h" + +#define AR9331_HDR_LEN 2 +#define AR9331_HDR_VERSION 1 + +#define AR9331_HDR_VERSION_MASK GENMASK(15, 14) +#define AR9331_HDR_PRIORITY_MASK GENMASK(13, 12) +#define AR9331_HDR_TYPE_MASK GENMASK(10, 8) +#define AR9331_HDR_BROADCAST BIT(7) +#define AR9331_HDR_FROM_CPU BIT(6) +/* AR9331_HDR_RESERVED - not used or may be version field. + * According to the AR8216 doc it should 0b10. On AR9331 it is 0b11 on RX path + * and should be set to 0b11 to make it work. + */ +#define AR9331_HDR_RESERVED_MASK GENMASK(5, 4) +#define AR9331_HDR_PORT_NUM_MASK GENMASK(3, 0) + +static struct sk_buff *ar9331_tag_xmit(struct sk_buff *skb, + struct net_device *dev) +{ + struct dsa_port *dp = dsa_slave_to_port(dev); + __le16 *phdr; + u16 hdr; + + if (skb_cow_head(skb, 0) < 0) + return NULL; + + phdr = skb_push(skb, AR9331_HDR_LEN); + + hdr = FIELD_PREP(AR9331_HDR_VERSION_MASK, AR9331_HDR_VERSION); + hdr |= AR9331_HDR_FROM_CPU | dp->index; + /* 0b10 for AR8216 and 0b11 for AR9331 */ + hdr |= AR9331_HDR_RESERVED_MASK; + + phdr[0] = cpu_to_le16(hdr); + + return skb; +} + +static struct sk_buff *ar9331_tag_rcv(struct sk_buff *skb, + struct net_device *ndev, + struct packet_type *pt) +{ + u8 ver, port; + u16 hdr; + + if (unlikely(!pskb_may_pull(skb, AR9331_HDR_LEN))) + return NULL; + + hdr = le16_to_cpu(*(__le16 *)skb_mac_header(skb)); + + ver = FIELD_GET(AR9331_HDR_VERSION_MASK, hdr); + if (unlikely(ver != AR9331_HDR_VERSION)) { + netdev_warn_once(ndev, "%s:%i wrong header version 0x%2x\n", + __func__, __LINE__, hdr); + return NULL; + } + + if (unlikely(hdr & AR9331_HDR_FROM_CPU)) { + netdev_warn_once(ndev, "%s:%i packet should not be from cpu 0x%2x\n", + __func__, __LINE__, hdr); + return NULL; + } + + skb_pull_rcsum(skb, AR9331_HDR_LEN); + + /* Get source port information */ + port = FIELD_GET(AR9331_HDR_PORT_NUM_MASK, hdr); + + skb->dev = dsa_master_find_slave(ndev, 0, port); + if (!skb->dev) + return NULL; + + return skb; +} + +static const struct dsa_device_ops ar9331_netdev_ops = { + .name = "ar9331", + .proto = DSA_TAG_PROTO_AR9331, + .xmit = ar9331_tag_xmit, + .rcv = ar9331_tag_rcv, + .overhead = AR9331_HDR_LEN, +}; + +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_AR9331); +module_dsa_tag_driver(ar9331_netdev_ops); -- GitLab From ec6698c272debe16c8825c88a5b3c3f68a12ef4a Mon Sep 17 00:00:00 2001 From: Oleksij Rempel Date: Wed, 18 Dec 2019 09:02:15 +0100 Subject: [PATCH 1208/1437] net: dsa: add support for Atheros AR9331 built-in switch Provide basic support for Atheros AR9331 built-in switch. So far it works as port multiplexer without any hardware offloading support. Reviewed-by: Andrew Lunn Reviewed-by: Vivien Didelot Signed-off-by: Oleksij Rempel Signed-off-by: David S. Miller --- drivers/net/dsa/Kconfig | 2 + drivers/net/dsa/Makefile | 1 + drivers/net/dsa/qca/Kconfig | 9 + drivers/net/dsa/qca/Makefile | 2 + drivers/net/dsa/qca/ar9331.c | 855 +++++++++++++++++++++++++++++++++++ 5 files changed, 869 insertions(+) create mode 100644 drivers/net/dsa/qca/Kconfig create mode 100644 drivers/net/dsa/qca/Makefile create mode 100644 drivers/net/dsa/qca/ar9331.c diff --git a/drivers/net/dsa/Kconfig b/drivers/net/dsa/Kconfig index c7667645f04ae..cbd74a72d0a19 100644 --- a/drivers/net/dsa/Kconfig +++ b/drivers/net/dsa/Kconfig @@ -54,6 +54,8 @@ source "drivers/net/dsa/mv88e6xxx/Kconfig" source "drivers/net/dsa/ocelot/Kconfig" +source "drivers/net/dsa/qca/Kconfig" + source "drivers/net/dsa/sja1105/Kconfig" config NET_DSA_QCA8K diff --git a/drivers/net/dsa/Makefile b/drivers/net/dsa/Makefile index 9d384a32b3a2c..4a943ccc2ca43 100644 --- a/drivers/net/dsa/Makefile +++ b/drivers/net/dsa/Makefile @@ -21,4 +21,5 @@ obj-y += b53/ obj-y += microchip/ obj-y += mv88e6xxx/ obj-y += ocelot/ +obj-y += qca/ obj-y += sja1105/ diff --git a/drivers/net/dsa/qca/Kconfig b/drivers/net/dsa/qca/Kconfig new file mode 100644 index 0000000000000..e3c8d715a18f4 --- /dev/null +++ b/drivers/net/dsa/qca/Kconfig @@ -0,0 +1,9 @@ +# SPDX-License-Identifier: GPL-2.0-only +config NET_DSA_AR9331 + tristate "Qualcomm Atheros AR9331 Ethernet switch support" + depends on NET_DSA + select NET_DSA_TAG_AR9331 + select REGMAP + ---help--- + This enables support for the Qualcomm Atheros AR9331 built-in Ethernet + switch. diff --git a/drivers/net/dsa/qca/Makefile b/drivers/net/dsa/qca/Makefile new file mode 100644 index 0000000000000..274022319066d --- /dev/null +++ b/drivers/net/dsa/qca/Makefile @@ -0,0 +1,2 @@ +# SPDX-License-Identifier: GPL-2.0-only +obj-$(CONFIG_NET_DSA_AR9331) += ar9331.o diff --git a/drivers/net/dsa/qca/ar9331.c b/drivers/net/dsa/qca/ar9331.c new file mode 100644 index 0000000000000..0d1a7cd85fe8f --- /dev/null +++ b/drivers/net/dsa/qca/ar9331.c @@ -0,0 +1,855 @@ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright (c) 2019 Pengutronix, Oleksij Rempel +/* + * +----------------------+ + * GMAC1----RGMII----|--MAC0 | + * \---MDIO1----|--REGs |----MDIO3----\ + * | | | +------+ + * | | +--| | + * | MAC1-|----RMII--M-----| PHY0 |-o P0 + * | | | | +------+ + * | | | +--| | + * | MAC2-|----RMII--------| PHY1 |-o P1 + * | | | | +------+ + * | | | +--| | + * | MAC3-|----RMII--------| PHY2 |-o P2 + * | | | | +------+ + * | | | +--| | + * | MAC4-|----RMII--------| PHY3 |-o P3 + * | | | | +------+ + * | | | +--| | + * | MAC5-|--+-RMII--M-----|-PHY4-|-o P4 + * | | | | +------+ + * +----------------------+ | \--CFG_SW_PHY_SWAP + * GMAC0---------------RMII--------------------/ \-CFG_SW_PHY_ADDR_SWAP + * \---MDIO0--NC + * + * GMAC0 and MAC5 are connected together and use same PHY. Depending on + * configuration it can be PHY4 (default) or PHY0. Only GMAC0 or MAC5 can be + * used at same time. If GMAC0 is used (default) then MAC5 should be disabled. + * + * CFG_SW_PHY_SWAP - swap connections of PHY0 and PHY4. If this bit is not set + * PHY4 is connected to GMAC0/MAC5 bundle and PHY0 is connected to MAC1. If this + * bit is set, PHY4 is connected to MAC1 and PHY0 is connected to GMAC0/MAC5 + * bundle. + * + * CFG_SW_PHY_ADDR_SWAP - swap addresses of PHY0 and PHY4 + * + * CFG_SW_PHY_SWAP and CFG_SW_PHY_ADDR_SWAP are part of SoC specific register + * set and not related to switch internal registers. + */ + +#include +#include +#include +#include +#include +#include +#include + +#define AR9331_SW_NAME "ar9331_switch" +#define AR9331_SW_PORTS 6 + +/* dummy reg to change page */ +#define AR9331_SW_REG_PAGE 0x40000 + +/* Global Interrupt */ +#define AR9331_SW_REG_GINT 0x10 +#define AR9331_SW_REG_GINT_MASK 0x14 +#define AR9331_SW_GINT_PHY_INT BIT(2) + +#define AR9331_SW_REG_FLOOD_MASK 0x2c +#define AR9331_SW_FLOOD_MASK_BROAD_TO_CPU BIT(26) + +#define AR9331_SW_REG_GLOBAL_CTRL 0x30 +#define AR9331_SW_GLOBAL_CTRL_MFS_M GENMASK(13, 0) + +#define AR9331_SW_REG_MDIO_CTRL 0x98 +#define AR9331_SW_MDIO_CTRL_BUSY BIT(31) +#define AR9331_SW_MDIO_CTRL_MASTER_EN BIT(30) +#define AR9331_SW_MDIO_CTRL_CMD_READ BIT(27) +#define AR9331_SW_MDIO_CTRL_PHY_ADDR_M GENMASK(25, 21) +#define AR9331_SW_MDIO_CTRL_REG_ADDR_M GENMASK(20, 16) +#define AR9331_SW_MDIO_CTRL_DATA_M GENMASK(16, 0) + +#define AR9331_SW_REG_PORT_STATUS(_port) (0x100 + (_port) * 0x100) + +/* FLOW_LINK_EN - enable mac flow control config auto-neg with phy. + * If not set, mac can be config by software. + */ +#define AR9331_SW_PORT_STATUS_FLOW_LINK_EN BIT(12) + +/* LINK_EN - If set, MAC is configured from PHY link status. + * If not set, MAC should be configured by software. + */ +#define AR9331_SW_PORT_STATUS_LINK_EN BIT(9) +#define AR9331_SW_PORT_STATUS_DUPLEX_MODE BIT(6) +#define AR9331_SW_PORT_STATUS_RX_FLOW_EN BIT(5) +#define AR9331_SW_PORT_STATUS_TX_FLOW_EN BIT(4) +#define AR9331_SW_PORT_STATUS_RXMAC BIT(3) +#define AR9331_SW_PORT_STATUS_TXMAC BIT(2) +#define AR9331_SW_PORT_STATUS_SPEED_M GENMASK(1, 0) +#define AR9331_SW_PORT_STATUS_SPEED_1000 2 +#define AR9331_SW_PORT_STATUS_SPEED_100 1 +#define AR9331_SW_PORT_STATUS_SPEED_10 0 + +#define AR9331_SW_PORT_STATUS_MAC_MASK \ + (AR9331_SW_PORT_STATUS_TXMAC | AR9331_SW_PORT_STATUS_RXMAC) + +#define AR9331_SW_PORT_STATUS_LINK_MASK \ + (AR9331_SW_PORT_STATUS_LINK_EN | AR9331_SW_PORT_STATUS_FLOW_LINK_EN | \ + AR9331_SW_PORT_STATUS_DUPLEX_MODE | \ + AR9331_SW_PORT_STATUS_RX_FLOW_EN | AR9331_SW_PORT_STATUS_TX_FLOW_EN | \ + AR9331_SW_PORT_STATUS_SPEED_M) + +/* Phy bypass mode + * ------------------------------------------------------------------------ + * Bit: | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |10 |11 |12 |13 |14 |15 | + * + * real | start | OP | PhyAddr | Reg Addr | TA | + * atheros| start | OP | 2'b00 |PhyAdd[2:0]| Reg Addr[4:0] | TA | + * + * + * Bit: |16 |17 |18 |19 |20 |21 |22 |23 |24 |25 |26 |27 |28 |29 |30 |31 | + * real | Data | + * atheros| Data | + * + * ------------------------------------------------------------------------ + * Page address mode + * ------------------------------------------------------------------------ + * Bit: | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |10 |11 |12 |13 |14 |15 | + * real | start | OP | PhyAddr | Reg Addr | TA | + * atheros| start | OP | 2'b11 | 8'b0 | TA | + * + * Bit: |16 |17 |18 |19 |20 |21 |22 |23 |24 |25 |26 |27 |28 |29 |30 |31 | + * real | Data | + * atheros| | Page [9:0] | + */ +/* In case of Page Address mode, Bit[18:9] of 32 bit register address should be + * written to bits[9:0] of mdio data register. + */ +#define AR9331_SW_ADDR_PAGE GENMASK(18, 9) + +/* ------------------------------------------------------------------------ + * Normal register access mode + * ------------------------------------------------------------------------ + * Bit: | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |10 |11 |12 |13 |14 |15 | + * real | start | OP | PhyAddr | Reg Addr | TA | + * atheros| start | OP | 2'b10 | low_addr[7:0] | TA | + * + * Bit: |16 |17 |18 |19 |20 |21 |22 |23 |24 |25 |26 |27 |28 |29 |30 |31 | + * real | Data | + * atheros| Data | + * ------------------------------------------------------------------------ + */ +#define AR9331_SW_LOW_ADDR_PHY GENMASK(8, 6) +#define AR9331_SW_LOW_ADDR_REG GENMASK(5, 1) + +#define AR9331_SW_MDIO_PHY_MODE_M GENMASK(4, 3) +#define AR9331_SW_MDIO_PHY_MODE_PAGE 3 +#define AR9331_SW_MDIO_PHY_MODE_REG 2 +#define AR9331_SW_MDIO_PHY_MODE_BYPASS 0 +#define AR9331_SW_MDIO_PHY_ADDR_M GENMASK(2, 0) + +/* Empirical determined values */ +#define AR9331_SW_MDIO_POLL_SLEEP_US 1 +#define AR9331_SW_MDIO_POLL_TIMEOUT_US 20 + +struct ar9331_sw_priv { + struct device *dev; + struct dsa_switch ds; + struct dsa_switch_ops ops; + struct irq_domain *irqdomain; + struct mii_bus *mbus; /* mdio master */ + struct mii_bus *sbus; /* mdio slave */ + struct regmap *regmap; + struct reset_control *sw_reset; +}; + +/* Warning: switch reset will reset last AR9331_SW_MDIO_PHY_MODE_PAGE request + * If some kind of optimization is used, the request should be repeated. + */ +static int ar9331_sw_reset(struct ar9331_sw_priv *priv) +{ + int ret; + + ret = reset_control_assert(priv->sw_reset); + if (ret) + goto error; + + /* AR9331 doc do not provide any information about proper reset + * sequence. The AR8136 (the closes switch to the AR9331) doc says: + * reset duration should be greater than 10ms. So, let's use this value + * for now. + */ + usleep_range(10000, 15000); + ret = reset_control_deassert(priv->sw_reset); + if (ret) + goto error; + /* There is no information on how long should we wait after reset. + * AR8136 has an EEPROM and there is an Interrupt for EEPROM load + * status. AR9331 has no EEPROM support. + * For now, do not wait. In case AR8136 will be needed, the after + * reset delay can be added as well. + */ + + return 0; +error: + dev_err_ratelimited(priv->dev, "%s: %i\n", __func__, ret); + return ret; +} + +static int ar9331_sw_mbus_write(struct mii_bus *mbus, int port, int regnum, + u16 data) +{ + struct ar9331_sw_priv *priv = mbus->priv; + struct regmap *regmap = priv->regmap; + u32 val; + int ret; + + ret = regmap_write(regmap, AR9331_SW_REG_MDIO_CTRL, + AR9331_SW_MDIO_CTRL_BUSY | + AR9331_SW_MDIO_CTRL_MASTER_EN | + FIELD_PREP(AR9331_SW_MDIO_CTRL_PHY_ADDR_M, port) | + FIELD_PREP(AR9331_SW_MDIO_CTRL_REG_ADDR_M, regnum) | + FIELD_PREP(AR9331_SW_MDIO_CTRL_DATA_M, data)); + if (ret) + goto error; + + ret = regmap_read_poll_timeout(regmap, AR9331_SW_REG_MDIO_CTRL, val, + !(val & AR9331_SW_MDIO_CTRL_BUSY), + AR9331_SW_MDIO_POLL_SLEEP_US, + AR9331_SW_MDIO_POLL_TIMEOUT_US); + if (ret) + goto error; + + return 0; +error: + dev_err_ratelimited(priv->dev, "PHY write error: %i\n", ret); + return ret; +} + +static int ar9331_sw_mbus_read(struct mii_bus *mbus, int port, int regnum) +{ + struct ar9331_sw_priv *priv = mbus->priv; + struct regmap *regmap = priv->regmap; + u32 val; + int ret; + + ret = regmap_write(regmap, AR9331_SW_REG_MDIO_CTRL, + AR9331_SW_MDIO_CTRL_BUSY | + AR9331_SW_MDIO_CTRL_MASTER_EN | + AR9331_SW_MDIO_CTRL_CMD_READ | + FIELD_PREP(AR9331_SW_MDIO_CTRL_PHY_ADDR_M, port) | + FIELD_PREP(AR9331_SW_MDIO_CTRL_REG_ADDR_M, regnum)); + if (ret) + goto error; + + ret = regmap_read_poll_timeout(regmap, AR9331_SW_REG_MDIO_CTRL, val, + !(val & AR9331_SW_MDIO_CTRL_BUSY), + AR9331_SW_MDIO_POLL_SLEEP_US, + AR9331_SW_MDIO_POLL_TIMEOUT_US); + if (ret) + goto error; + + ret = regmap_read(regmap, AR9331_SW_REG_MDIO_CTRL, &val); + if (ret) + goto error; + + return FIELD_GET(AR9331_SW_MDIO_CTRL_DATA_M, val); + +error: + dev_err_ratelimited(priv->dev, "PHY read error: %i\n", ret); + return ret; +} + +static int ar9331_sw_mbus_init(struct ar9331_sw_priv *priv) +{ + struct device *dev = priv->dev; + static struct mii_bus *mbus; + struct device_node *np, *mnp; + int ret; + + np = dev->of_node; + + mbus = devm_mdiobus_alloc(dev); + if (!mbus) + return -ENOMEM; + + mbus->name = np->full_name; + snprintf(mbus->id, MII_BUS_ID_SIZE, "%pOF", np); + + mbus->read = ar9331_sw_mbus_read; + mbus->write = ar9331_sw_mbus_write; + mbus->priv = priv; + mbus->parent = dev; + + mnp = of_get_child_by_name(np, "mdio"); + if (!mnp) + return -ENODEV; + + ret = of_mdiobus_register(mbus, mnp); + of_node_put(mnp); + if (ret) + return ret; + + priv->mbus = mbus; + + return 0; +} + +static int ar9331_sw_setup(struct dsa_switch *ds) +{ + struct ar9331_sw_priv *priv = (struct ar9331_sw_priv *)ds->priv; + struct regmap *regmap = priv->regmap; + int ret; + + ret = ar9331_sw_reset(priv); + if (ret) + return ret; + + /* Reset will set proper defaults. CPU - Port0 will be enabled and + * configured. All other ports (ports 1 - 5) are disabled + */ + ret = ar9331_sw_mbus_init(priv); + if (ret) + return ret; + + /* Do not drop broadcast frames */ + ret = regmap_write_bits(regmap, AR9331_SW_REG_FLOOD_MASK, + AR9331_SW_FLOOD_MASK_BROAD_TO_CPU, + AR9331_SW_FLOOD_MASK_BROAD_TO_CPU); + if (ret) + goto error; + + /* Set max frame size to the maximum supported value */ + ret = regmap_write_bits(regmap, AR9331_SW_REG_GLOBAL_CTRL, + AR9331_SW_GLOBAL_CTRL_MFS_M, + AR9331_SW_GLOBAL_CTRL_MFS_M); + if (ret) + goto error; + + return 0; +error: + dev_err_ratelimited(priv->dev, "%s: %i\n", __func__, ret); + return ret; +} + +static void ar9331_sw_port_disable(struct dsa_switch *ds, int port) +{ + struct ar9331_sw_priv *priv = (struct ar9331_sw_priv *)ds->priv; + struct regmap *regmap = priv->regmap; + int ret; + + ret = regmap_write(regmap, AR9331_SW_REG_PORT_STATUS(port), 0); + if (ret) + dev_err_ratelimited(priv->dev, "%s: %i\n", __func__, ret); +} + +static enum dsa_tag_protocol ar9331_sw_get_tag_protocol(struct dsa_switch *ds, + int port) +{ + return DSA_TAG_PROTO_AR9331; +} + +static void ar9331_sw_phylink_validate(struct dsa_switch *ds, int port, + unsigned long *supported, + struct phylink_link_state *state) +{ + __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, }; + + switch (port) { + case 0: + if (state->interface != PHY_INTERFACE_MODE_GMII) + goto unsupported; + + phylink_set(mask, 1000baseT_Full); + phylink_set(mask, 1000baseT_Half); + break; + case 1: + case 2: + case 3: + case 4: + case 5: + if (state->interface != PHY_INTERFACE_MODE_INTERNAL) + goto unsupported; + break; + default: + bitmap_zero(supported, __ETHTOOL_LINK_MODE_MASK_NBITS); + dev_err(ds->dev, "Unsupported port: %i\n", port); + return; + } + + phylink_set_port_modes(mask); + phylink_set(mask, Pause); + phylink_set(mask, Asym_Pause); + + phylink_set(mask, 10baseT_Half); + phylink_set(mask, 10baseT_Full); + phylink_set(mask, 100baseT_Half); + phylink_set(mask, 100baseT_Full); + + bitmap_and(supported, supported, mask, + __ETHTOOL_LINK_MODE_MASK_NBITS); + bitmap_and(state->advertising, state->advertising, mask, + __ETHTOOL_LINK_MODE_MASK_NBITS); + + return; + +unsupported: + bitmap_zero(supported, __ETHTOOL_LINK_MODE_MASK_NBITS); + dev_err(ds->dev, "Unsupported interface: %d, port: %d\n", + state->interface, port); +} + +static void ar9331_sw_phylink_mac_config(struct dsa_switch *ds, int port, + unsigned int mode, + const struct phylink_link_state *state) +{ + struct ar9331_sw_priv *priv = (struct ar9331_sw_priv *)ds->priv; + struct regmap *regmap = priv->regmap; + int ret; + u32 val; + + switch (state->speed) { + case SPEED_1000: + val = AR9331_SW_PORT_STATUS_SPEED_1000; + break; + case SPEED_100: + val = AR9331_SW_PORT_STATUS_SPEED_100; + break; + case SPEED_10: + val = AR9331_SW_PORT_STATUS_SPEED_10; + break; + default: + return; + } + + if (state->duplex) + val |= AR9331_SW_PORT_STATUS_DUPLEX_MODE; + + if (state->pause & MLO_PAUSE_TX) + val |= AR9331_SW_PORT_STATUS_TX_FLOW_EN; + + if (state->pause & MLO_PAUSE_RX) + val |= AR9331_SW_PORT_STATUS_RX_FLOW_EN; + + ret = regmap_update_bits(regmap, AR9331_SW_REG_PORT_STATUS(port), + AR9331_SW_PORT_STATUS_LINK_MASK, val); + if (ret) + dev_err_ratelimited(priv->dev, "%s: %i\n", __func__, ret); +} + +static void ar9331_sw_phylink_mac_link_down(struct dsa_switch *ds, int port, + unsigned int mode, + phy_interface_t interface) +{ + struct ar9331_sw_priv *priv = (struct ar9331_sw_priv *)ds->priv; + struct regmap *regmap = priv->regmap; + int ret; + + ret = regmap_update_bits(regmap, AR9331_SW_REG_PORT_STATUS(port), + AR9331_SW_PORT_STATUS_MAC_MASK, 0); + if (ret) + dev_err_ratelimited(priv->dev, "%s: %i\n", __func__, ret); +} + +static void ar9331_sw_phylink_mac_link_up(struct dsa_switch *ds, int port, + unsigned int mode, + phy_interface_t interface, + struct phy_device *phydev) +{ + struct ar9331_sw_priv *priv = (struct ar9331_sw_priv *)ds->priv; + struct regmap *regmap = priv->regmap; + int ret; + + ret = regmap_update_bits(regmap, AR9331_SW_REG_PORT_STATUS(port), + AR9331_SW_PORT_STATUS_MAC_MASK, + AR9331_SW_PORT_STATUS_MAC_MASK); + if (ret) + dev_err_ratelimited(priv->dev, "%s: %i\n", __func__, ret); +} + +static const struct dsa_switch_ops ar9331_sw_ops = { + .get_tag_protocol = ar9331_sw_get_tag_protocol, + .setup = ar9331_sw_setup, + .port_disable = ar9331_sw_port_disable, + .phylink_validate = ar9331_sw_phylink_validate, + .phylink_mac_config = ar9331_sw_phylink_mac_config, + .phylink_mac_link_down = ar9331_sw_phylink_mac_link_down, + .phylink_mac_link_up = ar9331_sw_phylink_mac_link_up, +}; + +static irqreturn_t ar9331_sw_irq(int irq, void *data) +{ + struct ar9331_sw_priv *priv = data; + struct regmap *regmap = priv->regmap; + u32 stat; + int ret; + + ret = regmap_read(regmap, AR9331_SW_REG_GINT, &stat); + if (ret) { + dev_err(priv->dev, "can't read interrupt status\n"); + return IRQ_NONE; + } + + if (!stat) + return IRQ_NONE; + + if (stat & AR9331_SW_GINT_PHY_INT) { + int child_irq; + + child_irq = irq_find_mapping(priv->irqdomain, 0); + handle_nested_irq(child_irq); + } + + ret = regmap_write(regmap, AR9331_SW_REG_GINT, stat); + if (ret) { + dev_err(priv->dev, "can't write interrupt status\n"); + return IRQ_NONE; + } + + return IRQ_HANDLED; +} + +static void ar9331_sw_mask_irq(struct irq_data *d) +{ + struct ar9331_sw_priv *priv = irq_data_get_irq_chip_data(d); + struct regmap *regmap = priv->regmap; + int ret; + + ret = regmap_update_bits(regmap, AR9331_SW_REG_GINT_MASK, + AR9331_SW_GINT_PHY_INT, 0); + if (ret) + dev_err(priv->dev, "could not mask IRQ\n"); +} + +static void ar9331_sw_unmask_irq(struct irq_data *d) +{ + struct ar9331_sw_priv *priv = irq_data_get_irq_chip_data(d); + struct regmap *regmap = priv->regmap; + int ret; + + ret = regmap_update_bits(regmap, AR9331_SW_REG_GINT_MASK, + AR9331_SW_GINT_PHY_INT, + AR9331_SW_GINT_PHY_INT); + if (ret) + dev_err(priv->dev, "could not unmask IRQ\n"); +} + +static struct irq_chip ar9331_sw_irq_chip = { + .name = AR9331_SW_NAME, + .irq_mask = ar9331_sw_mask_irq, + .irq_unmask = ar9331_sw_unmask_irq, +}; + +static int ar9331_sw_irq_map(struct irq_domain *domain, unsigned int irq, + irq_hw_number_t hwirq) +{ + irq_set_chip_data(irq, domain->host_data); + irq_set_chip_and_handler(irq, &ar9331_sw_irq_chip, handle_simple_irq); + irq_set_nested_thread(irq, 1); + irq_set_noprobe(irq); + + return 0; +} + +static void ar9331_sw_irq_unmap(struct irq_domain *d, unsigned int irq) +{ + irq_set_nested_thread(irq, 0); + irq_set_chip_and_handler(irq, NULL, NULL); + irq_set_chip_data(irq, NULL); +} + +static const struct irq_domain_ops ar9331_sw_irqdomain_ops = { + .map = ar9331_sw_irq_map, + .unmap = ar9331_sw_irq_unmap, + .xlate = irq_domain_xlate_onecell, +}; + +static int ar9331_sw_irq_init(struct ar9331_sw_priv *priv) +{ + struct device_node *np = priv->dev->of_node; + struct device *dev = priv->dev; + int ret, irq; + + irq = of_irq_get(np, 0); + if (irq <= 0) { + dev_err(dev, "failed to get parent IRQ\n"); + return irq ? irq : -EINVAL; + } + + ret = devm_request_threaded_irq(dev, irq, NULL, ar9331_sw_irq, + IRQF_ONESHOT, AR9331_SW_NAME, priv); + if (ret) { + dev_err(dev, "unable to request irq: %d\n", ret); + return ret; + } + + priv->irqdomain = irq_domain_add_linear(np, 1, &ar9331_sw_irqdomain_ops, + priv); + if (!priv->irqdomain) { + dev_err(dev, "failed to create IRQ domain\n"); + return -EINVAL; + } + + irq_set_parent(irq_create_mapping(priv->irqdomain, 0), irq); + + return 0; +} + +static int __ar9331_mdio_write(struct mii_bus *sbus, u8 mode, u16 reg, u16 val) +{ + u8 r, p; + + p = FIELD_PREP(AR9331_SW_MDIO_PHY_MODE_M, mode) | + FIELD_GET(AR9331_SW_LOW_ADDR_PHY, reg); + r = FIELD_GET(AR9331_SW_LOW_ADDR_REG, reg); + + return mdiobus_write(sbus, p, r, val); +} + +static int __ar9331_mdio_read(struct mii_bus *sbus, u16 reg) +{ + u8 r, p; + + p = FIELD_PREP(AR9331_SW_MDIO_PHY_MODE_M, AR9331_SW_MDIO_PHY_MODE_REG) | + FIELD_GET(AR9331_SW_LOW_ADDR_PHY, reg); + r = FIELD_GET(AR9331_SW_LOW_ADDR_REG, reg); + + return mdiobus_read(sbus, p, r); +} + +static int ar9331_mdio_read(void *ctx, const void *reg_buf, size_t reg_len, + void *val_buf, size_t val_len) +{ + struct ar9331_sw_priv *priv = ctx; + struct mii_bus *sbus = priv->sbus; + u32 reg = *(u32 *)reg_buf; + int ret; + + if (reg == AR9331_SW_REG_PAGE) { + /* We cannot read the page selector register from hardware and + * we cache its value in regmap. Return all bits set here, + * that regmap will always write the page on first use. + */ + *(u32 *)val_buf = GENMASK(9, 0); + return 0; + } + + ret = __ar9331_mdio_read(sbus, reg); + if (ret < 0) + goto error; + + *(u32 *)val_buf = ret; + ret = __ar9331_mdio_read(sbus, reg + 2); + if (ret < 0) + goto error; + + *(u32 *)val_buf |= ret << 16; + + return 0; +error: + dev_err_ratelimited(&sbus->dev, "Bus error. Failed to read register.\n"); + return ret; +} + +static int ar9331_mdio_write(void *ctx, u32 reg, u32 val) +{ + struct ar9331_sw_priv *priv = (struct ar9331_sw_priv *)ctx; + struct mii_bus *sbus = priv->sbus; + int ret; + + if (reg == AR9331_SW_REG_PAGE) { + ret = __ar9331_mdio_write(sbus, AR9331_SW_MDIO_PHY_MODE_PAGE, + 0, val); + if (ret < 0) + goto error; + + return 0; + } + + ret = __ar9331_mdio_write(sbus, AR9331_SW_MDIO_PHY_MODE_REG, reg, val); + if (ret < 0) + goto error; + + ret = __ar9331_mdio_write(sbus, AR9331_SW_MDIO_PHY_MODE_REG, reg + 2, + val >> 16); + if (ret < 0) + goto error; + + return 0; +error: + dev_err_ratelimited(&sbus->dev, "Bus error. Failed to write register.\n"); + return ret; +} + +static int ar9331_sw_bus_write(void *context, const void *data, size_t count) +{ + u32 reg = *(u32 *)data; + u32 val = *((u32 *)data + 1); + + return ar9331_mdio_write(context, reg, val); +} + +static const struct regmap_range ar9331_valid_regs[] = { + regmap_reg_range(0x0, 0x0), + regmap_reg_range(0x10, 0x14), + regmap_reg_range(0x20, 0x24), + regmap_reg_range(0x2c, 0x30), + regmap_reg_range(0x40, 0x44), + regmap_reg_range(0x50, 0x78), + regmap_reg_range(0x80, 0x98), + + regmap_reg_range(0x100, 0x120), + regmap_reg_range(0x200, 0x220), + regmap_reg_range(0x300, 0x320), + regmap_reg_range(0x400, 0x420), + regmap_reg_range(0x500, 0x520), + regmap_reg_range(0x600, 0x620), + + regmap_reg_range(0x20000, 0x200a4), + regmap_reg_range(0x20100, 0x201a4), + regmap_reg_range(0x20200, 0x202a4), + regmap_reg_range(0x20300, 0x203a4), + regmap_reg_range(0x20400, 0x204a4), + regmap_reg_range(0x20500, 0x205a4), + + /* dummy page selector reg */ + regmap_reg_range(AR9331_SW_REG_PAGE, AR9331_SW_REG_PAGE), +}; + +static const struct regmap_range ar9331_nonvolatile_regs[] = { + regmap_reg_range(AR9331_SW_REG_PAGE, AR9331_SW_REG_PAGE), +}; + +static const struct regmap_range_cfg ar9331_regmap_range[] = { + { + .selector_reg = AR9331_SW_REG_PAGE, + .selector_mask = GENMASK(9, 0), + .selector_shift = 0, + + .window_start = 0, + .window_len = 512, + + .range_min = 0, + .range_max = AR9331_SW_REG_PAGE - 4, + }, +}; + +static const struct regmap_access_table ar9331_register_set = { + .yes_ranges = ar9331_valid_regs, + .n_yes_ranges = ARRAY_SIZE(ar9331_valid_regs), +}; + +static const struct regmap_access_table ar9331_volatile_set = { + .no_ranges = ar9331_nonvolatile_regs, + .n_no_ranges = ARRAY_SIZE(ar9331_nonvolatile_regs), +}; + +static const struct regmap_config ar9331_mdio_regmap_config = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .max_register = AR9331_SW_REG_PAGE, + + .ranges = ar9331_regmap_range, + .num_ranges = ARRAY_SIZE(ar9331_regmap_range), + + .volatile_table = &ar9331_volatile_set, + .wr_table = &ar9331_register_set, + .rd_table = &ar9331_register_set, + + .cache_type = REGCACHE_RBTREE, +}; + +static struct regmap_bus ar9331_sw_bus = { + .reg_format_endian_default = REGMAP_ENDIAN_NATIVE, + .val_format_endian_default = REGMAP_ENDIAN_NATIVE, + .read = ar9331_mdio_read, + .write = ar9331_sw_bus_write, + .max_raw_read = 4, + .max_raw_write = 4, +}; + +static int ar9331_sw_probe(struct mdio_device *mdiodev) +{ + struct ar9331_sw_priv *priv; + struct dsa_switch *ds; + int ret; + + priv = devm_kzalloc(&mdiodev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->regmap = devm_regmap_init(&mdiodev->dev, &ar9331_sw_bus, priv, + &ar9331_mdio_regmap_config); + if (IS_ERR(priv->regmap)) { + ret = PTR_ERR(priv->regmap); + dev_err(&mdiodev->dev, "regmap init failed: %d\n", ret); + return ret; + } + + priv->sw_reset = devm_reset_control_get(&mdiodev->dev, "switch"); + if (IS_ERR(priv->sw_reset)) { + dev_err(&mdiodev->dev, "missing switch reset\n"); + return PTR_ERR(priv->sw_reset); + } + + priv->sbus = mdiodev->bus; + priv->dev = &mdiodev->dev; + + ret = ar9331_sw_irq_init(priv); + if (ret) + return ret; + + ds = &priv->ds; + ds->dev = &mdiodev->dev; + ds->num_ports = AR9331_SW_PORTS; + ds->priv = priv; + priv->ops = ar9331_sw_ops; + ds->ops = &priv->ops; + dev_set_drvdata(&mdiodev->dev, priv); + + ret = dsa_register_switch(ds); + if (ret) + goto err_remove_irq; + + return 0; + +err_remove_irq: + irq_domain_remove(priv->irqdomain); + + return ret; +} + +static void ar9331_sw_remove(struct mdio_device *mdiodev) +{ + struct ar9331_sw_priv *priv = dev_get_drvdata(&mdiodev->dev); + + irq_domain_remove(priv->irqdomain); + mdiobus_unregister(priv->mbus); + dsa_unregister_switch(&priv->ds); + + reset_control_assert(priv->sw_reset); +} + +static const struct of_device_id ar9331_sw_of_match[] = { + { .compatible = "qca,ar9331-switch" }, + { }, +}; + +static struct mdio_driver ar9331_sw_mdio_driver = { + .probe = ar9331_sw_probe, + .remove = ar9331_sw_remove, + .mdiodrv.driver = { + .name = AR9331_SW_NAME, + .of_match_table = ar9331_sw_of_match, + }, +}; + +mdio_module_driver(ar9331_sw_mdio_driver); + +MODULE_AUTHOR("Oleksij Rempel "); +MODULE_DESCRIPTION("Driver for Atheros AR9331 switch"); +MODULE_LICENSE("GPL v2"); -- GitLab From 2e3d7fa5d29b7ab649fdf8f9533ae0c0888a7fac Mon Sep 17 00:00:00 2001 From: Julian Wiedmann Date: Wed, 18 Dec 2019 16:32:26 +0100 Subject: [PATCH 1209/1437] s390/qeth: handle error due to unsupported transport mode Along with z/VM NICs, there's additional device types that only support a specific transport mode (eg. external-bridged IQD). Identify the corresponding error code, and raise a fitting error message so that the user knows to adjust their device configuration. On top of that also fix the subsequent error path, so that the rejected cmd doesn't need to wait for a timeout but gets cancelled straight away. Fixes: 4a71df50047f ("qeth: new qeth device driver") Signed-off-by: Julian Wiedmann Signed-off-by: David S. Miller --- drivers/s390/net/qeth_core_main.c | 14 +++++++------- drivers/s390/net/qeth_core_mpc.h | 5 +++++ 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c index b9a2349e4b909..bb406eacef829 100644 --- a/drivers/s390/net/qeth_core_main.c +++ b/drivers/s390/net/qeth_core_main.c @@ -655,17 +655,17 @@ static int qeth_check_idx_response(struct qeth_card *card, unsigned char *buffer) { QETH_DBF_HEX(CTRL, 2, buffer, QETH_DBF_CTRL_LEN); - if ((buffer[2] & 0xc0) == 0xc0) { + if ((buffer[2] & QETH_IDX_TERMINATE_MASK) == QETH_IDX_TERMINATE) { QETH_DBF_MESSAGE(2, "received an IDX TERMINATE with cause code %#04x\n", buffer[4]); QETH_CARD_TEXT(card, 2, "ckidxres"); QETH_CARD_TEXT(card, 2, " idxterm"); - QETH_CARD_TEXT_(card, 2, " rc%d", -EIO); - if (buffer[4] == 0xf6) { + QETH_CARD_TEXT_(card, 2, "rc%x", buffer[4]); + if (buffer[4] == QETH_IDX_TERM_BAD_TRANSPORT || + buffer[4] == QETH_IDX_TERM_BAD_TRANSPORT_VM) { dev_err(&card->gdev->dev, - "The qeth device is not configured " - "for the OSI layer required by z/VM\n"); - return -EPERM; + "The device does not support the configured transport mode\n"); + return -EPROTONOSUPPORT; } return -EIO; } @@ -742,10 +742,10 @@ static void qeth_issue_next_read_cb(struct qeth_card *card, case 0: break; case -EIO: - qeth_clear_ipacmd_list(card); qeth_schedule_recovery(card); /* fall through */ default: + qeth_clear_ipacmd_list(card); goto out; } diff --git a/drivers/s390/net/qeth_core_mpc.h b/drivers/s390/net/qeth_core_mpc.h index 88f4dc140751c..6f5290fabd2cf 100644 --- a/drivers/s390/net/qeth_core_mpc.h +++ b/drivers/s390/net/qeth_core_mpc.h @@ -899,6 +899,11 @@ extern unsigned char IDX_ACTIVATE_WRITE[]; #define QETH_IDX_ACT_ERR_AUTH 0x1E #define QETH_IDX_ACT_ERR_AUTH_USER 0x20 +#define QETH_IDX_TERMINATE 0xc0 +#define QETH_IDX_TERMINATE_MASK 0xc0 +#define QETH_IDX_TERM_BAD_TRANSPORT 0x41 +#define QETH_IDX_TERM_BAD_TRANSPORT_VM 0xf6 + #define PDU_ENCAPSULATION(buffer) \ (buffer + *(buffer + (*(buffer + 0x0b)) + \ *(buffer + *(buffer + 0x0b) + 0x11) + 0x07)) -- GitLab From 0f399305cd31e5c813086eaa264f7f47e205c10e Mon Sep 17 00:00:00 2001 From: Julian Wiedmann Date: Wed, 18 Dec 2019 16:32:27 +0100 Subject: [PATCH 1210/1437] s390/qeth: fix promiscuous mode after reset When managing the promiscuous mode during an RX modeset, qeth caches the current HW state to avoid repeated programming of the same state on each modeset. But while tearing down a device, we forget to clear the cached state. So when the device is later set online again, the initial RX modeset doesn't program the promiscuous mode since we believe it is already enabled. Fix this by clearing the cached state in the tear-down path. Note that for the SBP variant of promiscuous mode, this accidentally works right now because we unconditionally restore the SBP role while re-initializing. Fixes: 4a71df50047f ("qeth: new qeth device driver") Signed-off-by: Julian Wiedmann Reviewed-by: Alexandra Winter Signed-off-by: David S. Miller --- drivers/s390/net/qeth_l2_main.c | 1 + drivers/s390/net/qeth_l2_sys.c | 3 ++- drivers/s390/net/qeth_l3_main.c | 1 + 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/s390/net/qeth_l2_main.c b/drivers/s390/net/qeth_l2_main.c index 9086bc04fa6bd..8c95e6019bacd 100644 --- a/drivers/s390/net/qeth_l2_main.c +++ b/drivers/s390/net/qeth_l2_main.c @@ -295,6 +295,7 @@ static void qeth_l2_stop_card(struct qeth_card *card) flush_workqueue(card->event_wq); card->info.mac_bits &= ~QETH_LAYER2_MAC_REGISTERED; + card->info.promisc_mode = 0; } static int qeth_l2_process_inbound_buffer(struct qeth_card *card, diff --git a/drivers/s390/net/qeth_l2_sys.c b/drivers/s390/net/qeth_l2_sys.c index f70c7aac2dcc3..7fa325cf6f8d3 100644 --- a/drivers/s390/net/qeth_l2_sys.c +++ b/drivers/s390/net/qeth_l2_sys.c @@ -262,7 +262,8 @@ void qeth_l2_setup_bridgeport_attrs(struct qeth_card *card) return; mutex_lock(&card->sbp_lock); - if (card->options.sbp.role != QETH_SBP_ROLE_NONE) { + if (!card->options.sbp.reflect_promisc && + card->options.sbp.role != QETH_SBP_ROLE_NONE) { /* Conditional to avoid spurious error messages */ qeth_bridgeport_setrole(card, card->options.sbp.role); /* Let the callback function refresh the stored role value. */ diff --git a/drivers/s390/net/qeth_l3_main.c b/drivers/s390/net/qeth_l3_main.c index 27126330a4b00..04e301de376f0 100644 --- a/drivers/s390/net/qeth_l3_main.c +++ b/drivers/s390/net/qeth_l3_main.c @@ -1314,6 +1314,7 @@ static void qeth_l3_stop_card(struct qeth_card *card) } flush_workqueue(card->event_wq); + card->info.promisc_mode = 0; } static void qeth_l3_set_promisc_mode(struct qeth_card *card) -- GitLab From 39bdbf3e648d801596498a5a625fbc9fc1c0002f Mon Sep 17 00:00:00 2001 From: Julian Wiedmann Date: Wed, 18 Dec 2019 16:32:28 +0100 Subject: [PATCH 1211/1437] s390/qeth: don't return -ENOTSUPP to userspace ENOTSUPP is not uapi, use EOPNOTSUPP instead. Fixes: d66cb37e9664 ("qeth: Add new priority queueing options") Signed-off-by: Julian Wiedmann Signed-off-by: David S. Miller --- drivers/s390/net/qeth_core_sys.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/s390/net/qeth_core_sys.c b/drivers/s390/net/qeth_core_sys.c index e81170ab6d9aa..7bd86027f5595 100644 --- a/drivers/s390/net/qeth_core_sys.c +++ b/drivers/s390/net/qeth_core_sys.c @@ -207,7 +207,7 @@ static ssize_t qeth_dev_prioqing_store(struct device *dev, card->qdio.default_out_queue = QETH_DEFAULT_QUEUE; } else if (sysfs_streq(buf, "prio_queueing_vlan")) { if (IS_LAYER3(card)) { - rc = -ENOTSUPP; + rc = -EOPNOTSUPP; goto out; } card->qdio.do_prio_queueing = QETH_PRIO_Q_ING_VLAN; -- GitLab From 4249c507f4cbedbe0afc75a43a8808ee072f87b9 Mon Sep 17 00:00:00 2001 From: Michael Grzeschik Date: Wed, 18 Dec 2019 17:01:39 +0100 Subject: [PATCH 1212/1437] net: dsa: ksz: use common define for tag len Remove special taglen define KSZ8795_INGRESS_TAG_LEN and use generic KSZ_INGRESS_TAG_LEN instead. Signed-off-by: Michael Grzeschik Reviewed-by: Andrew Lunn Signed-off-by: David S. Miller --- net/dsa/tag_ksz.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/net/dsa/tag_ksz.c b/net/dsa/tag_ksz.c index 73605bcbb3851..90d055c4df9e8 100644 --- a/net/dsa/tag_ksz.c +++ b/net/dsa/tag_ksz.c @@ -84,8 +84,6 @@ static struct sk_buff *ksz_common_rcv(struct sk_buff *skb, * (eg, 0x00=port1, 0x02=port3, 0x06=port7) */ -#define KSZ8795_INGRESS_TAG_LEN 1 - #define KSZ8795_TAIL_TAG_OVERRIDE BIT(6) #define KSZ8795_TAIL_TAG_LOOKUP BIT(7) @@ -96,12 +94,12 @@ static struct sk_buff *ksz8795_xmit(struct sk_buff *skb, struct net_device *dev) u8 *tag; u8 *addr; - nskb = ksz_common_xmit(skb, dev, KSZ8795_INGRESS_TAG_LEN); + nskb = ksz_common_xmit(skb, dev, KSZ_INGRESS_TAG_LEN); if (!nskb) return NULL; /* Tag encoding */ - tag = skb_put(nskb, KSZ8795_INGRESS_TAG_LEN); + tag = skb_put(nskb, KSZ_INGRESS_TAG_LEN); addr = skb_mac_header(nskb); *tag = 1 << dp->index; @@ -124,7 +122,7 @@ static const struct dsa_device_ops ksz8795_netdev_ops = { .proto = DSA_TAG_PROTO_KSZ8795, .xmit = ksz8795_xmit, .rcv = ksz8795_rcv, - .overhead = KSZ8795_INGRESS_TAG_LEN, + .overhead = KSZ_INGRESS_TAG_LEN, }; DSA_TAG_DRIVER(ksz8795_netdev_ops); -- GitLab From 43985468df6757bc079ca0be44edf4da997891e0 Mon Sep 17 00:00:00 2001 From: Stefan Hajnoczi Date: Wed, 18 Dec 2019 19:06:58 +0100 Subject: [PATCH 1213/1437] VSOCK: fix header include in vsock_diag_test The vsock_diag_test program directly included ../../../include/uapi/ headers from the source tree. Tests are supposed to use the usr/include/linux/ headers that have been prepared with make headers_install instead. Suggested-by: David S. Miller Signed-off-by: Stefan Hajnoczi Signed-off-by: Stefano Garzarella Signed-off-by: David S. Miller --- tools/testing/vsock/Makefile | 2 +- tools/testing/vsock/README | 2 +- tools/testing/vsock/vsock_diag_test.c | 5 ++--- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/tools/testing/vsock/Makefile b/tools/testing/vsock/Makefile index 5be687b1e16c9..d41a4e13960ab 100644 --- a/tools/testing/vsock/Makefile +++ b/tools/testing/vsock/Makefile @@ -3,7 +3,7 @@ all: test test: vsock_diag_test vsock_diag_test: vsock_diag_test.o timeout.o control.o -CFLAGS += -g -O2 -Werror -Wall -I. -I../../include/uapi -I../../include -Wno-pointer-sign -fno-strict-overflow -fno-strict-aliasing -fno-common -MMD -U_FORTIFY_SOURCE -D_GNU_SOURCE +CFLAGS += -g -O2 -Werror -Wall -I. -I../../include -I../../../usr/include -Wno-pointer-sign -fno-strict-overflow -fno-strict-aliasing -fno-common -MMD -U_FORTIFY_SOURCE -D_GNU_SOURCE .PHONY: all test clean clean: ${RM} *.o *.d vsock_diag_test diff --git a/tools/testing/vsock/README b/tools/testing/vsock/README index 2cc6d7302db64..cf7dc64273bfa 100644 --- a/tools/testing/vsock/README +++ b/tools/testing/vsock/README @@ -10,7 +10,7 @@ The following tests are available: The following prerequisite steps are not automated and must be performed prior to running tests: -1. Build the kernel and these tests. +1. Build the kernel, make headers_install, and build these tests. 2. Install the kernel and tests on the host. 3. Install the kernel and tests inside the guest. 4. Boot the guest and ensure that the AF_VSOCK transport is enabled. diff --git a/tools/testing/vsock/vsock_diag_test.c b/tools/testing/vsock/vsock_diag_test.c index c481101364a4d..fc391e041954a 100644 --- a/tools/testing/vsock/vsock_diag_test.c +++ b/tools/testing/vsock/vsock_diag_test.c @@ -21,12 +21,11 @@ #include #include #include +#include #include +#include #include -#include "../../../include/uapi/linux/vm_sockets.h" -#include "../../../include/uapi/linux/vm_sockets_diag.h" - #include "timeout.h" #include "control.h" -- GitLab From ce1c7f84e276e8610ffa582517202ff76374cbc7 Mon Sep 17 00:00:00 2001 From: Stefan Hajnoczi Date: Wed, 18 Dec 2019 19:06:59 +0100 Subject: [PATCH 1214/1437] VSOCK: add SPDX identifiers to vsock tests Signed-off-by: Stefan Hajnoczi Signed-off-by: Stefano Garzarella Signed-off-by: David S. Miller --- tools/testing/vsock/control.h | 1 + tools/testing/vsock/timeout.h | 1 + 2 files changed, 2 insertions(+) diff --git a/tools/testing/vsock/control.h b/tools/testing/vsock/control.h index 54a07efd267c2..dac3964a891d3 100644 --- a/tools/testing/vsock/control.h +++ b/tools/testing/vsock/control.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ #ifndef CONTROL_H #define CONTROL_H diff --git a/tools/testing/vsock/timeout.h b/tools/testing/vsock/timeout.h index 77db9ce9860a9..ecb7c840e65ae 100644 --- a/tools/testing/vsock/timeout.h +++ b/tools/testing/vsock/timeout.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ #ifndef TIMEOUT_H #define TIMEOUT_H -- GitLab From df7e0e0d237e5a4ee262befb11b599058a178966 Mon Sep 17 00:00:00 2001 From: Stefan Hajnoczi Date: Wed, 18 Dec 2019 19:07:00 +0100 Subject: [PATCH 1215/1437] VSOCK: extract utility functions from vsock_diag_test.c Move useful functions into a separate file in preparation for more vsock test programs. Signed-off-by: Stefan Hajnoczi Signed-off-by: Stefano Garzarella Signed-off-by: David S. Miller --- tools/testing/vsock/Makefile | 2 +- tools/testing/vsock/util.c | 66 +++++++++++++++++++ tools/testing/vsock/util.h | 36 +++++++++++ tools/testing/vsock/vsock_diag_test.c | 92 +++++++-------------------- 4 files changed, 125 insertions(+), 71 deletions(-) create mode 100644 tools/testing/vsock/util.c create mode 100644 tools/testing/vsock/util.h diff --git a/tools/testing/vsock/Makefile b/tools/testing/vsock/Makefile index d41a4e13960ab..a916878a2d8ca 100644 --- a/tools/testing/vsock/Makefile +++ b/tools/testing/vsock/Makefile @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0-only all: test test: vsock_diag_test -vsock_diag_test: vsock_diag_test.o timeout.o control.o +vsock_diag_test: vsock_diag_test.o timeout.o control.o util.o CFLAGS += -g -O2 -Werror -Wall -I. -I../../include -I../../../usr/include -Wno-pointer-sign -fno-strict-overflow -fno-strict-aliasing -fno-common -MMD -U_FORTIFY_SOURCE -D_GNU_SOURCE .PHONY: all test clean diff --git a/tools/testing/vsock/util.c b/tools/testing/vsock/util.c new file mode 100644 index 0000000000000..f40f45b36d2fa --- /dev/null +++ b/tools/testing/vsock/util.c @@ -0,0 +1,66 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * vsock test utilities + * + * Copyright (C) 2017 Red Hat, Inc. + * + * Author: Stefan Hajnoczi + */ + +#include +#include +#include +#include + +#include "timeout.h" +#include "util.h" + +/* Install signal handlers */ +void init_signals(void) +{ + struct sigaction act = { + .sa_handler = sigalrm, + }; + + sigaction(SIGALRM, &act, NULL); + signal(SIGPIPE, SIG_IGN); +} + +/* Parse a CID in string representation */ +unsigned int parse_cid(const char *str) +{ + char *endptr = NULL; + unsigned long n; + + errno = 0; + n = strtoul(str, &endptr, 10); + if (errno || *endptr != '\0') { + fprintf(stderr, "malformed CID \"%s\"\n", str); + exit(EXIT_FAILURE); + } + return n; +} + +/* Run test cases. The program terminates if a failure occurs. */ +void run_tests(const struct test_case *test_cases, + const struct test_opts *opts) +{ + int i; + + for (i = 0; test_cases[i].name; i++) { + void (*run)(const struct test_opts *opts); + + printf("%s...", test_cases[i].name); + fflush(stdout); + + if (opts->mode == TEST_MODE_CLIENT) + run = test_cases[i].run_client; + else + run = test_cases[i].run_server; + + if (run) + run(opts); + + printf("ok\n"); + } +} diff --git a/tools/testing/vsock/util.h b/tools/testing/vsock/util.h new file mode 100644 index 0000000000000..033e7d59a42a1 --- /dev/null +++ b/tools/testing/vsock/util.h @@ -0,0 +1,36 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#ifndef UTIL_H +#define UTIL_H + +/* Tests can either run as the client or the server */ +enum test_mode { + TEST_MODE_UNSET, + TEST_MODE_CLIENT, + TEST_MODE_SERVER +}; + +/* Test runner options */ +struct test_opts { + enum test_mode mode; + unsigned int peer_cid; +}; + +/* A test case definition. Test functions must print failures to stderr and + * terminate with exit(EXIT_FAILURE). + */ +struct test_case { + const char *name; /* human-readable name */ + + /* Called when test mode is TEST_MODE_CLIENT */ + void (*run_client)(const struct test_opts *opts); + + /* Called when test mode is TEST_MODE_SERVER */ + void (*run_server)(const struct test_opts *opts); +}; + +void init_signals(void); +unsigned int parse_cid(const char *str); +void run_tests(const struct test_case *test_cases, + const struct test_opts *opts); + +#endif /* UTIL_H */ diff --git a/tools/testing/vsock/vsock_diag_test.c b/tools/testing/vsock/vsock_diag_test.c index fc391e041954a..944c8a72eed7b 100644 --- a/tools/testing/vsock/vsock_diag_test.c +++ b/tools/testing/vsock/vsock_diag_test.c @@ -9,12 +9,10 @@ #include #include -#include #include #include #include #include -#include #include #include #include @@ -28,12 +26,7 @@ #include "timeout.h" #include "control.h" - -enum test_mode { - TEST_MODE_UNSET, - TEST_MODE_CLIENT, - TEST_MODE_SERVER -}; +#include "util.h" /* Per-socket status */ struct vsock_stat { @@ -334,7 +327,7 @@ static void free_sock_stat(struct list_head *sockets) free(st); } -static void test_no_sockets(unsigned int peer_cid) +static void test_no_sockets(const struct test_opts *opts) { LIST_HEAD(sockets); @@ -345,7 +338,7 @@ static void test_no_sockets(unsigned int peer_cid) free_sock_stat(&sockets); } -static void test_listen_socket_server(unsigned int peer_cid) +static void test_listen_socket_server(const struct test_opts *opts) { union { struct sockaddr sa; @@ -383,7 +376,7 @@ static void test_listen_socket_server(unsigned int peer_cid) free_sock_stat(&sockets); } -static void test_connect_client(unsigned int peer_cid) +static void test_connect_client(const struct test_opts *opts) { union { struct sockaddr sa; @@ -392,7 +385,7 @@ static void test_connect_client(unsigned int peer_cid) .svm = { .svm_family = AF_VSOCK, .svm_port = 1234, - .svm_cid = peer_cid, + .svm_cid = opts->peer_cid, }, }; int fd; @@ -429,7 +422,7 @@ static void test_connect_client(unsigned int peer_cid) free_sock_stat(&sockets); } -static void test_connect_server(unsigned int peer_cid) +static void test_connect_server(const struct test_opts *opts) { union { struct sockaddr sa; @@ -481,9 +474,9 @@ static void test_connect_server(unsigned int peer_cid) clientaddr.sa.sa_family); exit(EXIT_FAILURE); } - if (clientaddr.svm.svm_cid != peer_cid) { + if (clientaddr.svm.svm_cid != opts->peer_cid) { fprintf(stderr, "expected peer CID %u from accept(2), got %u\n", - peer_cid, clientaddr.svm.svm_cid); + opts->peer_cid, clientaddr.svm.svm_cid); exit(EXIT_FAILURE); } @@ -502,11 +495,7 @@ static void test_connect_server(unsigned int peer_cid) free_sock_stat(&sockets); } -static struct { - const char *name; - void (*run_client)(unsigned int peer_cid); - void (*run_server)(unsigned int peer_cid); -} test_cases[] = { +static struct test_case test_cases[] = { { .name = "No sockets", .run_server = test_no_sockets, @@ -523,30 +512,6 @@ static struct { {}, }; -static void init_signals(void) -{ - struct sigaction act = { - .sa_handler = sigalrm, - }; - - sigaction(SIGALRM, &act, NULL); - signal(SIGPIPE, SIG_IGN); -} - -static unsigned int parse_cid(const char *str) -{ - char *endptr = NULL; - unsigned long int n; - - errno = 0; - n = strtoul(str, &endptr, 10); - if (errno || *endptr != '\0') { - fprintf(stderr, "malformed CID \"%s\"\n", str); - exit(EXIT_FAILURE); - } - return n; -} - static const char optstring[] = ""; static const struct option longopts[] = { { @@ -601,9 +566,10 @@ int main(int argc, char **argv) { const char *control_host = NULL; const char *control_port = NULL; - int mode = TEST_MODE_UNSET; - unsigned int peer_cid = VMADDR_CID_ANY; - int i; + struct test_opts opts = { + .mode = TEST_MODE_UNSET, + .peer_cid = VMADDR_CID_ANY, + }; init_signals(); @@ -619,16 +585,16 @@ int main(int argc, char **argv) break; case 'm': if (strcmp(optarg, "client") == 0) - mode = TEST_MODE_CLIENT; + opts.mode = TEST_MODE_CLIENT; else if (strcmp(optarg, "server") == 0) - mode = TEST_MODE_SERVER; + opts.mode = TEST_MODE_SERVER; else { fprintf(stderr, "--mode must be \"client\" or \"server\"\n"); return EXIT_FAILURE; } break; case 'p': - peer_cid = parse_cid(optarg); + opts.peer_cid = parse_cid(optarg); break; case 'P': control_port = optarg; @@ -641,35 +607,21 @@ int main(int argc, char **argv) if (!control_port) usage(); - if (mode == TEST_MODE_UNSET) + if (opts.mode == TEST_MODE_UNSET) usage(); - if (peer_cid == VMADDR_CID_ANY) + if (opts.peer_cid == VMADDR_CID_ANY) usage(); if (!control_host) { - if (mode != TEST_MODE_SERVER) + if (opts.mode != TEST_MODE_SERVER) usage(); control_host = "0.0.0.0"; } - control_init(control_host, control_port, mode == TEST_MODE_SERVER); - - for (i = 0; test_cases[i].name; i++) { - void (*run)(unsigned int peer_cid); + control_init(control_host, control_port, + opts.mode == TEST_MODE_SERVER); - printf("%s...", test_cases[i].name); - fflush(stdout); - - if (mode == TEST_MODE_CLIENT) - run = test_cases[i].run_client; - else - run = test_cases[i].run_server; - - if (run) - run(peer_cid); - - printf("ok\n"); - } + run_tests(test_cases, &opts); control_cleanup(); return EXIT_SUCCESS; -- GitLab From 9bb8a29d28e01bf7dcdb67f71edbca9d3826d004 Mon Sep 17 00:00:00 2001 From: Stefan Hajnoczi Date: Wed, 18 Dec 2019 19:07:01 +0100 Subject: [PATCH 1216/1437] VSOCK: extract connect/accept functions from vsock_diag_test.c Many test cases will need to connect to the server or accept incoming connections. This patch extracts these operations into utility functions that can be reused. Signed-off-by: Stefan Hajnoczi Signed-off-by: Stefano Garzarella Signed-off-by: David S. Miller --- tools/testing/vsock/util.c | 108 ++++++++++++++++++++++++++ tools/testing/vsock/util.h | 6 ++ tools/testing/vsock/vsock_diag_test.c | 81 ++----------------- 3 files changed, 119 insertions(+), 76 deletions(-) diff --git a/tools/testing/vsock/util.c b/tools/testing/vsock/util.c index f40f45b36d2fa..f838bcee35898 100644 --- a/tools/testing/vsock/util.c +++ b/tools/testing/vsock/util.c @@ -11,8 +11,10 @@ #include #include #include +#include #include "timeout.h" +#include "control.h" #include "util.h" /* Install signal handlers */ @@ -41,6 +43,112 @@ unsigned int parse_cid(const char *str) return n; } +/* Connect to and return the file descriptor. */ +int vsock_stream_connect(unsigned int cid, unsigned int port) +{ + union { + struct sockaddr sa; + struct sockaddr_vm svm; + } addr = { + .svm = { + .svm_family = AF_VSOCK, + .svm_port = port, + .svm_cid = cid, + }, + }; + int ret; + int fd; + + control_expectln("LISTENING"); + + fd = socket(AF_VSOCK, SOCK_STREAM, 0); + + timeout_begin(TIMEOUT); + do { + ret = connect(fd, &addr.sa, sizeof(addr.svm)); + timeout_check("connect"); + } while (ret < 0 && errno == EINTR); + timeout_end(); + + if (ret < 0) { + int old_errno = errno; + + close(fd); + fd = -1; + errno = old_errno; + } + return fd; +} + +/* Listen on and return the first incoming connection. The remote + * address is stored to clientaddrp. clientaddrp may be NULL. + */ +int vsock_stream_accept(unsigned int cid, unsigned int port, + struct sockaddr_vm *clientaddrp) +{ + union { + struct sockaddr sa; + struct sockaddr_vm svm; + } addr = { + .svm = { + .svm_family = AF_VSOCK, + .svm_port = port, + .svm_cid = cid, + }, + }; + union { + struct sockaddr sa; + struct sockaddr_vm svm; + } clientaddr; + socklen_t clientaddr_len = sizeof(clientaddr.svm); + int fd; + int client_fd; + int old_errno; + + fd = socket(AF_VSOCK, SOCK_STREAM, 0); + + if (bind(fd, &addr.sa, sizeof(addr.svm)) < 0) { + perror("bind"); + exit(EXIT_FAILURE); + } + + if (listen(fd, 1) < 0) { + perror("listen"); + exit(EXIT_FAILURE); + } + + control_writeln("LISTENING"); + + timeout_begin(TIMEOUT); + do { + client_fd = accept(fd, &clientaddr.sa, &clientaddr_len); + timeout_check("accept"); + } while (client_fd < 0 && errno == EINTR); + timeout_end(); + + old_errno = errno; + close(fd); + errno = old_errno; + + if (client_fd < 0) + return client_fd; + + if (clientaddr_len != sizeof(clientaddr.svm)) { + fprintf(stderr, "unexpected addrlen from accept(2), %zu\n", + (size_t)clientaddr_len); + exit(EXIT_FAILURE); + } + if (clientaddr.sa.sa_family != AF_VSOCK) { + fprintf(stderr, "expected AF_VSOCK from accept(2), got %d\n", + clientaddr.sa.sa_family); + exit(EXIT_FAILURE); + } + + if (clientaddrp) + *clientaddrp = clientaddr.svm; + return client_fd; +} + /* Run test cases. The program terminates if a failure occurs. */ void run_tests(const struct test_case *test_cases, const struct test_opts *opts) diff --git a/tools/testing/vsock/util.h b/tools/testing/vsock/util.h index 033e7d59a42a1..1786305cfdddf 100644 --- a/tools/testing/vsock/util.h +++ b/tools/testing/vsock/util.h @@ -2,6 +2,9 @@ #ifndef UTIL_H #define UTIL_H +#include +#include + /* Tests can either run as the client or the server */ enum test_mode { TEST_MODE_UNSET, @@ -30,6 +33,9 @@ struct test_case { void init_signals(void); unsigned int parse_cid(const char *str); +int vsock_stream_connect(unsigned int cid, unsigned int port); +int vsock_stream_accept(unsigned int cid, unsigned int port, + struct sockaddr_vm *clientaddrp); void run_tests(const struct test_case *test_cases, const struct test_opts *opts); diff --git a/tools/testing/vsock/vsock_diag_test.c b/tools/testing/vsock/vsock_diag_test.c index 944c8a72eed7b..abd7dc2a96310 100644 --- a/tools/testing/vsock/vsock_diag_test.c +++ b/tools/testing/vsock/vsock_diag_test.c @@ -13,13 +13,11 @@ #include #include #include -#include #include #include #include #include #include -#include #include #include #include @@ -378,33 +376,12 @@ static void test_listen_socket_server(const struct test_opts *opts) static void test_connect_client(const struct test_opts *opts) { - union { - struct sockaddr sa; - struct sockaddr_vm svm; - } addr = { - .svm = { - .svm_family = AF_VSOCK, - .svm_port = 1234, - .svm_cid = opts->peer_cid, - }, - }; int fd; - int ret; LIST_HEAD(sockets); struct vsock_stat *st; - control_expectln("LISTENING"); - - fd = socket(AF_VSOCK, SOCK_STREAM, 0); - - timeout_begin(TIMEOUT); - do { - ret = connect(fd, &addr.sa, sizeof(addr.svm)); - timeout_check("connect"); - } while (ret < 0 && errno == EINTR); - timeout_end(); - - if (ret < 0) { + fd = vsock_stream_connect(opts->peer_cid, 1234); + if (fd < 0) { perror("connect"); exit(EXIT_FAILURE); } @@ -424,66 +401,19 @@ static void test_connect_client(const struct test_opts *opts) static void test_connect_server(const struct test_opts *opts) { - union { - struct sockaddr sa; - struct sockaddr_vm svm; - } addr = { - .svm = { - .svm_family = AF_VSOCK, - .svm_port = 1234, - .svm_cid = VMADDR_CID_ANY, - }, - }; - union { - struct sockaddr sa; - struct sockaddr_vm svm; - } clientaddr; - socklen_t clientaddr_len = sizeof(clientaddr.svm); - LIST_HEAD(sockets); struct vsock_stat *st; - int fd; + LIST_HEAD(sockets); int client_fd; - fd = socket(AF_VSOCK, SOCK_STREAM, 0); - - if (bind(fd, &addr.sa, sizeof(addr.svm)) < 0) { - perror("bind"); - exit(EXIT_FAILURE); - } - - if (listen(fd, 1) < 0) { - perror("listen"); - exit(EXIT_FAILURE); - } - - control_writeln("LISTENING"); - - timeout_begin(TIMEOUT); - do { - client_fd = accept(fd, &clientaddr.sa, &clientaddr_len); - timeout_check("accept"); - } while (client_fd < 0 && errno == EINTR); - timeout_end(); - + client_fd = vsock_stream_accept(VMADDR_CID_ANY, 1234, NULL); if (client_fd < 0) { perror("accept"); exit(EXIT_FAILURE); } - if (clientaddr.sa.sa_family != AF_VSOCK) { - fprintf(stderr, "expected AF_VSOCK from accept(2), got %d\n", - clientaddr.sa.sa_family); - exit(EXIT_FAILURE); - } - if (clientaddr.svm.svm_cid != opts->peer_cid) { - fprintf(stderr, "expected peer CID %u from accept(2), got %u\n", - opts->peer_cid, clientaddr.svm.svm_cid); - exit(EXIT_FAILURE); - } read_vsock_stat(&sockets); - check_num_sockets(&sockets, 2); - find_vsock_stat(&sockets, fd); + check_num_sockets(&sockets, 1); st = find_vsock_stat(&sockets, client_fd); check_socket_state(st, TCP_ESTABLISHED); @@ -491,7 +421,6 @@ static void test_connect_server(const struct test_opts *opts) control_expectln("DONE"); close(client_fd); - close(fd); free_sock_stat(&sockets); } -- GitLab From 2f65b44e199c6521be8dbc7458250c74a86ce2aa Mon Sep 17 00:00:00 2001 From: Stefan Hajnoczi Date: Wed, 18 Dec 2019 19:07:02 +0100 Subject: [PATCH 1217/1437] VSOCK: add full barrier between test cases See code comment for details. Signed-off-by: Stefan Hajnoczi Signed-off-by: Stefano Garzarella Signed-off-by: David S. Miller --- tools/testing/vsock/util.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/tools/testing/vsock/util.c b/tools/testing/vsock/util.c index f838bcee35898..4280a56ba677d 100644 --- a/tools/testing/vsock/util.c +++ b/tools/testing/vsock/util.c @@ -161,10 +161,24 @@ void run_tests(const struct test_case *test_cases, printf("%s...", test_cases[i].name); fflush(stdout); - if (opts->mode == TEST_MODE_CLIENT) + if (opts->mode == TEST_MODE_CLIENT) { + /* Full barrier before executing the next test. This + * ensures that client and server are executing the + * same test case. In particular, it means whoever is + * faster will not see the peer still executing the + * last test. This is important because port numbers + * can be used by multiple test cases. + */ + control_expectln("NEXT"); + control_writeln("NEXT"); + run = test_cases[i].run_client; - else + } else { + control_writeln("NEXT"); + control_expectln("NEXT"); + run = test_cases[i].run_server; + } if (run) run(opts); -- GitLab From 092f32ae628ed4874caf349cc0cfa0626cc3c5b0 Mon Sep 17 00:00:00 2001 From: Stefan Hajnoczi Date: Wed, 18 Dec 2019 19:07:03 +0100 Subject: [PATCH 1218/1437] VSOCK: add send_byte()/recv_byte() test utilities Test cases will want to transfer data. This patch adds utility functions to do this. Signed-off-by: Stefan Hajnoczi Signed-off-by: Stefano Garzarella Signed-off-by: David S. Miller --- tools/testing/vsock/util.c | 103 +++++++++++++++++++++++++++++++++++++ tools/testing/vsock/util.h | 2 + 2 files changed, 105 insertions(+) diff --git a/tools/testing/vsock/util.c b/tools/testing/vsock/util.c index 4280a56ba677d..6026ef3ce5122 100644 --- a/tools/testing/vsock/util.c +++ b/tools/testing/vsock/util.c @@ -9,6 +9,7 @@ #include #include +#include #include #include #include @@ -149,6 +150,108 @@ int vsock_stream_accept(unsigned int cid, unsigned int port, return client_fd; } +/* Transmit one byte and check the return value. + * + * expected_ret: + * <0 Negative errno (for testing errors) + * 0 End-of-file + * 1 Success + */ +void send_byte(int fd, int expected_ret, int flags) +{ + const uint8_t byte = 'A'; + ssize_t nwritten; + + timeout_begin(TIMEOUT); + do { + nwritten = send(fd, &byte, sizeof(byte), flags); + timeout_check("write"); + } while (nwritten < 0 && errno == EINTR); + timeout_end(); + + if (expected_ret < 0) { + if (nwritten != -1) { + fprintf(stderr, "bogus send(2) return value %zd\n", + nwritten); + exit(EXIT_FAILURE); + } + if (errno != -expected_ret) { + perror("write"); + exit(EXIT_FAILURE); + } + return; + } + + if (nwritten < 0) { + perror("write"); + exit(EXIT_FAILURE); + } + if (nwritten == 0) { + if (expected_ret == 0) + return; + + fprintf(stderr, "unexpected EOF while sending byte\n"); + exit(EXIT_FAILURE); + } + if (nwritten != sizeof(byte)) { + fprintf(stderr, "bogus send(2) return value %zd\n", nwritten); + exit(EXIT_FAILURE); + } +} + +/* Receive one byte and check the return value. + * + * expected_ret: + * <0 Negative errno (for testing errors) + * 0 End-of-file + * 1 Success + */ +void recv_byte(int fd, int expected_ret, int flags) +{ + uint8_t byte; + ssize_t nread; + + timeout_begin(TIMEOUT); + do { + nread = recv(fd, &byte, sizeof(byte), flags); + timeout_check("read"); + } while (nread < 0 && errno == EINTR); + timeout_end(); + + if (expected_ret < 0) { + if (nread != -1) { + fprintf(stderr, "bogus recv(2) return value %zd\n", + nread); + exit(EXIT_FAILURE); + } + if (errno != -expected_ret) { + perror("read"); + exit(EXIT_FAILURE); + } + return; + } + + if (nread < 0) { + perror("read"); + exit(EXIT_FAILURE); + } + if (nread == 0) { + if (expected_ret == 0) + return; + + fprintf(stderr, "unexpected EOF while receiving byte\n"); + exit(EXIT_FAILURE); + } + if (nread != sizeof(byte)) { + fprintf(stderr, "bogus recv(2) return value %zd\n", nread); + exit(EXIT_FAILURE); + } + if (byte != 'A') { + fprintf(stderr, "unexpected byte read %c\n", byte); + exit(EXIT_FAILURE); + } +} + /* Run test cases. The program terminates if a failure occurs. */ void run_tests(const struct test_case *test_cases, const struct test_opts *opts) diff --git a/tools/testing/vsock/util.h b/tools/testing/vsock/util.h index 1786305cfdddf..4df12e4b5ebec 100644 --- a/tools/testing/vsock/util.h +++ b/tools/testing/vsock/util.h @@ -36,6 +36,8 @@ unsigned int parse_cid(const char *str); int vsock_stream_connect(unsigned int cid, unsigned int port); int vsock_stream_accept(unsigned int cid, unsigned int port, struct sockaddr_vm *clientaddrp); +void send_byte(int fd, int expected_ret, int flags); +void recv_byte(int fd, int expected_ret, int flags); void run_tests(const struct test_case *test_cases, const struct test_opts *opts); -- GitLab From cdbcc18de10a7d72b821fe40fbc2d0ec61e39448 Mon Sep 17 00:00:00 2001 From: Stefan Hajnoczi Date: Wed, 18 Dec 2019 19:07:04 +0100 Subject: [PATCH 1219/1437] VSOCK: add AF_VSOCK test cases The vsock_test.c program runs a test suite of AF_VSOCK test cases. Signed-off-by: Stefan Hajnoczi Signed-off-by: Stefano Garzarella Signed-off-by: David S. Miller --- tools/testing/vsock/.gitignore | 1 + tools/testing/vsock/Makefile | 5 +- tools/testing/vsock/README | 1 + tools/testing/vsock/vsock_test.c | 312 +++++++++++++++++++++++++++++++ 4 files changed, 317 insertions(+), 2 deletions(-) create mode 100644 tools/testing/vsock/vsock_test.c diff --git a/tools/testing/vsock/.gitignore b/tools/testing/vsock/.gitignore index dc5f11faf5300..7f7a2ccc30c4d 100644 --- a/tools/testing/vsock/.gitignore +++ b/tools/testing/vsock/.gitignore @@ -1,2 +1,3 @@ *.d +vsock_test vsock_diag_test diff --git a/tools/testing/vsock/Makefile b/tools/testing/vsock/Makefile index a916878a2d8ca..f8293c6910c92 100644 --- a/tools/testing/vsock/Makefile +++ b/tools/testing/vsock/Makefile @@ -1,10 +1,11 @@ # SPDX-License-Identifier: GPL-2.0-only all: test -test: vsock_diag_test +test: vsock_test vsock_diag_test +vsock_test: vsock_test.o timeout.o control.o util.o vsock_diag_test: vsock_diag_test.o timeout.o control.o util.o CFLAGS += -g -O2 -Werror -Wall -I. -I../../include -I../../../usr/include -Wno-pointer-sign -fno-strict-overflow -fno-strict-aliasing -fno-common -MMD -U_FORTIFY_SOURCE -D_GNU_SOURCE .PHONY: all test clean clean: - ${RM} *.o *.d vsock_diag_test + ${RM} *.o *.d vsock_test vsock_diag_test -include *.d diff --git a/tools/testing/vsock/README b/tools/testing/vsock/README index cf7dc64273bfa..4d5045e7d2c3d 100644 --- a/tools/testing/vsock/README +++ b/tools/testing/vsock/README @@ -5,6 +5,7 @@ Hyper-V. The following tests are available: + * vsock_test - core AF_VSOCK socket functionality * vsock_diag_test - vsock_diag.ko module for listing open sockets The following prerequisite steps are not automated and must be performed prior diff --git a/tools/testing/vsock/vsock_test.c b/tools/testing/vsock/vsock_test.c new file mode 100644 index 0000000000000..fae8ddc3ef72f --- /dev/null +++ b/tools/testing/vsock/vsock_test.c @@ -0,0 +1,312 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * vsock_test - vsock.ko test suite + * + * Copyright (C) 2017 Red Hat, Inc. + * + * Author: Stefan Hajnoczi + */ + +#include +#include +#include +#include +#include +#include + +#include "timeout.h" +#include "control.h" +#include "util.h" + +static void test_stream_connection_reset(const struct test_opts *opts) +{ + union { + struct sockaddr sa; + struct sockaddr_vm svm; + } addr = { + .svm = { + .svm_family = AF_VSOCK, + .svm_port = 1234, + .svm_cid = opts->peer_cid, + }, + }; + int ret; + int fd; + + fd = socket(AF_VSOCK, SOCK_STREAM, 0); + + timeout_begin(TIMEOUT); + do { + ret = connect(fd, &addr.sa, sizeof(addr.svm)); + timeout_check("connect"); + } while (ret < 0 && errno == EINTR); + timeout_end(); + + if (ret != -1) { + fprintf(stderr, "expected connect(2) failure, got %d\n", ret); + exit(EXIT_FAILURE); + } + if (errno != ECONNRESET) { + fprintf(stderr, "unexpected connect(2) errno %d\n", errno); + exit(EXIT_FAILURE); + } + + close(fd); +} + +static void test_stream_client_close_client(const struct test_opts *opts) +{ + int fd; + + fd = vsock_stream_connect(opts->peer_cid, 1234); + if (fd < 0) { + perror("connect"); + exit(EXIT_FAILURE); + } + + send_byte(fd, 1, 0); + close(fd); + control_writeln("CLOSED"); +} + +static void test_stream_client_close_server(const struct test_opts *opts) +{ + int fd; + + fd = vsock_stream_accept(VMADDR_CID_ANY, 1234, NULL); + if (fd < 0) { + perror("accept"); + exit(EXIT_FAILURE); + } + + control_expectln("CLOSED"); + + send_byte(fd, -EPIPE, 0); + recv_byte(fd, 1, 0); + recv_byte(fd, 0, 0); + close(fd); +} + +static void test_stream_server_close_client(const struct test_opts *opts) +{ + int fd; + + fd = vsock_stream_connect(opts->peer_cid, 1234); + if (fd < 0) { + perror("connect"); + exit(EXIT_FAILURE); + } + + control_expectln("CLOSED"); + + send_byte(fd, -EPIPE, 0); + recv_byte(fd, 1, 0); + recv_byte(fd, 0, 0); + close(fd); +} + +static void test_stream_server_close_server(const struct test_opts *opts) +{ + int fd; + + fd = vsock_stream_accept(VMADDR_CID_ANY, 1234, NULL); + if (fd < 0) { + perror("accept"); + exit(EXIT_FAILURE); + } + + send_byte(fd, 1, 0); + close(fd); + control_writeln("CLOSED"); +} + +/* With the standard socket sizes, VMCI is able to support about 100 + * concurrent stream connections. + */ +#define MULTICONN_NFDS 100 + +static void test_stream_multiconn_client(const struct test_opts *opts) +{ + int fds[MULTICONN_NFDS]; + int i; + + for (i = 0; i < MULTICONN_NFDS; i++) { + fds[i] = vsock_stream_connect(opts->peer_cid, 1234); + if (fds[i] < 0) { + perror("connect"); + exit(EXIT_FAILURE); + } + } + + for (i = 0; i < MULTICONN_NFDS; i++) { + if (i % 2) + recv_byte(fds[i], 1, 0); + else + send_byte(fds[i], 1, 0); + } + + for (i = 0; i < MULTICONN_NFDS; i++) + close(fds[i]); +} + +static void test_stream_multiconn_server(const struct test_opts *opts) +{ + int fds[MULTICONN_NFDS]; + int i; + + for (i = 0; i < MULTICONN_NFDS; i++) { + fds[i] = vsock_stream_accept(VMADDR_CID_ANY, 1234, NULL); + if (fds[i] < 0) { + perror("accept"); + exit(EXIT_FAILURE); + } + } + + for (i = 0; i < MULTICONN_NFDS; i++) { + if (i % 2) + send_byte(fds[i], 1, 0); + else + recv_byte(fds[i], 1, 0); + } + + for (i = 0; i < MULTICONN_NFDS; i++) + close(fds[i]); +} + +static struct test_case test_cases[] = { + { + .name = "SOCK_STREAM connection reset", + .run_client = test_stream_connection_reset, + }, + { + .name = "SOCK_STREAM client close", + .run_client = test_stream_client_close_client, + .run_server = test_stream_client_close_server, + }, + { + .name = "SOCK_STREAM server close", + .run_client = test_stream_server_close_client, + .run_server = test_stream_server_close_server, + }, + { + .name = "SOCK_STREAM multiple connections", + .run_client = test_stream_multiconn_client, + .run_server = test_stream_multiconn_server, + }, + {}, +}; + +static const char optstring[] = ""; +static const struct option longopts[] = { + { + .name = "control-host", + .has_arg = required_argument, + .val = 'H', + }, + { + .name = "control-port", + .has_arg = required_argument, + .val = 'P', + }, + { + .name = "mode", + .has_arg = required_argument, + .val = 'm', + }, + { + .name = "peer-cid", + .has_arg = required_argument, + .val = 'p', + }, + { + .name = "help", + .has_arg = no_argument, + .val = '?', + }, + {}, +}; + +static void usage(void) +{ + fprintf(stderr, "Usage: vsock_test [--help] [--control-host=] --control-port= --mode=client|server --peer-cid=\n" + "\n" + " Server: vsock_test --control-port=1234 --mode=server --peer-cid=3\n" + " Client: vsock_test --control-host=192.168.0.1 --control-port=1234 --mode=client --peer-cid=2\n" + "\n" + "Run vsock.ko tests. Must be launched in both guest\n" + "and host. One side must use --mode=client and\n" + "the other side must use --mode=server.\n" + "\n" + "A TCP control socket connection is used to coordinate tests\n" + "between the client and the server. The server requires a\n" + "listen address and the client requires an address to\n" + "connect to.\n" + "\n" + "The CID of the other side must be given with --peer-cid=.\n"); + exit(EXIT_FAILURE); +} + +int main(int argc, char **argv) +{ + const char *control_host = NULL; + const char *control_port = NULL; + struct test_opts opts = { + .mode = TEST_MODE_UNSET, + .peer_cid = VMADDR_CID_ANY, + }; + + init_signals(); + + for (;;) { + int opt = getopt_long(argc, argv, optstring, longopts, NULL); + + if (opt == -1) + break; + + switch (opt) { + case 'H': + control_host = optarg; + break; + case 'm': + if (strcmp(optarg, "client") == 0) + opts.mode = TEST_MODE_CLIENT; + else if (strcmp(optarg, "server") == 0) + opts.mode = TEST_MODE_SERVER; + else { + fprintf(stderr, "--mode must be \"client\" or \"server\"\n"); + return EXIT_FAILURE; + } + break; + case 'p': + opts.peer_cid = parse_cid(optarg); + break; + case 'P': + control_port = optarg; + break; + case '?': + default: + usage(); + } + } + + if (!control_port) + usage(); + if (opts.mode == TEST_MODE_UNSET) + usage(); + if (opts.peer_cid == VMADDR_CID_ANY) + usage(); + + if (!control_host) { + if (opts.mode != TEST_MODE_SERVER) + usage(); + control_host = "0.0.0.0"; + } + + control_init(control_host, control_port, + opts.mode == TEST_MODE_SERVER); + + run_tests(test_cases, &opts); + + control_cleanup(); + return EXIT_SUCCESS; +} -- GitLab From 770ce0078cbf97262e86c9cc210684ce3b4266f5 Mon Sep 17 00:00:00 2001 From: Stefano Garzarella Date: Wed, 18 Dec 2019 19:07:05 +0100 Subject: [PATCH 1220/1437] vsock_test: wait for the remote to close the connection Before check if a send returns -EPIPE, we need to make sure the connection is closed. To do that, we use epoll API to wait EPOLLRDHUP or EPOLLHUP events on the socket. Reported-by: Jorgen Hansen Signed-off-by: Stefano Garzarella Signed-off-by: David S. Miller --- tools/testing/vsock/util.c | 39 ++++++++++++++++++++++++++++++++ tools/testing/vsock/util.h | 1 + tools/testing/vsock/vsock_test.c | 12 ++++++---- 3 files changed, 48 insertions(+), 4 deletions(-) diff --git a/tools/testing/vsock/util.c b/tools/testing/vsock/util.c index 6026ef3ce5122..b132c96c87fcf 100644 --- a/tools/testing/vsock/util.c +++ b/tools/testing/vsock/util.c @@ -13,6 +13,8 @@ #include #include #include +#include +#include #include "timeout.h" #include "control.h" @@ -44,6 +46,43 @@ unsigned int parse_cid(const char *str) return n; } +/* Wait for the remote to close the connection */ +void vsock_wait_remote_close(int fd) +{ + struct epoll_event ev; + int epollfd, nfds; + + epollfd = epoll_create1(0); + if (epollfd == -1) { + perror("epoll_create1"); + exit(EXIT_FAILURE); + } + + ev.events = EPOLLRDHUP | EPOLLHUP; + ev.data.fd = fd; + if (epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &ev) == -1) { + perror("epoll_ctl"); + exit(EXIT_FAILURE); + } + + nfds = epoll_wait(epollfd, &ev, 1, TIMEOUT * 1000); + if (nfds == -1) { + perror("epoll_wait"); + exit(EXIT_FAILURE); + } + + if (nfds == 0) { + fprintf(stderr, "epoll_wait timed out\n"); + exit(EXIT_FAILURE); + } + + assert(nfds == 1); + assert(ev.events & (EPOLLRDHUP | EPOLLHUP)); + assert(ev.data.fd == fd); + + close(epollfd); +} + /* Connect to and return the file descriptor. */ int vsock_stream_connect(unsigned int cid, unsigned int port) { diff --git a/tools/testing/vsock/util.h b/tools/testing/vsock/util.h index 4df12e4b5ebec..331e945f3ae6a 100644 --- a/tools/testing/vsock/util.h +++ b/tools/testing/vsock/util.h @@ -36,6 +36,7 @@ unsigned int parse_cid(const char *str); int vsock_stream_connect(unsigned int cid, unsigned int port); int vsock_stream_accept(unsigned int cid, unsigned int port, struct sockaddr_vm *clientaddrp); +void vsock_wait_remote_close(int fd); void send_byte(int fd, int expected_ret, int flags); void recv_byte(int fd, int expected_ret, int flags); void run_tests(const struct test_case *test_cases, diff --git a/tools/testing/vsock/vsock_test.c b/tools/testing/vsock/vsock_test.c index fae8ddc3ef72f..629d7ce58202e 100644 --- a/tools/testing/vsock/vsock_test.c +++ b/tools/testing/vsock/vsock_test.c @@ -66,7 +66,6 @@ static void test_stream_client_close_client(const struct test_opts *opts) send_byte(fd, 1, 0); close(fd); - control_writeln("CLOSED"); } static void test_stream_client_close_server(const struct test_opts *opts) @@ -79,7 +78,10 @@ static void test_stream_client_close_server(const struct test_opts *opts) exit(EXIT_FAILURE); } - control_expectln("CLOSED"); + /* Wait for the remote to close the connection, before check + * -EPIPE error on send. + */ + vsock_wait_remote_close(fd); send_byte(fd, -EPIPE, 0); recv_byte(fd, 1, 0); @@ -97,7 +99,10 @@ static void test_stream_server_close_client(const struct test_opts *opts) exit(EXIT_FAILURE); } - control_expectln("CLOSED"); + /* Wait for the remote to close the connection, before check + * -EPIPE error on send. + */ + vsock_wait_remote_close(fd); send_byte(fd, -EPIPE, 0); recv_byte(fd, 1, 0); @@ -117,7 +122,6 @@ static void test_stream_server_close_server(const struct test_opts *opts) send_byte(fd, 1, 0); close(fd); - control_writeln("CLOSED"); } /* With the standard socket sizes, VMCI is able to support about 100 -- GitLab From 5a2b2425634dd8d340d3889f2317a752a1a87618 Mon Sep 17 00:00:00 2001 From: Stefano Garzarella Date: Wed, 18 Dec 2019 19:07:06 +0100 Subject: [PATCH 1221/1437] testing/vsock: add parameters to list and skip tests Some tests can fail with transports that have a slightly different behavior, so let's add the possibility to specify which tests to skip. Signed-off-by: Stefano Garzarella Signed-off-by: David S. Miller --- tools/testing/vsock/control.c | 15 +++++- tools/testing/vsock/control.h | 1 + tools/testing/vsock/util.c | 75 +++++++++++++++++++++------ tools/testing/vsock/util.h | 6 ++- tools/testing/vsock/vsock_diag_test.c | 19 ++++++- tools/testing/vsock/vsock_test.c | 20 ++++++- 6 files changed, 116 insertions(+), 20 deletions(-) diff --git a/tools/testing/vsock/control.c b/tools/testing/vsock/control.c index 45f328c6ff23b..4874872fc5a34 100644 --- a/tools/testing/vsock/control.c +++ b/tools/testing/vsock/control.c @@ -205,11 +205,22 @@ void control_expectln(const char *str) char *line; line = control_readln(); - if (strcmp(str, line) != 0) { + + control_cmpln(line, str, true); + + free(line); +} + +bool control_cmpln(char *line, const char *str, bool fail) +{ + if (strcmp(str, line) == 0) + return true; + + if (fail) { fprintf(stderr, "expected \"%s\" on control socket, got \"%s\"\n", str, line); exit(EXIT_FAILURE); } - free(line); + return false; } diff --git a/tools/testing/vsock/control.h b/tools/testing/vsock/control.h index dac3964a891d3..51814b4f9ac15 100644 --- a/tools/testing/vsock/control.h +++ b/tools/testing/vsock/control.h @@ -10,5 +10,6 @@ void control_cleanup(void); void control_writeln(const char *str); char *control_readln(void); void control_expectln(const char *str); +bool control_cmpln(char *line, const char *str, bool fail); #endif /* CONTROL_H */ diff --git a/tools/testing/vsock/util.c b/tools/testing/vsock/util.c index b132c96c87fcf..93cbd6f603f97 100644 --- a/tools/testing/vsock/util.c +++ b/tools/testing/vsock/util.c @@ -299,32 +299,77 @@ void run_tests(const struct test_case *test_cases, for (i = 0; test_cases[i].name; i++) { void (*run)(const struct test_opts *opts); + char *line; - printf("%s...", test_cases[i].name); + printf("%d - %s...", i, test_cases[i].name); fflush(stdout); - if (opts->mode == TEST_MODE_CLIENT) { - /* Full barrier before executing the next test. This - * ensures that client and server are executing the - * same test case. In particular, it means whoever is - * faster will not see the peer still executing the - * last test. This is important because port numbers - * can be used by multiple test cases. - */ - control_expectln("NEXT"); + /* Full barrier before executing the next test. This + * ensures that client and server are executing the + * same test case. In particular, it means whoever is + * faster will not see the peer still executing the + * last test. This is important because port numbers + * can be used by multiple test cases. + */ + if (test_cases[i].skip) + control_writeln("SKIP"); + else control_writeln("NEXT"); - run = test_cases[i].run_client; - } else { - control_writeln("NEXT"); - control_expectln("NEXT"); + line = control_readln(); + if (control_cmpln(line, "SKIP", false) || test_cases[i].skip) { - run = test_cases[i].run_server; + printf("skipped\n"); + + free(line); + continue; } + control_cmpln(line, "NEXT", true); + free(line); + + if (opts->mode == TEST_MODE_CLIENT) + run = test_cases[i].run_client; + else + run = test_cases[i].run_server; + if (run) run(opts); printf("ok\n"); } } + +void list_tests(const struct test_case *test_cases) +{ + int i; + + printf("ID\tTest name\n"); + + for (i = 0; test_cases[i].name; i++) + printf("%d\t%s\n", i, test_cases[i].name); + + exit(EXIT_FAILURE); +} + +void skip_test(struct test_case *test_cases, size_t test_cases_len, + const char *test_id_str) +{ + unsigned long test_id; + char *endptr = NULL; + + errno = 0; + test_id = strtoul(test_id_str, &endptr, 10); + if (errno || *endptr != '\0') { + fprintf(stderr, "malformed test ID \"%s\"\n", test_id_str); + exit(EXIT_FAILURE); + } + + if (test_id >= test_cases_len) { + fprintf(stderr, "test ID (%lu) larger than the max allowed (%lu)\n", + test_id, test_cases_len - 1); + exit(EXIT_FAILURE); + } + + test_cases[test_id].skip = true; +} diff --git a/tools/testing/vsock/util.h b/tools/testing/vsock/util.h index 331e945f3ae6a..e53dd09d26d90 100644 --- a/tools/testing/vsock/util.h +++ b/tools/testing/vsock/util.h @@ -29,6 +29,8 @@ struct test_case { /* Called when test mode is TEST_MODE_SERVER */ void (*run_server)(const struct test_opts *opts); + + bool skip; }; void init_signals(void); @@ -41,5 +43,7 @@ void send_byte(int fd, int expected_ret, int flags); void recv_byte(int fd, int expected_ret, int flags); void run_tests(const struct test_case *test_cases, const struct test_opts *opts); - +void list_tests(const struct test_case *test_cases); +void skip_test(struct test_case *test_cases, size_t test_cases_len, + const char *test_id_str); #endif /* UTIL_H */ diff --git a/tools/testing/vsock/vsock_diag_test.c b/tools/testing/vsock/vsock_diag_test.c index abd7dc2a96310..b824836272597 100644 --- a/tools/testing/vsock/vsock_diag_test.c +++ b/tools/testing/vsock/vsock_diag_test.c @@ -463,6 +463,16 @@ static const struct option longopts[] = { .has_arg = required_argument, .val = 'p', }, + { + .name = "list", + .has_arg = no_argument, + .val = 'l', + }, + { + .name = "skip", + .has_arg = required_argument, + .val = 's', + }, { .name = "help", .has_arg = no_argument, @@ -473,7 +483,7 @@ static const struct option longopts[] = { static void usage(void) { - fprintf(stderr, "Usage: vsock_diag_test [--help] [--control-host=] --control-port= --mode=client|server --peer-cid=\n" + fprintf(stderr, "Usage: vsock_diag_test [--help] [--control-host=] --control-port= --mode=client|server --peer-cid= [--list] [--skip=]\n" "\n" " Server: vsock_diag_test --control-port=1234 --mode=server --peer-cid=3\n" " Client: vsock_diag_test --control-host=192.168.0.1 --control-port=1234 --mode=client --peer-cid=2\n" @@ -528,6 +538,13 @@ int main(int argc, char **argv) case 'P': control_port = optarg; break; + case 'l': + list_tests(test_cases); + break; + case 's': + skip_test(test_cases, ARRAY_SIZE(test_cases) - 1, + optarg); + break; case '?': default: usage(); diff --git a/tools/testing/vsock/vsock_test.c b/tools/testing/vsock/vsock_test.c index 629d7ce58202e..3ac56651f3f9b 100644 --- a/tools/testing/vsock/vsock_test.c +++ b/tools/testing/vsock/vsock_test.c @@ -13,6 +13,7 @@ #include #include #include +#include #include "timeout.h" #include "control.h" @@ -222,6 +223,16 @@ static const struct option longopts[] = { .has_arg = required_argument, .val = 'p', }, + { + .name = "list", + .has_arg = no_argument, + .val = 'l', + }, + { + .name = "skip", + .has_arg = required_argument, + .val = 's', + }, { .name = "help", .has_arg = no_argument, @@ -232,7 +243,7 @@ static const struct option longopts[] = { static void usage(void) { - fprintf(stderr, "Usage: vsock_test [--help] [--control-host=] --control-port= --mode=client|server --peer-cid=\n" + fprintf(stderr, "Usage: vsock_test [--help] [--control-host=] --control-port= --mode=client|server --peer-cid= [--list] [--skip=]\n" "\n" " Server: vsock_test --control-port=1234 --mode=server --peer-cid=3\n" " Client: vsock_test --control-host=192.168.0.1 --control-port=1234 --mode=client --peer-cid=2\n" @@ -287,6 +298,13 @@ int main(int argc, char **argv) case 'P': control_port = optarg; break; + case 'l': + list_tests(test_cases); + break; + case 's': + skip_test(test_cases, ARRAY_SIZE(test_cases) - 1, + optarg); + break; case '?': default: usage(); -- GitLab From 8d00b93fece1cc3547a53abd6ef3358d23ff4d25 Mon Sep 17 00:00:00 2001 From: Stefano Garzarella Date: Wed, 18 Dec 2019 19:07:07 +0100 Subject: [PATCH 1222/1437] testing/vsock: print list of options and description Since we now have several options, in the help we print the list of all supported options and a brief description of them. Signed-off-by: Stefano Garzarella Signed-off-by: David S. Miller --- tools/testing/vsock/vsock_diag_test.c | 13 ++++++++++++- tools/testing/vsock/vsock_test.c | 13 ++++++++++++- 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/tools/testing/vsock/vsock_diag_test.c b/tools/testing/vsock/vsock_diag_test.c index b824836272597..cec6f5a738e1e 100644 --- a/tools/testing/vsock/vsock_diag_test.c +++ b/tools/testing/vsock/vsock_diag_test.c @@ -497,7 +497,18 @@ static void usage(void) "listen address and the client requires an address to\n" "connect to.\n" "\n" - "The CID of the other side must be given with --peer-cid=.\n"); + "The CID of the other side must be given with --peer-cid=.\n" + "\n" + "Options:\n" + " --help This help message\n" + " --control-host Server IP address to connect to\n" + " --control-port Server port to listen on/connect to\n" + " --mode client|server Server or client mode\n" + " --peer-cid CID of the other side\n" + " --list List of tests that will be executed\n" + " --skip Test ID to skip;\n" + " use multiple --skip options to skip more tests\n" + ); exit(EXIT_FAILURE); } diff --git a/tools/testing/vsock/vsock_test.c b/tools/testing/vsock/vsock_test.c index 3ac56651f3f9b..a63e05d6a0f9d 100644 --- a/tools/testing/vsock/vsock_test.c +++ b/tools/testing/vsock/vsock_test.c @@ -257,7 +257,18 @@ static void usage(void) "listen address and the client requires an address to\n" "connect to.\n" "\n" - "The CID of the other side must be given with --peer-cid=.\n"); + "The CID of the other side must be given with --peer-cid=.\n" + "\n" + "Options:\n" + " --help This help message\n" + " --control-host Server IP address to connect to\n" + " --control-port Server port to listen on/connect to\n" + " --mode client|server Server or client mode\n" + " --peer-cid CID of the other side\n" + " --list List of tests that will be executed\n" + " --skip Test ID to skip;\n" + " use multiple --skip options to skip more tests\n" + ); exit(EXIT_FAILURE); } -- GitLab From d6269a93ffc37effac85903122023a251dfa4aca Mon Sep 17 00:00:00 2001 From: Stefano Garzarella Date: Wed, 18 Dec 2019 19:07:08 +0100 Subject: [PATCH 1223/1437] vsock_test: add SOCK_STREAM MSG_PEEK test Test if the MSG_PEEK flags of recv(2) works as expected. Signed-off-by: Stefano Garzarella Signed-off-by: David S. Miller --- tools/testing/vsock/vsock_test.c | 34 ++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/tools/testing/vsock/vsock_test.c b/tools/testing/vsock/vsock_test.c index a63e05d6a0f9d..1d8b93f1af315 100644 --- a/tools/testing/vsock/vsock_test.c +++ b/tools/testing/vsock/vsock_test.c @@ -178,6 +178,35 @@ static void test_stream_multiconn_server(const struct test_opts *opts) close(fds[i]); } +static void test_stream_msg_peek_client(const struct test_opts *opts) +{ + int fd; + + fd = vsock_stream_connect(opts->peer_cid, 1234); + if (fd < 0) { + perror("connect"); + exit(EXIT_FAILURE); + } + + send_byte(fd, 1, 0); + close(fd); +} + +static void test_stream_msg_peek_server(const struct test_opts *opts) +{ + int fd; + + fd = vsock_stream_accept(VMADDR_CID_ANY, 1234, NULL); + if (fd < 0) { + perror("accept"); + exit(EXIT_FAILURE); + } + + recv_byte(fd, 1, MSG_PEEK); + recv_byte(fd, 1, 0); + close(fd); +} + static struct test_case test_cases[] = { { .name = "SOCK_STREAM connection reset", @@ -198,6 +227,11 @@ static struct test_case test_cases[] = { .run_client = test_stream_multiconn_client, .run_server = test_stream_multiconn_server, }, + { + .name = "SOCK_STREAM MSG_PEEK", + .run_client = test_stream_msg_peek_client, + .run_server = test_stream_msg_peek_server, + }, {}, }; -- GitLab From 90b3b339364c76baa2436445401ea9ade040c216 Mon Sep 17 00:00:00 2001 From: Jiangfeng Xiao Date: Thu, 19 Dec 2019 10:08:07 +0800 Subject: [PATCH 1224/1437] net: hisilicon: Fix a BUG trigered by wrong bytes_compl When doing stress test, we get the following trace: kernel BUG at lib/dynamic_queue_limits.c:26! Internal error: Oops - BUG: 0 [#1] SMP ARM Modules linked in: hip04_eth CPU: 0 PID: 2003 Comm: tDblStackPcap0 Tainted: G O L 4.4.197 #1 Hardware name: Hisilicon A15 task: c3637668 task.stack: de3bc000 PC is at dql_completed+0x18/0x154 LR is at hip04_tx_reclaim+0x110/0x174 [hip04_eth] pc : [] lr : [] psr: 800f0313 sp : de3bdc2c ip : 00000000 fp : c020fb10 r10: 00000000 r9 : c39b4224 r8 : 00000001 r7 : 00000046 r6 : c39b4000 r5 : 0078f392 r4 : 0078f392 r3 : 00000047 r2 : 00000000 r1 : 00000046 r0 : df5d5c80 Flags: Nzcv IRQs on FIQs on Mode SVC_32 ISA ARM Segment user Control: 32c5387d Table: 1e189b80 DAC: 55555555 Process tDblStackPcap0 (pid: 2003, stack limit = 0xde3bc190) Stack: (0xde3bdc2c to 0xde3be000) [] (dql_completed) from [] (hip04_tx_reclaim+0x110/0x174 [hip04_eth]) [] (hip04_tx_reclaim [hip04_eth]) from [] (hip04_rx_poll+0x20/0x388 [hip04_eth]) [] (hip04_rx_poll [hip04_eth]) from [] (net_rx_action+0x120/0x374) [] (net_rx_action) from [] (__do_softirq+0x218/0x318) [] (__do_softirq) from [] (irq_exit+0x88/0xac) [] (irq_exit) from [] (msa_irq_exit+0x11c/0x1d4) [] (msa_irq_exit) from [] (__handle_domain_irq+0x110/0x148) [] (__handle_domain_irq) from [] (gic_handle_irq+0xd4/0x118) [] (gic_handle_irq) from [] (__irq_svc+0x40/0x58) Exception stack(0xde3bdde0 to 0xde3bde28) dde0: 00000000 00008001 c3637668 00000000 00000000 a00f0213 dd3627a0 c0af6380 de00: c086d380 a00f0213 c0a22a50 de3bde6c 00000002 de3bde30 c0558138 c055813c de20: 600f0213 ffffffff [] (__irq_svc) from [] (_raw_spin_unlock_irqrestore+0x44/0x54) Kernel panic - not syncing: Fatal exception in interrupt Pre-modification code: int hip04_mac_start_xmit(struct sk_buff *skb, struct net_device *ndev) { [...] [1] priv->tx_head = TX_NEXT(tx_head); [2] count++; [3] netdev_sent_queue(ndev, skb->len); [...] } An rx interrupt occurs if hip04_mac_start_xmit just executes to the line 2, tx_head has been updated, but corresponding 'skb->len' has not been added to dql_queue. And then hip04_mac_interrupt->__napi_schedule->hip04_rx_poll->hip04_tx_reclaim In hip04_tx_reclaim, because tx_head has been updated, bytes_compl will plus an additional "skb-> len" which has not been added to dql_queue. And then trigger the BUG_ON(bytes_compl > num_queued - dql->num_completed). To solve the problem described above, we put "netdev_sent_queue(ndev, skb->len);" before "priv->tx_head = TX_NEXT(tx_head);" Fixes: a41ea46a9a12 ("net: hisilicon: new hip04 ethernet driver") Signed-off-by: Jiangfeng Xiao Signed-off-by: David S. Miller --- drivers/net/ethernet/hisilicon/hip04_eth.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/hisilicon/hip04_eth.c b/drivers/net/ethernet/hisilicon/hip04_eth.c index 3e9b6d543c770..150a8ccfb8b12 100644 --- a/drivers/net/ethernet/hisilicon/hip04_eth.c +++ b/drivers/net/ethernet/hisilicon/hip04_eth.c @@ -543,9 +543,9 @@ hip04_mac_start_xmit(struct sk_buff *skb, struct net_device *ndev) skb_tx_timestamp(skb); hip04_set_xmit_desc(priv, phys); - priv->tx_head = TX_NEXT(tx_head); count++; netdev_sent_queue(ndev, skb->len); + priv->tx_head = TX_NEXT(tx_head); stats->tx_bytes += skb->len; stats->tx_packets++; -- GitLab From e1b5e598e5a51b453328879682b178b4acc15105 Mon Sep 17 00:00:00 2001 From: John Rutherford Date: Thu, 19 Dec 2019 16:03:57 +1100 Subject: [PATCH 1225/1437] tipc: make legacy address flag readable over netlink To enable iproute2/tipc to generate backwards compatible printouts and validate command parameters for nodes using a node address, it needs to be able to read the legacy address flag from the kernel. The legacy address flag records the way in which the node identity was originally specified. The legacy address flag is requested by the netlink message TIPC_NL_ADDR_LEGACY_GET. If the flag is set the attribute TIPC_NLA_NET_ADDR_LEGACY is set in the return message. Signed-off-by: John Rutherford Acked-by: Jon Maloy Signed-off-by: David S. Miller --- include/uapi/linux/tipc_netlink.h | 2 ++ net/tipc/net.c | 56 +++++++++++++++++++++++++++++++ net/tipc/net.h | 1 + net/tipc/netlink.c | 6 ++++ 4 files changed, 65 insertions(+) diff --git a/include/uapi/linux/tipc_netlink.h b/include/uapi/linux/tipc_netlink.h index 6c2194ab745bd..dc0d23a50e69e 100644 --- a/include/uapi/linux/tipc_netlink.h +++ b/include/uapi/linux/tipc_netlink.h @@ -65,6 +65,7 @@ enum { TIPC_NL_UDP_GET_REMOTEIP, TIPC_NL_KEY_SET, TIPC_NL_KEY_FLUSH, + TIPC_NL_ADDR_LEGACY_GET, __TIPC_NL_CMD_MAX, TIPC_NL_CMD_MAX = __TIPC_NL_CMD_MAX - 1 @@ -176,6 +177,7 @@ enum { TIPC_NLA_NET_ADDR, /* u32 */ TIPC_NLA_NET_NODEID, /* u64 */ TIPC_NLA_NET_NODEID_W1, /* u64 */ + TIPC_NLA_NET_ADDR_LEGACY, /* flag */ __TIPC_NLA_NET_MAX, TIPC_NLA_NET_MAX = __TIPC_NLA_NET_MAX - 1 diff --git a/net/tipc/net.c b/net/tipc/net.c index 2de3cec9929d8..85400e4242de2 100644 --- a/net/tipc/net.c +++ b/net/tipc/net.c @@ -302,3 +302,59 @@ int tipc_nl_net_set(struct sk_buff *skb, struct genl_info *info) return err; } + +static int __tipc_nl_addr_legacy_get(struct net *net, struct tipc_nl_msg *msg) +{ + struct tipc_net *tn = tipc_net(net); + struct nlattr *attrs; + void *hdr; + + hdr = genlmsg_put(msg->skb, msg->portid, msg->seq, &tipc_genl_family, + 0, TIPC_NL_ADDR_LEGACY_GET); + if (!hdr) + return -EMSGSIZE; + + attrs = nla_nest_start(msg->skb, TIPC_NLA_NET); + if (!attrs) + goto msg_full; + + if (tn->legacy_addr_format) + if (nla_put_flag(msg->skb, TIPC_NLA_NET_ADDR_LEGACY)) + goto attr_msg_full; + + nla_nest_end(msg->skb, attrs); + genlmsg_end(msg->skb, hdr); + + return 0; + +attr_msg_full: + nla_nest_cancel(msg->skb, attrs); +msg_full: + genlmsg_cancel(msg->skb, hdr); + + return -EMSGSIZE; +} + +int tipc_nl_net_addr_legacy_get(struct sk_buff *skb, struct genl_info *info) +{ + struct net *net = sock_net(skb->sk); + struct tipc_nl_msg msg; + struct sk_buff *rep; + int err; + + rep = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); + if (!rep) + return -ENOMEM; + + msg.skb = rep; + msg.portid = info->snd_portid; + msg.seq = info->snd_seq; + + err = __tipc_nl_addr_legacy_get(net, &msg); + if (err) { + nlmsg_free(msg.skb); + return err; + } + + return genlmsg_reply(msg.skb, info); +} diff --git a/net/tipc/net.h b/net/tipc/net.h index b7f2e364eb99e..6740d97c706e5 100644 --- a/net/tipc/net.h +++ b/net/tipc/net.h @@ -47,5 +47,6 @@ void tipc_net_stop(struct net *net); int tipc_nl_net_dump(struct sk_buff *skb, struct netlink_callback *cb); int tipc_nl_net_set(struct sk_buff *skb, struct genl_info *info); int __tipc_nl_net_set(struct sk_buff *skb, struct genl_info *info); +int tipc_nl_net_addr_legacy_get(struct sk_buff *skb, struct genl_info *info); #endif diff --git a/net/tipc/netlink.c b/net/tipc/netlink.c index e53231bd23b42..7c35094c20b8b 100644 --- a/net/tipc/netlink.c +++ b/net/tipc/netlink.c @@ -83,6 +83,7 @@ const struct nla_policy tipc_nl_net_policy[TIPC_NLA_NET_MAX + 1] = { [TIPC_NLA_NET_ADDR] = { .type = NLA_U32 }, [TIPC_NLA_NET_NODEID] = { .type = NLA_U64 }, [TIPC_NLA_NET_NODEID_W1] = { .type = NLA_U64 }, + [TIPC_NLA_NET_ADDR_LEGACY] = { .type = NLA_FLAG } }; const struct nla_policy tipc_nl_link_policy[TIPC_NLA_LINK_MAX + 1] = { @@ -273,6 +274,11 @@ static const struct genl_ops tipc_genl_v2_ops[] = { .doit = tipc_nl_node_flush_key, }, #endif + { + .cmd = TIPC_NL_ADDR_LEGACY_GET, + .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, + .doit = tipc_nl_net_addr_legacy_get, + }, }; struct genl_family tipc_genl_family __ro_after_init = { -- GitLab From af1c0e4e00f3cc76cb136ebf2e2c04e8b6446285 Mon Sep 17 00:00:00 2001 From: "Chan Shu Tak, Alex" Date: Thu, 19 Dec 2019 14:16:18 +0800 Subject: [PATCH 1226/1437] llc2: Fix return statement of llc_stat_ev_rx_null_dsap_xid_c (and _test_c) When a frame with NULL DSAP is received, llc_station_rcv is called. In turn, llc_stat_ev_rx_null_dsap_xid_c is called to check if it is a NULL XID frame. The return statement of llc_stat_ev_rx_null_dsap_xid_c returns 1 when the incoming frame is not a NULL XID frame and 0 otherwise. Hence, a NULL XID response is returned unexpectedly, e.g. when the incoming frame is a NULL TEST command. To fix the error, simply remove the conditional operator. A similar error in llc_stat_ev_rx_null_dsap_test_c is also fixed. Signed-off-by: Chan Shu Tak, Alex Signed-off-by: David S. Miller --- net/llc/llc_station.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/llc/llc_station.c b/net/llc/llc_station.c index 204a8351efffc..c29170e767a8c 100644 --- a/net/llc/llc_station.c +++ b/net/llc/llc_station.c @@ -32,7 +32,7 @@ static int llc_stat_ev_rx_null_dsap_xid_c(struct sk_buff *skb) return LLC_PDU_IS_CMD(pdu) && /* command PDU */ LLC_PDU_TYPE_IS_U(pdu) && /* U type PDU */ LLC_U_PDU_CMD(pdu) == LLC_1_PDU_CMD_XID && - !pdu->dsap ? 0 : 1; /* NULL DSAP value */ + !pdu->dsap; /* NULL DSAP value */ } static int llc_stat_ev_rx_null_dsap_test_c(struct sk_buff *skb) @@ -42,7 +42,7 @@ static int llc_stat_ev_rx_null_dsap_test_c(struct sk_buff *skb) return LLC_PDU_IS_CMD(pdu) && /* command PDU */ LLC_PDU_TYPE_IS_U(pdu) && /* U type PDU */ LLC_U_PDU_CMD(pdu) == LLC_1_PDU_CMD_TEST && - !pdu->dsap ? 0 : 1; /* NULL DSAP */ + !pdu->dsap; /* NULL DSAP */ } static int llc_station_ac_send_xid_r(struct sk_buff *skb) -- GitLab From b2598318eb455c306e703ab30e18b88a1dc7df97 Mon Sep 17 00:00:00 2001 From: Yunsheng Lin Date: Thu, 19 Dec 2019 14:57:40 +0800 Subject: [PATCH 1227/1437] net: hns3: check FE bit before calling hns3_add_frag() A BD with FE bit means that it is the last BD of a packet, currently the FE bit is checked before calling hns3_add_frag(), which is unnecessary because the FE bit may have been checked in some case. This patch checks the FE bit before calling hns3_add_frag() after processing the first BD of a SKB and adjust the location of memcpy() to reduce duplication. Signed-off-by: Yunsheng Lin Signed-off-by: Huazhong Tan Signed-off-by: David S. Miller --- .../net/ethernet/hisilicon/hns3/hns3_enet.c | 46 ++++++------------- 1 file changed, 13 insertions(+), 33 deletions(-) diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c b/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c index b113b895dab71..5679a0cef975c 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c @@ -2788,7 +2788,6 @@ static bool hns3_parse_vlan_tag(struct hns3_enet_ring *ring, static int hns3_alloc_skb(struct hns3_enet_ring *ring, unsigned int length, unsigned char *va) { -#define HNS3_NEED_ADD_FRAG 1 struct hns3_desc_cb *desc_cb = &ring->desc_cb[ring->next_to_clean]; struct net_device *netdev = ring_to_netdev(ring); struct sk_buff *skb; @@ -2832,33 +2831,19 @@ static int hns3_alloc_skb(struct hns3_enet_ring *ring, unsigned int length, desc_cb); ring_ptr_move_fw(ring, next_to_clean); - return HNS3_NEED_ADD_FRAG; + return 0; } -static int hns3_add_frag(struct hns3_enet_ring *ring, struct hns3_desc *desc, - bool pending) +static int hns3_add_frag(struct hns3_enet_ring *ring) { struct sk_buff *skb = ring->skb; struct sk_buff *head_skb = skb; struct sk_buff *new_skb; struct hns3_desc_cb *desc_cb; - struct hns3_desc *pre_desc; + struct hns3_desc *desc; u32 bd_base_info; - int pre_bd; - - /* if there is pending bd, the SW param next_to_clean has moved - * to next and the next is NULL - */ - if (pending) { - pre_bd = (ring->next_to_clean - 1 + ring->desc_num) % - ring->desc_num; - pre_desc = &ring->desc[pre_bd]; - bd_base_info = le32_to_cpu(pre_desc->rx.bd_base_info); - } else { - bd_base_info = le32_to_cpu(desc->rx.bd_base_info); - } - while (!(bd_base_info & BIT(HNS3_RXD_FE_B))) { + do { desc = &ring->desc[ring->next_to_clean]; desc_cb = &ring->desc_cb[ring->next_to_clean]; bd_base_info = le32_to_cpu(desc->rx.bd_base_info); @@ -2895,7 +2880,7 @@ static int hns3_add_frag(struct hns3_enet_ring *ring, struct hns3_desc *desc, hns3_nic_reuse_page(skb, ring->frag_num++, ring, 0, desc_cb); ring_ptr_move_fw(ring, next_to_clean); ring->pending_buf++; - } + } while (!(bd_base_info & BIT(HNS3_RXD_FE_B))); return 0; } @@ -3063,28 +3048,23 @@ static int hns3_handle_rx_bd(struct hns3_enet_ring *ring) if (ret < 0) /* alloc buffer fail */ return ret; - if (ret > 0) { /* need add frag */ - ret = hns3_add_frag(ring, desc, false); + if (!(bd_base_info & BIT(HNS3_RXD_FE_B))) { /* need add frag */ + ret = hns3_add_frag(ring); if (ret) return ret; - - /* As the head data may be changed when GRO enable, copy - * the head data in after other data rx completed - */ - memcpy(skb->data, ring->va, - ALIGN(ring->pull_len, sizeof(long))); } } else { - ret = hns3_add_frag(ring, desc, true); + ret = hns3_add_frag(ring); if (ret) return ret; + } - /* As the head data may be changed when GRO enable, copy - * the head data in after other data rx completed - */ + /* As the head data may be changed when GRO enable, copy + * the head data in after other data rx completed + */ + if (skb->len > HNS3_RX_HEAD_SIZE) memcpy(skb->data, ring->va, ALIGN(ring->pull_len, sizeof(long))); - } ret = hns3_handle_bdinfo(ring, skb); if (unlikely(ret)) { -- GitLab From 31c92ccecaab75af4a746af6e071314bd3d8ded3 Mon Sep 17 00:00:00 2001 From: Huazhong Tan Date: Thu, 19 Dec 2019 14:57:41 +0800 Subject: [PATCH 1228/1437] net: hns3: remove useless mutex vport_cfg_mutex in the struct hclge_dev Mutex vport_cfg_mutex has been used to protect uc_mac_list, mc_mac_list and vlan_list from being modified by unloading or reset task at the same time. But now unloading will set up HCLGE_STATE_REMOVING flag and call cancel_work_sync to break down this race condition, so this mutex is unnecessary. Signed-off-by: Huazhong Tan Signed-off-by: David S. Miller --- drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c | 9 --------- drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.h | 2 -- drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_mbx.c | 2 -- 3 files changed, 13 deletions(-) diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c index 4e7a07877e5d7..76b4418d937c3 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c @@ -7534,7 +7534,6 @@ void hclge_uninit_vport_mac_table(struct hclge_dev *hdev) struct hclge_vport *vport; int i; - mutex_lock(&hdev->vport_cfg_mutex); for (i = 0; i < hdev->num_alloc_vport; i++) { vport = &hdev->vport[i]; list_for_each_entry_safe(mac, tmp, &vport->uc_mac_list, node) { @@ -7547,7 +7546,6 @@ void hclge_uninit_vport_mac_table(struct hclge_dev *hdev) kfree(mac); } } - mutex_unlock(&hdev->vport_cfg_mutex); } static int hclge_get_mac_ethertype_cmd_status(struct hclge_dev *hdev, @@ -8308,7 +8306,6 @@ void hclge_uninit_vport_vlan_table(struct hclge_dev *hdev) struct hclge_vport *vport; int i; - mutex_lock(&hdev->vport_cfg_mutex); for (i = 0; i < hdev->num_alloc_vport; i++) { vport = &hdev->vport[i]; list_for_each_entry_safe(vlan, tmp, &vport->vlan_list, node) { @@ -8316,7 +8313,6 @@ void hclge_uninit_vport_vlan_table(struct hclge_dev *hdev) kfree(vlan); } } - mutex_unlock(&hdev->vport_cfg_mutex); } static void hclge_restore_vlan_table(struct hnae3_handle *handle) @@ -8328,7 +8324,6 @@ static void hclge_restore_vlan_table(struct hnae3_handle *handle) u16 state, vlan_id; int i; - mutex_lock(&hdev->vport_cfg_mutex); for (i = 0; i < hdev->num_alloc_vport; i++) { vport = &hdev->vport[i]; vlan_proto = vport->port_base_vlan_cfg.vlan_info.vlan_proto; @@ -8354,8 +8349,6 @@ static void hclge_restore_vlan_table(struct hnae3_handle *handle) break; } } - - mutex_unlock(&hdev->vport_cfg_mutex); } int hclge_en_hw_strip_rxvtag(struct hnae3_handle *handle, bool enable) @@ -9390,7 +9383,6 @@ static int hclge_init_ae_dev(struct hnae3_ae_dev *ae_dev) hdev->mps = ETH_FRAME_LEN + ETH_FCS_LEN + 2 * VLAN_HLEN; mutex_init(&hdev->vport_lock); - mutex_init(&hdev->vport_cfg_mutex); spin_lock_init(&hdev->fd_rule_lock); ret = hclge_pci_init(hdev); @@ -9943,7 +9935,6 @@ static void hclge_uninit_ae_dev(struct hnae3_ae_dev *ae_dev) mutex_destroy(&hdev->vport_lock); hclge_uninit_vport_mac_table(hdev); hclge_uninit_vport_vlan_table(hdev); - mutex_destroy(&hdev->vport_cfg_mutex); ae_dev->priv = NULL; } diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.h b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.h index 3a913976b820d..4e5cfda16eb73 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.h +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.h @@ -821,8 +821,6 @@ struct hclge_dev { u16 share_umv_size; struct mutex umv_mutex; /* protect share_umv_size */ - struct mutex vport_cfg_mutex; /* Protect stored vf table */ - DECLARE_KFIFO(mac_tnl_log, struct hclge_mac_tnl_stats, HCLGE_MAC_TNL_LOG_SIZE); diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_mbx.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_mbx.c index a79d1512119d2..f905dd3386b3b 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_mbx.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_mbx.c @@ -797,13 +797,11 @@ void hclge_mbx_handler(struct hclge_dev *hdev) hclge_get_link_mode(vport, req); break; case HCLGE_MBX_GET_VF_FLR_STATUS: - mutex_lock(&hdev->vport_cfg_mutex); hclge_rm_vport_all_mac_table(vport, true, HCLGE_MAC_ADDR_UC); hclge_rm_vport_all_mac_table(vport, true, HCLGE_MAC_ADDR_MC); hclge_rm_vport_all_vlan_table(vport, true); - mutex_unlock(&hdev->vport_cfg_mutex); break; case HCLGE_MBX_GET_MEDIA_TYPE: ret = hclge_get_vf_media_type(vport, req); -- GitLab From a3374d7d335c24225f7d65bc0abb632d81dacdf6 Mon Sep 17 00:00:00 2001 From: Huazhong Tan Date: Thu, 19 Dec 2019 14:57:42 +0800 Subject: [PATCH 1229/1437] net: hns3: optimization for CMDQ uninitialization When uninitializing CMDQ, HCLGE_STATE_CMD_DISABLE will be set up firstly, then the driver does not send command anymore. So, hclge_free_cmd_desc can be called without holding ring->lock. hclge_destroy_cmd_queue() and hclge_destroy_queue() are unnecessary now, so removes them, the VF driver has implemented currently. BTW, the VF driver should set up HCLGEVF_STATE_CMD_DISABLE as well in the hclgevf_cmd_uninit(), just likes what the PF driver does. Signed-off-by: Huazhong Tan Signed-off-by: David S. Miller --- .../ethernet/hisilicon/hns3/hns3pf/hclge_cmd.c | 16 ++-------------- .../ethernet/hisilicon/hns3/hns3vf/hclgevf_cmd.c | 2 +- 2 files changed, 3 insertions(+), 15 deletions(-) diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_cmd.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_cmd.c index 940ead3970d18..7f509eff562ee 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_cmd.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_cmd.c @@ -479,19 +479,6 @@ static void hclge_cmd_uninit_regs(struct hclge_hw *hw) hclge_write_dev(hw, HCLGE_NIC_CRQ_TAIL_REG, 0); } -static void hclge_destroy_queue(struct hclge_cmq_ring *ring) -{ - spin_lock(&ring->lock); - hclge_free_cmd_desc(ring); - spin_unlock(&ring->lock); -} - -static void hclge_destroy_cmd_queue(struct hclge_hw *hw) -{ - hclge_destroy_queue(&hw->cmq.csq); - hclge_destroy_queue(&hw->cmq.crq); -} - void hclge_cmd_uninit(struct hclge_dev *hdev) { spin_lock_bh(&hdev->hw.cmq.csq.lock); @@ -501,5 +488,6 @@ void hclge_cmd_uninit(struct hclge_dev *hdev) spin_unlock(&hdev->hw.cmq.crq.lock); spin_unlock_bh(&hdev->hw.cmq.csq.lock); - hclge_destroy_cmd_queue(&hdev->hw); + hclge_free_cmd_desc(&hdev->hw.cmq.csq); + hclge_free_cmd_desc(&hdev->hw.cmq.crq); } diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_cmd.c b/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_cmd.c index af2245e3bb952..f38d236ebf4fc 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_cmd.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_cmd.c @@ -443,7 +443,7 @@ void hclgevf_cmd_uninit(struct hclgevf_dev *hdev) { spin_lock_bh(&hdev->hw.cmq.csq.lock); spin_lock(&hdev->hw.cmq.crq.lock); - clear_bit(HCLGEVF_STATE_CMD_DISABLE, &hdev->state); + set_bit(HCLGEVF_STATE_CMD_DISABLE, &hdev->state); hclgevf_cmd_uninit_regs(&hdev->hw); spin_unlock(&hdev->hw.cmq.crq.lock); spin_unlock_bh(&hdev->hw.cmq.csq.lock); -- GitLab From 44b6b88336a08be87a02d69f6a4754c86cda97f1 Mon Sep 17 00:00:00 2001 From: Yufeng Mo Date: Thu, 19 Dec 2019 14:57:43 +0800 Subject: [PATCH 1230/1437] net: hns3: get FD rules location before dump in debugfs Currently, the dump FD tcam mode in debugfs is to query all FD tcams, including empty rules, which is unnecessary. This patch modify to find the position of useful rules before dump FD tcam, so that it does not need to query empty rules. This patch also modifies some help information in debugfs. Signed-off-by: Yufeng Mo Signed-off-by: Huazhong Tan Signed-off-by: David S. Miller --- .../hisilicon/hns3/hns3pf/hclge_debugfs.c | 74 +++++++++++++++++-- 1 file changed, 67 insertions(+), 7 deletions(-) diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_debugfs.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_debugfs.c index 92a99e4fdda56..f3d4cbd28913e 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_debugfs.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_debugfs.c @@ -886,8 +886,8 @@ static void hclge_dbg_dump_mng_table(struct hclge_dev *hdev) } } -static void hclge_dbg_fd_tcam_read(struct hclge_dev *hdev, u8 stage, - bool sel_x, u32 loc) +static int hclge_dbg_fd_tcam_read(struct hclge_dev *hdev, u8 stage, + bool sel_x, u32 loc) { struct hclge_fd_tcam_config_1_cmd *req1; struct hclge_fd_tcam_config_2_cmd *req2; @@ -912,7 +912,7 @@ static void hclge_dbg_fd_tcam_read(struct hclge_dev *hdev, u8 stage, ret = hclge_cmd_send(&hdev->hw, desc, 3); if (ret) - return; + return ret; dev_info(&hdev->pdev->dev, " read result tcam key %s(%u):\n", sel_x ? "x" : "y", loc); @@ -931,16 +931,76 @@ static void hclge_dbg_fd_tcam_read(struct hclge_dev *hdev, u8 stage, req = (u32 *)req3->tcam_data; for (i = 0; i < 5; i++) dev_info(&hdev->pdev->dev, "%08x\n", *req++); + + return ret; +} + +static int hclge_dbg_get_rules_location(struct hclge_dev *hdev, u16 *rule_locs) +{ + struct hclge_fd_rule *rule; + struct hlist_node *node; + int cnt = 0; + + spin_lock_bh(&hdev->fd_rule_lock); + hlist_for_each_entry_safe(rule, node, &hdev->fd_rule_list, rule_node) { + rule_locs[cnt] = rule->location; + cnt++; + } + spin_unlock_bh(&hdev->fd_rule_lock); + + if (cnt != hdev->hclge_fd_rule_num) + return -EINVAL; + + return cnt; } static void hclge_dbg_fd_tcam(struct hclge_dev *hdev) { - u32 i; + int i, ret, rule_cnt; + u16 *rule_locs; + + if (!hnae3_dev_fd_supported(hdev)) { + dev_err(&hdev->pdev->dev, + "Only FD-supported dev supports dump fd tcam\n"); + return; + } + + if (!hdev->hclge_fd_rule_num || + !hdev->fd_cfg.rule_num[HCLGE_FD_STAGE_1]) + return; + + rule_locs = kcalloc(hdev->fd_cfg.rule_num[HCLGE_FD_STAGE_1], + sizeof(u16), GFP_KERNEL); + if (!rule_locs) + return; - for (i = 0; i < hdev->fd_cfg.rule_num[0]; i++) { - hclge_dbg_fd_tcam_read(hdev, 0, true, i); - hclge_dbg_fd_tcam_read(hdev, 0, false, i); + rule_cnt = hclge_dbg_get_rules_location(hdev, rule_locs); + if (rule_cnt <= 0) { + dev_err(&hdev->pdev->dev, + "failed to get rule number, ret = %d\n", rule_cnt); + kfree(rule_locs); + return; } + + for (i = 0; i < rule_cnt; i++) { + ret = hclge_dbg_fd_tcam_read(hdev, 0, true, rule_locs[i]); + if (ret) { + dev_err(&hdev->pdev->dev, + "failed to get fd tcam key x, ret = %d\n", ret); + kfree(rule_locs); + return; + } + + ret = hclge_dbg_fd_tcam_read(hdev, 0, false, rule_locs[i]); + if (ret) { + dev_err(&hdev->pdev->dev, + "failed to get fd tcam key y, ret = %d\n", ret); + kfree(rule_locs); + return; + } + } + + kfree(rule_locs); } void hclge_dbg_dump_rst_info(struct hclge_dev *hdev) -- GitLab From 2a7556bb2b7308c6e0fe646c1ddbbf9522bf1120 Mon Sep 17 00:00:00 2001 From: Yunsheng Lin Date: Thu, 19 Dec 2019 14:57:44 +0800 Subject: [PATCH 1231/1437] net: hns3: implement ndo_features_check ops for hns3 driver The function netif_skb_features() will disable the TSO feature by using dflt_features_check() if the driver does not implement ndo_features_check ops, which may cause performance degradation problem when hns3 hardware can do multiple tagged TSO. Also, the HNS3 hardware only supports checksum on the SKB with a max header len of 480 bytes, so remove the checksum and TSO related features when the header len is over 480 bytes. Signed-off-by: Yunsheng Lin Signed-off-by: Huazhong Tan Signed-off-by: David S. Miller --- .../net/ethernet/hisilicon/hns3/hns3_enet.c | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c b/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c index 5679a0cef975c..840a1fbc4d2e6 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c @@ -1556,6 +1556,37 @@ static int hns3_nic_set_features(struct net_device *netdev, return 0; } +static netdev_features_t hns3_features_check(struct sk_buff *skb, + struct net_device *dev, + netdev_features_t features) +{ +#define HNS3_MAX_HDR_LEN 480U +#define HNS3_MAX_L4_HDR_LEN 60U + + size_t len; + + if (skb->ip_summed != CHECKSUM_PARTIAL) + return features; + + if (skb->encapsulation) + len = skb_inner_transport_header(skb) - skb->data; + else + len = skb_transport_header(skb) - skb->data; + + /* Assume L4 is 60 byte as TCP is the only protocol with a + * a flexible value, and it's max len is 60 bytes. + */ + len += HNS3_MAX_L4_HDR_LEN; + + /* Hardware only supports checksum on the skb with a max header + * len of 480 bytes. + */ + if (len > HNS3_MAX_HDR_LEN) + features &= ~(NETIF_F_CSUM_MASK | NETIF_F_GSO_MASK); + + return features; +} + static void hns3_nic_get_stats64(struct net_device *netdev, struct rtnl_link_stats64 *stats) { @@ -1970,6 +2001,7 @@ static const struct net_device_ops hns3_nic_netdev_ops = { .ndo_do_ioctl = hns3_nic_do_ioctl, .ndo_change_mtu = hns3_nic_change_mtu, .ndo_set_features = hns3_nic_set_features, + .ndo_features_check = hns3_features_check, .ndo_get_stats64 = hns3_nic_get_stats64, .ndo_setup_tc = hns3_nic_setup_tc, .ndo_set_rx_mode = hns3_nic_set_rx_mode, -- GitLab From 89b40c7fe2a8b30ef6d46d45ae8455adcd9c1b47 Mon Sep 17 00:00:00 2001 From: Huazhong Tan Date: Thu, 19 Dec 2019 14:57:45 +0800 Subject: [PATCH 1232/1437] net: hns3: add some VF VLAN information for command "ip link show" This patch adds some VF VLAN information for command "ip link show". Signed-off-by: Huazhong Tan Signed-off-by: David S. Miller --- drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c index 76b4418d937c3..d1aafea918b07 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c @@ -2944,6 +2944,9 @@ static int hclge_get_vf_config(struct hnae3_handle *handle, int vf, ivf->trusted = vport->vf_info.trusted; ivf->min_tx_rate = 0; ivf->max_tx_rate = vport->vf_info.max_tx_rate; + ivf->vlan = vport->port_base_vlan_cfg.vlan_info.vlan_tag; + ivf->vlan_proto = htons(vport->port_base_vlan_cfg.vlan_info.vlan_proto); + ivf->qos = vport->port_base_vlan_cfg.vlan_info.qos; ether_addr_copy(ivf->mac, vport->vf_info.mac); return 0; -- GitLab From ff7dfcdd684b6277ed02c1025746a94cb1bfc397 Mon Sep 17 00:00:00 2001 From: Huazhong Tan Date: Thu, 19 Dec 2019 14:57:46 +0800 Subject: [PATCH 1233/1437] net: hns3: add a log for getting chain failure in hns3_nic_uninit_vector_data() Since the mapping can be overwritten, when fail to get the chain between vector and ring, we should go on to deal with the remaining options. For debugging, this patch adds log info for this failure. Signed-off-by: Huazhong Tan Signed-off-by: David S. Miller --- drivers/net/ethernet/hisilicon/hns3/hns3_enet.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c b/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c index 840a1fbc4d2e6..aee5facc89b5e 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c @@ -3602,7 +3602,12 @@ static void hns3_nic_uninit_vector_data(struct hns3_nic_priv *priv) if (!tqp_vector->rx_group.ring && !tqp_vector->tx_group.ring) continue; - hns3_get_vector_ring_chain(tqp_vector, &vector_ring_chain); + /* Since the mapping can be overwritten, when fail to get the + * chain between vector and ring, we should go on to deal with + * the remaining options. + */ + if (hns3_get_vector_ring_chain(tqp_vector, &vector_ring_chain)) + dev_warn(priv->dev, "failed to get ring chain\n"); h->ae_algo->ops->unmap_ring_from_vector(h, tqp_vector->vector_irq, &vector_ring_chain); -- GitLab From d9b81c963a4c4a0a6dd5d57c9d74d9dced77020d Mon Sep 17 00:00:00 2001 From: Huazhong Tan Date: Thu, 19 Dec 2019 14:57:47 +0800 Subject: [PATCH 1234/1437] net: hns3: only print misc interrupt status when handling fails Printing misc interrupt status of hardware error event in the IRQ handler is unnecessary, since hclge_handle_hw_msix_error() will print out the detail information for this hardware error when handling success. So, this patch removes the print in IRQ handler, and prints it when hclge_handle_hw_msix_error() fails. Signed-off-by: Huazhong Tan Signed-off-by: David S. Miller --- drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c index d1aafea918b07..a510f005209c8 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c @@ -3005,8 +3005,6 @@ static u32 hclge_check_event_cause(struct hclge_dev *hdev, u32 *clearval) /* check for vector0 msix event source */ if (msix_src_reg & HCLGE_VECTOR0_REG_MSIX_MASK) { - dev_info(&hdev->pdev->dev, "received event 0x%x\n", - msix_src_reg); *clearval = msix_src_reg; return HCLGE_VECTOR0_EVENT_ERR; } @@ -3505,10 +3503,15 @@ static enum hnae3_reset_type hclge_get_reset_level(struct hnae3_ae_dev *ae_dev, /* first, resolve any unknown reset type to the known type(s) */ if (test_bit(HNAE3_UNKNOWN_RESET, addr)) { + u32 msix_sts_reg = hclge_read_dev(&hdev->hw, + HCLGE_VECTOR0_PF_OTHER_INT_STS_REG); /* we will intentionally ignore any errors from this function * as we will end up in *some* reset request in any case */ - hclge_handle_hw_msix_error(hdev, addr); + if (hclge_handle_hw_msix_error(hdev, addr)) + dev_info(&hdev->pdev->dev, "received msix interrupt 0x%x\n", + msix_sts_reg); + clear_bit(HNAE3_UNKNOWN_RESET, addr); /* We defered the clearing of the error event which caused * interrupt since it was not posssible to do that in -- GitLab From d3e014ec7d5ebe9644b5486bc530b91e62bbf624 Mon Sep 17 00:00:00 2001 From: Padmanabhan Rajanbabu Date: Thu, 19 Dec 2019 15:47:01 +0530 Subject: [PATCH 1235/1437] net: stmmac: platform: Fix MDIO init for platforms without PHY The current implementation of "stmmac_dt_phy" function initializes the MDIO platform bus data, even in the absence of PHY. This fix will skip MDIO initialization if there is no PHY present. Fixes: 7437127 ("net: stmmac: Convert to phylink and remove phylib logic") Acked-by: Jayati Sahu Signed-off-by: Sriram Dash Signed-off-by: Padmanabhan Rajanbabu Signed-off-by: David S. Miller --- drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c index bedaff0c13bde..cc8d7e7bf9acc 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c @@ -320,7 +320,7 @@ out: static int stmmac_dt_phy(struct plat_stmmacenet_data *plat, struct device_node *np, struct device *dev) { - bool mdio = true; + bool mdio = false; static const struct of_device_id need_mdio_ids[] = { { .compatible = "snps,dwc-qos-ethernet-4.10" }, {}, -- GitLab From 28a3b8408f70b646e78880a7eb0a97c22ace98d1 Mon Sep 17 00:00:00 2001 From: Karsten Graul Date: Thu, 19 Dec 2019 12:51:13 +0100 Subject: [PATCH 1236/1437] net/smc: unregister ib devices in reboot_event In the reboot_event handler, unregister the ib devices and enable the IB layer to release the devices before the reboot. Fixes: a33a803cfe64 ("net/smc: guarantee removal of link groups in reboot") Signed-off-by: Karsten Graul Reviewed-by: Ursula Braun Signed-off-by: David S. Miller --- net/smc/smc_core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/smc/smc_core.c b/net/smc/smc_core.c index bb92c7c6214c3..e419ff277e558 100644 --- a/net/smc/smc_core.c +++ b/net/smc/smc_core.c @@ -1287,7 +1287,7 @@ static int smc_core_reboot_event(struct notifier_block *this, unsigned long event, void *ptr) { smc_lgrs_shutdown(); - + smc_ib_unregister_client(); return 0; } -- GitLab From 05785adf6e570a068adf0502b61fe2b521d7f0ca Mon Sep 17 00:00:00 2001 From: Arthur Kiyanovski Date: Thu, 19 Dec 2019 17:40:55 +0200 Subject: [PATCH 1237/1437] net: ena: fix default tx interrupt moderation interval Current default non-adaptive tx interrupt moderation interval is 196 us. This value is too high and might cause the tx queue to fill up. In this commit we set the default non-adaptive tx interrupt moderation interval to 64 us in order to: 1. Reduce the probability of the queue filling-up (when compared to the current default value of 196 us). 2. Reduce unnecessary tx interrupt overhead (which happens if we set the default tx interval to 0). We determined experimentally that 64 us is an optimal value that reduces interrupt rate by more than 20% without affecting performance. Fixes: 1738cd3ed342 ("net: ena: Add a driver for Amazon Elastic Network Adapters (ENA)") Signed-off-by: Arthur Kiyanovski Signed-off-by: David S. Miller --- drivers/net/ethernet/amazon/ena/ena_com.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/amazon/ena/ena_com.h b/drivers/net/ethernet/amazon/ena/ena_com.h index 7c941eba0bc9b..0ce37d54ed108 100644 --- a/drivers/net/ethernet/amazon/ena/ena_com.h +++ b/drivers/net/ethernet/amazon/ena/ena_com.h @@ -72,7 +72,7 @@ /*****************************************************************************/ /* ENA adaptive interrupt moderation settings */ -#define ENA_INTR_INITIAL_TX_INTERVAL_USECS 196 +#define ENA_INTR_INITIAL_TX_INTERVAL_USECS 64 #define ENA_INTR_INITIAL_RX_INTERVAL_USECS 0 #define ENA_DEFAULT_INTR_DELAY_RESOLUTION 1 -- GitLab From 41c53caa5a61ebc9221b71cc37f4a90549f1121d Mon Sep 17 00:00:00 2001 From: Arthur Kiyanovski Date: Thu, 19 Dec 2019 17:40:56 +0200 Subject: [PATCH 1238/1437] net: ena: fix issues in setting interrupt moderation params in ethtool Issue 1: -------- Reproduction steps: 1. sudo ethtool -C eth0 rx-usecs 128 2. sudo ethtool -C eth0 adaptive-rx on 3. sudo ethtool -C eth0 adaptive-rx off 4. ethtool -c eth0 expected output: rx-usecs 128 actual output: rx-usecs 0 Reason for issue: In stage 3, ethtool userspace calls first the ena_get_coalesce() handler to get the current value of all properties, and then the ena_set_coalesce() handler. When ena_get_coalesce() is called the adaptive interrupt moderation is still on. There is an if in the code that returns the rx_coalesce_usecs only if the adaptive interrupt moderation is off. And since it is still on, rx_coalesce_usecs is not set, meaning it stays 0. Solution to issue: Remove this if static interrupt moderation intervals have nothing to do with dynamic ones. Issue 2: -------- Reproduction steps: 1. sudo ethtool -C eth0 adaptive-rx on 2. sudo ethtool -C eth0 rx-usecs 128 3. ethtool -c eth0 expected output: rx-usecs 128 actual output: rx-usecs 0 Reason for issue: In stage 2, when ena_set_coalesce() is called, the handler tests if rx adaptive interrupt moderation is on, and if it is, it returns before getting to the part in the function that sets the rx non-adaptive interrupt moderation interval. Solution to issue: Remove the return from the function when rx adaptive interrupt moderation is on. Also cleaned up the fixed code in ena_set_coalesce by grouping together adaptive interrupt moderation toggling, and using && instead of nested ifs. Fixes: b3db86dc4b82 ("net: ena: reimplement set/get_coalesce()") Fixes: 0eda847953d8 ("net: ena: fix retrieval of nonadaptive interrupt moderation intervals") Fixes: 1738cd3ed342 ("net: ena: Add a driver for Amazon Elastic Network Adapters (ENA)") Signed-off-by: Arthur Kiyanovski Signed-off-by: David S. Miller --- drivers/net/ethernet/amazon/ena/ena_ethtool.c | 24 ++++++++----------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/drivers/net/ethernet/amazon/ena/ena_ethtool.c b/drivers/net/ethernet/amazon/ena/ena_ethtool.c index a3250dcf7d53b..fc96c66b44cb5 100644 --- a/drivers/net/ethernet/amazon/ena/ena_ethtool.c +++ b/drivers/net/ethernet/amazon/ena/ena_ethtool.c @@ -315,10 +315,9 @@ static int ena_get_coalesce(struct net_device *net_dev, ena_com_get_nonadaptive_moderation_interval_tx(ena_dev) * ena_dev->intr_delay_resolution; - if (!ena_com_get_adaptive_moderation_enabled(ena_dev)) - coalesce->rx_coalesce_usecs = - ena_com_get_nonadaptive_moderation_interval_rx(ena_dev) - * ena_dev->intr_delay_resolution; + coalesce->rx_coalesce_usecs = + ena_com_get_nonadaptive_moderation_interval_rx(ena_dev) + * ena_dev->intr_delay_resolution; coalesce->use_adaptive_rx_coalesce = ena_com_get_adaptive_moderation_enabled(ena_dev); @@ -367,12 +366,6 @@ static int ena_set_coalesce(struct net_device *net_dev, ena_update_tx_rings_intr_moderation(adapter); - if (coalesce->use_adaptive_rx_coalesce) { - if (!ena_com_get_adaptive_moderation_enabled(ena_dev)) - ena_com_enable_adaptive_moderation(ena_dev); - return 0; - } - rc = ena_com_update_nonadaptive_moderation_interval_rx(ena_dev, coalesce->rx_coalesce_usecs); if (rc) @@ -380,10 +373,13 @@ static int ena_set_coalesce(struct net_device *net_dev, ena_update_rx_rings_intr_moderation(adapter); - if (!coalesce->use_adaptive_rx_coalesce) { - if (ena_com_get_adaptive_moderation_enabled(ena_dev)) - ena_com_disable_adaptive_moderation(ena_dev); - } + if (coalesce->use_adaptive_rx_coalesce && + !ena_com_get_adaptive_moderation_enabled(ena_dev)) + ena_com_enable_adaptive_moderation(ena_dev); + + if (!coalesce->use_adaptive_rx_coalesce && + ena_com_get_adaptive_moderation_enabled(ena_dev)) + ena_com_disable_adaptive_moderation(ena_dev); return 0; } -- GitLab From 4c8dc00503db24deaf0b89dddfa84b7cba7cd4ce Mon Sep 17 00:00:00 2001 From: Manish Chopra Date: Thu, 19 Dec 2019 10:35:16 -0800 Subject: [PATCH 1239/1437] qede: Disable hardware gro when xdp prog is installed commit 18c602dee472 ("qede: Use NETIF_F_GRO_HW.") introduced a regression in driver that when xdp program is installed on qede device, device's aggregation feature (hardware GRO) is not getting disabled, which is unexpected with xdp. Fixes: 18c602dee472 ("qede: Use NETIF_F_GRO_HW.") Signed-off-by: Manish Chopra Signed-off-by: Ariel Elior Reviewed-by: Michael Chan Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/qede/qede_main.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/qlogic/qede/qede_main.c b/drivers/net/ethernet/qlogic/qede/qede_main.c index 481b096e984de..34fa3917eb330 100644 --- a/drivers/net/ethernet/qlogic/qede/qede_main.c +++ b/drivers/net/ethernet/qlogic/qede/qede_main.c @@ -1406,6 +1406,7 @@ static int qede_alloc_mem_rxq(struct qede_dev *edev, struct qede_rx_queue *rxq) rxq->rx_buf_seg_size = roundup_pow_of_two(size); } else { rxq->rx_buf_seg_size = PAGE_SIZE; + edev->ndev->features &= ~NETIF_F_GRO_HW; } /* Allocate the parallel driver ring for Rx buffers */ @@ -1450,6 +1451,7 @@ static int qede_alloc_mem_rxq(struct qede_dev *edev, struct qede_rx_queue *rxq) } } + edev->gro_disable = !(edev->ndev->features & NETIF_F_GRO_HW); if (!edev->gro_disable) qede_set_tpa_param(rxq); err: @@ -1702,8 +1704,6 @@ static void qede_init_fp(struct qede_dev *edev) snprintf(fp->name, sizeof(fp->name), "%s-fp-%d", edev->ndev->name, queue_id); } - - edev->gro_disable = !(edev->ndev->features & NETIF_F_GRO_HW); } static int qede_set_real_num_queues(struct qede_dev *edev) -- GitLab From d2ed49cf6c13e379c5819aa5ac20e1f9674ebc89 Mon Sep 17 00:00:00 2001 From: Russell King Date: Thu, 19 Dec 2019 23:24:47 +0000 Subject: [PATCH 1240/1437] mod_devicetable: fix PHY module format When a PHY is probed, if the top bit is set, we end up requesting a module with the string "mdio:-10101110000000100101000101010001" - the top bit is printed to a signed -1 value. This leads to the module not being loaded. Fix the module format string and the macro generating the values for it to ensure that we only print unsigned types and the top bit is always 0/1. We correctly end up with "mdio:10101110000000100101000101010001". Fixes: 8626d3b43280 ("phylib: Support phy module autoloading") Reviewed-by: Andrew Lunn Signed-off-by: Russell King Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller --- include/linux/mod_devicetable.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/linux/mod_devicetable.h b/include/linux/mod_devicetable.h index 5714fd35a83c4..e3596db077dc5 100644 --- a/include/linux/mod_devicetable.h +++ b/include/linux/mod_devicetable.h @@ -587,9 +587,9 @@ struct platform_device_id { #define MDIO_NAME_SIZE 32 #define MDIO_MODULE_PREFIX "mdio:" -#define MDIO_ID_FMT "%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d" +#define MDIO_ID_FMT "%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u" #define MDIO_ID_ARGS(_id) \ - (_id)>>31, ((_id)>>30) & 1, ((_id)>>29) & 1, ((_id)>>28) & 1, \ + ((_id)>>31) & 1, ((_id)>>30) & 1, ((_id)>>29) & 1, ((_id)>>28) & 1, \ ((_id)>>27) & 1, ((_id)>>26) & 1, ((_id)>>25) & 1, ((_id)>>24) & 1, \ ((_id)>>23) & 1, ((_id)>>22) & 1, ((_id)>>21) & 1, ((_id)>>20) & 1, \ ((_id)>>19) & 1, ((_id)>>18) & 1, ((_id)>>17) & 1, ((_id)>>16) & 1, \ -- GitLab From 7d49a32a66d2215c5b3bf9bc67c9036ea9904111 Mon Sep 17 00:00:00 2001 From: Russell King Date: Thu, 19 Dec 2019 23:24:52 +0000 Subject: [PATCH 1241/1437] net: phy: ensure that phy IDs are correctly typed PHY IDs are 32-bit unsigned quantities. Ensure that they are always treated as such, and not passed around as "int"s. Fixes: 13d0ab6750b2 ("net: phy: check return code when requesting PHY driver module") Signed-off-by: Russell King Reviewed-by: Florian Fainelli Reviewed-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/phy/phy_device.c | 8 ++++---- include/linux/phy.h | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c index 0887ed2bb0500..b13c52873ef5d 100644 --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c @@ -553,7 +553,7 @@ static const struct device_type mdio_bus_phy_type = { .pm = MDIO_BUS_PHY_PM_OPS, }; -static int phy_request_driver_module(struct phy_device *dev, int phy_id) +static int phy_request_driver_module(struct phy_device *dev, u32 phy_id) { int ret; @@ -565,15 +565,15 @@ static int phy_request_driver_module(struct phy_device *dev, int phy_id) * then modprobe isn't available. */ if (IS_ENABLED(CONFIG_MODULES) && ret < 0 && ret != -ENOENT) { - phydev_err(dev, "error %d loading PHY driver module for ID 0x%08x\n", - ret, phy_id); + phydev_err(dev, "error %d loading PHY driver module for ID 0x%08lx\n", + ret, (unsigned long)phy_id); return ret; } return 0; } -struct phy_device *phy_device_create(struct mii_bus *bus, int addr, int phy_id, +struct phy_device *phy_device_create(struct mii_bus *bus, int addr, u32 phy_id, bool is_c45, struct phy_c45_device_ids *c45_ids) { diff --git a/include/linux/phy.h b/include/linux/phy.h index 5032d453ac66a..dd4a91f1feaa8 100644 --- a/include/linux/phy.h +++ b/include/linux/phy.h @@ -1000,7 +1000,7 @@ int phy_modify_paged_changed(struct phy_device *phydev, int page, u32 regnum, int phy_modify_paged(struct phy_device *phydev, int page, u32 regnum, u16 mask, u16 set); -struct phy_device *phy_device_create(struct mii_bus *bus, int addr, int phy_id, +struct phy_device *phy_device_create(struct mii_bus *bus, int addr, u32 phy_id, bool is_c45, struct phy_c45_device_ids *c45_ids); #if IS_ENABLED(CONFIG_PHYLIB) -- GitLab From b0689faa8efc5a3391402d7ae93bd373b7248e51 Mon Sep 17 00:00:00 2001 From: Haiyang Zhang Date: Thu, 19 Dec 2019 18:28:10 -0800 Subject: [PATCH 1242/1437] hv_netvsc: Fix unwanted rx_table reset In existing code, the receive indirection table, rx_table, is in struct rndis_device, which will be reset when changing MTU, ringparam, etc. User configured receive indirection table values will be lost. To fix this, move rx_table to struct net_device_context, and check netif_is_rxfh_configured(), so rx_table will be set to default only if no user configured value. Fixes: ff4a44199012 ("netvsc: allow get/set of RSS indirection table") Signed-off-by: Haiyang Zhang Signed-off-by: David S. Miller --- drivers/net/hyperv/hyperv_net.h | 3 ++- drivers/net/hyperv/netvsc_drv.c | 4 ++-- drivers/net/hyperv/rndis_filter.c | 10 +++++++--- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/drivers/net/hyperv/hyperv_net.h b/drivers/net/hyperv/hyperv_net.h index 9caa876ce6e83..dc44819946e69 100644 --- a/drivers/net/hyperv/hyperv_net.h +++ b/drivers/net/hyperv/hyperv_net.h @@ -169,7 +169,6 @@ struct rndis_device { u8 hw_mac_adr[ETH_ALEN]; u8 rss_key[NETVSC_HASH_KEYLEN]; - u16 rx_table[ITAB_NUM]; }; @@ -940,6 +939,8 @@ struct net_device_context { u32 tx_table[VRSS_SEND_TAB_SIZE]; + u16 rx_table[ITAB_NUM]; + /* Ethtool settings */ u8 duplex; u32 speed; diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c index eff8fef4f775f..68bf671a51904 100644 --- a/drivers/net/hyperv/netvsc_drv.c +++ b/drivers/net/hyperv/netvsc_drv.c @@ -1662,7 +1662,7 @@ static int netvsc_get_rxfh(struct net_device *dev, u32 *indir, u8 *key, rndis_dev = ndev->extension; if (indir) { for (i = 0; i < ITAB_NUM; i++) - indir[i] = rndis_dev->rx_table[i]; + indir[i] = ndc->rx_table[i]; } if (key) @@ -1692,7 +1692,7 @@ static int netvsc_set_rxfh(struct net_device *dev, const u32 *indir, return -EINVAL; for (i = 0; i < ITAB_NUM; i++) - rndis_dev->rx_table[i] = indir[i]; + ndc->rx_table[i] = indir[i]; } if (!key) { diff --git a/drivers/net/hyperv/rndis_filter.c b/drivers/net/hyperv/rndis_filter.c index 05bc5ec8bb1f0..857c4bea451cd 100644 --- a/drivers/net/hyperv/rndis_filter.c +++ b/drivers/net/hyperv/rndis_filter.c @@ -773,6 +773,7 @@ static int rndis_set_rss_param_msg(struct rndis_device *rdev, const u8 *rss_key, u16 flag) { struct net_device *ndev = rdev->ndev; + struct net_device_context *ndc = netdev_priv(ndev); struct rndis_request *request; struct rndis_set_request *set; struct rndis_set_complete *set_complete; @@ -812,7 +813,7 @@ static int rndis_set_rss_param_msg(struct rndis_device *rdev, /* Set indirection table entries */ itab = (u32 *)(rssp + 1); for (i = 0; i < ITAB_NUM; i++) - itab[i] = rdev->rx_table[i]; + itab[i] = ndc->rx_table[i]; /* Set hask key values */ keyp = (u8 *)((unsigned long)rssp + rssp->hashkey_offset); @@ -1312,6 +1313,7 @@ struct netvsc_device *rndis_filter_device_add(struct hv_device *dev, struct netvsc_device_info *device_info) { struct net_device *net = hv_get_drvdata(dev); + struct net_device_context *ndc = netdev_priv(net); struct netvsc_device *net_device; struct rndis_device *rndis_device; struct ndis_recv_scale_cap rsscap; @@ -1398,9 +1400,11 @@ struct netvsc_device *rndis_filter_device_add(struct hv_device *dev, /* We will use the given number of channels if available. */ net_device->num_chn = min(net_device->max_chn, device_info->num_chn); - for (i = 0; i < ITAB_NUM; i++) - rndis_device->rx_table[i] = ethtool_rxfh_indir_default( + if (!netif_is_rxfh_configured(net)) { + for (i = 0; i < ITAB_NUM; i++) + ndc->rx_table[i] = ethtool_rxfh_indir_default( i, net_device->num_chn); + } atomic_set(&net_device->open_chn, 1); vmbus_set_sc_create_callback(dev->channel, netvsc_sc_open); -- GitLab From 152044775d0b9a9ed9509caed40efcba2677951d Mon Sep 17 00:00:00 2001 From: Hangbin Liu Date: Fri, 20 Dec 2019 15:08:06 +0800 Subject: [PATCH 1243/1437] selftests: pmtu: fix init mtu value in description There is no a_r3, a_r4 in the testing topology. It should be b_r1, b_r2. Also b_r1 mtu is 1400 and b_r2 mtu is 1500. Fixes: e44e428f59e4 ("selftests: pmtu: add basic IPv4 and IPv6 PMTU tests") Signed-off-by: Hangbin Liu Acked-by: Stefano Brivio Signed-off-by: David S. Miller --- tools/testing/selftests/net/pmtu.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/testing/selftests/net/pmtu.sh b/tools/testing/selftests/net/pmtu.sh index d697815d27855..71a62e7e35b1c 100755 --- a/tools/testing/selftests/net/pmtu.sh +++ b/tools/testing/selftests/net/pmtu.sh @@ -11,9 +11,9 @@ # R1 and R2 (also implemented with namespaces), with different MTUs: # # segment a_r1 segment b_r1 a_r1: 2000 -# .--------------R1--------------. a_r2: 1500 -# A B a_r3: 2000 -# '--------------R2--------------' a_r4: 1400 +# .--------------R1--------------. b_r1: 1400 +# A B a_r2: 2000 +# '--------------R2--------------' b_r2: 1500 # segment a_r2 segment b_r2 # # Check that PMTU exceptions with the correct PMTU are created. Then -- GitLab From 258a980d1ec23e2c786e9536a7dd260bea74bae6 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 20 Dec 2019 14:31:40 +0100 Subject: [PATCH 1244/1437] net: dst: Force 4-byte alignment of dst_metrics When storing a pointer to a dst_metrics structure in dst_entry._metrics, two flags are added in the least significant bits of the pointer value. Hence this assumes all pointers to dst_metrics structures have at least 4-byte alignment. However, on m68k, the minimum alignment of 32-bit values is 2 bytes, not 4 bytes. Hence in some kernel builds, dst_default_metrics may be only 2-byte aligned, leading to obscure boot warnings like: WARNING: CPU: 0 PID: 7 at lib/refcount.c:28 refcount_warn_saturate+0x44/0x9a refcount_t: underflow; use-after-free. Modules linked in: CPU: 0 PID: 7 Comm: ksoftirqd/0 Tainted: G W 5.5.0-rc2-atari-01448-g114a1a1038af891d-dirty #261 Stack from 10835e6c: 10835e6c 0038134f 00023fa6 00394b0f 0000001c 00000009 00321560 00023fea 00394b0f 0000001c 001a70f8 00000009 00000000 10835eb4 00000001 00000000 04208040 0000000a 00394b4a 10835ed4 00043aa8 001a70f8 00394b0f 0000001c 00000009 00394b4a 0026aba8 003215a4 00000003 00000000 0026d5a8 00000001 003215a4 003a4361 003238d6 000001f0 00000000 003215a4 10aa3b00 00025e84 003ddb00 10834000 002416a8 10aa3b00 00000000 00000080 000aa038 0004854a Call Trace: [<00023fa6>] __warn+0xb2/0xb4 [<00023fea>] warn_slowpath_fmt+0x42/0x64 [<001a70f8>] refcount_warn_saturate+0x44/0x9a [<00043aa8>] printk+0x0/0x18 [<001a70f8>] refcount_warn_saturate+0x44/0x9a [<0026aba8>] refcount_sub_and_test.constprop.73+0x38/0x3e [<0026d5a8>] ipv4_dst_destroy+0x5e/0x7e [<00025e84>] __local_bh_enable_ip+0x0/0x8e [<002416a8>] dst_destroy+0x40/0xae Fix this by forcing 4-byte alignment of all dst_metrics structures. Fixes: e5fd387ad5b30ca3 ("ipv6: do not overwrite inetpeer metrics prematurely") Signed-off-by: Geert Uytterhoeven Signed-off-by: David S. Miller --- include/net/dst.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/net/dst.h b/include/net/dst.h index fe62fe2eb781c..8224dad2ae946 100644 --- a/include/net/dst.h +++ b/include/net/dst.h @@ -82,7 +82,7 @@ struct dst_entry { struct dst_metrics { u32 metrics[RTAX_MAX]; refcount_t refcnt; -}; +} __aligned(4); /* Low pointer bits contain DST_METRICS_FLAGS */ extern const struct dst_metrics dst_default_metrics; u32 *dst_cow_metrics_generic(struct dst_entry *dst, unsigned long old); -- GitLab From 8700aff089843399f95bc7701ae87b642b35a716 Mon Sep 17 00:00:00 2001 From: Edward Cree Date: Fri, 20 Dec 2019 16:26:40 +0000 Subject: [PATCH 1245/1437] sfc: fix channel allocation with brute force It was possible for channel allocation logic to get confused between what it had and what it wanted, and end up trying to use the same channel for both PTP and regular TX. This led to a kernel panic: BUG: unable to handle page fault for address: 0000000000047635 #PF: supervisor write access in kernel mode #PF: error_code(0x0002) - not-present page PGD 0 P4D 0 Oops: 0002 [#1] SMP PTI CPU: 0 PID: 0 Comm: swapper/0 Tainted: G W 5.4.0-rc3-ehc14+ #900 Hardware name: Dell Inc. PowerEdge R710/0M233H, BIOS 6.4.0 07/23/2013 RIP: 0010:native_queued_spin_lock_slowpath+0x188/0x1e0 Code: f3 90 48 8b 32 48 85 f6 74 f6 eb e8 c1 ee 12 83 e0 03 83 ee 01 48 c1 e0 05 48 63 f6 48 05 c0 98 02 00 48 03 04 f5 a0 c6 ed 81 <48> 89 10 8b 42 08 85 c0 75 09 f3 90 8b 42 08 85 c0 74 f7 48 8b 32 RSP: 0018:ffffc90000003d28 EFLAGS: 00010006 RAX: 0000000000047635 RBX: 0000000000000246 RCX: 0000000000040000 RDX: ffff888627a298c0 RSI: 0000000000003ffe RDI: ffff88861f6b8dd4 RBP: ffff8886225c6e00 R08: 0000000000040000 R09: 0000000000000000 R10: 0000000616f080c6 R11: 00000000000000c0 R12: ffff88861f6b8dd4 R13: ffffc90000003dc8 R14: ffff88861942bf00 R15: ffff8886150f2000 FS: 0000000000000000(0000) GS:ffff888627a00000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 0000000000047635 CR3: 000000000200a000 CR4: 00000000000006f0 Call Trace: _raw_spin_lock_irqsave+0x22/0x30 skb_queue_tail+0x1b/0x50 sock_queue_err_skb+0x9d/0xf0 __skb_complete_tx_timestamp+0x9d/0xc0 efx_dequeue_buffer+0x126/0x180 [sfc] efx_xmit_done+0x73/0x1c0 [sfc] efx_ef10_ev_process+0x56a/0xfe0 [sfc] ? tick_sched_do_timer+0x60/0x60 ? timerqueue_add+0x5d/0x70 ? enqueue_hrtimer+0x39/0x90 efx_poll+0x111/0x380 [sfc] ? rcu_accelerate_cbs+0x50/0x160 net_rx_action+0x14a/0x400 __do_softirq+0xdd/0x2d0 irq_exit+0xa0/0xb0 do_IRQ+0x53/0xe0 common_interrupt+0xf/0xf In the long run we intend to rewrite the channel allocation code, but for 'net' fix this by allocating extra_channels, and giving them TX queues, even if we do not in fact need them (e.g. on NICs without MAC TX timestamping), and thereby using simpler logic to assign the channels once they're allocated. Fixes: 3990a8fffbda ("sfc: allocate channels for XDP tx queues") Signed-off-by: Edward Cree Signed-off-by: David S. Miller --- drivers/net/ethernet/sfc/efx.c | 37 +++++++++++++-------------- drivers/net/ethernet/sfc/net_driver.h | 4 +-- 2 files changed, 19 insertions(+), 22 deletions(-) diff --git a/drivers/net/ethernet/sfc/efx.c b/drivers/net/ethernet/sfc/efx.c index 992c773620ecf..7a38d7f282a11 100644 --- a/drivers/net/ethernet/sfc/efx.c +++ b/drivers/net/ethernet/sfc/efx.c @@ -1472,6 +1472,12 @@ static int efx_allocate_msix_channels(struct efx_nic *efx, n_xdp_tx = num_possible_cpus(); n_xdp_ev = DIV_ROUND_UP(n_xdp_tx, EFX_TXQ_TYPES); + vec_count = pci_msix_vec_count(efx->pci_dev); + if (vec_count < 0) + return vec_count; + + max_channels = min_t(unsigned int, vec_count, max_channels); + /* Check resources. * We need a channel per event queue, plus a VI per tx queue. * This may be more pessimistic than it needs to be. @@ -1493,11 +1499,6 @@ static int efx_allocate_msix_channels(struct efx_nic *efx, n_xdp_tx, n_xdp_ev); } - n_channels = min(n_channels, max_channels); - - vec_count = pci_msix_vec_count(efx->pci_dev); - if (vec_count < 0) - return vec_count; if (vec_count < n_channels) { netif_err(efx, drv, efx->net_dev, "WARNING: Insufficient MSI-X vectors available (%d < %u).\n", @@ -1507,11 +1508,9 @@ static int efx_allocate_msix_channels(struct efx_nic *efx, n_channels = vec_count; } - efx->n_channels = n_channels; + n_channels = min(n_channels, max_channels); - /* Do not create the PTP TX queue(s) if PTP uses the MC directly. */ - if (extra_channels && !efx_ptp_use_mac_tx_timestamps(efx)) - n_channels--; + efx->n_channels = n_channels; /* Ignore XDP tx channels when creating rx channels. */ n_channels -= efx->n_xdp_channels; @@ -1531,11 +1530,10 @@ static int efx_allocate_msix_channels(struct efx_nic *efx, efx->n_rx_channels = n_channels; } - if (efx->n_xdp_channels) - efx->xdp_channel_offset = efx->tx_channel_offset + - efx->n_tx_channels; - else - efx->xdp_channel_offset = efx->n_channels; + efx->n_rx_channels = min(efx->n_rx_channels, parallelism); + efx->n_tx_channels = min(efx->n_tx_channels, parallelism); + + efx->xdp_channel_offset = n_channels; netif_dbg(efx, drv, efx->net_dev, "Allocating %u RX channels\n", @@ -1550,6 +1548,7 @@ static int efx_allocate_msix_channels(struct efx_nic *efx, static int efx_probe_interrupts(struct efx_nic *efx) { unsigned int extra_channels = 0; + unsigned int rss_spread; unsigned int i, j; int rc; @@ -1631,8 +1630,7 @@ static int efx_probe_interrupts(struct efx_nic *efx) for (i = 0; i < EFX_MAX_EXTRA_CHANNELS; i++) { if (!efx->extra_channel_type[i]) continue; - if (efx->interrupt_mode != EFX_INT_MODE_MSIX || - efx->n_channels <= extra_channels) { + if (j <= efx->tx_channel_offset + efx->n_tx_channels) { efx->extra_channel_type[i]->handle_no_channel(efx); } else { --j; @@ -1643,16 +1641,17 @@ static int efx_probe_interrupts(struct efx_nic *efx) } } + rss_spread = efx->n_rx_channels; /* RSS might be usable on VFs even if it is disabled on the PF */ #ifdef CONFIG_SFC_SRIOV if (efx->type->sriov_wanted) { - efx->rss_spread = ((efx->n_rx_channels > 1 || + efx->rss_spread = ((rss_spread > 1 || !efx->type->sriov_wanted(efx)) ? - efx->n_rx_channels : efx_vf_size(efx)); + rss_spread : efx_vf_size(efx)); return 0; } #endif - efx->rss_spread = efx->n_rx_channels; + efx->rss_spread = rss_spread; return 0; } diff --git a/drivers/net/ethernet/sfc/net_driver.h b/drivers/net/ethernet/sfc/net_driver.h index 1f88212be085f..dfd5182d9e471 100644 --- a/drivers/net/ethernet/sfc/net_driver.h +++ b/drivers/net/ethernet/sfc/net_driver.h @@ -1533,9 +1533,7 @@ static inline bool efx_channel_is_xdp_tx(struct efx_channel *channel) static inline bool efx_channel_has_tx_queues(struct efx_channel *channel) { - return efx_channel_is_xdp_tx(channel) || - (channel->type && channel->type->want_txqs && - channel->type->want_txqs(channel)); + return true; } static inline struct efx_tx_queue * -- GitLab From 11a14dc8d7a76d3117896b975c536f1372f2a7c4 Mon Sep 17 00:00:00 2001 From: Charles McLachlan Date: Fri, 20 Dec 2019 16:27:10 +0000 Subject: [PATCH 1246/1437] sfc: Include XDP packet headroom in buffer step size. Correct a mismatch between rx_page_buf_step and the actual step size used when filling buffer pages. This patch fixes the page overrun that occured when the MTU was set to anything bigger than 1692. Fixes: 3990a8fffbda ("sfc: allocate channels for XDP tx queues") Signed-off-by: Charles McLachlan Signed-off-by: Edward Cree Signed-off-by: David S. Miller --- drivers/net/ethernet/sfc/rx.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/net/ethernet/sfc/rx.c b/drivers/net/ethernet/sfc/rx.c index ef52b24ad9e72..c29bf862a94c5 100644 --- a/drivers/net/ethernet/sfc/rx.c +++ b/drivers/net/ethernet/sfc/rx.c @@ -96,11 +96,12 @@ static inline void efx_sync_rx_buffer(struct efx_nic *efx, void efx_rx_config_page_split(struct efx_nic *efx) { - efx->rx_page_buf_step = ALIGN(efx->rx_dma_len + efx->rx_ip_align, + efx->rx_page_buf_step = ALIGN(efx->rx_dma_len + efx->rx_ip_align + + XDP_PACKET_HEADROOM, EFX_RX_BUF_ALIGNMENT); efx->rx_bufs_per_page = efx->rx_buffer_order ? 1 : ((PAGE_SIZE - sizeof(struct efx_rx_page_state)) / - (efx->rx_page_buf_step + XDP_PACKET_HEADROOM)); + efx->rx_page_buf_step); efx->rx_buffer_truesize = (PAGE_SIZE << efx->rx_buffer_order) / efx->rx_bufs_per_page; efx->rx_pages_per_batch = DIV_ROUND_UP(EFX_RX_PREFERRED_BATCH, @@ -190,14 +191,13 @@ static int efx_init_rx_buffers(struct efx_rx_queue *rx_queue, bool atomic) page_offset = sizeof(struct efx_rx_page_state); do { - page_offset += XDP_PACKET_HEADROOM; - dma_addr += XDP_PACKET_HEADROOM; - index = rx_queue->added_count & rx_queue->ptr_mask; rx_buf = efx_rx_buffer(rx_queue, index); - rx_buf->dma_addr = dma_addr + efx->rx_ip_align; + rx_buf->dma_addr = dma_addr + efx->rx_ip_align + + XDP_PACKET_HEADROOM; rx_buf->page = page; - rx_buf->page_offset = page_offset + efx->rx_ip_align; + rx_buf->page_offset = page_offset + efx->rx_ip_align + + XDP_PACKET_HEADROOM; rx_buf->len = efx->rx_dma_len; rx_buf->flags = 0; ++rx_queue->added_count; -- GitLab From 257bf4176362c3920250d6b972ccb36edd788487 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Mon, 9 Dec 2019 16:37:24 +0100 Subject: [PATCH 1247/1437] pktcdvd: fix regression on 64-bit architectures The support for the compat ioctl did not actually do what it was supposed to do because of a typo, instead it broke native support for CDROM_LAST_WRITTEN and CDROM_SEND_PACKET on all architectures with CONFIG_COMPAT enabled. Fixes: 1b114b0817cc ("pktcdvd: add compat_ioctl handler") Signed-off-by: Arnd Bergmann ---- Please apply for v5.5, I just noticed the regression while rebasing some of the patches I created on top. Signed-off-by: Jens Axboe --- drivers/block/pktcdvd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/block/pktcdvd.c b/drivers/block/pktcdvd.c index ee67bf929fac8..861fc65a1b751 100644 --- a/drivers/block/pktcdvd.c +++ b/drivers/block/pktcdvd.c @@ -2707,7 +2707,7 @@ static const struct block_device_operations pktcdvd_ops = { .release = pkt_close, .ioctl = pkt_ioctl, #ifdef CONFIG_COMPAT - .ioctl = pkt_compat_ioctl, + .compat_ioctl = pkt_compat_ioctl, #endif .check_events = pkt_check_events, }; -- GitLab From 673bdf8ce0a387ef585c13b69a2676096c6edfe9 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Fri, 29 Nov 2019 11:28:22 +0100 Subject: [PATCH 1248/1437] compat_ioctl: block: handle BLKREPORTZONE/BLKRESETZONE These were added to blkdev_ioctl() but not blkdev_compat_ioctl, so add them now. Cc: # v4.10+ Fixes: 3ed05a987e0f ("blk-zoned: implement ioctls") Reviewed-by: Damien Le Moal Signed-off-by: Arnd Bergmann Signed-off-by: Jens Axboe --- block/compat_ioctl.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/block/compat_ioctl.c b/block/compat_ioctl.c index 6ca015f92766e..830f91e05fe37 100644 --- a/block/compat_ioctl.c +++ b/block/compat_ioctl.c @@ -354,6 +354,8 @@ long compat_blkdev_ioctl(struct file *file, unsigned cmd, unsigned long arg) * but we call blkdev_ioctl, which gets the lock for us */ case BLKRRPART: + case BLKREPORTZONE: + case BLKRESETZONE: return blkdev_ioctl(bdev, mode, cmd, (unsigned long)compat_ptr(arg)); case BLKBSZSET_32: -- GitLab From 21d37340912d74b1222d43c11aa9dd0687162573 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Fri, 29 Nov 2019 11:28:22 +0100 Subject: [PATCH 1249/1437] compat_ioctl: block: handle BLKGETZONESZ/BLKGETNRZONES These were added to blkdev_ioctl() in v4.20 but not blkdev_compat_ioctl, so add them now. Cc: # v4.20+ Fixes: 72cd87576d1d ("block: Introduce BLKGETZONESZ ioctl") Fixes: 65e4e3eee83d ("block: Introduce BLKGETNRZONES ioctl") Reviewed-by: Damien Le Moal Signed-off-by: Arnd Bergmann Signed-off-by: Jens Axboe --- block/compat_ioctl.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/block/compat_ioctl.c b/block/compat_ioctl.c index 830f91e05fe37..f5c1140b8624b 100644 --- a/block/compat_ioctl.c +++ b/block/compat_ioctl.c @@ -356,6 +356,8 @@ long compat_blkdev_ioctl(struct file *file, unsigned cmd, unsigned long arg) case BLKRRPART: case BLKREPORTZONE: case BLKRESETZONE: + case BLKGETZONESZ: + case BLKGETNRZONES: return blkdev_ioctl(bdev, mode, cmd, (unsigned long)compat_ptr(arg)); case BLKBSZSET_32: -- GitLab From 4b43f31d65dac587dae09ee27a48fd68a0cfd8c2 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Fri, 29 Nov 2019 11:28:22 +0100 Subject: [PATCH 1250/1437] compat_ioctl: block: handle add zone open, close and finish ioctl These were added to blkdev_ioctl() in linux-5.5 but not blkdev_compat_ioctl, so add them now. Fixes: e876df1fe0ad ("block: add zone open, close and finish ioctl support") Reviewed-by: Damien Le Moal Signed-off-by: Arnd Bergmann Signed-off-by: Jens Axboe --- block/compat_ioctl.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/block/compat_ioctl.c b/block/compat_ioctl.c index f5c1140b8624b..5b13e344229c7 100644 --- a/block/compat_ioctl.c +++ b/block/compat_ioctl.c @@ -356,6 +356,9 @@ long compat_blkdev_ioctl(struct file *file, unsigned cmd, unsigned long arg) case BLKRRPART: case BLKREPORTZONE: case BLKRESETZONE: + case BLKOPENZONE: + case BLKCLOSEZONE: + case BLKFINISHZONE: case BLKGETZONESZ: case BLKGETNRZONES: return blkdev_ioctl(bdev, mode, cmd, -- GitLab From b2c0fcd28772f99236d261509bcd242135677965 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Fri, 29 Nov 2019 11:28:22 +0100 Subject: [PATCH 1251/1437] compat_ioctl: block: handle Persistent Reservations These were added to blkdev_ioctl() in linux-5.5 but not blkdev_compat_ioctl, so add them now. Cc: # v4.4+ Fixes: bbd3e064362e ("block: add an API for Persistent Reservations") Signed-off-by: Arnd Bergmann Fold in followup patch from Arnd with missing pr.h header include. Signed-off-by: Jens Axboe --- block/compat_ioctl.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/block/compat_ioctl.c b/block/compat_ioctl.c index 5b13e344229c7..3ed7a0f144a99 100644 --- a/block/compat_ioctl.c +++ b/block/compat_ioctl.c @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -408,6 +409,14 @@ long compat_blkdev_ioctl(struct file *file, unsigned cmd, unsigned long arg) case BLKTRACETEARDOWN: /* compatible */ ret = blk_trace_ioctl(bdev, cmd, compat_ptr(arg)); return ret; + case IOC_PR_REGISTER: + case IOC_PR_RESERVE: + case IOC_PR_RELEASE: + case IOC_PR_PREEMPT: + case IOC_PR_PREEMPT_ABORT: + case IOC_PR_CLEAR: + return blkdev_ioctl(bdev, mode, cmd, + (unsigned long)compat_ptr(arg)); default: if (disk->fops->compat_ioctl) ret = disk->fops->compat_ioctl(bdev, mode, cmd, arg); -- GitLab From 28f94a44298c99c0db85539874b62f21d94fcaa7 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Thu, 19 Dec 2019 20:51:00 +0900 Subject: [PATCH 1252/1437] kbuild: clarify the difference between obj-y and obj-m w.r.t. descending Kbuild descends into a directory by either 'y' or 'm', but there is an important difference. Kbuild combines the built-in objects into built-in.a in each directory. The built-in.a in the directory visited by obj-y is merged into the built-in.a in the parent directory. This merge happens recursively when Kbuild is ascending back towards the top directory, then built-in objects are linked into vmlinux eventually. This works properly only when the Makefile specifying obj-y is reachable by the chain of obj-y. On the other hand, Kbuild does not take built-in.a from the directory visited by obj-m. This it, all the objects in that directory are supposed to be modular. If Kbuild descends into a directory by obj-m, but the Makefile in the sub-directory specifies obj-y, those objects are just left orphan. The current statement "Kbuild only uses this information to decide that it needs to visit the directory" is misleading. Clarify the difference. Reported-by: Johan Hovold Signed-off-by: Masahiro Yamada Reviewed-by: Johan Hovold --- Documentation/kbuild/makefiles.rst | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/Documentation/kbuild/makefiles.rst b/Documentation/kbuild/makefiles.rst index b9b50553bfc56..d7e6534a8505e 100644 --- a/Documentation/kbuild/makefiles.rst +++ b/Documentation/kbuild/makefiles.rst @@ -297,9 +297,19 @@ more details, with real examples. If CONFIG_EXT2_FS is set to either 'y' (built-in) or 'm' (modular) the corresponding obj- variable will be set, and kbuild will descend down in the ext2 directory. - Kbuild only uses this information to decide that it needs to visit - the directory, it is the Makefile in the subdirectory that - specifies what is modular and what is built-in. + + Kbuild uses this information not only to decide that it needs to visit + the directory, but also to decide whether or not to link objects from + the directory into vmlinux. + + When Kbuild descends into the directory with 'y', all built-in objects + from that directory are combined into the built-in.a, which will be + eventually linked into vmlinux. + + When Kbuild descends into the directory with 'm', in contrast, nothing + from that directory will be linked into vmlinux. If the Makefile in + that directory specifies obj-y, those objects will be left orphan. + It is very likely a bug of the Makefile or of dependencies in Kconfig. It is good practice to use a `CONFIG_` variable when assigning directory names. This allows kbuild to totally skip the directory if the -- GitLab From 3a53acf1d9bea11b57c1f6205e3fe73f9d8a3688 Mon Sep 17 00:00:00 2001 From: Prateek Sood Date: Tue, 10 Dec 2019 09:15:16 +0000 Subject: [PATCH 1253/1437] tracing: Fix lock inversion in trace_event_enable_tgid_record() Task T2 Task T3 trace_options_core_write() subsystem_open() mutex_lock(trace_types_lock) mutex_lock(event_mutex) set_tracer_flag() trace_event_enable_tgid_record() mutex_lock(trace_types_lock) mutex_lock(event_mutex) This gives a circular dependency deadlock between trace_types_lock and event_mutex. To fix this invert the usage of trace_types_lock and event_mutex in trace_options_core_write(). This keeps the sequence of lock usage consistent. Link: http://lkml.kernel.org/r/0101016eef175e38-8ca71caf-a4eb-480d-a1e6-6f0bbc015495-000000@us-west-2.amazonses.com Cc: stable@vger.kernel.org Fixes: d914ba37d7145 ("tracing: Add support for recording tgid of tasks") Signed-off-by: Prateek Sood Signed-off-by: Steven Rostedt (VMware) --- kernel/trace/trace.c | 8 ++++++++ kernel/trace/trace_events.c | 8 ++++---- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index 6c75410f96988..ddb7e7f5fe8d8 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c @@ -4685,6 +4685,10 @@ int trace_keep_overwrite(struct tracer *tracer, u32 mask, int set) int set_tracer_flag(struct trace_array *tr, unsigned int mask, int enabled) { + if ((mask == TRACE_ITER_RECORD_TGID) || + (mask == TRACE_ITER_RECORD_CMD)) + lockdep_assert_held(&event_mutex); + /* do nothing if flag is already set */ if (!!(tr->trace_flags & mask) == !!enabled) return 0; @@ -4752,6 +4756,7 @@ static int trace_set_options(struct trace_array *tr, char *option) cmp += len; + mutex_lock(&event_mutex); mutex_lock(&trace_types_lock); ret = match_string(trace_options, -1, cmp); @@ -4762,6 +4767,7 @@ static int trace_set_options(struct trace_array *tr, char *option) ret = set_tracer_flag(tr, 1 << ret, !neg); mutex_unlock(&trace_types_lock); + mutex_unlock(&event_mutex); /* * If the first trailing whitespace is replaced with '\0' by strstrip, @@ -8076,9 +8082,11 @@ trace_options_core_write(struct file *filp, const char __user *ubuf, size_t cnt, if (val != 0 && val != 1) return -EINVAL; + mutex_lock(&event_mutex); mutex_lock(&trace_types_lock); ret = set_tracer_flag(tr, 1 << index, val); mutex_unlock(&trace_types_lock); + mutex_unlock(&event_mutex); if (ret < 0) return ret; diff --git a/kernel/trace/trace_events.c b/kernel/trace/trace_events.c index c6de3cebc127f..a5b614cc38879 100644 --- a/kernel/trace/trace_events.c +++ b/kernel/trace/trace_events.c @@ -320,7 +320,8 @@ void trace_event_enable_cmd_record(bool enable) struct trace_event_file *file; struct trace_array *tr; - mutex_lock(&event_mutex); + lockdep_assert_held(&event_mutex); + do_for_each_event_file(tr, file) { if (!(file->flags & EVENT_FILE_FL_ENABLED)) @@ -334,7 +335,6 @@ void trace_event_enable_cmd_record(bool enable) clear_bit(EVENT_FILE_FL_RECORDED_CMD_BIT, &file->flags); } } while_for_each_event_file(); - mutex_unlock(&event_mutex); } void trace_event_enable_tgid_record(bool enable) @@ -342,7 +342,8 @@ void trace_event_enable_tgid_record(bool enable) struct trace_event_file *file; struct trace_array *tr; - mutex_lock(&event_mutex); + lockdep_assert_held(&event_mutex); + do_for_each_event_file(tr, file) { if (!(file->flags & EVENT_FILE_FL_ENABLED)) continue; @@ -356,7 +357,6 @@ void trace_event_enable_tgid_record(bool enable) &file->flags); } } while_for_each_event_file(); - mutex_unlock(&event_mutex); } static int __ftrace_event_enable_disable(struct trace_event_file *file, -- GitLab From 01f36a554e3ef32f9fc4b81a4437cf08fd0e4742 Mon Sep 17 00:00:00 2001 From: Sven Schnelle Date: Wed, 18 Dec 2019 08:44:26 +0100 Subject: [PATCH 1254/1437] samples/trace_printk: Wait for IRQ work to finish trace_printk schedules work via irq_work_queue(), but doesn't wait until it was processed. The kprobe_module.tc testcase does: :;: "Load module again, which means the event1 should be recorded";: modprobe trace-printk grep "event1:" trace so the grep which checks the trace file might run before the irq work was processed. Fix this by adding a irq_work_sync(). Link: http://lore.kernel.org/linux-trace-devel/20191218074427.96184-3-svens@linux.ibm.com Cc: stable@vger.kernel.org Fixes: af2a0750f3749 ("selftests/ftrace: Improve kprobe on module testcase to load/unload module") Signed-off-by: Sven Schnelle Signed-off-by: Steven Rostedt (VMware) --- samples/trace_printk/trace-printk.c | 1 + 1 file changed, 1 insertion(+) diff --git a/samples/trace_printk/trace-printk.c b/samples/trace_printk/trace-printk.c index 7affc3b50b61d..cfc1595802632 100644 --- a/samples/trace_printk/trace-printk.c +++ b/samples/trace_printk/trace-printk.c @@ -36,6 +36,7 @@ static int __init trace_printk_init(void) /* Kick off printing in irq context */ irq_work_queue(&irqwork); + irq_work_sync(&irqwork); trace_printk("This is a %s that will use trace_bprintk()\n", "static string"); -- GitLab From fe6e096a5bbf73a142f09c72e7aa2835026eb1a3 Mon Sep 17 00:00:00 2001 From: Sven Schnelle Date: Wed, 18 Dec 2019 08:44:27 +0100 Subject: [PATCH 1255/1437] tracing: Fix endianness bug in histogram trigger At least on PA-RISC and s390 synthetic histogram triggers are failing selftests because trace_event_raw_event_synth() always writes a 64 bit values, but the reader expects a field->size sized value. On little endian machines this doesn't hurt, but on big endian this makes the reader always read zero values. Link: http://lore.kernel.org/linux-trace-devel/20191218074427.96184-4-svens@linux.ibm.com Cc: stable@vger.kernel.org Fixes: 4b147936fa509 ("tracing: Add support for 'synthetic' events") Acked-by: Tom Zanussi Signed-off-by: Sven Schnelle Signed-off-by: Steven Rostedt (VMware) --- kernel/trace/trace_events_hist.c | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/kernel/trace/trace_events_hist.c b/kernel/trace/trace_events_hist.c index f49d1a36d3ae3..f62de5f43e79d 100644 --- a/kernel/trace/trace_events_hist.c +++ b/kernel/trace/trace_events_hist.c @@ -911,7 +911,26 @@ static notrace void trace_event_raw_event_synth(void *__data, strscpy(str_field, str_val, STR_VAR_LEN_MAX); n_u64 += STR_VAR_LEN_MAX / sizeof(u64); } else { - entry->fields[n_u64] = var_ref_vals[var_ref_idx + i]; + struct synth_field *field = event->fields[i]; + u64 val = var_ref_vals[var_ref_idx + i]; + + switch (field->size) { + case 1: + *(u8 *)&entry->fields[n_u64] = (u8)val; + break; + + case 2: + *(u16 *)&entry->fields[n_u64] = (u16)val; + break; + + case 4: + *(u32 *)&entry->fields[n_u64] = (u32)val; + break; + + default: + entry->fields[n_u64] = val; + break; + } n_u64++; } } -- GitLab From 68d7b2d838f1aff4d46a793a2fae33e8ad19223e Mon Sep 17 00:00:00 2001 From: Yunfeng Ye Date: Tue, 17 Dec 2019 22:46:49 +0800 Subject: [PATCH 1256/1437] ext4: fix unused-but-set-variable warning in ext4_add_entry() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Warning is found when compile with "-Wunused-but-set-variable": fs/ext4/namei.c: In function ‘ext4_add_entry’: fs/ext4/namei.c:2167:23: warning: variable ‘sbi’ set but not used [-Wunused-but-set-variable] struct ext4_sb_info *sbi; ^~~ Fix this by moving the variable @sbi under CONFIG_UNICODE. Signed-off-by: Yunfeng Ye Reviewed-by: Ritesh Harjani Link: https://lore.kernel.org/r/cb5eb904-224a-9701-c38f-cb23514b1fff@huawei.com Signed-off-by: Theodore Ts'o --- fs/ext4/namei.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c index 9e6d14748b5a5..1cb42d9407847 100644 --- a/fs/ext4/namei.c +++ b/fs/ext4/namei.c @@ -2164,7 +2164,9 @@ static int ext4_add_entry(handle_t *handle, struct dentry *dentry, struct buffer_head *bh = NULL; struct ext4_dir_entry_2 *de; struct super_block *sb; +#ifdef CONFIG_UNICODE struct ext4_sb_info *sbi; +#endif struct ext4_filename fname; int retval; int dx_fallback=0; @@ -2176,12 +2178,12 @@ static int ext4_add_entry(handle_t *handle, struct dentry *dentry, csum_size = sizeof(struct ext4_dir_entry_tail); sb = dir->i_sb; - sbi = EXT4_SB(sb); blocksize = sb->s_blocksize; if (!dentry->d_name.len) return -EINVAL; #ifdef CONFIG_UNICODE + sbi = EXT4_SB(sb); if (ext4_has_strict_mode(sbi) && IS_CASEFOLDED(dir) && sbi->s_encoding && utf8_validate(sbi->s_encoding, &dentry->d_name)) return -EINVAL; -- GitLab From 23f6b02405343103791c6a9533d73716cdf0c672 Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Wed, 18 Dec 2019 12:12:10 +0100 Subject: [PATCH 1257/1437] ext4: clarify impact of 'commit' mount option The description of 'commit' mount option dates back to ext3 times. Update the description to match current meaning for ext4. Reported-by: Paul Richards Signed-off-by: Jan Kara Link: https://lore.kernel.org/r/20191218111210.14161-1-jack@suse.cz Signed-off-by: Theodore Ts'o --- Documentation/admin-guide/ext4.rst | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/Documentation/admin-guide/ext4.rst b/Documentation/admin-guide/ext4.rst index 059ddcbe769d9..9bc93f0ce0c90 100644 --- a/Documentation/admin-guide/ext4.rst +++ b/Documentation/admin-guide/ext4.rst @@ -181,14 +181,17 @@ When mounting an ext4 filesystem, the following option are accepted: system after its metadata has been committed to the journal. commit=nrsec (*) - Ext4 can be told to sync all its data and metadata every 'nrsec' - seconds. The default value is 5 seconds. This means that if you lose - your power, you will lose as much as the latest 5 seconds of work (your - filesystem will not be damaged though, thanks to the journaling). This - default value (or any low value) will hurt performance, but it's good - for data-safety. Setting it to 0 will have the same effect as leaving - it at the default (5 seconds). Setting it to very large values will - improve performance. + This setting limits the maximum age of the running transaction to + 'nrsec' seconds. The default value is 5 seconds. This means that if + you lose your power, you will lose as much as the latest 5 seconds of + metadata changes (your filesystem will not be damaged though, thanks + to the journaling). This default value (or any low value) will hurt + performance, but it's good for data-safety. Setting it to 0 will have + the same effect as leaving it at the default (5 seconds). Setting it + to very large values will improve performance. Note that due to + delayed allocation even older data can be lost on power failure since + writeback of those data begins only after time set in + /proc/sys/vm/dirty_expire_centisecs. barrier=<0|1(*)>, barrier(*), nobarrier This enables/disables the use of write barriers in the jbd code. -- GitLab From 088e11d4220b4802e23fa00fe610ea89a5094587 Mon Sep 17 00:00:00 2001 From: James Hogan Date: Sat, 21 Dec 2019 15:50:13 +0000 Subject: [PATCH 1258/1437] MAINTAINERS: Orphan KVM for MIPS MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit I haven't been active for 18 months, and don't have the hardware set up to test KVM for MIPS, so mark it as orphaned and remove myself as maintainer. Hopefully somebody from MIPS can pick this up. Signed-off-by: James Hogan Cc: Paolo Bonzini Cc: "Radim Krčmář" Cc: Paul Burton Cc: Ralf Baechle Cc: kvm@vger.kernel.org Cc: linux-mips@vger.kernel.org Signed-off-by: Paolo Bonzini --- MAINTAINERS | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/MAINTAINERS b/MAINTAINERS index eb19fad370d79..ec23101052f27 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -8962,9 +8962,9 @@ F: virt/kvm/arm/ F: include/kvm/arm_* KERNEL VIRTUAL MACHINE FOR MIPS (KVM/mips) -M: James Hogan L: linux-mips@vger.kernel.org -S: Supported +L: kvm@vger.kernel.org +S: Orphan F: arch/mips/include/uapi/asm/kvm* F: arch/mips/include/asm/kvm* F: arch/mips/kvm/ -- GitLab From 19a049f1a44d18e38a311e723c19c33c81020a30 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 4 Dec 2019 15:33:35 +0100 Subject: [PATCH 1259/1437] MAINTAINERS: remove Radim from KVM maintainers Radim's kernel.org email is bouncing, which I take as a signal that he is not really able to deal with KVM at this time. Make MAINTAINERS match the effective value of KVM's bus factor. Signed-off-by: Paolo Bonzini --- MAINTAINERS | 2 -- 1 file changed, 2 deletions(-) diff --git a/MAINTAINERS b/MAINTAINERS index ec23101052f27..f70f105a8b6a6 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -8927,7 +8927,6 @@ F: include/linux/umh.h KERNEL VIRTUAL MACHINE (KVM) M: Paolo Bonzini -M: Radim Krčmář L: kvm@vger.kernel.org W: http://www.linux-kvm.org T: git git://git.kernel.org/pub/scm/virt/kvm/kvm.git @@ -8999,7 +8998,6 @@ F: tools/testing/selftests/kvm/*/s390x/ KERNEL VIRTUAL MACHINE FOR X86 (KVM/x86) M: Paolo Bonzini -M: Radim Krčmář R: Sean Christopherson R: Vitaly Kuznetsov R: Wanpeng Li -- GitLab From 0dd1e3773ae8afc4bfdce782bdeffc10f9cae6ec Mon Sep 17 00:00:00 2001 From: Jan Stancek Date: Sun, 22 Dec 2019 13:33:24 +0100 Subject: [PATCH 1260/1437] pipe: fix empty pipe check in pipe_write() LTP pipeio_1 test is hanging with v5.5-rc2-385-gb8e382a185eb, with read side observing empty pipe and sleeping and write side running out of space and then sleeping as well. In this scenario there are 5 writers and 1 reader. Problem is that after pipe_write() reacquires pipe lock, it re-checks for empty pipe with potentially stale 'head' and doesn't wake up read side anymore. pipe->tail can advance beyond 'head', because there are multiple writers. Use pipe->head for empty pipe check after reacquiring lock to observe current state. Testing: With patch, LTP pipeio_1 ran successfully in loop for 1 hour. Without patch it hanged within a minute. Fixes: 1b6b26ae7053 ("pipe: fix and clarify pipe write wakeup logic") Reported-by: Rachel Sibley Signed-off-by: Jan Stancek Signed-off-by: Linus Torvalds --- fs/pipe.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/pipe.c b/fs/pipe.c index 04d004ee2e8c6..57502c3c0fba1 100644 --- a/fs/pipe.c +++ b/fs/pipe.c @@ -581,7 +581,7 @@ pipe_write(struct kiocb *iocb, struct iov_iter *from) } wait_event_interruptible(pipe->wait, pipe_writable(pipe)); __pipe_lock(pipe); - was_empty = pipe_empty(head, pipe->tail); + was_empty = pipe_empty(pipe->head, pipe->tail); } out: __pipe_unlock(pipe); -- GitLab From 46cf053efec6a3a5f343fead837777efe8252a46 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Sun, 22 Dec 2019 17:02:23 -0800 Subject: [PATCH 1261/1437] Linux 5.5-rc3 --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index e51b53c180bc0..caf14acf19539 100644 --- a/Makefile +++ b/Makefile @@ -2,7 +2,7 @@ VERSION = 5 PATCHLEVEL = 5 SUBLEVEL = 0 -EXTRAVERSION = -rc2 +EXTRAVERSION = -rc3 NAME = Kleptomaniac Octopus # *DOCUMENTATION* -- GitLab From f54c7898ed1c3c9331376c0337a5049c38f66497 Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Sun, 22 Dec 2019 23:37:40 +0100 Subject: [PATCH 1262/1437] bpf: Fix precision tracking for unbounded scalars Anatoly has been fuzzing with kBdysch harness and reported a hang in one of the outcomes. Upon closer analysis, it turns out that precise scalar value tracking is missing a few precision markings for unknown scalars: 0: R1=ctx(id=0,off=0,imm=0) R10=fp0 0: (b7) r0 = 0 1: R0_w=invP0 R1=ctx(id=0,off=0,imm=0) R10=fp0 1: (35) if r0 >= 0xf72e goto pc+0 --> only follow fallthrough 2: R0_w=invP0 R1=ctx(id=0,off=0,imm=0) R10=fp0 2: (35) if r0 >= 0x80fe0000 goto pc+0 --> only follow fallthrough 3: R0_w=invP0 R1=ctx(id=0,off=0,imm=0) R10=fp0 3: (14) w0 -= -536870912 4: R0_w=invP536870912 R1=ctx(id=0,off=0,imm=0) R10=fp0 4: (0f) r1 += r0 5: R0_w=invP536870912 R1_w=inv(id=0) R10=fp0 5: (55) if r1 != 0x104c1500 goto pc+0 --> push other branch for later analysis R0_w=invP536870912 R1_w=inv273421568 R10=fp0 6: R0_w=invP536870912 R1_w=inv273421568 R10=fp0 6: (b7) r0 = 0 7: R0=invP0 R1=inv273421568 R10=fp0 7: (76) if w1 s>= 0xffffff00 goto pc+3 --> only follow goto 11: R0=invP0 R1=inv273421568 R10=fp0 11: (95) exit 6: R0_w=invP536870912 R1_w=inv(id=0) R10=fp0 6: (b7) r0 = 0 propagating r0 7: safe processed 11 insns [...] In the analysis of the second path coming after the successful exit above, the path is being pruned at line 7. Pruning analysis found that both r0 are precise P0 and both R1 are non-precise scalars and given prior path with R1 as non-precise scalar succeeded, this one is therefore safe as well. However, problem is that given condition at insn 7 in the first run, we only followed goto and didn't push the other branch for later analysis, we've never walked the few insns in there and therefore dead-code sanitation rewrites it as goto pc-1, causing the hang depending on the skb address hitting these conditions. The issue is that R1 should have been marked as precise as well such that pruning enforces range check and conluded that new R1 is not in range of old R1. In insn 4, we mark R1 (skb) as unknown scalar via __mark_reg_unbounded() but not mark_reg_unbounded() and therefore regs->precise remains as false. Back in b5dc0163d8fd ("bpf: precise scalar_value tracking"), this was not the case since marking out of __mark_reg_unbounded() had this covered as well. Once in both are set as precise in 4 as they should have been, we conclude that given R1 was in prior fall-through path 0x104c1500 and now is completely unknown, the check at insn 7 concludes that we need to continue walking. Analysis after the fix: 0: R1=ctx(id=0,off=0,imm=0) R10=fp0 0: (b7) r0 = 0 1: R0_w=invP0 R1=ctx(id=0,off=0,imm=0) R10=fp0 1: (35) if r0 >= 0xf72e goto pc+0 2: R0_w=invP0 R1=ctx(id=0,off=0,imm=0) R10=fp0 2: (35) if r0 >= 0x80fe0000 goto pc+0 3: R0_w=invP0 R1=ctx(id=0,off=0,imm=0) R10=fp0 3: (14) w0 -= -536870912 4: R0_w=invP536870912 R1=ctx(id=0,off=0,imm=0) R10=fp0 4: (0f) r1 += r0 5: R0_w=invP536870912 R1_w=invP(id=0) R10=fp0 5: (55) if r1 != 0x104c1500 goto pc+0 R0_w=invP536870912 R1_w=invP273421568 R10=fp0 6: R0_w=invP536870912 R1_w=invP273421568 R10=fp0 6: (b7) r0 = 0 7: R0=invP0 R1=invP273421568 R10=fp0 7: (76) if w1 s>= 0xffffff00 goto pc+3 11: R0=invP0 R1=invP273421568 R10=fp0 11: (95) exit 6: R0_w=invP536870912 R1_w=invP(id=0) R10=fp0 6: (b7) r0 = 0 7: R0_w=invP0 R1_w=invP(id=0) R10=fp0 7: (76) if w1 s>= 0xffffff00 goto pc+3 R0_w=invP0 R1_w=invP(id=0) R10=fp0 8: R0_w=invP0 R1_w=invP(id=0) R10=fp0 8: (a5) if r0 < 0x2007002a goto pc+0 9: R0_w=invP0 R1_w=invP(id=0) R10=fp0 9: (57) r0 &= -16316416 10: R0_w=invP0 R1_w=invP(id=0) R10=fp0 10: (a6) if w0 < 0x1201 goto pc+0 11: R0_w=invP0 R1_w=invP(id=0) R10=fp0 11: (95) exit 11: R0=invP0 R1=invP(id=0) R10=fp0 11: (95) exit processed 16 insns [...] Fixes: 6754172c208d ("bpf: fix precision tracking in presence of bpf2bpf calls") Reported-by: Anatoly Trosinenko Signed-off-by: Daniel Borkmann Signed-off-by: Alexei Starovoitov Link: https://lore.kernel.org/bpf/20191222223740.25297-1-daniel@iogearbox.net --- kernel/bpf/verifier.c | 43 ++++++++++++++++++++++--------------------- 1 file changed, 22 insertions(+), 21 deletions(-) diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 4983940cbdca7..6f63ae7a370c0 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -907,7 +907,8 @@ static const int caller_saved[CALLER_SAVED_REGS] = { BPF_REG_0, BPF_REG_1, BPF_REG_2, BPF_REG_3, BPF_REG_4, BPF_REG_5 }; -static void __mark_reg_not_init(struct bpf_reg_state *reg); +static void __mark_reg_not_init(const struct bpf_verifier_env *env, + struct bpf_reg_state *reg); /* Mark the unknown part of a register (variable offset or scalar value) as * known to have the value @imm. @@ -945,7 +946,7 @@ static void mark_reg_known_zero(struct bpf_verifier_env *env, verbose(env, "mark_reg_known_zero(regs, %u)\n", regno); /* Something bad happened, let's kill all regs */ for (regno = 0; regno < MAX_BPF_REG; regno++) - __mark_reg_not_init(regs + regno); + __mark_reg_not_init(env, regs + regno); return; } __mark_reg_known_zero(regs + regno); @@ -1054,7 +1055,8 @@ static void __mark_reg_unbounded(struct bpf_reg_state *reg) } /* Mark a register as having a completely unknown (scalar) value. */ -static void __mark_reg_unknown(struct bpf_reg_state *reg) +static void __mark_reg_unknown(const struct bpf_verifier_env *env, + struct bpf_reg_state *reg) { /* * Clear type, id, off, and union(map_ptr, range) and @@ -1064,6 +1066,8 @@ static void __mark_reg_unknown(struct bpf_reg_state *reg) reg->type = SCALAR_VALUE; reg->var_off = tnum_unknown; reg->frameno = 0; + reg->precise = env->subprog_cnt > 1 || !env->allow_ptr_leaks ? + true : false; __mark_reg_unbounded(reg); } @@ -1074,19 +1078,16 @@ static void mark_reg_unknown(struct bpf_verifier_env *env, verbose(env, "mark_reg_unknown(regs, %u)\n", regno); /* Something bad happened, let's kill all regs except FP */ for (regno = 0; regno < BPF_REG_FP; regno++) - __mark_reg_not_init(regs + regno); + __mark_reg_not_init(env, regs + regno); return; } - regs += regno; - __mark_reg_unknown(regs); - /* constant backtracking is enabled for root without bpf2bpf calls */ - regs->precise = env->subprog_cnt > 1 || !env->allow_ptr_leaks ? - true : false; + __mark_reg_unknown(env, regs + regno); } -static void __mark_reg_not_init(struct bpf_reg_state *reg) +static void __mark_reg_not_init(const struct bpf_verifier_env *env, + struct bpf_reg_state *reg) { - __mark_reg_unknown(reg); + __mark_reg_unknown(env, reg); reg->type = NOT_INIT; } @@ -1097,10 +1098,10 @@ static void mark_reg_not_init(struct bpf_verifier_env *env, verbose(env, "mark_reg_not_init(regs, %u)\n", regno); /* Something bad happened, let's kill all regs except FP */ for (regno = 0; regno < BPF_REG_FP; regno++) - __mark_reg_not_init(regs + regno); + __mark_reg_not_init(env, regs + regno); return; } - __mark_reg_not_init(regs + regno); + __mark_reg_not_init(env, regs + regno); } #define DEF_NOT_SUBREG (0) @@ -3234,7 +3235,7 @@ static int check_stack_boundary(struct bpf_verifier_env *env, int regno, } if (state->stack[spi].slot_type[0] == STACK_SPILL && state->stack[spi].spilled_ptr.type == SCALAR_VALUE) { - __mark_reg_unknown(&state->stack[spi].spilled_ptr); + __mark_reg_unknown(env, &state->stack[spi].spilled_ptr); for (j = 0; j < BPF_REG_SIZE; j++) state->stack[spi].slot_type[j] = STACK_MISC; goto mark; @@ -3892,7 +3893,7 @@ static void __clear_all_pkt_pointers(struct bpf_verifier_env *env, if (!reg) continue; if (reg_is_pkt_pointer_any(reg)) - __mark_reg_unknown(reg); + __mark_reg_unknown(env, reg); } } @@ -3920,7 +3921,7 @@ static void release_reg_references(struct bpf_verifier_env *env, if (!reg) continue; if (reg->ref_obj_id == ref_obj_id) - __mark_reg_unknown(reg); + __mark_reg_unknown(env, reg); } } @@ -4582,7 +4583,7 @@ static int adjust_ptr_min_max_vals(struct bpf_verifier_env *env, /* Taint dst register if offset had invalid bounds derived from * e.g. dead branches. */ - __mark_reg_unknown(dst_reg); + __mark_reg_unknown(env, dst_reg); return 0; } @@ -4834,13 +4835,13 @@ static int adjust_scalar_min_max_vals(struct bpf_verifier_env *env, /* Taint dst register if offset had invalid bounds derived from * e.g. dead branches. */ - __mark_reg_unknown(dst_reg); + __mark_reg_unknown(env, dst_reg); return 0; } if (!src_known && opcode != BPF_ADD && opcode != BPF_SUB && opcode != BPF_AND) { - __mark_reg_unknown(dst_reg); + __mark_reg_unknown(env, dst_reg); return 0; } @@ -6982,7 +6983,7 @@ static void clean_func_state(struct bpf_verifier_env *env, /* since the register is unused, clear its state * to make further comparison simpler */ - __mark_reg_not_init(&st->regs[i]); + __mark_reg_not_init(env, &st->regs[i]); } for (i = 0; i < st->allocated_stack / BPF_REG_SIZE; i++) { @@ -6990,7 +6991,7 @@ static void clean_func_state(struct bpf_verifier_env *env, /* liveness must not touch this stack slot anymore */ st->stack[i].spilled_ptr.live |= REG_LIVE_DONE; if (!(live & REG_LIVE_READ)) { - __mark_reg_not_init(&st->stack[i].spilled_ptr); + __mark_reg_not_init(env, &st->stack[i].spilled_ptr); for (j = 0; j < BPF_REG_SIZE; j++) st->stack[i].slot_type[j] = STACK_INVALID; } -- GitLab From 996fa477d118a11e63f77fbb393173560e5a655d Mon Sep 17 00:00:00 2001 From: Leonard Crestez Date: Mon, 11 Nov 2019 20:16:10 +0200 Subject: [PATCH 1263/1437] PM / devfreq: Drop explicit selection of PM_OPP CONFIG_PM_OPP is already selected by CONFIG_PM_DEVFREQ since commit b9c69e043266 ("PM / devfreq: Add dependency on PM_OPP"). This means that individual drivers shouldn't "select PM_OPP" explicitly. Signed-off-by: Leonard Crestez [cw00.choi: Edit the patch title] Signed-off-by: Chanwoo Choi --- drivers/devfreq/Kconfig | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/devfreq/Kconfig b/drivers/devfreq/Kconfig index defe1d4387105..066e6c4efaa20 100644 --- a/drivers/devfreq/Kconfig +++ b/drivers/devfreq/Kconfig @@ -83,7 +83,6 @@ config ARM_EXYNOS_BUS_DEVFREQ select DEVFREQ_GOV_PASSIVE select DEVFREQ_EVENT_EXYNOS_PPMU select PM_DEVFREQ_EVENT - select PM_OPP help This adds the common DEVFREQ driver for Exynos Memory bus. Exynos Memory bus has one more group of memory bus (e.g, MIF and INT block). @@ -98,7 +97,6 @@ config ARM_TEGRA_DEVFREQ ARCH_TEGRA_132_SOC || ARCH_TEGRA_124_SOC || \ ARCH_TEGRA_210_SOC || \ COMPILE_TEST - select PM_OPP help This adds the DEVFREQ driver for the Tegra family of SoCs. It reads ACTMON counters of memory controllers and adjusts the @@ -109,7 +107,6 @@ config ARM_TEGRA20_DEVFREQ depends on (TEGRA_MC && TEGRA20_EMC) || COMPILE_TEST depends on COMMON_CLK select DEVFREQ_GOV_SIMPLE_ONDEMAND - select PM_OPP help This adds the DEVFREQ driver for the Tegra20 family of SoCs. It reads Memory Controller counters and adjusts the operating @@ -121,7 +118,6 @@ config ARM_RK3399_DMC_DEVFREQ select DEVFREQ_EVENT_ROCKCHIP_DFI select DEVFREQ_GOV_SIMPLE_ONDEMAND select PM_DEVFREQ_EVENT - select PM_OPP help This adds the DEVFREQ driver for the RK3399 DMC(Dynamic Memory Controller). It sets the frequency for the memory controller and reads the usage counts -- GitLab From 5fdb0684b5b0f41402161f068d3d84bf6ed1c3f4 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Thu, 12 Dec 2019 10:56:31 +0900 Subject: [PATCH 1264/1437] PM / devfreq: tegra: Add COMMON_CLK dependency Compile-testing this driver fails if CONFIG_COMMON_CLK is not set: drivers/devfreq/tegra30-devfreq.o: In function `tegra_devfreq_target': tegra30-devfreq.c:(.text+0x164): undefined reference to `clk_set_min_rate' Fixes: 35f8dbc72721 ("PM / devfreq: tegra: Enable COMPILE_TEST for the driver") Signed-off-by: Arnd Bergmann Reviewed-by: Dmitry Osipenko Signed-off-by: Chanwoo Choi --- drivers/devfreq/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/devfreq/Kconfig b/drivers/devfreq/Kconfig index 066e6c4efaa20..35535833b6f78 100644 --- a/drivers/devfreq/Kconfig +++ b/drivers/devfreq/Kconfig @@ -97,6 +97,7 @@ config ARM_TEGRA_DEVFREQ ARCH_TEGRA_132_SOC || ARCH_TEGRA_124_SOC || \ ARCH_TEGRA_210_SOC || \ COMPILE_TEST + depends on COMMON_CLK help This adds the DEVFREQ driver for the Tegra family of SoCs. It reads ACTMON counters of memory controllers and adjusts the -- GitLab From 74ac57806af124b57af30f2ca9fc9df0afe6269e Mon Sep 17 00:00:00 2001 From: Tvrtko Ursulin Date: Tue, 17 Dec 2019 14:20:57 +0000 Subject: [PATCH 1265/1437] drm/i915/pmu: Ensure monotonic rc6 Avoid rc6 counter going backward in close to 0% RC6 scenarios like: 15.005477996 114,246,613 ns i915/rc6-residency/ 16.005876662 667,657 ns i915/rc6-residency/ 17.006131417 7,286 ns i915/rc6-residency/ 18.006615031 18,446,744,073,708,914,688 ns i915/rc6-residency/ 19.007158361 18,446,744,073,709,447,168 ns i915/rc6-residency/ 20.007806498 0 ns i915/rc6-residency/ 21.008227495 1,440,403 ns i915/rc6-residency/ There are two aspects to this fix. First is not assuming rc6 value zero means GT is asleep since that can also mean GPU is fully busy and we do not want to enter the estimation path in that case. Second is ensuring monotonicity on the estimation path itself. I suspect what is happening is with extremely rapid park/unpark cycles we get no updates on the real rc6 and therefore have to careful not to unconditionally trust use last known real rc6 when creating a new estimation. v2: * Simplify logic by not tracking the estimate but last reported value. Signed-off-by: Tvrtko Ursulin Fixes: 16ffe73c186b ("drm/i915/pmu: Use GT parked for estimating RC6 while asleep") Cc: Chris Wilson Reviewed-by: Chris Wilson # v1 Reviewed-by: Chris Wilson Link: https://patchwork.freedesktop.org/patch/msgid/20191217142057.1000-1-tvrtko.ursulin@linux.intel.com (cherry picked from commit df6a42053513846475ae1fbd224dfbdbcd0c7010) Signed-off-by: Jani Nikula --- drivers/gpu/drm/i915/i915_pmu.c | 73 +++++++++------------------------ drivers/gpu/drm/i915/i915_pmu.h | 2 +- 2 files changed, 21 insertions(+), 54 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_pmu.c b/drivers/gpu/drm/i915/i915_pmu.c index 2814218c5ba18..6f09aa0be80ae 100644 --- a/drivers/gpu/drm/i915/i915_pmu.c +++ b/drivers/gpu/drm/i915/i915_pmu.c @@ -144,61 +144,40 @@ static inline s64 ktime_since(const ktime_t kt) return ktime_to_ns(ktime_sub(ktime_get(), kt)); } -static u64 __pmu_estimate_rc6(struct i915_pmu *pmu) -{ - u64 val; - - /* - * We think we are runtime suspended. - * - * Report the delta from when the device was suspended to now, - * on top of the last known real value, as the approximated RC6 - * counter value. - */ - val = ktime_since(pmu->sleep_last); - val += pmu->sample[__I915_SAMPLE_RC6].cur; - - pmu->sample[__I915_SAMPLE_RC6_ESTIMATED].cur = val; - - return val; -} - -static u64 __pmu_update_rc6(struct i915_pmu *pmu, u64 val) -{ - /* - * If we are coming back from being runtime suspended we must - * be careful not to report a larger value than returned - * previously. - */ - if (val >= pmu->sample[__I915_SAMPLE_RC6_ESTIMATED].cur) { - pmu->sample[__I915_SAMPLE_RC6_ESTIMATED].cur = 0; - pmu->sample[__I915_SAMPLE_RC6].cur = val; - } else { - val = pmu->sample[__I915_SAMPLE_RC6_ESTIMATED].cur; - } - - return val; -} - static u64 get_rc6(struct intel_gt *gt) { struct drm_i915_private *i915 = gt->i915; struct i915_pmu *pmu = &i915->pmu; unsigned long flags; + bool awake = false; u64 val; - val = 0; if (intel_gt_pm_get_if_awake(gt)) { val = __get_rc6(gt); intel_gt_pm_put_async(gt); + awake = true; } spin_lock_irqsave(&pmu->lock, flags); - if (val) - val = __pmu_update_rc6(pmu, val); + if (awake) { + pmu->sample[__I915_SAMPLE_RC6].cur = val; + } else { + /* + * We think we are runtime suspended. + * + * Report the delta from when the device was suspended to now, + * on top of the last known real value, as the approximated RC6 + * counter value. + */ + val = ktime_since(pmu->sleep_last); + val += pmu->sample[__I915_SAMPLE_RC6].cur; + } + + if (val < pmu->sample[__I915_SAMPLE_RC6_LAST_REPORTED].cur) + val = pmu->sample[__I915_SAMPLE_RC6_LAST_REPORTED].cur; else - val = __pmu_estimate_rc6(pmu); + pmu->sample[__I915_SAMPLE_RC6_LAST_REPORTED].cur = val; spin_unlock_irqrestore(&pmu->lock, flags); @@ -210,20 +189,11 @@ static void park_rc6(struct drm_i915_private *i915) struct i915_pmu *pmu = &i915->pmu; if (pmu->enable & config_enabled_mask(I915_PMU_RC6_RESIDENCY)) - __pmu_update_rc6(pmu, __get_rc6(&i915->gt)); + pmu->sample[__I915_SAMPLE_RC6].cur = __get_rc6(&i915->gt); pmu->sleep_last = ktime_get(); } -static void unpark_rc6(struct drm_i915_private *i915) -{ - struct i915_pmu *pmu = &i915->pmu; - - /* Estimate how long we slept and accumulate that into rc6 counters */ - if (pmu->enable & config_enabled_mask(I915_PMU_RC6_RESIDENCY)) - __pmu_estimate_rc6(pmu); -} - #else static u64 get_rc6(struct intel_gt *gt) @@ -232,7 +202,6 @@ static u64 get_rc6(struct intel_gt *gt) } static void park_rc6(struct drm_i915_private *i915) {} -static void unpark_rc6(struct drm_i915_private *i915) {} #endif @@ -281,8 +250,6 @@ void i915_pmu_gt_unparked(struct drm_i915_private *i915) */ __i915_pmu_maybe_start_timer(pmu); - unpark_rc6(i915); - spin_unlock_irq(&pmu->lock); } diff --git a/drivers/gpu/drm/i915/i915_pmu.h b/drivers/gpu/drm/i915/i915_pmu.h index bf52e3983631b..6c1647c5daf25 100644 --- a/drivers/gpu/drm/i915/i915_pmu.h +++ b/drivers/gpu/drm/i915/i915_pmu.h @@ -18,7 +18,7 @@ enum { __I915_SAMPLE_FREQ_ACT = 0, __I915_SAMPLE_FREQ_REQ, __I915_SAMPLE_RC6, - __I915_SAMPLE_RC6_ESTIMATED, + __I915_SAMPLE_RC6_LAST_REPORTED, __I915_NUM_PMU_SAMPLERS }; -- GitLab From d39da686c2f3b8518b6933951d53891aa83227d6 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Wed, 18 Dec 2019 09:35:04 +0000 Subject: [PATCH 1266/1437] drm/i915/gt: Ratelimit display power w/a For very light workloads that frequently park, acquiring the display power well (required to prevent the dmc from trashing the system) takes longer than the execution. A good example is the igt_coherency selftest, which is slowed down by an order of magnitude in the worst case with powerwell cycling. To prevent frequent cycling, while keeping our fast soft-rc6, use a timer to delay release of the display powerwell. Fixes: 311770173fac ("drm/i915/gt: Schedule request retirement when timeline idles") References: https://gitlab.freedesktop.org/drm/intel/issues/848 Signed-off-by: Chris Wilson Cc: Tvrtko Ursulin Reviewed-by: Tvrtko Ursulin Link: https://patchwork.freedesktop.org/patch/msgid/20191218093504.3477048-1-chris@chris-wilson.co.uk (cherry picked from commit 81ff52b705775433a955b2746d37b87bdc89a3d0) Signed-off-by: Jani Nikula --- drivers/gpu/drm/i915/gt/intel_gt_pm.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/gt/intel_gt_pm.c b/drivers/gpu/drm/i915/gt/intel_gt_pm.c index a459a42ad5c22..7e64b7d7d3301 100644 --- a/drivers/gpu/drm/i915/gt/intel_gt_pm.c +++ b/drivers/gpu/drm/i915/gt/intel_gt_pm.c @@ -94,8 +94,9 @@ static int __gt_park(struct intel_wakeref *wf) intel_uncore_forcewake_put(&i915->uncore, FORCEWAKE_ALL); } + /* Defer dropping the display power well for 100ms, it's slow! */ GEM_BUG_ON(!wakeref); - intel_display_power_put(i915, POWER_DOMAIN_GT_IRQ, wakeref); + intel_display_power_put_async(i915, POWER_DOMAIN_GT_IRQ, wakeref); i915_globals_park(); -- GitLab From e85ade1f50aae464ce196672faa7a099fd1721ed Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Wed, 18 Dec 2019 10:40:43 +0000 Subject: [PATCH 1267/1437] drm/i915: Hold reference to intel_frontbuffer as we track activity Since obj->frontbuffer is no longer protected by the struct_mutex, as we are processing the execbuf, it may be removed. Mark the intel_frontbuffer as rcu protected, and so acquire a reference to the struct as we track activity upon it. Closes: https://gitlab.freedesktop.org/drm/intel/issues/827 Fixes: 8e7cb1799b4f ("drm/i915: Extract intel_frontbuffer active tracking") Signed-off-by: Chris Wilson Cc: Matthew Auld Cc: # v5.4+ Reviewed-by: Joonas Lahtinen Link: https://patchwork.freedesktop.org/patch/msgid/20191218104043.3539458-1-chris@chris-wilson.co.uk (cherry picked from commit da42104f589d979bbe402703fd836cec60befae1) Signed-off-by: Jani Nikula --- drivers/gpu/drm/i915/display/intel_display.c | 2 +- .../gpu/drm/i915/display/intel_frontbuffer.c | 16 ++++----- .../gpu/drm/i915/display/intel_frontbuffer.h | 34 +++++++++++++++++-- drivers/gpu/drm/i915/display/intel_overlay.c | 17 +++++++--- drivers/gpu/drm/i915/gem/i915_gem_clflush.c | 3 +- drivers/gpu/drm/i915/gem/i915_gem_domain.c | 4 +-- drivers/gpu/drm/i915/gem/i915_gem_object.c | 26 +++++++++++++- drivers/gpu/drm/i915/gem/i915_gem_object.h | 23 ++++++++++++- .../gpu/drm/i915/gem/i915_gem_object_types.h | 2 +- drivers/gpu/drm/i915/i915_gem.c | 10 +++--- drivers/gpu/drm/i915/i915_vma.c | 10 ++++-- 11 files changed, 116 insertions(+), 31 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_display.c b/drivers/gpu/drm/i915/display/intel_display.c index 6f5e3bd13ad18..effc4250b230b 100644 --- a/drivers/gpu/drm/i915/display/intel_display.c +++ b/drivers/gpu/drm/i915/display/intel_display.c @@ -15112,7 +15112,7 @@ intel_prepare_plane_fb(struct drm_plane *plane, return ret; fb_obj_bump_render_priority(obj); - intel_frontbuffer_flush(obj->frontbuffer, ORIGIN_DIRTYFB); + i915_gem_object_flush_frontbuffer(obj, ORIGIN_DIRTYFB); if (!new_plane_state->base.fence) { /* implicit fencing */ struct dma_fence *fence; diff --git a/drivers/gpu/drm/i915/display/intel_frontbuffer.c b/drivers/gpu/drm/i915/display/intel_frontbuffer.c index 84b164f31895b..6cb02c912accf 100644 --- a/drivers/gpu/drm/i915/display/intel_frontbuffer.c +++ b/drivers/gpu/drm/i915/display/intel_frontbuffer.c @@ -229,11 +229,11 @@ static void frontbuffer_release(struct kref *ref) vma->display_alignment = I915_GTT_MIN_ALIGNMENT; spin_unlock(&obj->vma.lock); - obj->frontbuffer = NULL; + RCU_INIT_POINTER(obj->frontbuffer, NULL); spin_unlock(&to_i915(obj->base.dev)->fb_tracking.lock); i915_gem_object_put(obj); - kfree(front); + kfree_rcu(front, rcu); } struct intel_frontbuffer * @@ -242,11 +242,7 @@ intel_frontbuffer_get(struct drm_i915_gem_object *obj) struct drm_i915_private *i915 = to_i915(obj->base.dev); struct intel_frontbuffer *front; - spin_lock(&i915->fb_tracking.lock); - front = obj->frontbuffer; - if (front) - kref_get(&front->ref); - spin_unlock(&i915->fb_tracking.lock); + front = __intel_frontbuffer_get(obj); if (front) return front; @@ -262,13 +258,13 @@ intel_frontbuffer_get(struct drm_i915_gem_object *obj) i915_active_may_sleep(frontbuffer_retire)); spin_lock(&i915->fb_tracking.lock); - if (obj->frontbuffer) { + if (rcu_access_pointer(obj->frontbuffer)) { kfree(front); - front = obj->frontbuffer; + front = rcu_dereference_protected(obj->frontbuffer, true); kref_get(&front->ref); } else { i915_gem_object_get(obj); - obj->frontbuffer = front; + rcu_assign_pointer(obj->frontbuffer, front); } spin_unlock(&i915->fb_tracking.lock); diff --git a/drivers/gpu/drm/i915/display/intel_frontbuffer.h b/drivers/gpu/drm/i915/display/intel_frontbuffer.h index adc64d61a4a5c..6d41f53944250 100644 --- a/drivers/gpu/drm/i915/display/intel_frontbuffer.h +++ b/drivers/gpu/drm/i915/display/intel_frontbuffer.h @@ -27,10 +27,10 @@ #include #include +#include "gem/i915_gem_object_types.h" #include "i915_active.h" struct drm_i915_private; -struct drm_i915_gem_object; enum fb_op_origin { ORIGIN_GTT, @@ -45,6 +45,7 @@ struct intel_frontbuffer { atomic_t bits; struct i915_active write; struct drm_i915_gem_object *obj; + struct rcu_head rcu; }; void intel_frontbuffer_flip_prepare(struct drm_i915_private *i915, @@ -54,6 +55,35 @@ void intel_frontbuffer_flip_complete(struct drm_i915_private *i915, void intel_frontbuffer_flip(struct drm_i915_private *i915, unsigned frontbuffer_bits); +void intel_frontbuffer_put(struct intel_frontbuffer *front); + +static inline struct intel_frontbuffer * +__intel_frontbuffer_get(const struct drm_i915_gem_object *obj) +{ + struct intel_frontbuffer *front; + + if (likely(!rcu_access_pointer(obj->frontbuffer))) + return NULL; + + rcu_read_lock(); + do { + front = rcu_dereference(obj->frontbuffer); + if (!front) + break; + + if (unlikely(!kref_get_unless_zero(&front->ref))) + continue; + + if (likely(front == rcu_access_pointer(obj->frontbuffer))) + break; + + intel_frontbuffer_put(front); + } while (1); + rcu_read_unlock(); + + return front; +} + struct intel_frontbuffer * intel_frontbuffer_get(struct drm_i915_gem_object *obj); @@ -119,6 +149,4 @@ void intel_frontbuffer_track(struct intel_frontbuffer *old, struct intel_frontbuffer *new, unsigned int frontbuffer_bits); -void intel_frontbuffer_put(struct intel_frontbuffer *front); - #endif /* __INTEL_FRONTBUFFER_H__ */ diff --git a/drivers/gpu/drm/i915/display/intel_overlay.c b/drivers/gpu/drm/i915/display/intel_overlay.c index 848ce07a8ec2e..8a98a1aa7adcd 100644 --- a/drivers/gpu/drm/i915/display/intel_overlay.c +++ b/drivers/gpu/drm/i915/display/intel_overlay.c @@ -279,12 +279,21 @@ static void intel_overlay_flip_prepare(struct intel_overlay *overlay, struct i915_vma *vma) { enum pipe pipe = overlay->crtc->pipe; + struct intel_frontbuffer *from = NULL, *to = NULL; WARN_ON(overlay->old_vma); - intel_frontbuffer_track(overlay->vma ? overlay->vma->obj->frontbuffer : NULL, - vma ? vma->obj->frontbuffer : NULL, - INTEL_FRONTBUFFER_OVERLAY(pipe)); + if (overlay->vma) + from = intel_frontbuffer_get(overlay->vma->obj); + if (vma) + to = intel_frontbuffer_get(vma->obj); + + intel_frontbuffer_track(from, to, INTEL_FRONTBUFFER_OVERLAY(pipe)); + + if (to) + intel_frontbuffer_put(to); + if (from) + intel_frontbuffer_put(from); intel_frontbuffer_flip_prepare(overlay->i915, INTEL_FRONTBUFFER_OVERLAY(pipe)); @@ -766,7 +775,7 @@ static int intel_overlay_do_put_image(struct intel_overlay *overlay, ret = PTR_ERR(vma); goto out_pin_section; } - intel_frontbuffer_flush(new_bo->frontbuffer, ORIGIN_DIRTYFB); + i915_gem_object_flush_frontbuffer(new_bo, ORIGIN_DIRTYFB); if (!overlay->active) { u32 oconfig; diff --git a/drivers/gpu/drm/i915/gem/i915_gem_clflush.c b/drivers/gpu/drm/i915/gem/i915_gem_clflush.c index b9f504ba3b320..18ee708585a94 100644 --- a/drivers/gpu/drm/i915/gem/i915_gem_clflush.c +++ b/drivers/gpu/drm/i915/gem/i915_gem_clflush.c @@ -20,7 +20,8 @@ static void __do_clflush(struct drm_i915_gem_object *obj) { GEM_BUG_ON(!i915_gem_object_has_pages(obj)); drm_clflush_sg(obj->mm.pages); - intel_frontbuffer_flush(obj->frontbuffer, ORIGIN_CPU); + + i915_gem_object_flush_frontbuffer(obj, ORIGIN_CPU); } static int clflush_work(struct dma_fence_work *base) diff --git a/drivers/gpu/drm/i915/gem/i915_gem_domain.c b/drivers/gpu/drm/i915/gem/i915_gem_domain.c index 9937b4c341f1a..f86400a191b04 100644 --- a/drivers/gpu/drm/i915/gem/i915_gem_domain.c +++ b/drivers/gpu/drm/i915/gem/i915_gem_domain.c @@ -664,7 +664,7 @@ i915_gem_set_domain_ioctl(struct drm_device *dev, void *data, i915_gem_object_unlock(obj); if (write_domain) - intel_frontbuffer_invalidate(obj->frontbuffer, ORIGIN_CPU); + i915_gem_object_invalidate_frontbuffer(obj, ORIGIN_CPU); out_unpin: i915_gem_object_unpin_pages(obj); @@ -784,7 +784,7 @@ int i915_gem_object_prepare_write(struct drm_i915_gem_object *obj, } out: - intel_frontbuffer_invalidate(obj->frontbuffer, ORIGIN_CPU); + i915_gem_object_invalidate_frontbuffer(obj, ORIGIN_CPU); obj->mm.dirty = true; /* return with the pages pinned */ return 0; diff --git a/drivers/gpu/drm/i915/gem/i915_gem_object.c b/drivers/gpu/drm/i915/gem/i915_gem_object.c index a50296cce0d88..a596548c07bf8 100644 --- a/drivers/gpu/drm/i915/gem/i915_gem_object.c +++ b/drivers/gpu/drm/i915/gem/i915_gem_object.c @@ -280,7 +280,7 @@ i915_gem_object_flush_write_domain(struct drm_i915_gem_object *obj, for_each_ggtt_vma(vma, obj) intel_gt_flush_ggtt_writes(vma->vm->gt); - intel_frontbuffer_flush(obj->frontbuffer, ORIGIN_CPU); + i915_gem_object_flush_frontbuffer(obj, ORIGIN_CPU); for_each_ggtt_vma(vma, obj) { if (vma->iomap) @@ -308,6 +308,30 @@ i915_gem_object_flush_write_domain(struct drm_i915_gem_object *obj, obj->write_domain = 0; } +void __i915_gem_object_flush_frontbuffer(struct drm_i915_gem_object *obj, + enum fb_op_origin origin) +{ + struct intel_frontbuffer *front; + + front = __intel_frontbuffer_get(obj); + if (front) { + intel_frontbuffer_flush(front, origin); + intel_frontbuffer_put(front); + } +} + +void __i915_gem_object_invalidate_frontbuffer(struct drm_i915_gem_object *obj, + enum fb_op_origin origin) +{ + struct intel_frontbuffer *front; + + front = __intel_frontbuffer_get(obj); + if (front) { + intel_frontbuffer_invalidate(front, origin); + intel_frontbuffer_put(front); + } +} + void i915_gem_init__objects(struct drm_i915_private *i915) { INIT_WORK(&i915->mm.free_work, __i915_gem_free_work); diff --git a/drivers/gpu/drm/i915/gem/i915_gem_object.h b/drivers/gpu/drm/i915/gem/i915_gem_object.h index 458cd51331f18..4b93591fd5c7a 100644 --- a/drivers/gpu/drm/i915/gem/i915_gem_object.h +++ b/drivers/gpu/drm/i915/gem/i915_gem_object.h @@ -13,8 +13,8 @@ #include +#include "display/intel_frontbuffer.h" #include "i915_gem_object_types.h" - #include "i915_gem_gtt.h" void i915_gem_init__objects(struct drm_i915_private *i915); @@ -463,4 +463,25 @@ int i915_gem_object_wait_priority(struct drm_i915_gem_object *obj, unsigned int flags, const struct i915_sched_attr *attr); +void __i915_gem_object_flush_frontbuffer(struct drm_i915_gem_object *obj, + enum fb_op_origin origin); +void __i915_gem_object_invalidate_frontbuffer(struct drm_i915_gem_object *obj, + enum fb_op_origin origin); + +static inline void +i915_gem_object_flush_frontbuffer(struct drm_i915_gem_object *obj, + enum fb_op_origin origin) +{ + if (unlikely(rcu_access_pointer(obj->frontbuffer))) + __i915_gem_object_flush_frontbuffer(obj, origin); +} + +static inline void +i915_gem_object_invalidate_frontbuffer(struct drm_i915_gem_object *obj, + enum fb_op_origin origin) +{ + if (unlikely(rcu_access_pointer(obj->frontbuffer))) + __i915_gem_object_invalidate_frontbuffer(obj, origin); +} + #endif diff --git a/drivers/gpu/drm/i915/gem/i915_gem_object_types.h b/drivers/gpu/drm/i915/gem/i915_gem_object_types.h index 96008374a4120..e3f3944fbd90d 100644 --- a/drivers/gpu/drm/i915/gem/i915_gem_object_types.h +++ b/drivers/gpu/drm/i915/gem/i915_gem_object_types.h @@ -150,7 +150,7 @@ struct drm_i915_gem_object { */ u16 write_domain; - struct intel_frontbuffer *frontbuffer; + struct intel_frontbuffer __rcu *frontbuffer; /** Current tiling stride for the object, if it's tiled. */ unsigned int tiling_and_stride; diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index d034fa413164b..905890e3ac241 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -161,7 +161,7 @@ i915_gem_phys_pwrite(struct drm_i915_gem_object *obj, * We manually control the domain here and pretend that it * remains coherent i.e. in the GTT domain, like shmem_pwrite. */ - intel_frontbuffer_invalidate(obj->frontbuffer, ORIGIN_CPU); + i915_gem_object_invalidate_frontbuffer(obj, ORIGIN_CPU); if (copy_from_user(vaddr, user_data, args->size)) return -EFAULT; @@ -169,7 +169,7 @@ i915_gem_phys_pwrite(struct drm_i915_gem_object *obj, drm_clflush_virt_range(vaddr, args->size); intel_gt_chipset_flush(&to_i915(obj->base.dev)->gt); - intel_frontbuffer_flush(obj->frontbuffer, ORIGIN_CPU); + i915_gem_object_flush_frontbuffer(obj, ORIGIN_CPU); return 0; } @@ -589,7 +589,7 @@ i915_gem_gtt_pwrite_fast(struct drm_i915_gem_object *obj, goto out_unpin; } - intel_frontbuffer_invalidate(obj->frontbuffer, ORIGIN_CPU); + i915_gem_object_invalidate_frontbuffer(obj, ORIGIN_CPU); user_data = u64_to_user_ptr(args->data_ptr); offset = args->offset; @@ -631,7 +631,7 @@ i915_gem_gtt_pwrite_fast(struct drm_i915_gem_object *obj, user_data += page_length; offset += page_length; } - intel_frontbuffer_flush(obj->frontbuffer, ORIGIN_CPU); + i915_gem_object_flush_frontbuffer(obj, ORIGIN_CPU); i915_gem_object_unlock_fence(obj, fence); out_unpin: @@ -721,7 +721,7 @@ i915_gem_shmem_pwrite(struct drm_i915_gem_object *obj, offset = 0; } - intel_frontbuffer_flush(obj->frontbuffer, ORIGIN_CPU); + i915_gem_object_flush_frontbuffer(obj, ORIGIN_CPU); i915_gem_object_unlock_fence(obj, fence); return ret; diff --git a/drivers/gpu/drm/i915/i915_vma.c b/drivers/gpu/drm/i915/i915_vma.c index e5512f26e20a6..01c822256b395 100644 --- a/drivers/gpu/drm/i915/i915_vma.c +++ b/drivers/gpu/drm/i915/i915_vma.c @@ -1104,8 +1104,14 @@ int i915_vma_move_to_active(struct i915_vma *vma, return err; if (flags & EXEC_OBJECT_WRITE) { - if (intel_frontbuffer_invalidate(obj->frontbuffer, ORIGIN_CS)) - i915_active_add_request(&obj->frontbuffer->write, rq); + struct intel_frontbuffer *front; + + front = __intel_frontbuffer_get(obj); + if (unlikely(front)) { + if (intel_frontbuffer_invalidate(front, ORIGIN_CS)) + i915_active_add_request(&front->write, rq); + intel_frontbuffer_put(front); + } dma_resv_add_excl_fence(vma->resv, &rq->fence); obj->write_domain = I915_GEM_DOMAIN_RENDER; -- GitLab From fa633a0f89192379828103957874682d389eae83 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Mon, 23 Dec 2019 15:13:26 +0900 Subject: [PATCH 1268/1437] libbpf: Fix build on read-only filesystems I got the following error when I tried to build perf on a read-only filesystem with O=dir option. $ cd /some/where/ro/linux/tools/perf $ make O=$HOME/build/perf ... CC /home/namhyung/build/perf/lib.o /bin/sh: bpf_helper_defs.h: Read-only file system make[3]: *** [Makefile:184: bpf_helper_defs.h] Error 1 make[2]: *** [Makefile.perf:778: /home/namhyung/build/perf/libbpf.a] Error 2 make[2]: *** Waiting for unfinished jobs.... LD /home/namhyung/build/perf/libperf-in.o AR /home/namhyung/build/perf/libperf.a PERF_VERSION = 5.4.0 make[1]: *** [Makefile.perf:225: sub-make] Error 2 make: *** [Makefile:70: all] Error 2 It was becaused bpf_helper_defs.h was generated in current directory. Move it to OUTPUT directory. Signed-off-by: Namhyung Kim Signed-off-by: Daniel Borkmann Tested-by: Andrii Nakryiko Acked-by: Andrii Nakryiko Link: https://lore.kernel.org/bpf/20191223061326.843366-1-namhyung@kernel.org --- tools/lib/bpf/Makefile | 15 ++++++++------- tools/testing/selftests/bpf/.gitignore | 1 + tools/testing/selftests/bpf/Makefile | 6 +++--- 3 files changed, 12 insertions(+), 10 deletions(-) diff --git a/tools/lib/bpf/Makefile b/tools/lib/bpf/Makefile index defae23a0169d..97830e46d1a05 100644 --- a/tools/lib/bpf/Makefile +++ b/tools/lib/bpf/Makefile @@ -138,6 +138,7 @@ STATIC_OBJDIR := $(OUTPUT)staticobjs/ BPF_IN_SHARED := $(SHARED_OBJDIR)libbpf-in.o BPF_IN_STATIC := $(STATIC_OBJDIR)libbpf-in.o VERSION_SCRIPT := libbpf.map +BPF_HELPER_DEFS := $(OUTPUT)bpf_helper_defs.h LIB_TARGET := $(addprefix $(OUTPUT),$(LIB_TARGET)) LIB_FILE := $(addprefix $(OUTPUT),$(LIB_FILE)) @@ -159,7 +160,7 @@ all: fixdep all_cmd: $(CMD_TARGETS) check -$(BPF_IN_SHARED): force elfdep bpfdep bpf_helper_defs.h +$(BPF_IN_SHARED): force elfdep bpfdep $(BPF_HELPER_DEFS) @(test -f ../../include/uapi/linux/bpf.h -a -f ../../../include/uapi/linux/bpf.h && ( \ (diff -B ../../include/uapi/linux/bpf.h ../../../include/uapi/linux/bpf.h >/dev/null) || \ echo "Warning: Kernel ABI header at 'tools/include/uapi/linux/bpf.h' differs from latest version at 'include/uapi/linux/bpf.h'" >&2 )) || true @@ -177,12 +178,12 @@ $(BPF_IN_SHARED): force elfdep bpfdep bpf_helper_defs.h echo "Warning: Kernel ABI header at 'tools/include/uapi/linux/if_xdp.h' differs from latest version at 'include/uapi/linux/if_xdp.h'" >&2 )) || true $(Q)$(MAKE) $(build)=libbpf OUTPUT=$(SHARED_OBJDIR) CFLAGS="$(CFLAGS) $(SHLIB_FLAGS)" -$(BPF_IN_STATIC): force elfdep bpfdep bpf_helper_defs.h +$(BPF_IN_STATIC): force elfdep bpfdep $(BPF_HELPER_DEFS) $(Q)$(MAKE) $(build)=libbpf OUTPUT=$(STATIC_OBJDIR) -bpf_helper_defs.h: $(srctree)/tools/include/uapi/linux/bpf.h +$(BPF_HELPER_DEFS): $(srctree)/tools/include/uapi/linux/bpf.h $(Q)$(srctree)/scripts/bpf_helpers_doc.py --header \ - --file $(srctree)/tools/include/uapi/linux/bpf.h > bpf_helper_defs.h + --file $(srctree)/tools/include/uapi/linux/bpf.h > $(BPF_HELPER_DEFS) $(OUTPUT)libbpf.so: $(OUTPUT)libbpf.so.$(LIBBPF_VERSION) @@ -243,7 +244,7 @@ install_lib: all_cmd $(call do_install_mkdir,$(libdir_SQ)); \ cp -fpR $(LIB_FILE) $(DESTDIR)$(libdir_SQ) -install_headers: bpf_helper_defs.h +install_headers: $(BPF_HELPER_DEFS) $(call QUIET_INSTALL, headers) \ $(call do_install,bpf.h,$(prefix)/include/bpf,644); \ $(call do_install,libbpf.h,$(prefix)/include/bpf,644); \ @@ -251,7 +252,7 @@ install_headers: bpf_helper_defs.h $(call do_install,libbpf_util.h,$(prefix)/include/bpf,644); \ $(call do_install,xsk.h,$(prefix)/include/bpf,644); \ $(call do_install,bpf_helpers.h,$(prefix)/include/bpf,644); \ - $(call do_install,bpf_helper_defs.h,$(prefix)/include/bpf,644); \ + $(call do_install,$(BPF_HELPER_DEFS),$(prefix)/include/bpf,644); \ $(call do_install,bpf_tracing.h,$(prefix)/include/bpf,644); \ $(call do_install,bpf_endian.h,$(prefix)/include/bpf,644); \ $(call do_install,bpf_core_read.h,$(prefix)/include/bpf,644); @@ -271,7 +272,7 @@ config-clean: clean: $(call QUIET_CLEAN, libbpf) $(RM) -rf $(CMD_TARGETS) \ *.o *~ *.a *.so *.so.$(LIBBPF_MAJOR_VERSION) .*.d .*.cmd \ - *.pc LIBBPF-CFLAGS bpf_helper_defs.h \ + *.pc LIBBPF-CFLAGS $(BPF_HELPER_DEFS) \ $(SHARED_OBJDIR) $(STATIC_OBJDIR) $(call QUIET_CLEAN, core-gen) $(RM) $(OUTPUT)FEATURE-DUMP.libbpf diff --git a/tools/testing/selftests/bpf/.gitignore b/tools/testing/selftests/bpf/.gitignore index 419652458da4a..1ff0a9f49c01c 100644 --- a/tools/testing/selftests/bpf/.gitignore +++ b/tools/testing/selftests/bpf/.gitignore @@ -40,3 +40,4 @@ xdping test_cpp /no_alu32 /bpf_gcc +bpf_helper_defs.h diff --git a/tools/testing/selftests/bpf/Makefile b/tools/testing/selftests/bpf/Makefile index e0fe01d9ec33b..e2fd6f8d579cd 100644 --- a/tools/testing/selftests/bpf/Makefile +++ b/tools/testing/selftests/bpf/Makefile @@ -120,9 +120,9 @@ force: $(BPFOBJ): force $(MAKE) -C $(BPFDIR) OUTPUT=$(OUTPUT)/ -BPF_HELPERS := $(BPFDIR)/bpf_helper_defs.h $(wildcard $(BPFDIR)/bpf_*.h) -$(BPFDIR)/bpf_helper_defs.h: - $(MAKE) -C $(BPFDIR) OUTPUT=$(OUTPUT)/ bpf_helper_defs.h +BPF_HELPERS := $(OUTPUT)/bpf_helper_defs.h $(wildcard $(BPFDIR)/bpf_*.h) +$(OUTPUT)/bpf_helper_defs.h: + $(MAKE) -C $(BPFDIR) OUTPUT=$(OUTPUT)/ $(OUTPUT)/bpf_helper_defs.h # Get Clang's default includes on this system, as opposed to those seen by # '-target bpf'. This fixes "missing" files on some architectures/distros, -- GitLab From 7935799e041ae10d380d04ea23868240f082bd11 Mon Sep 17 00:00:00 2001 From: Nathan Chancellor Date: Tue, 17 Dec 2019 20:04:51 -0700 Subject: [PATCH 1269/1437] cifs: Adjust indentation in smb2_open_file Clang warns: ../fs/cifs/smb2file.c:70:3: warning: misleading indentation; statement is not part of the previous 'if' [-Wmisleading-indentation] if (oparms->tcon->use_resilient) { ^ ../fs/cifs/smb2file.c:66:2: note: previous statement is here if (rc) ^ 1 warning generated. This warning occurs because there is a space after the tab on this line. Remove it so that the indentation is consistent with the Linux kernel coding style and clang no longer warns. Fixes: 592fafe644bf ("Add resilienthandles mount parm") Link: https://github.com/ClangBuiltLinux/linux/issues/826 Signed-off-by: Nathan Chancellor Signed-off-by: Steve French --- fs/cifs/smb2file.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/cifs/smb2file.c b/fs/cifs/smb2file.c index 8b0b512c57920..afe1f03aabe38 100644 --- a/fs/cifs/smb2file.c +++ b/fs/cifs/smb2file.c @@ -67,7 +67,7 @@ smb2_open_file(const unsigned int xid, struct cifs_open_parms *oparms, goto out; - if (oparms->tcon->use_resilient) { + if (oparms->tcon->use_resilient) { /* default timeout is 0, servers pick default (120 seconds) */ nr_ioctl_req.Timeout = cpu_to_le32(oparms->tcon->handle_timeout); -- GitLab From 046aca3c25fd28da591f59a2dc1a01848e81e0b2 Mon Sep 17 00:00:00 2001 From: "Paulo Alcantara (SUSE)" Date: Wed, 18 Dec 2019 18:11:37 -0300 Subject: [PATCH 1270/1437] cifs: Optimize readdir on reparse points When listing a directory with thounsands of files and most of them are reparse points, we simply marked all those dentries for revalidation and then sending additional (compounded) create/getinfo/close requests for each of them. Instead, upon receiving a response from an SMB2_QUERY_DIRECTORY (FileIdFullDirectoryInformation) command, the directory entries that have a file attribute of FILE_ATTRIBUTE_REPARSE_POINT will contain an EaSize field with a reparse tag in it, so we parse it and mark the dentry for revalidation only if it is a DFS or a symlink. Signed-off-by: Paulo Alcantara (SUSE) Reviewed-by: Pavel Shilovsky Signed-off-by: Steve French --- fs/cifs/cifsglob.h | 1 + fs/cifs/readdir.c | 63 +++++++++++++++++++++++++++++++++++++++------- 2 files changed, 55 insertions(+), 9 deletions(-) diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index ce9bac756c2a1..40705e8624519 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -1693,6 +1693,7 @@ struct cifs_fattr { struct timespec64 cf_atime; struct timespec64 cf_mtime; struct timespec64 cf_ctime; + u32 cf_cifstag; }; static inline void free_dfs_info_param(struct dfs_info3_param *param) diff --git a/fs/cifs/readdir.c b/fs/cifs/readdir.c index 3925a7bfc74d6..d17587c2c4abb 100644 --- a/fs/cifs/readdir.c +++ b/fs/cifs/readdir.c @@ -139,6 +139,28 @@ retry: dput(dentry); } +static bool reparse_file_needs_reval(const struct cifs_fattr *fattr) +{ + if (!(fattr->cf_cifsattrs & ATTR_REPARSE)) + return false; + /* + * The DFS tags should be only intepreted by server side as per + * MS-FSCC 2.1.2.1, but let's include them anyway. + * + * Besides, if cf_cifstag is unset (0), then we still need it to be + * revalidated to know exactly what reparse point it is. + */ + switch (fattr->cf_cifstag) { + case IO_REPARSE_TAG_DFS: + case IO_REPARSE_TAG_DFSR: + case IO_REPARSE_TAG_SYMLINK: + case IO_REPARSE_TAG_NFS: + case 0: + return true; + } + return false; +} + static void cifs_fill_common_info(struct cifs_fattr *fattr, struct cifs_sb_info *cifs_sb) { @@ -158,7 +180,7 @@ cifs_fill_common_info(struct cifs_fattr *fattr, struct cifs_sb_info *cifs_sb) * is a symbolic link, DFS referral or a reparse point with a direct * access like junctions, deduplicated files, NFS symlinks. */ - if (fattr->cf_cifsattrs & ATTR_REPARSE) + if (reparse_file_needs_reval(fattr)) fattr->cf_flags |= CIFS_FATTR_NEED_REVAL; /* non-unix readdir doesn't provide nlink */ @@ -194,19 +216,37 @@ cifs_fill_common_info(struct cifs_fattr *fattr, struct cifs_sb_info *cifs_sb) } } +static void __dir_info_to_fattr(struct cifs_fattr *fattr, const void *info) +{ + const FILE_DIRECTORY_INFO *fi = info; + + memset(fattr, 0, sizeof(*fattr)); + fattr->cf_cifsattrs = le32_to_cpu(fi->ExtFileAttributes); + fattr->cf_eof = le64_to_cpu(fi->EndOfFile); + fattr->cf_bytes = le64_to_cpu(fi->AllocationSize); + fattr->cf_createtime = le64_to_cpu(fi->CreationTime); + fattr->cf_atime = cifs_NTtimeToUnix(fi->LastAccessTime); + fattr->cf_ctime = cifs_NTtimeToUnix(fi->ChangeTime); + fattr->cf_mtime = cifs_NTtimeToUnix(fi->LastWriteTime); +} + void cifs_dir_info_to_fattr(struct cifs_fattr *fattr, FILE_DIRECTORY_INFO *info, struct cifs_sb_info *cifs_sb) { - memset(fattr, 0, sizeof(*fattr)); - fattr->cf_cifsattrs = le32_to_cpu(info->ExtFileAttributes); - fattr->cf_eof = le64_to_cpu(info->EndOfFile); - fattr->cf_bytes = le64_to_cpu(info->AllocationSize); - fattr->cf_createtime = le64_to_cpu(info->CreationTime); - fattr->cf_atime = cifs_NTtimeToUnix(info->LastAccessTime); - fattr->cf_ctime = cifs_NTtimeToUnix(info->ChangeTime); - fattr->cf_mtime = cifs_NTtimeToUnix(info->LastWriteTime); + __dir_info_to_fattr(fattr, info); + cifs_fill_common_info(fattr, cifs_sb); +} +static void cifs_fulldir_info_to_fattr(struct cifs_fattr *fattr, + SEARCH_ID_FULL_DIR_INFO *info, + struct cifs_sb_info *cifs_sb) +{ + __dir_info_to_fattr(fattr, info); + + /* See MS-FSCC 2.4.18 FileIdFullDirectoryInformation */ + if (fattr->cf_cifsattrs & ATTR_REPARSE) + fattr->cf_cifstag = le32_to_cpu(info->EaSize); cifs_fill_common_info(fattr, cifs_sb); } @@ -755,6 +795,11 @@ static int cifs_filldir(char *find_entry, struct file *file, (FIND_FILE_STANDARD_INFO *)find_entry, cifs_sb); break; + case SMB_FIND_FILE_ID_FULL_DIR_INFO: + cifs_fulldir_info_to_fattr(&fattr, + (SEARCH_ID_FULL_DIR_INFO *)find_entry, + cifs_sb); + break; default: cifs_dir_info_to_fattr(&fattr, (FILE_DIRECTORY_INFO *)find_entry, -- GitLab From 1f424e8bd18754d27b15f49359004b0cea344fb5 Mon Sep 17 00:00:00 2001 From: Hillf Danton Date: Sun, 22 Dec 2019 22:46:54 +0800 Subject: [PATCH 1271/1437] io-wq: remove unused busy list from io_sqe Commit e61df66c69b1 ("io-wq: ensure free/busy list browsing see all items") added a list for io workers in addition to the free and busy lists, not only making worker walk cleaner, but leaving the busy list unused. Let's remove it. Signed-off-by: Hillf Danton Signed-off-by: Jens Axboe --- fs/io-wq.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/fs/io-wq.c b/fs/io-wq.c index 11e80b7252a82..a1c85458f021a 100644 --- a/fs/io-wq.c +++ b/fs/io-wq.c @@ -92,7 +92,6 @@ struct io_wqe { struct io_wqe_acct acct[2]; struct hlist_nulls_head free_list; - struct hlist_nulls_head busy_list; struct list_head all_list; struct io_wq *wq; @@ -327,7 +326,6 @@ static void __io_worker_busy(struct io_wqe *wqe, struct io_worker *worker, if (worker->flags & IO_WORKER_F_FREE) { worker->flags &= ~IO_WORKER_F_FREE; hlist_nulls_del_init_rcu(&worker->nulls_node); - hlist_nulls_add_head_rcu(&worker->nulls_node, &wqe->busy_list); } /* @@ -365,7 +363,6 @@ static bool __io_worker_idle(struct io_wqe *wqe, struct io_worker *worker) { if (!(worker->flags & IO_WORKER_F_FREE)) { worker->flags |= IO_WORKER_F_FREE; - hlist_nulls_del_init_rcu(&worker->nulls_node); hlist_nulls_add_head_rcu(&worker->nulls_node, &wqe->free_list); } @@ -798,10 +795,6 @@ void io_wq_cancel_all(struct io_wq *wq) set_bit(IO_WQ_BIT_CANCEL, &wq->state); - /* - * Browse both lists, as there's a gap between handing work off - * to a worker and the worker putting itself on the busy_list - */ rcu_read_lock(); for_each_node(node) { struct io_wqe *wqe = wq->wqes[node]; @@ -1049,7 +1042,6 @@ struct io_wq *io_wq_create(unsigned bounded, struct io_wq_data *data) spin_lock_init(&wqe->lock); INIT_WQ_LIST(&wqe->work_list); INIT_HLIST_NULLS_HEAD(&wqe->free_list, 0); - INIT_HLIST_NULLS_HEAD(&wqe->busy_list, 1); INIT_LIST_HEAD(&wqe->all_list); } -- GitLab From 7648dd9fc981cec52df8a635fa2eefc249e138b9 Mon Sep 17 00:00:00 2001 From: Shuah Khan Date: Tue, 10 Dec 2019 17:12:33 -0700 Subject: [PATCH 1272/1437] selftests: filesystems/epoll: fix build error epoll build fails to find pthread lib. Fix Makefile to use LDLIBS instead of LDFLAGS. LDLIBS is the right flag to use here with -l option when invoking ld. gcc -I../../../../../usr/include/ -lpthread epoll_wakeup_test.c -o .../tools/testing/selftests/filesystems/epoll/epoll_wakeup_test /usr/bin/ld: /tmp/ccaZvJUl.o: in function `kill_timeout': epoll_wakeup_test.c:(.text+0x4dd): undefined reference to `pthread_kill' /usr/bin/ld: epoll_wakeup_test.c:(.text+0x4f2): undefined reference to `pthread_kill' /usr/bin/ld: /tmp/ccaZvJUl.o: in function `epoll9': epoll_wakeup_test.c:(.text+0x6382): undefined reference to `pthread_create' /usr/bin/ld: epoll_wakeup_test.c:(.text+0x64d2): undefined reference to `pthread_create' /usr/bin/ld: epoll_wakeup_test.c:(.text+0x6626): undefined reference to `pthread_join' /usr/bin/ld: epoll_wakeup_test.c:(.text+0x684c): undefined reference to `pthread_tryjoin_np' /usr/bin/ld: epoll_wakeup_test.c:(.text+0x6864): undefined reference to `pthread_kill' /usr/bin/ld: epoll_wakeup_test.c:(.text+0x6878): undefined reference to `pthread_join' Signed-off-by: Shuah Khan --- tools/testing/selftests/filesystems/epoll/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/testing/selftests/filesystems/epoll/Makefile b/tools/testing/selftests/filesystems/epoll/Makefile index e62f3d4f68da4..78ae4aaf7141a 100644 --- a/tools/testing/selftests/filesystems/epoll/Makefile +++ b/tools/testing/selftests/filesystems/epoll/Makefile @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0 CFLAGS += -I../../../../../usr/include/ -LDFLAGS += -lpthread +LDLIBS += -lpthread TEST_GEN_PROGS := epoll_wakeup_test include ../../lib.mk -- GitLab From c65e41538b04e0d64a673828745a00cb68a24371 Mon Sep 17 00:00:00 2001 From: Shuah Khan Date: Thu, 12 Dec 2019 18:56:06 -0700 Subject: [PATCH 1273/1437] selftests: firmware: Fix it to do root uid check and skip firmware attempts to load test modules that require root access and fail. Fix it to check for root uid and exit with skip code instead. Before this fix: selftests: firmware: fw_run_tests.sh modprobe: ERROR: could not insert 'test_firmware': Operation not permitted You must have the following enabled in your kernel: CONFIG_TEST_FIRMWARE=y CONFIG_FW_LOADER=y CONFIG_FW_LOADER_USER_HELPER=y CONFIG_IKCONFIG=y CONFIG_IKCONFIG_PROC=y not ok 1 selftests: firmware: fw_run_tests.sh # SKIP With this fix: selftests: firmware: fw_run_tests.sh skip all tests: must be run as root not ok 1 selftests: firmware: fw_run_tests.sh # SKIP Signed-off-by: Shuah Khan Reviwed-by: Greg Kroah-Hartman Signed-off-by: Shuah Khan --- tools/testing/selftests/firmware/fw_lib.sh | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tools/testing/selftests/firmware/fw_lib.sh b/tools/testing/selftests/firmware/fw_lib.sh index b879305a766d0..5b8c0fedee761 100755 --- a/tools/testing/selftests/firmware/fw_lib.sh +++ b/tools/testing/selftests/firmware/fw_lib.sh @@ -34,6 +34,12 @@ test_modprobe() check_mods() { + local uid=$(id -u) + if [ $uid -ne 0 ]; then + echo "skip all tests: must be run as root" >&2 + exit $ksft_skip + fi + trap "test_modprobe" EXIT if [ ! -d $DIR ]; then modprobe test_firmware -- GitLab From 05564c29fc943c467abb1f60ef1f0a68c4c424e9 Mon Sep 17 00:00:00 2001 From: Shuah Khan Date: Mon, 16 Dec 2019 12:18:40 -0700 Subject: [PATCH 1274/1437] selftests: livepatch: Fix it to do root uid check and skip livepatch test configures the system and debug environment to run tests. Some of these actions fail without root access and test dumps several permission denied messages before it exits. Fix test-state.sh to call setup_config instead of set_dynamic_debug as suggested by Petr Mladek Fix it to check root uid and exit with skip code instead. Signed-off-by: Shuah Khan Acked-by: Joe Lawrence Reviewed-by: Petr Mladek Signed-off-by: Shuah Khan --- tools/testing/selftests/livepatch/functions.sh | 15 ++++++++++++++- tools/testing/selftests/livepatch/test-state.sh | 3 +-- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/tools/testing/selftests/livepatch/functions.sh b/tools/testing/selftests/livepatch/functions.sh index 31eb09e387295..a6e3d5517a6fa 100644 --- a/tools/testing/selftests/livepatch/functions.sh +++ b/tools/testing/selftests/livepatch/functions.sh @@ -7,6 +7,9 @@ MAX_RETRIES=600 RETRY_INTERVAL=".1" # seconds +# Kselftest framework requirement - SKIP code is 4 +ksft_skip=4 + # log(msg) - write message to kernel log # msg - insightful words function log() { @@ -18,7 +21,16 @@ function log() { function skip() { log "SKIP: $1" echo "SKIP: $1" >&2 - exit 4 + exit $ksft_skip +} + +# root test +function is_root() { + uid=$(id -u) + if [ $uid -ne 0 ]; then + echo "skip all tests: must be run as root" >&2 + exit $ksft_skip + fi } # die(msg) - game over, man @@ -62,6 +74,7 @@ function set_ftrace_enabled() { # for verbose livepatching output and turn on # the ftrace_enabled sysctl. function setup_config() { + is_root push_config set_dynamic_debug set_ftrace_enabled 1 diff --git a/tools/testing/selftests/livepatch/test-state.sh b/tools/testing/selftests/livepatch/test-state.sh index dc2908c22c265..a082127081157 100755 --- a/tools/testing/selftests/livepatch/test-state.sh +++ b/tools/testing/selftests/livepatch/test-state.sh @@ -8,8 +8,7 @@ MOD_LIVEPATCH=test_klp_state MOD_LIVEPATCH2=test_klp_state2 MOD_LIVEPATCH3=test_klp_state3 -set_dynamic_debug - +setup_config # TEST: Loading and removing a module that modifies the system state -- GitLab From 1c68cbadf09f685e1ffc6cc4c9094e67131f9a2f Mon Sep 17 00:00:00 2001 From: SeongJae Park Date: Fri, 20 Dec 2019 05:14:03 +0000 Subject: [PATCH 1275/1437] docs/kunit/start: Use in-tree 'kunit_defconfig' The kunit doc suggests users to get the default `kunitconfig` from an external git tree. However, the file is already located under the `arch/um/configs/` of the kernel tree. Because the local file is easier to access and maintain, this commit updates the doc to use it. Signed-off-by: SeongJae Park Reviewed-by: Brendan Higgins Tested-by: Brendan Higgins Signed-off-by: Shuah Khan --- Documentation/dev-tools/kunit/start.rst | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Documentation/dev-tools/kunit/start.rst b/Documentation/dev-tools/kunit/start.rst index 9d6db892c41c0..d5197f1a45cbb 100644 --- a/Documentation/dev-tools/kunit/start.rst +++ b/Documentation/dev-tools/kunit/start.rst @@ -32,9 +32,8 @@ regular Kernel config, with the specific test targets as well. .. code-block:: bash - git clone -b master https://kunit.googlesource.com/kunitconfig $PATH_TO_KUNITCONFIG_REPO cd $PATH_TO_LINUX_REPO - ln -s $PATH_TO_KUNIT_CONFIG_REPO/kunitconfig kunitconfig + cp arch/um/configs/kunit_defconfig kunitconfig You may want to add kunitconfig to your local gitignore. -- GitLab From 8c0140facb380da5740182d189be0b0d9deadb3d Mon Sep 17 00:00:00 2001 From: SeongJae Park Date: Fri, 20 Dec 2019 05:14:04 +0000 Subject: [PATCH 1276/1437] kunit: Remove duplicated defconfig creation '--defconfig' option is handled by the 'main() of the 'kunit.py' but again handled in following 'run_tests()'. This commit removes this duplicated handling of the option in the 'run_tests()'. Signed-off-by: SeongJae Park Reviewed-by: Brendan Higgins Tested-by: Brendan Higgins Signed-off-by: Shuah Khan --- tools/testing/kunit/kunit.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/tools/testing/kunit/kunit.py b/tools/testing/kunit/kunit.py index efe06d6219837..f8f26951cd1b5 100755 --- a/tools/testing/kunit/kunit.py +++ b/tools/testing/kunit/kunit.py @@ -37,9 +37,6 @@ def create_default_kunitconfig(): def run_tests(linux: kunit_kernel.LinuxSourceTree, request: KunitRequest) -> KunitResult: - if request.defconfig: - create_default_kunitconfig() - config_start = time.time() success = linux.build_reconfig(request.build_dir) config_end = time.time() -- GitLab From e3212513a8f07786cb262cf78dfe9c4d0c1b1786 Mon Sep 17 00:00:00 2001 From: SeongJae Park Date: Fri, 20 Dec 2019 05:14:05 +0000 Subject: [PATCH 1277/1437] kunit: Create default config in '--build_dir' If both '--build_dir' and '--defconfig' are given, the handling of '--defconfig' ignores '--build_dir' option. This commit modifies the behavior to respect '--build_dir' option. Reported-by: Brendan Higgins Suggested-by: Brendan Higgins Signed-off-by: SeongJae Park Reviewed-by: Brendan Higgins Tested-by: Brendan Higgins Signed-off-by: Shuah Khan --- tools/testing/kunit/kunit.py | 11 +++++++++-- tools/testing/kunit/kunit_kernel.py | 4 ++-- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/tools/testing/kunit/kunit.py b/tools/testing/kunit/kunit.py index f8f26951cd1b5..5b222418eacdf 100755 --- a/tools/testing/kunit/kunit.py +++ b/tools/testing/kunit/kunit.py @@ -31,9 +31,9 @@ class KunitStatus(Enum): TEST_FAILURE = auto() def create_default_kunitconfig(): - if not os.path.exists(kunit_kernel.KUNITCONFIG_PATH): + if not os.path.exists(kunit_kernel.kunitconfig_path): shutil.copyfile('arch/um/configs/kunit_defconfig', - kunit_kernel.KUNITCONFIG_PATH) + kunit_kernel.kunitconfig_path) def run_tests(linux: kunit_kernel.LinuxSourceTree, request: KunitRequest) -> KunitResult: @@ -114,6 +114,13 @@ def main(argv, linux=None): cli_args = parser.parse_args(argv) if cli_args.subcommand == 'run': + if cli_args.build_dir: + if not os.path.exists(cli_args.build_dir): + os.mkdir(cli_args.build_dir) + kunit_kernel.kunitconfig_path = os.path.join( + cli_args.build_dir, + kunit_kernel.kunitconfig_path) + if cli_args.defconfig: create_default_kunitconfig() diff --git a/tools/testing/kunit/kunit_kernel.py b/tools/testing/kunit/kunit_kernel.py index bf38768353313..c04a12e2f711d 100644 --- a/tools/testing/kunit/kunit_kernel.py +++ b/tools/testing/kunit/kunit_kernel.py @@ -14,7 +14,7 @@ import os import kunit_config KCONFIG_PATH = '.config' -KUNITCONFIG_PATH = 'kunitconfig' +kunitconfig_path = 'kunitconfig' class ConfigError(Exception): """Represents an error trying to configure the Linux kernel.""" @@ -82,7 +82,7 @@ class LinuxSourceTree(object): def __init__(self): self._kconfig = kunit_config.Kconfig() - self._kconfig.read_from_file(KUNITCONFIG_PATH) + self._kconfig.read_from_file(kunitconfig_path) self._ops = LinuxSourceTreeOperations() def clean(self): -- GitLab From 609952c2afc9542b336cf9a72d4a09606e05fa2a Mon Sep 17 00:00:00 2001 From: SeongJae Park Date: Fri, 20 Dec 2019 05:14:06 +0000 Subject: [PATCH 1278/1437] kunit: Place 'test.log' under the 'build_dir' 'kunit' writes the 'test.log' under the kernel source directory even though a 'build_dir' option is given. As users who use the option might expect the outputs to be placed under the specified directory, this commit modifies the logic to write the log file under the 'build_dir'. Signed-off-by: SeongJae Park Reviewed-by: Brendan Higgins Tested-by: Brendan Higgins Signed-off-by: Shuah Khan --- tools/testing/kunit/kunit.py | 2 +- tools/testing/kunit/kunit_kernel.py | 4 ++-- tools/testing/kunit/kunit_tool_test.py | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tools/testing/kunit/kunit.py b/tools/testing/kunit/kunit.py index 5b222418eacdf..e4250c4b06fba 100755 --- a/tools/testing/kunit/kunit.py +++ b/tools/testing/kunit/kunit.py @@ -105,7 +105,7 @@ def main(argv, linux=None): run_parser.add_argument('--build_dir', help='As in the make command, it specifies the build ' 'directory.', - type=str, default=None, metavar='build_dir') + type=str, default='', metavar='build_dir') run_parser.add_argument('--defconfig', help='Uses a default kunitconfig.', diff --git a/tools/testing/kunit/kunit_kernel.py b/tools/testing/kunit/kunit_kernel.py index c04a12e2f711d..a10c0c787bc19 100644 --- a/tools/testing/kunit/kunit_kernel.py +++ b/tools/testing/kunit/kunit_kernel.py @@ -140,10 +140,10 @@ class LinuxSourceTree(object): return False return True - def run_kernel(self, args=[], timeout=None, build_dir=None): + def run_kernel(self, args=[], timeout=None, build_dir=''): args.extend(['mem=256M']) process = self._ops.linux_bin(args, timeout, build_dir) - with open('test.log', 'w') as f: + with open(os.path.join(build_dir, 'test.log'), 'w') as f: for line in process.stdout: f.write(line.rstrip().decode('ascii') + '\n') yield line.rstrip().decode('ascii') diff --git a/tools/testing/kunit/kunit_tool_test.py b/tools/testing/kunit/kunit_tool_test.py index a2a8ea6beae34..22f16e66b3c14 100755 --- a/tools/testing/kunit/kunit_tool_test.py +++ b/tools/testing/kunit/kunit_tool_test.py @@ -199,7 +199,7 @@ class KUnitMainTest(unittest.TestCase): timeout = 3453 kunit.main(['run', '--timeout', str(timeout)], self.linux_source_mock) assert self.linux_source_mock.build_reconfig.call_count == 1 - self.linux_source_mock.run_kernel.assert_called_once_with(build_dir=None, timeout=timeout) + self.linux_source_mock.run_kernel.assert_called_once_with(build_dir='', timeout=timeout) self.print_mock.assert_any_call(StrContains('Testing complete.')) if __name__ == '__main__': -- GitLab From 14ee5cfd4512ee3d1e0047d8751450dcc6544070 Mon Sep 17 00:00:00 2001 From: SeongJae Park Date: Fri, 20 Dec 2019 05:14:07 +0000 Subject: [PATCH 1279/1437] kunit: Rename 'kunitconfig' to '.kunitconfig' This commit renames 'kunitconfig' to '.kunitconfig' so that it can be automatically ignored by git and do not disturb people who want to type 'kernel/' by pressing only the 'k' and then 'tab' key. Signed-off-by: SeongJae Park Reviewed-by: Brendan Higgins Tested-by: Brendan Higgins Signed-off-by: Shuah Khan --- Documentation/dev-tools/kunit/start.rst | 12 +++++------- tools/testing/kunit/kunit.py | 2 +- tools/testing/kunit/kunit_kernel.py | 4 ++-- 3 files changed, 8 insertions(+), 10 deletions(-) diff --git a/Documentation/dev-tools/kunit/start.rst b/Documentation/dev-tools/kunit/start.rst index d5197f1a45cbb..4e1d24db6b139 100644 --- a/Documentation/dev-tools/kunit/start.rst +++ b/Documentation/dev-tools/kunit/start.rst @@ -24,18 +24,16 @@ The wrapper can be run with: For more information on this wrapper (also called kunit_tool) checkout the :doc:`kunit-tool` page. -Creating a kunitconfig -====================== +Creating a .kunitconfig +======================= The Python script is a thin wrapper around Kbuild. As such, it needs to be -configured with a ``kunitconfig`` file. This file essentially contains the +configured with a ``.kunitconfig`` file. This file essentially contains the regular Kernel config, with the specific test targets as well. .. code-block:: bash cd $PATH_TO_LINUX_REPO - cp arch/um/configs/kunit_defconfig kunitconfig - -You may want to add kunitconfig to your local gitignore. + cp arch/um/configs/kunit_defconfig .kunitconfig Verifying KUnit Works --------------------- @@ -150,7 +148,7 @@ and the following to ``drivers/misc/Makefile``: obj-$(CONFIG_MISC_EXAMPLE_TEST) += example-test.o -Now add it to your ``kunitconfig``: +Now add it to your ``.kunitconfig``: .. code-block:: none diff --git a/tools/testing/kunit/kunit.py b/tools/testing/kunit/kunit.py index e4250c4b06fba..e59eb9e7f9236 100755 --- a/tools/testing/kunit/kunit.py +++ b/tools/testing/kunit/kunit.py @@ -108,7 +108,7 @@ def main(argv, linux=None): type=str, default='', metavar='build_dir') run_parser.add_argument('--defconfig', - help='Uses a default kunitconfig.', + help='Uses a default .kunitconfig.', action='store_true') cli_args = parser.parse_args(argv) diff --git a/tools/testing/kunit/kunit_kernel.py b/tools/testing/kunit/kunit_kernel.py index a10c0c787bc19..cc5d844ecca13 100644 --- a/tools/testing/kunit/kunit_kernel.py +++ b/tools/testing/kunit/kunit_kernel.py @@ -14,7 +14,7 @@ import os import kunit_config KCONFIG_PATH = '.config' -kunitconfig_path = 'kunitconfig' +kunitconfig_path = '.kunitconfig' class ConfigError(Exception): """Represents an error trying to configure the Linux kernel.""" @@ -111,7 +111,7 @@ class LinuxSourceTree(object): return True def build_reconfig(self, build_dir): - """Creates a new .config if it is not a subset of the kunitconfig.""" + """Creates a new .config if it is not a subset of the .kunitconfig.""" kconfig_path = get_kconfig_path(build_dir) if os.path.exists(kconfig_path): existing_kconfig = kunit_config.Kconfig() -- GitLab From b1b35201bcf411191986eefdef5f5fb5f41cd879 Mon Sep 17 00:00:00 2001 From: SeongJae Park Date: Fri, 20 Dec 2019 05:14:08 +0000 Subject: [PATCH 1280/1437] kunit/kunit_tool_test: Test '--build_dir' option run This commit adds kunit tool test for the '--build_dir' option. Signed-off-by: SeongJae Park Reviewed-by: Brendan Higgins Tested-by: Brendan Higgins Signed-off-by: Shuah Khan --- tools/testing/kunit/kunit_tool_test.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tools/testing/kunit/kunit_tool_test.py b/tools/testing/kunit/kunit_tool_test.py index 22f16e66b3c14..cba97756ac4a5 100755 --- a/tools/testing/kunit/kunit_tool_test.py +++ b/tools/testing/kunit/kunit_tool_test.py @@ -174,6 +174,7 @@ class KUnitMainTest(unittest.TestCase): kunit.main(['run'], self.linux_source_mock) assert self.linux_source_mock.build_reconfig.call_count == 1 assert self.linux_source_mock.run_kernel.call_count == 1 + self.linux_source_mock.run_kernel.assert_called_once_with(build_dir='', timeout=300) self.print_mock.assert_any_call(StrContains('Testing complete.')) def test_run_passes_args_fail(self): @@ -202,5 +203,12 @@ class KUnitMainTest(unittest.TestCase): self.linux_source_mock.run_kernel.assert_called_once_with(build_dir='', timeout=timeout) self.print_mock.assert_any_call(StrContains('Testing complete.')) + def test_run_builddir(self): + build_dir = '.kunit' + kunit.main(['run', '--build_dir', build_dir], self.linux_source_mock) + assert self.linux_source_mock.build_reconfig.call_count == 1 + self.linux_source_mock.run_kernel.assert_called_once_with(build_dir=build_dir, timeout=300) + self.print_mock.assert_any_call(StrContains('Testing complete.')) + if __name__ == '__main__': unittest.main() -- GitLab From af9cb29c5488381083b0b5ccdfb3cd931063384a Mon Sep 17 00:00:00 2001 From: Mathieu Desnoyers Date: Wed, 11 Dec 2019 11:28:57 -0500 Subject: [PATCH 1281/1437] rseq/selftests: Turn off timeout setting As the rseq selftests can run for a long period of time, disable the timeout that the general selftests have. Signed-off-by: Mathieu Desnoyers Cc: Shuah Khan Cc: Thomas Gleixner Cc: Peter Zijlstra (Intel) Cc: "Paul E. McKenney" Cc: Boqun Feng Cc: "H . Peter Anvin" Cc: Paul Turner Cc: Dmitry Vyukov Signed-off-by: Shuah Khan --- tools/testing/selftests/rseq/settings | 1 + 1 file changed, 1 insertion(+) create mode 100644 tools/testing/selftests/rseq/settings diff --git a/tools/testing/selftests/rseq/settings b/tools/testing/selftests/rseq/settings new file mode 100644 index 0000000000000..e7b9417537fbc --- /dev/null +++ b/tools/testing/selftests/rseq/settings @@ -0,0 +1 @@ +timeout=0 -- GitLab From 8df34c56321479bfa1ec732c675b686c2b4df412 Mon Sep 17 00:00:00 2001 From: Mathieu Desnoyers Date: Wed, 11 Dec 2019 11:17:13 -0500 Subject: [PATCH 1282/1437] rseq/selftests: Fix: Namespace gettid() for compatibility with glibc 2.30 glibc 2.30 introduces gettid() in public headers, which clashes with the internal static definition within rseq selftests. Rename gettid() to rseq_gettid() to eliminate this symbol name clash. Reported-by: Tommi T. Rantala Signed-off-by: Mathieu Desnoyers Cc: Shuah Khan Cc: Tommi T. Rantala Cc: Thomas Gleixner Cc: Peter Zijlstra (Intel) Cc: "Paul E. McKenney" Cc: Boqun Feng Cc: "H . Peter Anvin" Cc: Paul Turner Cc: Dmitry Vyukov Cc: # v4.18+ Signed-off-by: Shuah Khan --- tools/testing/selftests/rseq/param_test.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/tools/testing/selftests/rseq/param_test.c b/tools/testing/selftests/rseq/param_test.c index eec2663261f2a..e8a657a5f48a0 100644 --- a/tools/testing/selftests/rseq/param_test.c +++ b/tools/testing/selftests/rseq/param_test.c @@ -15,7 +15,7 @@ #include #include -static inline pid_t gettid(void) +static inline pid_t rseq_gettid(void) { return syscall(__NR_gettid); } @@ -373,11 +373,12 @@ void *test_percpu_spinlock_thread(void *arg) rseq_percpu_unlock(&data->lock, cpu); #ifndef BENCHMARK if (i != 0 && !(i % (reps / 10))) - printf_verbose("tid %d: count %lld\n", (int) gettid(), i); + printf_verbose("tid %d: count %lld\n", + (int) rseq_gettid(), i); #endif } printf_verbose("tid %d: number of rseq abort: %d, signals delivered: %u\n", - (int) gettid(), nr_abort, signals_delivered); + (int) rseq_gettid(), nr_abort, signals_delivered); if (!opt_disable_rseq && thread_data->reg && rseq_unregister_current_thread()) abort(); @@ -454,11 +455,12 @@ void *test_percpu_inc_thread(void *arg) } while (rseq_unlikely(ret)); #ifndef BENCHMARK if (i != 0 && !(i % (reps / 10))) - printf_verbose("tid %d: count %lld\n", (int) gettid(), i); + printf_verbose("tid %d: count %lld\n", + (int) rseq_gettid(), i); #endif } printf_verbose("tid %d: number of rseq abort: %d, signals delivered: %u\n", - (int) gettid(), nr_abort, signals_delivered); + (int) rseq_gettid(), nr_abort, signals_delivered); if (!opt_disable_rseq && thread_data->reg && rseq_unregister_current_thread()) abort(); @@ -605,7 +607,7 @@ void *test_percpu_list_thread(void *arg) } printf_verbose("tid %d: number of rseq abort: %d, signals delivered: %u\n", - (int) gettid(), nr_abort, signals_delivered); + (int) rseq_gettid(), nr_abort, signals_delivered); if (!opt_disable_rseq && rseq_unregister_current_thread()) abort(); @@ -796,7 +798,7 @@ void *test_percpu_buffer_thread(void *arg) } printf_verbose("tid %d: number of rseq abort: %d, signals delivered: %u\n", - (int) gettid(), nr_abort, signals_delivered); + (int) rseq_gettid(), nr_abort, signals_delivered); if (!opt_disable_rseq && rseq_unregister_current_thread()) abort(); @@ -1011,7 +1013,7 @@ void *test_percpu_memcpy_buffer_thread(void *arg) } printf_verbose("tid %d: number of rseq abort: %d, signals delivered: %u\n", - (int) gettid(), nr_abort, signals_delivered); + (int) rseq_gettid(), nr_abort, signals_delivered); if (!opt_disable_rseq && rseq_unregister_current_thread()) abort(); -- GitLab From 2a1f40adfb54ca65dc4c93bad444dd23b800a76e Mon Sep 17 00:00:00 2001 From: Mathieu Desnoyers Date: Fri, 20 Dec 2019 15:33:18 -0500 Subject: [PATCH 1283/1437] rseq/selftests: Clarify rseq_prepare_unload() helper requirements The rseq.h UAPI now documents that the rseq_cs field must be cleared before reclaiming memory that contains the targeted struct rseq_cs, but also that the rseq_cs field must be cleared before reclaiming memory of the code pointed to by the rseq_cs start_ip and post_commit_offset fields. While we can expect that use of dlclose(3) will typically unmap both struct rseq_cs and its associated code at once, nothing would theoretically prevent a JIT from reclaiming the code without reclaiming the struct rseq_cs, which would erroneously allow the kernel to consider new code which is not a rseq critical section as a rseq critical section following a code reclaim. Suggested-by: Florian Weimer Signed-off-by: Mathieu Desnoyers Cc: Shuah Khan Cc: Florian Weimer Cc: Thomas Gleixner Cc: Peter Zijlstra (Intel) Cc: "Paul E. McKenney" Cc: Boqun Feng Cc: "H . Peter Anvin" Cc: Paul Turner Cc: Dmitry Vyukov Signed-off-by: Shuah Khan --- tools/testing/selftests/rseq/rseq.h | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/tools/testing/selftests/rseq/rseq.h b/tools/testing/selftests/rseq/rseq.h index d40d60e7499e8..3f63eb362b92f 100644 --- a/tools/testing/selftests/rseq/rseq.h +++ b/tools/testing/selftests/rseq/rseq.h @@ -149,11 +149,13 @@ static inline void rseq_clear_rseq_cs(void) /* * rseq_prepare_unload() should be invoked by each thread executing a rseq * critical section at least once between their last critical section and - * library unload of the library defining the rseq critical section - * (struct rseq_cs). This also applies to use of rseq in code generated by - * JIT: rseq_prepare_unload() should be invoked at least once by each - * thread executing a rseq critical section before reclaim of the memory - * holding the struct rseq_cs. + * library unload of the library defining the rseq critical section (struct + * rseq_cs) or the code referred to by the struct rseq_cs start_ip and + * post_commit_offset fields. This also applies to use of rseq in code + * generated by JIT: rseq_prepare_unload() should be invoked at least once by + * each thread executing a rseq critical section before reclaim of the memory + * holding the struct rseq_cs or reclaim of the code pointed to by struct + * rseq_cs start_ip and post_commit_offset fields. */ static inline void rseq_prepare_unload(void) { -- GitLab From fd1c4bc6e9b34a5e4fe7a3130a49380ef9d7037c Mon Sep 17 00:00:00 2001 From: Hillf Danton Date: Tue, 24 Dec 2019 09:14:29 -0700 Subject: [PATCH 1284/1437] io-wq: add cond_resched() to worker thread Reschedule the current IO worker to cut the risk that it is becoming a cpu hog. Signed-off-by: Hillf Danton Signed-off-by: Jens Axboe --- fs/io-wq.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fs/io-wq.c b/fs/io-wq.c index a1c85458f021a..541c8a3e0bbb5 100644 --- a/fs/io-wq.c +++ b/fs/io-wq.c @@ -429,6 +429,8 @@ next: if (signal_pending(current)) flush_signals(current); + cond_resched(); + spin_lock_irq(&worker->lock); worker->cur_work = work; spin_unlock_irq(&worker->lock); -- GitLab From feed8a4fc9d46c3126fb9fcae0e9248270c6321a Mon Sep 17 00:00:00 2001 From: Antonio Messina Date: Thu, 19 Dec 2019 15:08:03 +0100 Subject: [PATCH 1285/1437] udp: fix integer overflow while computing available space in sk_rcvbuf When the size of the receive buffer for a socket is close to 2^31 when computing if we have enough space in the buffer to copy a packet from the queue to the buffer we might hit an integer overflow. When an user set net.core.rmem_default to a value close to 2^31 UDP packets are dropped because of this overflow. This can be visible, for instance, with failure to resolve hostnames. This can be fixed by casting sk_rcvbuf (which is an int) to unsigned int, similarly to how it is done in TCP. Signed-off-by: Antonio Messina Signed-off-by: David S. Miller --- net/ipv4/udp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index 4da5758cc7187..93a355b6b0924 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -1475,7 +1475,7 @@ int __udp_enqueue_schedule_skb(struct sock *sk, struct sk_buff *skb) * queue contains some other skb */ rmem = atomic_add_return(size, &sk->sk_rmem_alloc); - if (rmem > (size + sk->sk_rcvbuf)) + if (rmem > (size + (unsigned int)sk->sk_rcvbuf)) goto uncharge_drop; spin_lock(&list->lock); -- GitLab From 61d5d4062876e21331c3d0ba4b02dbd50c06a658 Mon Sep 17 00:00:00 2001 From: Marcelo Ricardo Leitner Date: Fri, 20 Dec 2019 15:03:44 -0300 Subject: [PATCH 1286/1437] sctp: fix err handling of stream initialization The fix on 951c6db954a1 fixed the issued reported there but introduced another. When the allocation fails within sctp_stream_init() it is okay/necessary to free the genradix. But it is also called when adding new streams, from sctp_send_add_streams() and sctp_process_strreset_addstrm_in() and in those situations it cannot just free the genradix because by then it is a fully operational association. The fix here then is to only free the genradix in sctp_stream_init() and on those other call sites move on with what it already had and let the subsequent error handling to handle it. Tested with the reproducers from this report and the previous one, with lksctp-tools and sctp-tests. Reported-by: syzbot+9a1bc632e78a1a98488b@syzkaller.appspotmail.com Fixes: 951c6db954a1 ("sctp: fix memleak on err handling of stream initialization") Signed-off-by: Marcelo Ricardo Leitner Signed-off-by: David S. Miller --- net/sctp/stream.c | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/net/sctp/stream.c b/net/sctp/stream.c index 6a30392068a04..c1a100d2fed39 100644 --- a/net/sctp/stream.c +++ b/net/sctp/stream.c @@ -84,10 +84,8 @@ static int sctp_stream_alloc_out(struct sctp_stream *stream, __u16 outcnt, return 0; ret = genradix_prealloc(&stream->out, outcnt, gfp); - if (ret) { - genradix_free(&stream->out); + if (ret) return ret; - } stream->outcnt = outcnt; return 0; @@ -102,10 +100,8 @@ static int sctp_stream_alloc_in(struct sctp_stream *stream, __u16 incnt, return 0; ret = genradix_prealloc(&stream->in, incnt, gfp); - if (ret) { - genradix_free(&stream->in); + if (ret) return ret; - } stream->incnt = incnt; return 0; @@ -123,7 +119,7 @@ int sctp_stream_init(struct sctp_stream *stream, __u16 outcnt, __u16 incnt, * a new one with new outcnt to save memory if needed. */ if (outcnt == stream->outcnt) - goto in; + goto handle_in; /* Filter out chunks queued on streams that won't exist anymore */ sched->unsched_all(stream); @@ -132,24 +128,28 @@ int sctp_stream_init(struct sctp_stream *stream, __u16 outcnt, __u16 incnt, ret = sctp_stream_alloc_out(stream, outcnt, gfp); if (ret) - goto out; + goto out_err; for (i = 0; i < stream->outcnt; i++) SCTP_SO(stream, i)->state = SCTP_STREAM_OPEN; -in: +handle_in: sctp_stream_interleave_init(stream); if (!incnt) goto out; ret = sctp_stream_alloc_in(stream, incnt, gfp); - if (ret) { - sched->free(stream); - genradix_free(&stream->out); - stream->outcnt = 0; - goto out; - } + if (ret) + goto in_err; + + goto out; +in_err: + sched->free(stream); + genradix_free(&stream->in); +out_err: + genradix_free(&stream->out); + stream->outcnt = 0; out: return ret; } -- GitLab From 7c3125f0a6ebc17846c5908ad7d6056d66c1c426 Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Fri, 20 Dec 2019 11:24:21 -0800 Subject: [PATCH 1287/1437] net: dsa: bcm_sf2: Fix IP fragment location and behavior The IP fragment is specified through user-defined field as the first bit of the first user-defined word. We were previously trying to extract it from the user-defined mask which could not possibly work. The ip_frag is also supposed to be a boolean, if we do not cast it as such, we risk overwriting the next fields in CFP_DATA(6) which would render the rule inoperative. Fixes: 7318166cacad ("net: dsa: bcm_sf2: Add support for ethtool::rxnfc") Signed-off-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/dsa/bcm_sf2_cfp.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/net/dsa/bcm_sf2_cfp.c b/drivers/net/dsa/bcm_sf2_cfp.c index f3f0c3f07391a..1962c8330daa0 100644 --- a/drivers/net/dsa/bcm_sf2_cfp.c +++ b/drivers/net/dsa/bcm_sf2_cfp.c @@ -358,7 +358,7 @@ static int bcm_sf2_cfp_ipv4_rule_set(struct bcm_sf2_priv *priv, int port, return -EINVAL; } - ip_frag = be32_to_cpu(fs->m_ext.data[0]); + ip_frag = !!(be32_to_cpu(fs->h_ext.data[0]) & 1); /* Locate the first rule available */ if (fs->location == RX_CLS_LOC_ANY) @@ -569,7 +569,7 @@ static int bcm_sf2_cfp_rule_cmp(struct bcm_sf2_priv *priv, int port, if (rule->fs.flow_type != fs->flow_type || rule->fs.ring_cookie != fs->ring_cookie || - rule->fs.m_ext.data[0] != fs->m_ext.data[0]) + rule->fs.h_ext.data[0] != fs->h_ext.data[0]) continue; switch (fs->flow_type & ~FLOW_EXT) { @@ -621,7 +621,7 @@ static int bcm_sf2_cfp_ipv6_rule_set(struct bcm_sf2_priv *priv, int port, return -EINVAL; } - ip_frag = be32_to_cpu(fs->m_ext.data[0]); + ip_frag = !!(be32_to_cpu(fs->h_ext.data[0]) & 1); layout = &udf_tcpip6_layout; slice_num = bcm_sf2_get_slice_number(layout, 0); -- GitLab From e7dbfed1adb0e9123f582293e79760dc52b00d74 Mon Sep 17 00:00:00 2001 From: Martin Varghese Date: Sat, 21 Dec 2019 08:50:01 +0530 Subject: [PATCH 1288/1437] net: skb_mpls_push() modified to allow MPLS header push at start of packet. The existing skb_mpls_push() implementation always inserts mpls header after the mac header. L2 VPN use cases requires MPLS header to be inserted before the ethernet header as the ethernet packet gets tunnelled inside MPLS header in those cases. Signed-off-by: Martin Varghese Acked-by: Pravin B Shelar Signed-off-by: David S. Miller --- net/core/skbuff.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/net/core/skbuff.c b/net/core/skbuff.c index 973a71f4bc898..d90c8276a9eef 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -5472,12 +5472,15 @@ static void skb_mod_eth_type(struct sk_buff *skb, struct ethhdr *hdr, } /** - * skb_mpls_push() - push a new MPLS header after the mac header + * skb_mpls_push() - push a new MPLS header after mac_len bytes from start of + * the packet * * @skb: buffer * @mpls_lse: MPLS label stack entry to push * @mpls_proto: ethertype of the new MPLS header (expects 0x8847 or 0x8848) * @mac_len: length of the MAC header + * @ethernet: flag to indicate if the resulting packet after skb_mpls_push is + * ethernet * * Expects skb->data at mac header. * @@ -5501,7 +5504,7 @@ int skb_mpls_push(struct sk_buff *skb, __be32 mpls_lse, __be16 mpls_proto, return err; if (!skb->inner_protocol) { - skb_set_inner_network_header(skb, mac_len); + skb_set_inner_network_header(skb, skb_network_offset(skb)); skb_set_inner_protocol(skb, skb->protocol); } @@ -5510,6 +5513,7 @@ int skb_mpls_push(struct sk_buff *skb, __be32 mpls_lse, __be16 mpls_proto, mac_len); skb_reset_mac_header(skb); skb_set_network_header(skb, mac_len); + skb_reset_mac_len(skb); lse = mpls_hdr(skb); lse->label_stack_entry = mpls_lse; -- GitLab From 76f99f987f2b7f95d43857b6e0362bd8dec9890c Mon Sep 17 00:00:00 2001 From: Martin Varghese Date: Sat, 21 Dec 2019 08:50:23 +0530 Subject: [PATCH 1289/1437] net: Rephrased comments section of skb_mpls_pop() Rephrased comments section of skb_mpls_pop() to align it with comments section of skb_mpls_push(). Signed-off-by: Martin Varghese Acked-by: Pravin B Shelar Signed-off-by: David S. Miller --- net/core/skbuff.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/core/skbuff.c b/net/core/skbuff.c index d90c8276a9eef..44b0894d8ae1f 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -5533,7 +5533,7 @@ EXPORT_SYMBOL_GPL(skb_mpls_push); * @skb: buffer * @next_proto: ethertype of header after popped MPLS header * @mac_len: length of the MAC header - * @ethernet: flag to indicate if ethernet header is present in packet + * @ethernet: flag to indicate if the packet is ethernet * * Expects skb->data at mac header. * -- GitLab From f66b53fdbb22ced1a323b22b9de84a61aacd8d18 Mon Sep 17 00:00:00 2001 From: Martin Varghese Date: Sat, 21 Dec 2019 08:50:46 +0530 Subject: [PATCH 1290/1437] openvswitch: New MPLS actions for layer 2 tunnelling The existing PUSH MPLS action inserts MPLS header between ethernet header and the IP header. Though this behaviour is fine for L3 VPN where an IP packet is encapsulated inside a MPLS tunnel, it does not suffice the L2 VPN (l2 tunnelling) requirements. In L2 VPN the MPLS header should encapsulate the ethernet packet. The new mpls action ADD_MPLS inserts MPLS header at the start of the packet or at the start of the l3 header depending on the value of l3 tunnel flag in the ADD_MPLS arguments. POP_MPLS action is extended to support ethertype 0x6558. Signed-off-by: Martin Varghese Acked-by: Pravin B Shelar Signed-off-by: David S. Miller --- include/uapi/linux/openvswitch.h | 31 +++++++++++++++++++++++++++++ net/openvswitch/actions.c | 30 ++++++++++++++++++++++------ net/openvswitch/flow_netlink.c | 34 ++++++++++++++++++++++++++++++++ 3 files changed, 89 insertions(+), 6 deletions(-) diff --git a/include/uapi/linux/openvswitch.h b/include/uapi/linux/openvswitch.h index a87b44cd5590a..ae2bff14e7e14 100644 --- a/include/uapi/linux/openvswitch.h +++ b/include/uapi/linux/openvswitch.h @@ -672,6 +672,32 @@ struct ovs_action_push_mpls { __be16 mpls_ethertype; /* Either %ETH_P_MPLS_UC or %ETH_P_MPLS_MC */ }; +/** + * struct ovs_action_add_mpls - %OVS_ACTION_ATTR_ADD_MPLS action + * argument. + * @mpls_lse: MPLS label stack entry to push. + * @mpls_ethertype: Ethertype to set in the encapsulating ethernet frame. + * @tun_flags: MPLS tunnel attributes. + * + * The only values @mpls_ethertype should ever be given are %ETH_P_MPLS_UC and + * %ETH_P_MPLS_MC, indicating MPLS unicast or multicast. Other are rejected. + */ +struct ovs_action_add_mpls { + __be32 mpls_lse; + __be16 mpls_ethertype; /* Either %ETH_P_MPLS_UC or %ETH_P_MPLS_MC */ + __u16 tun_flags; +}; + +#define OVS_MPLS_L3_TUNNEL_FLAG_MASK (1 << 0) /* Flag to specify the place of + * insertion of MPLS header. + * When false, the MPLS header + * will be inserted at the start + * of the packet. + * When true, the MPLS header + * will be inserted at the start + * of the l3 header. + */ + /** * struct ovs_action_push_vlan - %OVS_ACTION_ATTR_PUSH_VLAN action argument. * @vlan_tpid: Tag protocol identifier (TPID) to push. @@ -892,6 +918,10 @@ struct check_pkt_len_arg { * @OVS_ACTION_ATTR_CHECK_PKT_LEN: Check the packet length and execute a set * of actions if greater than the specified packet length, else execute * another set of actions. + * @OVS_ACTION_ATTR_ADD_MPLS: Push a new MPLS label stack entry at the + * start of the packet or at the start of the l3 header depending on the value + * of l3 tunnel flag in the tun_flags field of OVS_ACTION_ATTR_ADD_MPLS + * argument. * * Only a single header can be set with a single %OVS_ACTION_ATTR_SET. Not all * fields within a header are modifiable, e.g. the IPv4 protocol and fragment @@ -927,6 +957,7 @@ enum ovs_action_attr { OVS_ACTION_ATTR_METER, /* u32 meter ID. */ OVS_ACTION_ATTR_CLONE, /* Nested OVS_CLONE_ATTR_*. */ OVS_ACTION_ATTR_CHECK_PKT_LEN, /* Nested OVS_CHECK_PKT_LEN_ATTR_*. */ + OVS_ACTION_ATTR_ADD_MPLS, /* struct ovs_action_add_mpls. */ __OVS_ACTION_ATTR_MAX, /* Nothing past this will be accepted * from userspace. */ diff --git a/net/openvswitch/actions.c b/net/openvswitch/actions.c index 4c83954623034..7fbfe2adfffa6 100644 --- a/net/openvswitch/actions.c +++ b/net/openvswitch/actions.c @@ -161,16 +161,17 @@ static int do_execute_actions(struct datapath *dp, struct sk_buff *skb, const struct nlattr *attr, int len); static int push_mpls(struct sk_buff *skb, struct sw_flow_key *key, - const struct ovs_action_push_mpls *mpls) + __be32 mpls_lse, __be16 mpls_ethertype, __u16 mac_len) { int err; - err = skb_mpls_push(skb, mpls->mpls_lse, mpls->mpls_ethertype, - skb->mac_len, - ovs_key_mac_proto(key) == MAC_PROTO_ETHERNET); + err = skb_mpls_push(skb, mpls_lse, mpls_ethertype, mac_len, !!mac_len); if (err) return err; + if (!mac_len) + key->mac_proto = MAC_PROTO_NONE; + invalidate_flow_key(key); return 0; } @@ -185,6 +186,9 @@ static int pop_mpls(struct sk_buff *skb, struct sw_flow_key *key, if (err) return err; + if (ethertype == htons(ETH_P_TEB)) + key->mac_proto = MAC_PROTO_ETHERNET; + invalidate_flow_key(key); return 0; } @@ -1229,10 +1233,24 @@ static int do_execute_actions(struct datapath *dp, struct sk_buff *skb, execute_hash(skb, key, a); break; - case OVS_ACTION_ATTR_PUSH_MPLS: - err = push_mpls(skb, key, nla_data(a)); + case OVS_ACTION_ATTR_PUSH_MPLS: { + struct ovs_action_push_mpls *mpls = nla_data(a); + + err = push_mpls(skb, key, mpls->mpls_lse, + mpls->mpls_ethertype, skb->mac_len); break; + } + case OVS_ACTION_ATTR_ADD_MPLS: { + struct ovs_action_add_mpls *mpls = nla_data(a); + __u16 mac_len = 0; + + if (mpls->tun_flags & OVS_MPLS_L3_TUNNEL_FLAG_MASK) + mac_len = skb->mac_len; + err = push_mpls(skb, key, mpls->mpls_lse, + mpls->mpls_ethertype, mac_len); + break; + } case OVS_ACTION_ATTR_POP_MPLS: err = pop_mpls(skb, key, nla_get_be16(a)); break; diff --git a/net/openvswitch/flow_netlink.c b/net/openvswitch/flow_netlink.c index 65c2e3458ff58..7da4230627f51 100644 --- a/net/openvswitch/flow_netlink.c +++ b/net/openvswitch/flow_netlink.c @@ -79,6 +79,7 @@ static bool actions_may_change_flow(const struct nlattr *actions) case OVS_ACTION_ATTR_SET_MASKED: case OVS_ACTION_ATTR_METER: case OVS_ACTION_ATTR_CHECK_PKT_LEN: + case OVS_ACTION_ATTR_ADD_MPLS: default: return true; } @@ -3005,6 +3006,7 @@ static int __ovs_nla_copy_actions(struct net *net, const struct nlattr *attr, [OVS_ACTION_ATTR_METER] = sizeof(u32), [OVS_ACTION_ATTR_CLONE] = (u32)-1, [OVS_ACTION_ATTR_CHECK_PKT_LEN] = (u32)-1, + [OVS_ACTION_ATTR_ADD_MPLS] = sizeof(struct ovs_action_add_mpls), }; const struct ovs_action_push_vlan *vlan; int type = nla_type(a); @@ -3072,6 +3074,33 @@ static int __ovs_nla_copy_actions(struct net *net, const struct nlattr *attr, case OVS_ACTION_ATTR_RECIRC: break; + case OVS_ACTION_ATTR_ADD_MPLS: { + const struct ovs_action_add_mpls *mpls = nla_data(a); + + if (!eth_p_mpls(mpls->mpls_ethertype)) + return -EINVAL; + + if (mpls->tun_flags & OVS_MPLS_L3_TUNNEL_FLAG_MASK) { + if (vlan_tci & htons(VLAN_CFI_MASK) || + (eth_type != htons(ETH_P_IP) && + eth_type != htons(ETH_P_IPV6) && + eth_type != htons(ETH_P_ARP) && + eth_type != htons(ETH_P_RARP) && + !eth_p_mpls(eth_type))) + return -EINVAL; + mpls_label_count++; + } else { + if (mac_proto == MAC_PROTO_ETHERNET) { + mpls_label_count = 1; + mac_proto = MAC_PROTO_NONE; + } else { + mpls_label_count++; + } + } + eth_type = mpls->mpls_ethertype; + break; + } + case OVS_ACTION_ATTR_PUSH_MPLS: { const struct ovs_action_push_mpls *mpls = nla_data(a); @@ -3109,6 +3138,11 @@ static int __ovs_nla_copy_actions(struct net *net, const struct nlattr *attr, * recirculation. */ proto = nla_get_be16(a); + + if (proto == htons(ETH_P_TEB) && + mac_proto != MAC_PROTO_NONE) + return -EINVAL; + mpls_label_count--; if (!eth_p_mpls(proto) || !mpls_label_count) -- GitLab From 3127f7c9b7da3f64a7b970bfbfa307f48adbb72e Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Sat, 21 Dec 2019 14:03:38 +0100 Subject: [PATCH 1291/1437] r8169: factor out rtl8168h_2_get_adc_bias_ioffset Simplify and factor out this magic from rtl8168h_2_hw_phy_config() and name it based on the vendor driver. Signed-off-by: Heiner Kallweit Signed-off-by: David S. Miller --- drivers/net/ethernet/realtek/r8169_main.c | 39 ++++++++++++----------- 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/drivers/net/ethernet/realtek/r8169_main.c b/drivers/net/ethernet/realtek/r8169_main.c index a887b685d0704..c845a5850ce90 100644 --- a/drivers/net/ethernet/realtek/r8169_main.c +++ b/drivers/net/ethernet/realtek/r8169_main.c @@ -3204,11 +3204,26 @@ static void rtl8168h_1_hw_phy_config(struct rtl8169_private *tp) rtl_enable_eee(tp); } +static u16 rtl8168h_2_get_adc_bias_ioffset(struct rtl8169_private *tp) +{ + u16 data1, data2, ioffset; + + r8168_mac_ocp_write(tp, 0xdd02, 0x807d); + data1 = r8168_mac_ocp_read(tp, 0xdd02); + data2 = r8168_mac_ocp_read(tp, 0xdd00); + + ioffset = (data2 >> 1) & 0x7ff8; + ioffset |= data2 & 0x0007; + if (data1 & BIT(7)) + ioffset |= BIT(15); + + return ioffset; +} + static void rtl8168h_2_hw_phy_config(struct rtl8169_private *tp) { - u16 ioffset_p3, ioffset_p2, ioffset_p1, ioffset_p0; struct phy_device *phydev = tp->phydev; - u16 rlen; + u16 ioffset, rlen; u32 data; rtl_apply_firmware(tp); @@ -3223,23 +3238,9 @@ static void rtl8168h_2_hw_phy_config(struct rtl8169_private *tp) /* enable GPHY 10M */ phy_modify_paged(tp->phydev, 0x0a44, 0x11, 0, BIT(11)); - r8168_mac_ocp_write(tp, 0xdd02, 0x807d); - data = r8168_mac_ocp_read(tp, 0xdd02); - ioffset_p3 = ((data & 0x80)>>7); - ioffset_p3 <<= 3; - - data = r8168_mac_ocp_read(tp, 0xdd00); - ioffset_p3 |= ((data & (0xe000))>>13); - ioffset_p2 = ((data & (0x1e00))>>9); - ioffset_p1 = ((data & (0x01e0))>>5); - ioffset_p0 = ((data & 0x0010)>>4); - ioffset_p0 <<= 3; - ioffset_p0 |= (data & (0x07)); - data = (ioffset_p3<<12)|(ioffset_p2<<8)|(ioffset_p1<<4)|(ioffset_p0); - - if ((ioffset_p3 != 0x0f) || (ioffset_p2 != 0x0f) || - (ioffset_p1 != 0x0f) || (ioffset_p0 != 0x0f)) - phy_write_paged(phydev, 0x0bcf, 0x16, data); + ioffset = rtl8168h_2_get_adc_bias_ioffset(tp); + if (ioffset != 0xffff) + phy_write_paged(phydev, 0x0bcf, 0x16, ioffset); /* Modify rlen (TX LPF corner frequency) level */ data = phy_read_paged(phydev, 0x0bcd, 0x16); -- GitLab From 8722709b049473f9bd3ba9b817d52d9ab0a8e20b Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Sat, 21 Dec 2019 14:11:08 +0100 Subject: [PATCH 1292/1437] r8169: remove MAC workaround in rtl8168e_2_hw_phy_config Due to recent changes we don't need the call to rtl_rar_exgmac_set() and longer at this place. It's called from rtl_rar_set() which is called in rtl_init_mac_address() and rtl8169_resume(). Signed-off-by: Heiner Kallweit Signed-off-by: David S. Miller --- drivers/net/ethernet/realtek/r8169_main.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/net/ethernet/realtek/r8169_main.c b/drivers/net/ethernet/realtek/r8169_main.c index c845a5850ce90..38a09b5ee0b4b 100644 --- a/drivers/net/ethernet/realtek/r8169_main.c +++ b/drivers/net/ethernet/realtek/r8169_main.c @@ -2961,9 +2961,6 @@ static void rtl8168e_2_hw_phy_config(struct rtl8169_private *tp) rtl_writephy(tp, 0x1f, 0x0005); rtl_w0w1_phy(tp, 0x01, 0x0100, 0x0000); rtl_writephy(tp, 0x1f, 0x0000); - - /* Broken BIOS workaround: feed GigaMAC registers with MAC address. */ - rtl_rar_exgmac_set(tp, tp->dev->dev_addr); } static void rtl8168f_hw_phy_config(struct rtl8169_private *tp) -- GitLab From af7797785d61e1053d23764b5d3fbdf2ab926f31 Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Sat, 21 Dec 2019 14:15:21 +0100 Subject: [PATCH 1293/1437] r8169: move enabling EEE to rtl8169_init_phy Simplify the code by moving the call to rtl_enable_eee() from the individual PHY configs to rtl8169_init_phy(). Signed-off-by: Heiner Kallweit Signed-off-by: David S. Miller --- drivers/net/ethernet/realtek/r8169_main.c | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/drivers/net/ethernet/realtek/r8169_main.c b/drivers/net/ethernet/realtek/r8169_main.c index 38a09b5ee0b4b..0161d839fa870 100644 --- a/drivers/net/ethernet/realtek/r8169_main.c +++ b/drivers/net/ethernet/realtek/r8169_main.c @@ -2951,7 +2951,6 @@ static void rtl8168e_2_hw_phy_config(struct rtl8169_private *tp) r8168d_phy_param(phydev, 0x8b85, 0x0000, 0x4000); rtl8168f_config_eee_phy(tp); - rtl_enable_eee(tp); /* Green feature */ rtl_writephy(tp, 0x1f, 0x0003); @@ -2978,7 +2977,6 @@ static void rtl8168f_hw_phy_config(struct rtl8169_private *tp) r8168d_phy_param(phydev, 0x8b86, 0x0000, 0x0001); rtl8168f_config_eee_phy(tp); - rtl_enable_eee(tp); } static void rtl8168f_1_hw_phy_config(struct rtl8169_private *tp) @@ -3124,14 +3122,12 @@ static void rtl8168g_1_hw_phy_config(struct rtl8169_private *tp) rtl8168g_disable_aldps(tp); rtl8168g_config_eee_phy(tp); - rtl_enable_eee(tp); } static void rtl8168g_2_hw_phy_config(struct rtl8169_private *tp) { rtl_apply_firmware(tp); rtl8168g_config_eee_phy(tp); - rtl_enable_eee(tp); } static void rtl8168h_1_hw_phy_config(struct rtl8169_private *tp) @@ -3198,7 +3194,6 @@ static void rtl8168h_1_hw_phy_config(struct rtl8169_private *tp) rtl8168g_disable_aldps(tp); rtl8168h_config_eee_phy(tp); - rtl_enable_eee(tp); } static u16 rtl8168h_2_get_adc_bias_ioffset(struct rtl8169_private *tp) @@ -3253,7 +3248,6 @@ static void rtl8168h_2_hw_phy_config(struct rtl8169_private *tp) rtl8168g_disable_aldps(tp); rtl8168g_config_eee_phy(tp); - rtl_enable_eee(tp); } static void rtl8168ep_1_hw_phy_config(struct rtl8169_private *tp) @@ -3276,7 +3270,6 @@ static void rtl8168ep_1_hw_phy_config(struct rtl8169_private *tp) rtl8168g_disable_aldps(tp); rtl8168g_config_eee_phy(tp); - rtl_enable_eee(tp); } static void rtl8168ep_2_hw_phy_config(struct rtl8169_private *tp) @@ -3328,7 +3321,6 @@ static void rtl8168ep_2_hw_phy_config(struct rtl8169_private *tp) rtl8168g_disable_aldps(tp); rtl8168g_config_eee_phy(tp); - rtl_enable_eee(tp); } static void rtl8117_hw_phy_config(struct rtl8169_private *tp) @@ -3368,7 +3360,6 @@ static void rtl8117_hw_phy_config(struct rtl8169_private *tp) rtl8168g_disable_aldps(tp); rtl8168h_config_eee_phy(tp); - rtl_enable_eee(tp); } static void rtl8102e_hw_phy_config(struct rtl8169_private *tp) @@ -3475,7 +3466,6 @@ static void rtl8125_1_hw_phy_config(struct rtl8169_private *tp) phy_modify_paged(phydev, 0xa44, 0x11, 0x0000, 0x0800); rtl8125_config_eee_phy(tp); - rtl_enable_eee(tp); } static void rtl8125_2_hw_phy_config(struct rtl8169_private *tp) @@ -3541,7 +3531,6 @@ static void rtl8125_2_hw_phy_config(struct rtl8169_private *tp) phy_modify_paged(phydev, 0xa44, 0x11, 0x0000, 0x0800); rtl8125_config_eee_phy(tp); - rtl_enable_eee(tp); } static void rtl_hw_phy_config(struct net_device *dev) @@ -3630,6 +3619,9 @@ static void rtl8169_init_phy(struct net_device *dev, struct rtl8169_private *tp) /* We may have called phy_speed_down before */ phy_speed_up(tp->phydev); + if (rtl_supports_eee(tp)) + rtl_enable_eee(tp); + genphy_soft_reset(tp->phydev); } -- GitLab From bd085ef678b2cc8c38c105673dfe8ff8f5ec0c57 Mon Sep 17 00:00:00 2001 From: Hangbin Liu Date: Sun, 22 Dec 2019 10:51:09 +0800 Subject: [PATCH 1294/1437] net: add bool confirm_neigh parameter for dst_ops.update_pmtu The MTU update code is supposed to be invoked in response to real networking events that update the PMTU. In IPv6 PMTU update function __ip6_rt_update_pmtu() we called dst_confirm_neigh() to update neighbor confirmed time. But for tunnel code, it will call pmtu before xmit, like: - tnl_update_pmtu() - skb_dst_update_pmtu() - ip6_rt_update_pmtu() - __ip6_rt_update_pmtu() - dst_confirm_neigh() If the tunnel remote dst mac address changed and we still do the neigh confirm, we will not be able to update neigh cache and ping6 remote will failed. So for this ip_tunnel_xmit() case, _EVEN_ if the MTU is changed, we should not be invoking dst_confirm_neigh() as we have no evidence of successful two-way communication at this point. On the other hand it is also important to keep the neigh reachability fresh for TCP flows, so we cannot remove this dst_confirm_neigh() call. To fix the issue, we have to add a new bool parameter for dst_ops.update_pmtu to choose whether we should do neigh update or not. I will add the parameter in this patch and set all the callers to true to comply with the previous way, and fix the tunnel code one by one on later patches. v5: No change. v4: No change. v3: Do not remove dst_confirm_neigh, but add a new bool parameter in dst_ops.update_pmtu to control whether we should do neighbor confirm. Also split the big patch to small ones for each area. v2: Remove dst_confirm_neigh in __ip6_rt_update_pmtu. Suggested-by: David Miller Reviewed-by: Guillaume Nault Acked-by: David Ahern Signed-off-by: Hangbin Liu Signed-off-by: David S. Miller --- drivers/net/gtp.c | 2 +- include/net/dst.h | 2 +- include/net/dst_ops.h | 3 ++- net/bridge/br_nf_core.c | 3 ++- net/decnet/dn_route.c | 6 ++++-- net/ipv4/inet_connection_sock.c | 2 +- net/ipv4/route.c | 9 ++++++--- net/ipv4/xfrm4_policy.c | 5 +++-- net/ipv6/inet6_connection_sock.c | 2 +- net/ipv6/ip6_gre.c | 2 +- net/ipv6/route.c | 22 +++++++++++++++------- net/ipv6/xfrm6_policy.c | 5 +++-- net/netfilter/ipvs/ip_vs_xmit.c | 2 +- net/sctp/transport.c | 2 +- 14 files changed, 42 insertions(+), 25 deletions(-) diff --git a/drivers/net/gtp.c b/drivers/net/gtp.c index e5b7d6d2286e0..913062017be97 100644 --- a/drivers/net/gtp.c +++ b/drivers/net/gtp.c @@ -540,7 +540,7 @@ static int gtp_build_skb_ip4(struct sk_buff *skb, struct net_device *dev, mtu = dst_mtu(&rt->dst); } - rt->dst.ops->update_pmtu(&rt->dst, NULL, skb, mtu); + rt->dst.ops->update_pmtu(&rt->dst, NULL, skb, mtu, true); if (!skb_is_gso(skb) && (iph->frag_off & htons(IP_DF)) && mtu < ntohs(iph->tot_len)) { diff --git a/include/net/dst.h b/include/net/dst.h index 8224dad2ae946..593630e0e0763 100644 --- a/include/net/dst.h +++ b/include/net/dst.h @@ -516,7 +516,7 @@ static inline void skb_dst_update_pmtu(struct sk_buff *skb, u32 mtu) struct dst_entry *dst = skb_dst(skb); if (dst && dst->ops->update_pmtu) - dst->ops->update_pmtu(dst, NULL, skb, mtu); + dst->ops->update_pmtu(dst, NULL, skb, mtu, true); } static inline void skb_tunnel_check_pmtu(struct sk_buff *skb, diff --git a/include/net/dst_ops.h b/include/net/dst_ops.h index 5ec645f27ee38..443863c7b8da3 100644 --- a/include/net/dst_ops.h +++ b/include/net/dst_ops.h @@ -27,7 +27,8 @@ struct dst_ops { struct dst_entry * (*negative_advice)(struct dst_entry *); void (*link_failure)(struct sk_buff *); void (*update_pmtu)(struct dst_entry *dst, struct sock *sk, - struct sk_buff *skb, u32 mtu); + struct sk_buff *skb, u32 mtu, + bool confirm_neigh); void (*redirect)(struct dst_entry *dst, struct sock *sk, struct sk_buff *skb); int (*local_out)(struct net *net, struct sock *sk, struct sk_buff *skb); diff --git a/net/bridge/br_nf_core.c b/net/bridge/br_nf_core.c index 2cdfc5d6c25db..8c69f0c95a8ed 100644 --- a/net/bridge/br_nf_core.c +++ b/net/bridge/br_nf_core.c @@ -22,7 +22,8 @@ #endif static void fake_update_pmtu(struct dst_entry *dst, struct sock *sk, - struct sk_buff *skb, u32 mtu) + struct sk_buff *skb, u32 mtu, + bool confirm_neigh) { } diff --git a/net/decnet/dn_route.c b/net/decnet/dn_route.c index aea918135ec37..08c3dc45f1a43 100644 --- a/net/decnet/dn_route.c +++ b/net/decnet/dn_route.c @@ -110,7 +110,8 @@ static void dn_dst_ifdown(struct dst_entry *, struct net_device *dev, int how); static struct dst_entry *dn_dst_negative_advice(struct dst_entry *); static void dn_dst_link_failure(struct sk_buff *); static void dn_dst_update_pmtu(struct dst_entry *dst, struct sock *sk, - struct sk_buff *skb , u32 mtu); + struct sk_buff *skb , u32 mtu, + bool confirm_neigh); static void dn_dst_redirect(struct dst_entry *dst, struct sock *sk, struct sk_buff *skb); static struct neighbour *dn_dst_neigh_lookup(const struct dst_entry *dst, @@ -251,7 +252,8 @@ static int dn_dst_gc(struct dst_ops *ops) * advertise to the other end). */ static void dn_dst_update_pmtu(struct dst_entry *dst, struct sock *sk, - struct sk_buff *skb, u32 mtu) + struct sk_buff *skb, u32 mtu, + bool confirm_neigh) { struct dn_route *rt = (struct dn_route *) dst; struct neighbour *n = rt->n; diff --git a/net/ipv4/inet_connection_sock.c b/net/ipv4/inet_connection_sock.c index e4c6e8b404906..18c0d5bffe12b 100644 --- a/net/ipv4/inet_connection_sock.c +++ b/net/ipv4/inet_connection_sock.c @@ -1086,7 +1086,7 @@ struct dst_entry *inet_csk_update_pmtu(struct sock *sk, u32 mtu) if (!dst) goto out; } - dst->ops->update_pmtu(dst, sk, NULL, mtu); + dst->ops->update_pmtu(dst, sk, NULL, mtu, true); dst = __sk_dst_check(sk, 0); if (!dst) diff --git a/net/ipv4/route.c b/net/ipv4/route.c index f88c93c38f117..87e979f2b74a5 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -139,7 +139,8 @@ static unsigned int ipv4_mtu(const struct dst_entry *dst); static struct dst_entry *ipv4_negative_advice(struct dst_entry *dst); static void ipv4_link_failure(struct sk_buff *skb); static void ip_rt_update_pmtu(struct dst_entry *dst, struct sock *sk, - struct sk_buff *skb, u32 mtu); + struct sk_buff *skb, u32 mtu, + bool confirm_neigh); static void ip_do_redirect(struct dst_entry *dst, struct sock *sk, struct sk_buff *skb); static void ipv4_dst_destroy(struct dst_entry *dst); @@ -1043,7 +1044,8 @@ static void __ip_rt_update_pmtu(struct rtable *rt, struct flowi4 *fl4, u32 mtu) } static void ip_rt_update_pmtu(struct dst_entry *dst, struct sock *sk, - struct sk_buff *skb, u32 mtu) + struct sk_buff *skb, u32 mtu, + bool confirm_neigh) { struct rtable *rt = (struct rtable *) dst; struct flowi4 fl4; @@ -2687,7 +2689,8 @@ static unsigned int ipv4_blackhole_mtu(const struct dst_entry *dst) } static void ipv4_rt_blackhole_update_pmtu(struct dst_entry *dst, struct sock *sk, - struct sk_buff *skb, u32 mtu) + struct sk_buff *skb, u32 mtu, + bool confirm_neigh) { } diff --git a/net/ipv4/xfrm4_policy.c b/net/ipv4/xfrm4_policy.c index 35b84b52b7027..9ebd54752e03b 100644 --- a/net/ipv4/xfrm4_policy.c +++ b/net/ipv4/xfrm4_policy.c @@ -100,12 +100,13 @@ static int xfrm4_fill_dst(struct xfrm_dst *xdst, struct net_device *dev, } static void xfrm4_update_pmtu(struct dst_entry *dst, struct sock *sk, - struct sk_buff *skb, u32 mtu) + struct sk_buff *skb, u32 mtu, + bool confirm_neigh) { struct xfrm_dst *xdst = (struct xfrm_dst *)dst; struct dst_entry *path = xdst->route; - path->ops->update_pmtu(path, sk, skb, mtu); + path->ops->update_pmtu(path, sk, skb, mtu, confirm_neigh); } static void xfrm4_redirect(struct dst_entry *dst, struct sock *sk, diff --git a/net/ipv6/inet6_connection_sock.c b/net/ipv6/inet6_connection_sock.c index fe9cb8d1adca0..e315526fa244a 100644 --- a/net/ipv6/inet6_connection_sock.c +++ b/net/ipv6/inet6_connection_sock.c @@ -146,7 +146,7 @@ struct dst_entry *inet6_csk_update_pmtu(struct sock *sk, u32 mtu) if (IS_ERR(dst)) return NULL; - dst->ops->update_pmtu(dst, sk, NULL, mtu); + dst->ops->update_pmtu(dst, sk, NULL, mtu, true); dst = inet6_csk_route_socket(sk, &fl6); return IS_ERR(dst) ? NULL : dst; diff --git a/net/ipv6/ip6_gre.c b/net/ipv6/ip6_gre.c index 9d0965252ddf7..3ba69174ad6c0 100644 --- a/net/ipv6/ip6_gre.c +++ b/net/ipv6/ip6_gre.c @@ -1040,7 +1040,7 @@ static netdev_tx_t ip6erspan_tunnel_xmit(struct sk_buff *skb, /* TooBig packet may have updated dst->dev's mtu */ if (!t->parms.collect_md && dst && dst_mtu(dst) > dst->dev->mtu) - dst->ops->update_pmtu(dst, NULL, skb, dst->dev->mtu); + dst->ops->update_pmtu(dst, NULL, skb, dst->dev->mtu, true); err = ip6_tnl_xmit(skb, dev, dsfield, &fl6, encap_limit, &mtu, NEXTHDR_GRE); diff --git a/net/ipv6/route.c b/net/ipv6/route.c index b59940416cb57..affb51c11a25b 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -95,7 +95,8 @@ static int ip6_pkt_prohibit(struct sk_buff *skb); static int ip6_pkt_prohibit_out(struct net *net, struct sock *sk, struct sk_buff *skb); static void ip6_link_failure(struct sk_buff *skb); static void ip6_rt_update_pmtu(struct dst_entry *dst, struct sock *sk, - struct sk_buff *skb, u32 mtu); + struct sk_buff *skb, u32 mtu, + bool confirm_neigh); static void rt6_do_redirect(struct dst_entry *dst, struct sock *sk, struct sk_buff *skb); static int rt6_score_route(const struct fib6_nh *nh, u32 fib6_flags, int oif, @@ -264,7 +265,8 @@ static unsigned int ip6_blackhole_mtu(const struct dst_entry *dst) } static void ip6_rt_blackhole_update_pmtu(struct dst_entry *dst, struct sock *sk, - struct sk_buff *skb, u32 mtu) + struct sk_buff *skb, u32 mtu, + bool confirm_neigh) { } @@ -2692,7 +2694,8 @@ static bool rt6_cache_allowed_for_pmtu(const struct rt6_info *rt) } static void __ip6_rt_update_pmtu(struct dst_entry *dst, const struct sock *sk, - const struct ipv6hdr *iph, u32 mtu) + const struct ipv6hdr *iph, u32 mtu, + bool confirm_neigh) { const struct in6_addr *daddr, *saddr; struct rt6_info *rt6 = (struct rt6_info *)dst; @@ -2710,7 +2713,10 @@ static void __ip6_rt_update_pmtu(struct dst_entry *dst, const struct sock *sk, daddr = NULL; saddr = NULL; } - dst_confirm_neigh(dst, daddr); + + if (confirm_neigh) + dst_confirm_neigh(dst, daddr); + mtu = max_t(u32, mtu, IPV6_MIN_MTU); if (mtu >= dst_mtu(dst)) return; @@ -2764,9 +2770,11 @@ out_unlock: } static void ip6_rt_update_pmtu(struct dst_entry *dst, struct sock *sk, - struct sk_buff *skb, u32 mtu) + struct sk_buff *skb, u32 mtu, + bool confirm_neigh) { - __ip6_rt_update_pmtu(dst, sk, skb ? ipv6_hdr(skb) : NULL, mtu); + __ip6_rt_update_pmtu(dst, sk, skb ? ipv6_hdr(skb) : NULL, mtu, + confirm_neigh); } void ip6_update_pmtu(struct sk_buff *skb, struct net *net, __be32 mtu, @@ -2785,7 +2793,7 @@ void ip6_update_pmtu(struct sk_buff *skb, struct net *net, __be32 mtu, dst = ip6_route_output(net, NULL, &fl6); if (!dst->error) - __ip6_rt_update_pmtu(dst, NULL, iph, ntohl(mtu)); + __ip6_rt_update_pmtu(dst, NULL, iph, ntohl(mtu), true); dst_release(dst); } EXPORT_SYMBOL_GPL(ip6_update_pmtu); diff --git a/net/ipv6/xfrm6_policy.c b/net/ipv6/xfrm6_policy.c index 699e0730ce8e8..af7a4b8b1e9c4 100644 --- a/net/ipv6/xfrm6_policy.c +++ b/net/ipv6/xfrm6_policy.c @@ -98,12 +98,13 @@ static int xfrm6_fill_dst(struct xfrm_dst *xdst, struct net_device *dev, } static void xfrm6_update_pmtu(struct dst_entry *dst, struct sock *sk, - struct sk_buff *skb, u32 mtu) + struct sk_buff *skb, u32 mtu, + bool confirm_neigh) { struct xfrm_dst *xdst = (struct xfrm_dst *)dst; struct dst_entry *path = xdst->route; - path->ops->update_pmtu(path, sk, skb, mtu); + path->ops->update_pmtu(path, sk, skb, mtu, confirm_neigh); } static void xfrm6_redirect(struct dst_entry *dst, struct sock *sk, diff --git a/net/netfilter/ipvs/ip_vs_xmit.c b/net/netfilter/ipvs/ip_vs_xmit.c index b1e300f8881b2..b00866d777fe0 100644 --- a/net/netfilter/ipvs/ip_vs_xmit.c +++ b/net/netfilter/ipvs/ip_vs_xmit.c @@ -208,7 +208,7 @@ static inline void maybe_update_pmtu(int skb_af, struct sk_buff *skb, int mtu) struct rtable *ort = skb_rtable(skb); if (!skb->dev && sk && sk_fullsock(sk)) - ort->dst.ops->update_pmtu(&ort->dst, sk, NULL, mtu); + ort->dst.ops->update_pmtu(&ort->dst, sk, NULL, mtu, true); } static inline bool ensure_mtu_is_adequate(struct netns_ipvs *ipvs, int skb_af, diff --git a/net/sctp/transport.c b/net/sctp/transport.c index 7235a60326712..3bbe1a58ec876 100644 --- a/net/sctp/transport.c +++ b/net/sctp/transport.c @@ -263,7 +263,7 @@ bool sctp_transport_update_pmtu(struct sctp_transport *t, u32 pmtu) pf->af->from_sk(&addr, sk); pf->to_sk_daddr(&t->ipaddr, sk); - dst->ops->update_pmtu(dst, sk, NULL, pmtu); + dst->ops->update_pmtu(dst, sk, NULL, pmtu, true); pf->to_sk_daddr(&addr, sk); dst = sctp_transport_dst_check(t); -- GitLab From 675d76ad0ad5bf41c9a129772ef0aba8f57ea9a7 Mon Sep 17 00:00:00 2001 From: Hangbin Liu Date: Sun, 22 Dec 2019 10:51:10 +0800 Subject: [PATCH 1295/1437] ip6_gre: do not confirm neighbor when do pmtu update When we do ipv6 gre pmtu update, we will also do neigh confirm currently. This will cause the neigh cache be refreshed and set to REACHABLE before xmit. But if the remote mac address changed, e.g. device is deleted and recreated, we will not able to notice this and still use the old mac address as the neigh cache is REACHABLE. Fix this by disable neigh confirm when do pmtu update v5: No change. v4: No change. v3: Do not remove dst_confirm_neigh, but add a new bool parameter in dst_ops.update_pmtu to control whether we should do neighbor confirm. Also split the big patch to small ones for each area. v2: Remove dst_confirm_neigh in __ip6_rt_update_pmtu. Reported-by: Jianlin Shi Reviewed-by: Guillaume Nault Acked-by: David Ahern Signed-off-by: Hangbin Liu Signed-off-by: David S. Miller --- net/ipv6/ip6_gre.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/ipv6/ip6_gre.c b/net/ipv6/ip6_gre.c index 3ba69174ad6c0..ee968d9807463 100644 --- a/net/ipv6/ip6_gre.c +++ b/net/ipv6/ip6_gre.c @@ -1040,7 +1040,7 @@ static netdev_tx_t ip6erspan_tunnel_xmit(struct sk_buff *skb, /* TooBig packet may have updated dst->dev's mtu */ if (!t->parms.collect_md && dst && dst_mtu(dst) > dst->dev->mtu) - dst->ops->update_pmtu(dst, NULL, skb, dst->dev->mtu, true); + dst->ops->update_pmtu(dst, NULL, skb, dst->dev->mtu, false); err = ip6_tnl_xmit(skb, dev, dsfield, &fl6, encap_limit, &mtu, NEXTHDR_GRE); -- GitLab From 6e9105c73f8d2163d12d5dfd762fd75483ed30f5 Mon Sep 17 00:00:00 2001 From: Hangbin Liu Date: Sun, 22 Dec 2019 10:51:11 +0800 Subject: [PATCH 1296/1437] gtp: do not confirm neighbor when do pmtu update When do IPv6 tunnel PMTU update and calls __ip6_rt_update_pmtu() in the end, we should not call dst_confirm_neigh() as there is no two-way communication. Although GTP only support ipv4 right now, and __ip_rt_update_pmtu() does not call dst_confirm_neigh(), we still set it to false to keep consistency with IPv6 code. v5: No change. v4: No change. v3: Do not remove dst_confirm_neigh, but add a new bool parameter in dst_ops.update_pmtu to control whether we should do neighbor confirm. Also split the big patch to small ones for each area. v2: Remove dst_confirm_neigh in __ip6_rt_update_pmtu. Reviewed-by: Guillaume Nault Acked-by: David Ahern Signed-off-by: Hangbin Liu Signed-off-by: David S. Miller --- drivers/net/gtp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/gtp.c b/drivers/net/gtp.c index 913062017be97..fca471e27f39d 100644 --- a/drivers/net/gtp.c +++ b/drivers/net/gtp.c @@ -540,7 +540,7 @@ static int gtp_build_skb_ip4(struct sk_buff *skb, struct net_device *dev, mtu = dst_mtu(&rt->dst); } - rt->dst.ops->update_pmtu(&rt->dst, NULL, skb, mtu, true); + rt->dst.ops->update_pmtu(&rt->dst, NULL, skb, mtu, false); if (!skb_is_gso(skb) && (iph->frag_off & htons(IP_DF)) && mtu < ntohs(iph->tot_len)) { -- GitLab From 07dc35c6e3cc3c001915d05f5bf21f80a39a0970 Mon Sep 17 00:00:00 2001 From: Hangbin Liu Date: Sun, 22 Dec 2019 10:51:12 +0800 Subject: [PATCH 1297/1437] net/dst: add new function skb_dst_update_pmtu_no_confirm Add a new function skb_dst_update_pmtu_no_confirm() for callers who need update pmtu but should not do neighbor confirm. v5: No change. v4: No change. v3: Do not remove dst_confirm_neigh, but add a new bool parameter in dst_ops.update_pmtu to control whether we should do neighbor confirm. Also split the big patch to small ones for each area. v2: Remove dst_confirm_neigh in __ip6_rt_update_pmtu. Reviewed-by: Guillaume Nault Acked-by: David Ahern Signed-off-by: Hangbin Liu Signed-off-by: David S. Miller --- include/net/dst.h | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/include/net/dst.h b/include/net/dst.h index 593630e0e0763..dc7cc1f1051c6 100644 --- a/include/net/dst.h +++ b/include/net/dst.h @@ -519,6 +519,15 @@ static inline void skb_dst_update_pmtu(struct sk_buff *skb, u32 mtu) dst->ops->update_pmtu(dst, NULL, skb, mtu, true); } +/* update dst pmtu but not do neighbor confirm */ +static inline void skb_dst_update_pmtu_no_confirm(struct sk_buff *skb, u32 mtu) +{ + struct dst_entry *dst = skb_dst(skb); + + if (dst && dst->ops->update_pmtu) + dst->ops->update_pmtu(dst, NULL, skb, mtu, false); +} + static inline void skb_tunnel_check_pmtu(struct sk_buff *skb, struct dst_entry *encap_dst, int headroom) -- GitLab From 7a1592bcb15d71400a98632727791d1e68ea0ee8 Mon Sep 17 00:00:00 2001 From: Hangbin Liu Date: Sun, 22 Dec 2019 10:51:13 +0800 Subject: [PATCH 1298/1437] tunnel: do not confirm neighbor when do pmtu update When do tunnel PMTU update and calls __ip6_rt_update_pmtu() in the end, we should not call dst_confirm_neigh() as there is no two-way communication. v5: No Change. v4: Update commit description v3: Do not remove dst_confirm_neigh, but add a new bool parameter in dst_ops.update_pmtu to control whether we should do neighbor confirm. Also split the big patch to small ones for each area. v2: Remove dst_confirm_neigh in __ip6_rt_update_pmtu. Fixes: 0dec879f636f ("net: use dst_confirm_neigh for UDP, RAW, ICMP, L2TP") Reviewed-by: Guillaume Nault Tested-by: Guillaume Nault Acked-by: David Ahern Signed-off-by: Hangbin Liu Signed-off-by: David S. Miller --- net/ipv4/ip_tunnel.c | 2 +- net/ipv6/ip6_tunnel.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/net/ipv4/ip_tunnel.c b/net/ipv4/ip_tunnel.c index 38c02bb62e2cd..0fe2a5d3e258f 100644 --- a/net/ipv4/ip_tunnel.c +++ b/net/ipv4/ip_tunnel.c @@ -505,7 +505,7 @@ static int tnl_update_pmtu(struct net_device *dev, struct sk_buff *skb, mtu = skb_valid_dst(skb) ? dst_mtu(skb_dst(skb)) : dev->mtu; if (skb_valid_dst(skb)) - skb_dst_update_pmtu(skb, mtu); + skb_dst_update_pmtu_no_confirm(skb, mtu); if (skb->protocol == htons(ETH_P_IP)) { if (!skb_is_gso(skb) && diff --git a/net/ipv6/ip6_tunnel.c b/net/ipv6/ip6_tunnel.c index 754a484d35df6..2f376dbc37d52 100644 --- a/net/ipv6/ip6_tunnel.c +++ b/net/ipv6/ip6_tunnel.c @@ -640,7 +640,7 @@ ip4ip6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, if (rel_info > dst_mtu(skb_dst(skb2))) goto out; - skb_dst_update_pmtu(skb2, rel_info); + skb_dst_update_pmtu_no_confirm(skb2, rel_info); } icmp_send(skb2, rel_type, rel_code, htonl(rel_info)); @@ -1132,7 +1132,7 @@ route_lookup: mtu = max(mtu, skb->protocol == htons(ETH_P_IPV6) ? IPV6_MIN_MTU : IPV4_MIN_MTU); - skb_dst_update_pmtu(skb, mtu); + skb_dst_update_pmtu_no_confirm(skb, mtu); if (skb->len - t->tun_hlen - eth_hlen > mtu && !skb_is_gso(skb)) { *pmtu = mtu; err = -EMSGSIZE; -- GitLab From 8247a79efa2f28b44329f363272550c1738377de Mon Sep 17 00:00:00 2001 From: Hangbin Liu Date: Sun, 22 Dec 2019 10:51:14 +0800 Subject: [PATCH 1299/1437] vti: do not confirm neighbor when do pmtu update When do IPv6 tunnel PMTU update and calls __ip6_rt_update_pmtu() in the end, we should not call dst_confirm_neigh() as there is no two-way communication. Although vti and vti6 are immune to this problem because they are IFF_NOARP interfaces, as Guillaume pointed. There is still no sense to confirm neighbour here. v5: Update commit description. v4: No change. v3: Do not remove dst_confirm_neigh, but add a new bool parameter in dst_ops.update_pmtu to control whether we should do neighbor confirm. Also split the big patch to small ones for each area. v2: Remove dst_confirm_neigh in __ip6_rt_update_pmtu. Reviewed-by: Guillaume Nault Acked-by: David Ahern Signed-off-by: Hangbin Liu Signed-off-by: David S. Miller --- net/ipv4/ip_vti.c | 2 +- net/ipv6/ip6_vti.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/net/ipv4/ip_vti.c b/net/ipv4/ip_vti.c index 9b153c7fcbb4d..e90b600c7a25f 100644 --- a/net/ipv4/ip_vti.c +++ b/net/ipv4/ip_vti.c @@ -214,7 +214,7 @@ static netdev_tx_t vti_xmit(struct sk_buff *skb, struct net_device *dev, mtu = dst_mtu(dst); if (skb->len > mtu) { - skb_dst_update_pmtu(skb, mtu); + skb_dst_update_pmtu_no_confirm(skb, mtu); if (skb->protocol == htons(ETH_P_IP)) { icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED, htonl(mtu)); diff --git a/net/ipv6/ip6_vti.c b/net/ipv6/ip6_vti.c index 024db17386d2f..6f08b760c2a7c 100644 --- a/net/ipv6/ip6_vti.c +++ b/net/ipv6/ip6_vti.c @@ -479,7 +479,7 @@ vti6_xmit(struct sk_buff *skb, struct net_device *dev, struct flowi *fl) mtu = dst_mtu(dst); if (skb->len > mtu) { - skb_dst_update_pmtu(skb, mtu); + skb_dst_update_pmtu_no_confirm(skb, mtu); if (skb->protocol == htons(ETH_P_IPV6)) { if (mtu < IPV6_MIN_MTU) -- GitLab From 4d42df46d6372ece4cb4279870b46c2ea7304a47 Mon Sep 17 00:00:00 2001 From: Hangbin Liu Date: Sun, 22 Dec 2019 10:51:15 +0800 Subject: [PATCH 1300/1437] sit: do not confirm neighbor when do pmtu update When do IPv6 tunnel PMTU update and calls __ip6_rt_update_pmtu() in the end, we should not call dst_confirm_neigh() as there is no two-way communication. v5: No change. v4: No change. v3: Do not remove dst_confirm_neigh, but add a new bool parameter in dst_ops.update_pmtu to control whether we should do neighbor confirm. Also split the big patch to small ones for each area. v2: Remove dst_confirm_neigh in __ip6_rt_update_pmtu. Reviewed-by: Guillaume Nault Acked-by: David Ahern Signed-off-by: Hangbin Liu Signed-off-by: David S. Miller --- net/ipv6/sit.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/ipv6/sit.c b/net/ipv6/sit.c index b2ccbc4731277..98954830c40ba 100644 --- a/net/ipv6/sit.c +++ b/net/ipv6/sit.c @@ -944,7 +944,7 @@ static netdev_tx_t ipip6_tunnel_xmit(struct sk_buff *skb, } if (tunnel->parms.iph.daddr) - skb_dst_update_pmtu(skb, mtu); + skb_dst_update_pmtu_no_confirm(skb, mtu); if (skb->len > mtu && !skb_is_gso(skb)) { icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu); -- GitLab From f081042d128a0c7acbd67611def62e1b52e2d294 Mon Sep 17 00:00:00 2001 From: Hangbin Liu Date: Sun, 22 Dec 2019 10:51:16 +0800 Subject: [PATCH 1301/1437] net/dst: do not confirm neighbor for vxlan and geneve pmtu update When do IPv6 tunnel PMTU update and calls __ip6_rt_update_pmtu() in the end, we should not call dst_confirm_neigh() as there is no two-way communication. So disable the neigh confirm for vxlan and geneve pmtu update. v5: No change. v4: No change. v3: Do not remove dst_confirm_neigh, but add a new bool parameter in dst_ops.update_pmtu to control whether we should do neighbor confirm. Also split the big patch to small ones for each area. v2: Remove dst_confirm_neigh in __ip6_rt_update_pmtu. Fixes: a93bf0ff4490 ("vxlan: update skb dst pmtu on tx path") Fixes: 52a589d51f10 ("geneve: update skb dst pmtu on tx path") Reviewed-by: Guillaume Nault Tested-by: Guillaume Nault Acked-by: David Ahern Signed-off-by: Hangbin Liu Signed-off-by: David S. Miller --- include/net/dst.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/net/dst.h b/include/net/dst.h index dc7cc1f1051c6..3448cf865edee 100644 --- a/include/net/dst.h +++ b/include/net/dst.h @@ -535,7 +535,7 @@ static inline void skb_tunnel_check_pmtu(struct sk_buff *skb, u32 encap_mtu = dst_mtu(encap_dst); if (skb->len > encap_mtu - headroom) - skb_dst_update_pmtu(skb, encap_mtu - headroom); + skb_dst_update_pmtu_no_confirm(skb, encap_mtu - headroom); } #endif /* _NET_DST_H */ -- GitLab From 6b722237b656d045c0b9bab9966a5e46604258ba Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Mon, 23 Dec 2019 15:28:12 +0200 Subject: [PATCH 1302/1437] net: fib_notifier: Add temporary events to the FIB notification chain Subsequent patches are going to simplify the IPv6 route offload API, which will only use three events - replace, delete and append. Introduce a temporary version of replace and delete in order to make the conversion easier to review. Note that append does not need a temporary version, as it is currently not used. Signed-off-by: Ido Schimmel Reviewed-by: Jiri Pirko Reviewed-by: David Ahern Signed-off-by: David S. Miller --- include/net/fib_notifier.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/net/fib_notifier.h b/include/net/fib_notifier.h index 6d59221ff05ad..b3c54325caec0 100644 --- a/include/net/fib_notifier.h +++ b/include/net/fib_notifier.h @@ -23,6 +23,8 @@ enum fib_event_type { FIB_EVENT_NH_DEL, FIB_EVENT_VIF_ADD, FIB_EVENT_VIF_DEL, + FIB_EVENT_ENTRY_REPLACE_TMP, + FIB_EVENT_ENTRY_DEL_TMP, }; struct fib_notifier_ops { -- GitLab From c10c4279c778df03f404a4d6906d7d4b840eb95f Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Mon, 23 Dec 2019 15:28:13 +0200 Subject: [PATCH 1303/1437] ipv6: Notify newly added route if should be offloaded fib6_add_rt2node() takes care of adding a single route ('struct fib6_info') to a FIB node. The route in question should only be notified in case it is added as the first route in the node (lowest metric) or if it is added as a sibling route to the first route in the node. The first criterion can be tested by checking if the route is pointed to by 'fn->leaf'. The second criterion can be tested by checking the new 'notify_sibling_rt' variable that is set when the route is added as a sibling to the first route in the node. Signed-off-by: Ido Schimmel Reviewed-by: Jiri Pirko Reviewed-by: David Ahern Signed-off-by: David S. Miller --- net/ipv6/ip6_fib.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/net/ipv6/ip6_fib.c b/net/ipv6/ip6_fib.c index 7bae6a91b4872..045bcaf5e7709 100644 --- a/net/ipv6/ip6_fib.c +++ b/net/ipv6/ip6_fib.c @@ -1039,6 +1039,7 @@ static int fib6_add_rt2node(struct fib6_node *fn, struct fib6_info *rt, (info->nlh->nlmsg_flags & NLM_F_CREATE)); int found = 0; bool rt_can_ecmp = rt6_qualify_for_ecmp(rt); + bool notify_sibling_rt = false; u16 nlflags = NLM_F_EXCL; int err; @@ -1130,6 +1131,7 @@ next_iter: /* Find the first route that have the same metric */ sibling = leaf; + notify_sibling_rt = true; while (sibling) { if (sibling->fib6_metric == rt->fib6_metric && rt6_qualify_for_ecmp(sibling)) { @@ -1139,6 +1141,7 @@ next_iter: } sibling = rcu_dereference_protected(sibling->fib6_next, lockdep_is_held(&rt->fib6_table->tb6_lock)); + notify_sibling_rt = false; } /* For each sibling in the list, increment the counter of * siblings. BUG() if counters does not match, list of siblings @@ -1166,6 +1169,21 @@ add: nlflags |= NLM_F_CREATE; if (!info->skip_notify_kernel) { + enum fib_event_type fib_event; + + if (notify_sibling_rt) + fib_event = FIB_EVENT_ENTRY_APPEND; + else + fib_event = FIB_EVENT_ENTRY_REPLACE_TMP; + /* The route should only be notified if it is the first + * route in the node or if it is added as a sibling + * route to the first route in the node. + */ + if (notify_sibling_rt || ins == &fn->leaf) + err = call_fib6_entry_notifiers(info->nl_net, + fib_event, rt, + extack); + err = call_fib6_entry_notifiers(info->nl_net, FIB_EVENT_ENTRY_ADD, rt, extack); -- GitLab From 51bf7f387fdfe5ec8c33734b3124ccec83c8d0c3 Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Mon, 23 Dec 2019 15:28:14 +0200 Subject: [PATCH 1304/1437] ipv6: Notify route if replacing currently offloaded one Similar to the corresponding IPv4 patch, only notify the new route if it is replacing the currently offloaded one. Meaning, the one pointed to by 'fn->leaf'. Signed-off-by: Ido Schimmel Reviewed-by: Jiri Pirko Reviewed-by: David Ahern Signed-off-by: David S. Miller --- net/ipv6/ip6_fib.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/net/ipv6/ip6_fib.c b/net/ipv6/ip6_fib.c index 045bcaf5e7709..7cf9554888b04 100644 --- a/net/ipv6/ip6_fib.c +++ b/net/ipv6/ip6_fib.c @@ -1231,6 +1231,13 @@ add: } if (!info->skip_notify_kernel) { + enum fib_event_type fib_event; + + fib_event = FIB_EVENT_ENTRY_REPLACE_TMP; + if (ins == &fn->leaf) + err = call_fib6_entry_notifiers(info->nl_net, + fib_event, rt, + extack); err = call_fib6_entry_notifiers(info->nl_net, FIB_EVENT_ENTRY_REPLACE, rt, extack); -- GitLab From 0ee0f47c26b2f909ec95d7d8fed8119e288c4dd3 Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Mon, 23 Dec 2019 15:28:15 +0200 Subject: [PATCH 1305/1437] ipv6: Notify multipath route if should be offloaded In a similar fashion to previous patches, only notify the new multipath route if it is the first route in the node or if it was appended to such route. The type of the notification (replace vs. append) is determined based on the number of routes added ('nhn') and the number of sibling routes. If the two do not match, then an append notification should be sent. Signed-off-by: Ido Schimmel Reviewed-by: Jiri Pirko Reviewed-by: David Ahern Signed-off-by: David S. Miller --- net/ipv6/route.c | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/net/ipv6/route.c b/net/ipv6/route.c index b59940416cb57..c0809f52f9eff 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -5017,6 +5017,32 @@ static void ip6_route_mpath_notify(struct fib6_info *rt, inet6_rt_notify(RTM_NEWROUTE, rt, info, nlflags); } +static bool ip6_route_mpath_should_notify(const struct fib6_info *rt) +{ + bool rt_can_ecmp = rt6_qualify_for_ecmp(rt); + bool should_notify = false; + struct fib6_info *leaf; + struct fib6_node *fn; + + rcu_read_lock(); + fn = rcu_dereference(rt->fib6_node); + if (!fn) + goto out; + + leaf = rcu_dereference(fn->leaf); + if (!leaf) + goto out; + + if (rt == leaf || + (rt_can_ecmp && rt->fib6_metric == leaf->fib6_metric && + rt6_qualify_for_ecmp(leaf))) + should_notify = true; +out: + rcu_read_unlock(); + + return should_notify; +} + static int ip6_route_multipath_add(struct fib6_config *cfg, struct netlink_ext_ack *extack) { @@ -5147,6 +5173,28 @@ static int ip6_route_multipath_add(struct fib6_config *cfg, nhn++; } + /* An in-kernel notification should only be sent in case the new + * multipath route is added as the first route in the node, or if + * it was appended to it. We pass 'rt_notif' since it is the first + * sibling and might allow us to skip some checks in the replace case. + */ + if (ip6_route_mpath_should_notify(rt_notif)) { + enum fib_event_type fib_event; + + if (rt_notif->fib6_nsiblings != nhn - 1) + fib_event = FIB_EVENT_ENTRY_APPEND; + else + fib_event = FIB_EVENT_ENTRY_REPLACE_TMP; + + err = call_fib6_multipath_entry_notifiers(info->nl_net, + fib_event, rt_notif, + nhn - 1, extack); + if (err) { + /* Delete all the siblings that were just added */ + err_nh = NULL; + goto add_errout; + } + } event_type = replace ? FIB_EVENT_ENTRY_REPLACE : FIB_EVENT_ENTRY_ADD; err = call_fib6_multipath_entry_notifiers(info->nl_net, event_type, rt_notif, nhn - 1, extack); -- GitLab From 9c6ecd3cf62d0eb57539f966d7ad617ad3a59f4b Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Mon, 23 Dec 2019 15:28:16 +0200 Subject: [PATCH 1306/1437] ipv6: Only Replay routes of interest to new listeners When a new listener is registered to the FIB notification chain it receives a dump of all the available routes in the system. Instead, make sure to only replay the IPv6 routes that are actually used in the data path and are of any interest to the new listener. This is done by iterating over all the routing tables in the given namespace, but from each traversed node only the first route ('leaf') is notified. Multipath routes are notified in a single notification instead of one for each nexthop. Add fib6_rt_dump_tmp() to do that. Later on in the patch set it will be renamed to fib6_rt_dump() instead of the existing one. Signed-off-by: Ido Schimmel Reviewed-by: Jiri Pirko Reviewed-by: David Ahern Signed-off-by: David S. Miller --- net/ipv6/ip6_fib.c | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/net/ipv6/ip6_fib.c b/net/ipv6/ip6_fib.c index 7cf9554888b04..51cf848e38f0c 100644 --- a/net/ipv6/ip6_fib.c +++ b/net/ipv6/ip6_fib.c @@ -370,6 +370,21 @@ static int call_fib6_entry_notifier(struct notifier_block *nb, return call_fib6_notifier(nb, event_type, &info.info); } +static int call_fib6_multipath_entry_notifier(struct notifier_block *nb, + enum fib_event_type event_type, + struct fib6_info *rt, + unsigned int nsiblings, + struct netlink_ext_ack *extack) +{ + struct fib6_entry_notifier_info info = { + .info.extack = extack, + .rt = rt, + .nsiblings = nsiblings, + }; + + return call_fib6_notifier(nb, event_type, &info.info); +} + int call_fib6_entry_notifiers(struct net *net, enum fib_event_type event_type, struct fib6_info *rt, @@ -414,16 +429,41 @@ static int fib6_rt_dump(struct fib6_info *rt, struct fib6_dump_arg *arg) rt, arg->extack); } +static int fib6_rt_dump_tmp(struct fib6_info *rt, struct fib6_dump_arg *arg) +{ + enum fib_event_type fib_event = FIB_EVENT_ENTRY_REPLACE_TMP; + int err; + + if (!rt || rt == arg->net->ipv6.fib6_null_entry) + return 0; + + if (rt->fib6_nsiblings) + err = call_fib6_multipath_entry_notifier(arg->nb, fib_event, + rt, + rt->fib6_nsiblings, + arg->extack); + else + err = call_fib6_entry_notifier(arg->nb, fib_event, rt, + arg->extack); + + return err; +} + static int fib6_node_dump(struct fib6_walker *w) { struct fib6_info *rt; int err = 0; + err = fib6_rt_dump_tmp(w->leaf, w->args); + if (err) + goto out; + for_each_fib6_walker_rt(w) { err = fib6_rt_dump(rt, w->args); if (err) break; } +out: w->leaf = NULL; return err; } -- GitLab From d2f0c9b11410f9c6a07c126f8a215b0b81cdcf6c Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Mon, 23 Dec 2019 15:28:17 +0200 Subject: [PATCH 1307/1437] ipv6: Handle route deletion notification For the purpose of route offload, when a single route is deleted, it is only of interest if it is the first route in the node or if it is sibling to such a route. In the first case, distinguish between several possibilities: 1. Route is the last route in the node. Emit a delete notification 2. Route is followed by a non-multipath route. Emit a replace notification for the non-multipath route. 3. Route is followed by a multipath route. Emit a replace notification for the multipath route. In the second case, only emit a delete notification to ensure the route is no longer used as a valid nexthop. Signed-off-by: Ido Schimmel Reviewed-by: Jiri Pirko Reviewed-by: David Ahern Signed-off-by: David S. Miller --- include/net/ip6_fib.h | 1 + net/ipv6/ip6_fib.c | 44 ++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/include/net/ip6_fib.h b/include/net/ip6_fib.h index f1535f1729353..b579faea41e9c 100644 --- a/include/net/ip6_fib.h +++ b/include/net/ip6_fib.h @@ -487,6 +487,7 @@ int call_fib6_multipath_entry_notifiers(struct net *net, struct fib6_info *rt, unsigned int nsiblings, struct netlink_ext_ack *extack); +int call_fib6_entry_notifiers_replace(struct net *net, struct fib6_info *rt); void fib6_rt_update(struct net *net, struct fib6_info *rt, struct nl_info *info); void inet6_rt_notify(int event, struct fib6_info *rt, struct nl_info *info, diff --git a/net/ipv6/ip6_fib.c b/net/ipv6/ip6_fib.c index 51cf848e38f0c..67ddee539f77a 100644 --- a/net/ipv6/ip6_fib.c +++ b/net/ipv6/ip6_fib.c @@ -415,6 +415,18 @@ int call_fib6_multipath_entry_notifiers(struct net *net, return call_fib6_notifiers(net, event_type, &info.info); } +int call_fib6_entry_notifiers_replace(struct net *net, struct fib6_info *rt) +{ + struct fib6_entry_notifier_info info = { + .rt = rt, + .nsiblings = rt->fib6_nsiblings, + }; + + rt->fib6_table->fib_seq++; + return call_fib6_notifiers(net, FIB_EVENT_ENTRY_REPLACE_TMP, + &info.info); +} + struct fib6_dump_arg { struct net *net; struct notifier_block *nb; @@ -1910,13 +1922,29 @@ static struct fib6_node *fib6_repair_tree(struct net *net, static void fib6_del_route(struct fib6_table *table, struct fib6_node *fn, struct fib6_info __rcu **rtp, struct nl_info *info) { + struct fib6_info *leaf, *replace_rt = NULL; struct fib6_walker *w; struct fib6_info *rt = rcu_dereference_protected(*rtp, lockdep_is_held(&table->tb6_lock)); struct net *net = info->nl_net; + bool notify_del = false; RT6_TRACE("fib6_del_route\n"); + /* If the deleted route is the first in the node and it is not part of + * a multipath route, then we need to replace it with the next route + * in the node, if exists. + */ + leaf = rcu_dereference_protected(fn->leaf, + lockdep_is_held(&table->tb6_lock)); + if (leaf == rt && !rt->fib6_nsiblings) { + if (rcu_access_pointer(rt->fib6_next)) + replace_rt = rcu_dereference_protected(rt->fib6_next, + lockdep_is_held(&table->tb6_lock)); + else + notify_del = true; + } + /* Unlink it */ *rtp = rt->fib6_next; rt->fib6_node = NULL; @@ -1934,6 +1962,14 @@ static void fib6_del_route(struct fib6_table *table, struct fib6_node *fn, if (rt->fib6_nsiblings) { struct fib6_info *sibling, *next_sibling; + /* The route is deleted from a multipath route. If this + * multipath route is the first route in the node, then we need + * to emit a delete notification. Otherwise, we need to skip + * the notification. + */ + if (rt->fib6_metric == leaf->fib6_metric && + rt6_qualify_for_ecmp(leaf)) + notify_del = true; list_for_each_entry_safe(sibling, next_sibling, &rt->fib6_siblings, fib6_siblings) sibling->fib6_nsiblings--; @@ -1969,8 +2005,14 @@ static void fib6_del_route(struct fib6_table *table, struct fib6_node *fn, fib6_purge_rt(rt, fn, net); - if (!info->skip_notify_kernel) + if (!info->skip_notify_kernel) { + if (notify_del) + call_fib6_entry_notifiers(net, FIB_EVENT_ENTRY_DEL_TMP, + rt, NULL); + else if (replace_rt) + call_fib6_entry_notifiers_replace(net, replace_rt); call_fib6_entry_notifiers(net, FIB_EVENT_ENTRY_DEL, rt, NULL); + } if (!info->skip_notify) inet6_rt_notify(RTM_DELROUTE, rt, info, 0); -- GitLab From 0284696b97b2fad1b220871559dff410cc3187e0 Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Mon, 23 Dec 2019 15:28:18 +0200 Subject: [PATCH 1308/1437] ipv6: Handle multipath route deletion notification When an entire multipath route is deleted, only emit a notification if it is the first route in the node. Emit a replace notification in case the last sibling is followed by another route. Otherwise, emit a delete notification. Signed-off-by: Ido Schimmel Reviewed-by: Jiri Pirko Reviewed-by: David Ahern Signed-off-by: David S. Miller --- net/ipv6/route.c | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/net/ipv6/route.c b/net/ipv6/route.c index c0809f52f9eff..646716a47cc9a 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -3749,6 +3749,7 @@ static int __ip6_del_rt_siblings(struct fib6_info *rt, struct fib6_config *cfg) if (rt->fib6_nsiblings && cfg->fc_delete_all_nh) { struct fib6_info *sibling, *next_sibling; + struct fib6_node *fn; /* prefer to send a single notification with all hops */ skb = nlmsg_new(rt6_nlmsg_size(rt), gfp_any()); @@ -3764,7 +3765,32 @@ static int __ip6_del_rt_siblings(struct fib6_info *rt, struct fib6_config *cfg) info->skip_notify = 1; } + /* 'rt' points to the first sibling route. If it is not the + * leaf, then we do not need to send a notification. Otherwise, + * we need to check if the last sibling has a next route or not + * and emit a replace or delete notification, respectively. + */ info->skip_notify_kernel = 1; + fn = rcu_dereference_protected(rt->fib6_node, + lockdep_is_held(&table->tb6_lock)); + if (rcu_access_pointer(fn->leaf) == rt) { + struct fib6_info *last_sibling, *replace_rt; + + last_sibling = list_last_entry(&rt->fib6_siblings, + struct fib6_info, + fib6_siblings); + replace_rt = rcu_dereference_protected( + last_sibling->fib6_next, + lockdep_is_held(&table->tb6_lock)); + if (replace_rt) + call_fib6_entry_notifiers_replace(net, + replace_rt); + else + call_fib6_multipath_entry_notifiers(net, + FIB_EVENT_ENTRY_DEL_TMP, + rt, rt->fib6_nsiblings, + NULL); + } call_fib6_multipath_entry_notifiers(net, FIB_EVENT_ENTRY_DEL, rt, -- GitLab From dacad7b34b59fb06a242ef2cf64205f04f7c5339 Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Mon, 23 Dec 2019 15:28:19 +0200 Subject: [PATCH 1309/1437] mlxsw: spectrum_router: Start using new IPv6 route notifications With the new notifications mlxsw does not need to handle identical routes itself, as this is taken care of by the core IPv6 code. Instead, mlxsw only needs to take care of inserting and removing routes from the device. Convert mlxsw to use the new IPv6 route notifications and simplify the code. Signed-off-by: Ido Schimmel Reviewed-by: Jiri Pirko Signed-off-by: David S. Miller --- .../ethernet/mellanox/mlxsw/spectrum_router.c | 225 ++++++------------ 1 file changed, 76 insertions(+), 149 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c index 0678815efbf3e..295cdcb1c4c01 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c @@ -4989,13 +4989,6 @@ static void mlxsw_sp_rt6_destroy(struct mlxsw_sp_rt6 *mlxsw_sp_rt6) kfree(mlxsw_sp_rt6); } -static bool mlxsw_sp_fib6_rt_can_mp(const struct fib6_info *rt) -{ - /* RTF_CACHE routes are ignored */ - return !(rt->fib6_flags & RTF_ADDRCONF) && - rt->fib6_nh->fib_nh_gw_family; -} - static struct fib6_info * mlxsw_sp_fib6_entry_rt(const struct mlxsw_sp_fib6_entry *fib6_entry) { @@ -5003,37 +4996,6 @@ mlxsw_sp_fib6_entry_rt(const struct mlxsw_sp_fib6_entry *fib6_entry) list)->rt; } -static struct mlxsw_sp_fib6_entry * -mlxsw_sp_fib6_node_mp_entry_find(const struct mlxsw_sp_fib_node *fib_node, - const struct fib6_info *nrt, bool replace) -{ - struct mlxsw_sp_fib6_entry *fib6_entry; - - if (!mlxsw_sp_fib6_rt_can_mp(nrt) || replace) - return NULL; - - list_for_each_entry(fib6_entry, &fib_node->entry_list, common.list) { - struct fib6_info *rt = mlxsw_sp_fib6_entry_rt(fib6_entry); - - /* RT6_TABLE_LOCAL and RT6_TABLE_MAIN share the same - * virtual router. - */ - if (rt->fib6_table->tb6_id > nrt->fib6_table->tb6_id) - continue; - if (rt->fib6_table->tb6_id != nrt->fib6_table->tb6_id) - break; - if (rt->fib6_metric < nrt->fib6_metric) - continue; - if (rt->fib6_metric == nrt->fib6_metric && - mlxsw_sp_fib6_rt_can_mp(rt)) - return fib6_entry; - if (rt->fib6_metric > nrt->fib6_metric) - break; - } - - return NULL; -} - static struct mlxsw_sp_rt6 * mlxsw_sp_fib6_entry_rt_find(const struct mlxsw_sp_fib6_entry *fib6_entry, const struct fib6_info *rt) @@ -5424,86 +5386,13 @@ static void mlxsw_sp_fib6_entry_destroy(struct mlxsw_sp *mlxsw_sp, kfree(fib6_entry); } -static struct mlxsw_sp_fib6_entry * -mlxsw_sp_fib6_node_entry_find(const struct mlxsw_sp_fib_node *fib_node, - const struct fib6_info *nrt, bool replace) -{ - struct mlxsw_sp_fib6_entry *fib6_entry, *fallback = NULL; - - list_for_each_entry(fib6_entry, &fib_node->entry_list, common.list) { - struct fib6_info *rt = mlxsw_sp_fib6_entry_rt(fib6_entry); - - if (rt->fib6_table->tb6_id > nrt->fib6_table->tb6_id) - continue; - if (rt->fib6_table->tb6_id != nrt->fib6_table->tb6_id) - break; - if (replace && rt->fib6_metric == nrt->fib6_metric) { - if (mlxsw_sp_fib6_rt_can_mp(rt) == - mlxsw_sp_fib6_rt_can_mp(nrt)) - return fib6_entry; - if (mlxsw_sp_fib6_rt_can_mp(nrt)) - fallback = fallback ?: fib6_entry; - } - if (rt->fib6_metric > nrt->fib6_metric) - return fallback ?: fib6_entry; - } - - return fallback; -} - -static int -mlxsw_sp_fib6_node_list_insert(struct mlxsw_sp_fib6_entry *new6_entry, - bool *p_replace) -{ - struct mlxsw_sp_fib_node *fib_node = new6_entry->common.fib_node; - struct fib6_info *nrt = mlxsw_sp_fib6_entry_rt(new6_entry); - struct mlxsw_sp_fib6_entry *fib6_entry; - - fib6_entry = mlxsw_sp_fib6_node_entry_find(fib_node, nrt, *p_replace); - - if (*p_replace && !fib6_entry) - *p_replace = false; - - if (fib6_entry) { - list_add_tail(&new6_entry->common.list, - &fib6_entry->common.list); - } else { - struct mlxsw_sp_fib6_entry *last; - - list_for_each_entry(last, &fib_node->entry_list, common.list) { - struct fib6_info *rt = mlxsw_sp_fib6_entry_rt(last); - - if (nrt->fib6_table->tb6_id > rt->fib6_table->tb6_id) - break; - fib6_entry = last; - } - - if (fib6_entry) - list_add(&new6_entry->common.list, - &fib6_entry->common.list); - else - list_add(&new6_entry->common.list, - &fib_node->entry_list); - } - - return 0; -} - -static void -mlxsw_sp_fib6_node_list_remove(struct mlxsw_sp_fib6_entry *fib6_entry) -{ - list_del(&fib6_entry->common.list); -} - static int mlxsw_sp_fib6_node_entry_link(struct mlxsw_sp *mlxsw_sp, - struct mlxsw_sp_fib6_entry *fib6_entry, - bool *p_replace) + struct mlxsw_sp_fib6_entry *fib6_entry) { + struct mlxsw_sp_fib_node *fib_node = fib6_entry->common.fib_node; int err; - err = mlxsw_sp_fib6_node_list_insert(fib6_entry, p_replace); - if (err) - return err; + list_add(&fib6_entry->common.list, &fib_node->entry_list); err = mlxsw_sp_fib_node_entry_add(mlxsw_sp, &fib6_entry->common); if (err) @@ -5512,7 +5401,7 @@ static int mlxsw_sp_fib6_node_entry_link(struct mlxsw_sp *mlxsw_sp, return 0; err_fib_node_entry_add: - mlxsw_sp_fib6_node_list_remove(fib6_entry); + list_del(&fib6_entry->common.list); return err; } @@ -5521,7 +5410,7 @@ mlxsw_sp_fib6_node_entry_unlink(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_fib6_entry *fib6_entry) { mlxsw_sp_fib_node_entry_del(mlxsw_sp, &fib6_entry->common); - mlxsw_sp_fib6_node_list_remove(fib6_entry); + list_del(&fib6_entry->common.list); } static struct mlxsw_sp_fib6_entry * @@ -5557,15 +5446,15 @@ mlxsw_sp_fib6_entry_lookup(struct mlxsw_sp *mlxsw_sp, } static void mlxsw_sp_fib6_entry_replace(struct mlxsw_sp *mlxsw_sp, - struct mlxsw_sp_fib6_entry *fib6_entry, - bool replace) + struct mlxsw_sp_fib6_entry *fib6_entry) { struct mlxsw_sp_fib_node *fib_node = fib6_entry->common.fib_node; struct mlxsw_sp_fib6_entry *replaced; - if (!replace) + if (list_is_singular(&fib_node->entry_list)) return; + /* We inserted the new entry before replaced one */ replaced = list_next_entry(fib6_entry, common.list); mlxsw_sp_fib6_node_entry_unlink(mlxsw_sp, replaced); @@ -5573,9 +5462,9 @@ static void mlxsw_sp_fib6_entry_replace(struct mlxsw_sp *mlxsw_sp, mlxsw_sp_fib_node_put(mlxsw_sp, fib_node); } -static int mlxsw_sp_router_fib6_add(struct mlxsw_sp *mlxsw_sp, - struct fib6_info **rt_arr, - unsigned int nrt6, bool replace) +static int mlxsw_sp_router_fib6_replace(struct mlxsw_sp *mlxsw_sp, + struct fib6_info **rt_arr, + unsigned int nrt6) { struct mlxsw_sp_fib6_entry *fib6_entry; struct mlxsw_sp_fib_node *fib_node; @@ -5599,18 +5488,6 @@ static int mlxsw_sp_router_fib6_add(struct mlxsw_sp *mlxsw_sp, if (IS_ERR(fib_node)) return PTR_ERR(fib_node); - /* Before creating a new entry, try to append route to an existing - * multipath entry. - */ - fib6_entry = mlxsw_sp_fib6_node_mp_entry_find(fib_node, rt, replace); - if (fib6_entry) { - err = mlxsw_sp_fib6_entry_nexthop_add(mlxsw_sp, fib6_entry, - rt_arr, nrt6); - if (err) - goto err_fib6_entry_nexthop_add; - return 0; - } - fib6_entry = mlxsw_sp_fib6_entry_create(mlxsw_sp, fib_node, rt_arr, nrt6); if (IS_ERR(fib6_entry)) { @@ -5618,17 +5495,61 @@ static int mlxsw_sp_router_fib6_add(struct mlxsw_sp *mlxsw_sp, goto err_fib6_entry_create; } - err = mlxsw_sp_fib6_node_entry_link(mlxsw_sp, fib6_entry, &replace); + err = mlxsw_sp_fib6_node_entry_link(mlxsw_sp, fib6_entry); if (err) goto err_fib6_node_entry_link; - mlxsw_sp_fib6_entry_replace(mlxsw_sp, fib6_entry, replace); + mlxsw_sp_fib6_entry_replace(mlxsw_sp, fib6_entry); return 0; err_fib6_node_entry_link: mlxsw_sp_fib6_entry_destroy(mlxsw_sp, fib6_entry); err_fib6_entry_create: + mlxsw_sp_fib_node_put(mlxsw_sp, fib_node); + return err; +} + +static int mlxsw_sp_router_fib6_append(struct mlxsw_sp *mlxsw_sp, + struct fib6_info **rt_arr, + unsigned int nrt6) +{ + struct mlxsw_sp_fib6_entry *fib6_entry; + struct mlxsw_sp_fib_node *fib_node; + struct fib6_info *rt = rt_arr[0]; + int err; + + if (mlxsw_sp->router->aborted) + return 0; + + if (rt->fib6_src.plen) + return -EINVAL; + + if (mlxsw_sp_fib6_rt_should_ignore(rt)) + return 0; + + fib_node = mlxsw_sp_fib_node_get(mlxsw_sp, rt->fib6_table->tb6_id, + &rt->fib6_dst.addr, + sizeof(rt->fib6_dst.addr), + rt->fib6_dst.plen, + MLXSW_SP_L3_PROTO_IPV6); + if (IS_ERR(fib_node)) + return PTR_ERR(fib_node); + + if (WARN_ON_ONCE(list_empty(&fib_node->entry_list))) { + mlxsw_sp_fib_node_put(mlxsw_sp, fib_node); + return -EINVAL; + } + + fib6_entry = list_first_entry(&fib_node->entry_list, + struct mlxsw_sp_fib6_entry, common.list); + err = mlxsw_sp_fib6_entry_nexthop_add(mlxsw_sp, fib6_entry, rt_arr, + nrt6); + if (err) + goto err_fib6_entry_nexthop_add; + + return 0; + err_fib6_entry_nexthop_add: mlxsw_sp_fib_node_put(mlxsw_sp, fib_node); return err; @@ -6039,25 +5960,29 @@ static void mlxsw_sp_router_fib6_event_work(struct work_struct *work) struct mlxsw_sp_fib_event_work *fib_work = container_of(work, struct mlxsw_sp_fib_event_work, work); struct mlxsw_sp *mlxsw_sp = fib_work->mlxsw_sp; - bool replace; int err; rtnl_lock(); mlxsw_sp_span_respin(mlxsw_sp); switch (fib_work->event) { - case FIB_EVENT_ENTRY_REPLACE: /* fall through */ - case FIB_EVENT_ENTRY_ADD: - replace = fib_work->event == FIB_EVENT_ENTRY_REPLACE; - err = mlxsw_sp_router_fib6_add(mlxsw_sp, - fib_work->fib6_work.rt_arr, - fib_work->fib6_work.nrt6, - replace); + case FIB_EVENT_ENTRY_REPLACE_TMP: + err = mlxsw_sp_router_fib6_replace(mlxsw_sp, + fib_work->fib6_work.rt_arr, + fib_work->fib6_work.nrt6); if (err) mlxsw_sp_router_fib_abort(mlxsw_sp); mlxsw_sp_router_fib6_work_fini(&fib_work->fib6_work); break; - case FIB_EVENT_ENTRY_DEL: + case FIB_EVENT_ENTRY_APPEND: + err = mlxsw_sp_router_fib6_append(mlxsw_sp, + fib_work->fib6_work.rt_arr, + fib_work->fib6_work.nrt6); + if (err) + mlxsw_sp_router_fib_abort(mlxsw_sp); + mlxsw_sp_router_fib6_work_fini(&fib_work->fib6_work); + break; + case FIB_EVENT_ENTRY_DEL_TMP: mlxsw_sp_router_fib6_del(mlxsw_sp, fib_work->fib6_work.rt_arr, fib_work->fib6_work.nrt6); @@ -6143,9 +6068,9 @@ static int mlxsw_sp_router_fib6_event(struct mlxsw_sp_fib_event_work *fib_work, int err; switch (fib_work->event) { - case FIB_EVENT_ENTRY_REPLACE: /* fall through */ - case FIB_EVENT_ENTRY_ADD: /* fall through */ - case FIB_EVENT_ENTRY_DEL: + case FIB_EVENT_ENTRY_REPLACE_TMP: /* fall through */ + case FIB_EVENT_ENTRY_APPEND: /* fall through */ + case FIB_EVENT_ENTRY_DEL_TMP: fen6_info = container_of(info, struct fib6_entry_notifier_info, info); err = mlxsw_sp_router_fib6_work_init(&fib_work->fib6_work, @@ -6248,7 +6173,9 @@ static int mlxsw_sp_router_fib_event(struct notifier_block *nb, router->mlxsw_sp); return notifier_from_errno(err); case FIB_EVENT_ENTRY_ADD: /* fall through */ - case FIB_EVENT_ENTRY_REPLACE: + case FIB_EVENT_ENTRY_REPLACE: /* fall through */ + case FIB_EVENT_ENTRY_REPLACE_TMP: /* fall through */ + case FIB_EVENT_ENTRY_APPEND: if (router->aborted) { NL_SET_ERR_MSG_MOD(info->extack, "FIB offload was aborted. Not configuring route"); return notifier_from_errno(-EINVAL); -- GitLab From caafb2509fac1432849650826953dd88b7cbe374 Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Mon, 23 Dec 2019 15:28:20 +0200 Subject: [PATCH 1310/1437] ipv6: Remove old route notifications and convert listeners Now that mlxsw is converted to use the new FIB notifications it is possible to delete the old ones and use the new replace / append / delete notifications. Signed-off-by: Ido Schimmel Reviewed-by: Jiri Pirko Reviewed-by: David Ahern Signed-off-by: David S. Miller --- .../ethernet/mellanox/mlxsw/spectrum_router.c | 9 ++- drivers/net/netdevsim/fib.c | 1 - include/net/fib_notifier.h | 2 - net/ipv6/ip6_fib.c | 61 +++++-------------- net/ipv6/route.c | 18 +----- 5 files changed, 21 insertions(+), 70 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c index 295cdcb1c4c01..f62e8d67348cf 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c @@ -5966,7 +5966,7 @@ static void mlxsw_sp_router_fib6_event_work(struct work_struct *work) mlxsw_sp_span_respin(mlxsw_sp); switch (fib_work->event) { - case FIB_EVENT_ENTRY_REPLACE_TMP: + case FIB_EVENT_ENTRY_REPLACE: err = mlxsw_sp_router_fib6_replace(mlxsw_sp, fib_work->fib6_work.rt_arr, fib_work->fib6_work.nrt6); @@ -5982,7 +5982,7 @@ static void mlxsw_sp_router_fib6_event_work(struct work_struct *work) mlxsw_sp_router_fib_abort(mlxsw_sp); mlxsw_sp_router_fib6_work_fini(&fib_work->fib6_work); break; - case FIB_EVENT_ENTRY_DEL_TMP: + case FIB_EVENT_ENTRY_DEL: mlxsw_sp_router_fib6_del(mlxsw_sp, fib_work->fib6_work.rt_arr, fib_work->fib6_work.nrt6); @@ -6068,9 +6068,9 @@ static int mlxsw_sp_router_fib6_event(struct mlxsw_sp_fib_event_work *fib_work, int err; switch (fib_work->event) { - case FIB_EVENT_ENTRY_REPLACE_TMP: /* fall through */ + case FIB_EVENT_ENTRY_REPLACE: /* fall through */ case FIB_EVENT_ENTRY_APPEND: /* fall through */ - case FIB_EVENT_ENTRY_DEL_TMP: + case FIB_EVENT_ENTRY_DEL: fen6_info = container_of(info, struct fib6_entry_notifier_info, info); err = mlxsw_sp_router_fib6_work_init(&fib_work->fib6_work, @@ -6174,7 +6174,6 @@ static int mlxsw_sp_router_fib_event(struct notifier_block *nb, return notifier_from_errno(err); case FIB_EVENT_ENTRY_ADD: /* fall through */ case FIB_EVENT_ENTRY_REPLACE: /* fall through */ - case FIB_EVENT_ENTRY_REPLACE_TMP: /* fall through */ case FIB_EVENT_ENTRY_APPEND: if (router->aborted) { NL_SET_ERR_MSG_MOD(info->extack, "FIB offload was aborted. Not configuring route"); diff --git a/drivers/net/netdevsim/fib.c b/drivers/net/netdevsim/fib.c index 4e02a4231fcb9..b5df308b4e332 100644 --- a/drivers/net/netdevsim/fib.c +++ b/drivers/net/netdevsim/fib.c @@ -178,7 +178,6 @@ static int nsim_fib_event_nb(struct notifier_block *nb, unsigned long event, break; case FIB_EVENT_ENTRY_REPLACE: /* fall through */ - case FIB_EVENT_ENTRY_ADD: /* fall through */ case FIB_EVENT_ENTRY_DEL: err = nsim_fib_event(data, info, event != FIB_EVENT_ENTRY_DEL); break; diff --git a/include/net/fib_notifier.h b/include/net/fib_notifier.h index b3c54325caec0..6d59221ff05ad 100644 --- a/include/net/fib_notifier.h +++ b/include/net/fib_notifier.h @@ -23,8 +23,6 @@ enum fib_event_type { FIB_EVENT_NH_DEL, FIB_EVENT_VIF_ADD, FIB_EVENT_VIF_DEL, - FIB_EVENT_ENTRY_REPLACE_TMP, - FIB_EVENT_ENTRY_DEL_TMP, }; struct fib_notifier_ops { diff --git a/net/ipv6/ip6_fib.c b/net/ipv6/ip6_fib.c index 67ddee539f77a..b1e9a10e11335 100644 --- a/net/ipv6/ip6_fib.c +++ b/net/ipv6/ip6_fib.c @@ -423,8 +423,7 @@ int call_fib6_entry_notifiers_replace(struct net *net, struct fib6_info *rt) }; rt->fib6_table->fib_seq++; - return call_fib6_notifiers(net, FIB_EVENT_ENTRY_REPLACE_TMP, - &info.info); + return call_fib6_notifiers(net, FIB_EVENT_ENTRY_REPLACE, &info.info); } struct fib6_dump_arg { @@ -435,15 +434,7 @@ struct fib6_dump_arg { static int fib6_rt_dump(struct fib6_info *rt, struct fib6_dump_arg *arg) { - if (rt == arg->net->ipv6.fib6_null_entry) - return 0; - return call_fib6_entry_notifier(arg->nb, FIB_EVENT_ENTRY_ADD, - rt, arg->extack); -} - -static int fib6_rt_dump_tmp(struct fib6_info *rt, struct fib6_dump_arg *arg) -{ - enum fib_event_type fib_event = FIB_EVENT_ENTRY_REPLACE_TMP; + enum fib_event_type fib_event = FIB_EVENT_ENTRY_REPLACE; int err; if (!rt || rt == arg->net->ipv6.fib6_null_entry) @@ -463,19 +454,9 @@ static int fib6_rt_dump_tmp(struct fib6_info *rt, struct fib6_dump_arg *arg) static int fib6_node_dump(struct fib6_walker *w) { - struct fib6_info *rt; - int err = 0; - - err = fib6_rt_dump_tmp(w->leaf, w->args); - if (err) - goto out; + int err; - for_each_fib6_walker_rt(w) { - err = fib6_rt_dump(rt, w->args); - if (err) - break; - } -out: + err = fib6_rt_dump(w->leaf, w->args); w->leaf = NULL; return err; } @@ -1220,25 +1201,21 @@ next_iter: add: nlflags |= NLM_F_CREATE; - if (!info->skip_notify_kernel) { + /* The route should only be notified if it is the first + * route in the node or if it is added as a sibling + * route to the first route in the node. + */ + if (!info->skip_notify_kernel && + (notify_sibling_rt || ins == &fn->leaf)) { enum fib_event_type fib_event; if (notify_sibling_rt) fib_event = FIB_EVENT_ENTRY_APPEND; else - fib_event = FIB_EVENT_ENTRY_REPLACE_TMP; - /* The route should only be notified if it is the first - * route in the node or if it is added as a sibling - * route to the first route in the node. - */ - if (notify_sibling_rt || ins == &fn->leaf) - err = call_fib6_entry_notifiers(info->nl_net, - fib_event, rt, - extack); - + fib_event = FIB_EVENT_ENTRY_REPLACE; err = call_fib6_entry_notifiers(info->nl_net, - FIB_EVENT_ENTRY_ADD, - rt, extack); + fib_event, rt, + extack); if (err) { struct fib6_info *sibling, *next_sibling; @@ -1282,14 +1259,7 @@ add: return -ENOENT; } - if (!info->skip_notify_kernel) { - enum fib_event_type fib_event; - - fib_event = FIB_EVENT_ENTRY_REPLACE_TMP; - if (ins == &fn->leaf) - err = call_fib6_entry_notifiers(info->nl_net, - fib_event, rt, - extack); + if (!info->skip_notify_kernel && ins == &fn->leaf) { err = call_fib6_entry_notifiers(info->nl_net, FIB_EVENT_ENTRY_REPLACE, rt, extack); @@ -2007,11 +1977,10 @@ static void fib6_del_route(struct fib6_table *table, struct fib6_node *fn, if (!info->skip_notify_kernel) { if (notify_del) - call_fib6_entry_notifiers(net, FIB_EVENT_ENTRY_DEL_TMP, + call_fib6_entry_notifiers(net, FIB_EVENT_ENTRY_DEL, rt, NULL); else if (replace_rt) call_fib6_entry_notifiers_replace(net, replace_rt); - call_fib6_entry_notifiers(net, FIB_EVENT_ENTRY_DEL, rt, NULL); } if (!info->skip_notify) inet6_rt_notify(RTM_DELROUTE, rt, info, 0); diff --git a/net/ipv6/route.c b/net/ipv6/route.c index 646716a47cc9a..4b8659e077d3e 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -3787,15 +3787,10 @@ static int __ip6_del_rt_siblings(struct fib6_info *rt, struct fib6_config *cfg) replace_rt); else call_fib6_multipath_entry_notifiers(net, - FIB_EVENT_ENTRY_DEL_TMP, + FIB_EVENT_ENTRY_DEL, rt, rt->fib6_nsiblings, NULL); } - call_fib6_multipath_entry_notifiers(net, - FIB_EVENT_ENTRY_DEL, - rt, - rt->fib6_nsiblings, - NULL); list_for_each_entry_safe(sibling, next_sibling, &rt->fib6_siblings, fib6_siblings) { @@ -5074,7 +5069,6 @@ static int ip6_route_multipath_add(struct fib6_config *cfg, { struct fib6_info *rt_notif = NULL, *rt_last = NULL; struct nl_info *info = &cfg->fc_nlinfo; - enum fib_event_type event_type; struct fib6_config r_cfg; struct rtnexthop *rtnh; struct fib6_info *rt; @@ -5210,7 +5204,7 @@ static int ip6_route_multipath_add(struct fib6_config *cfg, if (rt_notif->fib6_nsiblings != nhn - 1) fib_event = FIB_EVENT_ENTRY_APPEND; else - fib_event = FIB_EVENT_ENTRY_REPLACE_TMP; + fib_event = FIB_EVENT_ENTRY_REPLACE; err = call_fib6_multipath_entry_notifiers(info->nl_net, fib_event, rt_notif, @@ -5221,14 +5215,6 @@ static int ip6_route_multipath_add(struct fib6_config *cfg, goto add_errout; } } - event_type = replace ? FIB_EVENT_ENTRY_REPLACE : FIB_EVENT_ENTRY_ADD; - err = call_fib6_multipath_entry_notifiers(info->nl_net, event_type, - rt_notif, nhn - 1, extack); - if (err) { - /* Delete all the siblings that were just added */ - err_nh = NULL; - goto add_errout; - } /* success ... tell user about new route */ ip6_route_mpath_notify(rt_notif, rt_last, info, nlflags); -- GitLab From 8b5026bc16938920e4780b9094c3bf20e1e0939d Mon Sep 17 00:00:00 2001 From: Julian Wiedmann Date: Mon, 23 Dec 2019 15:03:21 +0100 Subject: [PATCH 1311/1437] s390/qeth: fix qdio teardown after early init error qeth_l?_set_online() goes through a number of initialization steps, and on any error uses qeth_l?_stop_card() to tear down the residual state. The first initialization step is qeth_core_hardsetup_card(). When this fails after having established a QDIO context on the device (ie. somewhere after qeth_mpc_initialize()), qeth_l?_stop_card() doesn't shut down this QDIO context again (since the card state hasn't progressed from DOWN at this stage). Even worse, we then call qdio_free() as final teardown step to free the QDIO data structures - while some of them are still hooked into wider QDIO infrastructure such as the IRQ list. This is inevitably followed by use-after-frees and other nastyness. Fix this by unconditionally calling qeth_qdio_clear_card() to shut down the QDIO context, and also to halt/clear any pending activity on the various IO channels. Remove the naive attempt at handling the teardown in qeth_mpc_initialize(), it clearly doesn't suffice and we're handling it properly now in the wider teardown code. Fixes: 4a71df50047f ("qeth: new qeth device driver") Signed-off-by: Julian Wiedmann Signed-off-by: David S. Miller --- drivers/s390/net/qeth_core_main.c | 20 ++++++++------------ drivers/s390/net/qeth_l2_main.c | 2 +- drivers/s390/net/qeth_l3_main.c | 2 +- 3 files changed, 10 insertions(+), 14 deletions(-) diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c index bc4158888af96..324cf22f9111c 100644 --- a/drivers/s390/net/qeth_core_main.c +++ b/drivers/s390/net/qeth_core_main.c @@ -2482,50 +2482,46 @@ static int qeth_mpc_initialize(struct qeth_card *card) rc = qeth_cm_enable(card); if (rc) { QETH_CARD_TEXT_(card, 2, "2err%d", rc); - goto out_qdio; + return rc; } rc = qeth_cm_setup(card); if (rc) { QETH_CARD_TEXT_(card, 2, "3err%d", rc); - goto out_qdio; + return rc; } rc = qeth_ulp_enable(card); if (rc) { QETH_CARD_TEXT_(card, 2, "4err%d", rc); - goto out_qdio; + return rc; } rc = qeth_ulp_setup(card); if (rc) { QETH_CARD_TEXT_(card, 2, "5err%d", rc); - goto out_qdio; + return rc; } rc = qeth_alloc_qdio_queues(card); if (rc) { QETH_CARD_TEXT_(card, 2, "5err%d", rc); - goto out_qdio; + return rc; } rc = qeth_qdio_establish(card); if (rc) { QETH_CARD_TEXT_(card, 2, "6err%d", rc); qeth_free_qdio_queues(card); - goto out_qdio; + return rc; } rc = qeth_qdio_activate(card); if (rc) { QETH_CARD_TEXT_(card, 2, "7err%d", rc); - goto out_qdio; + return rc; } rc = qeth_dm_act(card); if (rc) { QETH_CARD_TEXT_(card, 2, "8err%d", rc); - goto out_qdio; + return rc; } return 0; -out_qdio: - qeth_qdio_clear_card(card, !IS_IQD(card)); - qdio_free(CARD_DDEV(card)); - return rc; } void qeth_print_status_message(struct qeth_card *card) diff --git a/drivers/s390/net/qeth_l2_main.c b/drivers/s390/net/qeth_l2_main.c index 8c95e6019bacd..15e2fd65d4346 100644 --- a/drivers/s390/net/qeth_l2_main.c +++ b/drivers/s390/net/qeth_l2_main.c @@ -287,12 +287,12 @@ static void qeth_l2_stop_card(struct qeth_card *card) card->state = CARD_STATE_HARDSETUP; } if (card->state == CARD_STATE_HARDSETUP) { - qeth_qdio_clear_card(card, 0); qeth_drain_output_queues(card); qeth_clear_working_pool_list(card); card->state = CARD_STATE_DOWN; } + qeth_qdio_clear_card(card, 0); flush_workqueue(card->event_wq); card->info.mac_bits &= ~QETH_LAYER2_MAC_REGISTERED; card->info.promisc_mode = 0; diff --git a/drivers/s390/net/qeth_l3_main.c b/drivers/s390/net/qeth_l3_main.c index 04e301de376f0..5508ab89b518d 100644 --- a/drivers/s390/net/qeth_l3_main.c +++ b/drivers/s390/net/qeth_l3_main.c @@ -1307,12 +1307,12 @@ static void qeth_l3_stop_card(struct qeth_card *card) card->state = CARD_STATE_HARDSETUP; } if (card->state == CARD_STATE_HARDSETUP) { - qeth_qdio_clear_card(card, 0); qeth_drain_output_queues(card); qeth_clear_working_pool_list(card); card->state = CARD_STATE_DOWN; } + qeth_qdio_clear_card(card, 0); flush_workqueue(card->event_wq); card->info.promisc_mode = 0; } -- GitLab From 5b6c7b55cfe26224b0f41b1c226d3534c542787f Mon Sep 17 00:00:00 2001 From: Julian Wiedmann Date: Mon, 23 Dec 2019 15:03:22 +0100 Subject: [PATCH 1312/1437] s390/qeth: lock the card while changing its hsuid qeth_l3_dev_hsuid_store() initially checks the card state, but doesn't take the conf_mutex to ensure that the card stays in this state while being reconfigured. Rework the code to take this lock, and drop a redundant state check in a helper function. Fixes: b333293058aa ("qeth: add support for af_iucv HiperSockets transport") Signed-off-by: Julian Wiedmann Signed-off-by: David S. Miller --- drivers/s390/net/qeth_core_main.c | 5 ---- drivers/s390/net/qeth_l3_sys.c | 40 +++++++++++++++++++++---------- 2 files changed, 28 insertions(+), 17 deletions(-) diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c index 324cf22f9111c..c64ef55f0dffc 100644 --- a/drivers/s390/net/qeth_core_main.c +++ b/drivers/s390/net/qeth_core_main.c @@ -3425,11 +3425,6 @@ int qeth_configure_cq(struct qeth_card *card, enum qeth_cq cq) goto out; } - if (card->state != CARD_STATE_DOWN) { - rc = -1; - goto out; - } - qeth_free_qdio_queues(card); card->options.cq = cq; rc = 0; diff --git a/drivers/s390/net/qeth_l3_sys.c b/drivers/s390/net/qeth_l3_sys.c index f9067ed6c7d32..e8c848f72c6d2 100644 --- a/drivers/s390/net/qeth_l3_sys.c +++ b/drivers/s390/net/qeth_l3_sys.c @@ -242,21 +242,33 @@ static ssize_t qeth_l3_dev_hsuid_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct qeth_card *card = dev_get_drvdata(dev); + int rc = 0; char *tmp; - int rc; if (!IS_IQD(card)) return -EPERM; - if (card->state != CARD_STATE_DOWN) - return -EPERM; - if (card->options.sniffer) - return -EPERM; - if (card->options.cq == QETH_CQ_NOTAVAILABLE) - return -EPERM; + + mutex_lock(&card->conf_mutex); + if (card->state != CARD_STATE_DOWN) { + rc = -EPERM; + goto out; + } + + if (card->options.sniffer) { + rc = -EPERM; + goto out; + } + + if (card->options.cq == QETH_CQ_NOTAVAILABLE) { + rc = -EPERM; + goto out; + } tmp = strsep((char **)&buf, "\n"); - if (strlen(tmp) > 8) - return -EINVAL; + if (strlen(tmp) > 8) { + rc = -EINVAL; + goto out; + } if (card->options.hsuid[0]) /* delete old ip address */ @@ -267,11 +279,13 @@ static ssize_t qeth_l3_dev_hsuid_store(struct device *dev, card->options.hsuid[0] = '\0'; memcpy(card->dev->perm_addr, card->options.hsuid, 9); qeth_configure_cq(card, QETH_CQ_DISABLED); - return count; + goto out; } - if (qeth_configure_cq(card, QETH_CQ_ENABLED)) - return -EPERM; + if (qeth_configure_cq(card, QETH_CQ_ENABLED)) { + rc = -EPERM; + goto out; + } snprintf(card->options.hsuid, sizeof(card->options.hsuid), "%-8s", tmp); @@ -280,6 +294,8 @@ static ssize_t qeth_l3_dev_hsuid_store(struct device *dev, rc = qeth_l3_modify_hsuid(card, true); +out: + mutex_unlock(&card->conf_mutex); return rc ? rc : count; } -- GitLab From 68c57bfd52836e31bff33e5e1fc64029749d2c35 Mon Sep 17 00:00:00 2001 From: Alexandra Winter Date: Mon, 23 Dec 2019 15:03:23 +0100 Subject: [PATCH 1313/1437] s390/qeth: fix false reporting of VNIC CHAR config failure Symptom: Error message "Configuring the VNIC characteristics failed" in dmesg whenever an OSA interface on z15 is set online. The VNIC characteristics get re-programmed when setting a L2 device online. This follows the selected 'wanted' characteristics - with the exception that the INVISIBLE characteristic unconditionally gets switched off. For devices that don't support INVISIBLE (ie. OSA), the resulting IO failure raises a noisy error message ("Configuring the VNIC characteristics failed"). For IQD, INVISIBLE is off by default anyways. So don't unnecessarily special-case the INVISIBLE characteristic, and thereby suppress the misleading error message on OSA devices. Fixes: caa1f0b10d18 ("s390/qeth: add VNICC enable/disable support") Signed-off-by: Alexandra Winter Reviewed-by: Julian Wiedmann Signed-off-by: Julian Wiedmann Signed-off-by: David S. Miller --- drivers/s390/net/qeth_l2_main.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/s390/net/qeth_l2_main.c b/drivers/s390/net/qeth_l2_main.c index 15e2fd65d4346..fc5d8ed3a7375 100644 --- a/drivers/s390/net/qeth_l2_main.c +++ b/drivers/s390/net/qeth_l2_main.c @@ -2041,7 +2041,6 @@ static void qeth_l2_vnicc_init(struct qeth_card *card) error |= qeth_l2_vnicc_recover_timeout(card, QETH_VNICC_LEARNING, timeout); chars_tmp = card->options.vnicc.wanted_chars ^ QETH_VNICC_DEFAULT; - chars_tmp |= QETH_VNICC_BRIDGE_INVISIBLE; chars_len = sizeof(card->options.vnicc.wanted_chars) * BITS_PER_BYTE; for_each_set_bit(i, &chars_tmp, chars_len) { vnicc = BIT(i); -- GitLab From e8a66d800471e2df7f0b484e2e46898b21d1fa82 Mon Sep 17 00:00:00 2001 From: Alexandra Winter Date: Mon, 23 Dec 2019 15:03:24 +0100 Subject: [PATCH 1314/1437] s390/qeth: Fix vnicc_is_in_use if rx_bcast not set Symptom: After vnicc/rx_bcast has been manually set to 0, bridge_* sysfs parameters can still be set or written. Only occurs on HiperSockets, as OSA doesn't support changing rx_bcast. Vnic characteristics and bridgeport settings are mutually exclusive. rx_bcast defaults to 1, so manually setting it to 0 should disable bridge_* parameters. Instead it makes sense here to check the supported mask. If the card does not support vnicc at all, bridge commands are always allowed. Fixes: caa1f0b10d18 ("s390/qeth: add VNICC enable/disable support") Signed-off-by: Alexandra Winter Signed-off-by: Julian Wiedmann Signed-off-by: David S. Miller --- drivers/s390/net/qeth_l2_main.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/s390/net/qeth_l2_main.c b/drivers/s390/net/qeth_l2_main.c index fc5d8ed3a7375..8024a2112a870 100644 --- a/drivers/s390/net/qeth_l2_main.c +++ b/drivers/s390/net/qeth_l2_main.c @@ -1952,8 +1952,7 @@ int qeth_l2_vnicc_get_timeout(struct qeth_card *card, u32 *timeout) /* check if VNICC is currently enabled */ bool qeth_l2_vnicc_is_in_use(struct qeth_card *card) { - /* if everything is turned off, VNICC is not active */ - if (!card->options.vnicc.cur_chars) + if (!card->options.vnicc.sup_chars) return false; /* default values are only OK if rx_bcast was not enabled by user * or the card is offline. -- GitLab From d1b9ae1864fc3c000e0eb4af8482d78c63e0915a Mon Sep 17 00:00:00 2001 From: Alexandra Winter Date: Mon, 23 Dec 2019 15:03:25 +0100 Subject: [PATCH 1315/1437] s390/qeth: vnicc Fix init to default During vnicc_init wanted_char should be compared to cur_char and not to QETH_VNICC_DEFAULT. Without this patch there is no way to enforce the default values as desired values. Note, that it is expected, that a card comes online with default values. This patch was tested with private card firmware. Fixes: caa1f0b10d18 ("s390/qeth: add VNICC enable/disable support") Signed-off-by: Alexandra Winter Signed-off-by: Julian Wiedmann Signed-off-by: David S. Miller --- drivers/s390/net/qeth_l2_main.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/s390/net/qeth_l2_main.c b/drivers/s390/net/qeth_l2_main.c index 8024a2112a870..47d37e75dda68 100644 --- a/drivers/s390/net/qeth_l2_main.c +++ b/drivers/s390/net/qeth_l2_main.c @@ -2039,7 +2039,9 @@ static void qeth_l2_vnicc_init(struct qeth_card *card) /* enforce assumed default values and recover settings, if changed */ error |= qeth_l2_vnicc_recover_timeout(card, QETH_VNICC_LEARNING, timeout); - chars_tmp = card->options.vnicc.wanted_chars ^ QETH_VNICC_DEFAULT; + /* Change chars, if necessary */ + chars_tmp = card->options.vnicc.wanted_chars ^ + card->options.vnicc.cur_chars; chars_len = sizeof(card->options.vnicc.wanted_chars) * BITS_PER_BYTE; for_each_set_bit(i, &chars_tmp, chars_len) { vnicc = BIT(i); -- GitLab From 0b698c838e84149b690c7e979f78cccb6f8aa4b9 Mon Sep 17 00:00:00 2001 From: Julian Wiedmann Date: Mon, 23 Dec 2019 15:03:26 +0100 Subject: [PATCH 1316/1437] s390/qeth: fix initialization on old HW I stumbled over an old OSA model that claims to support DIAG_ASSIST, but then rejects the cmd to query its DIAG capabilities. In the old code this was ok, as the returned raw error code was > 0. Now that we translate the raw codes to errnos, the "rc < 0" causes us to fail the initialization of the device. The fix is trivial: don't bail out when the DIAG query fails. Such an error is not critical, we can still use the device (with a slightly reduced set of features). Fixes: 742d4d40831d ("s390/qeth: convert remaining legacy cmd callbacks") Signed-off-by: Julian Wiedmann Signed-off-by: David S. Miller --- drivers/s390/net/qeth_core_main.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c index c64ef55f0dffc..29facb9136715 100644 --- a/drivers/s390/net/qeth_core_main.c +++ b/drivers/s390/net/qeth_core_main.c @@ -5026,10 +5026,8 @@ retriable: } if (qeth_adp_supported(card, IPA_SETADP_SET_DIAG_ASSIST)) { rc = qeth_query_setdiagass(card); - if (rc < 0) { + if (rc) QETH_CARD_TEXT_(card, 2, "8err%d", rc); - goto out; - } } if (!qeth_is_diagass_supported(card, QETH_DIAGS_CMD_TRAP) || -- GitLab From 8385d756e114f2df8568e508902d5f9850817ffb Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Fri, 13 Dec 2019 09:04:08 +0100 Subject: [PATCH 1317/1437] libata: Fix retrieving of active qcs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ata_qc_complete_multiple() is called with a mask of the still active tags. mv_sata doesn't have this information directly and instead calculates the still active tags from the started tags (ap->qc_active) and the finished tags as (ap->qc_active ^ done_mask) Since 28361c40368 the hw_tag and tag are no longer the same and the equation is no longer valid. In ata_exec_internal_sg() ap->qc_active is initialized as 1ULL << ATA_TAG_INTERNAL, but in hardware tag 0 is started and this will be in done_mask on completion. ap->qc_active ^ done_mask becomes 0x100000000 ^ 0x1 = 0x100000001 and thus tag 0 used as the internal tag will never be reported as completed. This is fixed by introducing ata_qc_get_active() which returns the active hardware tags and calling it where appropriate. This is tested on mv_sata, but sata_fsl and sata_nv suffer from the same problem. There is another case in sata_nv that most likely needs fixing as well, but this looks a little different, so I wasn't confident enough to change that. Fixes: 28361c403683 ("libata: add extra internal command") Cc: stable@vger.kernel.org Tested-by: Pali Rohár Signed-off-by: Sascha Hauer Add missing export of ata_qc_get_active(), as per Pali. Signed-off-by: Jens Axboe --- drivers/ata/libata-core.c | 24 ++++++++++++++++++++++++ drivers/ata/sata_fsl.c | 2 +- drivers/ata/sata_mv.c | 2 +- drivers/ata/sata_nv.c | 2 +- include/linux/libata.h | 1 + 5 files changed, 28 insertions(+), 3 deletions(-) diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index e9017c570bc51..6f4ab5c5b52dd 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -5328,6 +5328,30 @@ void ata_qc_complete(struct ata_queued_cmd *qc) } } +/** + * ata_qc_get_active - get bitmask of active qcs + * @ap: port in question + * + * LOCKING: + * spin_lock_irqsave(host lock) + * + * RETURNS: + * Bitmask of active qcs + */ +u64 ata_qc_get_active(struct ata_port *ap) +{ + u64 qc_active = ap->qc_active; + + /* ATA_TAG_INTERNAL is sent to hw as tag 0 */ + if (qc_active & (1ULL << ATA_TAG_INTERNAL)) { + qc_active |= (1 << 0); + qc_active &= ~(1ULL << ATA_TAG_INTERNAL); + } + + return qc_active; +} +EXPORT_SYMBOL_GPL(ata_qc_get_active); + /** * ata_qc_complete_multiple - Complete multiple qcs successfully * @ap: port in question diff --git a/drivers/ata/sata_fsl.c b/drivers/ata/sata_fsl.c index 9239615d8a047..d55ee244d6931 100644 --- a/drivers/ata/sata_fsl.c +++ b/drivers/ata/sata_fsl.c @@ -1280,7 +1280,7 @@ static void sata_fsl_host_intr(struct ata_port *ap) i, ioread32(hcr_base + CC), ioread32(hcr_base + CA)); } - ata_qc_complete_multiple(ap, ap->qc_active ^ done_mask); + ata_qc_complete_multiple(ap, ata_qc_get_active(ap) ^ done_mask); return; } else if ((ap->qc_active & (1ULL << ATA_TAG_INTERNAL))) { diff --git a/drivers/ata/sata_mv.c b/drivers/ata/sata_mv.c index 277f11909fc1a..d7228f8e9297c 100644 --- a/drivers/ata/sata_mv.c +++ b/drivers/ata/sata_mv.c @@ -2829,7 +2829,7 @@ static void mv_process_crpb_entries(struct ata_port *ap, struct mv_port_priv *pp } if (work_done) { - ata_qc_complete_multiple(ap, ap->qc_active ^ done_mask); + ata_qc_complete_multiple(ap, ata_qc_get_active(ap) ^ done_mask); /* Update the software queue position index in hardware */ writelfl((pp->crpb_dma & EDMA_RSP_Q_BASE_LO_MASK) | diff --git a/drivers/ata/sata_nv.c b/drivers/ata/sata_nv.c index f3e62f5528bdb..eb9dc14e5147a 100644 --- a/drivers/ata/sata_nv.c +++ b/drivers/ata/sata_nv.c @@ -984,7 +984,7 @@ static irqreturn_t nv_adma_interrupt(int irq, void *dev_instance) check_commands = 0; check_commands &= ~(1 << pos); } - ata_qc_complete_multiple(ap, ap->qc_active ^ done_mask); + ata_qc_complete_multiple(ap, ata_qc_get_active(ap) ^ done_mask); } } diff --git a/include/linux/libata.h b/include/linux/libata.h index d3bbfddf616aa..2dbde119721df 100644 --- a/include/linux/libata.h +++ b/include/linux/libata.h @@ -1175,6 +1175,7 @@ extern unsigned int ata_do_dev_read_id(struct ata_device *dev, struct ata_taskfile *tf, u16 *id); extern void ata_qc_complete(struct ata_queued_cmd *qc); extern int ata_qc_complete_multiple(struct ata_port *ap, u64 qc_active); +extern u64 ata_qc_get_active(struct ata_port *ap); extern void ata_scsi_simulate(struct ata_device *dev, struct scsi_cmnd *cmd); extern int ata_std_bios_param(struct scsi_device *sdev, struct block_device *bdev, -- GitLab From 57b948e2c9ce58e4fcb687bc285b5b5a2cc66640 Mon Sep 17 00:00:00 2001 From: Netanel Belgazal Date: Sun, 22 Dec 2019 09:47:59 +0000 Subject: [PATCH 1318/1437] MAINTAINERS: Add additional maintainers to ENA Ethernet driver Signed-off-by: Netanel Belgazal Signed-off-by: David S. Miller --- MAINTAINERS | 2 ++ 1 file changed, 2 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index ffa3371bc750c..992474b930bfe 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -771,6 +771,8 @@ F: drivers/thermal/thermal_mmio.c AMAZON ETHERNET DRIVERS M: Netanel Belgazal +M: Arthur Kiyanovski +R: Guy Tzalik R: Saeed Bishara R: Zorik Machulsky L: netdev@vger.kernel.org -- GitLab From 84bb59d773853bc2dda2ac1ef8474c40eb33a3c6 Mon Sep 17 00:00:00 2001 From: Taehee Yoo Date: Sun, 22 Dec 2019 11:25:27 +0000 Subject: [PATCH 1319/1437] hsr: avoid debugfs warning message when module is remove When hsr module is being removed, debugfs_remove() is called to remove both debugfs directory and file. When module is being removed, module state is changed to MODULE_STATE_GOING then exit() is called. At this moment, module couldn't be held so try_module_get() will be failed. debugfs's open() callback tries to hold the module if .owner is existing. If it fails, warning message is printed. CPU0 CPU1 delete_module() try_stop_module() hsr_exit() open() <-- WARNING debugfs_remove() In order to avoid the warning message, this patch makes hsr module does not set .owner. Unsetting .owner is safe because these are protected by inode_lock(). Test commands: #SHELL1 ip link add dummy0 type dummy ip link add dummy1 type dummy while : do ip link add hsr0 type hsr slave1 dummy0 slave2 dummy1 modprobe -rv hsr done #SHELL2 while : do cat /sys/kernel/debug/hsr0/node_table done Splat looks like: [ 101.223783][ T1271] ------------[ cut here ]------------ [ 101.230309][ T1271] debugfs file owner did not clean up at exit: node_table [ 101.230380][ T1271] WARNING: CPU: 3 PID: 1271 at fs/debugfs/file.c:309 full_proxy_open+0x10f/0x650 [ 101.233153][ T1271] Modules linked in: hsr(-) dummy veth openvswitch nsh nf_conncount nf_nat nf_conntrack nf_d] [ 101.237112][ T1271] CPU: 3 PID: 1271 Comm: cat Tainted: G W 5.5.0-rc1+ #204 [ 101.238270][ T1271] Hardware name: innotek GmbH VirtualBox/VirtualBox, BIOS VirtualBox 12/01/2006 [ 101.240379][ T1271] RIP: 0010:full_proxy_open+0x10f/0x650 [ 101.241166][ T1271] Code: 48 c1 ea 03 80 3c 02 00 0f 85 c1 04 00 00 49 8b 3c 24 e8 04 86 7e ff 84 c0 75 2d 4c 8 [ 101.251985][ T1271] RSP: 0018:ffff8880ca22fa38 EFLAGS: 00010286 [ 101.273355][ T1271] RAX: dffffc0000000008 RBX: ffff8880cc6e6200 RCX: 0000000000000000 [ 101.274466][ T1271] RDX: 0000000000000000 RSI: 0000000000000006 RDI: ffff8880c4dd5c14 [ 101.275581][ T1271] RBP: 0000000000000000 R08: fffffbfff2922f5d R09: 0000000000000000 [ 101.276733][ T1271] R10: 0000000000000001 R11: 0000000000000000 R12: ffffffffc0551bc0 [ 101.277853][ T1271] R13: ffff8880c4059a48 R14: ffff8880be50a5e0 R15: ffffffff941adaa0 [ 101.278956][ T1271] FS: 00007f8871cda540(0000) GS:ffff8880da800000(0000) knlGS:0000000000000000 [ 101.280216][ T1271] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 [ 101.282832][ T1271] CR2: 00007f88717cfd10 CR3: 00000000b9440005 CR4: 00000000000606e0 [ 101.283974][ T1271] Call Trace: [ 101.285328][ T1271] do_dentry_open+0x63c/0xf50 [ 101.286077][ T1271] ? open_proxy_open+0x270/0x270 [ 101.288271][ T1271] ? __x64_sys_fchdir+0x180/0x180 [ 101.288987][ T1271] ? inode_permission+0x65/0x390 [ 101.289682][ T1271] path_openat+0x701/0x2810 [ 101.290294][ T1271] ? path_lookupat+0x880/0x880 [ 101.290957][ T1271] ? check_chain_key+0x236/0x5d0 [ 101.291676][ T1271] ? __lock_acquire+0xdfe/0x3de0 [ 101.292358][ T1271] ? sched_clock+0x5/0x10 [ 101.292962][ T1271] ? sched_clock_cpu+0x18/0x170 [ 101.293644][ T1271] ? find_held_lock+0x39/0x1d0 [ 101.305616][ T1271] do_filp_open+0x17a/0x270 [ 101.306061][ T1271] ? may_open_dev+0xc0/0xc0 [ ... ] Fixes: fc4ecaeebd26 ("net: hsr: add debugfs support for display node list") Signed-off-by: Taehee Yoo Signed-off-by: David S. Miller --- net/hsr/hsr_debugfs.c | 1 - 1 file changed, 1 deletion(-) diff --git a/net/hsr/hsr_debugfs.c b/net/hsr/hsr_debugfs.c index 94447974a3c05..6135706f03d5f 100644 --- a/net/hsr/hsr_debugfs.c +++ b/net/hsr/hsr_debugfs.c @@ -64,7 +64,6 @@ hsr_node_table_open(struct inode *inode, struct file *filp) } static const struct file_operations hsr_fops = { - .owner = THIS_MODULE, .open = hsr_node_table_open, .read = seq_read, .llseek = seq_lseek, -- GitLab From 1d19e2d53e8ed9e4c98fc95e0067492cda7288b0 Mon Sep 17 00:00:00 2001 From: Taehee Yoo Date: Sun, 22 Dec 2019 11:26:15 +0000 Subject: [PATCH 1320/1437] hsr: fix error handling routine in hsr_dev_finalize() hsr_dev_finalize() is called to create new hsr interface. There are some wrong error handling codes. 1. wrong checking return value of debugfs_create_{dir/file}. These function doesn't return NULL. If error occurs in there, it returns error pointer. So, it should check error pointer instead of NULL. 2. It doesn't unregister interface if it fails to setup hsr interface. If it fails to initialize hsr interface after register_netdevice(), it should call unregister_netdevice(). 3. Ignore failure of creation of debugfs If creating of debugfs dir and file is failed, creating hsr interface will be failed. But debugfs doesn't affect actual logic of hsr module. So, ignoring this is more correct and this behavior is more general. Fixes: c5a759117210 ("net/hsr: Use list_head (and rcu) instead of array for slave devices.") Signed-off-by: Taehee Yoo Signed-off-by: David S. Miller --- net/hsr/hsr_debugfs.c | 15 +++++++-------- net/hsr/hsr_device.c | 19 ++++++++++--------- net/hsr/hsr_main.h | 11 ++++------- 3 files changed, 21 insertions(+), 24 deletions(-) diff --git a/net/hsr/hsr_debugfs.c b/net/hsr/hsr_debugfs.c index 6135706f03d5f..6618a9d8e58e1 100644 --- a/net/hsr/hsr_debugfs.c +++ b/net/hsr/hsr_debugfs.c @@ -77,15 +77,14 @@ static const struct file_operations hsr_fops = { * When debugfs is configured this routine sets up the node_table file per * hsr device for dumping the node_table entries */ -int hsr_debugfs_init(struct hsr_priv *priv, struct net_device *hsr_dev) +void hsr_debugfs_init(struct hsr_priv *priv, struct net_device *hsr_dev) { - int rc = -1; struct dentry *de = NULL; de = debugfs_create_dir(hsr_dev->name, NULL); - if (!de) { + if (IS_ERR(de)) { pr_err("Cannot create hsr debugfs root\n"); - return rc; + return; } priv->node_tbl_root = de; @@ -93,13 +92,13 @@ int hsr_debugfs_init(struct hsr_priv *priv, struct net_device *hsr_dev) de = debugfs_create_file("node_table", S_IFREG | 0444, priv->node_tbl_root, priv, &hsr_fops); - if (!de) { + if (IS_ERR(de)) { pr_err("Cannot create hsr node_table directory\n"); - return rc; + debugfs_remove(priv->node_tbl_root); + priv->node_tbl_root = NULL; + return; } priv->node_tbl_file = de; - - return 0; } /* hsr_debugfs_term - Tear down debugfs intrastructure diff --git a/net/hsr/hsr_device.c b/net/hsr/hsr_device.c index b01e1bae4ddc0..e73549075a03e 100644 --- a/net/hsr/hsr_device.c +++ b/net/hsr/hsr_device.c @@ -477,30 +477,31 @@ int hsr_dev_finalize(struct net_device *hsr_dev, struct net_device *slave[2], res = hsr_add_port(hsr, hsr_dev, HSR_PT_MASTER); if (res) - goto err_add_port; + goto err_add_master; res = register_netdevice(hsr_dev); if (res) - goto fail; + goto err_unregister; res = hsr_add_port(hsr, slave[0], HSR_PT_SLAVE_A); if (res) - goto fail; + goto err_add_slaves; + res = hsr_add_port(hsr, slave[1], HSR_PT_SLAVE_B); if (res) - goto fail; + goto err_add_slaves; + hsr_debugfs_init(hsr, hsr_dev); mod_timer(&hsr->prune_timer, jiffies + msecs_to_jiffies(PRUNE_PERIOD)); - res = hsr_debugfs_init(hsr, hsr_dev); - if (res) - goto fail; return 0; -fail: +err_add_slaves: + unregister_netdevice(hsr_dev); +err_unregister: list_for_each_entry_safe(port, tmp, &hsr->ports, port_list) hsr_del_port(port); -err_add_port: +err_add_master: hsr_del_self_node(&hsr->self_node_db); return res; diff --git a/net/hsr/hsr_main.h b/net/hsr/hsr_main.h index 96fac696a1e1a..acab9c353a49f 100644 --- a/net/hsr/hsr_main.h +++ b/net/hsr/hsr_main.h @@ -184,15 +184,12 @@ static inline u16 hsr_get_skb_sequence_nr(struct sk_buff *skb) } #if IS_ENABLED(CONFIG_DEBUG_FS) -int hsr_debugfs_init(struct hsr_priv *priv, struct net_device *hsr_dev); +void hsr_debugfs_init(struct hsr_priv *priv, struct net_device *hsr_dev); void hsr_debugfs_term(struct hsr_priv *priv); #else -static inline int hsr_debugfs_init(struct hsr_priv *priv, - struct net_device *hsr_dev) -{ - return 0; -} - +static inline void hsr_debugfs_init(struct hsr_priv *priv, + struct net_device *hsr_dev) +{} static inline void hsr_debugfs_term(struct hsr_priv *priv) {} #endif -- GitLab From c6c4ccd7f96993e106dfea7ef18127f972f2db5e Mon Sep 17 00:00:00 2001 From: Taehee Yoo Date: Sun, 22 Dec 2019 11:26:27 +0000 Subject: [PATCH 1321/1437] hsr: add hsr root debugfs directory In current hsr code, when hsr interface is created, it creates debugfs directory /sys/kernel/debug/. If there is same directory or file name in there, it fails. In order to reduce possibility of failure of creation of debugfs, this patch adds root directory. Test commands: ip link add dummy0 type dummy ip link add dummy1 type dummy ip link add hsr0 type hsr slave1 dummy0 slave2 dummy1 Before this patch: /sys/kernel/debug/hsr0/node_table After this patch: /sys/kernel/debug/hsr/hsr0/node_table Signed-off-by: Taehee Yoo Signed-off-by: David S. Miller --- net/hsr/hsr_debugfs.c | 23 ++++++++++++++++++++--- net/hsr/hsr_main.c | 1 + net/hsr/hsr_main.h | 6 ++++++ net/hsr/hsr_netlink.c | 1 + 4 files changed, 28 insertions(+), 3 deletions(-) diff --git a/net/hsr/hsr_debugfs.c b/net/hsr/hsr_debugfs.c index 6618a9d8e58e1..a7462a718e7ba 100644 --- a/net/hsr/hsr_debugfs.c +++ b/net/hsr/hsr_debugfs.c @@ -20,6 +20,8 @@ #include "hsr_main.h" #include "hsr_framereg.h" +static struct dentry *hsr_debugfs_root_dir; + static void print_mac_address(struct seq_file *sfp, unsigned char *mac) { seq_printf(sfp, "%02x:%02x:%02x:%02x:%02x:%02x:", @@ -81,9 +83,9 @@ void hsr_debugfs_init(struct hsr_priv *priv, struct net_device *hsr_dev) { struct dentry *de = NULL; - de = debugfs_create_dir(hsr_dev->name, NULL); + de = debugfs_create_dir(hsr_dev->name, hsr_debugfs_root_dir); if (IS_ERR(de)) { - pr_err("Cannot create hsr debugfs root\n"); + pr_err("Cannot create hsr debugfs directory\n"); return; } @@ -93,7 +95,7 @@ void hsr_debugfs_init(struct hsr_priv *priv, struct net_device *hsr_dev) priv->node_tbl_root, priv, &hsr_fops); if (IS_ERR(de)) { - pr_err("Cannot create hsr node_table directory\n"); + pr_err("Cannot create hsr node_table file\n"); debugfs_remove(priv->node_tbl_root); priv->node_tbl_root = NULL; return; @@ -115,3 +117,18 @@ hsr_debugfs_term(struct hsr_priv *priv) debugfs_remove(priv->node_tbl_root); priv->node_tbl_root = NULL; } + +void hsr_debugfs_create_root(void) +{ + hsr_debugfs_root_dir = debugfs_create_dir("hsr", NULL); + if (IS_ERR(hsr_debugfs_root_dir)) { + pr_err("Cannot create hsr debugfs root directory\n"); + hsr_debugfs_root_dir = NULL; + } +} + +void hsr_debugfs_remove_root(void) +{ + /* debugfs_remove() internally checks NULL and ERROR */ + debugfs_remove(hsr_debugfs_root_dir); +} diff --git a/net/hsr/hsr_main.c b/net/hsr/hsr_main.c index b9988a662ee1a..490896379073a 100644 --- a/net/hsr/hsr_main.c +++ b/net/hsr/hsr_main.c @@ -123,6 +123,7 @@ static void __exit hsr_exit(void) { unregister_netdevice_notifier(&hsr_nb); hsr_netlink_exit(); + hsr_debugfs_remove_root(); } module_init(hsr_init); diff --git a/net/hsr/hsr_main.h b/net/hsr/hsr_main.h index acab9c353a49f..55d2057bf749e 100644 --- a/net/hsr/hsr_main.h +++ b/net/hsr/hsr_main.h @@ -186,12 +186,18 @@ static inline u16 hsr_get_skb_sequence_nr(struct sk_buff *skb) #if IS_ENABLED(CONFIG_DEBUG_FS) void hsr_debugfs_init(struct hsr_priv *priv, struct net_device *hsr_dev); void hsr_debugfs_term(struct hsr_priv *priv); +void hsr_debugfs_create_root(void); +void hsr_debugfs_remove_root(void); #else static inline void hsr_debugfs_init(struct hsr_priv *priv, struct net_device *hsr_dev) {} static inline void hsr_debugfs_term(struct hsr_priv *priv) {} +static inline void hsr_debugfs_create_root(void) +{} +static inline void hsr_debugfs_remove_root(void) +{} #endif #endif /* __HSR_PRIVATE_H */ diff --git a/net/hsr/hsr_netlink.c b/net/hsr/hsr_netlink.c index 8f8337f893bad..8dc0547f01d0b 100644 --- a/net/hsr/hsr_netlink.c +++ b/net/hsr/hsr_netlink.c @@ -476,6 +476,7 @@ int __init hsr_netlink_init(void) if (rc) goto fail_genl_register_family; + hsr_debugfs_create_root(); return 0; fail_genl_register_family: -- GitLab From 4c2d5e33dcd3a6333a7895be3b542ff3d373177c Mon Sep 17 00:00:00 2001 From: Taehee Yoo Date: Sun, 22 Dec 2019 11:26:39 +0000 Subject: [PATCH 1322/1437] hsr: rename debugfs file when interface name is changed hsr interface has own debugfs file, which name is same with interface name. So, interface name is changed, debugfs file name should be changed too. Fixes: fc4ecaeebd26 ("net: hsr: add debugfs support for display node list") Signed-off-by: Taehee Yoo Signed-off-by: David S. Miller --- net/hsr/hsr_debugfs.c | 13 +++++++++++++ net/hsr/hsr_main.c | 3 +++ net/hsr/hsr_main.h | 4 ++++ 3 files changed, 20 insertions(+) diff --git a/net/hsr/hsr_debugfs.c b/net/hsr/hsr_debugfs.c index a7462a718e7ba..d5f709b940ffd 100644 --- a/net/hsr/hsr_debugfs.c +++ b/net/hsr/hsr_debugfs.c @@ -65,6 +65,19 @@ hsr_node_table_open(struct inode *inode, struct file *filp) return single_open(filp, hsr_node_table_show, inode->i_private); } +void hsr_debugfs_rename(struct net_device *dev) +{ + struct hsr_priv *priv = netdev_priv(dev); + struct dentry *d; + + d = debugfs_rename(hsr_debugfs_root_dir, priv->node_tbl_root, + hsr_debugfs_root_dir, dev->name); + if (IS_ERR(d)) + netdev_warn(dev, "failed to rename\n"); + else + priv->node_tbl_root = d; +} + static const struct file_operations hsr_fops = { .open = hsr_node_table_open, .read = seq_read, diff --git a/net/hsr/hsr_main.c b/net/hsr/hsr_main.c index 490896379073a..ea23eb7408e4a 100644 --- a/net/hsr/hsr_main.c +++ b/net/hsr/hsr_main.c @@ -45,6 +45,9 @@ static int hsr_netdev_notify(struct notifier_block *nb, unsigned long event, case NETDEV_CHANGE: /* Link (carrier) state changes */ hsr_check_carrier_and_operstate(hsr); break; + case NETDEV_CHANGENAME: + hsr_debugfs_rename(dev); + break; case NETDEV_CHANGEADDR: if (port->type == HSR_PT_MASTER) { /* This should not happen since there's no diff --git a/net/hsr/hsr_main.h b/net/hsr/hsr_main.h index 55d2057bf749e..8d885bc6a54db 100644 --- a/net/hsr/hsr_main.h +++ b/net/hsr/hsr_main.h @@ -184,11 +184,15 @@ static inline u16 hsr_get_skb_sequence_nr(struct sk_buff *skb) } #if IS_ENABLED(CONFIG_DEBUG_FS) +void hsr_debugfs_rename(struct net_device *dev); void hsr_debugfs_init(struct hsr_priv *priv, struct net_device *hsr_dev); void hsr_debugfs_term(struct hsr_priv *priv); void hsr_debugfs_create_root(void); void hsr_debugfs_remove_root(void); #else +static inline void void hsr_debugfs_rename(struct net_device *dev) +{ +} static inline void hsr_debugfs_init(struct hsr_priv *priv, struct net_device *hsr_dev) {} -- GitLab From 92a35678ec075100ce666a2fb6969151affb0e5d Mon Sep 17 00:00:00 2001 From: Taehee Yoo Date: Sun, 22 Dec 2019 11:26:54 +0000 Subject: [PATCH 1323/1437] hsr: fix a race condition in node list insertion and deletion hsr nodes are protected by RCU and there is no write side lock. But node insertions and deletions could be being operated concurrently. So write side locking is needed. Test commands: ip netns add nst ip link add veth0 type veth peer name veth1 ip link add veth2 type veth peer name veth3 ip link set veth1 netns nst ip link set veth3 netns nst ip link set veth0 up ip link set veth2 up ip link add hsr0 type hsr slave1 veth0 slave2 veth2 ip a a 192.168.100.1/24 dev hsr0 ip link set hsr0 up ip netns exec nst ip link set veth1 up ip netns exec nst ip link set veth3 up ip netns exec nst ip link add hsr1 type hsr slave1 veth1 slave2 veth3 ip netns exec nst ip a a 192.168.100.2/24 dev hsr1 ip netns exec nst ip link set hsr1 up for i in {0..9} do for j in {0..9} do for k in {0..9} do for l in {0..9} do arping 192.168.100.2 -I hsr0 -s 00:01:3$i:4$j:5$k:6$l -c1 & done done done done Splat looks like: [ 236.066091][ T3286] list_add corruption. next->prev should be prev (ffff8880a5940300), but was ffff8880a5940d0. [ 236.069617][ T3286] ------------[ cut here ]------------ [ 236.070545][ T3286] kernel BUG at lib/list_debug.c:25! [ 236.071391][ T3286] invalid opcode: 0000 [#1] SMP DEBUG_PAGEALLOC KASAN PTI [ 236.072343][ T3286] CPU: 0 PID: 3286 Comm: arping Tainted: G W 5.5.0-rc1+ #209 [ 236.073463][ T3286] Hardware name: innotek GmbH VirtualBox/VirtualBox, BIOS VirtualBox 12/01/2006 [ 236.074695][ T3286] RIP: 0010:__list_add_valid+0x74/0xd0 [ 236.075499][ T3286] Code: 48 39 da 75 27 48 39 f5 74 36 48 39 dd 74 31 48 83 c4 08 b8 01 00 00 00 5b 5d c3 48 b [ 236.078277][ T3286] RSP: 0018:ffff8880aaa97648 EFLAGS: 00010286 [ 236.086991][ T3286] RAX: 0000000000000075 RBX: ffff8880d4624c20 RCX: 0000000000000000 [ 236.088000][ T3286] RDX: 0000000000000075 RSI: 0000000000000008 RDI: ffffed1015552ebf [ 236.098897][ T3286] RBP: ffff88809b53d200 R08: ffffed101b3c04f9 R09: ffffed101b3c04f9 [ 236.099960][ T3286] R10: 00000000308769a1 R11: ffffed101b3c04f8 R12: ffff8880d4624c28 [ 236.100974][ T3286] R13: ffff8880d4624c20 R14: 0000000040310100 R15: ffff8880ce17ee02 [ 236.138967][ T3286] FS: 00007f23479fa680(0000) GS:ffff8880d9c00000(0000) knlGS:0000000000000000 [ 236.144852][ T3286] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 [ 236.145720][ T3286] CR2: 00007f4a14bab210 CR3: 00000000a61c6001 CR4: 00000000000606f0 [ 236.146776][ T3286] Call Trace: [ 236.147222][ T3286] hsr_add_node+0x314/0x490 [hsr] [ 236.153633][ T3286] hsr_forward_skb+0x2b6/0x1bc0 [hsr] [ 236.154362][ T3286] ? rcu_read_lock_sched_held+0x90/0xc0 [ 236.155091][ T3286] ? rcu_read_lock_bh_held+0xa0/0xa0 [ 236.156607][ T3286] hsr_dev_xmit+0x70/0xd0 [hsr] [ 236.157254][ T3286] dev_hard_start_xmit+0x160/0x740 [ 236.157941][ T3286] __dev_queue_xmit+0x1961/0x2e10 [ 236.158565][ T3286] ? netdev_core_pick_tx+0x2e0/0x2e0 [ ... ] Reported-by: syzbot+3924327f9ad5f4d2b343@syzkaller.appspotmail.com Fixes: f421436a591d ("net/hsr: Add support for the High-availability Seamless Redundancy protocol (HSRv0)") Signed-off-by: Taehee Yoo Signed-off-by: David S. Miller --- net/hsr/hsr_device.c | 7 ++-- net/hsr/hsr_framereg.c | 73 ++++++++++++++++++++++++++---------------- net/hsr/hsr_framereg.h | 6 ++-- net/hsr/hsr_main.c | 2 +- net/hsr/hsr_main.h | 5 +-- 5 files changed, 56 insertions(+), 37 deletions(-) diff --git a/net/hsr/hsr_device.c b/net/hsr/hsr_device.c index e73549075a03e..62c03f0d00794 100644 --- a/net/hsr/hsr_device.c +++ b/net/hsr/hsr_device.c @@ -368,7 +368,7 @@ static void hsr_dev_destroy(struct net_device *hsr_dev) del_timer_sync(&hsr->prune_timer); del_timer_sync(&hsr->announce_timer); - hsr_del_self_node(&hsr->self_node_db); + hsr_del_self_node(hsr); hsr_del_nodes(&hsr->node_db); } @@ -440,11 +440,12 @@ int hsr_dev_finalize(struct net_device *hsr_dev, struct net_device *slave[2], INIT_LIST_HEAD(&hsr->ports); INIT_LIST_HEAD(&hsr->node_db); INIT_LIST_HEAD(&hsr->self_node_db); + spin_lock_init(&hsr->list_lock); ether_addr_copy(hsr_dev->dev_addr, slave[0]->dev_addr); /* Make sure we recognize frames from ourselves in hsr_rcv() */ - res = hsr_create_self_node(&hsr->self_node_db, hsr_dev->dev_addr, + res = hsr_create_self_node(hsr, hsr_dev->dev_addr, slave[1]->dev_addr); if (res < 0) return res; @@ -502,7 +503,7 @@ err_unregister: list_for_each_entry_safe(port, tmp, &hsr->ports, port_list) hsr_del_port(port); err_add_master: - hsr_del_self_node(&hsr->self_node_db); + hsr_del_self_node(hsr); return res; } diff --git a/net/hsr/hsr_framereg.c b/net/hsr/hsr_framereg.c index 292be446007b4..27dc65d7de67a 100644 --- a/net/hsr/hsr_framereg.c +++ b/net/hsr/hsr_framereg.c @@ -75,10 +75,11 @@ static struct hsr_node *find_node_by_addr_A(struct list_head *node_db, /* Helper for device init; the self_node_db is used in hsr_rcv() to recognize * frames from self that's been looped over the HSR ring. */ -int hsr_create_self_node(struct list_head *self_node_db, +int hsr_create_self_node(struct hsr_priv *hsr, unsigned char addr_a[ETH_ALEN], unsigned char addr_b[ETH_ALEN]) { + struct list_head *self_node_db = &hsr->self_node_db; struct hsr_node *node, *oldnode; node = kmalloc(sizeof(*node), GFP_KERNEL); @@ -88,33 +89,33 @@ int hsr_create_self_node(struct list_head *self_node_db, ether_addr_copy(node->macaddress_A, addr_a); ether_addr_copy(node->macaddress_B, addr_b); - rcu_read_lock(); + spin_lock_bh(&hsr->list_lock); oldnode = list_first_or_null_rcu(self_node_db, struct hsr_node, mac_list); if (oldnode) { list_replace_rcu(&oldnode->mac_list, &node->mac_list); - rcu_read_unlock(); - synchronize_rcu(); - kfree(oldnode); + spin_unlock_bh(&hsr->list_lock); + kfree_rcu(oldnode, rcu_head); } else { - rcu_read_unlock(); list_add_tail_rcu(&node->mac_list, self_node_db); + spin_unlock_bh(&hsr->list_lock); } return 0; } -void hsr_del_self_node(struct list_head *self_node_db) +void hsr_del_self_node(struct hsr_priv *hsr) { + struct list_head *self_node_db = &hsr->self_node_db; struct hsr_node *node; - rcu_read_lock(); + spin_lock_bh(&hsr->list_lock); node = list_first_or_null_rcu(self_node_db, struct hsr_node, mac_list); - rcu_read_unlock(); if (node) { list_del_rcu(&node->mac_list); - kfree(node); + kfree_rcu(node, rcu_head); } + spin_unlock_bh(&hsr->list_lock); } void hsr_del_nodes(struct list_head *node_db) @@ -130,30 +131,43 @@ void hsr_del_nodes(struct list_head *node_db) * seq_out is used to initialize filtering of outgoing duplicate frames * originating from the newly added node. */ -struct hsr_node *hsr_add_node(struct list_head *node_db, unsigned char addr[], - u16 seq_out) +static struct hsr_node *hsr_add_node(struct hsr_priv *hsr, + struct list_head *node_db, + unsigned char addr[], + u16 seq_out) { - struct hsr_node *node; + struct hsr_node *new_node, *node; unsigned long now; int i; - node = kzalloc(sizeof(*node), GFP_ATOMIC); - if (!node) + new_node = kzalloc(sizeof(*new_node), GFP_ATOMIC); + if (!new_node) return NULL; - ether_addr_copy(node->macaddress_A, addr); + ether_addr_copy(new_node->macaddress_A, addr); /* We are only interested in time diffs here, so use current jiffies * as initialization. (0 could trigger an spurious ring error warning). */ now = jiffies; for (i = 0; i < HSR_PT_PORTS; i++) - node->time_in[i] = now; + new_node->time_in[i] = now; for (i = 0; i < HSR_PT_PORTS; i++) - node->seq_out[i] = seq_out; - - list_add_tail_rcu(&node->mac_list, node_db); + new_node->seq_out[i] = seq_out; + spin_lock_bh(&hsr->list_lock); + list_for_each_entry_rcu(node, node_db, mac_list) { + if (ether_addr_equal(node->macaddress_A, addr)) + goto out; + if (ether_addr_equal(node->macaddress_B, addr)) + goto out; + } + list_add_tail_rcu(&new_node->mac_list, node_db); + spin_unlock_bh(&hsr->list_lock); + return new_node; +out: + spin_unlock_bh(&hsr->list_lock); + kfree(new_node); return node; } @@ -163,6 +177,7 @@ struct hsr_node *hsr_get_node(struct hsr_port *port, struct sk_buff *skb, bool is_sup) { struct list_head *node_db = &port->hsr->node_db; + struct hsr_priv *hsr = port->hsr; struct hsr_node *node; struct ethhdr *ethhdr; u16 seq_out; @@ -196,7 +211,7 @@ struct hsr_node *hsr_get_node(struct hsr_port *port, struct sk_buff *skb, seq_out = HSR_SEQNR_START; } - return hsr_add_node(node_db, ethhdr->h_source, seq_out); + return hsr_add_node(hsr, node_db, ethhdr->h_source, seq_out); } /* Use the Supervision frame's info about an eventual macaddress_B for merging @@ -206,10 +221,11 @@ struct hsr_node *hsr_get_node(struct hsr_port *port, struct sk_buff *skb, void hsr_handle_sup_frame(struct sk_buff *skb, struct hsr_node *node_curr, struct hsr_port *port_rcv) { - struct ethhdr *ethhdr; - struct hsr_node *node_real; + struct hsr_priv *hsr = port_rcv->hsr; struct hsr_sup_payload *hsr_sp; + struct hsr_node *node_real; struct list_head *node_db; + struct ethhdr *ethhdr; int i; ethhdr = (struct ethhdr *)skb_mac_header(skb); @@ -231,7 +247,7 @@ void hsr_handle_sup_frame(struct sk_buff *skb, struct hsr_node *node_curr, node_real = find_node_by_addr_A(node_db, hsr_sp->macaddress_A); if (!node_real) /* No frame received from AddrA of this node yet */ - node_real = hsr_add_node(node_db, hsr_sp->macaddress_A, + node_real = hsr_add_node(hsr, node_db, hsr_sp->macaddress_A, HSR_SEQNR_START - 1); if (!node_real) goto done; /* No mem */ @@ -252,7 +268,9 @@ void hsr_handle_sup_frame(struct sk_buff *skb, struct hsr_node *node_curr, } node_real->addr_B_port = port_rcv->type; + spin_lock_bh(&hsr->list_lock); list_del_rcu(&node_curr->mac_list); + spin_unlock_bh(&hsr->list_lock); kfree_rcu(node_curr, rcu_head); done: @@ -368,12 +386,13 @@ void hsr_prune_nodes(struct timer_list *t) { struct hsr_priv *hsr = from_timer(hsr, t, prune_timer); struct hsr_node *node; + struct hsr_node *tmp; struct hsr_port *port; unsigned long timestamp; unsigned long time_a, time_b; - rcu_read_lock(); - list_for_each_entry_rcu(node, &hsr->node_db, mac_list) { + spin_lock_bh(&hsr->list_lock); + list_for_each_entry_safe(node, tmp, &hsr->node_db, mac_list) { /* Don't prune own node. Neither time_in[HSR_PT_SLAVE_A] * nor time_in[HSR_PT_SLAVE_B], will ever be updated for * the master port. Thus the master node will be repeatedly @@ -421,7 +440,7 @@ void hsr_prune_nodes(struct timer_list *t) kfree_rcu(node, rcu_head); } } - rcu_read_unlock(); + spin_unlock_bh(&hsr->list_lock); /* Restart timer */ mod_timer(&hsr->prune_timer, diff --git a/net/hsr/hsr_framereg.h b/net/hsr/hsr_framereg.h index 89a3ce38151d1..0f0fa12b43293 100644 --- a/net/hsr/hsr_framereg.h +++ b/net/hsr/hsr_framereg.h @@ -12,10 +12,8 @@ struct hsr_node; -void hsr_del_self_node(struct list_head *self_node_db); +void hsr_del_self_node(struct hsr_priv *hsr); void hsr_del_nodes(struct list_head *node_db); -struct hsr_node *hsr_add_node(struct list_head *node_db, unsigned char addr[], - u16 seq_out); struct hsr_node *hsr_get_node(struct hsr_port *port, struct sk_buff *skb, bool is_sup); void hsr_handle_sup_frame(struct sk_buff *skb, struct hsr_node *node_curr, @@ -33,7 +31,7 @@ int hsr_register_frame_out(struct hsr_port *port, struct hsr_node *node, void hsr_prune_nodes(struct timer_list *t); -int hsr_create_self_node(struct list_head *self_node_db, +int hsr_create_self_node(struct hsr_priv *hsr, unsigned char addr_a[ETH_ALEN], unsigned char addr_b[ETH_ALEN]); diff --git a/net/hsr/hsr_main.c b/net/hsr/hsr_main.c index ea23eb7408e4a..d2ee7125a7f19 100644 --- a/net/hsr/hsr_main.c +++ b/net/hsr/hsr_main.c @@ -67,7 +67,7 @@ static int hsr_netdev_notify(struct notifier_block *nb, unsigned long event, /* Make sure we recognize frames from ourselves in hsr_rcv() */ port = hsr_port_get_hsr(hsr, HSR_PT_SLAVE_B); - res = hsr_create_self_node(&hsr->self_node_db, + res = hsr_create_self_node(hsr, master->dev->dev_addr, port ? port->dev->dev_addr : diff --git a/net/hsr/hsr_main.h b/net/hsr/hsr_main.h index 8d885bc6a54db..d40de84a637f3 100644 --- a/net/hsr/hsr_main.h +++ b/net/hsr/hsr_main.h @@ -160,8 +160,9 @@ struct hsr_priv { int announce_count; u16 sequence_nr; u16 sup_sequence_nr; /* For HSRv1 separate seq_nr for supervision */ - u8 prot_version; /* Indicate if HSRv0 or HSRv1. */ - spinlock_t seqnr_lock; /* locking for sequence_nr */ + u8 prot_version; /* Indicate if HSRv0 or HSRv1. */ + spinlock_t seqnr_lock; /* locking for sequence_nr */ + spinlock_t list_lock; /* locking for node list */ unsigned char sup_multicast_addr[ETH_ALEN]; #ifdef CONFIG_DEBUG_FS struct dentry *node_tbl_root; -- GitLab From 3ed0a1d563903bdb4b4c36c58c4d9c1bcb23a6e6 Mon Sep 17 00:00:00 2001 From: Taehee Yoo Date: Sun, 22 Dec 2019 11:27:08 +0000 Subject: [PATCH 1324/1437] hsr: reset network header when supervision frame is created The supervision frame is L2 frame. When supervision frame is created, hsr module doesn't set network header. If tap routine is enabled, dev_queue_xmit_nit() is called and it checks network_header. If network_header pointer wasn't set(or invalid), it resets network_header and warns. In order to avoid unnecessary warning message, resetting network_header is needed. Test commands: ip netns add nst ip link add veth0 type veth peer name veth1 ip link add veth2 type veth peer name veth3 ip link set veth1 netns nst ip link set veth3 netns nst ip link set veth0 up ip link set veth2 up ip link add hsr0 type hsr slave1 veth0 slave2 veth2 ip a a 192.168.100.1/24 dev hsr0 ip link set hsr0 up ip netns exec nst ip link set veth1 up ip netns exec nst ip link set veth3 up ip netns exec nst ip link add hsr1 type hsr slave1 veth1 slave2 veth3 ip netns exec nst ip a a 192.168.100.2/24 dev hsr1 ip netns exec nst ip link set hsr1 up tcpdump -nei veth0 Splat looks like: [ 175.852292][ C3] protocol 88fb is buggy, dev veth0 Fixes: f421436a591d ("net/hsr: Add support for the High-availability Seamless Redundancy protocol (HSRv0)") Signed-off-by: Taehee Yoo Signed-off-by: David S. Miller --- net/hsr/hsr_device.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/net/hsr/hsr_device.c b/net/hsr/hsr_device.c index 62c03f0d00794..c7bd6c49fadff 100644 --- a/net/hsr/hsr_device.c +++ b/net/hsr/hsr_device.c @@ -272,6 +272,8 @@ static void send_hsr_supervision_frame(struct hsr_port *master, skb->dev->dev_addr, skb->len) <= 0) goto out; skb_reset_mac_header(skb); + skb_reset_network_header(skb); + skb_reset_transport_header(skb); if (hsr_ver > 0) { hsr_tag = skb_put(skb, sizeof(struct hsr_tag)); -- GitLab From 84b032dbfdf1c139cd2b864e43959510646975f8 Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Tue, 10 Dec 2019 10:53:44 -0800 Subject: [PATCH 1325/1437] ata: libahci_platform: Export again ahci_platform_able_phys() This reverts commit 6bb86fefa086faba7b60bb452300b76a47cde1a5 ("libahci_platform: Staticize ahci_platform_able_phys()") we are going to need ahci_platform_{enable,disable}_phys() in a subsequent commit for ahci_brcm.c in order to properly control the PHY initialization order. Also make sure the function prototypes are declared in include/linux/ahci_platform.h as a result. Cc: stable@vger.kernel.org Reviewed-by: Hans de Goede Signed-off-by: Florian Fainelli Signed-off-by: Jens Axboe --- drivers/ata/libahci_platform.c | 6 ++++-- include/linux/ahci_platform.h | 2 ++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/ata/libahci_platform.c b/drivers/ata/libahci_platform.c index 8befce036af84..129556fcf6be7 100644 --- a/drivers/ata/libahci_platform.c +++ b/drivers/ata/libahci_platform.c @@ -43,7 +43,7 @@ EXPORT_SYMBOL_GPL(ahci_platform_ops); * RETURNS: * 0 on success otherwise a negative error code */ -static int ahci_platform_enable_phys(struct ahci_host_priv *hpriv) +int ahci_platform_enable_phys(struct ahci_host_priv *hpriv) { int rc, i; @@ -74,6 +74,7 @@ disable_phys: } return rc; } +EXPORT_SYMBOL_GPL(ahci_platform_enable_phys); /** * ahci_platform_disable_phys - Disable PHYs @@ -81,7 +82,7 @@ disable_phys: * * This function disables all PHYs found in hpriv->phys. */ -static void ahci_platform_disable_phys(struct ahci_host_priv *hpriv) +void ahci_platform_disable_phys(struct ahci_host_priv *hpriv) { int i; @@ -90,6 +91,7 @@ static void ahci_platform_disable_phys(struct ahci_host_priv *hpriv) phy_exit(hpriv->phys[i]); } } +EXPORT_SYMBOL_GPL(ahci_platform_disable_phys); /** * ahci_platform_enable_clks - Enable platform clocks diff --git a/include/linux/ahci_platform.h b/include/linux/ahci_platform.h index 6782f0d45ebe5..49e5383d42222 100644 --- a/include/linux/ahci_platform.h +++ b/include/linux/ahci_platform.h @@ -19,6 +19,8 @@ struct ahci_host_priv; struct platform_device; struct scsi_host_template; +int ahci_platform_enable_phys(struct ahci_host_priv *hpriv); +void ahci_platform_disable_phys(struct ahci_host_priv *hpriv); int ahci_platform_enable_clks(struct ahci_host_priv *hpriv); void ahci_platform_disable_clks(struct ahci_host_priv *hpriv); int ahci_platform_enable_regulators(struct ahci_host_priv *hpriv); -- GitLab From c0cdf2ac4b5bf3e5ef2451ea29fb4104278cdabc Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Tue, 10 Dec 2019 10:53:45 -0800 Subject: [PATCH 1326/1437] ata: ahci_brcm: Fix AHCI resources management The AHCI resources management within ahci_brcm.c is a little convoluted, largely because it historically had a dedicated clock that was managed within this file in the downstream tree. Once brough upstream though, the clock was left to be managed by libahci_platform.c which is entirely appropriate. This patch series ensures that the AHCI resources are fetched and enabled before any register access is done, thus avoiding bus errors on platforms which clock gate the controller by default. As a result we need to re-arrange the suspend() and resume() functions in order to avoid accessing registers after the clocks have been turned off respectively before the clocks have been turned on. Finally, we can refactor brcm_ahci_get_portmask() in order to fetch the number of ports from hpriv->mmio which is now accessible without jumping through hoops like we used to do. The commit pointed in the Fixes tag is both old and new enough not to require major headaches for backporting of this patch. Fixes: eba68f829794 ("ata: ahci_brcmstb: rename to support across Broadcom SoC's") Cc: stable@vger.kernel.org Reviewed-by: Hans de Goede Signed-off-by: Florian Fainelli Signed-off-by: Jens Axboe --- drivers/ata/ahci_brcm.c | 105 +++++++++++++++++++++++++++++----------- 1 file changed, 76 insertions(+), 29 deletions(-) diff --git a/drivers/ata/ahci_brcm.c b/drivers/ata/ahci_brcm.c index f41744b9b38a6..a8b2f3f7bbbc9 100644 --- a/drivers/ata/ahci_brcm.c +++ b/drivers/ata/ahci_brcm.c @@ -213,19 +213,12 @@ static void brcm_sata_phys_disable(struct brcm_ahci_priv *priv) brcm_sata_phy_disable(priv, i); } -static u32 brcm_ahci_get_portmask(struct platform_device *pdev, +static u32 brcm_ahci_get_portmask(struct ahci_host_priv *hpriv, struct brcm_ahci_priv *priv) { - void __iomem *ahci; - struct resource *res; u32 impl; - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ahci"); - ahci = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(ahci)) - return 0; - - impl = readl(ahci + HOST_PORTS_IMPL); + impl = readl(hpriv->mmio + HOST_PORTS_IMPL); if (fls(impl) > SATA_TOP_MAX_PHYS) dev_warn(priv->dev, "warning: more ports than PHYs (%#x)\n", @@ -233,9 +226,6 @@ static u32 brcm_ahci_get_portmask(struct platform_device *pdev, else if (!impl) dev_info(priv->dev, "no ports found\n"); - devm_iounmap(&pdev->dev, ahci); - devm_release_mem_region(&pdev->dev, res->start, resource_size(res)); - return impl; } @@ -347,11 +337,10 @@ static int brcm_ahci_suspend(struct device *dev) struct ata_host *host = dev_get_drvdata(dev); struct ahci_host_priv *hpriv = host->private_data; struct brcm_ahci_priv *priv = hpriv->plat_data; - int ret; - ret = ahci_platform_suspend(dev); brcm_sata_phys_disable(priv); - return ret; + + return ahci_platform_suspend(dev); } static int brcm_ahci_resume(struct device *dev) @@ -359,11 +348,44 @@ static int brcm_ahci_resume(struct device *dev) struct ata_host *host = dev_get_drvdata(dev); struct ahci_host_priv *hpriv = host->private_data; struct brcm_ahci_priv *priv = hpriv->plat_data; + int ret; + + /* Make sure clocks are turned on before re-configuration */ + ret = ahci_platform_enable_clks(hpriv); + if (ret) + return ret; brcm_sata_init(priv); brcm_sata_phys_enable(priv); brcm_sata_alpm_init(hpriv); - return ahci_platform_resume(dev); + + /* Since we had to enable clocks earlier on, we cannot use + * ahci_platform_resume() as-is since a second call to + * ahci_platform_enable_resources() would bump up the resources + * (regulators, clocks, PHYs) count artificially so we copy the part + * after ahci_platform_enable_resources(). + */ + ret = ahci_platform_enable_phys(hpriv); + if (ret) + goto out_disable_phys; + + ret = ahci_platform_resume_host(dev); + if (ret) + goto out_disable_platform_phys; + + /* We resumed so update PM runtime state */ + pm_runtime_disable(dev); + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + + return 0; + +out_disable_platform_phys: + ahci_platform_disable_phys(hpriv); +out_disable_phys: + brcm_sata_phys_disable(priv); + ahci_platform_disable_clks(hpriv); + return ret; } #endif @@ -416,38 +438,63 @@ static int brcm_ahci_probe(struct platform_device *pdev) priv->quirks |= BRCM_AHCI_QUIRK_SKIP_PHY_ENABLE; } + hpriv = ahci_platform_get_resources(pdev, 0); + if (IS_ERR(hpriv)) { + ret = PTR_ERR(hpriv); + goto out_reset; + } + + ret = ahci_platform_enable_clks(hpriv); + if (ret) + goto out_reset; + + /* Must be first so as to configure endianness including that + * of the standard AHCI register space. + */ brcm_sata_init(priv); - priv->port_mask = brcm_ahci_get_portmask(pdev, priv); - if (!priv->port_mask) - return -ENODEV; + /* Initializes priv->port_mask which is used below */ + priv->port_mask = brcm_ahci_get_portmask(hpriv, priv); + if (!priv->port_mask) { + ret = -ENODEV; + goto out_disable_clks; + } + /* Must be done before ahci_platform_enable_phys() */ brcm_sata_phys_enable(priv); - hpriv = ahci_platform_get_resources(pdev, 0); - if (IS_ERR(hpriv)) - return PTR_ERR(hpriv); hpriv->plat_data = priv; hpriv->flags = AHCI_HFLAG_WAKE_BEFORE_STOP; brcm_sata_alpm_init(hpriv); - ret = ahci_platform_enable_resources(hpriv); - if (ret) - return ret; - if (priv->quirks & BRCM_AHCI_QUIRK_NO_NCQ) hpriv->flags |= AHCI_HFLAG_NO_NCQ; hpriv->flags |= AHCI_HFLAG_NO_WRITE_TO_RO; + ret = ahci_platform_enable_phys(hpriv); + if (ret) + goto out_disable_phys; + ret = ahci_platform_init_host(pdev, hpriv, &ahci_brcm_port_info, &ahci_platform_sht); if (ret) - return ret; + goto out_disable_platform_phys; dev_info(dev, "Broadcom AHCI SATA3 registered\n"); return 0; + +out_disable_platform_phys: + ahci_platform_disable_phys(hpriv); +out_disable_phys: + brcm_sata_phys_disable(priv); +out_disable_clks: + ahci_platform_disable_clks(hpriv); +out_reset: + if (!IS_ERR_OR_NULL(priv->rcdev)) + reset_control_assert(priv->rcdev); + return ret; } static int brcm_ahci_remove(struct platform_device *pdev) @@ -457,12 +504,12 @@ static int brcm_ahci_remove(struct platform_device *pdev) struct brcm_ahci_priv *priv = hpriv->plat_data; int ret; + brcm_sata_phys_disable(priv); + ret = ata_platform_remove_one(pdev); if (ret) return ret; - brcm_sata_phys_disable(priv); - return 0; } -- GitLab From 1a3d78cb6e20779a19388315bd8efefbd8d4a656 Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Tue, 10 Dec 2019 10:53:46 -0800 Subject: [PATCH 1327/1437] ata: ahci_brcm: BCM7425 AHCI requires AHCI_HFLAG_DELAY_ENGINE Set AHCI_HFLAG_DELAY_ENGINE for the BCM7425 AHCI controller thus making it conforming to the 'strict' AHCI implementation which this controller is based on. This solves long link establishment with specific hard drives (e.g.: Seagate ST1000VM002-9ZL1 SC12) that would otherwise have to complete the error recovery handling before finally establishing a succesful SATA link at the desired speed. We re-order the hpriv->flags assignment to also remove the NONCQ quirk since we can set the flag directly. Fixes: 9586114cf1e9 ("ata: ahci_brcmstb: add support MIPS-based platforms") Fixes: 423be77daabe ("ata: ahci_brcmstb: add quirk for broken ncq") Cc: stable@vger.kernel.org Reviewed-by: Hans de Goede Signed-off-by: Florian Fainelli Signed-off-by: Jens Axboe --- drivers/ata/ahci_brcm.c | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/drivers/ata/ahci_brcm.c b/drivers/ata/ahci_brcm.c index a8b2f3f7bbbc9..58f8fd7bb8b8b 100644 --- a/drivers/ata/ahci_brcm.c +++ b/drivers/ata/ahci_brcm.c @@ -76,8 +76,7 @@ enum brcm_ahci_version { }; enum brcm_ahci_quirks { - BRCM_AHCI_QUIRK_NO_NCQ = BIT(0), - BRCM_AHCI_QUIRK_SKIP_PHY_ENABLE = BIT(1), + BRCM_AHCI_QUIRK_SKIP_PHY_ENABLE = BIT(0), }; struct brcm_ahci_priv { @@ -432,18 +431,27 @@ static int brcm_ahci_probe(struct platform_device *pdev) if (!IS_ERR_OR_NULL(priv->rcdev)) reset_control_deassert(priv->rcdev); - if ((priv->version == BRCM_SATA_BCM7425) || - (priv->version == BRCM_SATA_NSP)) { - priv->quirks |= BRCM_AHCI_QUIRK_NO_NCQ; - priv->quirks |= BRCM_AHCI_QUIRK_SKIP_PHY_ENABLE; - } - hpriv = ahci_platform_get_resources(pdev, 0); if (IS_ERR(hpriv)) { ret = PTR_ERR(hpriv); goto out_reset; } + hpriv->plat_data = priv; + hpriv->flags = AHCI_HFLAG_WAKE_BEFORE_STOP | AHCI_HFLAG_NO_WRITE_TO_RO; + + switch (priv->version) { + case BRCM_SATA_BCM7425: + hpriv->flags |= AHCI_HFLAG_DELAY_ENGINE; + /* fall through */ + case BRCM_SATA_NSP: + hpriv->flags |= AHCI_HFLAG_NO_NCQ; + priv->quirks |= BRCM_AHCI_QUIRK_SKIP_PHY_ENABLE; + break; + default: + break; + } + ret = ahci_platform_enable_clks(hpriv); if (ret) goto out_reset; @@ -463,15 +471,8 @@ static int brcm_ahci_probe(struct platform_device *pdev) /* Must be done before ahci_platform_enable_phys() */ brcm_sata_phys_enable(priv); - hpriv->plat_data = priv; - hpriv->flags = AHCI_HFLAG_WAKE_BEFORE_STOP; - brcm_sata_alpm_init(hpriv); - if (priv->quirks & BRCM_AHCI_QUIRK_NO_NCQ) - hpriv->flags |= AHCI_HFLAG_NO_NCQ; - hpriv->flags |= AHCI_HFLAG_NO_WRITE_TO_RO; - ret = ahci_platform_enable_phys(hpriv); if (ret) goto out_disable_phys; -- GitLab From bf0e5013bc2dcac205417e1252205dca39dfc005 Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Tue, 10 Dec 2019 10:53:47 -0800 Subject: [PATCH 1328/1437] ata: ahci_brcm: Add missing clock management during recovery The downstream implementation of ahci_brcm.c did contain clock management recovery, but until recently, did that outside of the libahci_platform helpers and this was unintentionally stripped out while forward porting the patch upstream. Add the missing clock management during recovery and sleep for 10 milliseconds per the design team recommendations to ensure the SATA PHY controller and AFE have been fully quiesced. Fixes: eb73390ae241 ("ata: ahci_brcm: Recover from failures to identify devices") Cc: stable@vger.kernel.org Reviewed-by: Hans de Goede Signed-off-by: Florian Fainelli Signed-off-by: Jens Axboe --- drivers/ata/ahci_brcm.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/ata/ahci_brcm.c b/drivers/ata/ahci_brcm.c index 58f8fd7bb8b8b..66a570d0da837 100644 --- a/drivers/ata/ahci_brcm.c +++ b/drivers/ata/ahci_brcm.c @@ -274,6 +274,13 @@ static unsigned int brcm_ahci_read_id(struct ata_device *dev, /* Perform the SATA PHY reset sequence */ brcm_sata_phy_disable(priv, ap->port_no); + /* Reset the SATA clock */ + ahci_platform_disable_clks(hpriv); + msleep(10); + + ahci_platform_enable_clks(hpriv); + msleep(10); + /* Bring the PHY back on */ brcm_sata_phy_enable(priv, ap->port_no); -- GitLab From 0e5dafc8a6e540c0145b61545c557c43be70af10 Mon Sep 17 00:00:00 2001 From: Richard Cochran Date: Wed, 25 Dec 2019 18:16:09 -0800 Subject: [PATCH 1329/1437] net: phy: Introduce helper functions for time stamping support. Some parts of the networking stack and at least one driver test fields within the 'struct phy_device' in order to query time stamping capabilities and to invoke time stamping methods. This patch adds a functional interface around the time stamping fields. This will allow insulating the callers from future changes to the details of the time stamping implemenation. Signed-off-by: Richard Cochran Reviewed-by: Andrew Lunn Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller --- include/linux/phy.h | 60 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/include/linux/phy.h b/include/linux/phy.h index 7d530b3f88558..0248f5e9939de 100644 --- a/include/linux/phy.h +++ b/include/linux/phy.h @@ -936,6 +936,66 @@ static inline bool phy_polling_mode(struct phy_device *phydev) return phydev->irq == PHY_POLL; } +/** + * phy_has_hwtstamp - Tests whether a PHY time stamp configuration. + * @phydev: the phy_device struct + */ +static inline bool phy_has_hwtstamp(struct phy_device *phydev) +{ + return phydev && phydev->drv && phydev->drv->hwtstamp; +} + +/** + * phy_has_rxtstamp - Tests whether a PHY supports receive time stamping. + * @phydev: the phy_device struct + */ +static inline bool phy_has_rxtstamp(struct phy_device *phydev) +{ + return phydev && phydev->drv && phydev->drv->rxtstamp; +} + +/** + * phy_has_tsinfo - Tests whether a PHY reports time stamping and/or + * PTP hardware clock capabilities. + * @phydev: the phy_device struct + */ +static inline bool phy_has_tsinfo(struct phy_device *phydev) +{ + return phydev && phydev->drv && phydev->drv->ts_info; +} + +/** + * phy_has_txtstamp - Tests whether a PHY supports transmit time stamping. + * @phydev: the phy_device struct + */ +static inline bool phy_has_txtstamp(struct phy_device *phydev) +{ + return phydev && phydev->drv && phydev->drv->txtstamp; +} + +static inline int phy_hwtstamp(struct phy_device *phydev, struct ifreq *ifr) +{ + return phydev->drv->hwtstamp(phydev, ifr); +} + +static inline bool phy_rxtstamp(struct phy_device *phydev, struct sk_buff *skb, + int type) +{ + return phydev->drv->rxtstamp(phydev, skb, type); +} + +static inline int phy_ts_info(struct phy_device *phydev, + struct ethtool_ts_info *tsinfo) +{ + return phydev->drv->ts_info(phydev, tsinfo); +} + +static inline void phy_txtstamp(struct phy_device *phydev, struct sk_buff *skb, + int type) +{ + phydev->drv->txtstamp(phydev, skb, type); +} + /** * phy_is_internal - Convenience function for testing if a PHY is internal * @phydev: the phy_device struct -- GitLab From d25de984aaee5265ff0c2c4eb49e474096d29827 Mon Sep 17 00:00:00 2001 From: Richard Cochran Date: Wed, 25 Dec 2019 18:16:10 -0800 Subject: [PATCH 1330/1437] net: macvlan: Use the PHY time stamping interface. The macvlan layer tests fields of the phy_device in order to determine whether to invoke the PHY's tsinfo ethtool callback. This patch replaces the open coded logic with an invocation of the proper methods. Signed-off-by: Richard Cochran Reviewed-by: Andrew Lunn Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/macvlan.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/macvlan.c b/drivers/net/macvlan.c index 05631d97eeb4f..d066cf58c9266 100644 --- a/drivers/net/macvlan.c +++ b/drivers/net/macvlan.c @@ -1036,8 +1036,8 @@ static int macvlan_ethtool_get_ts_info(struct net_device *dev, const struct ethtool_ops *ops = real_dev->ethtool_ops; struct phy_device *phydev = real_dev->phydev; - if (phydev && phydev->drv && phydev->drv->ts_info) { - return phydev->drv->ts_info(phydev, info); + if (phy_has_tsinfo(phydev)) { + return phy_ts_info(phydev, info); } else if (ops->get_ts_info) { return ops->get_ts_info(real_dev, info); } else { -- GitLab From dfe6d68fc4049c799b2194d88523679a694ead75 Mon Sep 17 00:00:00 2001 From: Richard Cochran Date: Wed, 25 Dec 2019 18:16:11 -0800 Subject: [PATCH 1331/1437] net: vlan: Use the PHY time stamping interface. The vlan layer tests fields of the phy_device in order to determine whether to invoke the PHY's tsinfo ethtool callback. This patch replaces the open coded logic with an invocation of the proper methods. Signed-off-by: Richard Cochran Reviewed-by: Andrew Lunn Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller --- net/8021q/vlan_dev.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/8021q/vlan_dev.c b/net/8021q/vlan_dev.c index e5bff5cc6f975..5ff8059837b49 100644 --- a/net/8021q/vlan_dev.c +++ b/net/8021q/vlan_dev.c @@ -646,8 +646,8 @@ static int vlan_ethtool_get_ts_info(struct net_device *dev, const struct ethtool_ops *ops = vlan->real_dev->ethtool_ops; struct phy_device *phydev = vlan->real_dev->phydev; - if (phydev && phydev->drv && phydev->drv->ts_info) { - return phydev->drv->ts_info(phydev, info); + if (phy_has_tsinfo(phydev)) { + return phy_ts_info(phydev, info); } else if (ops->get_ts_info) { return ops->get_ts_info(vlan->real_dev, info); } else { -- GitLab From 7774ee23689d3c56ecb5eff6cd1bf6060bdd3d8e Mon Sep 17 00:00:00 2001 From: Richard Cochran Date: Wed, 25 Dec 2019 18:16:12 -0800 Subject: [PATCH 1332/1437] net: ethtool: Use the PHY time stamping interface. The ethtool layer tests fields of the phy_device in order to determine whether to invoke the PHY's tsinfo ethtool callback. This patch replaces the open coded logic with an invocation of the proper methods. Signed-off-by: Richard Cochran Reviewed-by: Andrew Lunn Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller --- net/ethtool/ioctl.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/ethtool/ioctl.c b/net/ethtool/ioctl.c index aed2c2cf1623f..88f7cddf5a6fe 100644 --- a/net/ethtool/ioctl.c +++ b/net/ethtool/ioctl.c @@ -2096,8 +2096,8 @@ static int ethtool_get_ts_info(struct net_device *dev, void __user *useraddr) memset(&info, 0, sizeof(info)); info.cmd = ETHTOOL_GET_TS_INFO; - if (phydev && phydev->drv && phydev->drv->ts_info) { - err = phydev->drv->ts_info(phydev, &info); + if (phy_has_tsinfo(phydev)) { + err = phy_ts_info(phydev, &info); } else if (ops->get_ts_info) { err = ops->get_ts_info(dev, &info); } else { -- GitLab From bfd57b5900212acd5a14b9bea9ef49a5606d31a6 Mon Sep 17 00:00:00 2001 From: Richard Cochran Date: Wed, 25 Dec 2019 18:16:13 -0800 Subject: [PATCH 1333/1437] net: netcp_ethss: Use the PHY time stamping interface. The netcp_ethss driver tests fields of the phy_device in order to determine whether to defer to the PHY's time stamping functionality. This patch replaces the open coded logic with an invocation of the proper methods. Signed-off-by: Richard Cochran Reviewed-by: Andrew Lunn Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/ethernet/ti/netcp_ethss.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/drivers/net/ethernet/ti/netcp_ethss.c b/drivers/net/ethernet/ti/netcp_ethss.c index d6a192c1f3378..fb36115e9c510 100644 --- a/drivers/net/ethernet/ti/netcp_ethss.c +++ b/drivers/net/ethernet/ti/netcp_ethss.c @@ -2533,8 +2533,6 @@ static int gbe_del_vid(void *intf_priv, int vid) } #if IS_ENABLED(CONFIG_TI_CPTS) -#define HAS_PHY_TXTSTAMP(p) ((p)->drv && (p)->drv->txtstamp) -#define HAS_PHY_RXTSTAMP(p) ((p)->drv && (p)->drv->rxtstamp) static void gbe_txtstamp(void *context, struct sk_buff *skb) { @@ -2566,7 +2564,7 @@ static int gbe_txtstamp_mark_pkt(struct gbe_intf *gbe_intf, * We mark it here because skb_tx_timestamp() is called * after all the txhooks are called. */ - if (phydev && HAS_PHY_TXTSTAMP(phydev)) { + if (phy_has_txtstamp(phydev)) { skb_shinfo(p_info->skb)->tx_flags |= SKBTX_IN_PROGRESS; return 0; } @@ -2588,7 +2586,7 @@ static int gbe_rxtstamp(struct gbe_intf *gbe_intf, struct netcp_packet *p_info) if (p_info->rxtstamp_complete) return 0; - if (phydev && HAS_PHY_RXTSTAMP(phydev)) { + if (phy_has_rxtstamp(phydev)) { p_info->rxtstamp_complete = true; return 0; } @@ -2830,7 +2828,7 @@ static int gbe_ioctl(void *intf_priv, struct ifreq *req, int cmd) struct gbe_intf *gbe_intf = intf_priv; struct phy_device *phy = gbe_intf->slave->phy; - if (!phy || !phy->drv->hwtstamp) { + if (!phy_has_hwtstamp(phy)) { switch (cmd) { case SIOCGHWTSTAMP: return gbe_hwtstamp_get(gbe_intf, req); -- GitLab From 12d0efb9e6d90e6ff5c94e46d1ea273a41e36454 Mon Sep 17 00:00:00 2001 From: Richard Cochran Date: Wed, 25 Dec 2019 18:16:14 -0800 Subject: [PATCH 1334/1437] net: phy: dp83640: Move the probe and remove methods around. An upcoming patch will change how the PHY time stamping functions are registered with the networking stack, and adapting this driver would entail adding forward declarations for four time stamping methods. However, forward declarations are considered to be stylistic defects. This patch avoids the issue by moving the probe and remove methods immediately above the phy_driver interface structure. Signed-off-by: Richard Cochran Reviewed-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/phy/dp83640.c | 180 +++++++++++++++++++------------------- 1 file changed, 90 insertions(+), 90 deletions(-) diff --git a/drivers/net/phy/dp83640.c b/drivers/net/phy/dp83640.c index 8f241b57fcf65..b58abdb5491e2 100644 --- a/drivers/net/phy/dp83640.c +++ b/drivers/net/phy/dp83640.c @@ -1131,96 +1131,6 @@ static void dp83640_clock_put(struct dp83640_clock *clock) mutex_unlock(&clock->clock_lock); } -static int dp83640_probe(struct phy_device *phydev) -{ - struct dp83640_clock *clock; - struct dp83640_private *dp83640; - int err = -ENOMEM, i; - - if (phydev->mdio.addr == BROADCAST_ADDR) - return 0; - - clock = dp83640_clock_get_bus(phydev->mdio.bus); - if (!clock) - goto no_clock; - - dp83640 = kzalloc(sizeof(struct dp83640_private), GFP_KERNEL); - if (!dp83640) - goto no_memory; - - dp83640->phydev = phydev; - INIT_DELAYED_WORK(&dp83640->ts_work, rx_timestamp_work); - - INIT_LIST_HEAD(&dp83640->rxts); - INIT_LIST_HEAD(&dp83640->rxpool); - for (i = 0; i < MAX_RXTS; i++) - list_add(&dp83640->rx_pool_data[i].list, &dp83640->rxpool); - - phydev->priv = dp83640; - - spin_lock_init(&dp83640->rx_lock); - skb_queue_head_init(&dp83640->rx_queue); - skb_queue_head_init(&dp83640->tx_queue); - - dp83640->clock = clock; - - if (choose_this_phy(clock, phydev)) { - clock->chosen = dp83640; - clock->ptp_clock = ptp_clock_register(&clock->caps, - &phydev->mdio.dev); - if (IS_ERR(clock->ptp_clock)) { - err = PTR_ERR(clock->ptp_clock); - goto no_register; - } - } else - list_add_tail(&dp83640->list, &clock->phylist); - - dp83640_clock_put(clock); - return 0; - -no_register: - clock->chosen = NULL; - kfree(dp83640); -no_memory: - dp83640_clock_put(clock); -no_clock: - return err; -} - -static void dp83640_remove(struct phy_device *phydev) -{ - struct dp83640_clock *clock; - struct list_head *this, *next; - struct dp83640_private *tmp, *dp83640 = phydev->priv; - - if (phydev->mdio.addr == BROADCAST_ADDR) - return; - - enable_status_frames(phydev, false); - cancel_delayed_work_sync(&dp83640->ts_work); - - skb_queue_purge(&dp83640->rx_queue); - skb_queue_purge(&dp83640->tx_queue); - - clock = dp83640_clock_get(dp83640->clock); - - if (dp83640 == clock->chosen) { - ptp_clock_unregister(clock->ptp_clock); - clock->chosen = NULL; - } else { - list_for_each_safe(this, next, &clock->phylist) { - tmp = list_entry(this, struct dp83640_private, list); - if (tmp == dp83640) { - list_del_init(&tmp->list); - break; - } - } - } - - dp83640_clock_put(clock); - kfree(dp83640); -} - static int dp83640_soft_reset(struct phy_device *phydev) { int ret; @@ -1526,6 +1436,96 @@ static int dp83640_ts_info(struct phy_device *dev, struct ethtool_ts_info *info) return 0; } +static int dp83640_probe(struct phy_device *phydev) +{ + struct dp83640_clock *clock; + struct dp83640_private *dp83640; + int err = -ENOMEM, i; + + if (phydev->mdio.addr == BROADCAST_ADDR) + return 0; + + clock = dp83640_clock_get_bus(phydev->mdio.bus); + if (!clock) + goto no_clock; + + dp83640 = kzalloc(sizeof(struct dp83640_private), GFP_KERNEL); + if (!dp83640) + goto no_memory; + + dp83640->phydev = phydev; + INIT_DELAYED_WORK(&dp83640->ts_work, rx_timestamp_work); + + INIT_LIST_HEAD(&dp83640->rxts); + INIT_LIST_HEAD(&dp83640->rxpool); + for (i = 0; i < MAX_RXTS; i++) + list_add(&dp83640->rx_pool_data[i].list, &dp83640->rxpool); + + phydev->priv = dp83640; + + spin_lock_init(&dp83640->rx_lock); + skb_queue_head_init(&dp83640->rx_queue); + skb_queue_head_init(&dp83640->tx_queue); + + dp83640->clock = clock; + + if (choose_this_phy(clock, phydev)) { + clock->chosen = dp83640; + clock->ptp_clock = ptp_clock_register(&clock->caps, + &phydev->mdio.dev); + if (IS_ERR(clock->ptp_clock)) { + err = PTR_ERR(clock->ptp_clock); + goto no_register; + } + } else + list_add_tail(&dp83640->list, &clock->phylist); + + dp83640_clock_put(clock); + return 0; + +no_register: + clock->chosen = NULL; + kfree(dp83640); +no_memory: + dp83640_clock_put(clock); +no_clock: + return err; +} + +static void dp83640_remove(struct phy_device *phydev) +{ + struct dp83640_clock *clock; + struct list_head *this, *next; + struct dp83640_private *tmp, *dp83640 = phydev->priv; + + if (phydev->mdio.addr == BROADCAST_ADDR) + return; + + enable_status_frames(phydev, false); + cancel_delayed_work_sync(&dp83640->ts_work); + + skb_queue_purge(&dp83640->rx_queue); + skb_queue_purge(&dp83640->tx_queue); + + clock = dp83640_clock_get(dp83640->clock); + + if (dp83640 == clock->chosen) { + ptp_clock_unregister(clock->ptp_clock); + clock->chosen = NULL; + } else { + list_for_each_safe(this, next, &clock->phylist) { + tmp = list_entry(this, struct dp83640_private, list); + if (tmp == dp83640) { + list_del_init(&tmp->list); + break; + } + } + } + + dp83640_clock_put(clock); + kfree(dp83640); +} + static struct phy_driver dp83640_driver = { .phy_id = DP83640_PHY_ID, .phy_id_mask = 0xfffffff0, -- GitLab From 4715f65ffa0520af0680dbfbedbe349f175adaf4 Mon Sep 17 00:00:00 2001 From: Richard Cochran Date: Wed, 25 Dec 2019 18:16:15 -0800 Subject: [PATCH 1335/1437] net: Introduce a new MII time stamping interface. Currently the stack supports time stamping in PHY devices. However, there are newer, non-PHY devices that can snoop an MII bus and provide time stamps. In order to support such devices, this patch introduces a new interface to be used by both PHY and non-PHY devices. In addition, the one and only user of the old PHY time stamping API is converted to the new interface. Signed-off-by: Richard Cochran Reviewed-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/phy/dp83640.c | 39 +++++++++++++--------- drivers/net/phy/phy.c | 4 +-- drivers/net/phy/phy_device.c | 2 ++ include/linux/mii_timestamper.h | 58 +++++++++++++++++++++++++++++++++ include/linux/phy.h | 41 ++++++----------------- net/core/timestamping.c | 20 ++++++------ 6 files changed, 106 insertions(+), 58 deletions(-) create mode 100644 include/linux/mii_timestamper.h diff --git a/drivers/net/phy/dp83640.c b/drivers/net/phy/dp83640.c index b58abdb5491e2..ac72a324fcd14 100644 --- a/drivers/net/phy/dp83640.c +++ b/drivers/net/phy/dp83640.c @@ -98,6 +98,7 @@ struct dp83640_private { struct list_head list; struct dp83640_clock *clock; struct phy_device *phydev; + struct mii_timestamper mii_ts; struct delayed_work ts_work; int hwts_tx_en; int hwts_rx_en; @@ -1229,9 +1230,10 @@ static int dp83640_config_intr(struct phy_device *phydev) } } -static int dp83640_hwtstamp(struct phy_device *phydev, struct ifreq *ifr) +static int dp83640_hwtstamp(struct mii_timestamper *mii_ts, struct ifreq *ifr) { - struct dp83640_private *dp83640 = phydev->priv; + struct dp83640_private *dp83640 = + container_of(mii_ts, struct dp83640_private, mii_ts); struct hwtstamp_config cfg; u16 txcfg0, rxcfg0; @@ -1307,8 +1309,8 @@ static int dp83640_hwtstamp(struct phy_device *phydev, struct ifreq *ifr) mutex_lock(&dp83640->clock->extreg_lock); - ext_write(0, phydev, PAGE5, PTP_TXCFG0, txcfg0); - ext_write(0, phydev, PAGE5, PTP_RXCFG0, rxcfg0); + ext_write(0, dp83640->phydev, PAGE5, PTP_TXCFG0, txcfg0); + ext_write(0, dp83640->phydev, PAGE5, PTP_RXCFG0, rxcfg0); mutex_unlock(&dp83640->clock->extreg_lock); @@ -1338,10 +1340,11 @@ static void rx_timestamp_work(struct work_struct *work) schedule_delayed_work(&dp83640->ts_work, SKB_TIMESTAMP_TIMEOUT); } -static bool dp83640_rxtstamp(struct phy_device *phydev, +static bool dp83640_rxtstamp(struct mii_timestamper *mii_ts, struct sk_buff *skb, int type) { - struct dp83640_private *dp83640 = phydev->priv; + struct dp83640_private *dp83640 = + container_of(mii_ts, struct dp83640_private, mii_ts); struct dp83640_skb_info *skb_info = (struct dp83640_skb_info *)skb->cb; struct list_head *this, *next; struct rxts *rxts; @@ -1387,11 +1390,12 @@ static bool dp83640_rxtstamp(struct phy_device *phydev, return true; } -static void dp83640_txtstamp(struct phy_device *phydev, +static void dp83640_txtstamp(struct mii_timestamper *mii_ts, struct sk_buff *skb, int type) { struct dp83640_skb_info *skb_info = (struct dp83640_skb_info *)skb->cb; - struct dp83640_private *dp83640 = phydev->priv; + struct dp83640_private *dp83640 = + container_of(mii_ts, struct dp83640_private, mii_ts); switch (dp83640->hwts_tx_en) { @@ -1414,9 +1418,11 @@ static void dp83640_txtstamp(struct phy_device *phydev, } } -static int dp83640_ts_info(struct phy_device *dev, struct ethtool_ts_info *info) +static int dp83640_ts_info(struct mii_timestamper *mii_ts, + struct ethtool_ts_info *info) { - struct dp83640_private *dp83640 = dev->priv; + struct dp83640_private *dp83640 = + container_of(mii_ts, struct dp83640_private, mii_ts); info->so_timestamping = SOF_TIMESTAMPING_TX_HARDWARE | @@ -1454,13 +1460,18 @@ static int dp83640_probe(struct phy_device *phydev) goto no_memory; dp83640->phydev = phydev; - INIT_DELAYED_WORK(&dp83640->ts_work, rx_timestamp_work); + dp83640->mii_ts.rxtstamp = dp83640_rxtstamp; + dp83640->mii_ts.txtstamp = dp83640_txtstamp; + dp83640->mii_ts.hwtstamp = dp83640_hwtstamp; + dp83640->mii_ts.ts_info = dp83640_ts_info; + INIT_DELAYED_WORK(&dp83640->ts_work, rx_timestamp_work); INIT_LIST_HEAD(&dp83640->rxts); INIT_LIST_HEAD(&dp83640->rxpool); for (i = 0; i < MAX_RXTS; i++) list_add(&dp83640->rx_pool_data[i].list, &dp83640->rxpool); + phydev->mii_ts = &dp83640->mii_ts; phydev->priv = dp83640; spin_lock_init(&dp83640->rx_lock); @@ -1501,6 +1512,8 @@ static void dp83640_remove(struct phy_device *phydev) if (phydev->mdio.addr == BROADCAST_ADDR) return; + phydev->mii_ts = NULL; + enable_status_frames(phydev, false); cancel_delayed_work_sync(&dp83640->ts_work); @@ -1537,10 +1550,6 @@ static struct phy_driver dp83640_driver = { .config_init = dp83640_config_init, .ack_interrupt = dp83640_ack_interrupt, .config_intr = dp83640_config_intr, - .ts_info = dp83640_ts_info, - .hwtstamp = dp83640_hwtstamp, - .rxtstamp = dp83640_rxtstamp, - .txtstamp = dp83640_txtstamp, }; static int __init dp83640_init(void) diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index 80be4d691e5bd..541ed01496bfa 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -422,8 +422,8 @@ int phy_mii_ioctl(struct phy_device *phydev, struct ifreq *ifr, int cmd) return 0; case SIOCSHWTSTAMP: - if (phydev->drv && phydev->drv->hwtstamp) - return phydev->drv->hwtstamp(phydev, ifr); + if (phydev->mii_ts && phydev->mii_ts->hwtstamp) + return phydev->mii_ts->hwtstamp(phydev->mii_ts, ifr); /* fall through */ default: diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c index 94961e7592e47..e55119cd6c863 100644 --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c @@ -919,6 +919,8 @@ static void phy_link_change(struct phy_device *phydev, bool up, bool do_carrier) netif_carrier_off(netdev); } phydev->adjust_link(netdev); + if (phydev->mii_ts && phydev->mii_ts->link_state) + phydev->mii_ts->link_state(phydev->mii_ts, phydev); } /** diff --git a/include/linux/mii_timestamper.h b/include/linux/mii_timestamper.h new file mode 100644 index 0000000000000..36002386029ce --- /dev/null +++ b/include/linux/mii_timestamper.h @@ -0,0 +1,58 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Support for generic time stamping devices on MII buses. + * Copyright (C) 2018 Richard Cochran + */ +#ifndef _LINUX_MII_TIMESTAMPER_H +#define _LINUX_MII_TIMESTAMPER_H + +#include +#include +#include + +struct phy_device; + +/** + * struct mii_timestamper - Callback interface to MII time stamping devices. + * + * @rxtstamp: Requests a Rx timestamp for 'skb'. If the skb is accepted, + * the MII time stamping device promises to deliver it using + * netif_rx() as soon as a timestamp becomes available. One of + * the PTP_CLASS_ values is passed in 'type'. The function + * must return true if the skb is accepted for delivery. + * + * @txtstamp: Requests a Tx timestamp for 'skb'. The MII time stamping + * device promises to deliver it using skb_complete_tx_timestamp() + * as soon as a timestamp becomes available. One of the PTP_CLASS_ + * values is passed in 'type'. + * + * @hwtstamp: Handles SIOCSHWTSTAMP ioctl for hardware time stamping. + * + * @link_state: Allows the device to respond to changes in the link + * state. The caller invokes this function while holding + * the phy_device mutex. + * + * @ts_info: Handles ethtool queries for hardware time stamping. + * + * Drivers for PHY time stamping devices should embed their + * mii_timestamper within a private structure, obtaining a reference + * to it using container_of(). + */ +struct mii_timestamper { + bool (*rxtstamp)(struct mii_timestamper *mii_ts, + struct sk_buff *skb, int type); + + void (*txtstamp)(struct mii_timestamper *mii_ts, + struct sk_buff *skb, int type); + + int (*hwtstamp)(struct mii_timestamper *mii_ts, + struct ifreq *ifreq); + + void (*link_state)(struct mii_timestamper *mii_ts, + struct phy_device *phydev); + + int (*ts_info)(struct mii_timestamper *mii_ts, + struct ethtool_ts_info *ts_info); +}; + +#endif diff --git a/include/linux/phy.h b/include/linux/phy.h index 0248f5e9939de..30e599c454dbd 100644 --- a/include/linux/phy.h +++ b/include/linux/phy.h @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -441,6 +442,7 @@ struct phy_device { struct sfp_bus *sfp_bus; struct phylink *phylink; struct net_device *attached_dev; + struct mii_timestamper *mii_ts; u8 mdix; u8 mdix_ctrl; @@ -546,29 +548,6 @@ struct phy_driver { */ int (*match_phy_device)(struct phy_device *phydev); - /* Handles ethtool queries for hardware time stamping. */ - int (*ts_info)(struct phy_device *phydev, struct ethtool_ts_info *ti); - - /* Handles SIOCSHWTSTAMP ioctl for hardware time stamping. */ - int (*hwtstamp)(struct phy_device *phydev, struct ifreq *ifr); - - /* - * Requests a Rx timestamp for 'skb'. If the skb is accepted, - * the phy driver promises to deliver it using netif_rx() as - * soon as a timestamp becomes available. One of the - * PTP_CLASS_ values is passed in 'type'. The function must - * return true if the skb is accepted for delivery. - */ - bool (*rxtstamp)(struct phy_device *dev, struct sk_buff *skb, int type); - - /* - * Requests a Tx timestamp for 'skb'. The phy driver promises - * to deliver it using skb_complete_tx_timestamp() as soon as a - * timestamp becomes available. One of the PTP_CLASS_ values - * is passed in 'type'. - */ - void (*txtstamp)(struct phy_device *dev, struct sk_buff *skb, int type); - /* Some devices (e.g. qnap TS-119P II) require PHY register changes to * enable Wake on LAN, so set_wol is provided to be called in the * ethernet driver's set_wol function. */ @@ -942,7 +921,7 @@ static inline bool phy_polling_mode(struct phy_device *phydev) */ static inline bool phy_has_hwtstamp(struct phy_device *phydev) { - return phydev && phydev->drv && phydev->drv->hwtstamp; + return phydev && phydev->mii_ts && phydev->mii_ts->hwtstamp; } /** @@ -951,7 +930,7 @@ static inline bool phy_has_hwtstamp(struct phy_device *phydev) */ static inline bool phy_has_rxtstamp(struct phy_device *phydev) { - return phydev && phydev->drv && phydev->drv->rxtstamp; + return phydev && phydev->mii_ts && phydev->mii_ts->rxtstamp; } /** @@ -961,7 +940,7 @@ static inline bool phy_has_rxtstamp(struct phy_device *phydev) */ static inline bool phy_has_tsinfo(struct phy_device *phydev) { - return phydev && phydev->drv && phydev->drv->ts_info; + return phydev && phydev->mii_ts && phydev->mii_ts->ts_info; } /** @@ -970,30 +949,30 @@ static inline bool phy_has_tsinfo(struct phy_device *phydev) */ static inline bool phy_has_txtstamp(struct phy_device *phydev) { - return phydev && phydev->drv && phydev->drv->txtstamp; + return phydev && phydev->mii_ts && phydev->mii_ts->txtstamp; } static inline int phy_hwtstamp(struct phy_device *phydev, struct ifreq *ifr) { - return phydev->drv->hwtstamp(phydev, ifr); + return phydev->mii_ts->hwtstamp(phydev->mii_ts, ifr); } static inline bool phy_rxtstamp(struct phy_device *phydev, struct sk_buff *skb, int type) { - return phydev->drv->rxtstamp(phydev, skb, type); + return phydev->mii_ts->rxtstamp(phydev->mii_ts, skb, type); } static inline int phy_ts_info(struct phy_device *phydev, struct ethtool_ts_info *tsinfo) { - return phydev->drv->ts_info(phydev, tsinfo); + return phydev->mii_ts->ts_info(phydev->mii_ts, tsinfo); } static inline void phy_txtstamp(struct phy_device *phydev, struct sk_buff *skb, int type) { - phydev->drv->txtstamp(phydev, skb, type); + phydev->mii_ts->txtstamp(phydev->mii_ts, skb, type); } /** diff --git a/net/core/timestamping.c b/net/core/timestamping.c index 7911235706a97..04840697fe79f 100644 --- a/net/core/timestamping.c +++ b/net/core/timestamping.c @@ -13,7 +13,7 @@ static unsigned int classify(const struct sk_buff *skb) { if (likely(skb->dev && skb->dev->phydev && - skb->dev->phydev->drv)) + skb->dev->phydev->mii_ts)) return ptp_classify_raw(skb); else return PTP_CLASS_NONE; @@ -21,7 +21,7 @@ static unsigned int classify(const struct sk_buff *skb) void skb_clone_tx_timestamp(struct sk_buff *skb) { - struct phy_device *phydev; + struct mii_timestamper *mii_ts; struct sk_buff *clone; unsigned int type; @@ -32,22 +32,22 @@ void skb_clone_tx_timestamp(struct sk_buff *skb) if (type == PTP_CLASS_NONE) return; - phydev = skb->dev->phydev; - if (likely(phydev->drv->txtstamp)) { + mii_ts = skb->dev->phydev->mii_ts; + if (likely(mii_ts->txtstamp)) { clone = skb_clone_sk(skb); if (!clone) return; - phydev->drv->txtstamp(phydev, clone, type); + mii_ts->txtstamp(mii_ts, clone, type); } } EXPORT_SYMBOL_GPL(skb_clone_tx_timestamp); bool skb_defer_rx_timestamp(struct sk_buff *skb) { - struct phy_device *phydev; + struct mii_timestamper *mii_ts; unsigned int type; - if (!skb->dev || !skb->dev->phydev || !skb->dev->phydev->drv) + if (!skb->dev || !skb->dev->phydev || !skb->dev->phydev->mii_ts) return false; if (skb_headroom(skb) < ETH_HLEN) @@ -62,9 +62,9 @@ bool skb_defer_rx_timestamp(struct sk_buff *skb) if (type == PTP_CLASS_NONE) return false; - phydev = skb->dev->phydev; - if (likely(phydev->drv->rxtstamp)) - return phydev->drv->rxtstamp(phydev, skb, type); + mii_ts = skb->dev->phydev->mii_ts; + if (likely(mii_ts->rxtstamp)) + return mii_ts->rxtstamp(mii_ts, skb, type); return false; } -- GitLab From 767ff483731502a0fc34f34a3a0851aca175eb71 Mon Sep 17 00:00:00 2001 From: Richard Cochran Date: Wed, 25 Dec 2019 18:16:16 -0800 Subject: [PATCH 1336/1437] net: Add a layer for non-PHY MII time stamping drivers. While PHY time stamping drivers can simply attach their interface directly to the PHY instance, stand alone drivers require support in order to manage their services. Non-PHY MII time stamping drivers have a control interface over another bus like I2C, SPI, UART, or via a memory mapped peripheral. The controller device will be associated with one or more time stamping channels, each of which sits snoops in on a MII bus. This patch provides a glue layer that will enable time stamping channels to find their controlling device. Signed-off-by: Richard Cochran Reviewed-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/phy/Makefile | 2 + drivers/net/phy/mii_timestamper.c | 125 ++++++++++++++++++++++++++++++ include/linux/mii_timestamper.h | 63 +++++++++++++++ net/Kconfig | 7 +- 4 files changed, 194 insertions(+), 3 deletions(-) create mode 100644 drivers/net/phy/mii_timestamper.c diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile index d846b4dc1c688..fe5badf13b651 100644 --- a/drivers/net/phy/Makefile +++ b/drivers/net/phy/Makefile @@ -43,6 +43,8 @@ obj-$(CONFIG_MDIO_SUN4I) += mdio-sun4i.o obj-$(CONFIG_MDIO_THUNDER) += mdio-thunder.o obj-$(CONFIG_MDIO_XGENE) += mdio-xgene.o +obj-$(CONFIG_NETWORK_PHY_TIMESTAMPING) += mii_timestamper.o + obj-$(CONFIG_SFP) += sfp.o sfp-obj-$(CONFIG_SFP) += sfp-bus.o obj-y += $(sfp-obj-y) $(sfp-obj-m) diff --git a/drivers/net/phy/mii_timestamper.c b/drivers/net/phy/mii_timestamper.c new file mode 100644 index 0000000000000..2f12c5d901df6 --- /dev/null +++ b/drivers/net/phy/mii_timestamper.c @@ -0,0 +1,125 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Support for generic time stamping devices on MII buses. +// Copyright (C) 2018 Richard Cochran +// + +#include + +static LIST_HEAD(mii_timestamping_devices); +static DEFINE_MUTEX(tstamping_devices_lock); + +struct mii_timestamping_desc { + struct list_head list; + struct mii_timestamping_ctrl *ctrl; + struct device *device; +}; + +/** + * register_mii_tstamp_controller() - registers an MII time stamping device. + * + * @device: The device to be registered. + * @ctrl: Pointer to device's control interface. + * + * Returns zero on success or non-zero on failure. + */ +int register_mii_tstamp_controller(struct device *device, + struct mii_timestamping_ctrl *ctrl) +{ + struct mii_timestamping_desc *desc; + + desc = kzalloc(sizeof(*desc), GFP_KERNEL); + if (!desc) + return -ENOMEM; + + INIT_LIST_HEAD(&desc->list); + desc->ctrl = ctrl; + desc->device = device; + + mutex_lock(&tstamping_devices_lock); + list_add_tail(&mii_timestamping_devices, &desc->list); + mutex_unlock(&tstamping_devices_lock); + + return 0; +} +EXPORT_SYMBOL(register_mii_tstamp_controller); + +/** + * unregister_mii_tstamp_controller() - unregisters an MII time stamping device. + * + * @device: A device previously passed to register_mii_tstamp_controller(). + */ +void unregister_mii_tstamp_controller(struct device *device) +{ + struct mii_timestamping_desc *desc; + struct list_head *this, *next; + + mutex_lock(&tstamping_devices_lock); + list_for_each_safe(this, next, &mii_timestamping_devices) { + desc = list_entry(this, struct mii_timestamping_desc, list); + if (desc->device == device) { + list_del_init(&desc->list); + kfree(desc); + break; + } + } + mutex_unlock(&tstamping_devices_lock); +} +EXPORT_SYMBOL(unregister_mii_tstamp_controller); + +/** + * register_mii_timestamper - Enables a given port of an MII time stamper. + * + * @node: The device tree node of the MII time stamp controller. + * @port: The index of the port to be enabled. + * + * Returns a valid interface on success or ERR_PTR otherwise. + */ +struct mii_timestamper *register_mii_timestamper(struct device_node *node, + unsigned int port) +{ + struct mii_timestamper *mii_ts = NULL; + struct mii_timestamping_desc *desc; + struct list_head *this; + + mutex_lock(&tstamping_devices_lock); + list_for_each(this, &mii_timestamping_devices) { + desc = list_entry(this, struct mii_timestamping_desc, list); + if (desc->device->of_node == node) { + mii_ts = desc->ctrl->probe_channel(desc->device, port); + if (!IS_ERR(mii_ts)) { + mii_ts->device = desc->device; + get_device(desc->device); + } + break; + } + } + mutex_unlock(&tstamping_devices_lock); + + return mii_ts ? mii_ts : ERR_PTR(-EPROBE_DEFER); +} +EXPORT_SYMBOL(register_mii_timestamper); + +/** + * unregister_mii_timestamper - Disables a given MII time stamper. + * + * @mii_ts: An interface obtained via register_mii_timestamper(). + * + */ +void unregister_mii_timestamper(struct mii_timestamper *mii_ts) +{ + struct mii_timestamping_desc *desc; + struct list_head *this; + + mutex_lock(&tstamping_devices_lock); + list_for_each(this, &mii_timestamping_devices) { + desc = list_entry(this, struct mii_timestamping_desc, list); + if (desc->device == mii_ts->device) { + desc->ctrl->release_channel(desc->device, mii_ts); + put_device(desc->device); + break; + } + } + mutex_unlock(&tstamping_devices_lock); +} +EXPORT_SYMBOL(unregister_mii_timestamper); diff --git a/include/linux/mii_timestamper.h b/include/linux/mii_timestamper.h index 36002386029ce..fa940bbaf8ae4 100644 --- a/include/linux/mii_timestamper.h +++ b/include/linux/mii_timestamper.h @@ -33,10 +33,15 @@ struct phy_device; * the phy_device mutex. * * @ts_info: Handles ethtool queries for hardware time stamping. + * @device: Remembers the device to which the instance belongs. * * Drivers for PHY time stamping devices should embed their * mii_timestamper within a private structure, obtaining a reference * to it using container_of(). + * + * Drivers for non-PHY time stamping devices should return a pointer + * to a mii_timestamper from the probe_channel() callback of their + * mii_timestamping_ctrl interface. */ struct mii_timestamper { bool (*rxtstamp)(struct mii_timestamper *mii_ts, @@ -53,6 +58,64 @@ struct mii_timestamper { int (*ts_info)(struct mii_timestamper *mii_ts, struct ethtool_ts_info *ts_info); + + struct device *device; +}; + +/** + * struct mii_timestamping_ctrl - MII time stamping controller interface. + * + * @probe_channel: Callback into the controller driver announcing the + * presence of the 'port' channel. The 'device' field + * had been passed to register_mii_tstamp_controller(). + * The driver must return either a pointer to a valid + * MII timestamper instance or PTR_ERR. + * + * @release_channel: Releases an instance obtained via .probe_channel. + */ +struct mii_timestamping_ctrl { + struct mii_timestamper *(*probe_channel)(struct device *device, + unsigned int port); + void (*release_channel)(struct device *device, + struct mii_timestamper *mii_ts); }; +#ifdef CONFIG_NETWORK_PHY_TIMESTAMPING + +int register_mii_tstamp_controller(struct device *device, + struct mii_timestamping_ctrl *ctrl); + +void unregister_mii_tstamp_controller(struct device *device); + +struct mii_timestamper *register_mii_timestamper(struct device_node *node, + unsigned int port); + +void unregister_mii_timestamper(struct mii_timestamper *mii_ts); + +#else + +static inline +int register_mii_tstamp_controller(struct device *device, + struct mii_timestamping_ctrl *ctrl) +{ + return -EOPNOTSUPP; +} + +static inline void unregister_mii_tstamp_controller(struct device *device) +{ +} + +static inline +struct mii_timestamper *register_mii_timestamper(struct device_node *node, + unsigned int port) +{ + return NULL; +} + +static inline void unregister_mii_timestamper(struct mii_timestamper *mii_ts) +{ +} + +#endif + #endif diff --git a/net/Kconfig b/net/Kconfig index bd191f978a235..52af65e5d28c3 100644 --- a/net/Kconfig +++ b/net/Kconfig @@ -108,9 +108,10 @@ config NETWORK_PHY_TIMESTAMPING bool "Timestamping in PHY devices" select NET_PTP_CLASSIFY help - This allows timestamping of network packets by PHYs with - hardware timestamping capabilities. This option adds some - overhead in the transmit and receive paths. + This allows timestamping of network packets by PHYs (or + other MII bus snooping devices) with hardware timestamping + capabilities. This option adds some overhead in the transmit + and receive paths. If you are unsure how to answer this question, answer N. -- GitLab From 25d12e1dde28fafd2ac37afadd24252fa19b80cd Mon Sep 17 00:00:00 2001 From: Richard Cochran Date: Wed, 25 Dec 2019 18:16:17 -0800 Subject: [PATCH 1337/1437] dt-bindings: ptp: Introduce MII time stamping devices. This patch add a new binding that allows non-PHY MII time stamping devices to find their buses. The new documentation covers both the generic binding and one upcoming user. Signed-off-by: Richard Cochran Reviewed-by: Andrew Lunn Signed-off-by: David S. Miller --- .../devicetree/bindings/ptp/ptp-ines.txt | 35 ++++++++++++++++ .../devicetree/bindings/ptp/timestamper.txt | 42 +++++++++++++++++++ 2 files changed, 77 insertions(+) create mode 100644 Documentation/devicetree/bindings/ptp/ptp-ines.txt create mode 100644 Documentation/devicetree/bindings/ptp/timestamper.txt diff --git a/Documentation/devicetree/bindings/ptp/ptp-ines.txt b/Documentation/devicetree/bindings/ptp/ptp-ines.txt new file mode 100644 index 0000000000000..4c242bd1ce9c4 --- /dev/null +++ b/Documentation/devicetree/bindings/ptp/ptp-ines.txt @@ -0,0 +1,35 @@ +ZHAW InES PTP time stamping IP core + +The IP core needs two different kinds of nodes. The control node +lives somewhere in the memory map and specifies the address of the +control registers. There can be up to three port handles placed as +attributes of PHY nodes. These associate a particular MII bus with a +port index within the IP core. + +Required properties of the control node: + +- compatible: "ines,ptp-ctrl" +- reg: physical address and size of the register bank + +Required format of the port handle within the PHY node: + +- timestamper: provides control node reference and + the port channel within the IP core + +Example: + + tstamper: timestamper@60000000 { + compatible = "ines,ptp-ctrl"; + reg = <0x60000000 0x80>; + }; + + ethernet@80000000 { + ... + mdio { + ... + ethernet-phy@3 { + ... + timestamper = <&tstamper 0>; + }; + }; + }; diff --git a/Documentation/devicetree/bindings/ptp/timestamper.txt b/Documentation/devicetree/bindings/ptp/timestamper.txt new file mode 100644 index 0000000000000..fc550ce4d4ea1 --- /dev/null +++ b/Documentation/devicetree/bindings/ptp/timestamper.txt @@ -0,0 +1,42 @@ +Time stamps from MII bus snooping devices + +This binding supports non-PHY devices that snoop the MII bus and +provide time stamps. In contrast to PHY time stamping drivers (which +can simply attach their interface directly to the PHY instance), stand +alone MII time stamping drivers use this binding to specify the +connection between the snooping device and a given network interface. + +Non-PHY MII time stamping drivers typically talk to the control +interface over another bus like I2C, SPI, UART, or via a memory mapped +peripheral. This controller device is associated with one or more +time stamping channels, each of which snoops on a MII bus. + +The "timestamper" property lives in a phy node and links a time +stamping channel from the controller device to that phy's MII bus. + +Example: + + tstamper: timestamper@10000000 { + compatible = "ines,ptp-ctrl"; + reg = <0x10000000 0x80>; + }; + + ethernet@20000000 { + mdio { + ethernet-phy@1 { + timestamper = <&tstamper 0>; + }; + }; + }; + + ethernet@30000000 { + mdio { + ethernet-phy@2 { + timestamper = <&tstamper 1>; + }; + }; + }; + +In this example, time stamps from the MII bus attached to phy@1 will +appear on time stamp channel 0 (zero), and those from phy@2 appear on +channel 1. -- GitLab From 1dca22b1842136f57e9bd60fefd1715c22ff1874 Mon Sep 17 00:00:00 2001 From: Richard Cochran Date: Wed, 25 Dec 2019 18:16:18 -0800 Subject: [PATCH 1338/1437] net: mdio: of: Register discovered MII time stampers. When parsing a PHY node, register its time stamper, if any, and attach the instance to the PHY device. Signed-off-by: Richard Cochran Reviewed-by: Andrew Lunn Reviewed-by: Florian Fainelli Reviewed-by: Rob Herring Signed-off-by: David S. Miller --- drivers/net/phy/phy_device.c | 3 +++ drivers/of/of_mdio.c | 30 +++++++++++++++++++++++++++++- 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c index e55119cd6c863..e5dc9f87f495a 100644 --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c @@ -881,6 +881,9 @@ EXPORT_SYMBOL(phy_device_register); */ void phy_device_remove(struct phy_device *phydev) { + if (phydev->mii_ts) + unregister_mii_timestamper(phydev->mii_ts); + device_del(&phydev->mdio.dev); /* Assert the reset signal */ diff --git a/drivers/of/of_mdio.c b/drivers/of/of_mdio.c index fc757ef6eadc5..f5c2a54877610 100644 --- a/drivers/of/of_mdio.c +++ b/drivers/of/of_mdio.c @@ -42,14 +42,37 @@ static int of_get_phy_id(struct device_node *device, u32 *phy_id) return -EINVAL; } +static struct mii_timestamper *of_find_mii_timestamper(struct device_node *node) +{ + struct of_phandle_args arg; + int err; + + err = of_parse_phandle_with_fixed_args(node, "timestamper", 1, 0, &arg); + + if (err == -ENOENT) + return NULL; + else if (err) + return ERR_PTR(err); + + if (arg.args_count != 1) + return ERR_PTR(-EINVAL); + + return register_mii_timestamper(arg.np, arg.args[0]); +} + static int of_mdiobus_register_phy(struct mii_bus *mdio, struct device_node *child, u32 addr) { + struct mii_timestamper *mii_ts; struct phy_device *phy; bool is_c45; int rc; u32 phy_id; + mii_ts = of_find_mii_timestamper(child); + if (IS_ERR(mii_ts)) + return PTR_ERR(mii_ts); + is_c45 = of_device_is_compatible(child, "ethernet-phy-ieee802.3-c45"); @@ -57,11 +80,14 @@ static int of_mdiobus_register_phy(struct mii_bus *mdio, phy = phy_device_create(mdio, addr, phy_id, 0, NULL); else phy = get_phy_device(mdio, addr, is_c45); - if (IS_ERR(phy)) + if (IS_ERR(phy)) { + unregister_mii_timestamper(mii_ts); return PTR_ERR(phy); + } rc = of_irq_get(child, 0); if (rc == -EPROBE_DEFER) { + unregister_mii_timestamper(mii_ts); phy_device_free(phy); return rc; } @@ -90,10 +116,12 @@ static int of_mdiobus_register_phy(struct mii_bus *mdio, * register it */ rc = phy_device_register(phy); if (rc) { + unregister_mii_timestamper(mii_ts); phy_device_free(phy); of_node_put(child); return rc; } + phy->mii_ts = mii_ts; dev_dbg(&mdio->dev, "registered phy %pOFn at address %i\n", child, addr); -- GitLab From b6fd7b96366769651ab23988607ce9c5c9042cdb Mon Sep 17 00:00:00 2001 From: Richard Cochran Date: Wed, 25 Dec 2019 18:16:19 -0800 Subject: [PATCH 1339/1437] net: Introduce peer to peer one step PTP time stamping. The 1588 standard defines one step operation for both Sync and PDelay_Resp messages. Up until now, hardware with P2P one step has been rare, and kernel support was lacking. This patch adds support of the mode in anticipation of new hardware developments. Signed-off-by: Richard Cochran Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c | 1 + drivers/net/ethernet/mellanox/mlxsw/spectrum_ptp.c | 1 + drivers/net/ethernet/microchip/lan743x_ptp.c | 3 +++ drivers/net/ethernet/qlogic/qede/qede_ptp.c | 1 + include/uapi/linux/net_tstamp.h | 8 ++++++++ net/core/dev_ioctl.c | 1 + 6 files changed, 15 insertions(+) diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c index cff64e43bdd80..741d865e4afc6 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c @@ -15410,6 +15410,7 @@ int bnx2x_configure_ptp_filters(struct bnx2x *bp) REG_WR(bp, rule, BNX2X_PTP_TX_ON_RULE_MASK); break; case HWTSTAMP_TX_ONESTEP_SYNC: + case HWTSTAMP_TX_ONESTEP_P2P: BNX2X_ERR("One-step timestamping is not supported\n"); return -ERANGE; } diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_ptp.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_ptp.c index ec2ff3d7f41c4..4aaaa4937b1a7 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_ptp.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_ptp.c @@ -920,6 +920,7 @@ static int mlxsw_sp_ptp_get_message_types(const struct hwtstamp_config *config, egr_types = 0xff; break; case HWTSTAMP_TX_ONESTEP_SYNC: + case HWTSTAMP_TX_ONESTEP_P2P: return -ERANGE; } diff --git a/drivers/net/ethernet/microchip/lan743x_ptp.c b/drivers/net/ethernet/microchip/lan743x_ptp.c index afe52463dc576..9399f6a987487 100644 --- a/drivers/net/ethernet/microchip/lan743x_ptp.c +++ b/drivers/net/ethernet/microchip/lan743x_ptp.c @@ -1265,6 +1265,9 @@ int lan743x_ptp_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd) lan743x_ptp_set_sync_ts_insert(adapter, true); break; + case HWTSTAMP_TX_ONESTEP_P2P: + ret = -ERANGE; + break; default: netif_warn(adapter, drv, adapter->netdev, " tx_type = %d, UNKNOWN\n", config.tx_type); diff --git a/drivers/net/ethernet/qlogic/qede/qede_ptp.c b/drivers/net/ethernet/qlogic/qede/qede_ptp.c index f815435cf1061..4c7f7a7fc1515 100644 --- a/drivers/net/ethernet/qlogic/qede/qede_ptp.c +++ b/drivers/net/ethernet/qlogic/qede/qede_ptp.c @@ -247,6 +247,7 @@ static int qede_ptp_cfg_filters(struct qede_dev *edev) break; case HWTSTAMP_TX_ONESTEP_SYNC: + case HWTSTAMP_TX_ONESTEP_P2P: DP_ERR(edev, "One-step timestamping is not supported\n"); return -ERANGE; } diff --git a/include/uapi/linux/net_tstamp.h b/include/uapi/linux/net_tstamp.h index e5b39721c6e48..f96e650d0af99 100644 --- a/include/uapi/linux/net_tstamp.h +++ b/include/uapi/linux/net_tstamp.h @@ -90,6 +90,14 @@ enum hwtstamp_tx_types { * queue. */ HWTSTAMP_TX_ONESTEP_SYNC, + + /* + * Same as HWTSTAMP_TX_ONESTEP_SYNC, but also enables time + * stamp insertion directly into PDelay_Resp packets. In this + * case, neither transmitted Sync nor PDelay_Resp packets will + * receive a time stamp via the socket error queue. + */ + HWTSTAMP_TX_ONESTEP_P2P, }; /* possible values for hwtstamp_config->rx_filter */ diff --git a/net/core/dev_ioctl.c b/net/core/dev_ioctl.c index 5163d900bb4fb..dbaebbe573f00 100644 --- a/net/core/dev_ioctl.c +++ b/net/core/dev_ioctl.c @@ -187,6 +187,7 @@ static int net_hwtstamp_validate(struct ifreq *ifr) case HWTSTAMP_TX_OFF: case HWTSTAMP_TX_ON: case HWTSTAMP_TX_ONESTEP_SYNC: + case HWTSTAMP_TX_ONESTEP_P2P: tx_type_valid = 1; break; } -- GitLab From bad1eaa6ac312ffd7aa46dd5a4d9843b824aa023 Mon Sep 17 00:00:00 2001 From: Richard Cochran Date: Wed, 25 Dec 2019 18:16:20 -0800 Subject: [PATCH 1340/1437] ptp: Add a driver for InES time stamping IP core. The InES at the ZHAW offers a PTP time stamping IP core. The FPGA logic recognizes and time stamps PTP frames on the MII bus. This patch adds a driver for the core along with a device tree binding to allow hooking the driver to MII buses. Signed-off-by: Richard Cochran Signed-off-by: David S. Miller --- drivers/ptp/Kconfig | 10 + drivers/ptp/Makefile | 1 + drivers/ptp/ptp_ines.c | 852 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 863 insertions(+) create mode 100644 drivers/ptp/ptp_ines.c diff --git a/drivers/ptp/Kconfig b/drivers/ptp/Kconfig index b0d1b8d264fa5..c382158f587da 100644 --- a/drivers/ptp/Kconfig +++ b/drivers/ptp/Kconfig @@ -89,6 +89,16 @@ config DP83640_PHY In order for this to work, your MAC driver must also implement the skb_tx_timestamp() function. +config PTP_1588_CLOCK_INES + tristate "ZHAW InES PTP time stamping IP core" + depends on NETWORK_PHY_TIMESTAMPING + depends on PHYLIB + depends on PTP_1588_CLOCK + help + This driver adds support for using the ZHAW InES 1588 IP + core. This clock is only useful if the MII bus of your MAC + is wired up to the core. + config PTP_1588_CLOCK_PCH tristate "Intel PCH EG20T as PTP clock" depends on X86_32 || COMPILE_TEST diff --git a/drivers/ptp/Makefile b/drivers/ptp/Makefile index 69a06f86a4504..3fb91bebbaf7b 100644 --- a/drivers/ptp/Makefile +++ b/drivers/ptp/Makefile @@ -6,6 +6,7 @@ ptp-y := ptp_clock.o ptp_chardev.o ptp_sysfs.o obj-$(CONFIG_PTP_1588_CLOCK) += ptp.o obj-$(CONFIG_PTP_1588_CLOCK_DTE) += ptp_dte.o +obj-$(CONFIG_PTP_1588_CLOCK_INES) += ptp_ines.o obj-$(CONFIG_PTP_1588_CLOCK_IXP46X) += ptp_ixp46x.o obj-$(CONFIG_PTP_1588_CLOCK_PCH) += ptp_pch.o obj-$(CONFIG_PTP_1588_CLOCK_KVM) += ptp_kvm.o diff --git a/drivers/ptp/ptp_ines.c b/drivers/ptp/ptp_ines.c new file mode 100644 index 0000000000000..dfda54cbd8663 --- /dev/null +++ b/drivers/ptp/ptp_ines.c @@ -0,0 +1,852 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Copyright (C) 2018 MOSER-BAER AG +// + +#define pr_fmt(fmt) "InES_PTP: " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_DESCRIPTION("Driver for the ZHAW InES PTP time stamping IP core"); +MODULE_AUTHOR("Richard Cochran "); +MODULE_VERSION("1.0"); +MODULE_LICENSE("GPL"); + +/* GLOBAL register */ +#define MCAST_MAC_SELECT_SHIFT 2 +#define MCAST_MAC_SELECT_MASK 0x3 +#define IO_RESET BIT(1) +#define PTP_RESET BIT(0) + +/* VERSION register */ +#define IF_MAJOR_VER_SHIFT 12 +#define IF_MAJOR_VER_MASK 0xf +#define IF_MINOR_VER_SHIFT 8 +#define IF_MINOR_VER_MASK 0xf +#define FPGA_MAJOR_VER_SHIFT 4 +#define FPGA_MAJOR_VER_MASK 0xf +#define FPGA_MINOR_VER_SHIFT 0 +#define FPGA_MINOR_VER_MASK 0xf + +/* INT_STAT register */ +#define RX_INTR_STATUS_3 BIT(5) +#define RX_INTR_STATUS_2 BIT(4) +#define RX_INTR_STATUS_1 BIT(3) +#define TX_INTR_STATUS_3 BIT(2) +#define TX_INTR_STATUS_2 BIT(1) +#define TX_INTR_STATUS_1 BIT(0) + +/* INT_MSK register */ +#define RX_INTR_MASK_3 BIT(5) +#define RX_INTR_MASK_2 BIT(4) +#define RX_INTR_MASK_1 BIT(3) +#define TX_INTR_MASK_3 BIT(2) +#define TX_INTR_MASK_2 BIT(1) +#define TX_INTR_MASK_1 BIT(0) + +/* BUF_STAT register */ +#define RX_FIFO_NE_3 BIT(5) +#define RX_FIFO_NE_2 BIT(4) +#define RX_FIFO_NE_1 BIT(3) +#define TX_FIFO_NE_3 BIT(2) +#define TX_FIFO_NE_2 BIT(1) +#define TX_FIFO_NE_1 BIT(0) + +/* PORT_CONF register */ +#define CM_ONE_STEP BIT(6) +#define PHY_SPEED_SHIFT 4 +#define PHY_SPEED_MASK 0x3 +#define P2P_DELAY_WR_POS_SHIFT 2 +#define P2P_DELAY_WR_POS_MASK 0x3 +#define PTP_MODE_SHIFT 0 +#define PTP_MODE_MASK 0x3 + +/* TS_STAT_TX register */ +#define TS_ENABLE BIT(15) +#define DATA_READ_POS_SHIFT 8 +#define DATA_READ_POS_MASK 0x1f +#define DISCARDED_EVENTS_SHIFT 4 +#define DISCARDED_EVENTS_MASK 0xf + +#define INES_N_PORTS 3 +#define INES_REGISTER_SIZE 0x80 +#define INES_PORT_OFFSET 0x20 +#define INES_PORT_SIZE 0x20 +#define INES_FIFO_DEPTH 90 +#define INES_MAX_EVENTS 100 + +#define BC_PTP_V1 0 +#define BC_PTP_V2 1 +#define TC_E2E_PTP_V2 2 +#define TC_P2P_PTP_V2 3 + +#define OFF_PTP_CLOCK_ID 20 +#define OFF_PTP_PORT_NUM 28 + +#define PHY_SPEED_10 0 +#define PHY_SPEED_100 1 +#define PHY_SPEED_1000 2 + +#define PORT_CONF \ + ((PHY_SPEED_1000 << PHY_SPEED_SHIFT) | (BC_PTP_V2 << PTP_MODE_SHIFT)) + +#define ines_read32(s, r) __raw_readl((void __iomem *)&s->regs->r) +#define ines_write32(s, v, r) __raw_writel(v, (void __iomem *)&s->regs->r) + +#define MESSAGE_TYPE_SYNC 1 +#define MESSAGE_TYPE_P_DELAY_REQ 2 +#define MESSAGE_TYPE_P_DELAY_RESP 3 +#define MESSAGE_TYPE_DELAY_REQ 4 + +#define SYNC 0x0 +#define DELAY_REQ 0x1 +#define PDELAY_REQ 0x2 +#define PDELAY_RESP 0x3 + +static LIST_HEAD(ines_clocks); +static DEFINE_MUTEX(ines_clocks_lock); + +struct ines_global_regs { + u32 id; + u32 test; + u32 global; + u32 version; + u32 test2; + u32 int_stat; + u32 int_msk; + u32 buf_stat; +}; + +struct ines_port_registers { + u32 port_conf; + u32 p_delay; + u32 ts_stat_tx; + u32 ts_stat_rx; + u32 ts_tx; + u32 ts_rx; +}; + +struct ines_timestamp { + struct list_head list; + unsigned long tmo; + u16 tag; + u64 sec; + u64 nsec; + u64 clkid; + u16 portnum; + u16 seqid; +}; + +struct ines_port { + struct ines_port_registers *regs; + struct mii_timestamper mii_ts; + struct ines_clock *clock; + bool rxts_enabled; + bool txts_enabled; + unsigned int index; + struct delayed_work ts_work; + /* lock protects event list and tx_skb */ + spinlock_t lock; + struct sk_buff *tx_skb; + struct list_head events; + struct list_head pool; + struct ines_timestamp pool_data[INES_MAX_EVENTS]; +}; + +struct ines_clock { + struct ines_port port[INES_N_PORTS]; + struct ines_global_regs __iomem *regs; + void __iomem *base; + struct device_node *node; + struct device *dev; + struct list_head list; +}; + +static bool ines_match(struct sk_buff *skb, unsigned int ptp_class, + struct ines_timestamp *ts, struct device *dev); +static int ines_rxfifo_read(struct ines_port *port); +static u64 ines_rxts64(struct ines_port *port, unsigned int words); +static bool ines_timestamp_expired(struct ines_timestamp *ts); +static u64 ines_txts64(struct ines_port *port, unsigned int words); +static void ines_txtstamp_work(struct work_struct *work); +static bool is_sync_pdelay_resp(struct sk_buff *skb, int type); +static u8 tag_to_msgtype(u8 tag); + +static void ines_clock_cleanup(struct ines_clock *clock) +{ + struct ines_port *port; + int i; + + for (i = 0; i < INES_N_PORTS; i++) { + port = &clock->port[i]; + cancel_delayed_work_sync(&port->ts_work); + } +} + +static int ines_clock_init(struct ines_clock *clock, struct device *device, + void __iomem *addr) +{ + struct device_node *node = device->of_node; + unsigned long port_addr; + struct ines_port *port; + int i, j; + + INIT_LIST_HEAD(&clock->list); + clock->node = node; + clock->dev = device; + clock->base = addr; + clock->regs = clock->base; + + for (i = 0; i < INES_N_PORTS; i++) { + port = &clock->port[i]; + port_addr = (unsigned long) clock->base + + INES_PORT_OFFSET + i * INES_PORT_SIZE; + port->regs = (struct ines_port_registers *) port_addr; + port->clock = clock; + port->index = i; + INIT_DELAYED_WORK(&port->ts_work, ines_txtstamp_work); + spin_lock_init(&port->lock); + INIT_LIST_HEAD(&port->events); + INIT_LIST_HEAD(&port->pool); + for (j = 0; j < INES_MAX_EVENTS; j++) + list_add(&port->pool_data[j].list, &port->pool); + } + + ines_write32(clock, 0xBEEF, test); + ines_write32(clock, 0xBEEF, test2); + + dev_dbg(device, "ID 0x%x\n", ines_read32(clock, id)); + dev_dbg(device, "TEST 0x%x\n", ines_read32(clock, test)); + dev_dbg(device, "VERSION 0x%x\n", ines_read32(clock, version)); + dev_dbg(device, "TEST2 0x%x\n", ines_read32(clock, test2)); + + for (i = 0; i < INES_N_PORTS; i++) { + port = &clock->port[i]; + ines_write32(port, PORT_CONF, port_conf); + } + + return 0; +} + +static struct ines_port *ines_find_port(struct device_node *node, u32 index) +{ + struct ines_port *port = NULL; + struct ines_clock *clock; + struct list_head *this; + + mutex_lock(&ines_clocks_lock); + list_for_each(this, &ines_clocks) { + clock = list_entry(this, struct ines_clock, list); + if (clock->node == node) { + port = &clock->port[index]; + break; + } + } + mutex_unlock(&ines_clocks_lock); + return port; +} + +static u64 ines_find_rxts(struct ines_port *port, struct sk_buff *skb, int type) +{ + struct list_head *this, *next; + struct ines_timestamp *ts; + unsigned long flags; + u64 ns = 0; + + if (type == PTP_CLASS_NONE) + return 0; + + spin_lock_irqsave(&port->lock, flags); + ines_rxfifo_read(port); + list_for_each_safe(this, next, &port->events) { + ts = list_entry(this, struct ines_timestamp, list); + if (ines_timestamp_expired(ts)) { + list_del_init(&ts->list); + list_add(&ts->list, &port->pool); + continue; + } + if (ines_match(skb, type, ts, port->clock->dev)) { + ns = ts->sec * 1000000000ULL + ts->nsec; + list_del_init(&ts->list); + list_add(&ts->list, &port->pool); + break; + } + } + spin_unlock_irqrestore(&port->lock, flags); + + return ns; +} + +static u64 ines_find_txts(struct ines_port *port, struct sk_buff *skb) +{ + unsigned int class = ptp_classify_raw(skb), i; + u32 data_rd_pos, buf_stat, mask, ts_stat_tx; + struct ines_timestamp ts; + unsigned long flags; + u64 ns = 0; + + mask = TX_FIFO_NE_1 << port->index; + + spin_lock_irqsave(&port->lock, flags); + + for (i = 0; i < INES_FIFO_DEPTH; i++) { + + buf_stat = ines_read32(port->clock, buf_stat); + if (!(buf_stat & mask)) { + dev_dbg(port->clock->dev, + "Tx timestamp FIFO unexpectedly empty\n"); + break; + } + ts_stat_tx = ines_read32(port, ts_stat_tx); + data_rd_pos = (ts_stat_tx >> DATA_READ_POS_SHIFT) & + DATA_READ_POS_MASK; + if (data_rd_pos) { + dev_err(port->clock->dev, + "unexpected Tx read pos %u\n", data_rd_pos); + break; + } + + ts.tag = ines_read32(port, ts_tx); + ts.sec = ines_txts64(port, 3); + ts.nsec = ines_txts64(port, 2); + ts.clkid = ines_txts64(port, 4); + ts.portnum = ines_read32(port, ts_tx); + ts.seqid = ines_read32(port, ts_tx); + + if (ines_match(skb, class, &ts, port->clock->dev)) { + ns = ts.sec * 1000000000ULL + ts.nsec; + break; + } + } + + spin_unlock_irqrestore(&port->lock, flags); + return ns; +} + +static int ines_hwtstamp(struct mii_timestamper *mii_ts, struct ifreq *ifr) +{ + struct ines_port *port = container_of(mii_ts, struct ines_port, mii_ts); + u32 cm_one_step = 0, port_conf, ts_stat_rx, ts_stat_tx; + struct hwtstamp_config cfg; + unsigned long flags; + + if (copy_from_user(&cfg, ifr->ifr_data, sizeof(cfg))) + return -EFAULT; + + /* reserved for future extensions */ + if (cfg.flags) + return -EINVAL; + + switch (cfg.tx_type) { + case HWTSTAMP_TX_OFF: + ts_stat_tx = 0; + break; + case HWTSTAMP_TX_ON: + ts_stat_tx = TS_ENABLE; + break; + case HWTSTAMP_TX_ONESTEP_P2P: + ts_stat_tx = TS_ENABLE; + cm_one_step = CM_ONE_STEP; + break; + default: + return -ERANGE; + } + + switch (cfg.rx_filter) { + case HWTSTAMP_FILTER_NONE: + ts_stat_rx = 0; + break; + case HWTSTAMP_FILTER_ALL: + case HWTSTAMP_FILTER_PTP_V1_L4_EVENT: + case HWTSTAMP_FILTER_PTP_V1_L4_SYNC: + case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ: + return -ERANGE; + case HWTSTAMP_FILTER_PTP_V2_L4_EVENT: + case HWTSTAMP_FILTER_PTP_V2_L4_SYNC: + case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ: + case HWTSTAMP_FILTER_PTP_V2_L2_EVENT: + case HWTSTAMP_FILTER_PTP_V2_L2_SYNC: + case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ: + case HWTSTAMP_FILTER_PTP_V2_EVENT: + case HWTSTAMP_FILTER_PTP_V2_SYNC: + case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ: + ts_stat_rx = TS_ENABLE; + cfg.rx_filter = HWTSTAMP_FILTER_PTP_V2_EVENT; + break; + default: + return -ERANGE; + } + + spin_lock_irqsave(&port->lock, flags); + + port_conf = ines_read32(port, port_conf); + port_conf &= ~CM_ONE_STEP; + port_conf |= cm_one_step; + + ines_write32(port, port_conf, port_conf); + ines_write32(port, ts_stat_rx, ts_stat_rx); + ines_write32(port, ts_stat_tx, ts_stat_tx); + + port->rxts_enabled = ts_stat_rx == TS_ENABLE ? true : false; + port->txts_enabled = ts_stat_tx == TS_ENABLE ? true : false; + + spin_unlock_irqrestore(&port->lock, flags); + + return copy_to_user(ifr->ifr_data, &cfg, sizeof(cfg)) ? -EFAULT : 0; +} + +static void ines_link_state(struct mii_timestamper *mii_ts, + struct phy_device *phydev) +{ + struct ines_port *port = container_of(mii_ts, struct ines_port, mii_ts); + u32 port_conf, speed_conf; + unsigned long flags; + + switch (phydev->speed) { + case SPEED_10: + speed_conf = PHY_SPEED_10 << PHY_SPEED_SHIFT; + break; + case SPEED_100: + speed_conf = PHY_SPEED_100 << PHY_SPEED_SHIFT; + break; + case SPEED_1000: + speed_conf = PHY_SPEED_1000 << PHY_SPEED_SHIFT; + break; + default: + dev_err(port->clock->dev, "bad speed: %d\n", phydev->speed); + return; + } + spin_lock_irqsave(&port->lock, flags); + + port_conf = ines_read32(port, port_conf); + port_conf &= ~(0x3 << PHY_SPEED_SHIFT); + port_conf |= speed_conf; + + ines_write32(port, port_conf, port_conf); + + spin_unlock_irqrestore(&port->lock, flags); +} + +static bool ines_match(struct sk_buff *skb, unsigned int ptp_class, + struct ines_timestamp *ts, struct device *dev) +{ + u8 *msgtype, *data = skb_mac_header(skb); + unsigned int offset = 0; + __be16 *portn, *seqid; + __be64 *clkid; + + if (unlikely(ptp_class & PTP_CLASS_V1)) + return false; + + if (ptp_class & PTP_CLASS_VLAN) + offset += VLAN_HLEN; + + switch (ptp_class & PTP_CLASS_PMASK) { + case PTP_CLASS_IPV4: + offset += ETH_HLEN + IPV4_HLEN(data + offset) + UDP_HLEN; + break; + case PTP_CLASS_IPV6: + offset += ETH_HLEN + IP6_HLEN + UDP_HLEN; + break; + case PTP_CLASS_L2: + offset += ETH_HLEN; + break; + default: + return false; + } + + if (skb->len + ETH_HLEN < offset + OFF_PTP_SEQUENCE_ID + sizeof(*seqid)) + return false; + + msgtype = data + offset; + clkid = (__be64 *)(data + offset + OFF_PTP_CLOCK_ID); + portn = (__be16 *)(data + offset + OFF_PTP_PORT_NUM); + seqid = (__be16 *)(data + offset + OFF_PTP_SEQUENCE_ID); + + if (tag_to_msgtype(ts->tag & 0x7) != (*msgtype & 0xf)) { + dev_dbg(dev, "msgtype mismatch ts %hhu != skb %hhu\n", + tag_to_msgtype(ts->tag & 0x7), *msgtype & 0xf); + return false; + } + if (cpu_to_be64(ts->clkid) != *clkid) { + dev_dbg(dev, "clkid mismatch ts %llx != skb %llx\n", + cpu_to_be64(ts->clkid), *clkid); + return false; + } + if (ts->portnum != ntohs(*portn)) { + dev_dbg(dev, "portn mismatch ts %hu != skb %hu\n", + ts->portnum, ntohs(*portn)); + return false; + } + if (ts->seqid != ntohs(*seqid)) { + dev_dbg(dev, "seqid mismatch ts %hu != skb %hu\n", + ts->seqid, ntohs(*seqid)); + return false; + } + + return true; +} + +static bool ines_rxtstamp(struct mii_timestamper *mii_ts, + struct sk_buff *skb, int type) +{ + struct ines_port *port = container_of(mii_ts, struct ines_port, mii_ts); + struct skb_shared_hwtstamps *ssh; + u64 ns; + + if (!port->rxts_enabled) + return false; + + ns = ines_find_rxts(port, skb, type); + if (!ns) + return false; + + ssh = skb_hwtstamps(skb); + ssh->hwtstamp = ns_to_ktime(ns); + netif_rx(skb); + + return true; +} + +static int ines_rxfifo_read(struct ines_port *port) +{ + u32 data_rd_pos, buf_stat, mask, ts_stat_rx; + struct ines_timestamp *ts; + unsigned int i; + + mask = RX_FIFO_NE_1 << port->index; + + for (i = 0; i < INES_FIFO_DEPTH; i++) { + if (list_empty(&port->pool)) { + dev_err(port->clock->dev, "event pool is empty\n"); + return -1; + } + buf_stat = ines_read32(port->clock, buf_stat); + if (!(buf_stat & mask)) + break; + + ts_stat_rx = ines_read32(port, ts_stat_rx); + data_rd_pos = (ts_stat_rx >> DATA_READ_POS_SHIFT) & + DATA_READ_POS_MASK; + if (data_rd_pos) { + dev_err(port->clock->dev, "unexpected Rx read pos %u\n", + data_rd_pos); + break; + } + + ts = list_first_entry(&port->pool, struct ines_timestamp, list); + ts->tmo = jiffies + HZ; + ts->tag = ines_read32(port, ts_rx); + ts->sec = ines_rxts64(port, 3); + ts->nsec = ines_rxts64(port, 2); + ts->clkid = ines_rxts64(port, 4); + ts->portnum = ines_read32(port, ts_rx); + ts->seqid = ines_read32(port, ts_rx); + + list_del_init(&ts->list); + list_add_tail(&ts->list, &port->events); + } + + return 0; +} + +static u64 ines_rxts64(struct ines_port *port, unsigned int words) +{ + unsigned int i; + u64 result; + u16 word; + + word = ines_read32(port, ts_rx); + result = word; + words--; + for (i = 0; i < words; i++) { + word = ines_read32(port, ts_rx); + result <<= 16; + result |= word; + } + return result; +} + +static bool ines_timestamp_expired(struct ines_timestamp *ts) +{ + return time_after(jiffies, ts->tmo); +} + +static int ines_ts_info(struct mii_timestamper *mii_ts, + struct ethtool_ts_info *info) +{ + info->so_timestamping = + SOF_TIMESTAMPING_TX_HARDWARE | + SOF_TIMESTAMPING_TX_SOFTWARE | + SOF_TIMESTAMPING_RX_HARDWARE | + SOF_TIMESTAMPING_RX_SOFTWARE | + SOF_TIMESTAMPING_SOFTWARE | + SOF_TIMESTAMPING_RAW_HARDWARE; + + info->phc_index = -1; + + info->tx_types = + (1 << HWTSTAMP_TX_OFF) | + (1 << HWTSTAMP_TX_ON) | + (1 << HWTSTAMP_TX_ONESTEP_P2P); + + info->rx_filters = + (1 << HWTSTAMP_FILTER_NONE) | + (1 << HWTSTAMP_FILTER_PTP_V2_EVENT); + + return 0; +} + +static u64 ines_txts64(struct ines_port *port, unsigned int words) +{ + unsigned int i; + u64 result; + u16 word; + + word = ines_read32(port, ts_tx); + result = word; + words--; + for (i = 0; i < words; i++) { + word = ines_read32(port, ts_tx); + result <<= 16; + result |= word; + } + return result; +} + +static bool ines_txts_onestep(struct ines_port *port, struct sk_buff *skb, int type) +{ + unsigned long flags; + u32 port_conf; + + spin_lock_irqsave(&port->lock, flags); + port_conf = ines_read32(port, port_conf); + spin_unlock_irqrestore(&port->lock, flags); + + if (port_conf & CM_ONE_STEP) + return is_sync_pdelay_resp(skb, type); + + return false; +} + +static void ines_txtstamp(struct mii_timestamper *mii_ts, + struct sk_buff *skb, int type) +{ + struct ines_port *port = container_of(mii_ts, struct ines_port, mii_ts); + struct sk_buff *old_skb = NULL; + unsigned long flags; + + if (!port->txts_enabled || ines_txts_onestep(port, skb, type)) { + kfree_skb(skb); + return; + } + + spin_lock_irqsave(&port->lock, flags); + + if (port->tx_skb) + old_skb = port->tx_skb; + + port->tx_skb = skb; + + spin_unlock_irqrestore(&port->lock, flags); + + if (old_skb) + kfree_skb(old_skb); + + schedule_delayed_work(&port->ts_work, 1); +} + +static void ines_txtstamp_work(struct work_struct *work) +{ + struct ines_port *port = + container_of(work, struct ines_port, ts_work.work); + struct skb_shared_hwtstamps ssh; + struct sk_buff *skb; + unsigned long flags; + u64 ns; + + spin_lock_irqsave(&port->lock, flags); + skb = port->tx_skb; + port->tx_skb = NULL; + spin_unlock_irqrestore(&port->lock, flags); + + ns = ines_find_txts(port, skb); + if (!ns) { + kfree_skb(skb); + return; + } + ssh.hwtstamp = ns_to_ktime(ns); + skb_complete_tx_timestamp(skb, &ssh); +} + +static bool is_sync_pdelay_resp(struct sk_buff *skb, int type) +{ + u8 *data = skb->data, *msgtype; + unsigned int offset = 0; + + if (type & PTP_CLASS_VLAN) + offset += VLAN_HLEN; + + switch (type & PTP_CLASS_PMASK) { + case PTP_CLASS_IPV4: + offset += ETH_HLEN + IPV4_HLEN(data + offset) + UDP_HLEN; + break; + case PTP_CLASS_IPV6: + offset += ETH_HLEN + IP6_HLEN + UDP_HLEN; + break; + case PTP_CLASS_L2: + offset += ETH_HLEN; + break; + default: + return 0; + } + + if (type & PTP_CLASS_V1) + offset += OFF_PTP_CONTROL; + + if (skb->len < offset + 1) + return 0; + + msgtype = data + offset; + + switch ((*msgtype & 0xf)) { + case SYNC: + case PDELAY_RESP: + return true; + default: + return false; + } +} + +static u8 tag_to_msgtype(u8 tag) +{ + switch (tag) { + case MESSAGE_TYPE_SYNC: + return SYNC; + case MESSAGE_TYPE_P_DELAY_REQ: + return PDELAY_REQ; + case MESSAGE_TYPE_P_DELAY_RESP: + return PDELAY_RESP; + case MESSAGE_TYPE_DELAY_REQ: + return DELAY_REQ; + } + return 0xf; +} + +static struct mii_timestamper *ines_ptp_probe_channel(struct device *device, + unsigned int index) +{ + struct device_node *node = device->of_node; + struct ines_port *port; + + if (index > INES_N_PORTS - 1) { + dev_err(device, "bad port index %u\n", index); + return ERR_PTR(-EINVAL); + } + port = ines_find_port(node, index); + if (!port) { + dev_err(device, "missing port index %u\n", index); + return ERR_PTR(-ENODEV); + } + port->mii_ts.rxtstamp = ines_rxtstamp; + port->mii_ts.txtstamp = ines_txtstamp; + port->mii_ts.hwtstamp = ines_hwtstamp; + port->mii_ts.link_state = ines_link_state; + port->mii_ts.ts_info = ines_ts_info; + + return &port->mii_ts; +} + +static void ines_ptp_release_channel(struct device *device, + struct mii_timestamper *mii_ts) +{ +} + +static struct mii_timestamping_ctrl ines_ctrl = { + .probe_channel = ines_ptp_probe_channel, + .release_channel = ines_ptp_release_channel, +}; + +static int ines_ptp_ctrl_probe(struct platform_device *pld) +{ + struct ines_clock *clock; + struct resource *res; + void __iomem *addr; + int err = 0; + + res = platform_get_resource(pld, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pld->dev, "missing memory resource\n"); + return -EINVAL; + } + addr = devm_ioremap_resource(&pld->dev, res); + if (IS_ERR(addr)) { + err = PTR_ERR(addr); + goto out; + } + clock = kzalloc(sizeof(*clock), GFP_KERNEL); + if (!clock) { + err = -ENOMEM; + goto out; + } + if (ines_clock_init(clock, &pld->dev, addr)) { + kfree(clock); + err = -ENOMEM; + goto out; + } + err = register_mii_tstamp_controller(&pld->dev, &ines_ctrl); + if (err) { + kfree(clock); + goto out; + } + mutex_lock(&ines_clocks_lock); + list_add_tail(&ines_clocks, &clock->list); + mutex_unlock(&ines_clocks_lock); + + dev_set_drvdata(&pld->dev, clock); +out: + return err; +} + +static int ines_ptp_ctrl_remove(struct platform_device *pld) +{ + struct ines_clock *clock = dev_get_drvdata(&pld->dev); + + unregister_mii_tstamp_controller(&pld->dev); + mutex_lock(&ines_clocks_lock); + list_del(&clock->list); + mutex_unlock(&ines_clocks_lock); + ines_clock_cleanup(clock); + kfree(clock); + return 0; +} + +static const struct of_device_id ines_ptp_ctrl_of_match[] = { + { .compatible = "ines,ptp-ctrl" }, + { } +}; + +MODULE_DEVICE_TABLE(of, ines_ptp_ctrl_of_match); + +static struct platform_driver ines_ptp_ctrl_driver = { + .probe = ines_ptp_ctrl_probe, + .remove = ines_ptp_ctrl_remove, + .driver = { + .name = "ines_ptp_ctrl", + .of_match_table = of_match_ptr(ines_ptp_ctrl_of_match), + }, +}; +module_platform_driver(ines_ptp_ctrl_driver); -- GitLab From 8ab9da573dad95536d8a92f19d7967e8142cd827 Mon Sep 17 00:00:00 2001 From: Andrii Nakryiko Date: Mon, 23 Dec 2019 10:03:05 -0800 Subject: [PATCH 1341/1437] libbpf: Support CO-RE relocations for LDX/ST/STX instructions Clang patch [0] enables emitting relocatable generic ALU/ALU64 instructions (i.e, shifts and arithmetic operations), as well as generic load/store instructions. The former ones are already supported by libbpf as is. This patch adds further support for load/store instructions. Relocatable field offset is encoded in BPF instruction's 16-bit offset section and are adjusted by libbpf based on target kernel BTF. These Clang changes and corresponding libbpf changes allow for more succinct generated BPF code by encoding relocatable field reads as a single ST/LDX/STX instruction. It also enables relocatable access to BPF context. Previously, if context struct (e.g., __sk_buff) was accessed with CO-RE relocations (e.g., due to preserve_access_index attribute), it would be rejected by BPF verifier due to modified context pointer dereference. With Clang patch, such context accesses are both relocatable and have a fixed offset from the point of view of BPF verifier. [0] https://reviews.llvm.org/D71790 Signed-off-by: Andrii Nakryiko Signed-off-by: Daniel Borkmann Acked-by: Yonghong Song Link: https://lore.kernel.org/bpf/20191223180305.86417-1-andriin@fb.com --- tools/lib/bpf/libbpf.c | 31 ++++++++++++++++++++++++++++--- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c index 9576a90c5a1ca..7513165b104f5 100644 --- a/tools/lib/bpf/libbpf.c +++ b/tools/lib/bpf/libbpf.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -3810,11 +3811,13 @@ static int bpf_core_reloc_insn(struct bpf_program *prog, insn = &prog->insns[insn_idx]; class = BPF_CLASS(insn->code); - if (class == BPF_ALU || class == BPF_ALU64) { + switch (class) { + case BPF_ALU: + case BPF_ALU64: if (BPF_SRC(insn->code) != BPF_K) return -EINVAL; if (!failed && validate && insn->imm != orig_val) { - pr_warn("prog '%s': unexpected insn #%d value: got %u, exp %u -> %u\n", + pr_warn("prog '%s': unexpected insn #%d (ALU/ALU64) value: got %u, exp %u -> %u\n", bpf_program__title(prog, false), insn_idx, insn->imm, orig_val, new_val); return -EINVAL; @@ -3824,7 +3827,29 @@ static int bpf_core_reloc_insn(struct bpf_program *prog, pr_debug("prog '%s': patched insn #%d (ALU/ALU64)%s imm %u -> %u\n", bpf_program__title(prog, false), insn_idx, failed ? " w/ failed reloc" : "", orig_val, new_val); - } else { + break; + case BPF_LDX: + case BPF_ST: + case BPF_STX: + if (!failed && validate && insn->off != orig_val) { + pr_warn("prog '%s': unexpected insn #%d (LD/LDX/ST/STX) value: got %u, exp %u -> %u\n", + bpf_program__title(prog, false), insn_idx, + insn->off, orig_val, new_val); + return -EINVAL; + } + if (new_val > SHRT_MAX) { + pr_warn("prog '%s': insn #%d (LD/LDX/ST/STX) value too big: %u\n", + bpf_program__title(prog, false), insn_idx, + new_val); + return -ERANGE; + } + orig_val = insn->off; + insn->off = new_val; + pr_debug("prog '%s': patched insn #%d (LD/LDX/ST/STX)%s off %u -> %u\n", + bpf_program__title(prog, false), insn_idx, + failed ? " w/ failed reloc" : "", orig_val, new_val); + break; + default: pr_warn("prog '%s': trying to relocate unrecognized insn #%d, code:%x, src:%x, dst:%x, off:%x, imm:%x\n", bpf_program__title(prog, false), insn_idx, insn->code, insn->src_reg, insn->dst_reg, -- GitLab From 1162f844030ac1ac7321b5e8f6c9badc7a11428f Mon Sep 17 00:00:00 2001 From: Hechao Li Date: Mon, 23 Dec 2019 17:17:42 -0800 Subject: [PATCH 1342/1437] bpf: Print error message for bpftool cgroup show Currently, when bpftool cgroup show has an error, no error message is printed. This is confusing because the user may think the result is empty. Before the change: $ bpftool cgroup show /sys/fs/cgroup ID AttachType AttachFlags Name $ echo $? 255 After the change: $ ./bpftool cgroup show /sys/fs/cgroup Error: can't query bpf programs attached to /sys/fs/cgroup: Operation not permitted v2: Rename check_query_cgroup_progs to cgroup_has_attached_progs Signed-off-by: Hechao Li Signed-off-by: Daniel Borkmann Link: https://lore.kernel.org/bpf/20191224011742.3714301-1-hechaol@fb.com --- tools/bpf/bpftool/cgroup.c | 56 ++++++++++++++++++++++++++------------ 1 file changed, 39 insertions(+), 17 deletions(-) diff --git a/tools/bpf/bpftool/cgroup.c b/tools/bpf/bpftool/cgroup.c index 1ef45e55039e1..2f017caa678dc 100644 --- a/tools/bpf/bpftool/cgroup.c +++ b/tools/bpf/bpftool/cgroup.c @@ -117,6 +117,25 @@ static int count_attached_bpf_progs(int cgroup_fd, enum bpf_attach_type type) return prog_cnt; } +static int cgroup_has_attached_progs(int cgroup_fd) +{ + enum bpf_attach_type type; + bool no_prog = true; + + for (type = 0; type < __MAX_BPF_ATTACH_TYPE; type++) { + int count = count_attached_bpf_progs(cgroup_fd, type); + + if (count < 0 && errno != EINVAL) + return -1; + + if (count > 0) { + no_prog = false; + break; + } + } + + return no_prog ? 0 : 1; +} static int show_attached_bpf_progs(int cgroup_fd, enum bpf_attach_type type, int level) { @@ -161,6 +180,7 @@ static int show_attached_bpf_progs(int cgroup_fd, enum bpf_attach_type type, static int do_show(int argc, char **argv) { enum bpf_attach_type type; + int has_attached_progs; const char *path; int cgroup_fd; int ret = -1; @@ -192,6 +212,16 @@ static int do_show(int argc, char **argv) goto exit; } + has_attached_progs = cgroup_has_attached_progs(cgroup_fd); + if (has_attached_progs < 0) { + p_err("can't query bpf programs attached to %s: %s", + path, strerror(errno)); + goto exit_cgroup; + } else if (!has_attached_progs) { + ret = 0; + goto exit_cgroup; + } + if (json_output) jsonw_start_array(json_wtr); else @@ -212,6 +242,7 @@ static int do_show(int argc, char **argv) if (json_output) jsonw_end_array(json_wtr); +exit_cgroup: close(cgroup_fd); exit: return ret; @@ -228,7 +259,7 @@ static int do_show_tree_fn(const char *fpath, const struct stat *sb, int typeflag, struct FTW *ftw) { enum bpf_attach_type type; - bool skip = true; + int has_attached_progs; int cgroup_fd; if (typeflag != FTW_D) @@ -240,22 +271,13 @@ static int do_show_tree_fn(const char *fpath, const struct stat *sb, return SHOW_TREE_FN_ERR; } - for (type = 0; type < __MAX_BPF_ATTACH_TYPE; type++) { - int count = count_attached_bpf_progs(cgroup_fd, type); - - if (count < 0 && errno != EINVAL) { - p_err("can't query bpf programs attached to %s: %s", - fpath, strerror(errno)); - close(cgroup_fd); - return SHOW_TREE_FN_ERR; - } - if (count > 0) { - skip = false; - break; - } - } - - if (skip) { + has_attached_progs = cgroup_has_attached_progs(cgroup_fd); + if (has_attached_progs < 0) { + p_err("can't query bpf programs attached to %s: %s", + fpath, strerror(errno)); + close(cgroup_fd); + return SHOW_TREE_FN_ERR; + } else if (!has_attached_progs) { close(cgroup_fd); return 0; } -- GitLab From db8f6f5c8de6dae924a68858ad6a4217f735be13 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Wed, 18 Dec 2019 12:05:13 +0100 Subject: [PATCH 1343/1437] netfilter: nft_meta: move time handling to helper reduce size of the (large) meta evaluation function. Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- net/netfilter/nft_meta.c | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/net/netfilter/nft_meta.c b/net/netfilter/nft_meta.c index 9740b554fdb31..ba74f3ee72643 100644 --- a/net/netfilter/nft_meta.c +++ b/net/netfilter/nft_meta.c @@ -33,8 +33,9 @@ static DEFINE_PER_CPU(struct rnd_state, nft_prandom_state); -static u8 nft_meta_weekday(time64_t secs) +static u8 nft_meta_weekday(void) { + time64_t secs = ktime_get_real_seconds(); unsigned int dse; u8 wday; @@ -56,6 +57,25 @@ static u32 nft_meta_hour(time64_t secs) + tm.tm_sec; } +static noinline_for_stack void +nft_meta_get_eval_time(enum nft_meta_keys key, + u32 *dest) +{ + switch (key) { + case NFT_META_TIME_NS: + nft_reg_store64(dest, ktime_get_real_ns()); + break; + case NFT_META_TIME_DAY: + nft_reg_store8(dest, nft_meta_weekday()); + break; + case NFT_META_TIME_HOUR: + *dest = nft_meta_hour(ktime_get_real_seconds()); + break; + default: + break; + } +} + void nft_meta_get_eval(const struct nft_expr *expr, struct nft_regs *regs, const struct nft_pktinfo *pkt) @@ -247,13 +267,9 @@ void nft_meta_get_eval(const struct nft_expr *expr, strncpy((char *)dest, out->rtnl_link_ops->kind, IFNAMSIZ); break; case NFT_META_TIME_NS: - nft_reg_store64(dest, ktime_get_real_ns()); - break; case NFT_META_TIME_DAY: - nft_reg_store8(dest, nft_meta_weekday(ktime_get_real_seconds())); - break; case NFT_META_TIME_HOUR: - *dest = nft_meta_hour(ktime_get_real_seconds()); + nft_meta_get_eval_time(priv->key, dest); break; default: WARN_ON(1); -- GitLab From 4a54594abdbee230a0471aa137b3d5405c897661 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Wed, 18 Dec 2019 12:05:14 +0100 Subject: [PATCH 1344/1437] netfilter: nft_meta: move pkttype handling to helper When pkttype is loopback, nft_meta performs guesswork to detect broad/multicast packets. Place this in a helper, this is hardly a hot path. Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- net/netfilter/nft_meta.c | 90 +++++++++++++++++++++++----------------- 1 file changed, 51 insertions(+), 39 deletions(-) diff --git a/net/netfilter/nft_meta.c b/net/netfilter/nft_meta.c index ba74f3ee72643..fe49b27dfa872 100644 --- a/net/netfilter/nft_meta.c +++ b/net/netfilter/nft_meta.c @@ -76,6 +76,56 @@ nft_meta_get_eval_time(enum nft_meta_keys key, } } +static noinline bool +nft_meta_get_eval_pkttype_lo(const struct nft_pktinfo *pkt, + u32 *dest) +{ + const struct sk_buff *skb = pkt->skb; + + switch (nft_pf(pkt)) { + case NFPROTO_IPV4: + if (ipv4_is_multicast(ip_hdr(skb)->daddr)) + nft_reg_store8(dest, PACKET_MULTICAST); + else + nft_reg_store8(dest, PACKET_BROADCAST); + break; + case NFPROTO_IPV6: + nft_reg_store8(dest, PACKET_MULTICAST); + break; + case NFPROTO_NETDEV: + switch (skb->protocol) { + case htons(ETH_P_IP): { + int noff = skb_network_offset(skb); + struct iphdr *iph, _iph; + + iph = skb_header_pointer(skb, noff, + sizeof(_iph), &_iph); + if (!iph) + return false; + + if (ipv4_is_multicast(iph->daddr)) + nft_reg_store8(dest, PACKET_MULTICAST); + else + nft_reg_store8(dest, PACKET_BROADCAST); + + break; + } + case htons(ETH_P_IPV6): + nft_reg_store8(dest, PACKET_MULTICAST); + break; + default: + WARN_ON_ONCE(1); + return false; + } + break; + default: + WARN_ON_ONCE(1); + return false; + } + + return true; +} + void nft_meta_get_eval(const struct nft_expr *expr, struct nft_regs *regs, const struct nft_pktinfo *pkt) @@ -183,46 +233,8 @@ void nft_meta_get_eval(const struct nft_expr *expr, break; } - switch (nft_pf(pkt)) { - case NFPROTO_IPV4: - if (ipv4_is_multicast(ip_hdr(skb)->daddr)) - nft_reg_store8(dest, PACKET_MULTICAST); - else - nft_reg_store8(dest, PACKET_BROADCAST); - break; - case NFPROTO_IPV6: - nft_reg_store8(dest, PACKET_MULTICAST); - break; - case NFPROTO_NETDEV: - switch (skb->protocol) { - case htons(ETH_P_IP): { - int noff = skb_network_offset(skb); - struct iphdr *iph, _iph; - - iph = skb_header_pointer(skb, noff, - sizeof(_iph), &_iph); - if (!iph) - goto err; - - if (ipv4_is_multicast(iph->daddr)) - nft_reg_store8(dest, PACKET_MULTICAST); - else - nft_reg_store8(dest, PACKET_BROADCAST); - - break; - } - case htons(ETH_P_IPV6): - nft_reg_store8(dest, PACKET_MULTICAST); - break; - default: - WARN_ON_ONCE(1); - goto err; - } - break; - default: - WARN_ON_ONCE(1); + if (!nft_meta_get_eval_pkttype_lo(pkt, dest)) goto err; - } break; case NFT_META_CPU: *dest = raw_smp_processor_id(); -- GitLab From 726b44f044e8e67cbe2209c1a5704aca981be3b2 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Wed, 18 Dec 2019 12:05:15 +0100 Subject: [PATCH 1345/1437] netfilter: nft_meta: move sk uid/git handling to helper Not a hot path. Also, both have copy&paste case statements, so use a common helper for both. Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- net/netfilter/nft_meta.c | 65 ++++++++++++++++++++++------------------ 1 file changed, 36 insertions(+), 29 deletions(-) diff --git a/net/netfilter/nft_meta.c b/net/netfilter/nft_meta.c index fe49b27dfa872..1b32440ec2e60 100644 --- a/net/netfilter/nft_meta.c +++ b/net/netfilter/nft_meta.c @@ -126,6 +126,41 @@ nft_meta_get_eval_pkttype_lo(const struct nft_pktinfo *pkt, return true; } +static noinline bool +nft_meta_get_eval_skugid(enum nft_meta_keys key, + u32 *dest, + const struct nft_pktinfo *pkt) +{ + struct sock *sk = skb_to_full_sk(pkt->skb); + struct socket *sock; + + if (!sk || !sk_fullsock(sk) || !net_eq(nft_net(pkt), sock_net(sk))) + return false; + + read_lock_bh(&sk->sk_callback_lock); + sock = sk->sk_socket; + if (!sock || !sock->file) { + read_unlock_bh(&sk->sk_callback_lock); + return false; + } + + switch (key) { + case NFT_META_SKUID: + *dest = from_kuid_munged(&init_user_ns, + sock->file->f_cred->fsuid); + break; + case NFT_META_SKGID: + *dest = from_kgid_munged(&init_user_ns, + sock->file->f_cred->fsgid); + break; + default: + break; + } + + read_unlock_bh(&sk->sk_callback_lock); + return true; +} + void nft_meta_get_eval(const struct nft_expr *expr, struct nft_regs *regs, const struct nft_pktinfo *pkt) @@ -180,37 +215,9 @@ void nft_meta_get_eval(const struct nft_expr *expr, nft_reg_store16(dest, out->type); break; case NFT_META_SKUID: - sk = skb_to_full_sk(skb); - if (!sk || !sk_fullsock(sk) || - !net_eq(nft_net(pkt), sock_net(sk))) - goto err; - - read_lock_bh(&sk->sk_callback_lock); - if (sk->sk_socket == NULL || - sk->sk_socket->file == NULL) { - read_unlock_bh(&sk->sk_callback_lock); - goto err; - } - - *dest = from_kuid_munged(&init_user_ns, - sk->sk_socket->file->f_cred->fsuid); - read_unlock_bh(&sk->sk_callback_lock); - break; case NFT_META_SKGID: - sk = skb_to_full_sk(skb); - if (!sk || !sk_fullsock(sk) || - !net_eq(nft_net(pkt), sock_net(sk))) + if (!nft_meta_get_eval_skugid(priv->key, dest, pkt)) goto err; - - read_lock_bh(&sk->sk_callback_lock); - if (sk->sk_socket == NULL || - sk->sk_socket->file == NULL) { - read_unlock_bh(&sk->sk_callback_lock); - goto err; - } - *dest = from_kgid_munged(&init_user_ns, - sk->sk_socket->file->f_cred->fsgid); - read_unlock_bh(&sk->sk_callback_lock); break; #ifdef CONFIG_IP_ROUTE_CLASSID case NFT_META_RTCLASSID: { -- GitLab From b1327fbc29915ef5bf0eec5abc2e2e67983cb037 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Wed, 18 Dec 2019 12:05:16 +0100 Subject: [PATCH 1346/1437] netfilter: nft_meta: move cgroup handling to helper Reduce size of main eval function. Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- net/netfilter/nft_meta.c | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/net/netfilter/nft_meta.c b/net/netfilter/nft_meta.c index 1b32440ec2e60..3fca1c3ec3610 100644 --- a/net/netfilter/nft_meta.c +++ b/net/netfilter/nft_meta.c @@ -161,6 +161,20 @@ nft_meta_get_eval_skugid(enum nft_meta_keys key, return true; } +#ifdef CONFIG_CGROUP_NET_CLASSID +static noinline bool +nft_meta_get_eval_cgroup(u32 *dest, const struct nft_pktinfo *pkt) +{ + struct sock *sk = skb_to_full_sk(pkt->skb); + + if (!sk || !sk_fullsock(sk) || !net_eq(nft_net(pkt), sock_net(sk))) + return false; + + *dest = sock_cgroup_classid(&sk->sk_cgrp_data); + return true; +} +#endif + void nft_meta_get_eval(const struct nft_expr *expr, struct nft_regs *regs, const struct nft_pktinfo *pkt) @@ -168,7 +182,6 @@ void nft_meta_get_eval(const struct nft_expr *expr, const struct nft_meta *priv = nft_expr_priv(expr); const struct sk_buff *skb = pkt->skb; const struct net_device *in = nft_in(pkt), *out = nft_out(pkt); - struct sock *sk; u32 *dest = ®s->data[priv->dreg]; switch (priv->key) { @@ -258,11 +271,8 @@ void nft_meta_get_eval(const struct nft_expr *expr, break; #ifdef CONFIG_CGROUP_NET_CLASSID case NFT_META_CGROUP: - sk = skb_to_full_sk(skb); - if (!sk || !sk_fullsock(sk) || - !net_eq(nft_net(pkt), sock_net(sk))) + if (!nft_meta_get_eval_cgroup(dest, pkt)) goto err; - *dest = sock_cgroup_classid(&sk->sk_cgrp_data); break; #endif case NFT_META_PRANDOM: { -- GitLab From a4150a1faa3604beef25ed2374d537f86059ad52 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Wed, 18 Dec 2019 12:05:17 +0100 Subject: [PATCH 1347/1437] netfilter: nft_meta: move interface kind handling to helper checkpatch complains about == NULL checks in original code, so use !in instead. Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- net/netfilter/nft_meta.c | 31 +++++++++++++++++++++++++------ 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/net/netfilter/nft_meta.c b/net/netfilter/nft_meta.c index 3fca1c3ec3610..2f7cc64b0c15f 100644 --- a/net/netfilter/nft_meta.c +++ b/net/netfilter/nft_meta.c @@ -175,6 +175,30 @@ nft_meta_get_eval_cgroup(u32 *dest, const struct nft_pktinfo *pkt) } #endif +static noinline bool nft_meta_get_eval_kind(enum nft_meta_keys key, + u32 *dest, + const struct nft_pktinfo *pkt) +{ + const struct net_device *in = nft_in(pkt), *out = nft_out(pkt); + + switch (key) { + case NFT_META_IIFKIND: + if (!in || !in->rtnl_link_ops) + return false; + strncpy((char *)dest, in->rtnl_link_ops->kind, IFNAMSIZ); + break; + case NFT_META_OIFKIND: + if (!out || !out->rtnl_link_ops) + return false; + strncpy((char *)dest, out->rtnl_link_ops->kind, IFNAMSIZ); + break; + default: + return false; + } + + return true; +} + void nft_meta_get_eval(const struct nft_expr *expr, struct nft_regs *regs, const struct nft_pktinfo *pkt) @@ -286,14 +310,9 @@ void nft_meta_get_eval(const struct nft_expr *expr, break; #endif case NFT_META_IIFKIND: - if (in == NULL || in->rtnl_link_ops == NULL) - goto err; - strncpy((char *)dest, in->rtnl_link_ops->kind, IFNAMSIZ); - break; case NFT_META_OIFKIND: - if (out == NULL || out->rtnl_link_ops == NULL) + if (!nft_meta_get_eval_kind(priv->key, dest, pkt)) goto err; - strncpy((char *)dest, out->rtnl_link_ops->kind, IFNAMSIZ); break; case NFT_META_TIME_NS: case NFT_META_TIME_DAY: -- GitLab From 8724e819cc9a8fab4c18e791cb0bd602fb294971 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Wed, 18 Dec 2019 12:05:18 +0100 Subject: [PATCH 1348/1437] netfilter: nft_meta: move all interface related keys to helper Reduces repetiveness and reduces size of meta eval function. Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- net/netfilter/nft_meta.c | 95 +++++++++++++++++++++++++++++----------- 1 file changed, 70 insertions(+), 25 deletions(-) diff --git a/net/netfilter/nft_meta.c b/net/netfilter/nft_meta.c index 2f7cc64b0c15f..022f1473ddd16 100644 --- a/net/netfilter/nft_meta.c +++ b/net/netfilter/nft_meta.c @@ -199,13 +199,79 @@ static noinline bool nft_meta_get_eval_kind(enum nft_meta_keys key, return true; } +static void nft_meta_store_ifindex(u32 *dest, const struct net_device *dev) +{ + *dest = dev ? dev->ifindex : 0; +} + +static void nft_meta_store_ifname(u32 *dest, const struct net_device *dev) +{ + strncpy((char *)dest, dev ? dev->name : "", IFNAMSIZ); +} + +static bool nft_meta_store_iftype(u32 *dest, const struct net_device *dev) +{ + if (!dev) + return false; + + nft_reg_store16(dest, dev->type); + return true; +} + +static bool nft_meta_store_ifgroup(u32 *dest, const struct net_device *dev) +{ + if (!dev) + return false; + + *dest = dev->group; + return true; +} + +static bool nft_meta_get_eval_ifname(enum nft_meta_keys key, u32 *dest, + const struct nft_pktinfo *pkt) +{ + switch (key) { + case NFT_META_IIFNAME: + nft_meta_store_ifname(dest, nft_in(pkt)); + break; + case NFT_META_OIFNAME: + nft_meta_store_ifname(dest, nft_out(pkt)); + break; + case NFT_META_IIF: + nft_meta_store_ifindex(dest, nft_in(pkt)); + break; + case NFT_META_OIF: + nft_meta_store_ifindex(dest, nft_out(pkt)); + break; + case NFT_META_IIFTYPE: + if (!nft_meta_store_iftype(dest, nft_in(pkt))) + return false; + break; + case NFT_META_OIFTYPE: + if (!nft_meta_store_iftype(dest, nft_out(pkt))) + return false; + break; + case NFT_META_IIFGROUP: + if (!nft_meta_store_ifgroup(dest, nft_out(pkt))) + return false; + break; + case NFT_META_OIFGROUP: + if (!nft_meta_store_ifgroup(dest, nft_out(pkt))) + return false; + break; + default: + return false; + } + + return true; +} + void nft_meta_get_eval(const struct nft_expr *expr, struct nft_regs *regs, const struct nft_pktinfo *pkt) { const struct nft_meta *priv = nft_expr_priv(expr); const struct sk_buff *skb = pkt->skb; - const struct net_device *in = nft_in(pkt), *out = nft_out(pkt); u32 *dest = ®s->data[priv->dreg]; switch (priv->key) { @@ -230,26 +296,15 @@ void nft_meta_get_eval(const struct nft_expr *expr, *dest = skb->mark; break; case NFT_META_IIF: - *dest = in ? in->ifindex : 0; - break; case NFT_META_OIF: - *dest = out ? out->ifindex : 0; - break; case NFT_META_IIFNAME: - strncpy((char *)dest, in ? in->name : "", IFNAMSIZ); - break; case NFT_META_OIFNAME: - strncpy((char *)dest, out ? out->name : "", IFNAMSIZ); - break; case NFT_META_IIFTYPE: - if (in == NULL) - goto err; - nft_reg_store16(dest, in->type); - break; case NFT_META_OIFTYPE: - if (out == NULL) + case NFT_META_IIFGROUP: + case NFT_META_OIFGROUP: + if (!nft_meta_get_eval_ifname(priv->key, dest, pkt)) goto err; - nft_reg_store16(dest, out->type); break; case NFT_META_SKUID: case NFT_META_SKGID: @@ -283,16 +338,6 @@ void nft_meta_get_eval(const struct nft_expr *expr, case NFT_META_CPU: *dest = raw_smp_processor_id(); break; - case NFT_META_IIFGROUP: - if (in == NULL) - goto err; - *dest = in->group; - break; - case NFT_META_OIFGROUP: - if (out == NULL) - goto err; - *dest = out->group; - break; #ifdef CONFIG_CGROUP_NET_CLASSID case NFT_META_CGROUP: if (!nft_meta_get_eval_cgroup(dest, pkt)) -- GitLab From 6b2faee0ca91b63bd5a3b2087d4f25c76b983961 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Wed, 18 Dec 2019 12:05:19 +0100 Subject: [PATCH 1349/1437] netfilter: nft_meta: place prandom handling in a helper Move this out of the main eval loop, the numgen expression provides a better alternative to meta random. Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- net/netfilter/nft_meta.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/net/netfilter/nft_meta.c b/net/netfilter/nft_meta.c index 022f1473ddd16..ac6fc95387dce 100644 --- a/net/netfilter/nft_meta.c +++ b/net/netfilter/nft_meta.c @@ -266,6 +266,13 @@ static bool nft_meta_get_eval_ifname(enum nft_meta_keys key, u32 *dest, return true; } +static noinline u32 nft_prandom_u32(void) +{ + struct rnd_state *state = this_cpu_ptr(&nft_prandom_state); + + return prandom_u32_state(state); +} + void nft_meta_get_eval(const struct nft_expr *expr, struct nft_regs *regs, const struct nft_pktinfo *pkt) @@ -344,11 +351,9 @@ void nft_meta_get_eval(const struct nft_expr *expr, goto err; break; #endif - case NFT_META_PRANDOM: { - struct rnd_state *state = this_cpu_ptr(&nft_prandom_state); - *dest = prandom_u32_state(state); + case NFT_META_PRANDOM: + *dest = nft_prandom_u32(); break; - } #ifdef CONFIG_XFRM case NFT_META_SECPATH: nft_reg_store8(dest, secpath_exists(skb)); -- GitLab From 01a0fc82252d82eda50d4e1252b826a3ef7afb3d Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Wed, 18 Dec 2019 12:05:20 +0100 Subject: [PATCH 1350/1437] netfilter: nft_meta: place rtclassid handling in a helper skb_dst is an inline helper with a WARN_ON(), so this is a bit more code than it looks like. Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- net/netfilter/nft_meta.c | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/net/netfilter/nft_meta.c b/net/netfilter/nft_meta.c index ac6fc95387dce..fb1a571db9241 100644 --- a/net/netfilter/nft_meta.c +++ b/net/netfilter/nft_meta.c @@ -273,6 +273,20 @@ static noinline u32 nft_prandom_u32(void) return prandom_u32_state(state); } +#ifdef CONFIG_IP_ROUTE_CLASSID +static noinline bool +nft_meta_get_eval_rtclassid(const struct sk_buff *skb, u32 *dest) +{ + const struct dst_entry *dst = skb_dst(skb); + + if (!dst) + return false; + + *dest = dst->tclassid; + return true; +} +#endif + void nft_meta_get_eval(const struct nft_expr *expr, struct nft_regs *regs, const struct nft_pktinfo *pkt) @@ -319,14 +333,10 @@ void nft_meta_get_eval(const struct nft_expr *expr, goto err; break; #ifdef CONFIG_IP_ROUTE_CLASSID - case NFT_META_RTCLASSID: { - const struct dst_entry *dst = skb_dst(skb); - - if (dst == NULL) + case NFT_META_RTCLASSID: + if (!nft_meta_get_eval_rtclassid(skb, dest)) goto err; - *dest = dst->tclassid; break; - } #endif #ifdef CONFIG_NETWORK_SECMARK case NFT_META_SECMARK: -- GitLab From c14ceb0ec727187f71a487a592ffa91767fed66e Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Wed, 18 Dec 2019 12:05:21 +0100 Subject: [PATCH 1351/1437] netfilter: nft_meta: add support for slave device ifindex matching Allow to match on vrf slave ifindex or name. In case there was no slave interface involved, store 0 in the destination register just like existing iif/oif matching. sdif(name) is restricted to the ipv4/ipv6 input and forward hooks, as it depends on ip(6) stack parsing/storing info in skb->cb[]. Cc: Martin Willi Cc: David Ahern Cc: Shrijeet Mukherjee Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- include/uapi/linux/netfilter/nf_tables.h | 4 ++ net/netfilter/nft_meta.c | 76 +++++++++++++++++++++--- 2 files changed, 73 insertions(+), 7 deletions(-) diff --git a/include/uapi/linux/netfilter/nf_tables.h b/include/uapi/linux/netfilter/nf_tables.h index bb9b049310dff..e237ecbdcd8ac 100644 --- a/include/uapi/linux/netfilter/nf_tables.h +++ b/include/uapi/linux/netfilter/nf_tables.h @@ -805,6 +805,8 @@ enum nft_exthdr_attributes { * @NFT_META_TIME_NS: time since epoch (in nanoseconds) * @NFT_META_TIME_DAY: day of week (from 0 = Sunday to 6 = Saturday) * @NFT_META_TIME_HOUR: hour of day (in seconds) + * @NFT_META_SDIF: slave device interface index + * @NFT_META_SDIFNAME: slave device interface name */ enum nft_meta_keys { NFT_META_LEN, @@ -840,6 +842,8 @@ enum nft_meta_keys { NFT_META_TIME_NS, NFT_META_TIME_DAY, NFT_META_TIME_HOUR, + NFT_META_SDIF, + NFT_META_SDIFNAME, }; /** diff --git a/net/netfilter/nft_meta.c b/net/netfilter/nft_meta.c index fb1a571db9241..951b6e87ed5d9 100644 --- a/net/netfilter/nft_meta.c +++ b/net/netfilter/nft_meta.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include /* for TCP_TIME_WAIT */ #include @@ -287,6 +288,28 @@ nft_meta_get_eval_rtclassid(const struct sk_buff *skb, u32 *dest) } #endif +static noinline u32 nft_meta_get_eval_sdif(const struct nft_pktinfo *pkt) +{ + switch (nft_pf(pkt)) { + case NFPROTO_IPV4: + return inet_sdif(pkt->skb); + case NFPROTO_IPV6: + return inet6_sdif(pkt->skb); + } + + return 0; +} + +static noinline void +nft_meta_get_eval_sdifname(u32 *dest, const struct nft_pktinfo *pkt) +{ + u32 sdif = nft_meta_get_eval_sdif(pkt); + const struct net_device *dev; + + dev = sdif ? dev_get_by_index_rcu(nft_net(pkt), sdif) : NULL; + nft_meta_store_ifname(dest, dev); +} + void nft_meta_get_eval(const struct nft_expr *expr, struct nft_regs *regs, const struct nft_pktinfo *pkt) @@ -379,6 +402,12 @@ void nft_meta_get_eval(const struct nft_expr *expr, case NFT_META_TIME_HOUR: nft_meta_get_eval_time(priv->key, dest); break; + case NFT_META_SDIF: + *dest = nft_meta_get_eval_sdif(pkt); + break; + case NFT_META_SDIFNAME: + nft_meta_get_eval_sdifname(dest, pkt); + break; default: WARN_ON(1); goto err; @@ -459,6 +488,7 @@ int nft_meta_get_init(const struct nft_ctx *ctx, case NFT_META_MARK: case NFT_META_IIF: case NFT_META_OIF: + case NFT_META_SDIF: case NFT_META_SKUID: case NFT_META_SKGID: #ifdef CONFIG_IP_ROUTE_CLASSID @@ -480,6 +510,7 @@ int nft_meta_get_init(const struct nft_ctx *ctx, case NFT_META_OIFNAME: case NFT_META_IIFKIND: case NFT_META_OIFKIND: + case NFT_META_SDIFNAME: len = IFNAMSIZ; break; case NFT_META_PRANDOM: @@ -510,16 +541,28 @@ int nft_meta_get_init(const struct nft_ctx *ctx, } EXPORT_SYMBOL_GPL(nft_meta_get_init); -static int nft_meta_get_validate(const struct nft_ctx *ctx, - const struct nft_expr *expr, - const struct nft_data **data) +static int nft_meta_get_validate_sdif(const struct nft_ctx *ctx) { -#ifdef CONFIG_XFRM - const struct nft_meta *priv = nft_expr_priv(expr); unsigned int hooks; - if (priv->key != NFT_META_SECPATH) - return 0; + switch (ctx->family) { + case NFPROTO_IPV4: + case NFPROTO_IPV6: + case NFPROTO_INET: + hooks = (1 << NF_INET_LOCAL_IN) | + (1 << NF_INET_FORWARD); + break; + default: + return -EOPNOTSUPP; + } + + return nft_chain_validate_hooks(ctx->chain, hooks); +} + +static int nft_meta_get_validate_xfrm(const struct nft_ctx *ctx) +{ +#ifdef CONFIG_XFRM + unsigned int hooks; switch (ctx->family) { case NFPROTO_NETDEV: @@ -542,6 +585,25 @@ static int nft_meta_get_validate(const struct nft_ctx *ctx, #endif } +static int nft_meta_get_validate(const struct nft_ctx *ctx, + const struct nft_expr *expr, + const struct nft_data **data) +{ + const struct nft_meta *priv = nft_expr_priv(expr); + + switch (priv->key) { + case NFT_META_SECPATH: + return nft_meta_get_validate_xfrm(ctx); + case NFT_META_SDIF: + case NFT_META_SDIFNAME: + return nft_meta_get_validate_sdif(ctx); + default: + break; + } + + return 0; +} + int nft_meta_set_validate(const struct nft_ctx *ctx, const struct nft_expr *expr, const struct nft_data **data) -- GitLab From a5bcd72e054aabb93ddc51ed8cde36a5bfc50271 Mon Sep 17 00:00:00 2001 From: Vladyslav Tarasiuk Date: Thu, 26 Dec 2019 10:41:56 +0200 Subject: [PATCH 1352/1437] net/mlxfw: Fix out-of-memory error in mfa2 flash burning The burning process requires to perform internal allocations of large chunks of memory. This memory doesn't need to be contiguous and can be safely allocated by vzalloc() instead of kzalloc(). This patch changes such allocation to avoid possible out-of-memory failure. Fixes: 410ed13cae39 ("Add the mlxfw module for Mellanox firmware flash process") Signed-off-by: Vladyslav Tarasiuk Reviewed-by: Aya Levin Signed-off-by: Leon Romanovsky Tested-by: Ido Schimmel Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxfw/mlxfw_mfa2.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlxfw/mlxfw_mfa2.c b/drivers/net/ethernet/mellanox/mlxfw/mlxfw_mfa2.c index 544344ac4894c..79057af4fe997 100644 --- a/drivers/net/ethernet/mellanox/mlxfw/mlxfw_mfa2.c +++ b/drivers/net/ethernet/mellanox/mlxfw/mlxfw_mfa2.c @@ -6,6 +6,7 @@ #include #include #include +#include #include #include "mlxfw_mfa2.h" #include "mlxfw_mfa2_file.h" @@ -548,7 +549,7 @@ mlxfw_mfa2_file_component_get(const struct mlxfw_mfa2_file *mfa2_file, comp_size = be32_to_cpu(comp->size); comp_buf_size = comp_size + mlxfw_mfa2_comp_magic_len; - comp_data = kmalloc(sizeof(*comp_data) + comp_buf_size, GFP_KERNEL); + comp_data = vzalloc(sizeof(*comp_data) + comp_buf_size); if (!comp_data) return ERR_PTR(-ENOMEM); comp_data->comp.data_size = comp_size; @@ -570,7 +571,7 @@ mlxfw_mfa2_file_component_get(const struct mlxfw_mfa2_file *mfa2_file, comp_data->comp.data = comp_data->buff + mlxfw_mfa2_comp_magic_len; return &comp_data->comp; err_out: - kfree(comp_data); + vfree(comp_data); return ERR_PTR(err); } @@ -579,7 +580,7 @@ void mlxfw_mfa2_file_component_put(struct mlxfw_mfa2_component *comp) const struct mlxfw_mfa2_comp_data *comp_data; comp_data = container_of(comp, struct mlxfw_mfa2_comp_data, comp); - kfree(comp_data); + vfree(comp_data); } void mlxfw_mfa2_file_fini(struct mlxfw_mfa2_file *mfa2_file) -- GitLab From f643ee295c1c63bc117fb052d4da681354d6f732 Mon Sep 17 00:00:00 2001 From: Kevin Kou Date: Thu, 26 Dec 2019 12:29:17 +0000 Subject: [PATCH 1353/1437] sctp: move trace_sctp_probe_path into sctp_outq_sack The original patch bringed in the "SCTP ACK tracking trace event" feature was committed at Dec.20, 2017, it replaced jprobe usage with trace events, and bringed in two trace events, one is TRACE_EVENT(sctp_probe), another one is TRACE_EVENT(sctp_probe_path). The original patch intended to trigger the trace_sctp_probe_path in TRACE_EVENT(sctp_probe) as below code, +TRACE_EVENT(sctp_probe, + + TP_PROTO(const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + struct sctp_chunk *chunk), + + TP_ARGS(ep, asoc, chunk), + + TP_STRUCT__entry( + __field(__u64, asoc) + __field(__u32, mark) + __field(__u16, bind_port) + __field(__u16, peer_port) + __field(__u32, pathmtu) + __field(__u32, rwnd) + __field(__u16, unack_data) + ), + + TP_fast_assign( + struct sk_buff *skb = chunk->skb; + + __entry->asoc = (unsigned long)asoc; + __entry->mark = skb->mark; + __entry->bind_port = ep->base.bind_addr.port; + __entry->peer_port = asoc->peer.port; + __entry->pathmtu = asoc->pathmtu; + __entry->rwnd = asoc->peer.rwnd; + __entry->unack_data = asoc->unack_data; + + if (trace_sctp_probe_path_enabled()) { + struct sctp_transport *sp; + + list_for_each_entry(sp, &asoc->peer.transport_addr_list, + transports) { + trace_sctp_probe_path(sp, asoc); + } + } + ), But I found it did not work when I did testing, and trace_sctp_probe_path had no output, I finally found that there is trace buffer lock operation(trace_event_buffer_reserve) in include/trace/trace_events.h: static notrace void \ trace_event_raw_event_##call(void *__data, proto) \ { \ struct trace_event_file *trace_file = __data; \ struct trace_event_data_offsets_##call __maybe_unused __data_offsets;\ struct trace_event_buffer fbuffer; \ struct trace_event_raw_##call *entry; \ int __data_size; \ \ if (trace_trigger_soft_disabled(trace_file)) \ return; \ \ __data_size = trace_event_get_offsets_##call(&__data_offsets, args); \ \ entry = trace_event_buffer_reserve(&fbuffer, trace_file, \ sizeof(*entry) + __data_size); \ \ if (!entry) \ return; \ \ tstruct \ \ { assign; } \ \ trace_event_buffer_commit(&fbuffer); \ } The reason caused no output of trace_sctp_probe_path is that trace_sctp_probe_path written in TP_fast_assign part of TRACE_EVENT(sctp_probe), and it will be placed( { assign; } ) after the trace_event_buffer_reserve() when compiler expands Macro, entry = trace_event_buffer_reserve(&fbuffer, trace_file, \ sizeof(*entry) + __data_size); \ \ if (!entry) \ return; \ \ tstruct \ \ { assign; } \ so trace_sctp_probe_path finally can not acquire trace_event_buffer and return no output, that is to say the nest of tracepoint entry function is not allowed. The function call flow is: trace_sctp_probe() -> trace_event_raw_event_sctp_probe() -> lock buffer -> trace_sctp_probe_path() -> trace_event_raw_event_sctp_probe_path() --nested -> buffer has been locked and return no output. This patch is to remove trace_sctp_probe_path from the TP_fast_assign part of TRACE_EVENT(sctp_probe) to avoid the nest of entry function, and trigger sctp_probe_path_trace in sctp_outq_sack. After this patch, you can enable both events individually, # cd /sys/kernel/debug/tracing # echo 1 > events/sctp/sctp_probe/enable # echo 1 > events/sctp/sctp_probe_path/enable Or, you can enable all the events under sctp. # echo 1 > events/sctp/enable Signed-off-by: Kevin Kou Acked-by: Marcelo Ricardo Leitner Signed-off-by: David S. Miller --- include/trace/events/sctp.h | 9 --------- net/sctp/outqueue.c | 6 ++++++ 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/include/trace/events/sctp.h b/include/trace/events/sctp.h index 7475c7be165aa..d4aac34365955 100644 --- a/include/trace/events/sctp.h +++ b/include/trace/events/sctp.h @@ -75,15 +75,6 @@ TRACE_EVENT(sctp_probe, __entry->pathmtu = asoc->pathmtu; __entry->rwnd = asoc->peer.rwnd; __entry->unack_data = asoc->unack_data; - - if (trace_sctp_probe_path_enabled()) { - struct sctp_transport *sp; - - list_for_each_entry(sp, &asoc->peer.transport_addr_list, - transports) { - trace_sctp_probe_path(sp, asoc); - } - } ), TP_printk("asoc=%#llx mark=%#x bind_port=%d peer_port=%d pathmtu=%d " diff --git a/net/sctp/outqueue.c b/net/sctp/outqueue.c index a031d1134c604..6b0b3bad4daa6 100644 --- a/net/sctp/outqueue.c +++ b/net/sctp/outqueue.c @@ -36,6 +36,7 @@ #include #include #include +#include /* Declare internal functions here. */ static int sctp_acked(struct sctp_sackhdr *sack, __u32 tsn); @@ -1238,6 +1239,11 @@ int sctp_outq_sack(struct sctp_outq *q, struct sctp_chunk *chunk) /* Grab the association's destination address list. */ transport_list = &asoc->peer.transport_addr_list; + /* SCTP path tracepoint for congestion control debugging. */ + list_for_each_entry(transport, transport_list, transports) { + trace_sctp_probe_path(transport, asoc); + } + sack_ctsn = ntohl(sack->cum_tsn_ack); gap_ack_blocks = ntohs(sack->num_gap_ack_blocks); asoc->stats.gapcnt += gap_ack_blocks; -- GitLab From c1e469902640ed0a82d0b8df5951199c17a49e3c Mon Sep 17 00:00:00 2001 From: Andy Roulin Date: Thu, 26 Dec 2019 05:41:57 -0800 Subject: [PATCH 1354/1437] bonding: rename AD_STATE_* to LACP_STATE_* As the LACP actor/partner state is now part of the uapi, rename the 3ad state defines with LACP prefix. The LACP prefix is preferred over BOND_3AD as the LACP standard moved to 802.1AX. Fixes: 826f66b30c2e3 ("bonding: move 802.3ad port state flags to uapi") Signed-off-by: Andy Roulin Signed-off-by: David S. Miller --- drivers/net/bonding/bond_3ad.c | 112 ++++++++++++++++---------------- include/uapi/linux/if_bonding.h | 16 ++--- 2 files changed, 64 insertions(+), 64 deletions(-) diff --git a/drivers/net/bonding/bond_3ad.c b/drivers/net/bonding/bond_3ad.c index 34bfe99641a3c..31e43a2197a30 100644 --- a/drivers/net/bonding/bond_3ad.c +++ b/drivers/net/bonding/bond_3ad.c @@ -447,8 +447,8 @@ static void __choose_matched(struct lacpdu *lacpdu, struct port *port) MAC_ADDRESS_EQUAL(&(lacpdu->partner_system), &(port->actor_system)) && (ntohs(lacpdu->partner_system_priority) == port->actor_system_priority) && (ntohs(lacpdu->partner_key) == port->actor_oper_port_key) && - ((lacpdu->partner_state & AD_STATE_AGGREGATION) == (port->actor_oper_port_state & AD_STATE_AGGREGATION))) || - ((lacpdu->actor_state & AD_STATE_AGGREGATION) == 0) + ((lacpdu->partner_state & LACP_STATE_AGGREGATION) == (port->actor_oper_port_state & LACP_STATE_AGGREGATION))) || + ((lacpdu->actor_state & LACP_STATE_AGGREGATION) == 0) ) { port->sm_vars |= AD_PORT_MATCHED; } else { @@ -482,18 +482,18 @@ static void __record_pdu(struct lacpdu *lacpdu, struct port *port) partner->port_state = lacpdu->actor_state; /* set actor_oper_port_state.defaulted to FALSE */ - port->actor_oper_port_state &= ~AD_STATE_DEFAULTED; + port->actor_oper_port_state &= ~LACP_STATE_DEFAULTED; /* set the partner sync. to on if the partner is sync, * and the port is matched */ if ((port->sm_vars & AD_PORT_MATCHED) && - (lacpdu->actor_state & AD_STATE_SYNCHRONIZATION)) { - partner->port_state |= AD_STATE_SYNCHRONIZATION; + (lacpdu->actor_state & LACP_STATE_SYNCHRONIZATION)) { + partner->port_state |= LACP_STATE_SYNCHRONIZATION; slave_dbg(port->slave->bond->dev, port->slave->dev, "partner sync=1\n"); } else { - partner->port_state &= ~AD_STATE_SYNCHRONIZATION; + partner->port_state &= ~LACP_STATE_SYNCHRONIZATION; slave_dbg(port->slave->bond->dev, port->slave->dev, "partner sync=0\n"); } @@ -516,7 +516,7 @@ static void __record_default(struct port *port) sizeof(struct port_params)); /* set actor_oper_port_state.defaulted to true */ - port->actor_oper_port_state |= AD_STATE_DEFAULTED; + port->actor_oper_port_state |= LACP_STATE_DEFAULTED; } } @@ -546,7 +546,7 @@ static void __update_selected(struct lacpdu *lacpdu, struct port *port) !MAC_ADDRESS_EQUAL(&lacpdu->actor_system, &partner->system) || ntohs(lacpdu->actor_system_priority) != partner->system_priority || ntohs(lacpdu->actor_key) != partner->key || - (lacpdu->actor_state & AD_STATE_AGGREGATION) != (partner->port_state & AD_STATE_AGGREGATION)) { + (lacpdu->actor_state & LACP_STATE_AGGREGATION) != (partner->port_state & LACP_STATE_AGGREGATION)) { port->sm_vars &= ~AD_PORT_SELECTED; } } @@ -578,8 +578,8 @@ static void __update_default_selected(struct port *port) !MAC_ADDRESS_EQUAL(&admin->system, &oper->system) || admin->system_priority != oper->system_priority || admin->key != oper->key || - (admin->port_state & AD_STATE_AGGREGATION) - != (oper->port_state & AD_STATE_AGGREGATION)) { + (admin->port_state & LACP_STATE_AGGREGATION) + != (oper->port_state & LACP_STATE_AGGREGATION)) { port->sm_vars &= ~AD_PORT_SELECTED; } } @@ -609,10 +609,10 @@ static void __update_ntt(struct lacpdu *lacpdu, struct port *port) !MAC_ADDRESS_EQUAL(&(lacpdu->partner_system), &(port->actor_system)) || (ntohs(lacpdu->partner_system_priority) != port->actor_system_priority) || (ntohs(lacpdu->partner_key) != port->actor_oper_port_key) || - ((lacpdu->partner_state & AD_STATE_LACP_ACTIVITY) != (port->actor_oper_port_state & AD_STATE_LACP_ACTIVITY)) || - ((lacpdu->partner_state & AD_STATE_LACP_TIMEOUT) != (port->actor_oper_port_state & AD_STATE_LACP_TIMEOUT)) || - ((lacpdu->partner_state & AD_STATE_SYNCHRONIZATION) != (port->actor_oper_port_state & AD_STATE_SYNCHRONIZATION)) || - ((lacpdu->partner_state & AD_STATE_AGGREGATION) != (port->actor_oper_port_state & AD_STATE_AGGREGATION)) + ((lacpdu->partner_state & LACP_STATE_LACP_ACTIVITY) != (port->actor_oper_port_state & LACP_STATE_LACP_ACTIVITY)) || + ((lacpdu->partner_state & LACP_STATE_LACP_TIMEOUT) != (port->actor_oper_port_state & LACP_STATE_LACP_TIMEOUT)) || + ((lacpdu->partner_state & LACP_STATE_SYNCHRONIZATION) != (port->actor_oper_port_state & LACP_STATE_SYNCHRONIZATION)) || + ((lacpdu->partner_state & LACP_STATE_AGGREGATION) != (port->actor_oper_port_state & LACP_STATE_AGGREGATION)) ) { port->ntt = true; } @@ -968,7 +968,7 @@ static void ad_mux_machine(struct port *port, bool *update_slave_arr) * edable port will take place only after this timer) */ if ((port->sm_vars & AD_PORT_SELECTED) && - (port->partner_oper.port_state & AD_STATE_SYNCHRONIZATION) && + (port->partner_oper.port_state & LACP_STATE_SYNCHRONIZATION) && !__check_agg_selection_timer(port)) { if (port->aggregator->is_active) port->sm_mux_state = @@ -986,14 +986,14 @@ static void ad_mux_machine(struct port *port, bool *update_slave_arr) port->sm_mux_state = AD_MUX_DETACHED; } else if (port->aggregator->is_active) { port->actor_oper_port_state |= - AD_STATE_SYNCHRONIZATION; + LACP_STATE_SYNCHRONIZATION; } break; case AD_MUX_COLLECTING_DISTRIBUTING: if (!(port->sm_vars & AD_PORT_SELECTED) || (port->sm_vars & AD_PORT_STANDBY) || - !(port->partner_oper.port_state & AD_STATE_SYNCHRONIZATION) || - !(port->actor_oper_port_state & AD_STATE_SYNCHRONIZATION)) { + !(port->partner_oper.port_state & LACP_STATE_SYNCHRONIZATION) || + !(port->actor_oper_port_state & LACP_STATE_SYNCHRONIZATION)) { port->sm_mux_state = AD_MUX_ATTACHED; } else { /* if port state hasn't changed make @@ -1022,11 +1022,11 @@ static void ad_mux_machine(struct port *port, bool *update_slave_arr) port->sm_mux_state); switch (port->sm_mux_state) { case AD_MUX_DETACHED: - port->actor_oper_port_state &= ~AD_STATE_SYNCHRONIZATION; + port->actor_oper_port_state &= ~LACP_STATE_SYNCHRONIZATION; ad_disable_collecting_distributing(port, update_slave_arr); - port->actor_oper_port_state &= ~AD_STATE_COLLECTING; - port->actor_oper_port_state &= ~AD_STATE_DISTRIBUTING; + port->actor_oper_port_state &= ~LACP_STATE_COLLECTING; + port->actor_oper_port_state &= ~LACP_STATE_DISTRIBUTING; port->ntt = true; break; case AD_MUX_WAITING: @@ -1035,20 +1035,20 @@ static void ad_mux_machine(struct port *port, bool *update_slave_arr) case AD_MUX_ATTACHED: if (port->aggregator->is_active) port->actor_oper_port_state |= - AD_STATE_SYNCHRONIZATION; + LACP_STATE_SYNCHRONIZATION; else port->actor_oper_port_state &= - ~AD_STATE_SYNCHRONIZATION; - port->actor_oper_port_state &= ~AD_STATE_COLLECTING; - port->actor_oper_port_state &= ~AD_STATE_DISTRIBUTING; + ~LACP_STATE_SYNCHRONIZATION; + port->actor_oper_port_state &= ~LACP_STATE_COLLECTING; + port->actor_oper_port_state &= ~LACP_STATE_DISTRIBUTING; ad_disable_collecting_distributing(port, update_slave_arr); port->ntt = true; break; case AD_MUX_COLLECTING_DISTRIBUTING: - port->actor_oper_port_state |= AD_STATE_COLLECTING; - port->actor_oper_port_state |= AD_STATE_DISTRIBUTING; - port->actor_oper_port_state |= AD_STATE_SYNCHRONIZATION; + port->actor_oper_port_state |= LACP_STATE_COLLECTING; + port->actor_oper_port_state |= LACP_STATE_DISTRIBUTING; + port->actor_oper_port_state |= LACP_STATE_SYNCHRONIZATION; ad_enable_collecting_distributing(port, update_slave_arr); port->ntt = true; @@ -1146,7 +1146,7 @@ static void ad_rx_machine(struct lacpdu *lacpdu, struct port *port) port->sm_vars |= AD_PORT_LACP_ENABLED; port->sm_vars &= ~AD_PORT_SELECTED; __record_default(port); - port->actor_oper_port_state &= ~AD_STATE_EXPIRED; + port->actor_oper_port_state &= ~LACP_STATE_EXPIRED; port->sm_rx_state = AD_RX_PORT_DISABLED; /* Fall Through */ @@ -1156,9 +1156,9 @@ static void ad_rx_machine(struct lacpdu *lacpdu, struct port *port) case AD_RX_LACP_DISABLED: port->sm_vars &= ~AD_PORT_SELECTED; __record_default(port); - port->partner_oper.port_state &= ~AD_STATE_AGGREGATION; + port->partner_oper.port_state &= ~LACP_STATE_AGGREGATION; port->sm_vars |= AD_PORT_MATCHED; - port->actor_oper_port_state &= ~AD_STATE_EXPIRED; + port->actor_oper_port_state &= ~LACP_STATE_EXPIRED; break; case AD_RX_EXPIRED: /* Reset of the Synchronization flag (Standard 43.4.12) @@ -1167,19 +1167,19 @@ static void ad_rx_machine(struct lacpdu *lacpdu, struct port *port) * case of EXPIRED even if LINK_DOWN didn't arrive for * the port. */ - port->partner_oper.port_state &= ~AD_STATE_SYNCHRONIZATION; + port->partner_oper.port_state &= ~LACP_STATE_SYNCHRONIZATION; port->sm_vars &= ~AD_PORT_MATCHED; - port->partner_oper.port_state |= AD_STATE_LACP_TIMEOUT; - port->partner_oper.port_state |= AD_STATE_LACP_ACTIVITY; + port->partner_oper.port_state |= LACP_STATE_LACP_TIMEOUT; + port->partner_oper.port_state |= LACP_STATE_LACP_ACTIVITY; port->sm_rx_timer_counter = __ad_timer_to_ticks(AD_CURRENT_WHILE_TIMER, (u16)(AD_SHORT_TIMEOUT)); - port->actor_oper_port_state |= AD_STATE_EXPIRED; + port->actor_oper_port_state |= LACP_STATE_EXPIRED; port->sm_vars |= AD_PORT_CHURNED; break; case AD_RX_DEFAULTED: __update_default_selected(port); __record_default(port); port->sm_vars |= AD_PORT_MATCHED; - port->actor_oper_port_state &= ~AD_STATE_EXPIRED; + port->actor_oper_port_state &= ~LACP_STATE_EXPIRED; break; case AD_RX_CURRENT: /* detect loopback situation */ @@ -1192,8 +1192,8 @@ static void ad_rx_machine(struct lacpdu *lacpdu, struct port *port) __update_selected(lacpdu, port); __update_ntt(lacpdu, port); __record_pdu(lacpdu, port); - port->sm_rx_timer_counter = __ad_timer_to_ticks(AD_CURRENT_WHILE_TIMER, (u16)(port->actor_oper_port_state & AD_STATE_LACP_TIMEOUT)); - port->actor_oper_port_state &= ~AD_STATE_EXPIRED; + port->sm_rx_timer_counter = __ad_timer_to_ticks(AD_CURRENT_WHILE_TIMER, (u16)(port->actor_oper_port_state & LACP_STATE_LACP_TIMEOUT)); + port->actor_oper_port_state &= ~LACP_STATE_EXPIRED; break; default: break; @@ -1221,7 +1221,7 @@ static void ad_churn_machine(struct port *port) if (port->sm_churn_actor_timer_counter && !(--port->sm_churn_actor_timer_counter) && port->sm_churn_actor_state == AD_CHURN_MONITOR) { - if (port->actor_oper_port_state & AD_STATE_SYNCHRONIZATION) { + if (port->actor_oper_port_state & LACP_STATE_SYNCHRONIZATION) { port->sm_churn_actor_state = AD_NO_CHURN; } else { port->churn_actor_count++; @@ -1231,7 +1231,7 @@ static void ad_churn_machine(struct port *port) if (port->sm_churn_partner_timer_counter && !(--port->sm_churn_partner_timer_counter) && port->sm_churn_partner_state == AD_CHURN_MONITOR) { - if (port->partner_oper.port_state & AD_STATE_SYNCHRONIZATION) { + if (port->partner_oper.port_state & LACP_STATE_SYNCHRONIZATION) { port->sm_churn_partner_state = AD_NO_CHURN; } else { port->churn_partner_count++; @@ -1288,7 +1288,7 @@ static void ad_periodic_machine(struct port *port) /* check if port was reinitialized */ if (((port->sm_vars & AD_PORT_BEGIN) || !(port->sm_vars & AD_PORT_LACP_ENABLED) || !port->is_enabled) || - (!(port->actor_oper_port_state & AD_STATE_LACP_ACTIVITY) && !(port->partner_oper.port_state & AD_STATE_LACP_ACTIVITY)) + (!(port->actor_oper_port_state & LACP_STATE_LACP_ACTIVITY) && !(port->partner_oper.port_state & LACP_STATE_LACP_ACTIVITY)) ) { port->sm_periodic_state = AD_NO_PERIODIC; } @@ -1305,11 +1305,11 @@ static void ad_periodic_machine(struct port *port) switch (port->sm_periodic_state) { case AD_FAST_PERIODIC: if (!(port->partner_oper.port_state - & AD_STATE_LACP_TIMEOUT)) + & LACP_STATE_LACP_TIMEOUT)) port->sm_periodic_state = AD_SLOW_PERIODIC; break; case AD_SLOW_PERIODIC: - if ((port->partner_oper.port_state & AD_STATE_LACP_TIMEOUT)) { + if ((port->partner_oper.port_state & LACP_STATE_LACP_TIMEOUT)) { port->sm_periodic_timer_counter = 0; port->sm_periodic_state = AD_PERIODIC_TX; } @@ -1325,7 +1325,7 @@ static void ad_periodic_machine(struct port *port) break; case AD_PERIODIC_TX: if (!(port->partner_oper.port_state & - AD_STATE_LACP_TIMEOUT)) + LACP_STATE_LACP_TIMEOUT)) port->sm_periodic_state = AD_SLOW_PERIODIC; else port->sm_periodic_state = AD_FAST_PERIODIC; @@ -1532,7 +1532,7 @@ static void ad_port_selection_logic(struct port *port, bool *update_slave_arr) ad_agg_selection_logic(aggregator, update_slave_arr); if (!port->aggregator->is_active) - port->actor_oper_port_state &= ~AD_STATE_SYNCHRONIZATION; + port->actor_oper_port_state &= ~LACP_STATE_SYNCHRONIZATION; } /* Decide if "agg" is a better choice for the new active aggregator that @@ -1838,13 +1838,13 @@ static void ad_initialize_port(struct port *port, int lacp_fast) port->actor_port_priority = 0xff; port->actor_port_aggregator_identifier = 0; port->ntt = false; - port->actor_admin_port_state = AD_STATE_AGGREGATION | - AD_STATE_LACP_ACTIVITY; - port->actor_oper_port_state = AD_STATE_AGGREGATION | - AD_STATE_LACP_ACTIVITY; + port->actor_admin_port_state = LACP_STATE_AGGREGATION | + LACP_STATE_LACP_ACTIVITY; + port->actor_oper_port_state = LACP_STATE_AGGREGATION | + LACP_STATE_LACP_ACTIVITY; if (lacp_fast) - port->actor_oper_port_state |= AD_STATE_LACP_TIMEOUT; + port->actor_oper_port_state |= LACP_STATE_LACP_TIMEOUT; memcpy(&port->partner_admin, &tmpl, sizeof(tmpl)); memcpy(&port->partner_oper, &tmpl, sizeof(tmpl)); @@ -2095,10 +2095,10 @@ void bond_3ad_unbind_slave(struct slave *slave) aggregator->aggregator_identifier); /* Tell the partner that this port is not suitable for aggregation */ - port->actor_oper_port_state &= ~AD_STATE_SYNCHRONIZATION; - port->actor_oper_port_state &= ~AD_STATE_COLLECTING; - port->actor_oper_port_state &= ~AD_STATE_DISTRIBUTING; - port->actor_oper_port_state &= ~AD_STATE_AGGREGATION; + port->actor_oper_port_state &= ~LACP_STATE_SYNCHRONIZATION; + port->actor_oper_port_state &= ~LACP_STATE_COLLECTING; + port->actor_oper_port_state &= ~LACP_STATE_DISTRIBUTING; + port->actor_oper_port_state &= ~LACP_STATE_AGGREGATION; __update_lacpdu_from_port(port); ad_lacpdu_send(port); @@ -2685,9 +2685,9 @@ void bond_3ad_update_lacp_rate(struct bonding *bond) bond_for_each_slave(bond, slave, iter) { port = &(SLAVE_AD_INFO(slave)->port); if (lacp_fast) - port->actor_oper_port_state |= AD_STATE_LACP_TIMEOUT; + port->actor_oper_port_state |= LACP_STATE_LACP_TIMEOUT; else - port->actor_oper_port_state &= ~AD_STATE_LACP_TIMEOUT; + port->actor_oper_port_state &= ~LACP_STATE_LACP_TIMEOUT; } spin_unlock_bh(&bond->mode_lock); } diff --git a/include/uapi/linux/if_bonding.h b/include/uapi/linux/if_bonding.h index 6829213a54c5c..45f3750aa861b 100644 --- a/include/uapi/linux/if_bonding.h +++ b/include/uapi/linux/if_bonding.h @@ -96,14 +96,14 @@ #define BOND_XMIT_POLICY_ENCAP34 4 /* encapsulated layer 3+4 */ /* 802.3ad port state definitions (43.4.2.2 in the 802.3ad standard) */ -#define AD_STATE_LACP_ACTIVITY 0x1 -#define AD_STATE_LACP_TIMEOUT 0x2 -#define AD_STATE_AGGREGATION 0x4 -#define AD_STATE_SYNCHRONIZATION 0x8 -#define AD_STATE_COLLECTING 0x10 -#define AD_STATE_DISTRIBUTING 0x20 -#define AD_STATE_DEFAULTED 0x40 -#define AD_STATE_EXPIRED 0x80 +#define LACP_STATE_LACP_ACTIVITY 0x1 +#define LACP_STATE_LACP_TIMEOUT 0x2 +#define LACP_STATE_AGGREGATION 0x4 +#define LACP_STATE_SYNCHRONIZATION 0x8 +#define LACP_STATE_COLLECTING 0x10 +#define LACP_STATE_DISTRIBUTING 0x20 +#define LACP_STATE_DEFAULTED 0x40 +#define LACP_STATE_EXPIRED 0x80 typedef struct ifbond { __s32 bond_mode; -- GitLab From 231c8d2bbc4feac4c5db6afbda2484f24661736b Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Thu, 26 Dec 2019 18:41:13 +0200 Subject: [PATCH 1355/1437] mlxsw: spectrum_router: Remove unnecessary checks Now that the networking stack takes care of only notifying the routes of interest, we do not need to maintain a list of identical routes. Remove the check that tests if the route is the first route in the FIB node. Signed-off-by: Ido Schimmel Acked-by: Jiri Pirko Signed-off-by: David S. Miller --- .../ethernet/mellanox/mlxsw/spectrum_router.c | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c index f62e8d67348cf..87a010cb43b1b 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c @@ -3231,10 +3231,6 @@ mlxsw_sp_nexthop_group_update(struct mlxsw_sp *mlxsw_sp, return 0; } -static bool -mlxsw_sp_fib_node_entry_is_first(const struct mlxsw_sp_fib_node *fib_node, - const struct mlxsw_sp_fib_entry *fib_entry); - static int mlxsw_sp_nexthop_fib_entries_update(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_nexthop_group *nh_grp) @@ -3243,9 +3239,6 @@ mlxsw_sp_nexthop_fib_entries_update(struct mlxsw_sp *mlxsw_sp, int err; list_for_each_entry(fib_entry, &nh_grp->fib_list, nexthop_group_node) { - if (!mlxsw_sp_fib_node_entry_is_first(fib_entry->fib_node, - fib_entry)) - continue; err = mlxsw_sp_fib_entry_update(mlxsw_sp, fib_entry); if (err) return err; @@ -3263,12 +3256,8 @@ mlxsw_sp_nexthop_fib_entries_refresh(struct mlxsw_sp_nexthop_group *nh_grp) enum mlxsw_reg_ralue_op op = MLXSW_REG_RALUE_OP_WRITE_WRITE; struct mlxsw_sp_fib_entry *fib_entry; - list_for_each_entry(fib_entry, &nh_grp->fib_list, nexthop_group_node) { - if (!mlxsw_sp_fib_node_entry_is_first(fib_entry->fib_node, - fib_entry)) - continue; + list_for_each_entry(fib_entry, &nh_grp->fib_list, nexthop_group_node) mlxsw_sp_fib_entry_offload_refresh(fib_entry, op, 0); - } } static void mlxsw_sp_adj_grp_size_round_up(u16 *p_adj_grp_size) @@ -4785,9 +4774,6 @@ static int mlxsw_sp_fib_node_entry_add(struct mlxsw_sp *mlxsw_sp, { struct mlxsw_sp_fib_node *fib_node = fib_entry->fib_node; - if (!mlxsw_sp_fib_node_entry_is_first(fib_node, fib_entry)) - return 0; - /* To prevent packet loss, overwrite the previously offloaded * entry. */ -- GitLab From 0d2fb5aa93a634316aa5b061170e9732a84d1217 Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Thu, 26 Dec 2019 18:41:14 +0200 Subject: [PATCH 1356/1437] mlxsw: spectrum_router: Eliminate dead code Since the driver no longer maintains a list of identical routes there is no route to promote when a route is deleted. Remove that code that took care of it. Signed-off-by: Ido Schimmel Acked-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c index 87a010cb43b1b..0439b2399a535 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c @@ -4795,16 +4795,6 @@ static void mlxsw_sp_fib_node_entry_del(struct mlxsw_sp *mlxsw_sp, if (!mlxsw_sp_fib_node_entry_is_first(fib_node, fib_entry)) return; - /* Promote the next entry by overwriting the deleted entry */ - if (!list_is_singular(&fib_node->entry_list)) { - struct mlxsw_sp_fib_entry *n = list_next_entry(fib_entry, list); - enum mlxsw_reg_ralue_op op = MLXSW_REG_RALUE_OP_WRITE_DELETE; - - mlxsw_sp_fib_entry_update(mlxsw_sp, n); - mlxsw_sp_fib_entry_offload_refresh(fib_entry, op, 0); - return; - } - mlxsw_sp_fib_entry_del(mlxsw_sp, fib_entry); } -- GitLab From 0705297e511a334e99edf7da0d9a79f4d6b1c028 Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Thu, 26 Dec 2019 18:41:15 +0200 Subject: [PATCH 1357/1437] mlxsw: spectrum_router: Make route creation and destruction symmetric Host routes that perform decapsulation of IP in IP tunnels have a special adjacency entry linked to them. This entry stores information such as the expected underlay source IP. When the route is deleted this entry needs to be freed. The allocation of the adjacency entry happens in mlxsw_sp_fib4_entry_type_set(), but it is freed in mlxsw_sp_fib4_node_entry_unlink(). Create a new function - mlxsw_sp_fib4_entry_type_unset() - and free the adjacency entry there. This will allow us to consolidate mlxsw_sp_fib{4,6}_node_entry_unlink() in the next patch. Signed-off-by: Ido Schimmel Acked-by: Jiri Pirko Signed-off-by: David S. Miller --- .../ethernet/mellanox/mlxsw/spectrum_router.c | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c index 0439b2399a535..940832761d1b7 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c @@ -4480,6 +4480,19 @@ mlxsw_sp_fib4_entry_type_set(struct mlxsw_sp *mlxsw_sp, } } +static void +mlxsw_sp_fib4_entry_type_unset(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_fib_entry *fib_entry) +{ + switch (fib_entry->type) { + case MLXSW_SP_FIB_ENTRY_TYPE_IPIP_DECAP: + mlxsw_sp_fib_entry_decap_fini(mlxsw_sp, fib_entry); + break; + default: + break; + } +} + static struct mlxsw_sp_fib4_entry * mlxsw_sp_fib4_entry_create(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_fib_node *fib_node, @@ -4512,6 +4525,7 @@ mlxsw_sp_fib4_entry_create(struct mlxsw_sp *mlxsw_sp, return fib4_entry; err_nexthop4_group_get: + mlxsw_sp_fib4_entry_type_unset(mlxsw_sp, fib_entry); err_fib4_entry_type_set: kfree(fib4_entry); return ERR_PTR(err); @@ -4521,6 +4535,7 @@ static void mlxsw_sp_fib4_entry_destroy(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_fib4_entry *fib4_entry) { mlxsw_sp_nexthop4_group_put(mlxsw_sp, &fib4_entry->common); + mlxsw_sp_fib4_entry_type_unset(mlxsw_sp, &fib4_entry->common); kfree(fib4_entry); } @@ -4823,9 +4838,6 @@ mlxsw_sp_fib4_node_entry_unlink(struct mlxsw_sp *mlxsw_sp, { mlxsw_sp_fib_node_entry_del(mlxsw_sp, &fib4_entry->common); list_del(&fib4_entry->common.list); - - if (fib4_entry->common.type == MLXSW_SP_FIB_ENTRY_TYPE_IPIP_DECAP) - mlxsw_sp_fib_entry_decap_fini(mlxsw_sp, &fib4_entry->common); } static void mlxsw_sp_fib4_entry_replace(struct mlxsw_sp *mlxsw_sp, -- GitLab From b04720aee9a61b0b2f03a81d575fdcfb84c43c34 Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Thu, 26 Dec 2019 18:41:16 +0200 Subject: [PATCH 1358/1437] mlxsw: spectrum_router: Consolidate identical functions After the last patch mlxsw_sp_fib{4,6}_node_entry_link() and mlxsw_sp_fib{4,6}_node_entry_unlink() are identical and can therefore be consolidated into the same common function. Perform the consolidation. Signed-off-by: Ido Schimmel Acked-by: Jiri Pirko Signed-off-by: David S. Miller --- .../ethernet/mellanox/mlxsw/spectrum_router.c | 71 ++++++------------- 1 file changed, 22 insertions(+), 49 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c index 940832761d1b7..f332c55fc83e9 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c @@ -4813,31 +4813,31 @@ static void mlxsw_sp_fib_node_entry_del(struct mlxsw_sp *mlxsw_sp, mlxsw_sp_fib_entry_del(mlxsw_sp, fib_entry); } -static int mlxsw_sp_fib4_node_entry_link(struct mlxsw_sp *mlxsw_sp, - struct mlxsw_sp_fib4_entry *fib4_entry) +static int mlxsw_sp_fib_node_entry_link(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_fib_entry *fib_entry) { - struct mlxsw_sp_fib_node *fib_node = fib4_entry->common.fib_node; + struct mlxsw_sp_fib_node *fib_node = fib_entry->fib_node; int err; - list_add(&fib4_entry->common.list, &fib_node->entry_list); + list_add(&fib_entry->list, &fib_node->entry_list); - err = mlxsw_sp_fib_node_entry_add(mlxsw_sp, &fib4_entry->common); + err = mlxsw_sp_fib_node_entry_add(mlxsw_sp, fib_entry); if (err) goto err_fib_node_entry_add; return 0; err_fib_node_entry_add: - list_del(&fib4_entry->common.list); + list_del(&fib_entry->list); return err; } static void -mlxsw_sp_fib4_node_entry_unlink(struct mlxsw_sp *mlxsw_sp, - struct mlxsw_sp_fib4_entry *fib4_entry) +mlxsw_sp_fib_node_entry_unlink(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_fib_entry *fib_entry) { - mlxsw_sp_fib_node_entry_del(mlxsw_sp, &fib4_entry->common); - list_del(&fib4_entry->common.list); + mlxsw_sp_fib_node_entry_del(mlxsw_sp, fib_entry); + list_del(&fib_entry->list); } static void mlxsw_sp_fib4_entry_replace(struct mlxsw_sp *mlxsw_sp, @@ -4852,7 +4852,7 @@ static void mlxsw_sp_fib4_entry_replace(struct mlxsw_sp *mlxsw_sp, /* We inserted the new entry before replaced one */ replaced = list_next_entry(fib4_entry, common.list); - mlxsw_sp_fib4_node_entry_unlink(mlxsw_sp, replaced); + mlxsw_sp_fib_node_entry_unlink(mlxsw_sp, &replaced->common); mlxsw_sp_fib4_entry_destroy(mlxsw_sp, replaced); mlxsw_sp_fib_node_put(mlxsw_sp, fib_node); } @@ -4884,17 +4884,17 @@ mlxsw_sp_router_fib4_replace(struct mlxsw_sp *mlxsw_sp, goto err_fib4_entry_create; } - err = mlxsw_sp_fib4_node_entry_link(mlxsw_sp, fib4_entry); + err = mlxsw_sp_fib_node_entry_link(mlxsw_sp, &fib4_entry->common); if (err) { dev_warn(mlxsw_sp->bus_info->dev, "Failed to link FIB entry to node\n"); - goto err_fib4_node_entry_link; + goto err_fib_node_entry_link; } mlxsw_sp_fib4_entry_replace(mlxsw_sp, fib4_entry); return 0; -err_fib4_node_entry_link: +err_fib_node_entry_link: mlxsw_sp_fib4_entry_destroy(mlxsw_sp, fib4_entry); err_fib4_entry_create: mlxsw_sp_fib_node_put(mlxsw_sp, fib_node); @@ -4915,7 +4915,7 @@ static void mlxsw_sp_router_fib4_del(struct mlxsw_sp *mlxsw_sp, return; fib_node = fib4_entry->common.fib_node; - mlxsw_sp_fib4_node_entry_unlink(mlxsw_sp, fib4_entry); + mlxsw_sp_fib_node_entry_unlink(mlxsw_sp, &fib4_entry->common); mlxsw_sp_fib4_entry_destroy(mlxsw_sp, fib4_entry); mlxsw_sp_fib_node_put(mlxsw_sp, fib_node); } @@ -5374,33 +5374,6 @@ static void mlxsw_sp_fib6_entry_destroy(struct mlxsw_sp *mlxsw_sp, kfree(fib6_entry); } -static int mlxsw_sp_fib6_node_entry_link(struct mlxsw_sp *mlxsw_sp, - struct mlxsw_sp_fib6_entry *fib6_entry) -{ - struct mlxsw_sp_fib_node *fib_node = fib6_entry->common.fib_node; - int err; - - list_add(&fib6_entry->common.list, &fib_node->entry_list); - - err = mlxsw_sp_fib_node_entry_add(mlxsw_sp, &fib6_entry->common); - if (err) - goto err_fib_node_entry_add; - - return 0; - -err_fib_node_entry_add: - list_del(&fib6_entry->common.list); - return err; -} - -static void -mlxsw_sp_fib6_node_entry_unlink(struct mlxsw_sp *mlxsw_sp, - struct mlxsw_sp_fib6_entry *fib6_entry) -{ - mlxsw_sp_fib_node_entry_del(mlxsw_sp, &fib6_entry->common); - list_del(&fib6_entry->common.list); -} - static struct mlxsw_sp_fib6_entry * mlxsw_sp_fib6_entry_lookup(struct mlxsw_sp *mlxsw_sp, const struct fib6_info *rt) @@ -5445,7 +5418,7 @@ static void mlxsw_sp_fib6_entry_replace(struct mlxsw_sp *mlxsw_sp, /* We inserted the new entry before replaced one */ replaced = list_next_entry(fib6_entry, common.list); - mlxsw_sp_fib6_node_entry_unlink(mlxsw_sp, replaced); + mlxsw_sp_fib_node_entry_unlink(mlxsw_sp, &replaced->common); mlxsw_sp_fib6_entry_destroy(mlxsw_sp, replaced); mlxsw_sp_fib_node_put(mlxsw_sp, fib_node); } @@ -5483,15 +5456,15 @@ static int mlxsw_sp_router_fib6_replace(struct mlxsw_sp *mlxsw_sp, goto err_fib6_entry_create; } - err = mlxsw_sp_fib6_node_entry_link(mlxsw_sp, fib6_entry); + err = mlxsw_sp_fib_node_entry_link(mlxsw_sp, &fib6_entry->common); if (err) - goto err_fib6_node_entry_link; + goto err_fib_node_entry_link; mlxsw_sp_fib6_entry_replace(mlxsw_sp, fib6_entry); return 0; -err_fib6_node_entry_link: +err_fib_node_entry_link: mlxsw_sp_fib6_entry_destroy(mlxsw_sp, fib6_entry); err_fib6_entry_create: mlxsw_sp_fib_node_put(mlxsw_sp, fib_node); @@ -5577,7 +5550,7 @@ static void mlxsw_sp_router_fib6_del(struct mlxsw_sp *mlxsw_sp, fib_node = fib6_entry->common.fib_node; - mlxsw_sp_fib6_node_entry_unlink(mlxsw_sp, fib6_entry); + mlxsw_sp_fib_node_entry_unlink(mlxsw_sp, &fib6_entry->common); mlxsw_sp_fib6_entry_destroy(mlxsw_sp, fib6_entry); mlxsw_sp_fib_node_put(mlxsw_sp, fib_node); } @@ -5737,7 +5710,7 @@ static void mlxsw_sp_fib4_node_flush(struct mlxsw_sp *mlxsw_sp, common.list) { bool do_break = &tmp->common.list == &fib_node->entry_list; - mlxsw_sp_fib4_node_entry_unlink(mlxsw_sp, fib4_entry); + mlxsw_sp_fib_node_entry_unlink(mlxsw_sp, &fib4_entry->common); mlxsw_sp_fib4_entry_destroy(mlxsw_sp, fib4_entry); mlxsw_sp_fib_node_put(mlxsw_sp, fib_node); /* Break when entry list is empty and node was freed. @@ -5758,7 +5731,7 @@ static void mlxsw_sp_fib6_node_flush(struct mlxsw_sp *mlxsw_sp, common.list) { bool do_break = &tmp->common.list == &fib_node->entry_list; - mlxsw_sp_fib6_node_entry_unlink(mlxsw_sp, fib6_entry); + mlxsw_sp_fib_node_entry_unlink(mlxsw_sp, &fib6_entry->common); mlxsw_sp_fib6_entry_destroy(mlxsw_sp, fib6_entry); mlxsw_sp_fib_node_put(mlxsw_sp, fib_node); if (do_break) -- GitLab From 7c4a7ec8558dfc770c27931b43f01aede613faac Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Thu, 26 Dec 2019 18:41:17 +0200 Subject: [PATCH 1359/1437] mlxsw: spectrum_router: Remove FIB entry list from FIB node As explained in previous patches, the driver no longer needs to maintain a list of identical FIB entries (i.e, same {tb_id, prefix, prefix length}) and therefore each FIB node can only store one FIB entry. Remove the FIB entry list and simplify the code. Signed-off-by: Ido Schimmel Acked-by: Jiri Pirko Signed-off-by: David S. Miller --- .../ethernet/mellanox/mlxsw/spectrum_router.c | 225 ++++++------------ 1 file changed, 74 insertions(+), 151 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c index f332c55fc83e9..da1c8342c8f58 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c @@ -382,9 +382,10 @@ enum mlxsw_sp_fib_entry_type { }; struct mlxsw_sp_nexthop_group; +struct mlxsw_sp_fib_entry; struct mlxsw_sp_fib_node { - struct list_head entry_list; + struct mlxsw_sp_fib_entry *fib_entry; struct list_head list; struct rhash_head ht_node; struct mlxsw_sp_fib *fib; @@ -397,7 +398,6 @@ struct mlxsw_sp_fib_entry_decap { }; struct mlxsw_sp_fib_entry { - struct list_head list; struct mlxsw_sp_fib_node *fib_node; enum mlxsw_sp_fib_entry_type type; struct list_head nexthop_group_node; @@ -1162,7 +1162,6 @@ mlxsw_sp_router_ip2me_fib_entry_find(struct mlxsw_sp *mlxsw_sp, u32 tb_id, const union mlxsw_sp_l3addr *addr, enum mlxsw_sp_fib_entry_type type) { - struct mlxsw_sp_fib_entry *fib_entry; struct mlxsw_sp_fib_node *fib_node; unsigned char addr_prefix_len; struct mlxsw_sp_fib *fib; @@ -1191,15 +1190,10 @@ mlxsw_sp_router_ip2me_fib_entry_find(struct mlxsw_sp *mlxsw_sp, u32 tb_id, fib_node = mlxsw_sp_fib_node_lookup(fib, addrp, addr_len, addr_prefix_len); - if (!fib_node || list_empty(&fib_node->entry_list)) + if (!fib_node || fib_node->fib_entry->type != type) return NULL; - fib_entry = list_first_entry(&fib_node->entry_list, - struct mlxsw_sp_fib_entry, list); - if (fib_entry->type != type) - return NULL; - - return fib_entry; + return fib_node->fib_entry; } /* Given an IPIP entry, find the corresponding decap route. */ @@ -1209,7 +1203,6 @@ mlxsw_sp_ipip_entry_find_decap(struct mlxsw_sp *mlxsw_sp, { static struct mlxsw_sp_fib_node *fib_node; const struct mlxsw_sp_ipip_ops *ipip_ops; - struct mlxsw_sp_fib_entry *fib_entry; unsigned char saddr_prefix_len; union mlxsw_sp_l3addr saddr; struct mlxsw_sp_fib *ul_fib; @@ -1244,15 +1237,11 @@ mlxsw_sp_ipip_entry_find_decap(struct mlxsw_sp *mlxsw_sp, fib_node = mlxsw_sp_fib_node_lookup(ul_fib, saddrp, saddr_len, saddr_prefix_len); - if (!fib_node || list_empty(&fib_node->entry_list)) + if (!fib_node || + fib_node->fib_entry->type != MLXSW_SP_FIB_ENTRY_TYPE_TRAP) return NULL; - fib_entry = list_first_entry(&fib_node->entry_list, - struct mlxsw_sp_fib_entry, list); - if (fib_entry->type != MLXSW_SP_FIB_ENTRY_TYPE_TRAP) - return NULL; - - return fib_entry; + return fib_node->fib_entry; } static struct mlxsw_sp_ipip_entry * @@ -4559,15 +4548,14 @@ mlxsw_sp_fib4_entry_lookup(struct mlxsw_sp *mlxsw_sp, if (!fib_node) return NULL; - list_for_each_entry(fib4_entry, &fib_node->entry_list, common.list) { - if (fib4_entry->tb_id == fen_info->tb_id && - fib4_entry->tos == fen_info->tos && - fib4_entry->type == fen_info->type && - mlxsw_sp_nexthop4_group_fi(fib4_entry->common.nh_group) == - fen_info->fi) { - return fib4_entry; - } - } + fib4_entry = container_of(fib_node->fib_entry, + struct mlxsw_sp_fib4_entry, common); + if (fib4_entry->tb_id == fen_info->tb_id && + fib4_entry->tos == fen_info->tos && + fib4_entry->type == fen_info->type && + mlxsw_sp_nexthop4_group_fi(fib4_entry->common.nh_group) == + fen_info->fi) + return fib4_entry; return NULL; } @@ -4615,7 +4603,6 @@ mlxsw_sp_fib_node_create(struct mlxsw_sp_fib *fib, const void *addr, if (!fib_node) return NULL; - INIT_LIST_HEAD(&fib_node->entry_list); list_add(&fib_node->list, &fib->node_list); memcpy(fib_node->key.addr, addr, addr_len); fib_node->key.prefix_len = prefix_len; @@ -4626,18 +4613,9 @@ mlxsw_sp_fib_node_create(struct mlxsw_sp_fib *fib, const void *addr, static void mlxsw_sp_fib_node_destroy(struct mlxsw_sp_fib_node *fib_node) { list_del(&fib_node->list); - WARN_ON(!list_empty(&fib_node->entry_list)); kfree(fib_node); } -static bool -mlxsw_sp_fib_node_entry_is_first(const struct mlxsw_sp_fib_node *fib_node, - const struct mlxsw_sp_fib_entry *fib_entry) -{ - return list_first_entry(&fib_node->entry_list, - struct mlxsw_sp_fib_entry, list) == fib_entry; -} - static int mlxsw_sp_fib_lpm_tree_link(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_fib_node *fib_node) { @@ -4777,58 +4755,29 @@ static void mlxsw_sp_fib_node_put(struct mlxsw_sp *mlxsw_sp, { struct mlxsw_sp_vr *vr = fib_node->fib->vr; - if (!list_empty(&fib_node->entry_list)) + if (fib_node->fib_entry) return; mlxsw_sp_fib_node_fini(mlxsw_sp, fib_node); mlxsw_sp_fib_node_destroy(fib_node); mlxsw_sp_vr_put(mlxsw_sp, vr); } -static int mlxsw_sp_fib_node_entry_add(struct mlxsw_sp *mlxsw_sp, - struct mlxsw_sp_fib_entry *fib_entry) -{ - struct mlxsw_sp_fib_node *fib_node = fib_entry->fib_node; - - /* To prevent packet loss, overwrite the previously offloaded - * entry. - */ - if (!list_is_singular(&fib_node->entry_list)) { - enum mlxsw_reg_ralue_op op = MLXSW_REG_RALUE_OP_WRITE_DELETE; - struct mlxsw_sp_fib_entry *n = list_next_entry(fib_entry, list); - - mlxsw_sp_fib_entry_offload_refresh(n, op, 0); - } - - return mlxsw_sp_fib_entry_update(mlxsw_sp, fib_entry); -} - -static void mlxsw_sp_fib_node_entry_del(struct mlxsw_sp *mlxsw_sp, - struct mlxsw_sp_fib_entry *fib_entry) -{ - struct mlxsw_sp_fib_node *fib_node = fib_entry->fib_node; - - if (!mlxsw_sp_fib_node_entry_is_first(fib_node, fib_entry)) - return; - - mlxsw_sp_fib_entry_del(mlxsw_sp, fib_entry); -} - static int mlxsw_sp_fib_node_entry_link(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_fib_entry *fib_entry) { struct mlxsw_sp_fib_node *fib_node = fib_entry->fib_node; int err; - list_add(&fib_entry->list, &fib_node->entry_list); + fib_node->fib_entry = fib_entry; - err = mlxsw_sp_fib_node_entry_add(mlxsw_sp, fib_entry); + err = mlxsw_sp_fib_entry_update(mlxsw_sp, fib_entry); if (err) - goto err_fib_node_entry_add; + goto err_fib_entry_update; return 0; -err_fib_node_entry_add: - list_del(&fib_entry->list); +err_fib_entry_update: + fib_node->fib_entry = NULL; return err; } @@ -4836,32 +4785,18 @@ static void mlxsw_sp_fib_node_entry_unlink(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_fib_entry *fib_entry) { - mlxsw_sp_fib_node_entry_del(mlxsw_sp, fib_entry); - list_del(&fib_entry->list); -} - -static void mlxsw_sp_fib4_entry_replace(struct mlxsw_sp *mlxsw_sp, - struct mlxsw_sp_fib4_entry *fib4_entry) -{ - struct mlxsw_sp_fib_node *fib_node = fib4_entry->common.fib_node; - struct mlxsw_sp_fib4_entry *replaced; - - if (list_is_singular(&fib_node->entry_list)) - return; - - /* We inserted the new entry before replaced one */ - replaced = list_next_entry(fib4_entry, common.list); + struct mlxsw_sp_fib_node *fib_node = fib_entry->fib_node; - mlxsw_sp_fib_node_entry_unlink(mlxsw_sp, &replaced->common); - mlxsw_sp_fib4_entry_destroy(mlxsw_sp, replaced); - mlxsw_sp_fib_node_put(mlxsw_sp, fib_node); + mlxsw_sp_fib_entry_del(mlxsw_sp, fib_entry); + fib_node->fib_entry = NULL; } static int mlxsw_sp_router_fib4_replace(struct mlxsw_sp *mlxsw_sp, const struct fib_entry_notifier_info *fen_info) { - struct mlxsw_sp_fib4_entry *fib4_entry; + struct mlxsw_sp_fib4_entry *fib4_entry, *fib4_replaced; + struct mlxsw_sp_fib_entry *replaced; struct mlxsw_sp_fib_node *fib_node; int err; @@ -4884,17 +4819,26 @@ mlxsw_sp_router_fib4_replace(struct mlxsw_sp *mlxsw_sp, goto err_fib4_entry_create; } + replaced = fib_node->fib_entry; err = mlxsw_sp_fib_node_entry_link(mlxsw_sp, &fib4_entry->common); if (err) { dev_warn(mlxsw_sp->bus_info->dev, "Failed to link FIB entry to node\n"); goto err_fib_node_entry_link; } - mlxsw_sp_fib4_entry_replace(mlxsw_sp, fib4_entry); + /* Nothing to replace */ + if (!replaced) + return 0; + + mlxsw_sp_fib_entry_offload_unset(replaced); + fib4_replaced = container_of(replaced, struct mlxsw_sp_fib4_entry, + common); + mlxsw_sp_fib4_entry_destroy(mlxsw_sp, fib4_replaced); return 0; err_fib_node_entry_link: + fib_node->fib_entry = replaced; mlxsw_sp_fib4_entry_destroy(mlxsw_sp, fib4_entry); err_fib4_entry_create: mlxsw_sp_fib_node_put(mlxsw_sp, fib_node); @@ -5201,16 +5145,16 @@ mlxsw_sp_nexthop6_group_update(struct mlxsw_sp *mlxsw_sp, * currently associated with it in the device's table is that * of the old group. Start using the new one instead. */ - err = mlxsw_sp_fib_node_entry_add(mlxsw_sp, &fib6_entry->common); + err = mlxsw_sp_fib_entry_update(mlxsw_sp, &fib6_entry->common); if (err) - goto err_fib_node_entry_add; + goto err_fib_entry_update; if (list_empty(&old_nh_grp->fib_list)) mlxsw_sp_nexthop6_group_destroy(mlxsw_sp, old_nh_grp); return 0; -err_fib_node_entry_add: +err_fib_entry_update: mlxsw_sp_nexthop6_group_put(mlxsw_sp, &fib6_entry->common); err_nexthop6_group_get: list_add_tail(&fib6_entry->common.nexthop_group_node, @@ -5381,6 +5325,7 @@ mlxsw_sp_fib6_entry_lookup(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_fib6_entry *fib6_entry; struct mlxsw_sp_fib_node *fib_node; struct mlxsw_sp_fib *fib; + struct fib6_info *cmp_rt; struct mlxsw_sp_vr *vr; vr = mlxsw_sp_vr_find(mlxsw_sp, rt->fib6_table->tb6_id); @@ -5394,40 +5339,23 @@ mlxsw_sp_fib6_entry_lookup(struct mlxsw_sp *mlxsw_sp, if (!fib_node) return NULL; - list_for_each_entry(fib6_entry, &fib_node->entry_list, common.list) { - struct fib6_info *iter_rt = mlxsw_sp_fib6_entry_rt(fib6_entry); - - if (rt->fib6_table->tb6_id == iter_rt->fib6_table->tb6_id && - rt->fib6_metric == iter_rt->fib6_metric && - mlxsw_sp_fib6_entry_rt_find(fib6_entry, rt)) - return fib6_entry; - } + fib6_entry = container_of(fib_node->fib_entry, + struct mlxsw_sp_fib6_entry, common); + cmp_rt = mlxsw_sp_fib6_entry_rt(fib6_entry); + if (rt->fib6_table->tb6_id == cmp_rt->fib6_table->tb6_id && + rt->fib6_metric == cmp_rt->fib6_metric && + mlxsw_sp_fib6_entry_rt_find(fib6_entry, rt)) + return fib6_entry; return NULL; } -static void mlxsw_sp_fib6_entry_replace(struct mlxsw_sp *mlxsw_sp, - struct mlxsw_sp_fib6_entry *fib6_entry) -{ - struct mlxsw_sp_fib_node *fib_node = fib6_entry->common.fib_node; - struct mlxsw_sp_fib6_entry *replaced; - - if (list_is_singular(&fib_node->entry_list)) - return; - - /* We inserted the new entry before replaced one */ - replaced = list_next_entry(fib6_entry, common.list); - - mlxsw_sp_fib_node_entry_unlink(mlxsw_sp, &replaced->common); - mlxsw_sp_fib6_entry_destroy(mlxsw_sp, replaced); - mlxsw_sp_fib_node_put(mlxsw_sp, fib_node); -} - static int mlxsw_sp_router_fib6_replace(struct mlxsw_sp *mlxsw_sp, struct fib6_info **rt_arr, unsigned int nrt6) { - struct mlxsw_sp_fib6_entry *fib6_entry; + struct mlxsw_sp_fib6_entry *fib6_entry, *fib6_replaced; + struct mlxsw_sp_fib_entry *replaced; struct mlxsw_sp_fib_node *fib_node; struct fib6_info *rt = rt_arr[0]; int err; @@ -5456,15 +5384,24 @@ static int mlxsw_sp_router_fib6_replace(struct mlxsw_sp *mlxsw_sp, goto err_fib6_entry_create; } + replaced = fib_node->fib_entry; err = mlxsw_sp_fib_node_entry_link(mlxsw_sp, &fib6_entry->common); if (err) goto err_fib_node_entry_link; - mlxsw_sp_fib6_entry_replace(mlxsw_sp, fib6_entry); + /* Nothing to replace */ + if (!replaced) + return 0; + + mlxsw_sp_fib_entry_offload_unset(replaced); + fib6_replaced = container_of(replaced, struct mlxsw_sp_fib6_entry, + common); + mlxsw_sp_fib6_entry_destroy(mlxsw_sp, fib6_replaced); return 0; err_fib_node_entry_link: + fib_node->fib_entry = replaced; mlxsw_sp_fib6_entry_destroy(mlxsw_sp, fib6_entry); err_fib6_entry_create: mlxsw_sp_fib_node_put(mlxsw_sp, fib_node); @@ -5497,13 +5434,13 @@ static int mlxsw_sp_router_fib6_append(struct mlxsw_sp *mlxsw_sp, if (IS_ERR(fib_node)) return PTR_ERR(fib_node); - if (WARN_ON_ONCE(list_empty(&fib_node->entry_list))) { + if (WARN_ON_ONCE(!fib_node->fib_entry)) { mlxsw_sp_fib_node_put(mlxsw_sp, fib_node); return -EINVAL; } - fib6_entry = list_first_entry(&fib_node->entry_list, - struct mlxsw_sp_fib6_entry, common.list); + fib6_entry = container_of(fib_node->fib_entry, + struct mlxsw_sp_fib6_entry, common); err = mlxsw_sp_fib6_entry_nexthop_add(mlxsw_sp, fib6_entry, rt_arr, nrt6); if (err) @@ -5704,39 +5641,25 @@ static int mlxsw_sp_router_set_abort_trap(struct mlxsw_sp *mlxsw_sp) static void mlxsw_sp_fib4_node_flush(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_fib_node *fib_node) { - struct mlxsw_sp_fib4_entry *fib4_entry, *tmp; - - list_for_each_entry_safe(fib4_entry, tmp, &fib_node->entry_list, - common.list) { - bool do_break = &tmp->common.list == &fib_node->entry_list; + struct mlxsw_sp_fib4_entry *fib4_entry; - mlxsw_sp_fib_node_entry_unlink(mlxsw_sp, &fib4_entry->common); - mlxsw_sp_fib4_entry_destroy(mlxsw_sp, fib4_entry); - mlxsw_sp_fib_node_put(mlxsw_sp, fib_node); - /* Break when entry list is empty and node was freed. - * Otherwise, we'll access freed memory in the next - * iteration. - */ - if (do_break) - break; - } + fib4_entry = container_of(fib_node->fib_entry, + struct mlxsw_sp_fib4_entry, common); + mlxsw_sp_fib_node_entry_unlink(mlxsw_sp, fib_node->fib_entry); + mlxsw_sp_fib4_entry_destroy(mlxsw_sp, fib4_entry); + mlxsw_sp_fib_node_put(mlxsw_sp, fib_node); } static void mlxsw_sp_fib6_node_flush(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_fib_node *fib_node) { - struct mlxsw_sp_fib6_entry *fib6_entry, *tmp; - - list_for_each_entry_safe(fib6_entry, tmp, &fib_node->entry_list, - common.list) { - bool do_break = &tmp->common.list == &fib_node->entry_list; + struct mlxsw_sp_fib6_entry *fib6_entry; - mlxsw_sp_fib_node_entry_unlink(mlxsw_sp, &fib6_entry->common); - mlxsw_sp_fib6_entry_destroy(mlxsw_sp, fib6_entry); - mlxsw_sp_fib_node_put(mlxsw_sp, fib_node); - if (do_break) - break; - } + fib6_entry = container_of(fib_node->fib_entry, + struct mlxsw_sp_fib6_entry, common); + mlxsw_sp_fib_node_entry_unlink(mlxsw_sp, fib_node->fib_entry); + mlxsw_sp_fib6_entry_destroy(mlxsw_sp, fib6_entry); + mlxsw_sp_fib_node_put(mlxsw_sp, fib_node); } static void mlxsw_sp_fib_node_flush(struct mlxsw_sp *mlxsw_sp, -- GitLab From 3aec743d69822d22d4a5b60deb9518ed8be6fa67 Mon Sep 17 00:00:00 2001 From: Martin Blumenstingl Date: Thu, 26 Dec 2019 19:51:47 +0100 Subject: [PATCH 1360/1437] net: phy: realtek: add logging for the RGMII TX delay configuration RGMII requires a delay of 2ns between the data and the clock signal. There are at least three ways this can happen. One possibility is by having the PHY generate this delay. This is a common source for problems (for example with slow TX speeds or packet loss when sending data). The TX delay configuration of the RTL8211F PHY can be set either by pin-strappping the RXD1 pin (HIGH means enabled, LOW means disabled) or through configuring a paged register. The setting from the RXD1 pin is also reflected in the register. Add debug logging to the TX delay configuration on RTL8211F so it's easier to spot these issues (for example if the TX delay is enabled for both, the RTL8211F PHY and the MAC). This is especially helpful because there is no public datasheet for the RTL8211F PHY available with all the RX/TX delay specifics. Signed-off-by: Martin Blumenstingl Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/phy/realtek.c | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/drivers/net/phy/realtek.c b/drivers/net/phy/realtek.c index 476db5345e1af..879ca37c85081 100644 --- a/drivers/net/phy/realtek.c +++ b/drivers/net/phy/realtek.c @@ -171,7 +171,9 @@ static int rtl8211c_config_init(struct phy_device *phydev) static int rtl8211f_config_init(struct phy_device *phydev) { + struct device *dev = &phydev->mdio.dev; u16 val; + int ret; /* enable TX-delay for rgmii-{id,txid}, and disable it for rgmii and * rgmii-rxid. The RX-delay can be enabled by the external RXDLY pin. @@ -189,7 +191,22 @@ static int rtl8211f_config_init(struct phy_device *phydev) return 0; } - return phy_modify_paged(phydev, 0xd08, 0x11, RTL8211F_TX_DELAY, val); + ret = phy_modify_paged_changed(phydev, 0xd08, 0x11, RTL8211F_TX_DELAY, + val); + if (ret < 0) { + dev_err(dev, "Failed to update the TX delay register\n"); + return ret; + } else if (ret) { + dev_dbg(dev, + "%s 2ns TX delay (and changing the value from pin-strapping RXD1 or the bootloader)\n", + val ? "Enabling" : "Disabling"); + } else { + dev_dbg(dev, + "2ns TX delay was already %s (by pin-strapping RXD1 or bootloader configuration)\n", + val ? "enabled" : "disabled"); + } + + return 0; } static int rtl8211e_config_init(struct phy_device *phydev) -- GitLab From 1b3047b5208a80e7213bc4621f6fa4e5feb8dee4 Mon Sep 17 00:00:00 2001 From: Martin Blumenstingl Date: Thu, 26 Dec 2019 19:51:48 +0100 Subject: [PATCH 1361/1437] net: phy: realtek: add support for configuring the RX delay on RTL8211F On RTL8211F the RX and TX delays (2ns) can be configured in two ways: - pin strapping (RXD1 for the TX delay and RXD0 for the RX delay, LOW means "off" and HIGH means "on") which is read during PHY reset - using software to configure the TX and RX delay registers So far only the configuration using pin strapping has been supported. Add support for enabling or disabling the RGMII RX delay based on the phy-mode to be able to get the RX delay into a known state. This is important because the RX delay has to be coordinated between the PHY, MAC and the PCB design (trace length). With an invalid RX delay applied (for example if both PHY and MAC add a 2ns RX delay) Ethernet may not work at all. Also add debug logging when configuring the RX delay (just like the TX delay) because this is a common source of problems. Signed-off-by: Martin Blumenstingl Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/phy/realtek.c | 46 ++++++++++++++++++++++++++++++--------- 1 file changed, 36 insertions(+), 10 deletions(-) diff --git a/drivers/net/phy/realtek.c b/drivers/net/phy/realtek.c index 879ca37c85081..f5fa2fff3ddc8 100644 --- a/drivers/net/phy/realtek.c +++ b/drivers/net/phy/realtek.c @@ -29,6 +29,8 @@ #define RTL8211F_INSR 0x1d #define RTL8211F_TX_DELAY BIT(8) +#define RTL8211F_RX_DELAY BIT(3) + #define RTL8211E_TX_DELAY BIT(1) #define RTL8211E_RX_DELAY BIT(2) #define RTL8211E_MODE_MII_GMII BIT(3) @@ -172,38 +174,62 @@ static int rtl8211c_config_init(struct phy_device *phydev) static int rtl8211f_config_init(struct phy_device *phydev) { struct device *dev = &phydev->mdio.dev; - u16 val; + u16 val_txdly, val_rxdly; int ret; - /* enable TX-delay for rgmii-{id,txid}, and disable it for rgmii and - * rgmii-rxid. The RX-delay can be enabled by the external RXDLY pin. - */ switch (phydev->interface) { case PHY_INTERFACE_MODE_RGMII: + val_txdly = 0; + val_rxdly = 0; + break; + case PHY_INTERFACE_MODE_RGMII_RXID: - val = 0; + val_txdly = 0; + val_rxdly = RTL8211F_RX_DELAY; break; - case PHY_INTERFACE_MODE_RGMII_ID: + case PHY_INTERFACE_MODE_RGMII_TXID: - val = RTL8211F_TX_DELAY; + val_txdly = RTL8211F_TX_DELAY; + val_rxdly = 0; + break; + + case PHY_INTERFACE_MODE_RGMII_ID: + val_txdly = RTL8211F_TX_DELAY; + val_rxdly = RTL8211F_RX_DELAY; break; + default: /* the rest of the modes imply leaving delay as is. */ return 0; } ret = phy_modify_paged_changed(phydev, 0xd08, 0x11, RTL8211F_TX_DELAY, - val); + val_txdly); if (ret < 0) { dev_err(dev, "Failed to update the TX delay register\n"); return ret; } else if (ret) { dev_dbg(dev, "%s 2ns TX delay (and changing the value from pin-strapping RXD1 or the bootloader)\n", - val ? "Enabling" : "Disabling"); + val_txdly ? "Enabling" : "Disabling"); } else { dev_dbg(dev, "2ns TX delay was already %s (by pin-strapping RXD1 or bootloader configuration)\n", - val ? "enabled" : "disabled"); + val_txdly ? "enabled" : "disabled"); + } + + ret = phy_modify_paged_changed(phydev, 0xd08, 0x15, RTL8211F_RX_DELAY, + val_rxdly); + if (ret < 0) { + dev_err(dev, "Failed to update the RX delay register\n"); + return ret; + } else if (ret) { + dev_dbg(dev, + "%s 2ns RX delay (and changing the value from pin-strapping RXD0 or the bootloader)\n", + val_rxdly ? "Enabling" : "Disabling"); + } else { + dev_dbg(dev, + "2ns RX delay was already %s (by pin-strapping RXD0 or bootloader configuration)\n", + val_rxdly ? "enabled" : "disabled"); } return 0; -- GitLab From c27569fcd6e1b11bd24361346504f2995a256e4e Mon Sep 17 00:00:00 2001 From: Madalin Bucur Date: Mon, 23 Dec 2019 09:39:22 +0200 Subject: [PATCH 1362/1437] dpaa_eth: fix DMA mapping leak On the error path some fragments remain DMA mapped. Adding a fix that unmaps all the fragments. Rework cleanup path to be simpler. Fixes: 8151ee88bad5 ("dpaa_eth: use page backed rx buffers") Signed-off-by: Madalin Bucur Signed-off-by: David S. Miller --- .../net/ethernet/freescale/dpaa/dpaa_eth.c | 39 ++++++++++--------- 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c b/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c index 6a9d12dad5d9d..a301f00952231 100644 --- a/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c +++ b/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c @@ -1719,7 +1719,7 @@ static struct sk_buff *sg_fd_to_skb(const struct dpaa_priv *priv, int page_offset; unsigned int sz; int *count_ptr; - int i; + int i, j; vaddr = phys_to_virt(addr); WARN_ON(!IS_ALIGNED((unsigned long)vaddr, SMP_CACHE_BYTES)); @@ -1736,14 +1736,14 @@ static struct sk_buff *sg_fd_to_skb(const struct dpaa_priv *priv, WARN_ON(!IS_ALIGNED((unsigned long)sg_vaddr, SMP_CACHE_BYTES)); + dma_unmap_page(priv->rx_dma_dev, sg_addr, + DPAA_BP_RAW_SIZE, DMA_FROM_DEVICE); + /* We may use multiple Rx pools */ dpaa_bp = dpaa_bpid2pool(sgt[i].bpid); if (!dpaa_bp) goto free_buffers; - count_ptr = this_cpu_ptr(dpaa_bp->percpu_count); - dma_unmap_page(priv->rx_dma_dev, sg_addr, - DPAA_BP_RAW_SIZE, DMA_FROM_DEVICE); if (!skb) { sz = dpaa_bp->size + SKB_DATA_ALIGN(sizeof(struct skb_shared_info)); @@ -1786,7 +1786,9 @@ static struct sk_buff *sg_fd_to_skb(const struct dpaa_priv *priv, skb_add_rx_frag(skb, i - 1, head_page, frag_off, frag_len, dpaa_bp->size); } + /* Update the pool count for the current {cpu x bpool} */ + count_ptr = this_cpu_ptr(dpaa_bp->percpu_count); (*count_ptr)--; if (qm_sg_entry_is_final(&sgt[i])) @@ -1800,26 +1802,25 @@ static struct sk_buff *sg_fd_to_skb(const struct dpaa_priv *priv, return skb; free_buffers: - /* compensate sw bpool counter changes */ - for (i--; i >= 0; i--) { - dpaa_bp = dpaa_bpid2pool(sgt[i].bpid); - if (dpaa_bp) { - count_ptr = this_cpu_ptr(dpaa_bp->percpu_count); - (*count_ptr)++; - } - } /* free all the SG entries */ - for (i = 0; i < DPAA_SGT_MAX_ENTRIES ; i++) { - sg_addr = qm_sg_addr(&sgt[i]); + for (j = 0; j < DPAA_SGT_MAX_ENTRIES ; j++) { + sg_addr = qm_sg_addr(&sgt[j]); sg_vaddr = phys_to_virt(sg_addr); + /* all pages 0..i were unmaped */ + if (j > i) + dma_unmap_page(priv->rx_dma_dev, qm_sg_addr(&sgt[j]), + DPAA_BP_RAW_SIZE, DMA_FROM_DEVICE); free_pages((unsigned long)sg_vaddr, 0); - dpaa_bp = dpaa_bpid2pool(sgt[i].bpid); - if (dpaa_bp) { - count_ptr = this_cpu_ptr(dpaa_bp->percpu_count); - (*count_ptr)--; + /* counters 0..i-1 were decremented */ + if (j >= i) { + dpaa_bp = dpaa_bpid2pool(sgt[j].bpid); + if (dpaa_bp) { + count_ptr = this_cpu_ptr(dpaa_bp->percpu_count); + (*count_ptr)--; + } } - if (qm_sg_entry_is_final(&sgt[i])) + if (qm_sg_entry_is_final(&sgt[j])) break; } /* free the SGT fragment */ -- GitLab From 1c93fb45761e79b3c00080e71523886cefaf351c Mon Sep 17 00:00:00 2001 From: Madalin Bucur Date: Mon, 23 Dec 2019 10:06:10 +0200 Subject: [PATCH 1363/1437] net: phy: aquantia: add suspend / resume ops for AQR105 The suspend/resume code for AQR107 works on AQR105 too. This patch fixes issues with the partner not seeing the link down when the interface using AQR105 is brought down. Fixes: bee8259dd31f ("net: phy: add driver for aquantia phy") Signed-off-by: Madalin Bucur Signed-off-by: David S. Miller --- drivers/net/phy/aquantia_main.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/phy/aquantia_main.c b/drivers/net/phy/aquantia_main.c index 3b29d381116f8..975789d9349dc 100644 --- a/drivers/net/phy/aquantia_main.c +++ b/drivers/net/phy/aquantia_main.c @@ -627,6 +627,8 @@ static struct phy_driver aqr_driver[] = { .config_intr = aqr_config_intr, .ack_interrupt = aqr_ack_interrupt, .read_status = aqr_read_status, + .suspend = aqr107_suspend, + .resume = aqr107_resume, }, { PHY_ID_MATCH_MODEL(PHY_ID_AQR106), -- GitLab From 9476654bd5e8ad42abe8ee9f9e90069ff8e60c17 Mon Sep 17 00:00:00 2001 From: Paul Durrant Date: Mon, 23 Dec 2019 09:59:23 +0000 Subject: [PATCH 1364/1437] xen-netback: support dynamic unbind/bind By re-attaching RX, TX, and CTL rings during connect() rather than assuming they are freshly allocated (i.e. assuming the counters are zero), and avoiding forcing state to Closed in netback_remove() it is possible for vif instances to be unbound and re-bound from and to (respectively) a running guest. Dynamic unbind/bind is a highly useful feature for a backend module as it allows it to be unloaded and re-loaded (i.e. updated) without requiring domUs to be halted. This has been tested by running iperf as a server in the test VM and then running a client against it in a continuous loop, whilst also running: while true; do echo vif-$DOMID-$VIF >unbind; echo down; rmmod xen-netback; echo unloaded; modprobe xen-netback; cd $(pwd); brctl addif xenbr0 vif$DOMID.$VIF; ip link set vif$DOMID.$VIF up; echo up; sleep 5; done in dom0 from /sys/bus/xen-backend/drivers/vif to continuously unbind, unload, re-load, re-bind and re-plumb the backend. Clearly a performance drop was seen but no TCP connection resets were observed during this test and moreover a parallel SSH connection into the guest remained perfectly usable throughout. Signed-off-by: Paul Durrant Reviewed-by: Wei Liu Signed-off-by: David S. Miller --- drivers/net/xen-netback/interface.c | 10 +++++++++- drivers/net/xen-netback/netback.c | 20 +++++++++++++++++--- drivers/net/xen-netback/xenbus.c | 5 ++--- 3 files changed, 28 insertions(+), 7 deletions(-) diff --git a/drivers/net/xen-netback/interface.c b/drivers/net/xen-netback/interface.c index f15ba3de6195a..0c8a02a1ead74 100644 --- a/drivers/net/xen-netback/interface.c +++ b/drivers/net/xen-netback/interface.c @@ -585,6 +585,7 @@ int xenvif_connect_ctrl(struct xenvif *vif, grant_ref_t ring_ref, struct net_device *dev = vif->dev; void *addr; struct xen_netif_ctrl_sring *shared; + RING_IDX rsp_prod, req_prod; int err; err = xenbus_map_ring_valloc(xenvif_to_xenbus_device(vif), @@ -593,7 +594,14 @@ int xenvif_connect_ctrl(struct xenvif *vif, grant_ref_t ring_ref, goto err; shared = (struct xen_netif_ctrl_sring *)addr; - BACK_RING_INIT(&vif->ctrl, shared, XEN_PAGE_SIZE); + rsp_prod = READ_ONCE(shared->rsp_prod); + req_prod = READ_ONCE(shared->req_prod); + + BACK_RING_ATTACH(&vif->ctrl, shared, rsp_prod, XEN_PAGE_SIZE); + + err = -EIO; + if (req_prod - rsp_prod > RING_SIZE(&vif->ctrl)) + goto err_unmap; err = bind_interdomain_evtchn_to_irq(vif->domid, evtchn); if (err < 0) diff --git a/drivers/net/xen-netback/netback.c b/drivers/net/xen-netback/netback.c index 0020b2e8c279d..315dfc6ea2972 100644 --- a/drivers/net/xen-netback/netback.c +++ b/drivers/net/xen-netback/netback.c @@ -1453,7 +1453,7 @@ int xenvif_map_frontend_data_rings(struct xenvif_queue *queue, void *addr; struct xen_netif_tx_sring *txs; struct xen_netif_rx_sring *rxs; - + RING_IDX rsp_prod, req_prod; int err = -ENOMEM; err = xenbus_map_ring_valloc(xenvif_to_xenbus_device(queue->vif), @@ -1462,7 +1462,14 @@ int xenvif_map_frontend_data_rings(struct xenvif_queue *queue, goto err; txs = (struct xen_netif_tx_sring *)addr; - BACK_RING_INIT(&queue->tx, txs, XEN_PAGE_SIZE); + rsp_prod = READ_ONCE(txs->rsp_prod); + req_prod = READ_ONCE(txs->req_prod); + + BACK_RING_ATTACH(&queue->tx, txs, rsp_prod, XEN_PAGE_SIZE); + + err = -EIO; + if (req_prod - rsp_prod > RING_SIZE(&queue->tx)) + goto err; err = xenbus_map_ring_valloc(xenvif_to_xenbus_device(queue->vif), &rx_ring_ref, 1, &addr); @@ -1470,7 +1477,14 @@ int xenvif_map_frontend_data_rings(struct xenvif_queue *queue, goto err; rxs = (struct xen_netif_rx_sring *)addr; - BACK_RING_INIT(&queue->rx, rxs, XEN_PAGE_SIZE); + rsp_prod = READ_ONCE(rxs->rsp_prod); + req_prod = READ_ONCE(rxs->req_prod); + + BACK_RING_ATTACH(&queue->rx, rxs, rsp_prod, XEN_PAGE_SIZE); + + err = -EIO; + if (req_prod - rsp_prod > RING_SIZE(&queue->rx)) + goto err; return 0; diff --git a/drivers/net/xen-netback/xenbus.c b/drivers/net/xen-netback/xenbus.c index 17b4950ec051f..286054b60d474 100644 --- a/drivers/net/xen-netback/xenbus.c +++ b/drivers/net/xen-netback/xenbus.c @@ -954,12 +954,10 @@ static int netback_remove(struct xenbus_device *dev) { struct backend_info *be = dev_get_drvdata(&dev->dev); - set_backend_state(be, XenbusStateClosed); - unregister_hotplug_status_watch(be); if (be->vif) { kobject_uevent(&dev->dev.kobj, KOBJ_OFFLINE); - xen_unregister_watchers(be->vif); + backend_disconnect(be); xenvif_free(be->vif); be->vif = NULL; } @@ -1131,6 +1129,7 @@ static struct xenbus_driver netback_driver = { .remove = netback_remove, .uevent = netback_uevent, .otherend_changed = frontend_changed, + .allow_rebind = true, }; int xenvif_xenbus_init(void) -- GitLab From 7df2281a174bd0fdbb2211a26914e5440740fcde Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Mon, 23 Dec 2019 11:03:21 +0100 Subject: [PATCH 1365/1437] of: mdio: Add missing inline to of_mdiobus_child_is_phy() dummy MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If CONFIG_OF_MDIO=n: drivers/net/phy/mdio_bus.c:23: include/linux/of_mdio.h:58:13: warning: ‘of_mdiobus_child_is_phy’ defined but not used [-Wunused-function] static bool of_mdiobus_child_is_phy(struct device_node *child) ^~~~~~~~~~~~~~~~~~~~~~~ Fix this by adding the missing "inline" keyword. Fixes: 0aa4d016c043d16a ("of: mdio: export of_mdiobus_child_is_phy") Signed-off-by: Geert Uytterhoeven Reviewed-by: Andrew Lunn Acked-by: Borislav Petkov Signed-off-by: David S. Miller --- include/linux/of_mdio.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/linux/of_mdio.h b/include/linux/of_mdio.h index 79bc82e30c023..491a2b7e77c1e 100644 --- a/include/linux/of_mdio.h +++ b/include/linux/of_mdio.h @@ -55,7 +55,7 @@ static inline int of_mdio_parse_addr(struct device *dev, } #else /* CONFIG_OF_MDIO */ -static bool of_mdiobus_child_is_phy(struct device_node *child) +static inline bool of_mdiobus_child_is_phy(struct device_node *child) { return false; } -- GitLab From 0914d2bb11cc182039084ac3b961dc359b647468 Mon Sep 17 00:00:00 2001 From: Mao Wenan Date: Mon, 23 Dec 2019 18:42:57 +0800 Subject: [PATCH 1366/1437] af_packet: refactoring code for prb_calc_retire_blk_tmo If __ethtool_get_link_ksettings() is failed and with non-zero value, prb_calc_retire_blk_tmo() should return DEFAULT_PRB_RETIRE_TOV firstly. This patch is to refactory code and make it more readable. Signed-off-by: Mao Wenan Signed-off-by: David S. Miller --- net/packet/af_packet.c | 30 ++++++++++++------------------ 1 file changed, 12 insertions(+), 18 deletions(-) diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c index 118cd66b75163..3bec515ccde3b 100644 --- a/net/packet/af_packet.c +++ b/net/packet/af_packet.c @@ -520,7 +520,7 @@ static int prb_calc_retire_blk_tmo(struct packet_sock *po, int blk_size_in_bytes) { struct net_device *dev; - unsigned int mbits = 0, msec = 0, div = 0, tmo = 0; + unsigned int mbits, div; struct ethtool_link_ksettings ecmd; int err; @@ -532,31 +532,25 @@ static int prb_calc_retire_blk_tmo(struct packet_sock *po, } err = __ethtool_get_link_ksettings(dev, &ecmd); rtnl_unlock(); - if (!err) { - /* - * If the link speed is so slow you don't really - * need to worry about perf anyways - */ - if (ecmd.base.speed < SPEED_1000 || - ecmd.base.speed == SPEED_UNKNOWN) { - return DEFAULT_PRB_RETIRE_TOV; - } else { - msec = 1; - div = ecmd.base.speed / 1000; - } - } else + if (err) return DEFAULT_PRB_RETIRE_TOV; + /* If the link speed is so slow you don't really + * need to worry about perf anyways + */ + if (ecmd.base.speed < SPEED_1000 || + ecmd.base.speed == SPEED_UNKNOWN) + return DEFAULT_PRB_RETIRE_TOV; + + div = ecmd.base.speed / 1000; mbits = (blk_size_in_bytes * 8) / (1024 * 1024); if (div) mbits /= div; - tmo = mbits * msec; - if (div) - return tmo+1; - return tmo; + return mbits + 1; + return mbits; } static void prb_init_ft_ops(struct tpacket_kbdq_core *p1, -- GitLab From c04b116a2866b3e3a6b909a576769ccde3d622ac Mon Sep 17 00:00:00 2001 From: Julian Wiedmann Date: Mon, 23 Dec 2019 15:22:25 +0100 Subject: [PATCH 1367/1437] s390/qeth: consolidate RX code To reduce the path length and levels of indirection, move the RX processing from the sub-drivers into the core. Signed-off-by: Julian Wiedmann Signed-off-by: David S. Miller --- drivers/s390/net/qeth_core.h | 16 --- drivers/s390/net/qeth_core_main.c | 180 +++++++++++++++++++++++++----- drivers/s390/net/qeth_l2_main.c | 40 ------- drivers/s390/net/qeth_l3_main.c | 91 --------------- 4 files changed, 153 insertions(+), 174 deletions(-) diff --git a/drivers/s390/net/qeth_core.h b/drivers/s390/net/qeth_core.h index 6e16b19732f6a..4ab3be814ea75 100644 --- a/drivers/s390/net/qeth_core.h +++ b/drivers/s390/net/qeth_core.h @@ -728,7 +728,6 @@ struct qeth_osn_info { struct qeth_discipline { const struct device_type *devtype; - int (*process_rx_buffer)(struct qeth_card *card, int budget, int *done); int (*recover)(void *ptr); int (*setup) (struct ccwgroup_device *); void (*remove) (struct ccwgroup_device *); @@ -923,18 +922,6 @@ static inline struct dst_entry *qeth_dst_check_rcu(struct sk_buff *skb, int ipv) return dst; } -static inline void qeth_rx_csum(struct qeth_card *card, struct sk_buff *skb, - u8 flags) -{ - if ((card->dev->features & NETIF_F_RXCSUM) && - (flags & QETH_HDR_EXT_CSUM_TRANSP_REQ)) { - skb->ip_summed = CHECKSUM_UNNECESSARY; - QETH_CARD_STAT_INC(card, rx_skb_csum); - } else { - skb->ip_summed = CHECKSUM_NONE; - } -} - static inline void qeth_tx_csum(struct sk_buff *skb, u8 *flags, int ipv) { *flags |= QETH_HDR_EXT_CSUM_TRANSP_REQ; @@ -1031,9 +1018,6 @@ struct qeth_cmd_buffer *qeth_get_diag_cmd(struct qeth_card *card, void qeth_notify_cmd(struct qeth_cmd_buffer *iob, int reason); void qeth_put_cmd(struct qeth_cmd_buffer *iob); -struct sk_buff *qeth_core_get_next_skb(struct qeth_card *, - struct qeth_qdio_buffer *, struct qdio_buffer_element **, int *, - struct qeth_hdr **); void qeth_schedule_recovery(struct qeth_card *); int qeth_poll(struct napi_struct *napi, int budget); void qeth_clear_ipacmd_list(struct qeth_card *); diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c index 41f37ce89a4f5..8e2c0588525f3 100644 --- a/drivers/s390/net/qeth_core_main.c +++ b/drivers/s390/net/qeth_core_main.c @@ -5046,6 +5046,114 @@ out: } EXPORT_SYMBOL_GPL(qeth_core_hardsetup_card); +#if IS_ENABLED(CONFIG_QETH_L3) +static void qeth_l3_rebuild_skb(struct qeth_card *card, struct sk_buff *skb, + struct qeth_hdr *hdr) +{ + struct af_iucv_trans_hdr *iucv = (struct af_iucv_trans_hdr *) skb->data; + struct qeth_hdr_layer3 *l3_hdr = &hdr->hdr.l3; + struct net_device *dev = skb->dev; + + if (IS_IQD(card) && iucv->magic == ETH_P_AF_IUCV) { + dev_hard_header(skb, dev, ETH_P_AF_IUCV, dev->dev_addr, + "FAKELL", skb->len); + return; + } + + if (!(l3_hdr->flags & QETH_HDR_PASSTHRU)) { + u16 prot = (l3_hdr->flags & QETH_HDR_IPV6) ? ETH_P_IPV6 : + ETH_P_IP; + unsigned char tg_addr[ETH_ALEN]; + + skb_reset_network_header(skb); + switch (l3_hdr->flags & QETH_HDR_CAST_MASK) { + case QETH_CAST_MULTICAST: + if (prot == ETH_P_IP) + ip_eth_mc_map(ip_hdr(skb)->daddr, tg_addr); + else + ipv6_eth_mc_map(&ipv6_hdr(skb)->daddr, tg_addr); + QETH_CARD_STAT_INC(card, rx_multicast); + break; + case QETH_CAST_BROADCAST: + ether_addr_copy(tg_addr, dev->broadcast); + QETH_CARD_STAT_INC(card, rx_multicast); + break; + default: + if (card->options.sniffer) + skb->pkt_type = PACKET_OTHERHOST; + ether_addr_copy(tg_addr, dev->dev_addr); + } + + if (l3_hdr->ext_flags & QETH_HDR_EXT_SRC_MAC_ADDR) + dev_hard_header(skb, dev, prot, tg_addr, + &l3_hdr->next_hop.rx.src_mac, skb->len); + else + dev_hard_header(skb, dev, prot, tg_addr, "FAKELL", + skb->len); + } + + /* copy VLAN tag from hdr into skb */ + if (!card->options.sniffer && + (l3_hdr->ext_flags & (QETH_HDR_EXT_VLAN_FRAME | + QETH_HDR_EXT_INCLUDE_VLAN_TAG))) { + u16 tag = (l3_hdr->ext_flags & QETH_HDR_EXT_VLAN_FRAME) ? + l3_hdr->vlan_id : + l3_hdr->next_hop.rx.vlan_id; + + __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), tag); + } +} +#endif + +static void qeth_receive_skb(struct qeth_card *card, struct sk_buff *skb, + struct qeth_hdr *hdr) +{ + bool is_cso; + + switch (hdr->hdr.l2.id) { + case QETH_HEADER_TYPE_OSN: + skb_push(skb, sizeof(*hdr)); + skb_copy_to_linear_data(skb, hdr, sizeof(*hdr)); + QETH_CARD_STAT_ADD(card, rx_bytes, skb->len); + QETH_CARD_STAT_INC(card, rx_packets); + + card->osn_info.data_cb(skb); + return; +#if IS_ENABLED(CONFIG_QETH_L3) + case QETH_HEADER_TYPE_LAYER3: + qeth_l3_rebuild_skb(card, skb, hdr); + is_cso = hdr->hdr.l3.ext_flags & QETH_HDR_EXT_CSUM_TRANSP_REQ; + break; +#endif + case QETH_HEADER_TYPE_LAYER2: + is_cso = hdr->hdr.l2.flags[1] & QETH_HDR_EXT_CSUM_TRANSP_REQ; + break; + default: + /* never happens */ + dev_kfree_skb_any(skb); + return; + } + + if (is_cso && (card->dev->features & NETIF_F_RXCSUM)) { + skb->ip_summed = CHECKSUM_UNNECESSARY; + QETH_CARD_STAT_INC(card, rx_skb_csum); + } else { + skb->ip_summed = CHECKSUM_NONE; + } + + skb->protocol = eth_type_trans(skb, skb->dev); + + QETH_CARD_STAT_ADD(card, rx_bytes, skb->len); + QETH_CARD_STAT_INC(card, rx_packets); + if (skb_is_nonlinear(skb)) { + QETH_CARD_STAT_INC(card, rx_sg_skbs); + QETH_CARD_STAT_ADD(card, rx_sg_frags, + skb_shinfo(skb)->nr_frags); + } + + napi_gro_receive(&card->napi, skb); +} + static void qeth_create_skb_frag(struct sk_buff *skb, char *data, int data_len) { struct page *page = virt_to_page(data); @@ -5062,10 +5170,10 @@ static inline int qeth_is_last_sbale(struct qdio_buffer_element *sbale) return (sbale->eflags & SBAL_EFLAGS_LAST_ENTRY); } -struct sk_buff *qeth_core_get_next_skb(struct qeth_card *card, - struct qeth_qdio_buffer *qethbuffer, - struct qdio_buffer_element **__element, int *__offset, - struct qeth_hdr **hdr) +static int qeth_extract_skb(struct qeth_card *card, + struct qeth_qdio_buffer *qethbuffer, + struct qdio_buffer_element **__element, + int *__offset) { struct qdio_buffer_element *element = *__element; struct qdio_buffer *buffer = qethbuffer->buffer; @@ -5073,6 +5181,7 @@ struct sk_buff *qeth_core_get_next_skb(struct qeth_card *card, int offset = *__offset; bool use_rx_sg = false; unsigned int headroom; + struct qeth_hdr *hdr; struct sk_buff *skb; int skb_len = 0; @@ -5080,42 +5189,42 @@ next_packet: /* qeth_hdr must not cross element boundaries */ while (element->length < offset + sizeof(struct qeth_hdr)) { if (qeth_is_last_sbale(element)) - return NULL; + return -ENODATA; element++; offset = 0; } - *hdr = element->addr + offset; - offset += sizeof(struct qeth_hdr); + hdr = element->addr + offset; + offset += sizeof(*hdr); skb = NULL; - switch ((*hdr)->hdr.l2.id) { + switch (hdr->hdr.l2.id) { case QETH_HEADER_TYPE_LAYER2: - skb_len = (*hdr)->hdr.l2.pkt_length; + skb_len = hdr->hdr.l2.pkt_length; linear_len = ETH_HLEN; headroom = 0; break; case QETH_HEADER_TYPE_LAYER3: - skb_len = (*hdr)->hdr.l3.length; + skb_len = hdr->hdr.l3.length; if (!IS_LAYER3(card)) { QETH_CARD_STAT_INC(card, rx_dropped_notsupp); goto walk_packet; } - if ((*hdr)->hdr.l3.flags & QETH_HDR_PASSTHRU) { + if (hdr->hdr.l3.flags & QETH_HDR_PASSTHRU) { linear_len = ETH_HLEN; headroom = 0; break; } - if ((*hdr)->hdr.l3.flags & QETH_HDR_IPV6) + if (hdr->hdr.l3.flags & QETH_HDR_IPV6) linear_len = sizeof(struct ipv6hdr); else linear_len = sizeof(struct iphdr); headroom = ETH_HLEN; break; case QETH_HEADER_TYPE_OSN: - skb_len = (*hdr)->hdr.osn.pdu_length; + skb_len = hdr->hdr.osn.pdu_length; if (!IS_OSN(card)) { QETH_CARD_STAT_INC(card, rx_dropped_notsupp); goto walk_packet; @@ -5125,13 +5234,13 @@ next_packet: headroom = sizeof(struct qeth_hdr); break; default: - if ((*hdr)->hdr.l2.id & QETH_HEADER_MASK_INVAL) + if (hdr->hdr.l2.id & QETH_HEADER_MASK_INVAL) QETH_CARD_STAT_INC(card, rx_frame_errors); else QETH_CARD_STAT_INC(card, rx_dropped_notsupp); /* Can't determine packet length, drop the whole buffer. */ - return NULL; + return -EPROTONOSUPPORT; } if (skb_len < linear_len) { @@ -5195,7 +5304,7 @@ walk_packet: QETH_CARD_STAT_INC(card, rx_length_errors); } - return NULL; + return -EMSGSIZE; } element++; offset = 0; @@ -5208,22 +5317,40 @@ walk_packet: *__element = element; *__offset = offset; - if (use_rx_sg) { - QETH_CARD_STAT_INC(card, rx_sg_skbs); - QETH_CARD_STAT_ADD(card, rx_sg_frags, - skb_shinfo(skb)->nr_frags); + + qeth_receive_skb(card, skb, hdr); + return 0; +} + +static int qeth_extract_skbs(struct qeth_card *card, int budget, + struct qeth_qdio_buffer *buf, bool *done) +{ + int work_done = 0; + + WARN_ON_ONCE(!budget); + *done = false; + + while (budget) { + if (qeth_extract_skb(card, buf, &card->rx.b_element, + &card->rx.e_offset)) { + *done = true; + break; + } + + work_done++; + budget--; } - return skb; + + return work_done; } -EXPORT_SYMBOL_GPL(qeth_core_get_next_skb); int qeth_poll(struct napi_struct *napi, int budget) { struct qeth_card *card = container_of(napi, struct qeth_card, napi); int work_done = 0; struct qeth_qdio_buffer *buffer; - int done; int new_budget = budget; + bool done; while (1) { if (!card->rx.b_count) { @@ -5246,11 +5373,10 @@ int qeth_poll(struct napi_struct *napi, int budget) if (!(card->rx.qdio_err && qeth_check_qdio_errors(card, buffer->buffer, card->rx.qdio_err, "qinerr"))) - work_done += - card->discipline->process_rx_buffer( - card, new_budget, &done); + work_done += qeth_extract_skbs(card, new_budget, + buffer, &done); else - done = 1; + done = true; if (done) { QETH_CARD_STAT_INC(card, rx_bufs); diff --git a/drivers/s390/net/qeth_l2_main.c b/drivers/s390/net/qeth_l2_main.c index 8c95e6019bacd..69d147c4d2ca3 100644 --- a/drivers/s390/net/qeth_l2_main.c +++ b/drivers/s390/net/qeth_l2_main.c @@ -298,45 +298,6 @@ static void qeth_l2_stop_card(struct qeth_card *card) card->info.promisc_mode = 0; } -static int qeth_l2_process_inbound_buffer(struct qeth_card *card, - int budget, int *done) -{ - int work_done = 0; - struct sk_buff *skb; - struct qeth_hdr *hdr; - unsigned int len; - - *done = 0; - WARN_ON_ONCE(!budget); - while (budget) { - skb = qeth_core_get_next_skb(card, - &card->qdio.in_q->bufs[card->rx.b_index], - &card->rx.b_element, &card->rx.e_offset, &hdr); - if (!skb) { - *done = 1; - break; - } - - if (hdr->hdr.l2.id == QETH_HEADER_TYPE_LAYER2) { - skb->protocol = eth_type_trans(skb, skb->dev); - qeth_rx_csum(card, skb, hdr->hdr.l2.flags[1]); - len = skb->len; - napi_gro_receive(&card->napi, skb); - } else { - skb_push(skb, sizeof(*hdr)); - skb_copy_to_linear_data(skb, hdr, sizeof(*hdr)); - len = skb->len; - card->osn_info.data_cb(skb); - } - - work_done++; - budget--; - QETH_CARD_STAT_INC(card, rx_packets); - QETH_CARD_STAT_ADD(card, rx_bytes, len); - } - return work_done; -} - static int qeth_l2_request_initial_mac(struct qeth_card *card) { int rc = 0; @@ -961,7 +922,6 @@ static int qeth_l2_control_event(struct qeth_card *card, struct qeth_discipline qeth_l2_discipline = { .devtype = &qeth_l2_devtype, - .process_rx_buffer = qeth_l2_process_inbound_buffer, .recover = qeth_l2_recover, .setup = qeth_l2_probe_device, .remove = qeth_l2_remove_device, diff --git a/drivers/s390/net/qeth_l3_main.c b/drivers/s390/net/qeth_l3_main.c index 04d7777b7d167..8a1535ee14675 100644 --- a/drivers/s390/net/qeth_l3_main.c +++ b/drivers/s390/net/qeth_l3_main.c @@ -1164,96 +1164,6 @@ static int qeth_l3_vlan_rx_kill_vid(struct net_device *dev, return 0; } -static void qeth_l3_rebuild_skb(struct qeth_card *card, struct sk_buff *skb, - struct qeth_hdr *hdr) -{ - struct af_iucv_trans_hdr *iucv = (struct af_iucv_trans_hdr *) skb->data; - struct net_device *dev = skb->dev; - - if (IS_IQD(card) && iucv->magic == ETH_P_AF_IUCV) { - dev_hard_header(skb, dev, ETH_P_AF_IUCV, dev->dev_addr, - "FAKELL", skb->len); - return; - } - - if (!(hdr->hdr.l3.flags & QETH_HDR_PASSTHRU)) { - u16 prot = (hdr->hdr.l3.flags & QETH_HDR_IPV6) ? ETH_P_IPV6 : - ETH_P_IP; - unsigned char tg_addr[ETH_ALEN]; - - skb_reset_network_header(skb); - switch (hdr->hdr.l3.flags & QETH_HDR_CAST_MASK) { - case QETH_CAST_MULTICAST: - if (prot == ETH_P_IP) - ip_eth_mc_map(ip_hdr(skb)->daddr, tg_addr); - else - ipv6_eth_mc_map(&ipv6_hdr(skb)->daddr, tg_addr); - QETH_CARD_STAT_INC(card, rx_multicast); - break; - case QETH_CAST_BROADCAST: - ether_addr_copy(tg_addr, card->dev->broadcast); - QETH_CARD_STAT_INC(card, rx_multicast); - break; - default: - if (card->options.sniffer) - skb->pkt_type = PACKET_OTHERHOST; - ether_addr_copy(tg_addr, card->dev->dev_addr); - } - - if (hdr->hdr.l3.ext_flags & QETH_HDR_EXT_SRC_MAC_ADDR) - card->dev->header_ops->create(skb, card->dev, prot, - tg_addr, &hdr->hdr.l3.next_hop.rx.src_mac, - skb->len); - else - card->dev->header_ops->create(skb, card->dev, prot, - tg_addr, "FAKELL", skb->len); - } - - /* copy VLAN tag from hdr into skb */ - if (!card->options.sniffer && - (hdr->hdr.l3.ext_flags & (QETH_HDR_EXT_VLAN_FRAME | - QETH_HDR_EXT_INCLUDE_VLAN_TAG))) { - u16 tag = (hdr->hdr.l3.ext_flags & QETH_HDR_EXT_VLAN_FRAME) ? - hdr->hdr.l3.vlan_id : - hdr->hdr.l3.next_hop.rx.vlan_id; - __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), tag); - } - - qeth_rx_csum(card, skb, hdr->hdr.l3.ext_flags); -} - -static int qeth_l3_process_inbound_buffer(struct qeth_card *card, - int budget, int *done) -{ - int work_done = 0; - struct sk_buff *skb; - struct qeth_hdr *hdr; - - *done = 0; - WARN_ON_ONCE(!budget); - while (budget) { - skb = qeth_core_get_next_skb(card, - &card->qdio.in_q->bufs[card->rx.b_index], - &card->rx.b_element, &card->rx.e_offset, &hdr); - if (!skb) { - *done = 1; - break; - } - - if (hdr->hdr.l3.id == QETH_HEADER_TYPE_LAYER3) - qeth_l3_rebuild_skb(card, skb, hdr); - - skb->protocol = eth_type_trans(skb, skb->dev); - QETH_CARD_STAT_INC(card, rx_packets); - QETH_CARD_STAT_ADD(card, rx_bytes, skb->len); - - napi_gro_receive(&card->napi, skb); - work_done++; - budget--; - } - return work_done; -} - static void qeth_l3_stop_card(struct qeth_card *card) { QETH_CARD_TEXT(card, 2, "stopcard"); @@ -2317,7 +2227,6 @@ static int qeth_l3_control_event(struct qeth_card *card, struct qeth_discipline qeth_l3_discipline = { .devtype = &qeth_l3_devtype, - .process_rx_buffer = qeth_l3_process_inbound_buffer, .recover = qeth_l3_recover, .setup = qeth_l3_probe_device, .remove = qeth_l3_remove_device, -- GitLab From dcdcf8670dad7547cff59ef9ddd7312fc706464a Mon Sep 17 00:00:00 2001 From: Julian Wiedmann Date: Mon, 23 Dec 2019 15:22:26 +0100 Subject: [PATCH 1368/1437] s390/qeth: use napi_gro_frags() for SG skbs For non-linear packets, get the skb for attaching the page fragments from napi_get_frags() so that it can be recycled during GRO. Signed-off-by: Julian Wiedmann Signed-off-by: David S. Miller --- drivers/s390/net/qeth_core_main.c | 67 +++++++++++++++++++++++-------- 1 file changed, 50 insertions(+), 17 deletions(-) diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c index 8e2c0588525f3..b32b50384c5ca 100644 --- a/drivers/s390/net/qeth_core_main.c +++ b/drivers/s390/net/qeth_core_main.c @@ -5106,8 +5106,9 @@ static void qeth_l3_rebuild_skb(struct qeth_card *card, struct sk_buff *skb, #endif static void qeth_receive_skb(struct qeth_card *card, struct sk_buff *skb, - struct qeth_hdr *hdr) + struct qeth_hdr *hdr, bool uses_frags) { + struct napi_struct *napi = &card->napi; bool is_cso; switch (hdr->hdr.l2.id) { @@ -5130,7 +5131,10 @@ static void qeth_receive_skb(struct qeth_card *card, struct sk_buff *skb, break; default: /* never happens */ - dev_kfree_skb_any(skb); + if (uses_frags) + napi_free_frags(napi); + else + dev_kfree_skb_any(skb); return; } @@ -5141,8 +5145,6 @@ static void qeth_receive_skb(struct qeth_card *card, struct sk_buff *skb, skb->ip_summed = CHECKSUM_NONE; } - skb->protocol = eth_type_trans(skb, skb->dev); - QETH_CARD_STAT_ADD(card, rx_bytes, skb->len); QETH_CARD_STAT_INC(card, rx_packets); if (skb_is_nonlinear(skb)) { @@ -5151,7 +5153,12 @@ static void qeth_receive_skb(struct qeth_card *card, struct sk_buff *skb, skb_shinfo(skb)->nr_frags); } - napi_gro_receive(&card->napi, skb); + if (uses_frags) { + napi_gro_frags(napi); + } else { + skb->protocol = eth_type_trans(skb, skb->dev); + napi_gro_receive(napi, skb); + } } static void qeth_create_skb_frag(struct sk_buff *skb, char *data, int data_len) @@ -5177,7 +5184,9 @@ static int qeth_extract_skb(struct qeth_card *card, { struct qdio_buffer_element *element = *__element; struct qdio_buffer *buffer = qethbuffer->buffer; + struct napi_struct *napi = &card->napi; unsigned int linear_len = 0; + bool uses_frags = false; int offset = *__offset; bool use_rx_sg = false; unsigned int headroom; @@ -5253,21 +5262,42 @@ next_packet: !atomic_read(&card->force_alloc_skb) && !IS_OSN(card)); - if (use_rx_sg && qethbuffer->rx_skb) { + if (use_rx_sg) { /* QETH_CQ_ENABLED only: */ - skb = qethbuffer->rx_skb; - qethbuffer->rx_skb = NULL; - } else { - if (!use_rx_sg) - linear_len = skb_len; - skb = napi_alloc_skb(&card->napi, linear_len + headroom); + if (qethbuffer->rx_skb) { + skb = qethbuffer->rx_skb; + qethbuffer->rx_skb = NULL; + goto use_skb; + } + + skb = napi_get_frags(napi); + if (!skb) { + /* -ENOMEM, no point in falling back further. */ + QETH_CARD_STAT_INC(card, rx_dropped_nomem); + goto walk_packet; + } + + if (skb_tailroom(skb) >= linear_len + headroom) { + uses_frags = true; + goto use_skb; + } + + netdev_info_once(card->dev, + "Insufficient linear space in NAPI frags skb, need %u but have %u\n", + linear_len + headroom, skb_tailroom(skb)); + /* Shouldn't happen. Don't optimize, fall back to linear skb. */ } - if (!skb) + linear_len = skb_len; + skb = napi_alloc_skb(napi, linear_len + headroom); + if (!skb) { QETH_CARD_STAT_INC(card, rx_dropped_nomem); - else if (headroom) - skb_reserve(skb, headroom); + goto walk_packet; + } +use_skb: + if (headroom) + skb_reserve(skb, headroom); walk_packet: while (skb_len) { int data_len = min(skb_len, (int)(element->length - offset)); @@ -5300,7 +5330,10 @@ walk_packet: QETH_CARD_TEXT(card, 4, "unexeob"); QETH_CARD_HEX(card, 2, buffer, sizeof(void *)); if (skb) { - dev_kfree_skb_any(skb); + if (uses_frags) + napi_free_frags(napi); + else + dev_kfree_skb_any(skb); QETH_CARD_STAT_INC(card, rx_length_errors); } @@ -5318,7 +5351,7 @@ walk_packet: *__element = element; *__offset = offset; - qeth_receive_skb(card, skb, hdr); + qeth_receive_skb(card, skb, hdr, uses_frags); return 0; } -- GitLab From 8ca8559ff3cefd78e12b9658f21cc8bcc02407ec Mon Sep 17 00:00:00 2001 From: Julian Wiedmann Date: Mon, 23 Dec 2019 15:22:27 +0100 Subject: [PATCH 1369/1437] s390/qeth: remove QETH_RX_PULL_LEN Since commit f677fcb9aeb6 ("s390/qeth: ensure linear access to packet headers"), the CQ-specific skbs are allocated with a slightly bigger linear part than necessary. Shrink it down to the maximum that's needed by qeth_extract_skb(). Signed-off-by: Julian Wiedmann Signed-off-by: David S. Miller --- drivers/s390/net/qeth_core.h | 1 - drivers/s390/net/qeth_core_main.c | 6 ++++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/s390/net/qeth_core.h b/drivers/s390/net/qeth_core.h index 4ab3be814ea75..7c37e94fb28be 100644 --- a/drivers/s390/net/qeth_core.h +++ b/drivers/s390/net/qeth_core.h @@ -221,7 +221,6 @@ struct qeth_vnicc_info { /* large receive scatter gather copy break */ #define QETH_RX_SG_CB (PAGE_SIZE >> 1) -#define QETH_RX_PULL_LEN 256 struct qeth_hdr_layer3 { __u8 id; diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c index b32b50384c5ca..3be3d13f8d65e 100644 --- a/drivers/s390/net/qeth_core_main.c +++ b/drivers/s390/net/qeth_core_main.c @@ -2627,7 +2627,8 @@ static int qeth_init_input_buffer(struct qeth_card *card, if ((card->options.cq == QETH_CQ_ENABLED) && (!buf->rx_skb)) { buf->rx_skb = netdev_alloc_skb(card->dev, - QETH_RX_PULL_LEN + ETH_HLEN); + ETH_HLEN + + sizeof(struct ipv6hdr)); if (!buf->rx_skb) return 1; } @@ -5264,7 +5265,8 @@ next_packet: if (use_rx_sg) { /* QETH_CQ_ENABLED only: */ - if (qethbuffer->rx_skb) { + if (qethbuffer->rx_skb && + skb_tailroom(qethbuffer->rx_skb) >= linear_len + headroom) { skb = qethbuffer->rx_skb; qethbuffer->rx_skb = NULL; goto use_skb; -- GitLab From 0444716a5dd563526e53ae686115987d5d4c249e Mon Sep 17 00:00:00 2001 From: Manish Chopra Date: Mon, 23 Dec 2019 10:23:08 -0800 Subject: [PATCH 1370/1437] bnx2x: Use appropriate define for vlan credit Although it has same value as MAX_MAC_CREDIT_E2, use MAX_VLAN_CREDIT_E2 appropriately. Signed-off-by: Manish Chopra Signed-off-by: Ariel Elior Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bnx2x/bnx2x_sp.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sp.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sp.h index 7a6e82db42312..ed237854939a0 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sp.h +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sp.h @@ -1537,7 +1537,7 @@ void bnx2x_get_rss_ind_table(struct bnx2x_rss_config_obj *rss_obj, func_num + GET_NUM_VFS_PER_PF(bp) * VF_MAC_CREDIT_CNT) #define PF_VLAN_CREDIT_E2(bp, func_num) \ - ((MAX_MAC_CREDIT_E2 - GET_NUM_VFS_PER_PATH(bp) * VF_VLAN_CREDIT_CNT) / \ + ((MAX_VLAN_CREDIT_E2 - GET_NUM_VFS_PER_PATH(bp) * VF_VLAN_CREDIT_CNT) /\ func_num + GET_NUM_VFS_PER_PF(bp) * VF_VLAN_CREDIT_CNT) #endif /* BNX2X_SP_VERBS */ -- GitLab From 5cdc40c7820ff66c2271e0884bd8ee8f7cfd769b Mon Sep 17 00:00:00 2001 From: Manish Chopra Date: Mon, 23 Dec 2019 10:23:09 -0800 Subject: [PATCH 1371/1437] bnx2x: Fix accounting of vlan resources among the PFs While testing max vlan configuration on the PF, firmware gets assert as driver was configuring number of vlans more than what is supported per port/engine, it was figured out that there is an implicit vlan (hidden default vlan consuming hardware cam entry resource) which is configured default for all the clients (PF/VFs) on client_init ramrod by the adapter implicitly, so when allocating resources among the PFs this implicit vlan should be considered or total vlan entries should be reduced by one to accommodate that default/implicit vlan entry. Signed-off-by: Manish Chopra Signed-off-by: Ariel Elior Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bnx2x/bnx2x_sp.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sp.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sp.h index ed237854939a0..bacc8552bce1c 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sp.h +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sp.h @@ -1536,8 +1536,11 @@ void bnx2x_get_rss_ind_table(struct bnx2x_rss_config_obj *rss_obj, ((MAX_MAC_CREDIT_E2 - GET_NUM_VFS_PER_PATH(bp) * VF_MAC_CREDIT_CNT) / \ func_num + GET_NUM_VFS_PER_PF(bp) * VF_MAC_CREDIT_CNT) +#define BNX2X_VFS_VLAN_CREDIT(bp) \ + (GET_NUM_VFS_PER_PATH(bp) * VF_VLAN_CREDIT_CNT) + #define PF_VLAN_CREDIT_E2(bp, func_num) \ - ((MAX_VLAN_CREDIT_E2 - GET_NUM_VFS_PER_PATH(bp) * VF_VLAN_CREDIT_CNT) /\ + ((MAX_VLAN_CREDIT_E2 - 1 - BNX2X_VFS_VLAN_CREDIT(bp)) / \ func_num + GET_NUM_VFS_PER_PF(bp) * VF_VLAN_CREDIT_CNT) #endif /* BNX2X_SP_VERBS */ -- GitLab From bb3d0b8bf5be61ab1d6f472c43cbf34de17e796b Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Mon, 23 Dec 2019 11:13:24 -0800 Subject: [PATCH 1372/1437] net_sched: sch_fq: properly set sk->sk_pacing_status If fq_classify() recycles a struct fq_flow because a socket structure has been reallocated, we do not set sk->sk_pacing_status immediately, but later if the flow becomes detached. This means that any flow requiring pacing (BBR, or SO_MAX_PACING_RATE) might fallback to TCP internal pacing, which requires a per-socket high resolution timer, and therefore more cpu cycles. Fixes: 218af599fa63 ("tcp: internal implementation for pacing") Signed-off-by: Eric Dumazet Cc: Soheil Hassas Yeganeh Cc: Neal Cardwell Acked-by: Soheil Hassas Yeganeh Signed-off-by: David S. Miller --- net/sched/sch_fq.c | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/net/sched/sch_fq.c b/net/sched/sch_fq.c index b1c7e726ce5d1..ff4c5e9d0d777 100644 --- a/net/sched/sch_fq.c +++ b/net/sched/sch_fq.c @@ -301,6 +301,9 @@ static struct fq_flow *fq_classify(struct sk_buff *skb, struct fq_sched_data *q) f->socket_hash != sk->sk_hash)) { f->credit = q->initial_quantum; f->socket_hash = sk->sk_hash; + if (q->rate_enable) + smp_store_release(&sk->sk_pacing_status, + SK_PACING_FQ); if (fq_flow_is_throttled(f)) fq_flow_unset_throttled(q, f); f->time_next_packet = 0ULL; @@ -322,8 +325,12 @@ static struct fq_flow *fq_classify(struct sk_buff *skb, struct fq_sched_data *q) fq_flow_set_detached(f); f->sk = sk; - if (skb->sk == sk) + if (skb->sk == sk) { f->socket_hash = sk->sk_hash; + if (q->rate_enable) + smp_store_release(&sk->sk_pacing_status, + SK_PACING_FQ); + } f->credit = q->initial_quantum; rb_link_node(&f->fq_node, parent, p); @@ -428,17 +435,9 @@ static int fq_enqueue(struct sk_buff *skb, struct Qdisc *sch, f->qlen++; qdisc_qstats_backlog_inc(sch, skb); if (fq_flow_is_detached(f)) { - struct sock *sk = skb->sk; - fq_flow_add_tail(&q->new_flows, f); if (time_after(jiffies, f->age + q->flow_refill_delay)) f->credit = max_t(u32, f->credit, q->quantum); - if (sk && q->rate_enable) { - if (unlikely(smp_load_acquire(&sk->sk_pacing_status) != - SK_PACING_FQ)) - smp_store_release(&sk->sk_pacing_status, - SK_PACING_FQ); - } q->inactive_flows--; } -- GitLab From 7c8dce4b166113743adad131b5a24c4acc12f92c Mon Sep 17 00:00:00 2001 From: Andrii Nakryiko Date: Thu, 26 Dec 2019 13:02:53 -0800 Subject: [PATCH 1373/1437] bpftool: Make skeleton C code compilable with C++ compiler When auto-generated BPF skeleton C code is included from C++ application, it triggers compilation error due to void * being implicitly casted to whatever target pointer type. This is supported by C, but not C++. To solve this problem, add explicit casts, where necessary. To ensure issues like this are captured going forward, add skeleton usage in test_cpp test. Signed-off-by: Andrii Nakryiko Signed-off-by: Daniel Borkmann Link: https://lore.kernel.org/bpf/20191226210253.3132060-1-andriin@fb.com --- tools/bpf/bpftool/gen.c | 10 +++++----- tools/testing/selftests/bpf/Makefile | 2 +- tools/testing/selftests/bpf/test_cpp.cpp | 10 ++++++++++ 3 files changed, 16 insertions(+), 6 deletions(-) diff --git a/tools/bpf/bpftool/gen.c b/tools/bpf/bpftool/gen.c index a14d8bc5d31d5..7ce09a9a69994 100644 --- a/tools/bpf/bpftool/gen.c +++ b/tools/bpf/bpftool/gen.c @@ -397,7 +397,7 @@ static int do_skeleton(int argc, char **argv) { \n\ struct %1$s *obj; \n\ \n\ - obj = calloc(1, sizeof(*obj)); \n\ + obj = (typeof(obj))calloc(1, sizeof(*obj)); \n\ if (!obj) \n\ return NULL; \n\ if (%1$s__create_skeleton(obj)) \n\ @@ -461,7 +461,7 @@ static int do_skeleton(int argc, char **argv) { \n\ struct bpf_object_skeleton *s; \n\ \n\ - s = calloc(1, sizeof(*s)); \n\ + s = (typeof(s))calloc(1, sizeof(*s)); \n\ if (!s) \n\ return -1; \n\ obj->skeleton = s; \n\ @@ -479,7 +479,7 @@ static int do_skeleton(int argc, char **argv) /* maps */ \n\ s->map_cnt = %zu; \n\ s->map_skel_sz = sizeof(*s->maps); \n\ - s->maps = calloc(s->map_cnt, s->map_skel_sz);\n\ + s->maps = (typeof(s->maps))calloc(s->map_cnt, s->map_skel_sz);\n\ if (!s->maps) \n\ goto err; \n\ ", @@ -515,7 +515,7 @@ static int do_skeleton(int argc, char **argv) /* programs */ \n\ s->prog_cnt = %zu; \n\ s->prog_skel_sz = sizeof(*s->progs); \n\ - s->progs = calloc(s->prog_cnt, s->prog_skel_sz);\n\ + s->progs = (typeof(s->progs))calloc(s->prog_cnt, s->prog_skel_sz);\n\ if (!s->progs) \n\ goto err; \n\ ", @@ -538,7 +538,7 @@ static int do_skeleton(int argc, char **argv) \n\ \n\ s->data_sz = %d; \n\ - s->data = \"\\ \n\ + s->data = (void *)\"\\ \n\ ", file_sz); diff --git a/tools/testing/selftests/bpf/Makefile b/tools/testing/selftests/bpf/Makefile index 866fc1cadd7c2..41691fb067da0 100644 --- a/tools/testing/selftests/bpf/Makefile +++ b/tools/testing/selftests/bpf/Makefile @@ -371,7 +371,7 @@ $(OUTPUT)/test_verifier: test_verifier.c verifier/tests.h $(BPFOBJ) | $(OUTPUT) $(CC) $(CFLAGS) $(filter %.a %.o %.c,$^) $(LDLIBS) -o $@ # Make sure we are able to include and link libbpf against c++. -$(OUTPUT)/test_cpp: test_cpp.cpp $(BPFOBJ) +$(OUTPUT)/test_cpp: test_cpp.cpp $(OUTPUT)/test_core_extern.skel.h $(BPFOBJ) $(call msg, CXX,,$@) $(CXX) $(CFLAGS) $^ $(LDLIBS) -o $@ diff --git a/tools/testing/selftests/bpf/test_cpp.cpp b/tools/testing/selftests/bpf/test_cpp.cpp index f0eb2727b7669..6fe23a10d48aa 100644 --- a/tools/testing/selftests/bpf/test_cpp.cpp +++ b/tools/testing/selftests/bpf/test_cpp.cpp @@ -1,12 +1,16 @@ /* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */ +#include #include "libbpf.h" #include "bpf.h" #include "btf.h" +#include "test_core_extern.skel.h" /* do nothing, just make sure we can link successfully */ int main(int argc, char *argv[]) { + struct test_core_extern *skel; + /* libbpf.h */ libbpf_set_print(NULL); @@ -16,5 +20,11 @@ int main(int argc, char *argv[]) /* btf.h */ btf__new(NULL, 0); + /* BPF skeleton */ + skel = test_core_extern__open_and_load(); + test_core_extern__destroy(skel); + + std::cout << "DONE!" << std::endl; + return 0; } -- GitLab From 473900a504e510cf9175876de8892ad1e3e7efab Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Mon, 23 Dec 2019 12:27:50 -0800 Subject: [PATCH 1374/1437] tcp_cubic: optimize hystart_update() We do not care which bit in ca->found is set. We avoid accessing hystart and hystart_detect unless really needed, possibly avoiding one cache line miss. Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- net/ipv4/tcp_cubic.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/net/ipv4/tcp_cubic.c b/net/ipv4/tcp_cubic.c index 1b3d032a4df2a..297936033c335 100644 --- a/net/ipv4/tcp_cubic.c +++ b/net/ipv4/tcp_cubic.c @@ -381,9 +381,6 @@ static void hystart_update(struct sock *sk, u32 delay) struct tcp_sock *tp = tcp_sk(sk); struct bictcp *ca = inet_csk_ca(sk); - if (ca->found & hystart_detect) - return; - if (hystart_detect & HYSTART_ACK_TRAIN) { u32 now = bictcp_clock(); @@ -391,7 +388,7 @@ static void hystart_update(struct sock *sk, u32 delay) if ((s32)(now - ca->last_ack) <= hystart_ack_delta) { ca->last_ack = now; if ((s32)(now - ca->round_start) > ca->delay_min >> 4) { - ca->found |= HYSTART_ACK_TRAIN; + ca->found = 1; NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPHYSTARTTRAINDETECT); NET_ADD_STATS(sock_net(sk), @@ -412,7 +409,7 @@ static void hystart_update(struct sock *sk, u32 delay) } else { if (ca->curr_rtt > ca->delay_min + HYSTART_DELAY_THRESH(ca->delay_min >> 3)) { - ca->found |= HYSTART_DELAY; + ca->found = 1; NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPHYSTARTDELAYDETECT); NET_ADD_STATS(sock_net(sk), @@ -450,7 +447,7 @@ static void bictcp_acked(struct sock *sk, const struct ack_sample *sample) ca->delay_min = delay; /* hystart triggers when cwnd is larger than some threshold */ - if (hystart && tcp_in_slow_start(tp) && + if (!ca->found && hystart && tcp_in_slow_start(tp) && tp->snd_cwnd >= hystart_low_window) hystart_update(sk, delay); } -- GitLab From 35821fc2b41c51161e503bade3630603668dd3af Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Mon, 23 Dec 2019 12:27:51 -0800 Subject: [PATCH 1375/1437] tcp_cubic: remove one conditional from hystart_update() If we initialize ca->curr_rtt to ~0U, we do not need to test for zero value in hystart_update() We only read ca->curr_rtt if at least HYSTART_MIN_SAMPLES have been processed, and thus ca->curr_rtt will have a sane value. Signed-off-by: Eric Dumazet Acked-by: Neal Cardwell Signed-off-by: David S. Miller --- net/ipv4/tcp_cubic.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/ipv4/tcp_cubic.c b/net/ipv4/tcp_cubic.c index 297936033c335..5eff762acd7fe 100644 --- a/net/ipv4/tcp_cubic.c +++ b/net/ipv4/tcp_cubic.c @@ -133,7 +133,7 @@ static inline void bictcp_hystart_reset(struct sock *sk) ca->round_start = ca->last_ack = bictcp_clock(); ca->end_seq = tp->snd_nxt; - ca->curr_rtt = 0; + ca->curr_rtt = ~0U; ca->sample_cnt = 0; } @@ -402,7 +402,7 @@ static void hystart_update(struct sock *sk, u32 delay) if (hystart_detect & HYSTART_DELAY) { /* obtain the minimum delay of more than sampling packets */ if (ca->sample_cnt < HYSTART_MIN_SAMPLES) { - if (ca->curr_rtt == 0 || ca->curr_rtt > delay) + if (ca->curr_rtt > delay) ca->curr_rtt = delay; ca->sample_cnt++; -- GitLab From cff04e2da308c522f654237b45dd64248fe8d1fa Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Mon, 23 Dec 2019 12:27:52 -0800 Subject: [PATCH 1376/1437] tcp_cubic: switch bictcp_clock() to usec resolution Current 1ms clock feeds ca->round_start, ca->delay_min, ca->last_ack. This is quite problematic for data-center flows, where delay_min is way below 1 ms. This means Hystart Train detection triggers every time jiffies value is updated, since "((s32)(now - ca->round_start) > ca->delay_min >> 4)" expression becomes true. This kind of random behavior can be solved by reusing the existing usec timestamp that TCP keeps in tp->tcp_mstamp Note that a followup patch will tweak things a bit, because during slow start, GRO aggregation on receivers naturally increases the RTT as TSO packets gradually come to ~64KB size. To recap, right after this patch CUBIC Hystart train detection is more aggressive, since short RTT flows might exit slow start at cwnd = 20, instead of being possibly unbounded. Following patch will address this problem. Signed-off-by: Eric Dumazet Acked-by: Neal Cardwell Signed-off-by: David S. Miller --- net/ipv4/tcp_cubic.c | 35 ++++++++++++++--------------------- 1 file changed, 14 insertions(+), 21 deletions(-) diff --git a/net/ipv4/tcp_cubic.c b/net/ipv4/tcp_cubic.c index 5eff762acd7fe..068775b91fb57 100644 --- a/net/ipv4/tcp_cubic.c +++ b/net/ipv4/tcp_cubic.c @@ -40,8 +40,8 @@ /* Number of delay samples for detecting the increase of delay */ #define HYSTART_MIN_SAMPLES 8 -#define HYSTART_DELAY_MIN (4U<<3) -#define HYSTART_DELAY_MAX (16U<<3) +#define HYSTART_DELAY_MIN (4000U) /* 4 ms */ +#define HYSTART_DELAY_MAX (16000U) /* 16 ms */ #define HYSTART_DELAY_THRESH(x) clamp(x, HYSTART_DELAY_MIN, HYSTART_DELAY_MAX) static int fast_convergence __read_mostly = 1; @@ -53,7 +53,7 @@ static int tcp_friendliness __read_mostly = 1; static int hystart __read_mostly = 1; static int hystart_detect __read_mostly = HYSTART_ACK_TRAIN | HYSTART_DELAY; static int hystart_low_window __read_mostly = 16; -static int hystart_ack_delta __read_mostly = 2; +static int hystart_ack_delta_us __read_mostly = 2000; static u32 cube_rtt_scale __read_mostly; static u32 beta_scale __read_mostly; @@ -77,8 +77,8 @@ MODULE_PARM_DESC(hystart_detect, "hybrid slow start detection mechanisms" " 1: packet-train 2: delay 3: both packet-train and delay"); module_param(hystart_low_window, int, 0644); MODULE_PARM_DESC(hystart_low_window, "lower bound cwnd for hybrid slow start"); -module_param(hystart_ack_delta, int, 0644); -MODULE_PARM_DESC(hystart_ack_delta, "spacing between ack's indicating train (msecs)"); +module_param(hystart_ack_delta_us, int, 0644); +MODULE_PARM_DESC(hystart_ack_delta_us, "spacing between ack's indicating train (usecs)"); /* BIC TCP Parameters */ struct bictcp { @@ -89,7 +89,7 @@ struct bictcp { u32 bic_origin_point;/* origin point of bic function */ u32 bic_K; /* time to origin point from the beginning of the current epoch */ - u32 delay_min; /* min delay (msec << 3) */ + u32 delay_min; /* min delay (usec) */ u32 epoch_start; /* beginning of an epoch */ u32 ack_cnt; /* number of acks */ u32 tcp_cwnd; /* estimated tcp cwnd */ @@ -117,13 +117,9 @@ static inline void bictcp_reset(struct bictcp *ca) ca->found = 0; } -static inline u32 bictcp_clock(void) +static inline u32 bictcp_clock_us(const struct sock *sk) { -#if HZ < 1000 - return ktime_to_ms(ktime_get_real()); -#else - return jiffies_to_msecs(jiffies); -#endif + return tcp_sk(sk)->tcp_mstamp; } static inline void bictcp_hystart_reset(struct sock *sk) @@ -131,7 +127,7 @@ static inline void bictcp_hystart_reset(struct sock *sk) struct tcp_sock *tp = tcp_sk(sk); struct bictcp *ca = inet_csk_ca(sk); - ca->round_start = ca->last_ack = bictcp_clock(); + ca->round_start = ca->last_ack = bictcp_clock_us(sk); ca->end_seq = tp->snd_nxt; ca->curr_rtt = ~0U; ca->sample_cnt = 0; @@ -276,7 +272,7 @@ static inline void bictcp_update(struct bictcp *ca, u32 cwnd, u32 acked) */ t = (s32)(tcp_jiffies32 - ca->epoch_start); - t += msecs_to_jiffies(ca->delay_min >> 3); + t += usecs_to_jiffies(ca->delay_min); /* change the unit from HZ to bictcp_HZ */ t <<= BICTCP_HZ; do_div(t, HZ); @@ -382,12 +378,12 @@ static void hystart_update(struct sock *sk, u32 delay) struct bictcp *ca = inet_csk_ca(sk); if (hystart_detect & HYSTART_ACK_TRAIN) { - u32 now = bictcp_clock(); + u32 now = bictcp_clock_us(sk); /* first detection parameter - ack-train detection */ - if ((s32)(now - ca->last_ack) <= hystart_ack_delta) { + if ((s32)(now - ca->last_ack) <= hystart_ack_delta_us) { ca->last_ack = now; - if ((s32)(now - ca->round_start) > ca->delay_min >> 4) { + if ((s32)(now - ca->round_start) > ca->delay_min >> 1) { ca->found = 1; NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPHYSTARTTRAINDETECT); @@ -421,9 +417,6 @@ static void hystart_update(struct sock *sk, u32 delay) } } -/* Track delayed acknowledgment ratio using sliding window - * ratio = (15*ratio + sample) / 16 - */ static void bictcp_acked(struct sock *sk, const struct ack_sample *sample) { const struct tcp_sock *tp = tcp_sk(sk); @@ -438,7 +431,7 @@ static void bictcp_acked(struct sock *sk, const struct ack_sample *sample) if (ca->epoch_start && (s32)(tcp_jiffies32 - ca->epoch_start) < HZ) return; - delay = (sample->rtt_us << 3) / USEC_PER_MSEC; + delay = sample->rtt_us; if (delay == 0) delay = 1; -- GitLab From 42f3a8aaae66d31d87850fb4b02979a0fc5dc541 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Mon, 23 Dec 2019 12:27:53 -0800 Subject: [PATCH 1377/1437] tcp_cubic: tweak Hystart detection for short RTT flows After switching ca->delay_min to usec resolution, we exit slow start prematurely for very low RTT flows, setting snd_ssthresh to 20. The reason is that delay_min is fed with RTT of small packet trains. Then as cwnd is increased, TCP sends bigger TSO packets. LRO/GRO aggregation and/or interrupt mitigation strategies on receiver tend to inflate RTT samples. Fix this by adding to delay_min the expected delay of two TSO packets, given current pacing rate. Tested: Sender uses pfifo_fast qdisc Before : $ nstat -n;for f in {1..10}; do ./super_netperf 1 -H lpaa24 -l -4000000; done;nstat|egrep "Hystart" 11348 11707 11562 11428 11773 11534 9878 11693 10597 10968 TcpExtTCPHystartTrainDetect 10 0.0 TcpExtTCPHystartTrainCwnd 200 0.0 After : $ nstat -n;for f in {1..10}; do ./super_netperf 1 -H lpaa24 -l -4000000; done;nstat|egrep "Hystart" 14877 14517 15797 18466 17376 14833 17558 17933 16039 18059 TcpExtTCPHystartTrainDetect 10 0.0 TcpExtTCPHystartTrainCwnd 1670 0.0 Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- net/ipv4/tcp_cubic.c | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/net/ipv4/tcp_cubic.c b/net/ipv4/tcp_cubic.c index 068775b91fb57..0e5428ed04fe4 100644 --- a/net/ipv4/tcp_cubic.c +++ b/net/ipv4/tcp_cubic.c @@ -436,8 +436,27 @@ static void bictcp_acked(struct sock *sk, const struct ack_sample *sample) delay = 1; /* first time call or link delay decreases */ - if (ca->delay_min == 0 || ca->delay_min > delay) - ca->delay_min = delay; + if (ca->delay_min == 0 || ca->delay_min > delay) { + unsigned long rate = READ_ONCE(sk->sk_pacing_rate); + + /* Account for TSO/GRO delays. + * Otherwise short RTT flows could get too small ssthresh, + * since during slow start we begin with small TSO packets + * and could lower ca->delay_min too much. + * Ideally even with a very small RTT we would like to have + * at least one TSO packet being sent and received by GRO, + * and another one in qdisc layer. + * We apply another 100% factor because @rate is doubled at + * this point. + * We cap the cushion to 1ms. + */ + if (rate) + delay += min_t(u64, USEC_PER_MSEC, + div64_ul((u64)GSO_MAX_SIZE * + 4 * USEC_PER_SEC, rate)); + if (ca->delay_min == 0 || ca->delay_min > delay) + ca->delay_min = delay; + } /* hystart triggers when cwnd is larger than some threshold */ if (!ca->found && hystart && tcp_in_slow_start(tp) && -- GitLab From ede656e8465839530c3287c7f54adf75dc2b9563 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Mon, 23 Dec 2019 12:27:54 -0800 Subject: [PATCH 1378/1437] tcp_cubic: make Hystart aware of pacing For years we disabled Hystart ACK train detection at Google because it was fooled by TCP pacing. ACK train detection uses a simple heuristic, detecting if we receive ACK past half the RTT, to exit slow start before hitting the bottleneck and experience massive drops. But pacing by design might delay packets up to RTT/2, so we need to tweak the Hystart logic to be aware of this extra delay. Tested: Added a 100 usec delay at receiver. Before: nstat -n;for f in {1..10}; do ./super_netperf 1 -H lpaa24 -l -4000000; done;nstat|egrep "Hystart" 9117 7057 9553 8300 7030 6849 9533 10126 6876 8473 TcpExtTCPHystartTrainDetect 10 0.0 TcpExtTCPHystartTrainCwnd 1230 0.0 After : nstat -n;for f in {1..10}; do ./super_netperf 1 -H lpaa24 -l -4000000; done;nstat|egrep "Hystart" 9845 10103 10866 11096 11936 11487 11773 12188 11066 11894 TcpExtTCPHystartTrainDetect 10 0.0 TcpExtTCPHystartTrainCwnd 6462 0.0 Disabling Hystart ACK Train detection gives similar numbers echo 2 >/sys/module/tcp_cubic/parameters/hystart_detect nstat -n;for f in {1..10}; do ./super_netperf 1 -H lpaa24 -l -4000000; done;nstat|egrep "Hystart" 11173 10954 12455 10627 11578 11583 11222 10880 10665 11366 Signed-off-by: Eric Dumazet Acked-by: Neal Cardwell Signed-off-by: David S. Miller --- net/ipv4/tcp_cubic.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/net/ipv4/tcp_cubic.c b/net/ipv4/tcp_cubic.c index 0e5428ed04fe4..d02bb283c6890 100644 --- a/net/ipv4/tcp_cubic.c +++ b/net/ipv4/tcp_cubic.c @@ -376,6 +376,7 @@ static void hystart_update(struct sock *sk, u32 delay) { struct tcp_sock *tp = tcp_sk(sk); struct bictcp *ca = inet_csk_ca(sk); + u32 threshold; if (hystart_detect & HYSTART_ACK_TRAIN) { u32 now = bictcp_clock_us(sk); @@ -383,7 +384,17 @@ static void hystart_update(struct sock *sk, u32 delay) /* first detection parameter - ack-train detection */ if ((s32)(now - ca->last_ack) <= hystart_ack_delta_us) { ca->last_ack = now; - if ((s32)(now - ca->round_start) > ca->delay_min >> 1) { + + threshold = ca->delay_min; + /* Hystart ack train triggers if we get ack past + * ca->delay_min/2. + * Pacing might have delayed packets up to RTT/2 + * during slow start. + */ + if (sk->sk_pacing_status == SK_PACING_NONE) + threshold >>= 1; + + if ((s32)(now - ca->round_start) > threshold) { ca->found = 1; NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPHYSTARTTRAINDETECT); -- GitLab From 8a3f44a0bb76c4e69dea3ad398ada41ae4ffc3ea Mon Sep 17 00:00:00 2001 From: Xu Wang Date: Tue, 24 Dec 2019 09:37:04 +0000 Subject: [PATCH 1379/1437] ppp: Remove redundant BUG_ON() check in ppp_pernet Passing NULL to ppp_pernet causes a crash via BUG_ON. Dereferencing net in net_generic() also has the same effect. This patch removes the redundant BUG_ON check on the same parameter. Signed-off-by: Xu Wang Signed-off-by: David S. Miller --- drivers/net/ppp/ppp_generic.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/net/ppp/ppp_generic.c b/drivers/net/ppp/ppp_generic.c index 3bf8a8b429830..22cc2cb9d878f 100644 --- a/drivers/net/ppp/ppp_generic.c +++ b/drivers/net/ppp/ppp_generic.c @@ -296,8 +296,6 @@ static struct class *ppp_class; /* per net-namespace data */ static inline struct ppp_net *ppp_pernet(struct net *net) { - BUG_ON(!net); - return net_generic(net, ppp_net_id); } -- GitLab From c8f957df6e338406128cadb2ee30e38f9a816ed6 Mon Sep 17 00:00:00 2001 From: Mao Wenan Date: Tue, 24 Dec 2019 19:58:12 +0800 Subject: [PATCH 1380/1437] net: dsa: qca: ar9331: drop pointless static qualifier in ar9331_sw_mbus_init There is no need to set variable 'mbus' static since new value always be assigned before use it. Signed-off-by: Mao Wenan Reviewed-by: Oleksij Rempel Reviewed-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/dsa/qca/ar9331.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/dsa/qca/ar9331.c b/drivers/net/dsa/qca/ar9331.c index 0d1a7cd85fe8f..da3bece75e217 100644 --- a/drivers/net/dsa/qca/ar9331.c +++ b/drivers/net/dsa/qca/ar9331.c @@ -266,7 +266,7 @@ error: static int ar9331_sw_mbus_init(struct ar9331_sw_priv *priv) { struct device *dev = priv->dev; - static struct mii_bus *mbus; + struct mii_bus *mbus; struct device_node *np, *mnp; int ret; -- GitLab From cad451dd2427d6ddd38a411e688def499daa798d Mon Sep 17 00:00:00 2001 From: YueHaibing Date: Tue, 24 Dec 2019 20:51:28 +0800 Subject: [PATCH 1381/1437] net: ena: remove set but not used variable 'rx_ring' drivers/net/ethernet/amazon/ena/ena_netdev.c: In function ena_xdp_xmit_buff: drivers/net/ethernet/amazon/ena/ena_netdev.c:316:19: warning: variable rx_ring set but not used [-Wunused-but-set-variable] commit 548c4940b9f1 ("net: ena: Implement XDP_TX action") left behind this unused variable. Reported-by: Hulk Robot Signed-off-by: YueHaibing Signed-off-by: David S. Miller --- drivers/net/ethernet/amazon/ena/ena_netdev.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/net/ethernet/amazon/ena/ena_netdev.c b/drivers/net/ethernet/amazon/ena/ena_netdev.c index 081acf077bb52..894e8c1a8cf15 100644 --- a/drivers/net/ethernet/amazon/ena/ena_netdev.c +++ b/drivers/net/ethernet/amazon/ena/ena_netdev.c @@ -313,7 +313,6 @@ static int ena_xdp_xmit_buff(struct net_device *dev, struct ena_com_tx_ctx ena_tx_ctx = {0}; struct ena_tx_buffer *tx_info; struct ena_ring *xdp_ring; - struct ena_ring *rx_ring; u16 next_to_use, req_id; int rc; void *push_hdr; @@ -324,8 +323,6 @@ static int ena_xdp_xmit_buff(struct net_device *dev, req_id = xdp_ring->free_ids[next_to_use]; tx_info = &xdp_ring->tx_buffer_info[req_id]; tx_info->num_of_bufs = 0; - rx_ring = &xdp_ring->adapter->rx_ring[qid - - xdp_ring->adapter->xdp_first_ring]; page_ref_inc(rx_info->page); tx_info->xdp_rx_page = rx_info->page; -- GitLab From 0df952873636a6df4b7469a4eb4da1e477031e3a Mon Sep 17 00:00:00 2001 From: Nikita Yushchenko Date: Wed, 25 Dec 2019 08:22:38 +0300 Subject: [PATCH 1382/1437] mv88e6xxx: Add serdes Rx statistics If packet checker is enabled in the serdes, then Rx counter registers start working, and no side effects have been detected. This patch enables packet checker automatically when powering serdes on, and exposes Rx counter registers via ethtool statistics interface. Code partially basded by older attempt by Andrew Lunn. Signed-off-by: Nikita Yushchenko Reviewed-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/dsa/mv88e6xxx/chip.c | 3 + drivers/net/dsa/mv88e6xxx/serdes.c | 100 ++++++++++++++++++++++++++++- drivers/net/dsa/mv88e6xxx/serdes.h | 9 +++ 3 files changed, 109 insertions(+), 3 deletions(-) diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index 3bd9885291782..5eeeb6566196b 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -4424,6 +4424,9 @@ static const struct mv88e6xxx_ops mv88e6390_ops = { .gpio_ops = &mv88e6352_gpio_ops, .avb_ops = &mv88e6390_avb_ops, .ptp_ops = &mv88e6352_ptp_ops, + .serdes_get_sset_count = mv88e6390_serdes_get_sset_count, + .serdes_get_strings = mv88e6390_serdes_get_strings, + .serdes_get_stats = mv88e6390_serdes_get_stats, .phylink_validate = mv88e6390_phylink_validate, }; diff --git a/drivers/net/dsa/mv88e6xxx/serdes.c b/drivers/net/dsa/mv88e6xxx/serdes.c index 902feb3987465..8d8b3b74aee15 100644 --- a/drivers/net/dsa/mv88e6xxx/serdes.c +++ b/drivers/net/dsa/mv88e6xxx/serdes.c @@ -405,22 +405,116 @@ static int mv88e6390_serdes_power_sgmii(struct mv88e6xxx_chip *chip, u8 lane, return err; } +struct mv88e6390_serdes_hw_stat { + char string[ETH_GSTRING_LEN]; + int reg; +}; + +static struct mv88e6390_serdes_hw_stat mv88e6390_serdes_hw_stats[] = { + { "serdes_rx_pkts", 0xf021 }, + { "serdes_rx_bytes", 0xf024 }, + { "serdes_rx_pkts_error", 0xf027 }, +}; + +int mv88e6390_serdes_get_sset_count(struct mv88e6xxx_chip *chip, int port) +{ + if (mv88e6390_serdes_get_lane(chip, port) == 0) + return 0; + + return ARRAY_SIZE(mv88e6390_serdes_hw_stats); +} + +int mv88e6390_serdes_get_strings(struct mv88e6xxx_chip *chip, + int port, uint8_t *data) +{ + struct mv88e6390_serdes_hw_stat *stat; + int i; + + if (mv88e6390_serdes_get_lane(chip, port) == 0) + return 0; + + for (i = 0; i < ARRAY_SIZE(mv88e6390_serdes_hw_stats); i++) { + stat = &mv88e6390_serdes_hw_stats[i]; + memcpy(data + i * ETH_GSTRING_LEN, stat->string, + ETH_GSTRING_LEN); + } + return ARRAY_SIZE(mv88e6390_serdes_hw_stats); +} + +static uint64_t mv88e6390_serdes_get_stat(struct mv88e6xxx_chip *chip, int lane, + struct mv88e6390_serdes_hw_stat *stat) +{ + u16 reg[3]; + int err, i; + + for (i = 0; i < 3; i++) { + err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS, + stat->reg + i, ®[i]); + if (err) { + dev_err(chip->dev, "failed to read statistic\n"); + return 0; + } + } + + return reg[0] | ((u64)reg[1] << 16) | ((u64)reg[2] << 32); +} + +int mv88e6390_serdes_get_stats(struct mv88e6xxx_chip *chip, int port, + uint64_t *data) +{ + struct mv88e6390_serdes_hw_stat *stat; + int lane; + int i; + + lane = mv88e6390_serdes_get_lane(chip, port); + if (lane == 0) + return 0; + + for (i = 0; i < ARRAY_SIZE(mv88e6390_serdes_hw_stats); i++) { + stat = &mv88e6390_serdes_hw_stats[i]; + data[i] = mv88e6390_serdes_get_stat(chip, lane, stat); + } + + return ARRAY_SIZE(mv88e6390_serdes_hw_stats); +} + +static int mv88e6390_serdes_enable_checker(struct mv88e6xxx_chip *chip, u8 lane) +{ + u16 reg; + int err; + + err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS, + MV88E6390_PG_CONTROL, ®); + if (err) + return err; + + reg |= MV88E6390_PG_CONTROL_ENABLE_PC; + return mv88e6390_serdes_write(chip, lane, MDIO_MMD_PHYXS, + MV88E6390_PG_CONTROL, reg); +} + int mv88e6390_serdes_power(struct mv88e6xxx_chip *chip, int port, u8 lane, bool up) { u8 cmode = chip->ports[port].cmode; + int err = 0; switch (cmode) { case MV88E6XXX_PORT_STS_CMODE_SGMII: case MV88E6XXX_PORT_STS_CMODE_1000BASEX: case MV88E6XXX_PORT_STS_CMODE_2500BASEX: - return mv88e6390_serdes_power_sgmii(chip, lane, up); + err = mv88e6390_serdes_power_sgmii(chip, lane, up); + break; case MV88E6XXX_PORT_STS_CMODE_XAUI: case MV88E6XXX_PORT_STS_CMODE_RXAUI: - return mv88e6390_serdes_power_10g(chip, lane, up); + err = mv88e6390_serdes_power_10g(chip, lane, up); + break; } - return 0; + if (!err && up) + err = mv88e6390_serdes_enable_checker(chip, lane); + + return err; } static void mv88e6390_serdes_irq_link_sgmii(struct mv88e6xxx_chip *chip, diff --git a/drivers/net/dsa/mv88e6xxx/serdes.h b/drivers/net/dsa/mv88e6xxx/serdes.h index bd8df36ab537c..d16ef4da20b02 100644 --- a/drivers/net/dsa/mv88e6xxx/serdes.h +++ b/drivers/net/dsa/mv88e6xxx/serdes.h @@ -74,6 +74,10 @@ #define MV88E6390_SGMII_PHY_STATUS_SPD_DPL_VALID BIT(11) #define MV88E6390_SGMII_PHY_STATUS_LINK BIT(10) +/* Packet generator pad packet checker */ +#define MV88E6390_PG_CONTROL 0xf010 +#define MV88E6390_PG_CONTROL_ENABLE_PC BIT(0) + u8 mv88e6341_serdes_get_lane(struct mv88e6xxx_chip *chip, int port); u8 mv88e6352_serdes_get_lane(struct mv88e6xxx_chip *chip, int port); u8 mv88e6390_serdes_get_lane(struct mv88e6xxx_chip *chip, int port); @@ -99,6 +103,11 @@ int mv88e6352_serdes_get_strings(struct mv88e6xxx_chip *chip, int port, uint8_t *data); int mv88e6352_serdes_get_stats(struct mv88e6xxx_chip *chip, int port, uint64_t *data); +int mv88e6390_serdes_get_sset_count(struct mv88e6xxx_chip *chip, int port); +int mv88e6390_serdes_get_strings(struct mv88e6xxx_chip *chip, + int port, uint8_t *data); +int mv88e6390_serdes_get_stats(struct mv88e6xxx_chip *chip, int port, + uint64_t *data); /* Return the (first) SERDES lane address a port is using, 0 otherwise. */ static inline u8 mv88e6xxx_serdes_get_lane(struct mv88e6xxx_chip *chip, -- GitLab From 356b23c073dd063427102329b296061855b912d9 Mon Sep 17 00:00:00 2001 From: Kevin Kou Date: Wed, 25 Dec 2019 08:27:25 +0000 Subject: [PATCH 1383/1437] sctp: do trace_sctp_probe after SACK validation and check The function sctp_sf_eat_sack_6_2 now performs the Verification Tag validation, Chunk length validation, Bogu check, and also the detection of out-of-order SACK based on the RFC2960 Section 6.2 at the beginning, and finally performs the further processing of SACK. The trace_sctp_probe now triggered before the above necessary validation and check. this patch is to do the trace_sctp_probe after the chunk sanity tests, but keep doing trace if the SACK received is out of order, for the out-of-order SACK is valuable to congestion control debugging. v1->v2: - keep doing SCTP trace if the SACK is out of order as Marcelo's suggestion. v2->v3: - regenerate the patch as v2 generated on top of v1, and add 'net-next' tag to the new one as Marcelo's comments. Signed-off-by: Kevin Kou Acked-by: Marcelo Ricardo Leitner Acked-by: Neil Horman Signed-off-by: David S. Miller --- net/sctp/sm_statefuns.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c index 42558fa3f3e4f..748e3b19ec1dc 100644 --- a/net/sctp/sm_statefuns.c +++ b/net/sctp/sm_statefuns.c @@ -3281,8 +3281,6 @@ enum sctp_disposition sctp_sf_eat_sack_6_2(struct net *net, struct sctp_sackhdr *sackh; __u32 ctsn; - trace_sctp_probe(ep, asoc, chunk); - if (!sctp_vtag_verify(chunk, asoc)) return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands); @@ -3299,6 +3297,15 @@ enum sctp_disposition sctp_sf_eat_sack_6_2(struct net *net, chunk->subh.sack_hdr = sackh; ctsn = ntohl(sackh->cum_tsn_ack); + /* If Cumulative TSN Ack beyond the max tsn currently + * send, terminating the association and respond to the + * sender with an ABORT. + */ + if (TSN_lte(asoc->next_tsn, ctsn)) + return sctp_sf_violation_ctsn(net, ep, asoc, type, arg, commands); + + trace_sctp_probe(ep, asoc, chunk); + /* i) If Cumulative TSN Ack is less than the Cumulative TSN * Ack Point, then drop the SACK. Since Cumulative TSN * Ack is monotonically increasing, a SACK whose @@ -3312,13 +3319,6 @@ enum sctp_disposition sctp_sf_eat_sack_6_2(struct net *net, return SCTP_DISPOSITION_DISCARD; } - /* If Cumulative TSN Ack beyond the max tsn currently - * send, terminating the association and respond to the - * sender with an ABORT. - */ - if (!TSN_lt(ctsn, asoc->next_tsn)) - return sctp_sf_violation_ctsn(net, ep, asoc, type, arg, commands); - /* Return this SACK for further processing. */ sctp_add_cmd_sf(commands, SCTP_CMD_PROCESS_SACK, SCTP_CHUNK(chunk)); -- GitLab From 70cf3dc7313207816255b9acb0dffb19dae78144 Mon Sep 17 00:00:00 2001 From: Shmulik Ladkani Date: Wed, 25 Dec 2019 10:51:01 +0200 Subject: [PATCH 1384/1437] net/sched: act_mirred: Pull mac prior redir to non mac_header_xmit device There's no skb_pull performed when a mirred action is set at egress of a mac device, with a target device/action that expects skb->data to point at the network header. As a result, either the target device is errornously given an skb with data pointing to the mac (egress case), or the net stack receives the skb with data pointing to the mac (ingress case). E.g: # tc qdisc add dev eth9 root handle 1: prio # tc filter add dev eth9 parent 1: prio 9 protocol ip handle 9 basic \ action mirred egress redirect dev tun0 (tun0 is a tun device. result: tun0 errornously gets the eth header instead of the iph) Revise the push/pull logic of tcf_mirred_act() to not rely on the skb_at_tc_ingress() vs tcf_mirred_act_wants_ingress() comparison, as it does not cover all "pull" cases. Instead, calculate whether the required action on the target device requires the data to point at the network header, and compare this to whether skb->data points to network header - and make the push/pull adjustments as necessary. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Signed-off-by: Shmulik Ladkani Tested-by: Jamal Hadi Salim Acked-by: Jamal Hadi Salim Signed-off-by: David S. Miller --- net/sched/act_mirred.c | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/net/sched/act_mirred.c b/net/sched/act_mirred.c index 1e3eb3a975324..1ad300e6dbc0a 100644 --- a/net/sched/act_mirred.c +++ b/net/sched/act_mirred.c @@ -219,8 +219,10 @@ static int tcf_mirred_act(struct sk_buff *skb, const struct tc_action *a, bool use_reinsert; bool want_ingress; bool is_redirect; + bool expects_nh; int m_eaction; int mac_len; + bool at_nh; rec_level = __this_cpu_inc_return(mirred_rec_level); if (unlikely(rec_level > MIRRED_RECURSION_LIMIT)) { @@ -261,19 +263,19 @@ static int tcf_mirred_act(struct sk_buff *skb, const struct tc_action *a, goto out; } - /* If action's target direction differs than filter's direction, - * and devices expect a mac header on xmit, then mac push/pull is - * needed. - */ want_ingress = tcf_mirred_act_wants_ingress(m_eaction); - if (skb_at_tc_ingress(skb) != want_ingress && m_mac_header_xmit) { - if (!skb_at_tc_ingress(skb)) { - /* caught at egress, act ingress: pull mac */ - mac_len = skb_network_header(skb) - skb_mac_header(skb); + + expects_nh = want_ingress || !m_mac_header_xmit; + at_nh = skb->data == skb_network_header(skb); + if (at_nh != expects_nh) { + mac_len = skb_at_tc_ingress(skb) ? skb->mac_len : + skb_network_header(skb) - skb_mac_header(skb); + if (expects_nh) { + /* target device/action expect data at nh */ skb_pull_rcsum(skb2, mac_len); } else { - /* caught at ingress, act egress: push mac */ - skb_push_rcsum(skb2, skb->mac_len); + /* target device/action expect data at mac */ + skb_push_rcsum(skb2, mac_len); } } -- GitLab From bd6f48546b9cb7a785344fc78058c420923d7ed8 Mon Sep 17 00:00:00 2001 From: Martin Blumenstingl Date: Thu, 26 Dec 2019 20:01:01 +0100 Subject: [PATCH 1385/1437] net: stmmac: dwmac-meson8b: Fix the RGMII TX delay on Meson8b/8m2 SoCs GXBB and newer SoCs use the fixed FCLK_DIV2 (1GHz) clock as input for the m250_sel clock. Meson8b and Meson8m2 use MPLL2 instead, whose rate can be adjusted at runtime. So far we have been running MPLL2 with ~250MHz (and the internal m250_div with value 1), which worked enough that we could transfer data with an TX delay of 4ns. Unfortunately there is high packet loss with an RGMII PHY when transferring data (receiving data works fine though). Odroid-C1's u-boot is running with a TX delay of only 2ns as well as the internal m250_div set to 2 - no lost (TX) packets can be observed with that setting in u-boot. Manual testing has shown that the TX packet loss goes away when using the following settings in Linux (the vendor kernel uses the same settings): - MPLL2 clock set to ~500MHz - m250_div set to 2 - TX delay set to 2ns on the MAC side Update the m250_div divider settings to only accept dividers greater or equal 2 to fix the TX delay generated by the MAC. iperf3 results before the change: [ ID] Interval Transfer Bitrate Retr [ 5] 0.00-10.00 sec 182 MBytes 153 Mbits/sec 514 sender [ 5] 0.00-10.00 sec 182 MBytes 152 Mbits/sec receiver iperf3 results after the change (including an updated TX delay of 2ns): [ ID] Interval Transfer Bitrate Retr Cwnd [ 5] 0.00-10.00 sec 927 MBytes 778 Mbits/sec 0 sender [ 5] 0.00-10.01 sec 927 MBytes 777 Mbits/sec receiver Fixes: 4f6a71b84e1afd ("net: stmmac: dwmac-meson8b: fix internal RGMII clock configuration") Signed-off-by: Martin Blumenstingl Reviewed-by: Andrew Lunn Signed-off-by: David S. Miller --- .../net/ethernet/stmicro/stmmac/dwmac-meson8b.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-meson8b.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-meson8b.c index bd6c01004913a..0e2fa14f14237 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-meson8b.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-meson8b.c @@ -112,6 +112,14 @@ static int meson8b_init_rgmii_tx_clk(struct meson8b_dwmac *dwmac) struct device *dev = dwmac->dev; const char *parent_name, *mux_parent_names[MUX_CLK_NUM_PARENTS]; struct meson8b_dwmac_clk_configs *clk_configs; + static const struct clk_div_table div_table[] = { + { .div = 2, .val = 2, }, + { .div = 3, .val = 3, }, + { .div = 4, .val = 4, }, + { .div = 5, .val = 5, }, + { .div = 6, .val = 6, }, + { .div = 7, .val = 7, }, + }; clk_configs = devm_kzalloc(dev, sizeof(*clk_configs), GFP_KERNEL); if (!clk_configs) @@ -146,9 +154,9 @@ static int meson8b_init_rgmii_tx_clk(struct meson8b_dwmac *dwmac) clk_configs->m250_div.reg = dwmac->regs + PRG_ETH0; clk_configs->m250_div.shift = PRG_ETH0_CLK_M250_DIV_SHIFT; clk_configs->m250_div.width = PRG_ETH0_CLK_M250_DIV_WIDTH; - clk_configs->m250_div.flags = CLK_DIVIDER_ONE_BASED | - CLK_DIVIDER_ALLOW_ZERO | - CLK_DIVIDER_ROUND_CLOSEST; + clk_configs->m250_div.table = div_table; + clk_configs->m250_div.flags = CLK_DIVIDER_ALLOW_ZERO | + CLK_DIVIDER_ROUND_CLOSEST; clk = meson8b_dwmac_register_clk(dwmac, "m250_div", &parent_name, 1, &clk_divider_ops, &clk_configs->m250_div.hw); -- GitLab From 2b4a8990b7df55875745a80a609a1ceaaf51f322 Mon Sep 17 00:00:00 2001 From: Michal Kubecek Date: Fri, 27 Dec 2019 15:55:18 +0100 Subject: [PATCH 1386/1437] ethtool: introduce ethtool netlink interface Basic genetlink and init infrastructure for the netlink interface, register genetlink family "ethtool". Add CONFIG_ETHTOOL_NETLINK Kconfig option to make the build optional. Add initial overall interface description into Documentation/networking/ethtool-netlink.rst, further patches will add more detailed information. Signed-off-by: Michal Kubecek Reviewed-by: Florian Fainelli Reviewed-by: Andrew Lunn Signed-off-by: David S. Miller --- Documentation/networking/ethtool-netlink.rst | 216 +++++++++++++++++++ Documentation/networking/index.rst | 1 + include/linux/ethtool_netlink.h | 9 + include/uapi/linux/ethtool_netlink.h | 36 ++++ net/Kconfig | 8 + net/ethtool/Makefile | 6 +- net/ethtool/netlink.c | 33 +++ net/ethtool/netlink.h | 10 + 8 files changed, 318 insertions(+), 1 deletion(-) create mode 100644 Documentation/networking/ethtool-netlink.rst create mode 100644 include/linux/ethtool_netlink.h create mode 100644 include/uapi/linux/ethtool_netlink.h create mode 100644 net/ethtool/netlink.c create mode 100644 net/ethtool/netlink.h diff --git a/Documentation/networking/ethtool-netlink.rst b/Documentation/networking/ethtool-netlink.rst new file mode 100644 index 0000000000000..9448442ad293a --- /dev/null +++ b/Documentation/networking/ethtool-netlink.rst @@ -0,0 +1,216 @@ +============================= +Netlink interface for ethtool +============================= + + +Basic information +================= + +Netlink interface for ethtool uses generic netlink family ``ethtool`` +(userspace application should use macros ``ETHTOOL_GENL_NAME`` and +``ETHTOOL_GENL_VERSION`` defined in ```` uapi +header). This family does not use a specific header, all information in +requests and replies is passed using netlink attributes. + +The ethtool netlink interface uses extended ACK for error and warning +reporting, userspace application developers are encouraged to make these +messages available to user in a suitable way. + +Requests can be divided into three categories: "get" (retrieving information), +"set" (setting parameters) and "action" (invoking an action). + +All "set" and "action" type requests require admin privileges +(``CAP_NET_ADMIN`` in the namespace). Most "get" type requests are allowed for +anyone but there are exceptions (where the response contains sensitive +information). In some cases, the request as such is allowed for anyone but +unprivileged users have attributes with sensitive information (e.g. +wake-on-lan password) omitted. + + +Conventions +=========== + +Attributes which represent a boolean value usually use NLA_U8 type so that we +can distinguish three states: "on", "off" and "not present" (meaning the +information is not available in "get" requests or value is not to be changed +in "set" requests). For these attributes, the "true" value should be passed as +number 1 but any non-zero value should be understood as "true" by recipient. +In the tables below, "bool" denotes NLA_U8 attributes interpreted in this way. + +In the message structure descriptions below, if an attribute name is suffixed +with "+", parent nest can contain multiple attributes of the same type. This +implements an array of entries. + + +Request header +============== + +Each request or reply message contains a nested attribute with common header. +Structure of this header is + + ============================== ====== ============================= + ``ETHTOOL_A_HEADER_DEV_INDEX`` u32 device ifindex + ``ETHTOOL_A_HEADER_DEV_NAME`` string device name + ``ETHTOOL_A_HEADER_FLAGS`` u32 flags common for all requests + ============================== ====== ============================= + +``ETHTOOL_A_HEADER_DEV_INDEX`` and ``ETHTOOL_A_HEADER_DEV_NAME`` identify the +device message relates to. One of them is sufficient in requests, if both are +used, they must identify the same device. Some requests, e.g. global string +sets, do not require device identification. Most ``GET`` requests also allow +dump requests without device identification to query the same information for +all devices providing it (each device in a separate message). + +``ETHTOOL_A_HEADER_FLAGS`` is a bitmap of request flags common for all request +types. The interpretation of these flags is the same for all request types but +the flags may not apply to requests. Recognized flags are: + + ================================= =================================== + ``ETHTOOL_FLAG_COMPACT_BITSETS`` use compact format bitsets in reply + ``ETHTOOL_FLAG_OMIT_REPLY`` omit optional reply (_SET and _ACT) + ================================= =================================== + +New request flags should follow the general idea that if the flag is not set, +the behaviour is backward compatible, i.e. requests from old clients not aware +of the flag should be interpreted the way the client expects. A client must +not set flags it does not understand. + + +List of message types +===================== + +All constants identifying message types use ``ETHTOOL_CMD_`` prefix and suffix +according to message purpose: + + ============== ====================================== + ``_GET`` userspace request to retrieve data + ``_SET`` userspace request to set data + ``_ACT`` userspace request to perform an action + ``_GET_REPLY`` kernel reply to a ``GET`` request + ``_SET_REPLY`` kernel reply to a ``SET`` request + ``_ACT_REPLY`` kernel reply to an ``ACT`` request + ``_NTF`` kernel notification + ============== ====================================== + +``GET`` requests are sent by userspace applications to retrieve device +information. They usually do not contain any message specific attributes. +Kernel replies with corresponding "GET_REPLY" message. For most types, ``GET`` +request with ``NLM_F_DUMP`` and no device identification can be used to query +the information for all devices supporting the request. + +If the data can be also modified, corresponding ``SET`` message with the same +layout as corresponding ``GET_REPLY`` is used to request changes. Only +attributes where a change is requested are included in such request (also, not +all attributes may be changed). Replies to most ``SET`` request consist only +of error code and extack; if kernel provides additional data, it is sent in +the form of corresponding ``SET_REPLY`` message which can be suppressed by +setting ``ETHTOOL_FLAG_OMIT_REPLY`` flag in request header. + +Data modification also triggers sending a ``NTF`` message with a notification. +These usually bear only a subset of attributes which was affected by the +change. The same notification is issued if the data is modified using other +means (mostly ioctl ethtool interface). Unlike notifications from ethtool +netlink code which are only sent if something actually changed, notifications +triggered by ioctl interface may be sent even if the request did not actually +change any data. + +``ACT`` messages request kernel (driver) to perform a specific action. If some +information is reported by kernel (which can be suppressed by setting +``ETHTOOL_FLAG_OMIT_REPLY`` flag in request header), the reply takes form of +an ``ACT_REPLY`` message. Performing an action also triggers a notification +(``NTF`` message). + +Later sections describe the format and semantics of these messages. + + +Request translation +=================== + +The following table maps ioctl commands to netlink commands providing their +functionality. Entries with "n/a" in right column are commands which do not +have their netlink replacement yet. + + =================================== ===================================== + ioctl command netlink command + =================================== ===================================== + ``ETHTOOL_GSET`` n/a + ``ETHTOOL_SSET`` n/a + ``ETHTOOL_GDRVINFO`` n/a + ``ETHTOOL_GREGS`` n/a + ``ETHTOOL_GWOL`` n/a + ``ETHTOOL_SWOL`` n/a + ``ETHTOOL_GMSGLVL`` n/a + ``ETHTOOL_SMSGLVL`` n/a + ``ETHTOOL_NWAY_RST`` n/a + ``ETHTOOL_GLINK`` n/a + ``ETHTOOL_GEEPROM`` n/a + ``ETHTOOL_SEEPROM`` n/a + ``ETHTOOL_GCOALESCE`` n/a + ``ETHTOOL_SCOALESCE`` n/a + ``ETHTOOL_GRINGPARAM`` n/a + ``ETHTOOL_SRINGPARAM`` n/a + ``ETHTOOL_GPAUSEPARAM`` n/a + ``ETHTOOL_SPAUSEPARAM`` n/a + ``ETHTOOL_GRXCSUM`` n/a + ``ETHTOOL_SRXCSUM`` n/a + ``ETHTOOL_GTXCSUM`` n/a + ``ETHTOOL_STXCSUM`` n/a + ``ETHTOOL_GSG`` n/a + ``ETHTOOL_SSG`` n/a + ``ETHTOOL_TEST`` n/a + ``ETHTOOL_GSTRINGS`` n/a + ``ETHTOOL_PHYS_ID`` n/a + ``ETHTOOL_GSTATS`` n/a + ``ETHTOOL_GTSO`` n/a + ``ETHTOOL_STSO`` n/a + ``ETHTOOL_GPERMADDR`` rtnetlink ``RTM_GETLINK`` + ``ETHTOOL_GUFO`` n/a + ``ETHTOOL_SUFO`` n/a + ``ETHTOOL_GGSO`` n/a + ``ETHTOOL_SGSO`` n/a + ``ETHTOOL_GFLAGS`` n/a + ``ETHTOOL_SFLAGS`` n/a + ``ETHTOOL_GPFLAGS`` n/a + ``ETHTOOL_SPFLAGS`` n/a + ``ETHTOOL_GRXFH`` n/a + ``ETHTOOL_SRXFH`` n/a + ``ETHTOOL_GGRO`` n/a + ``ETHTOOL_SGRO`` n/a + ``ETHTOOL_GRXRINGS`` n/a + ``ETHTOOL_GRXCLSRLCNT`` n/a + ``ETHTOOL_GRXCLSRULE`` n/a + ``ETHTOOL_GRXCLSRLALL`` n/a + ``ETHTOOL_SRXCLSRLDEL`` n/a + ``ETHTOOL_SRXCLSRLINS`` n/a + ``ETHTOOL_FLASHDEV`` n/a + ``ETHTOOL_RESET`` n/a + ``ETHTOOL_SRXNTUPLE`` n/a + ``ETHTOOL_GRXNTUPLE`` n/a + ``ETHTOOL_GSSET_INFO`` n/a + ``ETHTOOL_GRXFHINDIR`` n/a + ``ETHTOOL_SRXFHINDIR`` n/a + ``ETHTOOL_GFEATURES`` n/a + ``ETHTOOL_SFEATURES`` n/a + ``ETHTOOL_GCHANNELS`` n/a + ``ETHTOOL_SCHANNELS`` n/a + ``ETHTOOL_SET_DUMP`` n/a + ``ETHTOOL_GET_DUMP_FLAG`` n/a + ``ETHTOOL_GET_DUMP_DATA`` n/a + ``ETHTOOL_GET_TS_INFO`` n/a + ``ETHTOOL_GMODULEINFO`` n/a + ``ETHTOOL_GMODULEEEPROM`` n/a + ``ETHTOOL_GEEE`` n/a + ``ETHTOOL_SEEE`` n/a + ``ETHTOOL_GRSSH`` n/a + ``ETHTOOL_SRSSH`` n/a + ``ETHTOOL_GTUNABLE`` n/a + ``ETHTOOL_STUNABLE`` n/a + ``ETHTOOL_GPHYSTATS`` n/a + ``ETHTOOL_PERQUEUE`` n/a + ``ETHTOOL_GLINKSETTINGS`` n/a + ``ETHTOOL_SLINKSETTINGS`` n/a + ``ETHTOOL_PHY_GTUNABLE`` n/a + ``ETHTOOL_PHY_STUNABLE`` n/a + ``ETHTOOL_GFECPARAM`` n/a + ``ETHTOOL_SFECPARAM`` n/a + =================================== ===================================== diff --git a/Documentation/networking/index.rst b/Documentation/networking/index.rst index 5acab1290e034..bee73be7af93f 100644 --- a/Documentation/networking/index.rst +++ b/Documentation/networking/index.rst @@ -16,6 +16,7 @@ Contents: devlink-info-versions devlink-trap devlink-trap-netdevsim + ethtool-netlink ieee802154 j1939 kapi diff --git a/include/linux/ethtool_netlink.h b/include/linux/ethtool_netlink.h new file mode 100644 index 0000000000000..f27e92b5f344b --- /dev/null +++ b/include/linux/ethtool_netlink.h @@ -0,0 +1,9 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#ifndef _LINUX_ETHTOOL_NETLINK_H_ +#define _LINUX_ETHTOOL_NETLINK_H_ + +#include +#include + +#endif /* _LINUX_ETHTOOL_NETLINK_H_ */ diff --git a/include/uapi/linux/ethtool_netlink.h b/include/uapi/linux/ethtool_netlink.h new file mode 100644 index 0000000000000..3c93276ba0666 --- /dev/null +++ b/include/uapi/linux/ethtool_netlink.h @@ -0,0 +1,36 @@ +/* SPDX-License-Identifier: GPL-2.0-only WITH Linux-syscall-note */ +/* + * include/uapi/linux/ethtool_netlink.h - netlink interface for ethtool + * + * See Documentation/networking/ethtool-netlink.txt in kernel source tree for + * doucumentation of the interface. + */ + +#ifndef _UAPI_LINUX_ETHTOOL_NETLINK_H_ +#define _UAPI_LINUX_ETHTOOL_NETLINK_H_ + +#include + +/* message types - userspace to kernel */ +enum { + ETHTOOL_MSG_USER_NONE, + + /* add new constants above here */ + __ETHTOOL_MSG_USER_CNT, + ETHTOOL_MSG_USER_MAX = __ETHTOOL_MSG_USER_CNT - 1 +}; + +/* message types - kernel to userspace */ +enum { + ETHTOOL_MSG_KERNEL_NONE, + + /* add new constants above here */ + __ETHTOOL_MSG_KERNEL_CNT, + ETHTOOL_MSG_KERNEL_MAX = __ETHTOOL_MSG_KERNEL_CNT - 1 +}; + +/* generic netlink info */ +#define ETHTOOL_GENL_NAME "ethtool" +#define ETHTOOL_GENL_VERSION 1 + +#endif /* _UAPI_LINUX_ETHTOOL_NETLINK_H_ */ diff --git a/net/Kconfig b/net/Kconfig index 52af65e5d28c3..54916b7adb9b3 100644 --- a/net/Kconfig +++ b/net/Kconfig @@ -449,6 +449,14 @@ config FAILOVER migration of VMs with direct attached VFs by failing over to the paravirtual datapath when the VF is unplugged. +config ETHTOOL_NETLINK + bool "Netlink interface for ethtool" + default y + help + An alternative userspace interface for ethtool based on generic + netlink. It provides better extensibility and some new features, + e.g. notification messages. + endif # if NET # Used by archs to tell that they support BPF JIT compiler plus which flavour. diff --git a/net/ethtool/Makefile b/net/ethtool/Makefile index f683876189731..59d5ee230c292 100644 --- a/net/ethtool/Makefile +++ b/net/ethtool/Makefile @@ -1,3 +1,7 @@ # SPDX-License-Identifier: GPL-2.0-only -obj-y += ioctl.o common.o +obj-y += ioctl.o common.o + +obj-$(CONFIG_ETHTOOL_NETLINK) += ethtool_nl.o + +ethtool_nl-y := netlink.o diff --git a/net/ethtool/netlink.c b/net/ethtool/netlink.c new file mode 100644 index 0000000000000..59e1ebde2f15a --- /dev/null +++ b/net/ethtool/netlink.c @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include +#include "netlink.h" + +/* genetlink setup */ + +static const struct genl_ops ethtool_genl_ops[] = { +}; + +static struct genl_family ethtool_genl_family = { + .name = ETHTOOL_GENL_NAME, + .version = ETHTOOL_GENL_VERSION, + .netnsok = true, + .parallel_ops = true, + .ops = ethtool_genl_ops, + .n_ops = ARRAY_SIZE(ethtool_genl_ops), +}; + +/* module setup */ + +static int __init ethnl_init(void) +{ + int ret; + + ret = genl_register_family(ðtool_genl_family); + if (WARN(ret < 0, "ethtool: genetlink family registration failed")) + return ret; + + return 0; +} + +subsys_initcall(ethnl_init); diff --git a/net/ethtool/netlink.h b/net/ethtool/netlink.h new file mode 100644 index 0000000000000..e4220780d3685 --- /dev/null +++ b/net/ethtool/netlink.h @@ -0,0 +1,10 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#ifndef _NET_ETHTOOL_NETLINK_H +#define _NET_ETHTOOL_NETLINK_H + +#include +#include +#include + +#endif /* _NET_ETHTOOL_NETLINK_H */ -- GitLab From 041b1c5d4a53e97fc9e029ae32469552ca12cb9b Mon Sep 17 00:00:00 2001 From: Michal Kubecek Date: Fri, 27 Dec 2019 15:55:23 +0100 Subject: [PATCH 1387/1437] ethtool: helper functions for netlink interface Add common request/reply header definition and helpers to parse request header and fill reply header. Provide ethnl_update_* helpers to update structure members from request attributes (to be used for *_SET requests). Signed-off-by: Michal Kubecek Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller --- include/uapi/linux/ethtool_netlink.h | 21 +++ net/ethtool/netlink.c | 166 ++++++++++++++++++++++ net/ethtool/netlink.h | 205 +++++++++++++++++++++++++++ 3 files changed, 392 insertions(+) diff --git a/include/uapi/linux/ethtool_netlink.h b/include/uapi/linux/ethtool_netlink.h index 3c93276ba0666..82fc3b5f41c9c 100644 --- a/include/uapi/linux/ethtool_netlink.h +++ b/include/uapi/linux/ethtool_netlink.h @@ -29,6 +29,27 @@ enum { ETHTOOL_MSG_KERNEL_MAX = __ETHTOOL_MSG_KERNEL_CNT - 1 }; +/* request header */ + +/* use compact bitsets in reply */ +#define ETHTOOL_FLAG_COMPACT_BITSETS (1 << 0) +/* provide optional reply for SET or ACT requests */ +#define ETHTOOL_FLAG_OMIT_REPLY (1 << 1) + +#define ETHTOOL_FLAG_ALL (ETHTOOL_FLAG_COMPACT_BITSETS | \ + ETHTOOL_FLAG_OMIT_REPLY) + +enum { + ETHTOOL_A_HEADER_UNSPEC, + ETHTOOL_A_HEADER_DEV_INDEX, /* u32 */ + ETHTOOL_A_HEADER_DEV_NAME, /* string */ + ETHTOOL_A_HEADER_FLAGS, /* u32 - ETHTOOL_FLAG_* */ + + /* add new constants above here */ + __ETHTOOL_A_HEADER_CNT, + ETHTOOL_A_HEADER_MAX = __ETHTOOL_A_HEADER_CNT - 1 +}; + /* generic netlink info */ #define ETHTOOL_GENL_NAME "ethtool" #define ETHTOOL_GENL_VERSION 1 diff --git a/net/ethtool/netlink.c b/net/ethtool/netlink.c index 59e1ebde2f15a..aef882e0c3f57 100644 --- a/net/ethtool/netlink.c +++ b/net/ethtool/netlink.c @@ -1,8 +1,174 @@ // SPDX-License-Identifier: GPL-2.0-only +#include #include #include "netlink.h" +static struct genl_family ethtool_genl_family; + +static const struct nla_policy ethnl_header_policy[ETHTOOL_A_HEADER_MAX + 1] = { + [ETHTOOL_A_HEADER_UNSPEC] = { .type = NLA_REJECT }, + [ETHTOOL_A_HEADER_DEV_INDEX] = { .type = NLA_U32 }, + [ETHTOOL_A_HEADER_DEV_NAME] = { .type = NLA_NUL_STRING, + .len = ALTIFNAMSIZ - 1 }, + [ETHTOOL_A_HEADER_FLAGS] = { .type = NLA_U32 }, +}; + +/** + * ethnl_parse_header() - parse request header + * @req_info: structure to put results into + * @header: nest attribute with request header + * @net: request netns + * @extack: netlink extack for error reporting + * @require_dev: fail if no device identified in header + * + * Parse request header in nested attribute @nest and puts results into + * the structure pointed to by @req_info. Extack from @info is used for error + * reporting. If req_info->dev is not null on return, reference to it has + * been taken. If error is returned, *req_info is null initialized and no + * reference is held. + * + * Return: 0 on success or negative error code + */ +int ethnl_parse_header(struct ethnl_req_info *req_info, + const struct nlattr *header, struct net *net, + struct netlink_ext_ack *extack, bool require_dev) +{ + struct nlattr *tb[ETHTOOL_A_HEADER_MAX + 1]; + const struct nlattr *devname_attr; + struct net_device *dev = NULL; + int ret; + + if (!header) { + NL_SET_ERR_MSG(extack, "request header missing"); + return -EINVAL; + } + ret = nla_parse_nested(tb, ETHTOOL_A_HEADER_MAX, header, + ethnl_header_policy, extack); + if (ret < 0) + return ret; + devname_attr = tb[ETHTOOL_A_HEADER_DEV_NAME]; + + if (tb[ETHTOOL_A_HEADER_DEV_INDEX]) { + u32 ifindex = nla_get_u32(tb[ETHTOOL_A_HEADER_DEV_INDEX]); + + dev = dev_get_by_index(net, ifindex); + if (!dev) { + NL_SET_ERR_MSG_ATTR(extack, + tb[ETHTOOL_A_HEADER_DEV_INDEX], + "no device matches ifindex"); + return -ENODEV; + } + /* if both ifindex and ifname are passed, they must match */ + if (devname_attr && + strncmp(dev->name, nla_data(devname_attr), IFNAMSIZ)) { + dev_put(dev); + NL_SET_ERR_MSG_ATTR(extack, header, + "ifindex and name do not match"); + return -ENODEV; + } + } else if (devname_attr) { + dev = dev_get_by_name(net, nla_data(devname_attr)); + if (!dev) { + NL_SET_ERR_MSG_ATTR(extack, devname_attr, + "no device matches name"); + return -ENODEV; + } + } else if (require_dev) { + NL_SET_ERR_MSG_ATTR(extack, header, + "neither ifindex nor name specified"); + return -EINVAL; + } + + if (dev && !netif_device_present(dev)) { + dev_put(dev); + NL_SET_ERR_MSG(extack, "device not present"); + return -ENODEV; + } + + req_info->dev = dev; + if (tb[ETHTOOL_A_HEADER_FLAGS]) + req_info->flags = nla_get_u32(tb[ETHTOOL_A_HEADER_FLAGS]); + + return 0; +} + +/** + * ethnl_fill_reply_header() - Put common header into a reply message + * @skb: skb with the message + * @dev: network device to describe in header + * @attrtype: attribute type to use for the nest + * + * Create a nested attribute with attributes describing given network device. + * + * Return: 0 on success, error value (-EMSGSIZE only) on error + */ +int ethnl_fill_reply_header(struct sk_buff *skb, struct net_device *dev, + u16 attrtype) +{ + struct nlattr *nest; + + if (!dev) + return 0; + nest = nla_nest_start(skb, attrtype); + if (!nest) + return -EMSGSIZE; + + if (nla_put_u32(skb, ETHTOOL_A_HEADER_DEV_INDEX, (u32)dev->ifindex) || + nla_put_string(skb, ETHTOOL_A_HEADER_DEV_NAME, dev->name)) + goto nla_put_failure; + /* If more attributes are put into reply header, ethnl_header_size() + * must be updated to account for them. + */ + + nla_nest_end(skb, nest); + return 0; + +nla_put_failure: + nla_nest_cancel(skb, nest); + return -EMSGSIZE; +} + +/** + * ethnl_reply_init() - Create skb for a reply and fill device identification + * @payload: payload length (without netlink and genetlink header) + * @dev: device the reply is about (may be null) + * @cmd: ETHTOOL_MSG_* message type for reply + * @info: genetlink info of the received packet we respond to + * @ehdrp: place to store payload pointer returned by genlmsg_new() + * + * Return: pointer to allocated skb on success, NULL on error + */ +struct sk_buff *ethnl_reply_init(size_t payload, struct net_device *dev, u8 cmd, + u16 hdr_attrtype, struct genl_info *info, + void **ehdrp) +{ + struct sk_buff *skb; + + skb = genlmsg_new(payload, GFP_KERNEL); + if (!skb) + goto err; + *ehdrp = genlmsg_put_reply(skb, info, ðtool_genl_family, 0, cmd); + if (!*ehdrp) + goto err_free; + + if (dev) { + int ret; + + ret = ethnl_fill_reply_header(skb, dev, hdr_attrtype); + if (ret < 0) + goto err_free; + } + return skb; + +err_free: + nlmsg_free(skb); +err: + if (info) + GENL_SET_ERR_MSG(info, "failed to setup reply message"); + return NULL; +} + /* genetlink setup */ static const struct genl_ops ethtool_genl_ops[] = { diff --git a/net/ethtool/netlink.h b/net/ethtool/netlink.h index e4220780d3685..05d7183da8946 100644 --- a/net/ethtool/netlink.h +++ b/net/ethtool/netlink.h @@ -6,5 +6,210 @@ #include #include #include +#include + +struct ethnl_req_info; + +int ethnl_parse_header(struct ethnl_req_info *req_info, + const struct nlattr *nest, struct net *net, + struct netlink_ext_ack *extack, bool require_dev); +int ethnl_fill_reply_header(struct sk_buff *skb, struct net_device *dev, + u16 attrtype); +struct sk_buff *ethnl_reply_init(size_t payload, struct net_device *dev, u8 cmd, + u16 hdr_attrtype, struct genl_info *info, + void **ehdrp); + +/** + * ethnl_strz_size() - calculate attribute length for fixed size string + * @s: ETH_GSTRING_LEN sized string (may not be null terminated) + * + * Return: total length of an attribute with null terminated string from @s + */ +static inline int ethnl_strz_size(const char *s) +{ + return nla_total_size(strnlen(s, ETH_GSTRING_LEN) + 1); +} + +/** + * ethnl_put_strz() - put string attribute with fixed size string + * @skb: skb with the message + * @attrype: attribute type + * @s: ETH_GSTRING_LEN sized string (may not be null terminated) + * + * Puts an attribute with null terminated string from @s into the message. + * + * Return: 0 on success, negative error code on failure + */ +static inline int ethnl_put_strz(struct sk_buff *skb, u16 attrtype, + const char *s) +{ + unsigned int len = strnlen(s, ETH_GSTRING_LEN); + struct nlattr *attr; + + attr = nla_reserve(skb, attrtype, len + 1); + if (!attr) + return -EMSGSIZE; + + memcpy(nla_data(attr), s, len); + ((char *)nla_data(attr))[len] = '\0'; + return 0; +} + +/** + * ethnl_update_u32() - update u32 value from NLA_U32 attribute + * @dst: value to update + * @attr: netlink attribute with new value or null + * @mod: pointer to bool for modification tracking + * + * Copy the u32 value from NLA_U32 netlink attribute @attr into variable + * pointed to by @dst; do nothing if @attr is null. Bool pointed to by @mod + * is set to true if this function changed the value of *dst, otherwise it + * is left as is. + */ +static inline void ethnl_update_u32(u32 *dst, const struct nlattr *attr, + bool *mod) +{ + u32 val; + + if (!attr) + return; + val = nla_get_u32(attr); + if (*dst == val) + return; + + *dst = val; + *mod = true; +} + +/** + * ethnl_update_u8() - update u8 value from NLA_U8 attribute + * @dst: value to update + * @attr: netlink attribute with new value or null + * @mod: pointer to bool for modification tracking + * + * Copy the u8 value from NLA_U8 netlink attribute @attr into variable + * pointed to by @dst; do nothing if @attr is null. Bool pointed to by @mod + * is set to true if this function changed the value of *dst, otherwise it + * is left as is. + */ +static inline void ethnl_update_u8(u8 *dst, const struct nlattr *attr, + bool *mod) +{ + u8 val; + + if (!attr) + return; + val = nla_get_u8(attr); + if (*dst == val) + return; + + *dst = val; + *mod = true; +} + +/** + * ethnl_update_bool32() - update u32 used as bool from NLA_U8 attribute + * @dst: value to update + * @attr: netlink attribute with new value or null + * @mod: pointer to bool for modification tracking + * + * Use the u8 value from NLA_U8 netlink attribute @attr to set u32 variable + * pointed to by @dst to 0 (if zero) or 1 (if not); do nothing if @attr is + * null. Bool pointed to by @mod is set to true if this function changed the + * logical value of *dst, otherwise it is left as is. + */ +static inline void ethnl_update_bool32(u32 *dst, const struct nlattr *attr, + bool *mod) +{ + u8 val; + + if (!attr) + return; + val = !!nla_get_u8(attr); + if (!!*dst == val) + return; + + *dst = val; + *mod = true; +} + +/** + * ethnl_update_binary() - update binary data from NLA_BINARY atribute + * @dst: value to update + * @len: destination buffer length + * @attr: netlink attribute with new value or null + * @mod: pointer to bool for modification tracking + * + * Use the u8 value from NLA_U8 netlink attribute @attr to rewrite data block + * of length @len at @dst by attribute payload; do nothing if @attr is null. + * Bool pointed to by @mod is set to true if this function changed the logical + * value of *dst, otherwise it is left as is. + */ +static inline void ethnl_update_binary(void *dst, unsigned int len, + const struct nlattr *attr, bool *mod) +{ + if (!attr) + return; + if (nla_len(attr) < len) + len = nla_len(attr); + if (!memcmp(dst, nla_data(attr), len)) + return; + + memcpy(dst, nla_data(attr), len); + *mod = true; +} + +/** + * ethnl_update_bitfield32() - update u32 value from NLA_BITFIELD32 attribute + * @dst: value to update + * @attr: netlink attribute with new value or null + * @mod: pointer to bool for modification tracking + * + * Update bits in u32 value which are set in attribute's mask to values from + * attribute's value. Do nothing if @attr is null or the value wouldn't change; + * otherwise, set bool pointed to by @mod to true. + */ +static inline void ethnl_update_bitfield32(u32 *dst, const struct nlattr *attr, + bool *mod) +{ + struct nla_bitfield32 change; + u32 newval; + + if (!attr) + return; + change = nla_get_bitfield32(attr); + newval = (*dst & ~change.selector) | (change.value & change.selector); + if (*dst == newval) + return; + + *dst = newval; + *mod = true; +} + +/** + * ethnl_reply_header_size() - total size of reply header + * + * This is an upper estimate so that we do not need to hold RTNL lock longer + * than necessary (to prevent rename between size estimate and composing the + * message). Accounts only for device ifindex and name as those are the only + * attributes ethnl_fill_reply_header() puts into the reply header. + */ +static inline unsigned int ethnl_reply_header_size(void) +{ + return nla_total_size(nla_total_size(sizeof(u32)) + + nla_total_size(IFNAMSIZ)); +} + +/** + * struct ethnl_req_info - base type of request information for GET requests + * @dev: network device the request is for (may be null) + * @flags: request flags common for all request types + * + * This is a common base, additional members may follow after this structure. + */ +struct ethnl_req_info { + struct net_device *dev; + u32 flags; +}; #endif /* _NET_ETHTOOL_NETLINK_H */ -- GitLab From 10b518d4e6dd5390e40f7d8de0f08753c1195a7e Mon Sep 17 00:00:00 2001 From: Michal Kubecek Date: Fri, 27 Dec 2019 15:55:28 +0100 Subject: [PATCH 1388/1437] ethtool: netlink bitset handling The ethtool netlink code uses common framework for passing arbitrary length bit sets to allow future extensions. A bitset can be a list (only one bitmap) or can consist of value and mask pair (used e.g. when client want to modify only some bits). A bitset can use one of two formats: verbose (bit by bit) or compact. Verbose format consists of bitset size (number of bits), list flag and an array of bit nests, telling which bits are part of the list or which bits are in the mask and which of them are to be set. In requests, bits can be identified by index (position) or by name. In replies, kernel provides both index and name. Verbose format is suitable for "one shot" applications like standard ethtool command as it avoids the need to either keep bit names (e.g. link modes) in sync with kernel or having to add an extra roundtrip for string set request (e.g. for private flags). Compact format uses one (list) or two (value/mask) arrays of 32-bit words to store the bitmap(s). It is more suitable for long running applications (ethtool in monitor mode or network management daemons) which can retrieve the names once and then pass only compact bitmaps to save space. Userspace requests can use either format; ETHTOOL_FLAG_COMPACT_BITSETS flag in request header tells kernel which format to use in reply. Notifications always use compact format. As some code uses arrays of unsigned long for internal representation and some arrays of u32 (or even a single u32), two sets of parse/compose helpers are introduced. To avoid code duplication, helpers for unsigned long arrays are implemented as wrappers around helpers for u32 arrays. There are two reasons for this choice: (1) u32 arrays are more frequent in ethtool code and (2) unsigned long array can be always interpreted as an u32 array on little endian 64-bit and all 32-bit architectures while we would need special handling for odd number of u32 words in the opposite direction. Signed-off-by: Michal Kubecek Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller --- Documentation/networking/ethtool-netlink.rst | 84 +++ include/uapi/linux/ethtool_netlink.h | 35 + net/ethtool/Makefile | 2 +- net/ethtool/bitset.c | 735 +++++++++++++++++++ net/ethtool/bitset.h | 28 + 5 files changed, 883 insertions(+), 1 deletion(-) create mode 100644 net/ethtool/bitset.c create mode 100644 net/ethtool/bitset.h diff --git a/Documentation/networking/ethtool-netlink.rst b/Documentation/networking/ethtool-netlink.rst index 9448442ad293a..7797f1237472f 100644 --- a/Documentation/networking/ethtool-netlink.rst +++ b/Documentation/networking/ethtool-netlink.rst @@ -76,6 +76,90 @@ of the flag should be interpreted the way the client expects. A client must not set flags it does not understand. +Bit sets +======== + +For short bitmaps of (reasonably) fixed length, standard ``NLA_BITFIELD32`` +type is used. For arbitrary length bitmaps, ethtool netlink uses a nested +attribute with contents of one of two forms: compact (two binary bitmaps +representing bit values and mask of affected bits) and bit-by-bit (list of +bits identified by either index or name). + +Verbose (bit-by-bit) bitsets allow sending symbolic names for bits together +with their values which saves a round trip (when the bitset is passed in a +request) or at least a second request (when the bitset is in a reply). This is +useful for one shot applications like traditional ethtool command. On the +other hand, long running applications like ethtool monitor (displaying +notifications) or network management daemons may prefer fetching the names +only once and using compact form to save message size. Notifications from +ethtool netlink interface always use compact form for bitsets. + +A bitset can represent either a value/mask pair (``ETHTOOL_A_BITSET_NOMASK`` +not set) or a single bitmap (``ETHTOOL_A_BITSET_NOMASK`` set). In requests +modifying a bitmap, the former changes the bit set in mask to values set in +value and preserves the rest; the latter sets the bits set in the bitmap and +clears the rest. + +Compact form: nested (bitset) atrribute contents: + + ============================ ====== ============================ + ``ETHTOOL_A_BITSET_NOMASK`` flag no mask, only a list + ``ETHTOOL_A_BITSET_SIZE`` u32 number of significant bits + ``ETHTOOL_A_BITSET_VALUE`` binary bitmap of bit values + ``ETHTOOL_A_BITSET_MASK`` binary bitmap of valid bits + ============================ ====== ============================ + +Value and mask must have length at least ``ETHTOOL_A_BITSET_SIZE`` bits +rounded up to a multiple of 32 bits. They consist of 32-bit words in host byte +order, words ordered from least significant to most significant (i.e. the same +way as bitmaps are passed with ioctl interface). + +For compact form, ``ETHTOOL_A_BITSET_SIZE`` and ``ETHTOOL_A_BITSET_VALUE`` are +mandatory. ``ETHTOOL_A_BITSET_MASK`` attribute is mandatory if +``ETHTOOL_A_BITSET_NOMASK`` is not set (bitset represents a value/mask pair); +if ``ETHTOOL_A_BITSET_NOMASK`` is not set, ``ETHTOOL_A_BITSET_MASK`` is not +allowed (bitset represents a single bitmap. + +Kernel bit set length may differ from userspace length if older application is +used on newer kernel or vice versa. If userspace bitmap is longer, an error is +issued only if the request actually tries to set values of some bits not +recognized by kernel. + +Bit-by-bit form: nested (bitset) attribute contents: + + +------------------------------------+--------+-----------------------------+ + | ``ETHTOOL_A_BITSET_NOMASK`` | flag | no mask, only a list | + +------------------------------------+--------+-----------------------------+ + | ``ETHTOOL_A_BITSET_SIZE`` | u32 | number of significant bits | + +------------------------------------+--------+-----------------------------+ + | ``ETHTOOL_A_BITSET_BITS`` | nested | array of bits | + +-+----------------------------------+--------+-----------------------------+ + | | ``ETHTOOL_A_BITSET_BITS_BIT+`` | nested | one bit | + +-+-+--------------------------------+--------+-----------------------------+ + | | | ``ETHTOOL_A_BITSET_BIT_INDEX`` | u32 | bit index (0 for LSB) | + +-+-+--------------------------------+--------+-----------------------------+ + | | | ``ETHTOOL_A_BITSET_BIT_NAME`` | string | bit name | + +-+-+--------------------------------+--------+-----------------------------+ + | | | ``ETHTOOL_A_BITSET_BIT_VALUE`` | flag | present if bit is set | + +-+-+--------------------------------+--------+-----------------------------+ + +Bit size is optional for bit-by-bit form. ``ETHTOOL_A_BITSET_BITS`` nest can +only contain ``ETHTOOL_A_BITSET_BITS_BIT`` attributes but there can be an +arbitrary number of them. A bit may be identified by its index or by its +name. When used in requests, listed bits are set to 0 or 1 according to +``ETHTOOL_A_BITSET_BIT_VALUE``, the rest is preserved. A request fails if +index exceeds kernel bit length or if name is not recognized. + +When ``ETHTOOL_A_BITSET_NOMASK`` flag is present, bitset is interpreted as +a simple bitmap. ``ETHTOOL_A_BITSET_BIT_VALUE`` attributes are not used in +such case. Such bitset represents a bitmap with listed bits set and the rest +zero. + +In requests, application can use either form. Form used by kernel in reply is +determined by ``ETHTOOL_FLAG_COMPACT_BITSETS`` flag in flags field of request +header. Semantics of value and mask depends on the attribute. + + List of message types ===================== diff --git a/include/uapi/linux/ethtool_netlink.h b/include/uapi/linux/ethtool_netlink.h index 82fc3b5f41c9c..951203049615b 100644 --- a/include/uapi/linux/ethtool_netlink.h +++ b/include/uapi/linux/ethtool_netlink.h @@ -50,6 +50,41 @@ enum { ETHTOOL_A_HEADER_MAX = __ETHTOOL_A_HEADER_CNT - 1 }; +/* bit sets */ + +enum { + ETHTOOL_A_BITSET_BIT_UNSPEC, + ETHTOOL_A_BITSET_BIT_INDEX, /* u32 */ + ETHTOOL_A_BITSET_BIT_NAME, /* string */ + ETHTOOL_A_BITSET_BIT_VALUE, /* flag */ + + /* add new constants above here */ + __ETHTOOL_A_BITSET_BIT_CNT, + ETHTOOL_A_BITSET_BIT_MAX = __ETHTOOL_A_BITSET_BIT_CNT - 1 +}; + +enum { + ETHTOOL_A_BITSET_BITS_UNSPEC, + ETHTOOL_A_BITSET_BITS_BIT, /* nest - _A_BITSET_BIT_* */ + + /* add new constants above here */ + __ETHTOOL_A_BITSET_BITS_CNT, + ETHTOOL_A_BITSET_BITS_MAX = __ETHTOOL_A_BITSET_BITS_CNT - 1 +}; + +enum { + ETHTOOL_A_BITSET_UNSPEC, + ETHTOOL_A_BITSET_NOMASK, /* flag */ + ETHTOOL_A_BITSET_SIZE, /* u32 */ + ETHTOOL_A_BITSET_BITS, /* nest - _A_BITSET_BITS_* */ + ETHTOOL_A_BITSET_VALUE, /* binary */ + ETHTOOL_A_BITSET_MASK, /* binary */ + + /* add new constants above here */ + __ETHTOOL_A_BITSET_CNT, + ETHTOOL_A_BITSET_MAX = __ETHTOOL_A_BITSET_CNT - 1 +}; + /* generic netlink info */ #define ETHTOOL_GENL_NAME "ethtool" #define ETHTOOL_GENL_VERSION 1 diff --git a/net/ethtool/Makefile b/net/ethtool/Makefile index 59d5ee230c292..a7e6c2c85db90 100644 --- a/net/ethtool/Makefile +++ b/net/ethtool/Makefile @@ -4,4 +4,4 @@ obj-y += ioctl.o common.o obj-$(CONFIG_ETHTOOL_NETLINK) += ethtool_nl.o -ethtool_nl-y := netlink.o +ethtool_nl-y := netlink.o bitset.o diff --git a/net/ethtool/bitset.c b/net/ethtool/bitset.c new file mode 100644 index 0000000000000..fce45dac42056 --- /dev/null +++ b/net/ethtool/bitset.c @@ -0,0 +1,735 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include +#include +#include "netlink.h" +#include "bitset.h" + +/* Some bitmaps are internally represented as an array of unsigned long, some + * as an array of u32 (some even as single u32 for now). To avoid the need of + * wrappers on caller side, we provide two set of functions: those with "32" + * suffix in their names expect u32 based bitmaps, those without it expect + * unsigned long bitmaps. + */ + +static u32 ethnl_lower_bits(unsigned int n) +{ + return ~(u32)0 >> (32 - n % 32); +} + +static u32 ethnl_upper_bits(unsigned int n) +{ + return ~(u32)0 << (n % 32); +} + +/** + * ethnl_bitmap32_clear() - Clear u32 based bitmap + * @dst: bitmap to clear + * @start: beginning of the interval + * @end: end of the interval + * @mod: set if bitmap was modified + * + * Clear @nbits bits of a bitmap with indices @start <= i < @end + */ +static void ethnl_bitmap32_clear(u32 *dst, unsigned int start, unsigned int end, + bool *mod) +{ + unsigned int start_word = start / 32; + unsigned int end_word = end / 32; + unsigned int i; + u32 mask; + + if (end <= start) + return; + + if (start % 32) { + mask = ethnl_upper_bits(start); + if (end_word == start_word) { + mask &= ethnl_lower_bits(end); + if (dst[start_word] & mask) { + dst[start_word] &= ~mask; + *mod = true; + } + return; + } + if (dst[start_word] & mask) { + dst[start_word] &= ~mask; + *mod = true; + } + start_word++; + } + + for (i = start_word; i < end_word; i++) { + if (dst[i]) { + dst[i] = 0; + *mod = true; + } + } + if (end % 32) { + mask = ethnl_lower_bits(end); + if (dst[end_word] & mask) { + dst[end_word] &= ~mask; + *mod = true; + } + } +} + +/** + * ethnl_bitmap32_not_zero() - Check if any bit is set in an interval + * @map: bitmap to test + * @start: beginning of the interval + * @end: end of the interval + * + * Return: true if there is non-zero bit with index @start <= i < @end, + * false if the whole interval is zero + */ +static bool ethnl_bitmap32_not_zero(const u32 *map, unsigned int start, + unsigned int end) +{ + unsigned int start_word = start / 32; + unsigned int end_word = end / 32; + u32 mask; + + if (end <= start) + return true; + + if (start % 32) { + mask = ethnl_upper_bits(start); + if (end_word == start_word) { + mask &= ethnl_lower_bits(end); + return map[start_word] & mask; + } + if (map[start_word] & mask) + return true; + start_word++; + } + + if (!memchr_inv(map + start_word, '\0', + (end_word - start_word) * sizeof(u32))) + return true; + if (end % 32 == 0) + return true; + return map[end_word] & ethnl_lower_bits(end); +} + +/** + * ethnl_bitmap32_update() - Modify u32 based bitmap according to value/mask + * pair + * @dst: bitmap to update + * @nbits: bit size of the bitmap + * @value: values to set + * @mask: mask of bits to set + * @mod: set to true if bitmap is modified, preserve if not + * + * Set bits in @dst bitmap which are set in @mask to values from @value, leave + * the rest untouched. If destination bitmap was modified, set @mod to true, + * leave as it is if not. + */ +static void ethnl_bitmap32_update(u32 *dst, unsigned int nbits, + const u32 *value, const u32 *mask, bool *mod) +{ + while (nbits > 0) { + u32 real_mask = mask ? *mask : ~(u32)0; + u32 new_value; + + if (nbits < 32) + real_mask &= ethnl_lower_bits(nbits); + new_value = (*dst & ~real_mask) | (*value & real_mask); + if (new_value != *dst) { + *dst = new_value; + *mod = true; + } + + if (nbits <= 32) + break; + dst++; + nbits -= 32; + value++; + if (mask) + mask++; + } +} + +static bool ethnl_bitmap32_test_bit(const u32 *map, unsigned int index) +{ + return map[index / 32] & (1U << (index % 32)); +} + +/** + * ethnl_bitset32_size() - Calculate size of bitset nested attribute + * @val: value bitmap (u32 based) + * @mask: mask bitmap (u32 based, optional) + * @nbits: bit length of the bitset + * @names: array of bit names (optional) + * @compact: assume compact format for output + * + * Estimate length of netlink attribute composed by a later call to + * ethnl_put_bitset32() call with the same arguments. + * + * Return: negative error code or attribute length estimate + */ +int ethnl_bitset32_size(const u32 *val, const u32 *mask, unsigned int nbits, + ethnl_string_array_t names, bool compact) +{ + unsigned int len = 0; + + /* list flag */ + if (!mask) + len += nla_total_size(sizeof(u32)); + /* size */ + len += nla_total_size(sizeof(u32)); + + if (compact) { + unsigned int nwords = DIV_ROUND_UP(nbits, 32); + + /* value, mask */ + len += (mask ? 2 : 1) * nla_total_size(nwords * sizeof(u32)); + } else { + unsigned int bits_len = 0; + unsigned int bit_len, i; + + for (i = 0; i < nbits; i++) { + const char *name = names ? names[i] : NULL; + + if (!ethnl_bitmap32_test_bit(mask ?: val, i)) + continue; + /* index */ + bit_len = nla_total_size(sizeof(u32)); + /* name */ + if (name) + bit_len += ethnl_strz_size(name); + /* value */ + if (mask && ethnl_bitmap32_test_bit(val, i)) + bit_len += nla_total_size(0); + + /* bit nest */ + bits_len += nla_total_size(bit_len); + } + /* bits nest */ + len += nla_total_size(bits_len); + } + + /* outermost nest */ + return nla_total_size(len); +} + +/** + * ethnl_put_bitset32() - Put a bitset nest into a message + * @skb: skb with the message + * @attrtype: attribute type for the bitset nest + * @val: value bitmap (u32 based) + * @mask: mask bitmap (u32 based, optional) + * @nbits: bit length of the bitset + * @names: array of bit names (optional) + * @compact: use compact format for the output + * + * Compose a nested attribute representing a bitset. If @mask is null, simple + * bitmap (bit list) is created, if @mask is provided, represent a value/mask + * pair. Bit names are only used in verbose mode and when provided by calller. + * + * Return: 0 on success, negative error value on error + */ +int ethnl_put_bitset32(struct sk_buff *skb, int attrtype, const u32 *val, + const u32 *mask, unsigned int nbits, + ethnl_string_array_t names, bool compact) +{ + struct nlattr *nest; + struct nlattr *attr; + + nest = nla_nest_start(skb, attrtype); + if (!nest) + return -EMSGSIZE; + + if (!mask && nla_put_flag(skb, ETHTOOL_A_BITSET_NOMASK)) + goto nla_put_failure; + if (nla_put_u32(skb, ETHTOOL_A_BITSET_SIZE, nbits)) + goto nla_put_failure; + if (compact) { + unsigned int nwords = DIV_ROUND_UP(nbits, 32); + unsigned int nbytes = nwords * sizeof(u32); + u32 *dst; + + attr = nla_reserve(skb, ETHTOOL_A_BITSET_VALUE, nbytes); + if (!attr) + goto nla_put_failure; + dst = nla_data(attr); + memcpy(dst, val, nbytes); + if (nbits % 32) + dst[nwords - 1] &= ethnl_lower_bits(nbits); + + if (mask) { + attr = nla_reserve(skb, ETHTOOL_A_BITSET_MASK, nbytes); + if (!attr) + goto nla_put_failure; + dst = nla_data(attr); + memcpy(dst, mask, nbytes); + if (nbits % 32) + dst[nwords - 1] &= ethnl_lower_bits(nbits); + } + } else { + struct nlattr *bits; + unsigned int i; + + bits = nla_nest_start(skb, ETHTOOL_A_BITSET_BITS); + if (!bits) + goto nla_put_failure; + for (i = 0; i < nbits; i++) { + const char *name = names ? names[i] : NULL; + + if (!ethnl_bitmap32_test_bit(mask ?: val, i)) + continue; + attr = nla_nest_start(skb, ETHTOOL_A_BITSET_BITS_BIT); + if (!attr) + goto nla_put_failure; + if (nla_put_u32(skb, ETHTOOL_A_BITSET_BIT_INDEX, i)) + goto nla_put_failure; + if (name && + ethnl_put_strz(skb, ETHTOOL_A_BITSET_BIT_NAME, name)) + goto nla_put_failure; + if (mask && ethnl_bitmap32_test_bit(val, i) && + nla_put_flag(skb, ETHTOOL_A_BITSET_BIT_VALUE)) + goto nla_put_failure; + nla_nest_end(skb, attr); + } + nla_nest_end(skb, bits); + } + + nla_nest_end(skb, nest); + return 0; + +nla_put_failure: + nla_nest_cancel(skb, nest); + return -EMSGSIZE; +} + +static const struct nla_policy bitset_policy[ETHTOOL_A_BITSET_MAX + 1] = { + [ETHTOOL_A_BITSET_UNSPEC] = { .type = NLA_REJECT }, + [ETHTOOL_A_BITSET_NOMASK] = { .type = NLA_FLAG }, + [ETHTOOL_A_BITSET_SIZE] = { .type = NLA_U32 }, + [ETHTOOL_A_BITSET_BITS] = { .type = NLA_NESTED }, + [ETHTOOL_A_BITSET_VALUE] = { .type = NLA_BINARY }, + [ETHTOOL_A_BITSET_MASK] = { .type = NLA_BINARY }, +}; + +static const struct nla_policy bit_policy[ETHTOOL_A_BITSET_BIT_MAX + 1] = { + [ETHTOOL_A_BITSET_BIT_UNSPEC] = { .type = NLA_REJECT }, + [ETHTOOL_A_BITSET_BIT_INDEX] = { .type = NLA_U32 }, + [ETHTOOL_A_BITSET_BIT_NAME] = { .type = NLA_NUL_STRING }, + [ETHTOOL_A_BITSET_BIT_VALUE] = { .type = NLA_FLAG }, +}; + +/** + * ethnl_bitset_is_compact() - check if bitset attribute represents a compact + * bitset + * @bitset: nested attribute representing a bitset + * @compact: pointer for return value + * + * Return: 0 on success, negative error code on failure + */ +int ethnl_bitset_is_compact(const struct nlattr *bitset, bool *compact) +{ + struct nlattr *tb[ETHTOOL_A_BITSET_MAX + 1]; + int ret; + + ret = nla_parse_nested(tb, ETHTOOL_A_BITSET_MAX, bitset, + bitset_policy, NULL); + if (ret < 0) + return ret; + + if (tb[ETHTOOL_A_BITSET_BITS]) { + if (tb[ETHTOOL_A_BITSET_VALUE] || tb[ETHTOOL_A_BITSET_MASK]) + return -EINVAL; + *compact = false; + return 0; + } + if (!tb[ETHTOOL_A_BITSET_SIZE] || !tb[ETHTOOL_A_BITSET_VALUE]) + return -EINVAL; + + *compact = true; + return 0; +} + +/** + * ethnl_name_to_idx() - look up string index for a name + * @names: array of ETH_GSTRING_LEN sized strings + * @n_names: number of strings in the array + * @name: name to look up + * + * Return: index of the string if found, -ENOENT if not found + */ +static int ethnl_name_to_idx(ethnl_string_array_t names, unsigned int n_names, + const char *name) +{ + unsigned int i; + + if (!names) + return -ENOENT; + + for (i = 0; i < n_names; i++) { + /* names[i] may not be null terminated */ + if (!strncmp(names[i], name, ETH_GSTRING_LEN) && + strlen(name) <= ETH_GSTRING_LEN) + return i; + } + + return -ENOENT; +} + +static int ethnl_parse_bit(unsigned int *index, bool *val, unsigned int nbits, + const struct nlattr *bit_attr, bool no_mask, + ethnl_string_array_t names, + struct netlink_ext_ack *extack) +{ + struct nlattr *tb[ETHTOOL_A_BITSET_BIT_MAX + 1]; + int ret, idx; + + ret = nla_parse_nested(tb, ETHTOOL_A_BITSET_BIT_MAX, bit_attr, + bit_policy, extack); + if (ret < 0) + return ret; + + if (tb[ETHTOOL_A_BITSET_BIT_INDEX]) { + const char *name; + + idx = nla_get_u32(tb[ETHTOOL_A_BITSET_BIT_INDEX]); + if (idx >= nbits) { + NL_SET_ERR_MSG_ATTR(extack, + tb[ETHTOOL_A_BITSET_BIT_INDEX], + "bit index too high"); + return -EOPNOTSUPP; + } + name = names ? names[idx] : NULL; + if (tb[ETHTOOL_A_BITSET_BIT_NAME] && name && + strncmp(nla_data(tb[ETHTOOL_A_BITSET_BIT_NAME]), name, + nla_len(tb[ETHTOOL_A_BITSET_BIT_NAME]))) { + NL_SET_ERR_MSG_ATTR(extack, bit_attr, + "bit index and name mismatch"); + return -EINVAL; + } + } else if (tb[ETHTOOL_A_BITSET_BIT_NAME]) { + idx = ethnl_name_to_idx(names, nbits, + nla_data(tb[ETHTOOL_A_BITSET_BIT_NAME])); + if (idx < 0) { + NL_SET_ERR_MSG_ATTR(extack, + tb[ETHTOOL_A_BITSET_BIT_NAME], + "bit name not found"); + return -EOPNOTSUPP; + } + } else { + NL_SET_ERR_MSG_ATTR(extack, bit_attr, + "neither bit index nor name specified"); + return -EINVAL; + } + + *index = idx; + *val = no_mask || tb[ETHTOOL_A_BITSET_BIT_VALUE]; + return 0; +} + +static int +ethnl_update_bitset32_verbose(u32 *bitmap, unsigned int nbits, + const struct nlattr *attr, struct nlattr **tb, + ethnl_string_array_t names, + struct netlink_ext_ack *extack, bool *mod) +{ + struct nlattr *bit_attr; + bool no_mask; + int rem; + int ret; + + if (tb[ETHTOOL_A_BITSET_VALUE]) { + NL_SET_ERR_MSG_ATTR(extack, tb[ETHTOOL_A_BITSET_VALUE], + "value only allowed in compact bitset"); + return -EINVAL; + } + if (tb[ETHTOOL_A_BITSET_MASK]) { + NL_SET_ERR_MSG_ATTR(extack, tb[ETHTOOL_A_BITSET_MASK], + "mask only allowed in compact bitset"); + return -EINVAL; + } + no_mask = tb[ETHTOOL_A_BITSET_NOMASK]; + + nla_for_each_nested(bit_attr, tb[ETHTOOL_A_BITSET_BITS], rem) { + bool old_val, new_val; + unsigned int idx; + + if (nla_type(bit_attr) != ETHTOOL_A_BITSET_BITS_BIT) { + NL_SET_ERR_MSG_ATTR(extack, bit_attr, + "only ETHTOOL_A_BITSET_BITS_BIT allowed in ETHTOOL_A_BITSET_BITS"); + return -EINVAL; + } + ret = ethnl_parse_bit(&idx, &new_val, nbits, bit_attr, no_mask, + names, extack); + if (ret < 0) + return ret; + old_val = bitmap[idx / 32] & ((u32)1 << (idx % 32)); + if (new_val != old_val) { + if (new_val) + bitmap[idx / 32] |= ((u32)1 << (idx % 32)); + else + bitmap[idx / 32] &= ~((u32)1 << (idx % 32)); + *mod = true; + } + } + + return 0; +} + +static int ethnl_compact_sanity_checks(unsigned int nbits, + const struct nlattr *nest, + struct nlattr **tb, + struct netlink_ext_ack *extack) +{ + bool no_mask = tb[ETHTOOL_A_BITSET_NOMASK]; + unsigned int attr_nbits, attr_nwords; + const struct nlattr *test_attr; + + if (no_mask && tb[ETHTOOL_A_BITSET_MASK]) { + NL_SET_ERR_MSG_ATTR(extack, tb[ETHTOOL_A_BITSET_MASK], + "mask not allowed in list bitset"); + return -EINVAL; + } + if (!tb[ETHTOOL_A_BITSET_SIZE]) { + NL_SET_ERR_MSG_ATTR(extack, nest, + "missing size in compact bitset"); + return -EINVAL; + } + if (!tb[ETHTOOL_A_BITSET_VALUE]) { + NL_SET_ERR_MSG_ATTR(extack, nest, + "missing value in compact bitset"); + return -EINVAL; + } + if (!no_mask && !tb[ETHTOOL_A_BITSET_MASK]) { + NL_SET_ERR_MSG_ATTR(extack, nest, + "missing mask in compact nonlist bitset"); + return -EINVAL; + } + + attr_nbits = nla_get_u32(tb[ETHTOOL_A_BITSET_SIZE]); + attr_nwords = DIV_ROUND_UP(attr_nbits, 32); + if (nla_len(tb[ETHTOOL_A_BITSET_VALUE]) != attr_nwords * sizeof(u32)) { + NL_SET_ERR_MSG_ATTR(extack, tb[ETHTOOL_A_BITSET_VALUE], + "bitset value length does not match size"); + return -EINVAL; + } + if (tb[ETHTOOL_A_BITSET_MASK] && + nla_len(tb[ETHTOOL_A_BITSET_MASK]) != attr_nwords * sizeof(u32)) { + NL_SET_ERR_MSG_ATTR(extack, tb[ETHTOOL_A_BITSET_MASK], + "bitset mask length does not match size"); + return -EINVAL; + } + if (attr_nbits <= nbits) + return 0; + + test_attr = no_mask ? tb[ETHTOOL_A_BITSET_VALUE] : + tb[ETHTOOL_A_BITSET_MASK]; + if (ethnl_bitmap32_not_zero(nla_data(test_attr), nbits, attr_nbits)) { + NL_SET_ERR_MSG_ATTR(extack, test_attr, + "cannot modify bits past kernel bitset size"); + return -EINVAL; + } + return 0; +} + +/** + * ethnl_update_bitset32() - Apply a bitset nest to a u32 based bitmap + * @bitmap: bitmap to update + * @nbits: size of the updated bitmap in bits + * @attr: nest attribute to parse and apply + * @names: array of bit names; may be null for compact format + * @extack: extack for error reporting + * @mod: set this to true if bitmap is modified, leave as it is if not + * + * Apply bitset netsted attribute to a bitmap. If the attribute represents + * a bit list, @bitmap is set to its contents; otherwise, bits in mask are + * set to values from value. Bitmaps in the attribute may be longer than + * @nbits but the message must not request modifying any bits past @nbits. + * + * Return: negative error code on failure, 0 on success + */ +int ethnl_update_bitset32(u32 *bitmap, unsigned int nbits, + const struct nlattr *attr, ethnl_string_array_t names, + struct netlink_ext_ack *extack, bool *mod) +{ + struct nlattr *tb[ETHTOOL_A_BITSET_MAX + 1]; + unsigned int change_bits; + bool no_mask; + int ret; + + if (!attr) + return 0; + ret = nla_parse_nested(tb, ETHTOOL_A_BITSET_MAX, attr, bitset_policy, + extack); + if (ret < 0) + return ret; + + if (tb[ETHTOOL_A_BITSET_BITS]) + return ethnl_update_bitset32_verbose(bitmap, nbits, attr, tb, + names, extack, mod); + ret = ethnl_compact_sanity_checks(nbits, attr, tb, extack); + if (ret < 0) + return ret; + + no_mask = tb[ETHTOOL_A_BITSET_NOMASK]; + change_bits = min_t(unsigned int, + nla_get_u32(tb[ETHTOOL_A_BITSET_SIZE]), nbits); + ethnl_bitmap32_update(bitmap, change_bits, + nla_data(tb[ETHTOOL_A_BITSET_VALUE]), + no_mask ? NULL : + nla_data(tb[ETHTOOL_A_BITSET_MASK]), + mod); + if (no_mask && change_bits < nbits) + ethnl_bitmap32_clear(bitmap, change_bits, nbits, mod); + + return 0; +} + +#if BITS_PER_LONG == 64 && defined(__BIG_ENDIAN) + +/* 64-bit big endian architectures are the only case when u32 based bitmaps + * and unsigned long based bitmaps have different memory layout so that we + * cannot simply cast the latter to the former and need actual wrappers + * converting the latter to the former. + * + * To reduce the number of slab allocations, the wrappers use fixed size local + * variables for bitmaps up to ETHNL_SMALL_BITMAP_BITS bits which is the + * majority of bitmaps used by ethtool. + */ +#define ETHNL_SMALL_BITMAP_BITS 128 +#define ETHNL_SMALL_BITMAP_WORDS DIV_ROUND_UP(ETHNL_SMALL_BITMAP_BITS, 32) + +int ethnl_bitset_size(const unsigned long *val, const unsigned long *mask, + unsigned int nbits, ethnl_string_array_t names, + bool compact) +{ + u32 small_mask32[ETHNL_SMALL_BITMAP_WORDS]; + u32 small_val32[ETHNL_SMALL_BITMAP_WORDS]; + u32 *mask32; + u32 *val32; + int ret; + + if (nbits > ETHNL_SMALL_BITMAP_BITS) { + unsigned int nwords = DIV_ROUND_UP(nbits, 32); + + val32 = kmalloc_array(2 * nwords, sizeof(u32), GFP_KERNEL); + if (!val32) + return -ENOMEM; + mask32 = val32 + nwords; + } else { + val32 = small_val32; + mask32 = small_mask32; + } + + bitmap_to_arr32(val32, val, nbits); + if (mask) + bitmap_to_arr32(mask32, mask, nbits); + else + mask32 = NULL; + ret = ethnl_bitset32_size(val32, mask32, nbits, names, compact); + + if (nbits > ETHNL_SMALL_BITMAP_BITS) + kfree(val32); + + return ret; +} + +int ethnl_put_bitset(struct sk_buff *skb, int attrtype, + const unsigned long *val, const unsigned long *mask, + unsigned int nbits, ethnl_string_array_t names, + bool compact) +{ + u32 small_mask32[ETHNL_SMALL_BITMAP_WORDS]; + u32 small_val32[ETHNL_SMALL_BITMAP_WORDS]; + u32 *mask32; + u32 *val32; + int ret; + + if (nbits > ETHNL_SMALL_BITMAP_BITS) { + unsigned int nwords = DIV_ROUND_UP(nbits, 32); + + val32 = kmalloc_array(2 * nwords, sizeof(u32), GFP_KERNEL); + if (!val32) + return -ENOMEM; + mask32 = val32 + nwords; + } else { + val32 = small_val32; + mask32 = small_mask32; + } + + bitmap_to_arr32(val32, val, nbits); + if (mask) + bitmap_to_arr32(mask32, mask, nbits); + else + mask32 = NULL; + ret = ethnl_put_bitset32(skb, attrtype, val32, mask32, nbits, names, + compact); + + if (nbits > ETHNL_SMALL_BITMAP_BITS) + kfree(val32); + + return ret; +} + +int ethnl_update_bitset(unsigned long *bitmap, unsigned int nbits, + const struct nlattr *attr, ethnl_string_array_t names, + struct netlink_ext_ack *extack, bool *mod) +{ + u32 small_bitmap32[ETHNL_SMALL_BITMAP_WORDS]; + u32 *bitmap32 = small_bitmap32; + bool u32_mod = false; + int ret; + + if (nbits > ETHNL_SMALL_BITMAP_BITS) { + unsigned int dst_words = DIV_ROUND_UP(nbits, 32); + + bitmap32 = kmalloc_array(dst_words, sizeof(u32), GFP_KERNEL); + if (!bitmap32) + return -ENOMEM; + } + + bitmap_to_arr32(bitmap32, bitmap, nbits); + ret = ethnl_update_bitset32(bitmap32, nbits, attr, names, extack, + &u32_mod); + if (u32_mod) { + bitmap_from_arr32(bitmap, bitmap32, nbits); + *mod = true; + } + + if (nbits > ETHNL_SMALL_BITMAP_BITS) + kfree(bitmap32); + + return ret; +} + +#else + +/* On little endian 64-bit and all 32-bit architectures, an unsigned long + * based bitmap can be interpreted as u32 based one using a simple cast. + */ + +int ethnl_bitset_size(const unsigned long *val, const unsigned long *mask, + unsigned int nbits, ethnl_string_array_t names, + bool compact) +{ + return ethnl_bitset32_size((const u32 *)val, (const u32 *)mask, nbits, + names, compact); +} + +int ethnl_put_bitset(struct sk_buff *skb, int attrtype, + const unsigned long *val, const unsigned long *mask, + unsigned int nbits, ethnl_string_array_t names, + bool compact) +{ + return ethnl_put_bitset32(skb, attrtype, (const u32 *)val, + (const u32 *)mask, nbits, names, compact); +} + +int ethnl_update_bitset(unsigned long *bitmap, unsigned int nbits, + const struct nlattr *attr, ethnl_string_array_t names, + struct netlink_ext_ack *extack, bool *mod) +{ + return ethnl_update_bitset32((u32 *)bitmap, nbits, attr, names, extack, + mod); +} + +#endif /* BITS_PER_LONG == 64 && defined(__BIG_ENDIAN) */ diff --git a/net/ethtool/bitset.h b/net/ethtool/bitset.h new file mode 100644 index 0000000000000..b8247e34109d0 --- /dev/null +++ b/net/ethtool/bitset.h @@ -0,0 +1,28 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#ifndef _NET_ETHTOOL_BITSET_H +#define _NET_ETHTOOL_BITSET_H + +typedef const char (*const ethnl_string_array_t)[ETH_GSTRING_LEN]; + +int ethnl_bitset_is_compact(const struct nlattr *bitset, bool *compact); +int ethnl_bitset_size(const unsigned long *val, const unsigned long *mask, + unsigned int nbits, ethnl_string_array_t names, + bool compact); +int ethnl_bitset32_size(const u32 *val, const u32 *mask, unsigned int nbits, + ethnl_string_array_t names, bool compact); +int ethnl_put_bitset(struct sk_buff *skb, int attrtype, + const unsigned long *val, const unsigned long *mask, + unsigned int nbits, ethnl_string_array_t names, + bool compact); +int ethnl_put_bitset32(struct sk_buff *skb, int attrtype, const u32 *val, + const u32 *mask, unsigned int nbits, + ethnl_string_array_t names, bool compact); +int ethnl_update_bitset(unsigned long *bitmap, unsigned int nbits, + const struct nlattr *attr, ethnl_string_array_t names, + struct netlink_ext_ack *extack, bool *mod); +int ethnl_update_bitset32(u32 *bitmap, unsigned int nbits, + const struct nlattr *attr, ethnl_string_array_t names, + struct netlink_ext_ack *extack, bool *mod); + +#endif /* _NET_ETHTOOL_BITSET_H */ -- GitLab From 6b08d6c146f4c5ed451c45339c10feb06d619db2 Mon Sep 17 00:00:00 2001 From: Michal Kubecek Date: Fri, 27 Dec 2019 15:55:33 +0100 Subject: [PATCH 1389/1437] ethtool: support for netlink notifications Add infrastructure for ethtool netlink notifications. There is only one multicast group "monitor" which is used to notify userspace about changes and actions performed. Notification messages (types using suffix _NTF) share the format with replies to GET requests. Notifications are supposed to be broadcasted on every configuration change, whether it is done using the netlink interface or ioctl one. Netlink SET requests only trigger a notification if some data is actually changed. To trigger an ethtool notification, both ethtool netlink and external code use ethtool_notify() helper. This helper requires RTNL to be held and may sleep. Handlers sending messages for specific notification message types are registered in ethnl_notify_handlers array. As notifications can be triggered from other code, ethnl_ok flag is used to prevent an attempt to send notification before genetlink family is registered. Signed-off-by: Michal Kubecek Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller --- include/linux/ethtool_netlink.h | 5 +++++ include/linux/netdevice.h | 9 ++++++++ include/uapi/linux/ethtool_netlink.h | 2 ++ net/ethtool/netlink.c | 32 ++++++++++++++++++++++++++++ 4 files changed, 48 insertions(+) diff --git a/include/linux/ethtool_netlink.h b/include/linux/ethtool_netlink.h index f27e92b5f344b..c98f6852c8eb5 100644 --- a/include/linux/ethtool_netlink.h +++ b/include/linux/ethtool_netlink.h @@ -5,5 +5,10 @@ #include #include +#include + +enum ethtool_multicast_groups { + ETHNL_MCGRP_MONITOR, +}; #endif /* _LINUX_ETHTOOL_NETLINK_H_ */ diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 469a297b58c0e..f007155ae8f48 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -4393,6 +4393,15 @@ struct netdev_notifier_bonding_info { void netdev_bonding_info_change(struct net_device *dev, struct netdev_bonding_info *bonding_info); +#if IS_ENABLED(CONFIG_ETHTOOL_NETLINK) +void ethtool_notify(struct net_device *dev, unsigned int cmd, const void *data); +#else +static inline void ethtool_notify(struct net_device *dev, unsigned int cmd, + const void *data) +{ +} +#endif + static inline struct sk_buff *skb_gso_segment(struct sk_buff *skb, netdev_features_t features) { diff --git a/include/uapi/linux/ethtool_netlink.h b/include/uapi/linux/ethtool_netlink.h index 951203049615b..d530ccb6f7e6d 100644 --- a/include/uapi/linux/ethtool_netlink.h +++ b/include/uapi/linux/ethtool_netlink.h @@ -89,4 +89,6 @@ enum { #define ETHTOOL_GENL_NAME "ethtool" #define ETHTOOL_GENL_VERSION 1 +#define ETHTOOL_MCGRP_MONITOR_NAME "monitor" + #endif /* _UAPI_LINUX_ETHTOOL_NETLINK_H_ */ diff --git a/net/ethtool/netlink.c b/net/ethtool/netlink.c index aef882e0c3f57..c0f25c8f35655 100644 --- a/net/ethtool/netlink.c +++ b/net/ethtool/netlink.c @@ -6,6 +6,8 @@ static struct genl_family ethtool_genl_family; +static bool ethnl_ok __read_mostly; + static const struct nla_policy ethnl_header_policy[ETHTOOL_A_HEADER_MAX + 1] = { [ETHTOOL_A_HEADER_UNSPEC] = { .type = NLA_REJECT }, [ETHTOOL_A_HEADER_DEV_INDEX] = { .type = NLA_U32 }, @@ -169,11 +171,38 @@ err: return NULL; } +/* notifications */ + +typedef void (*ethnl_notify_handler_t)(struct net_device *dev, unsigned int cmd, + const void *data); + +static const ethnl_notify_handler_t ethnl_notify_handlers[] = { +}; + +void ethtool_notify(struct net_device *dev, unsigned int cmd, const void *data) +{ + if (unlikely(!ethnl_ok)) + return; + ASSERT_RTNL(); + + if (likely(cmd < ARRAY_SIZE(ethnl_notify_handlers) && + ethnl_notify_handlers[cmd])) + ethnl_notify_handlers[cmd](dev, cmd, data); + else + WARN_ONCE(1, "notification %u not implemented (dev=%s)\n", + cmd, netdev_name(dev)); +} +EXPORT_SYMBOL(ethtool_notify); + /* genetlink setup */ static const struct genl_ops ethtool_genl_ops[] = { }; +static const struct genl_multicast_group ethtool_nl_mcgrps[] = { + [ETHNL_MCGRP_MONITOR] = { .name = ETHTOOL_MCGRP_MONITOR_NAME }, +}; + static struct genl_family ethtool_genl_family = { .name = ETHTOOL_GENL_NAME, .version = ETHTOOL_GENL_VERSION, @@ -181,6 +210,8 @@ static struct genl_family ethtool_genl_family = { .parallel_ops = true, .ops = ethtool_genl_ops, .n_ops = ARRAY_SIZE(ethtool_genl_ops), + .mcgrps = ethtool_nl_mcgrps, + .n_mcgrps = ARRAY_SIZE(ethtool_nl_mcgrps), }; /* module setup */ @@ -192,6 +223,7 @@ static int __init ethnl_init(void) ret = genl_register_family(ðtool_genl_family); if (WARN(ret < 0, "ethtool: genetlink family registration failed")) return ret; + ethnl_ok = true; return 0; } -- GitLab From 728480f1244200fe294073afeb612c583a2080d2 Mon Sep 17 00:00:00 2001 From: Michal Kubecek Date: Fri, 27 Dec 2019 15:55:38 +0100 Subject: [PATCH 1390/1437] ethtool: default handlers for GET requests Significant part of GET request processing is common for most request types but unfortunately it cannot be easily separated from type specific code as we need to alternate between common actions (parsing common request header, allocating message and filling netlink/genetlink headers etc.) and specific actions (querying the device, composing the reply). The processing also happens in three different situations: "do" request, "dump" request and notification, each doing things in slightly different way. The request specific code is implemented in four or five callbacks defined in an instance of struct get_request_ops: parse_request() - parse incoming message prepare_data() - retrieve data from driver or NIC reply_size() - estimate reply message size fill_reply() - compose reply message cleanup_data() - (optional) clean up additional data Other members of struct get_request_ops describe the data structure holding information from client request and data used to compose the message. The default handlers ethnl_default_doit(), ethnl_default_dumpit(), ethnl_default_start() and ethnl_default_done() can be then used in genl_ops handler. Notification handler will be introduced in a later patch. Signed-off-by: Michal Kubecek Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller --- net/ethtool/netlink.c | 321 ++++++++++++++++++++++++++++++++++++++++++ net/ethtool/netlink.h | 116 ++++++++++++++- 2 files changed, 436 insertions(+), 1 deletion(-) diff --git a/net/ethtool/netlink.c b/net/ethtool/netlink.c index c0f25c8f35655..ae63e8423072e 100644 --- a/net/ethtool/netlink.c +++ b/net/ethtool/netlink.c @@ -171,6 +171,327 @@ err: return NULL; } +/* GET request helpers */ + +/** + * struct ethnl_dump_ctx - context structure for generic dumpit() callback + * @ops: request ops of currently processed message type + * @req_info: parsed request header of processed request + * @pos_hash: saved iteration position - hashbucket + * @pos_idx: saved iteration position - index + * + * These parameters are kept in struct netlink_callback as context preserved + * between iterations. They are initialized by ethnl_default_start() and used + * in ethnl_default_dumpit() and ethnl_default_done(). + */ +struct ethnl_dump_ctx { + const struct ethnl_request_ops *ops; + struct ethnl_req_info *req_info; + struct ethnl_reply_data *reply_data; + int pos_hash; + int pos_idx; +}; + +static const struct ethnl_request_ops * +ethnl_default_requests[__ETHTOOL_MSG_USER_CNT] = { +}; + +static struct ethnl_dump_ctx *ethnl_dump_context(struct netlink_callback *cb) +{ + return (struct ethnl_dump_ctx *)cb->ctx; +} + +/** + * ethnl_default_parse() - Parse request message + * @req_info: pointer to structure to put data into + * @nlhdr: pointer to request message header + * @net: request netns + * @request_ops: struct request_ops for request type + * @extack: netlink extack for error reporting + * @require_dev: fail if no device identified in header + * + * Parse universal request header and call request specific ->parse_request() + * callback (if defined) to parse the rest of the message. + * + * Return: 0 on success or negative error code + */ +static int ethnl_default_parse(struct ethnl_req_info *req_info, + const struct nlmsghdr *nlhdr, struct net *net, + const struct ethnl_request_ops *request_ops, + struct netlink_ext_ack *extack, bool require_dev) +{ + struct nlattr **tb; + int ret; + + tb = kmalloc_array(request_ops->max_attr + 1, sizeof(tb[0]), + GFP_KERNEL); + if (!tb) + return -ENOMEM; + + ret = nlmsg_parse(nlhdr, GENL_HDRLEN, tb, request_ops->max_attr, + request_ops->request_policy, extack); + if (ret < 0) + goto out; + ret = ethnl_parse_header(req_info, tb[request_ops->hdr_attr], net, + extack, require_dev); + if (ret < 0) + goto out; + + if (request_ops->parse_request) { + ret = request_ops->parse_request(req_info, tb, extack); + if (ret < 0) + goto out; + } + + ret = 0; +out: + kfree(tb); + return ret; +} + +/** + * ethnl_init_reply_data() - Initialize reply data for GET request + * @req_info: pointer to embedded struct ethnl_req_info + * @ops: instance of struct ethnl_request_ops describing the layout + * @dev: network device to initialize the reply for + * + * Fills the reply data part with zeros and sets the dev member. Must be called + * before calling the ->fill_reply() callback (for each iteration when handling + * dump requests). + */ +static void ethnl_init_reply_data(struct ethnl_reply_data *reply_data, + const struct ethnl_request_ops *ops, + struct net_device *dev) +{ + memset(reply_data, 0, ops->reply_data_size); + reply_data->dev = dev; +} + +/* default ->doit() handler for GET type requests */ +static int ethnl_default_doit(struct sk_buff *skb, struct genl_info *info) +{ + struct ethnl_reply_data *reply_data = NULL; + struct ethnl_req_info *req_info = NULL; + const u8 cmd = info->genlhdr->cmd; + const struct ethnl_request_ops *ops; + struct sk_buff *rskb; + void *reply_payload; + int reply_len; + int ret; + + ops = ethnl_default_requests[cmd]; + if (WARN_ONCE(!ops, "cmd %u has no ethnl_request_ops\n", cmd)) + return -EOPNOTSUPP; + req_info = kzalloc(ops->req_info_size, GFP_KERNEL); + if (!req_info) + return -ENOMEM; + reply_data = kmalloc(ops->reply_data_size, GFP_KERNEL); + if (!reply_data) { + kfree(req_info); + return -ENOMEM; + } + + ret = ethnl_default_parse(req_info, info->nlhdr, genl_info_net(info), ops, + info->extack, !ops->allow_nodev_do); + if (ret < 0) + goto err_dev; + ethnl_init_reply_data(reply_data, ops, req_info->dev); + + rtnl_lock(); + ret = ops->prepare_data(req_info, reply_data, info); + rtnl_unlock(); + if (ret < 0) + goto err_cleanup; + reply_len = ops->reply_size(req_info, reply_data); + if (ret < 0) + goto err_cleanup; + ret = -ENOMEM; + rskb = ethnl_reply_init(reply_len, req_info->dev, ops->reply_cmd, + ops->hdr_attr, info, &reply_payload); + if (!rskb) + goto err_cleanup; + ret = ops->fill_reply(rskb, req_info, reply_data); + if (ret < 0) + goto err_msg; + if (ops->cleanup_data) + ops->cleanup_data(reply_data); + + genlmsg_end(rskb, reply_payload); + if (req_info->dev) + dev_put(req_info->dev); + kfree(reply_data); + kfree(req_info); + return genlmsg_reply(rskb, info); + +err_msg: + WARN_ONCE(ret == -EMSGSIZE, "calculated message payload length (%d) not sufficient\n", reply_len); + nlmsg_free(rskb); +err_cleanup: + if (ops->cleanup_data) + ops->cleanup_data(reply_data); +err_dev: + if (req_info->dev) + dev_put(req_info->dev); + kfree(reply_data); + kfree(req_info); + return ret; +} + +static int ethnl_default_dump_one(struct sk_buff *skb, struct net_device *dev, + const struct ethnl_dump_ctx *ctx) +{ + int ret; + + ethnl_init_reply_data(ctx->reply_data, ctx->ops, dev); + rtnl_lock(); + ret = ctx->ops->prepare_data(ctx->req_info, ctx->reply_data, NULL); + rtnl_unlock(); + if (ret < 0) + goto out; + ret = ethnl_fill_reply_header(skb, dev, ctx->ops->hdr_attr); + if (ret < 0) + goto out; + ret = ctx->ops->fill_reply(skb, ctx->req_info, ctx->reply_data); + +out: + if (ctx->ops->cleanup_data) + ctx->ops->cleanup_data(ctx->reply_data); + ctx->reply_data->dev = NULL; + return ret; +} + +/* Default ->dumpit() handler for GET requests. Device iteration copied from + * rtnl_dump_ifinfo(); we have to be more careful about device hashtable + * persistence as we cannot guarantee to hold RTNL lock through the whole + * function as rtnetnlink does. + */ +static int ethnl_default_dumpit(struct sk_buff *skb, + struct netlink_callback *cb) +{ + struct ethnl_dump_ctx *ctx = ethnl_dump_context(cb); + struct net *net = sock_net(skb->sk); + int s_idx = ctx->pos_idx; + int h, idx = 0; + int ret = 0; + void *ehdr; + + rtnl_lock(); + for (h = ctx->pos_hash; h < NETDEV_HASHENTRIES; h++, s_idx = 0) { + struct hlist_head *head; + struct net_device *dev; + unsigned int seq; + + head = &net->dev_index_head[h]; + +restart_chain: + seq = net->dev_base_seq; + cb->seq = seq; + idx = 0; + hlist_for_each_entry(dev, head, index_hlist) { + if (idx < s_idx) + goto cont; + dev_hold(dev); + rtnl_unlock(); + + ehdr = genlmsg_put(skb, NETLINK_CB(cb->skb).portid, + cb->nlh->nlmsg_seq, + ðtool_genl_family, 0, + ctx->ops->reply_cmd); + if (!ehdr) { + dev_put(dev); + ret = -EMSGSIZE; + goto out; + } + ret = ethnl_default_dump_one(skb, dev, ctx); + dev_put(dev); + if (ret < 0) { + genlmsg_cancel(skb, ehdr); + if (ret == -EOPNOTSUPP) + goto lock_and_cont; + if (likely(skb->len)) + ret = skb->len; + goto out; + } + genlmsg_end(skb, ehdr); +lock_and_cont: + rtnl_lock(); + if (net->dev_base_seq != seq) { + s_idx = idx + 1; + goto restart_chain; + } +cont: + idx++; + } + + } + rtnl_unlock(); + +out: + ctx->pos_hash = h; + ctx->pos_idx = idx; + nl_dump_check_consistent(cb, nlmsg_hdr(skb)); + + return ret; +} + +/* generic ->start() handler for GET requests */ +static int ethnl_default_start(struct netlink_callback *cb) +{ + struct ethnl_dump_ctx *ctx = ethnl_dump_context(cb); + struct ethnl_reply_data *reply_data; + const struct ethnl_request_ops *ops; + struct ethnl_req_info *req_info; + struct genlmsghdr *ghdr; + int ret; + + BUILD_BUG_ON(sizeof(*ctx) > sizeof(cb->ctx)); + + ghdr = nlmsg_data(cb->nlh); + ops = ethnl_default_requests[ghdr->cmd]; + if (WARN_ONCE(!ops, "cmd %u has no ethnl_request_ops\n", ghdr->cmd)) + return -EOPNOTSUPP; + req_info = kzalloc(ops->req_info_size, GFP_KERNEL); + if (!req_info) + return -ENOMEM; + reply_data = kmalloc(ops->reply_data_size, GFP_KERNEL); + if (!reply_data) { + kfree(req_info); + return -ENOMEM; + } + + ret = ethnl_default_parse(req_info, cb->nlh, sock_net(cb->skb->sk), ops, + cb->extack, false); + if (req_info->dev) { + /* We ignore device specification in dump requests but as the + * same parser as for non-dump (doit) requests is used, it + * would take reference to the device if it finds one + */ + dev_put(req_info->dev); + req_info->dev = NULL; + } + if (ret < 0) + return ret; + + ctx->ops = ops; + ctx->req_info = req_info; + ctx->reply_data = reply_data; + ctx->pos_hash = 0; + ctx->pos_idx = 0; + + return 0; +} + +/* default ->done() handler for GET requests */ +static int ethnl_default_done(struct netlink_callback *cb) +{ + struct ethnl_dump_ctx *ctx = ethnl_dump_context(cb); + + kfree(ctx->reply_data); + kfree(ctx->req_info); + + return 0; +} + /* notifications */ typedef void (*ethnl_notify_handler_t)(struct net_device *dev, unsigned int cmd, diff --git a/net/ethtool/netlink.h b/net/ethtool/netlink.h index 05d7183da8946..6ec0dd06277f3 100644 --- a/net/ethtool/netlink.h +++ b/net/ethtool/netlink.h @@ -200,16 +200,130 @@ static inline unsigned int ethnl_reply_header_size(void) nla_total_size(IFNAMSIZ)); } +/* GET request handling */ + +/* Unified processing of GET requests uses two data structures: request info + * and reply data. Request info holds information parsed from client request + * and its stays constant through all request processing. Reply data holds data + * retrieved from ethtool_ops callbacks or other internal sources which is used + * to compose the reply. When processing a dump request, request info is filled + * only once (when the request message is parsed) but reply data is filled for + * each reply message. + * + * Both structures consist of part common for all request types (struct + * ethnl_req_info and struct ethnl_reply_data defined below) and optional + * parts specific for each request type. Common part always starts at offset 0. + */ + /** * struct ethnl_req_info - base type of request information for GET requests * @dev: network device the request is for (may be null) * @flags: request flags common for all request types * - * This is a common base, additional members may follow after this structure. + * This is a common base for request specific structures holding data from + * parsed userspace request. These always embed struct ethnl_req_info at + * zero offset. */ struct ethnl_req_info { struct net_device *dev; u32 flags; }; +/** + * struct ethnl_reply_data - base type of reply data for GET requests + * @dev: device for current reply message; in single shot requests it is + * equal to ðnl_req_info.dev; in dumps it's different for each + * reply message + * + * This is a common base for request specific structures holding data for + * kernel reply message. These always embed struct ethnl_reply_data at zero + * offset. + */ +struct ethnl_reply_data { + struct net_device *dev; +}; + +static inline int ethnl_ops_begin(struct net_device *dev) +{ + if (dev && dev->ethtool_ops->begin) + return dev->ethtool_ops->begin(dev); + else + return 0; +} + +static inline void ethnl_ops_complete(struct net_device *dev) +{ + if (dev && dev->ethtool_ops->complete) + dev->ethtool_ops->complete(dev); +} + +/** + * struct ethnl_request_ops - unified handling of GET requests + * @request_cmd: command id for request (GET) + * @reply_cmd: command id for reply (GET_REPLY) + * @hdr_attr: attribute type for request header + * @max_attr: maximum (top level) attribute type + * @req_info_size: size of request info + * @reply_data_size: size of reply data + * @request_policy: netlink policy for message contents + * @allow_nodev_do: allow non-dump request with no device identification + * @parse_request: + * Parse request except common header (struct ethnl_req_info). Common + * header is already filled on entry, the rest up to @repdata_offset + * is zero initialized. This callback should only modify type specific + * request info by parsed attributes from request message. + * @prepare_data: + * Retrieve and prepare data needed to compose a reply message. Calls to + * ethtool_ops handlers are limited to this callback. Common reply data + * (struct ethnl_reply_data) is filled on entry, type specific part after + * it is zero initialized. This callback should only modify the type + * specific part of reply data. Device identification from struct + * ethnl_reply_data is to be used as for dump requests, it iterates + * through network devices while dev member of struct ethnl_req_info + * points to the device from client request. + * @reply_size: + * Estimate reply message size. Returned value must be sufficient for + * message payload without common reply header. The callback may returned + * estimate higher than actual message size if exact calculation would + * not be worth the saved memory space. + * @fill_reply: + * Fill reply message payload (except for common header) from reply data. + * The callback must not generate more payload than previously called + * ->reply_size() estimated. + * @cleanup_data: + * Optional cleanup called when reply data is no longer needed. Can be + * used e.g. to free any additional data structures outside the main + * structure which were allocated by ->prepare_data(). When processing + * dump requests, ->cleanup() is called for each message. + * + * Description of variable parts of GET request handling when using the + * unified infrastructure. When used, a pointer to an instance of this + * structure is to be added to ðnl_default_requests array and generic + * handlers ethnl_default_doit(), ethnl_default_dumpit(), + * ethnl_default_start() and ethnl_default_done() used in @ethtool_genl_ops. + */ +struct ethnl_request_ops { + u8 request_cmd; + u8 reply_cmd; + u16 hdr_attr; + unsigned int max_attr; + unsigned int req_info_size; + unsigned int reply_data_size; + const struct nla_policy *request_policy; + bool allow_nodev_do; + + int (*parse_request)(struct ethnl_req_info *req_info, + struct nlattr **tb, + struct netlink_ext_ack *extack); + int (*prepare_data)(const struct ethnl_req_info *req_info, + struct ethnl_reply_data *reply_data, + struct genl_info *info); + int (*reply_size)(const struct ethnl_req_info *req_info, + const struct ethnl_reply_data *reply_data); + int (*fill_reply)(struct sk_buff *skb, + const struct ethnl_req_info *req_info, + const struct ethnl_reply_data *reply_data); + void (*cleanup_data)(struct ethnl_reply_data *reply_data); +}; + #endif /* _NET_ETHTOOL_NETLINK_H */ -- GitLab From 71921690f9745fef60a2bad425f30adf8cdc9da0 Mon Sep 17 00:00:00 2001 From: Michal Kubecek Date: Fri, 27 Dec 2019 15:55:43 +0100 Subject: [PATCH 1391/1437] ethtool: provide string sets with STRSET_GET request Requests a contents of one or more string sets, i.e. indexed arrays of strings; this information is provided by ETHTOOL_GSSET_INFO and ETHTOOL_GSTRINGS commands of ioctl interface. Unlike ioctl interface, all information can be retrieved with one request and mulitple string sets can be requested at once. There are three types of requests: - no NLM_F_DUMP, no device: get "global" stringsets - no NLM_F_DUMP, with device: get string sets related to the device - NLM_F_DUMP, no device: get device related string sets for all devices Client can request either all string sets of given type (global or device related) or only specific sets. With ETHTOOL_A_STRSET_COUNTS flag set, only set sizes (numbers of strings) are returned. Signed-off-by: Michal Kubecek Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller --- Documentation/networking/ethtool-netlink.rst | 75 +++- include/uapi/linux/ethtool.h | 3 + include/uapi/linux/ethtool_netlink.h | 56 +++ net/ethtool/Makefile | 2 +- net/ethtool/netlink.c | 8 + net/ethtool/netlink.h | 4 + net/ethtool/strset.c | 425 +++++++++++++++++++ 7 files changed, 570 insertions(+), 3 deletions(-) create mode 100644 net/ethtool/strset.c diff --git a/Documentation/networking/ethtool-netlink.rst b/Documentation/networking/ethtool-netlink.rst index 7797f1237472f..3912cb0eb9c64 100644 --- a/Documentation/networking/ethtool-netlink.rst +++ b/Documentation/networking/ethtool-netlink.rst @@ -176,6 +176,18 @@ according to message purpose: ``_NTF`` kernel notification ============== ====================================== +Userspace to kernel: + + ===================================== ================================ + ``ETHTOOL_MSG_STRSET_GET`` get string set + ===================================== ================================ + +Kernel to userspace: + + ===================================== ================================ + ``ETHTOOL_MSG_STRSET_GET_REPLY`` string set contents + ===================================== ================================ + ``GET`` requests are sent by userspace applications to retrieve device information. They usually do not contain any message specific attributes. Kernel replies with corresponding "GET_REPLY" message. For most types, ``GET`` @@ -207,6 +219,65 @@ an ``ACT_REPLY`` message. Performing an action also triggers a notification Later sections describe the format and semantics of these messages. +STRSET_GET +========== + +Requests contents of a string set as provided by ioctl commands +``ETHTOOL_GSSET_INFO`` and ``ETHTOOL_GSTRINGS.`` String sets are not user +writeable so that the corresponding ``STRSET_SET`` message is only used in +kernel replies. There are two types of string sets: global (independent of +a device, e.g. device feature names) and device specific (e.g. device private +flags). + +Request contents: + + +---------------------------------------+--------+------------------------+ + | ``ETHTOOL_A_STRSET_HEADER`` | nested | request header | + +---------------------------------------+--------+------------------------+ + | ``ETHTOOL_A_STRSET_STRINGSETS`` | nested | string set to request | + +-+-------------------------------------+--------+------------------------+ + | | ``ETHTOOL_A_STRINGSETS_STRINGSET+`` | nested | one string set | + +-+-+-----------------------------------+--------+------------------------+ + | | | ``ETHTOOL_A_STRINGSET_ID`` | u32 | set id | + +-+-+-----------------------------------+--------+------------------------+ + +Kernel response contents: + + +---------------------------------------+--------+-----------------------+ + | ``ETHTOOL_A_STRSET_HEADER`` | nested | reply header | + +---------------------------------------+--------+-----------------------+ + | ``ETHTOOL_A_STRSET_STRINGSETS`` | nested | array of string sets | + +-+-------------------------------------+--------+-----------------------+ + | | ``ETHTOOL_A_STRINGSETS_STRINGSET+`` | nested | one string set | + +-+-+-----------------------------------+--------+-----------------------+ + | | | ``ETHTOOL_A_STRINGSET_ID`` | u32 | set id | + +-+-+-----------------------------------+--------+-----------------------+ + | | | ``ETHTOOL_A_STRINGSET_COUNT`` | u32 | number of strings | + +-+-+-----------------------------------+--------+-----------------------+ + | | | ``ETHTOOL_A_STRINGSET_STRINGS`` | nested | array of strings | + +-+-+-+---------------------------------+--------+-----------------------+ + | | | | ``ETHTOOL_A_STRINGS_STRING+`` | nested | one string | + +-+-+-+-+-------------------------------+--------+-----------------------+ + | | | | | ``ETHTOOL_A_STRING_INDEX`` | u32 | string index | + +-+-+-+-+-------------------------------+--------+-----------------------+ + | | | | | ``ETHTOOL_A_STRING_VALUE`` | string | string value | + +-+-+-+-+-------------------------------+--------+-----------------------+ + | ``ETHTOOL_A_STRSET_COUNTS_ONLY`` | flag | return only counts | + +---------------------------------------+--------+-----------------------+ + +Device identification in request header is optional. Depending on its presence +a and ``NLM_F_DUMP`` flag, there are three type of ``STRSET_GET`` requests: + + - no ``NLM_F_DUMP,`` no device: get "global" stringsets + - no ``NLM_F_DUMP``, with device: get string sets related to the device + - ``NLM_F_DUMP``, no device: get device related string sets for all devices + +If there is no ``ETHTOOL_A_STRSET_STRINGSETS`` array, all string sets of +requested type are returned, otherwise only those specified in the request. +Flag ``ETHTOOL_A_STRSET_COUNTS_ONLY`` tells kernel to only return string +counts of the sets, not the actual strings. + + Request translation =================== @@ -242,7 +313,7 @@ have their netlink replacement yet. ``ETHTOOL_GSG`` n/a ``ETHTOOL_SSG`` n/a ``ETHTOOL_TEST`` n/a - ``ETHTOOL_GSTRINGS`` n/a + ``ETHTOOL_GSTRINGS`` ``ETHTOOL_MSG_STRSET_GET`` ``ETHTOOL_PHYS_ID`` n/a ``ETHTOOL_GSTATS`` n/a ``ETHTOOL_GTSO`` n/a @@ -270,7 +341,7 @@ have their netlink replacement yet. ``ETHTOOL_RESET`` n/a ``ETHTOOL_SRXNTUPLE`` n/a ``ETHTOOL_GRXNTUPLE`` n/a - ``ETHTOOL_GSSET_INFO`` n/a + ``ETHTOOL_GSSET_INFO`` ``ETHTOOL_MSG_STRSET_GET`` ``ETHTOOL_GRXFHINDIR`` n/a ``ETHTOOL_SRXFHINDIR`` n/a ``ETHTOOL_GFEATURES`` n/a diff --git a/include/uapi/linux/ethtool.h b/include/uapi/linux/ethtool.h index f44155840b07d..116bcbf09c746 100644 --- a/include/uapi/linux/ethtool.h +++ b/include/uapi/linux/ethtool.h @@ -606,6 +606,9 @@ enum ethtool_stringset { ETH_SS_PHY_STATS, ETH_SS_PHY_TUNABLES, ETH_SS_LINK_MODES, + + /* add new constants above here */ + ETH_SS_COUNT }; /** diff --git a/include/uapi/linux/ethtool_netlink.h b/include/uapi/linux/ethtool_netlink.h index d530ccb6f7e6d..cabef1fec42ae 100644 --- a/include/uapi/linux/ethtool_netlink.h +++ b/include/uapi/linux/ethtool_netlink.h @@ -14,6 +14,7 @@ /* message types - userspace to kernel */ enum { ETHTOOL_MSG_USER_NONE, + ETHTOOL_MSG_STRSET_GET, /* add new constants above here */ __ETHTOOL_MSG_USER_CNT, @@ -23,6 +24,7 @@ enum { /* message types - kernel to userspace */ enum { ETHTOOL_MSG_KERNEL_NONE, + ETHTOOL_MSG_STRSET_GET_REPLY, /* add new constants above here */ __ETHTOOL_MSG_KERNEL_CNT, @@ -85,6 +87,60 @@ enum { ETHTOOL_A_BITSET_MAX = __ETHTOOL_A_BITSET_CNT - 1 }; +/* string sets */ + +enum { + ETHTOOL_A_STRING_UNSPEC, + ETHTOOL_A_STRING_INDEX, /* u32 */ + ETHTOOL_A_STRING_VALUE, /* string */ + + /* add new constants above here */ + __ETHTOOL_A_STRING_CNT, + ETHTOOL_A_STRING_MAX = __ETHTOOL_A_STRING_CNT - 1 +}; + +enum { + ETHTOOL_A_STRINGS_UNSPEC, + ETHTOOL_A_STRINGS_STRING, /* nest - _A_STRINGS_* */ + + /* add new constants above here */ + __ETHTOOL_A_STRINGS_CNT, + ETHTOOL_A_STRINGS_MAX = __ETHTOOL_A_STRINGS_CNT - 1 +}; + +enum { + ETHTOOL_A_STRINGSET_UNSPEC, + ETHTOOL_A_STRINGSET_ID, /* u32 */ + ETHTOOL_A_STRINGSET_COUNT, /* u32 */ + ETHTOOL_A_STRINGSET_STRINGS, /* nest - _A_STRINGS_* */ + + /* add new constants above here */ + __ETHTOOL_A_STRINGSET_CNT, + ETHTOOL_A_STRINGSET_MAX = __ETHTOOL_A_STRINGSET_CNT - 1 +}; + +enum { + ETHTOOL_A_STRINGSETS_UNSPEC, + ETHTOOL_A_STRINGSETS_STRINGSET, /* nest - _A_STRINGSET_* */ + + /* add new constants above here */ + __ETHTOOL_A_STRINGSETS_CNT, + ETHTOOL_A_STRINGSETS_MAX = __ETHTOOL_A_STRINGSETS_CNT - 1 +}; + +/* STRSET */ + +enum { + ETHTOOL_A_STRSET_UNSPEC, + ETHTOOL_A_STRSET_HEADER, /* nest - _A_HEADER_* */ + ETHTOOL_A_STRSET_STRINGSETS, /* nest - _A_STRINGSETS_* */ + ETHTOOL_A_STRSET_COUNTS_ONLY, /* flag */ + + /* add new constants above here */ + __ETHTOOL_A_STRSET_CNT, + ETHTOOL_A_STRSET_MAX = __ETHTOOL_A_STRSET_CNT - 1 +}; + /* generic netlink info */ #define ETHTOOL_GENL_NAME "ethtool" #define ETHTOOL_GENL_VERSION 1 diff --git a/net/ethtool/Makefile b/net/ethtool/Makefile index a7e6c2c85db90..efcc42c34d620 100644 --- a/net/ethtool/Makefile +++ b/net/ethtool/Makefile @@ -4,4 +4,4 @@ obj-y += ioctl.o common.o obj-$(CONFIG_ETHTOOL_NETLINK) += ethtool_nl.o -ethtool_nl-y := netlink.o bitset.o +ethtool_nl-y := netlink.o bitset.o strset.o diff --git a/net/ethtool/netlink.c b/net/ethtool/netlink.c index ae63e8423072e..7dc082bde6709 100644 --- a/net/ethtool/netlink.c +++ b/net/ethtool/netlink.c @@ -194,6 +194,7 @@ struct ethnl_dump_ctx { static const struct ethnl_request_ops * ethnl_default_requests[__ETHTOOL_MSG_USER_CNT] = { + [ETHTOOL_MSG_STRSET_GET] = ðnl_strset_request_ops, }; static struct ethnl_dump_ctx *ethnl_dump_context(struct netlink_callback *cb) @@ -518,6 +519,13 @@ EXPORT_SYMBOL(ethtool_notify); /* genetlink setup */ static const struct genl_ops ethtool_genl_ops[] = { + { + .cmd = ETHTOOL_MSG_STRSET_GET, + .doit = ethnl_default_doit, + .start = ethnl_default_start, + .dumpit = ethnl_default_dumpit, + .done = ethnl_default_done, + }, }; static const struct genl_multicast_group ethtool_nl_mcgrps[] = { diff --git a/net/ethtool/netlink.h b/net/ethtool/netlink.h index 6ec0dd06277f3..44e9f63aefb7c 100644 --- a/net/ethtool/netlink.h +++ b/net/ethtool/netlink.h @@ -326,4 +326,8 @@ struct ethnl_request_ops { void (*cleanup_data)(struct ethnl_reply_data *reply_data); }; +/* request handlers */ + +extern const struct ethnl_request_ops ethnl_strset_request_ops; + #endif /* _NET_ETHTOOL_NETLINK_H */ diff --git a/net/ethtool/strset.c b/net/ethtool/strset.c new file mode 100644 index 0000000000000..9f2243329015c --- /dev/null +++ b/net/ethtool/strset.c @@ -0,0 +1,425 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include +#include +#include "netlink.h" +#include "common.h" + +struct strset_info { + bool per_dev; + bool free_strings; + unsigned int count; + const char (*strings)[ETH_GSTRING_LEN]; +}; + +static const struct strset_info info_template[] = { + [ETH_SS_TEST] = { + .per_dev = true, + }, + [ETH_SS_STATS] = { + .per_dev = true, + }, + [ETH_SS_PRIV_FLAGS] = { + .per_dev = true, + }, + [ETH_SS_FEATURES] = { + .per_dev = false, + .count = ARRAY_SIZE(netdev_features_strings), + .strings = netdev_features_strings, + }, + [ETH_SS_RSS_HASH_FUNCS] = { + .per_dev = false, + .count = ARRAY_SIZE(rss_hash_func_strings), + .strings = rss_hash_func_strings, + }, + [ETH_SS_TUNABLES] = { + .per_dev = false, + .count = ARRAY_SIZE(tunable_strings), + .strings = tunable_strings, + }, + [ETH_SS_PHY_STATS] = { + .per_dev = true, + }, + [ETH_SS_PHY_TUNABLES] = { + .per_dev = false, + .count = ARRAY_SIZE(phy_tunable_strings), + .strings = phy_tunable_strings, + }, + [ETH_SS_LINK_MODES] = { + .per_dev = false, + .count = __ETHTOOL_LINK_MODE_MASK_NBITS, + .strings = link_mode_names, + }, +}; + +struct strset_req_info { + struct ethnl_req_info base; + u32 req_ids; + bool counts_only; +}; + +#define STRSET_REQINFO(__req_base) \ + container_of(__req_base, struct strset_req_info, base) + +struct strset_reply_data { + struct ethnl_reply_data base; + struct strset_info sets[ETH_SS_COUNT]; +}; + +#define STRSET_REPDATA(__reply_base) \ + container_of(__reply_base, struct strset_reply_data, base) + +static const struct nla_policy strset_get_policy[ETHTOOL_A_STRSET_MAX + 1] = { + [ETHTOOL_A_STRSET_UNSPEC] = { .type = NLA_REJECT }, + [ETHTOOL_A_STRSET_HEADER] = { .type = NLA_NESTED }, + [ETHTOOL_A_STRSET_STRINGSETS] = { .type = NLA_NESTED }, +}; + +static const struct nla_policy +get_stringset_policy[ETHTOOL_A_STRINGSET_MAX + 1] = { + [ETHTOOL_A_STRINGSET_UNSPEC] = { .type = NLA_REJECT }, + [ETHTOOL_A_STRINGSET_ID] = { .type = NLA_U32 }, + [ETHTOOL_A_STRINGSET_COUNT] = { .type = NLA_REJECT }, + [ETHTOOL_A_STRINGSET_STRINGS] = { .type = NLA_REJECT }, +}; + +/** + * strset_include() - test if a string set should be included in reply + * @data: pointer to request data structure + * @id: id of string set to check (ETH_SS_* constants) + */ +static bool strset_include(const struct strset_req_info *info, + const struct strset_reply_data *data, u32 id) +{ + bool per_dev; + + BUILD_BUG_ON(ETH_SS_COUNT >= BITS_PER_BYTE * sizeof(info->req_ids)); + + if (info->req_ids) + return info->req_ids & (1U << id); + per_dev = data->sets[id].per_dev; + if (!per_dev && !data->sets[id].strings) + return false; + + return data->base.dev ? per_dev : !per_dev; +} + +static int strset_get_id(const struct nlattr *nest, u32 *val, + struct netlink_ext_ack *extack) +{ + struct nlattr *tb[ETHTOOL_A_STRINGSET_MAX + 1]; + int ret; + + ret = nla_parse_nested(tb, ETHTOOL_A_STRINGSET_MAX, nest, + get_stringset_policy, extack); + if (ret < 0) + return ret; + if (!tb[ETHTOOL_A_STRINGSET_ID]) + return -EINVAL; + + *val = nla_get_u32(tb[ETHTOOL_A_STRINGSET_ID]); + return 0; +} + +static const struct nla_policy +strset_stringsets_policy[ETHTOOL_A_STRINGSETS_MAX + 1] = { + [ETHTOOL_A_STRINGSETS_UNSPEC] = { .type = NLA_REJECT }, + [ETHTOOL_A_STRINGSETS_STRINGSET] = { .type = NLA_NESTED }, +}; + +static int strset_parse_request(struct ethnl_req_info *req_base, + struct nlattr **tb, + struct netlink_ext_ack *extack) +{ + struct strset_req_info *req_info = STRSET_REQINFO(req_base); + struct nlattr *nest = tb[ETHTOOL_A_STRSET_STRINGSETS]; + struct nlattr *attr; + int rem, ret; + + if (!nest) + return 0; + ret = nla_validate_nested(nest, ETHTOOL_A_STRINGSETS_MAX, + strset_stringsets_policy, extack); + if (ret < 0) + return ret; + + req_info->counts_only = tb[ETHTOOL_A_STRSET_COUNTS_ONLY]; + nla_for_each_nested(attr, nest, rem) { + u32 id; + + if (WARN_ONCE(nla_type(attr) != ETHTOOL_A_STRINGSETS_STRINGSET, + "unexpected attrtype %u in ETHTOOL_A_STRSET_STRINGSETS\n", + nla_type(attr))) + return -EINVAL; + + ret = strset_get_id(attr, &id, extack); + if (ret < 0) + return ret; + if (ret >= ETH_SS_COUNT) { + NL_SET_ERR_MSG_ATTR(extack, attr, + "unknown string set id"); + return -EOPNOTSUPP; + } + + req_info->req_ids |= (1U << id); + } + + return 0; +} + +static void strset_cleanup_data(struct ethnl_reply_data *reply_base) +{ + struct strset_reply_data *data = STRSET_REPDATA(reply_base); + unsigned int i; + + for (i = 0; i < ETH_SS_COUNT; i++) + if (data->sets[i].free_strings) { + kfree(data->sets[i].strings); + data->sets[i].strings = NULL; + data->sets[i].free_strings = false; + } +} + +static int strset_prepare_set(struct strset_info *info, struct net_device *dev, + unsigned int id, bool counts_only) +{ + const struct ethtool_ops *ops = dev->ethtool_ops; + void *strings; + int count, ret; + + if (id == ETH_SS_PHY_STATS && dev->phydev && + !ops->get_ethtool_phy_stats) + ret = phy_ethtool_get_sset_count(dev->phydev); + else if (ops->get_sset_count && ops->get_strings) + ret = ops->get_sset_count(dev, id); + else + ret = -EOPNOTSUPP; + if (ret <= 0) { + info->count = 0; + return 0; + } + + count = ret; + if (!counts_only) { + strings = kcalloc(count, ETH_GSTRING_LEN, GFP_KERNEL); + if (!strings) + return -ENOMEM; + if (id == ETH_SS_PHY_STATS && dev->phydev && + !ops->get_ethtool_phy_stats) + phy_ethtool_get_strings(dev->phydev, strings); + else + ops->get_strings(dev, id, strings); + info->strings = strings; + info->free_strings = true; + } + info->count = count; + + return 0; +} + +static int strset_prepare_data(const struct ethnl_req_info *req_base, + struct ethnl_reply_data *reply_base, + struct genl_info *info) +{ + const struct strset_req_info *req_info = STRSET_REQINFO(req_base); + struct strset_reply_data *data = STRSET_REPDATA(reply_base); + struct net_device *dev = reply_base->dev; + unsigned int i; + int ret; + + BUILD_BUG_ON(ARRAY_SIZE(info_template) != ETH_SS_COUNT); + memcpy(&data->sets, &info_template, sizeof(data->sets)); + + if (!dev) { + for (i = 0; i < ETH_SS_COUNT; i++) { + if ((req_info->req_ids & (1U << i)) && + data->sets[i].per_dev) { + if (info) + GENL_SET_ERR_MSG(info, "requested per device strings without dev"); + return -EINVAL; + } + } + } + + ret = ethnl_ops_begin(dev); + if (ret < 0) + goto err_strset; + for (i = 0; i < ETH_SS_COUNT; i++) { + if (!strset_include(req_info, data, i) || + !data->sets[i].per_dev) + continue; + + ret = strset_prepare_set(&data->sets[i], dev, i, + req_info->counts_only); + if (ret < 0) + goto err_ops; + } + ethnl_ops_complete(dev); + + return 0; +err_ops: + ethnl_ops_complete(dev); +err_strset: + strset_cleanup_data(reply_base); + return ret; +} + +/* calculate size of ETHTOOL_A_STRSET_STRINGSET nest for one string set */ +static int strset_set_size(const struct strset_info *info, bool counts_only) +{ + unsigned int len = 0; + unsigned int i; + + if (info->count == 0) + return 0; + if (counts_only) + return nla_total_size(2 * nla_total_size(sizeof(u32))); + + for (i = 0; i < info->count; i++) { + const char *str = info->strings[i]; + + /* ETHTOOL_A_STRING_INDEX, ETHTOOL_A_STRING_VALUE, nest */ + len += nla_total_size(nla_total_size(sizeof(u32)) + + ethnl_strz_size(str)); + } + /* ETHTOOL_A_STRINGSET_ID, ETHTOOL_A_STRINGSET_COUNT */ + len = 2 * nla_total_size(sizeof(u32)) + nla_total_size(len); + + return nla_total_size(len); +} + +static int strset_reply_size(const struct ethnl_req_info *req_base, + const struct ethnl_reply_data *reply_base) +{ + const struct strset_req_info *req_info = STRSET_REQINFO(req_base); + const struct strset_reply_data *data = STRSET_REPDATA(reply_base); + unsigned int i; + int len = 0; + int ret; + + len += ethnl_reply_header_size(); + for (i = 0; i < ETH_SS_COUNT; i++) { + const struct strset_info *set_info = &data->sets[i]; + + if (!strset_include(req_info, data, i)) + continue; + + ret = strset_set_size(set_info, req_info->counts_only); + if (ret < 0) + return ret; + len += ret; + } + + return len; +} + +/* fill one string into reply */ +static int strset_fill_string(struct sk_buff *skb, + const struct strset_info *set_info, u32 idx) +{ + struct nlattr *string_attr; + const char *value; + + value = set_info->strings[idx]; + + string_attr = nla_nest_start(skb, ETHTOOL_A_STRINGS_STRING); + if (!string_attr) + return -EMSGSIZE; + if (nla_put_u32(skb, ETHTOOL_A_STRING_INDEX, idx) || + ethnl_put_strz(skb, ETHTOOL_A_STRING_VALUE, value)) + goto nla_put_failure; + nla_nest_end(skb, string_attr); + + return 0; +nla_put_failure: + nla_nest_cancel(skb, string_attr); + return -EMSGSIZE; +} + +/* fill one string set into reply */ +static int strset_fill_set(struct sk_buff *skb, + const struct strset_info *set_info, u32 id, + bool counts_only) +{ + struct nlattr *stringset_attr; + struct nlattr *strings_attr; + unsigned int i; + + if (!set_info->per_dev && !set_info->strings) + return -EOPNOTSUPP; + if (set_info->count == 0) + return 0; + stringset_attr = nla_nest_start(skb, ETHTOOL_A_STRINGSETS_STRINGSET); + if (!stringset_attr) + return -EMSGSIZE; + + if (nla_put_u32(skb, ETHTOOL_A_STRINGSET_ID, id) || + nla_put_u32(skb, ETHTOOL_A_STRINGSET_COUNT, set_info->count)) + goto nla_put_failure; + + if (!counts_only) { + strings_attr = nla_nest_start(skb, ETHTOOL_A_STRINGSET_STRINGS); + if (!strings_attr) + goto nla_put_failure; + for (i = 0; i < set_info->count; i++) { + if (strset_fill_string(skb, set_info, i) < 0) + goto nla_put_failure; + } + nla_nest_end(skb, strings_attr); + } + + nla_nest_end(skb, stringset_attr); + return 0; + +nla_put_failure: + nla_nest_cancel(skb, stringset_attr); + return -EMSGSIZE; +} + +static int strset_fill_reply(struct sk_buff *skb, + const struct ethnl_req_info *req_base, + const struct ethnl_reply_data *reply_base) +{ + const struct strset_req_info *req_info = STRSET_REQINFO(req_base); + const struct strset_reply_data *data = STRSET_REPDATA(reply_base); + struct nlattr *nest; + unsigned int i; + int ret; + + nest = nla_nest_start(skb, ETHTOOL_A_STRSET_STRINGSETS); + if (!nest) + return -EMSGSIZE; + + for (i = 0; i < ETH_SS_COUNT; i++) { + if (strset_include(req_info, data, i)) { + ret = strset_fill_set(skb, &data->sets[i], i, + req_info->counts_only); + if (ret < 0) + goto nla_put_failure; + } + } + + nla_nest_end(skb, nest); + return 0; + +nla_put_failure: + nla_nest_cancel(skb, nest); + return ret; +} + +const struct ethnl_request_ops ethnl_strset_request_ops = { + .request_cmd = ETHTOOL_MSG_STRSET_GET, + .reply_cmd = ETHTOOL_MSG_STRSET_GET_REPLY, + .hdr_attr = ETHTOOL_A_STRSET_HEADER, + .max_attr = ETHTOOL_A_STRSET_MAX, + .req_info_size = sizeof(struct strset_req_info), + .reply_data_size = sizeof(struct strset_reply_data), + .request_policy = strset_get_policy, + .allow_nodev_do = true, + + .parse_request = strset_parse_request, + .prepare_data = strset_prepare_data, + .reply_size = strset_reply_size, + .fill_reply = strset_fill_reply, + .cleanup_data = strset_cleanup_data, +}; -- GitLab From 459e0b81b37043545d90629fdfb243444151e77d Mon Sep 17 00:00:00 2001 From: Michal Kubecek Date: Fri, 27 Dec 2019 15:55:48 +0100 Subject: [PATCH 1392/1437] ethtool: provide link settings with LINKINFO_GET request Implement LINKINFO_GET netlink request to get basic link settings provided by ETHTOOL_GLINKSETTINGS and ETHTOOL_GSET ioctl commands. This request provides settings not directly related to autonegotiation and link mode selection: physical port, phy MDIO address, MDI(-X) status, MDI(-X) control and transceiver. LINKINFO_GET request can be used with NLM_F_DUMP (without device identification) to request the information for all devices in current network namespace providing the data. Signed-off-by: Michal Kubecek Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller --- Documentation/networking/ethtool-netlink.rst | 37 +++++++- include/uapi/linux/ethtool_netlink.h | 18 ++++ net/ethtool/Makefile | 2 +- net/ethtool/common.c | 48 ++++++++++ net/ethtool/common.h | 4 + net/ethtool/ioctl.c | 48 ---------- net/ethtool/linkinfo.c | 94 ++++++++++++++++++++ net/ethtool/netlink.c | 8 ++ net/ethtool/netlink.h | 1 + 9 files changed, 209 insertions(+), 51 deletions(-) create mode 100644 net/ethtool/linkinfo.c diff --git a/Documentation/networking/ethtool-netlink.rst b/Documentation/networking/ethtool-netlink.rst index 3912cb0eb9c64..60684786a92c8 100644 --- a/Documentation/networking/ethtool-netlink.rst +++ b/Documentation/networking/ethtool-netlink.rst @@ -180,12 +180,14 @@ Userspace to kernel: ===================================== ================================ ``ETHTOOL_MSG_STRSET_GET`` get string set + ``ETHTOOL_MSG_LINKINFO_GET`` get link settings ===================================== ================================ Kernel to userspace: ===================================== ================================ ``ETHTOOL_MSG_STRSET_GET_REPLY`` string set contents + ``ETHTOOL_MSG_LINKINFO_GET_REPLY`` link settings ===================================== ================================ ``GET`` requests are sent by userspace applications to retrieve device @@ -278,6 +280,37 @@ Flag ``ETHTOOL_A_STRSET_COUNTS_ONLY`` tells kernel to only return string counts of the sets, not the actual strings. +LINKINFO_GET +============ + +Requests link settings as provided by ``ETHTOOL_GLINKSETTINGS`` except for +link modes and autonegotiation related information. The request does not use +any attributes. + +Request contents: + + ==================================== ====== ========================== + ``ETHTOOL_A_LINKINFO_HEADER`` nested request header + ==================================== ====== ========================== + +Kernel response contents: + + ==================================== ====== ========================== + ``ETHTOOL_A_LINKINFO_HEADER`` nested reply header + ``ETHTOOL_A_LINKINFO_PORT`` u8 physical port + ``ETHTOOL_A_LINKINFO_PHYADDR`` u8 phy MDIO address + ``ETHTOOL_A_LINKINFO_TP_MDIX`` u8 MDI(-X) status + ``ETHTOOL_A_LINKINFO_TP_MDIX_CTRL`` u8 MDI(-X) control + ``ETHTOOL_A_LINKINFO_TRANSCEIVER`` u8 transceiver + ==================================== ====== ========================== + +Attributes and their values have the same meaning as matching members of the +corresponding ioctl structures. + +``LINKINFO_GET`` allows dump requests (kernel returns reply message for all +devices supporting the request). + + Request translation =================== @@ -288,7 +321,7 @@ have their netlink replacement yet. =================================== ===================================== ioctl command netlink command =================================== ===================================== - ``ETHTOOL_GSET`` n/a + ``ETHTOOL_GSET`` ``ETHTOOL_MSG_LINKINFO_GET`` ``ETHTOOL_SSET`` n/a ``ETHTOOL_GDRVINFO`` n/a ``ETHTOOL_GREGS`` n/a @@ -362,7 +395,7 @@ have their netlink replacement yet. ``ETHTOOL_STUNABLE`` n/a ``ETHTOOL_GPHYSTATS`` n/a ``ETHTOOL_PERQUEUE`` n/a - ``ETHTOOL_GLINKSETTINGS`` n/a + ``ETHTOOL_GLINKSETTINGS`` ``ETHTOOL_MSG_LINKINFO_GET`` ``ETHTOOL_SLINKSETTINGS`` n/a ``ETHTOOL_PHY_GTUNABLE`` n/a ``ETHTOOL_PHY_STUNABLE`` n/a diff --git a/include/uapi/linux/ethtool_netlink.h b/include/uapi/linux/ethtool_netlink.h index cabef1fec42ae..1966532993e5a 100644 --- a/include/uapi/linux/ethtool_netlink.h +++ b/include/uapi/linux/ethtool_netlink.h @@ -15,6 +15,7 @@ enum { ETHTOOL_MSG_USER_NONE, ETHTOOL_MSG_STRSET_GET, + ETHTOOL_MSG_LINKINFO_GET, /* add new constants above here */ __ETHTOOL_MSG_USER_CNT, @@ -25,6 +26,7 @@ enum { enum { ETHTOOL_MSG_KERNEL_NONE, ETHTOOL_MSG_STRSET_GET_REPLY, + ETHTOOL_MSG_LINKINFO_GET_REPLY, /* add new constants above here */ __ETHTOOL_MSG_KERNEL_CNT, @@ -141,6 +143,22 @@ enum { ETHTOOL_A_STRSET_MAX = __ETHTOOL_A_STRSET_CNT - 1 }; +/* LINKINFO */ + +enum { + ETHTOOL_A_LINKINFO_UNSPEC, + ETHTOOL_A_LINKINFO_HEADER, /* nest - _A_HEADER_* */ + ETHTOOL_A_LINKINFO_PORT, /* u8 */ + ETHTOOL_A_LINKINFO_PHYADDR, /* u8 */ + ETHTOOL_A_LINKINFO_TP_MDIX, /* u8 */ + ETHTOOL_A_LINKINFO_TP_MDIX_CTRL, /* u8 */ + ETHTOOL_A_LINKINFO_TRANSCEIVER, /* u8 */ + + /* add new constants above here */ + __ETHTOOL_A_LINKINFO_CNT, + ETHTOOL_A_LINKINFO_MAX = __ETHTOOL_A_LINKINFO_CNT - 1 +}; + /* generic netlink info */ #define ETHTOOL_GENL_NAME "ethtool" #define ETHTOOL_GENL_VERSION 1 diff --git a/net/ethtool/Makefile b/net/ethtool/Makefile index efcc42c34d620..765736ec52c02 100644 --- a/net/ethtool/Makefile +++ b/net/ethtool/Makefile @@ -4,4 +4,4 @@ obj-y += ioctl.o common.o obj-$(CONFIG_ETHTOOL_NETLINK) += ethtool_nl.o -ethtool_nl-y := netlink.o bitset.o strset.o +ethtool_nl-y := netlink.o bitset.o strset.o linkinfo.o diff --git a/net/ethtool/common.c b/net/ethtool/common.c index 0a87285653569..1d4a0aeff2cb7 100644 --- a/net/ethtool/common.c +++ b/net/ethtool/common.c @@ -169,3 +169,51 @@ const char link_mode_names[][ETH_GSTRING_LEN] = { __DEFINE_LINK_MODE_NAME(400000, CR8, Full), }; static_assert(ARRAY_SIZE(link_mode_names) == __ETHTOOL_LINK_MODE_MASK_NBITS); + +/* return false if legacy contained non-0 deprecated fields + * maxtxpkt/maxrxpkt. rest of ksettings always updated + */ +bool +convert_legacy_settings_to_link_ksettings( + struct ethtool_link_ksettings *link_ksettings, + const struct ethtool_cmd *legacy_settings) +{ + bool retval = true; + + memset(link_ksettings, 0, sizeof(*link_ksettings)); + + /* This is used to tell users that driver is still using these + * deprecated legacy fields, and they should not use + * %ETHTOOL_GLINKSETTINGS/%ETHTOOL_SLINKSETTINGS + */ + if (legacy_settings->maxtxpkt || + legacy_settings->maxrxpkt) + retval = false; + + ethtool_convert_legacy_u32_to_link_mode( + link_ksettings->link_modes.supported, + legacy_settings->supported); + ethtool_convert_legacy_u32_to_link_mode( + link_ksettings->link_modes.advertising, + legacy_settings->advertising); + ethtool_convert_legacy_u32_to_link_mode( + link_ksettings->link_modes.lp_advertising, + legacy_settings->lp_advertising); + link_ksettings->base.speed + = ethtool_cmd_speed(legacy_settings); + link_ksettings->base.duplex + = legacy_settings->duplex; + link_ksettings->base.port + = legacy_settings->port; + link_ksettings->base.phy_address + = legacy_settings->phy_address; + link_ksettings->base.autoneg + = legacy_settings->autoneg; + link_ksettings->base.mdio_support + = legacy_settings->mdio_support; + link_ksettings->base.eth_tp_mdix + = legacy_settings->eth_tp_mdix; + link_ksettings->base.eth_tp_mdix_ctrl + = legacy_settings->eth_tp_mdix_ctrl; + return retval; +} diff --git a/net/ethtool/common.h b/net/ethtool/common.h index bbb788908cb11..c8a2374027298 100644 --- a/net/ethtool/common.h +++ b/net/ethtool/common.h @@ -19,4 +19,8 @@ extern const char phy_tunable_strings[__ETHTOOL_PHY_TUNABLE_COUNT][ETH_GSTRING_LEN]; extern const char link_mode_names[][ETH_GSTRING_LEN]; +bool convert_legacy_settings_to_link_ksettings( + struct ethtool_link_ksettings *link_ksettings, + const struct ethtool_cmd *legacy_settings); + #endif /* _ETHTOOL_COMMON_H */ diff --git a/net/ethtool/ioctl.c b/net/ethtool/ioctl.c index 88f7cddf5a6fe..8a0a13b478e0a 100644 --- a/net/ethtool/ioctl.c +++ b/net/ethtool/ioctl.c @@ -358,54 +358,6 @@ bool ethtool_convert_link_mode_to_legacy_u32(u32 *legacy_u32, } EXPORT_SYMBOL(ethtool_convert_link_mode_to_legacy_u32); -/* return false if legacy contained non-0 deprecated fields - * maxtxpkt/maxrxpkt. rest of ksettings always updated - */ -static bool -convert_legacy_settings_to_link_ksettings( - struct ethtool_link_ksettings *link_ksettings, - const struct ethtool_cmd *legacy_settings) -{ - bool retval = true; - - memset(link_ksettings, 0, sizeof(*link_ksettings)); - - /* This is used to tell users that driver is still using these - * deprecated legacy fields, and they should not use - * %ETHTOOL_GLINKSETTINGS/%ETHTOOL_SLINKSETTINGS - */ - if (legacy_settings->maxtxpkt || - legacy_settings->maxrxpkt) - retval = false; - - ethtool_convert_legacy_u32_to_link_mode( - link_ksettings->link_modes.supported, - legacy_settings->supported); - ethtool_convert_legacy_u32_to_link_mode( - link_ksettings->link_modes.advertising, - legacy_settings->advertising); - ethtool_convert_legacy_u32_to_link_mode( - link_ksettings->link_modes.lp_advertising, - legacy_settings->lp_advertising); - link_ksettings->base.speed - = ethtool_cmd_speed(legacy_settings); - link_ksettings->base.duplex - = legacy_settings->duplex; - link_ksettings->base.port - = legacy_settings->port; - link_ksettings->base.phy_address - = legacy_settings->phy_address; - link_ksettings->base.autoneg - = legacy_settings->autoneg; - link_ksettings->base.mdio_support - = legacy_settings->mdio_support; - link_ksettings->base.eth_tp_mdix - = legacy_settings->eth_tp_mdix; - link_ksettings->base.eth_tp_mdix_ctrl - = legacy_settings->eth_tp_mdix_ctrl; - return retval; -} - /* return false if ksettings link modes had higher bits * set. legacy_settings always updated (best effort) */ diff --git a/net/ethtool/linkinfo.c b/net/ethtool/linkinfo.c new file mode 100644 index 0000000000000..f52a31af6fff0 --- /dev/null +++ b/net/ethtool/linkinfo.c @@ -0,0 +1,94 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include "netlink.h" +#include "common.h" + +struct linkinfo_req_info { + struct ethnl_req_info base; +}; + +struct linkinfo_reply_data { + struct ethnl_reply_data base; + struct ethtool_link_ksettings ksettings; + struct ethtool_link_settings *lsettings; +}; + +#define LINKINFO_REPDATA(__reply_base) \ + container_of(__reply_base, struct linkinfo_reply_data, base) + +static const struct nla_policy +linkinfo_get_policy[ETHTOOL_A_LINKINFO_MAX + 1] = { + [ETHTOOL_A_LINKINFO_UNSPEC] = { .type = NLA_REJECT }, + [ETHTOOL_A_LINKINFO_HEADER] = { .type = NLA_NESTED }, + [ETHTOOL_A_LINKINFO_PORT] = { .type = NLA_REJECT }, + [ETHTOOL_A_LINKINFO_PHYADDR] = { .type = NLA_REJECT }, + [ETHTOOL_A_LINKINFO_TP_MDIX] = { .type = NLA_REJECT }, + [ETHTOOL_A_LINKINFO_TP_MDIX_CTRL] = { .type = NLA_REJECT }, + [ETHTOOL_A_LINKINFO_TRANSCEIVER] = { .type = NLA_REJECT }, +}; + +static int linkinfo_prepare_data(const struct ethnl_req_info *req_base, + struct ethnl_reply_data *reply_base, + struct genl_info *info) +{ + struct linkinfo_reply_data *data = LINKINFO_REPDATA(reply_base); + struct net_device *dev = reply_base->dev; + int ret; + + data->lsettings = &data->ksettings.base; + + ret = ethnl_ops_begin(dev); + if (ret < 0) + return ret; + ret = __ethtool_get_link_ksettings(dev, &data->ksettings); + if (ret < 0 && info) + GENL_SET_ERR_MSG(info, "failed to retrieve link settings"); + ethnl_ops_complete(dev); + + return ret; +} + +static int linkinfo_reply_size(const struct ethnl_req_info *req_base, + const struct ethnl_reply_data *reply_base) +{ + return nla_total_size(sizeof(u8)) /* LINKINFO_PORT */ + + nla_total_size(sizeof(u8)) /* LINKINFO_PHYADDR */ + + nla_total_size(sizeof(u8)) /* LINKINFO_TP_MDIX */ + + nla_total_size(sizeof(u8)) /* LINKINFO_TP_MDIX_CTRL */ + + nla_total_size(sizeof(u8)) /* LINKINFO_TRANSCEIVER */ + + 0; +} + +static int linkinfo_fill_reply(struct sk_buff *skb, + const struct ethnl_req_info *req_base, + const struct ethnl_reply_data *reply_base) +{ + const struct linkinfo_reply_data *data = LINKINFO_REPDATA(reply_base); + + if (nla_put_u8(skb, ETHTOOL_A_LINKINFO_PORT, data->lsettings->port) || + nla_put_u8(skb, ETHTOOL_A_LINKINFO_PHYADDR, + data->lsettings->phy_address) || + nla_put_u8(skb, ETHTOOL_A_LINKINFO_TP_MDIX, + data->lsettings->eth_tp_mdix) || + nla_put_u8(skb, ETHTOOL_A_LINKINFO_TP_MDIX_CTRL, + data->lsettings->eth_tp_mdix_ctrl) || + nla_put_u8(skb, ETHTOOL_A_LINKINFO_TRANSCEIVER, + data->lsettings->transceiver)) + return -EMSGSIZE; + + return 0; +} + +const struct ethnl_request_ops ethnl_linkinfo_request_ops = { + .request_cmd = ETHTOOL_MSG_LINKINFO_GET, + .reply_cmd = ETHTOOL_MSG_LINKINFO_GET_REPLY, + .hdr_attr = ETHTOOL_A_LINKINFO_HEADER, + .max_attr = ETHTOOL_A_LINKINFO_MAX, + .req_info_size = sizeof(struct linkinfo_req_info), + .reply_data_size = sizeof(struct linkinfo_reply_data), + .request_policy = linkinfo_get_policy, + + .prepare_data = linkinfo_prepare_data, + .reply_size = linkinfo_reply_size, + .fill_reply = linkinfo_fill_reply, +}; diff --git a/net/ethtool/netlink.c b/net/ethtool/netlink.c index 7dc082bde6709..ce86d97c922e7 100644 --- a/net/ethtool/netlink.c +++ b/net/ethtool/netlink.c @@ -195,6 +195,7 @@ struct ethnl_dump_ctx { static const struct ethnl_request_ops * ethnl_default_requests[__ETHTOOL_MSG_USER_CNT] = { [ETHTOOL_MSG_STRSET_GET] = ðnl_strset_request_ops, + [ETHTOOL_MSG_LINKINFO_GET] = ðnl_linkinfo_request_ops, }; static struct ethnl_dump_ctx *ethnl_dump_context(struct netlink_callback *cb) @@ -526,6 +527,13 @@ static const struct genl_ops ethtool_genl_ops[] = { .dumpit = ethnl_default_dumpit, .done = ethnl_default_done, }, + { + .cmd = ETHTOOL_MSG_LINKINFO_GET, + .doit = ethnl_default_doit, + .start = ethnl_default_start, + .dumpit = ethnl_default_dumpit, + .done = ethnl_default_done, + }, }; static const struct genl_multicast_group ethtool_nl_mcgrps[] = { diff --git a/net/ethtool/netlink.h b/net/ethtool/netlink.h index 44e9f63aefb7c..9fc8f94d8dce4 100644 --- a/net/ethtool/netlink.h +++ b/net/ethtool/netlink.h @@ -329,5 +329,6 @@ struct ethnl_request_ops { /* request handlers */ extern const struct ethnl_request_ops ethnl_strset_request_ops; +extern const struct ethnl_request_ops ethnl_linkinfo_request_ops; #endif /* _NET_ETHTOOL_NETLINK_H */ -- GitLab From a53f3d41e4d3df46aba254ba51e7fbe45470fece Mon Sep 17 00:00:00 2001 From: Michal Kubecek Date: Fri, 27 Dec 2019 15:55:53 +0100 Subject: [PATCH 1393/1437] ethtool: set link settings with LINKINFO_SET request Implement LINKINFO_SET netlink request to set link settings queried by LINKINFO_GET message. Only physical port, phy MDIO address and MDI(-X) control can be set, attempt to modify MDI(-X) status and transceiver is rejected. Signed-off-by: Michal Kubecek Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller --- Documentation/networking/ethtool-netlink.rst | 24 ++++++- include/uapi/linux/ethtool_netlink.h | 1 + net/ethtool/linkinfo.c | 71 ++++++++++++++++++++ net/ethtool/netlink.c | 5 ++ net/ethtool/netlink.h | 2 + 5 files changed, 101 insertions(+), 2 deletions(-) diff --git a/Documentation/networking/ethtool-netlink.rst b/Documentation/networking/ethtool-netlink.rst index 60684786a92c8..39c4aba324c3b 100644 --- a/Documentation/networking/ethtool-netlink.rst +++ b/Documentation/networking/ethtool-netlink.rst @@ -181,6 +181,7 @@ Userspace to kernel: ===================================== ================================ ``ETHTOOL_MSG_STRSET_GET`` get string set ``ETHTOOL_MSG_LINKINFO_GET`` get link settings + ``ETHTOOL_MSG_LINKINFO_SET`` set link settings ===================================== ================================ Kernel to userspace: @@ -311,6 +312,25 @@ corresponding ioctl structures. devices supporting the request). +LINKINFO_SET +============ + +``LINKINFO_SET`` request allows setting some of the attributes reported by +``LINKINFO_GET``. + +Request contents: + + ==================================== ====== ========================== + ``ETHTOOL_A_LINKINFO_HEADER`` nested request header + ``ETHTOOL_A_LINKINFO_PORT`` u8 physical port + ``ETHTOOL_A_LINKINFO_PHYADDR`` u8 phy MDIO address + ``ETHTOOL_A_LINKINFO_TP_MDIX_CTRL`` u8 MDI(-X) control + ==================================== ====== ========================== + +MDI(-X) status and transceiver cannot be set, request with the corresponding +attributes is rejected. + + Request translation =================== @@ -322,7 +342,7 @@ have their netlink replacement yet. ioctl command netlink command =================================== ===================================== ``ETHTOOL_GSET`` ``ETHTOOL_MSG_LINKINFO_GET`` - ``ETHTOOL_SSET`` n/a + ``ETHTOOL_SSET`` ``ETHTOOL_MSG_LINKINFO_SET`` ``ETHTOOL_GDRVINFO`` n/a ``ETHTOOL_GREGS`` n/a ``ETHTOOL_GWOL`` n/a @@ -396,7 +416,7 @@ have their netlink replacement yet. ``ETHTOOL_GPHYSTATS`` n/a ``ETHTOOL_PERQUEUE`` n/a ``ETHTOOL_GLINKSETTINGS`` ``ETHTOOL_MSG_LINKINFO_GET`` - ``ETHTOOL_SLINKSETTINGS`` n/a + ``ETHTOOL_SLINKSETTINGS`` ``ETHTOOL_MSG_LINKINFO_SET`` ``ETHTOOL_PHY_GTUNABLE`` n/a ``ETHTOOL_PHY_STUNABLE`` n/a ``ETHTOOL_GFECPARAM`` n/a diff --git a/include/uapi/linux/ethtool_netlink.h b/include/uapi/linux/ethtool_netlink.h index 1966532993e5a..5b7806a5bef8e 100644 --- a/include/uapi/linux/ethtool_netlink.h +++ b/include/uapi/linux/ethtool_netlink.h @@ -16,6 +16,7 @@ enum { ETHTOOL_MSG_USER_NONE, ETHTOOL_MSG_STRSET_GET, ETHTOOL_MSG_LINKINFO_GET, + ETHTOOL_MSG_LINKINFO_SET, /* add new constants above here */ __ETHTOOL_MSG_USER_CNT, diff --git a/net/ethtool/linkinfo.c b/net/ethtool/linkinfo.c index f52a31af6fff0..8a5f68f92425f 100644 --- a/net/ethtool/linkinfo.c +++ b/net/ethtool/linkinfo.c @@ -92,3 +92,74 @@ const struct ethnl_request_ops ethnl_linkinfo_request_ops = { .reply_size = linkinfo_reply_size, .fill_reply = linkinfo_fill_reply, }; + +/* LINKINFO_SET */ + +static const struct nla_policy +linkinfo_set_policy[ETHTOOL_A_LINKINFO_MAX + 1] = { + [ETHTOOL_A_LINKINFO_UNSPEC] = { .type = NLA_REJECT }, + [ETHTOOL_A_LINKINFO_HEADER] = { .type = NLA_NESTED }, + [ETHTOOL_A_LINKINFO_PORT] = { .type = NLA_U8 }, + [ETHTOOL_A_LINKINFO_PHYADDR] = { .type = NLA_U8 }, + [ETHTOOL_A_LINKINFO_TP_MDIX] = { .type = NLA_REJECT }, + [ETHTOOL_A_LINKINFO_TP_MDIX_CTRL] = { .type = NLA_U8 }, + [ETHTOOL_A_LINKINFO_TRANSCEIVER] = { .type = NLA_REJECT }, +}; + +int ethnl_set_linkinfo(struct sk_buff *skb, struct genl_info *info) +{ + struct nlattr *tb[ETHTOOL_A_LINKINFO_MAX + 1]; + struct ethtool_link_ksettings ksettings = {}; + struct ethtool_link_settings *lsettings; + struct ethnl_req_info req_info = {}; + struct net_device *dev; + bool mod = false; + int ret; + + ret = nlmsg_parse(info->nlhdr, GENL_HDRLEN, tb, + ETHTOOL_A_LINKINFO_MAX, linkinfo_set_policy, + info->extack); + if (ret < 0) + return ret; + ret = ethnl_parse_header(&req_info, tb[ETHTOOL_A_LINKINFO_HEADER], + genl_info_net(info), info->extack, true); + if (ret < 0) + return ret; + dev = req_info.dev; + if (!dev->ethtool_ops->get_link_ksettings || + !dev->ethtool_ops->set_link_ksettings) + return -EOPNOTSUPP; + + rtnl_lock(); + ret = ethnl_ops_begin(dev); + if (ret < 0) + goto out_rtnl; + + ret = __ethtool_get_link_ksettings(dev, &ksettings); + if (ret < 0) { + if (info) + GENL_SET_ERR_MSG(info, "failed to retrieve link settings"); + goto out_ops; + } + lsettings = &ksettings.base; + + ethnl_update_u8(&lsettings->port, tb[ETHTOOL_A_LINKINFO_PORT], &mod); + ethnl_update_u8(&lsettings->phy_address, tb[ETHTOOL_A_LINKINFO_PHYADDR], + &mod); + ethnl_update_u8(&lsettings->eth_tp_mdix_ctrl, + tb[ETHTOOL_A_LINKINFO_TP_MDIX_CTRL], &mod); + ret = 0; + if (!mod) + goto out_ops; + + ret = dev->ethtool_ops->set_link_ksettings(dev, &ksettings); + if (ret < 0) + GENL_SET_ERR_MSG(info, "link settings update failed"); + +out_ops: + ethnl_ops_complete(dev); +out_rtnl: + rtnl_unlock(); + dev_put(dev); + return ret; +} diff --git a/net/ethtool/netlink.c b/net/ethtool/netlink.c index ce86d97c922e7..7867425956f69 100644 --- a/net/ethtool/netlink.c +++ b/net/ethtool/netlink.c @@ -534,6 +534,11 @@ static const struct genl_ops ethtool_genl_ops[] = { .dumpit = ethnl_default_dumpit, .done = ethnl_default_done, }, + { + .cmd = ETHTOOL_MSG_LINKINFO_SET, + .flags = GENL_UNS_ADMIN_PERM, + .doit = ethnl_set_linkinfo, + }, }; static const struct genl_multicast_group ethtool_nl_mcgrps[] = { diff --git a/net/ethtool/netlink.h b/net/ethtool/netlink.h index 9fc8f94d8dce4..bbe5fe60a023b 100644 --- a/net/ethtool/netlink.h +++ b/net/ethtool/netlink.h @@ -331,4 +331,6 @@ struct ethnl_request_ops { extern const struct ethnl_request_ops ethnl_strset_request_ops; extern const struct ethnl_request_ops ethnl_linkinfo_request_ops; +int ethnl_set_linkinfo(struct sk_buff *skb, struct genl_info *info); + #endif /* _NET_ETHTOOL_NETLINK_H */ -- GitLab From 5cf2a548bcbd3da3e79adb0b64ef96dbb9988d78 Mon Sep 17 00:00:00 2001 From: Michal Kubecek Date: Fri, 27 Dec 2019 15:55:58 +0100 Subject: [PATCH 1394/1437] ethtool: add default notification handler The ethtool netlink notifications have the same format as related GET replies so that if generic GET handling framework is used to process GET requests, its callbacks and instance of struct get_request_ops can be also used to compose corresponding notification message. Provide function ethnl_std_notify() to be used as notification handler in ethnl_notify_handlers table. Signed-off-by: Michal Kubecek Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller --- net/ethtool/netlink.c | 89 +++++++++++++++++++++++++++++++++++++++++++ net/ethtool/netlink.h | 4 +- 2 files changed, 92 insertions(+), 1 deletion(-) diff --git a/net/ethtool/netlink.c b/net/ethtool/netlink.c index 7867425956f69..057b67f8ba8ca 100644 --- a/net/ethtool/netlink.c +++ b/net/ethtool/netlink.c @@ -7,6 +7,7 @@ static struct genl_family ethtool_genl_family; static bool ethnl_ok __read_mostly; +static u32 ethnl_bcast_seq; static const struct nla_policy ethnl_header_policy[ETHTOOL_A_HEADER_MAX + 1] = { [ETHTOOL_A_HEADER_UNSPEC] = { .type = NLA_REJECT }, @@ -171,6 +172,18 @@ err: return NULL; } +static void *ethnl_bcastmsg_put(struct sk_buff *skb, u8 cmd) +{ + return genlmsg_put(skb, 0, ++ethnl_bcast_seq, ðtool_genl_family, 0, + cmd); +} + +static int ethnl_multicast(struct sk_buff *skb, struct net_device *dev) +{ + return genlmsg_multicast_netns(ðtool_genl_family, dev_net(dev), skb, + 0, ETHNL_MCGRP_MONITOR, GFP_KERNEL); +} + /* GET request helpers */ /** @@ -494,6 +507,82 @@ static int ethnl_default_done(struct netlink_callback *cb) return 0; } +static const struct ethnl_request_ops * +ethnl_default_notify_ops[ETHTOOL_MSG_KERNEL_MAX + 1] = { +}; + +/* default notification handler */ +static void ethnl_default_notify(struct net_device *dev, unsigned int cmd, + const void *data) +{ + struct ethnl_reply_data *reply_data; + const struct ethnl_request_ops *ops; + struct ethnl_req_info *req_info; + struct sk_buff *skb; + void *reply_payload; + int reply_len; + int ret; + + if (WARN_ONCE(cmd > ETHTOOL_MSG_KERNEL_MAX || + !ethnl_default_notify_ops[cmd], + "unexpected notification type %u\n", cmd)) + return; + ops = ethnl_default_notify_ops[cmd]; + req_info = kzalloc(ops->req_info_size, GFP_KERNEL); + if (!req_info) + return; + reply_data = kmalloc(ops->reply_data_size, GFP_KERNEL); + if (!reply_data) { + kfree(req_info); + return; + } + + req_info->dev = dev; + req_info->flags |= ETHTOOL_FLAG_COMPACT_BITSETS; + + ethnl_init_reply_data(reply_data, ops, dev); + ret = ops->prepare_data(req_info, reply_data, NULL); + if (ret < 0) + goto err_cleanup; + reply_len = ops->reply_size(req_info, reply_data); + if (ret < 0) + goto err_cleanup; + ret = -ENOMEM; + skb = genlmsg_new(reply_len, GFP_KERNEL); + if (!skb) + goto err_cleanup; + reply_payload = ethnl_bcastmsg_put(skb, cmd); + if (!reply_payload) + goto err_skb; + ret = ethnl_fill_reply_header(skb, dev, ops->hdr_attr); + if (ret < 0) + goto err_msg; + ret = ops->fill_reply(skb, req_info, reply_data); + if (ret < 0) + goto err_msg; + if (ops->cleanup_data) + ops->cleanup_data(reply_data); + + genlmsg_end(skb, reply_payload); + kfree(reply_data); + kfree(req_info); + ethnl_multicast(skb, dev); + return; + +err_msg: + WARN_ONCE(ret == -EMSGSIZE, + "calculated message payload length (%d) not sufficient\n", + reply_len); +err_skb: + nlmsg_free(skb); +err_cleanup: + if (ops->cleanup_data) + ops->cleanup_data(reply_data); + kfree(reply_data); + kfree(req_info); + return; +} + /* notifications */ typedef void (*ethnl_notify_handler_t)(struct net_device *dev, unsigned int cmd, diff --git a/net/ethtool/netlink.h b/net/ethtool/netlink.h index bbe5fe60a023b..5d56d7779a060 100644 --- a/net/ethtool/netlink.h +++ b/net/ethtool/netlink.h @@ -300,7 +300,9 @@ static inline void ethnl_ops_complete(struct net_device *dev) * unified infrastructure. When used, a pointer to an instance of this * structure is to be added to ðnl_default_requests array and generic * handlers ethnl_default_doit(), ethnl_default_dumpit(), - * ethnl_default_start() and ethnl_default_done() used in @ethtool_genl_ops. + * ethnl_default_start() and ethnl_default_done() used in @ethtool_genl_ops; + * ethnl_default_notify() can be used in @ethnl_notify_handlers to send + * notifications of the corresponding type. */ struct ethnl_request_ops { u8 request_cmd; -- GitLab From 73286734c1b0d009fd317c2a74e173cb22233dad Mon Sep 17 00:00:00 2001 From: Michal Kubecek Date: Fri, 27 Dec 2019 15:56:03 +0100 Subject: [PATCH 1395/1437] ethtool: add LINKINFO_NTF notification Send ETHTOOL_MSG_LINKINFO_NTF notification message whenever device link settings are modified using ETHTOOL_MSG_LINKINFO_SET netlink message or ETHTOOL_SLINKSETTINGS or ETHTOOL_SSET ioctl commands. The notification message has the same format as reply to LINKINFO_GET request. ETHTOOL_MSG_LINKINFO_SET netlink request only triggers the notification if there is a change but the ioctl command handlers do not check if there is an actual change and trigger the notification whenever the commands are executed. As all work is done by ethnl_default_notify() handler and callback functions introduced to handle LINKINFO_GET requests, all that remains is adding entries for ETHTOOL_MSG_LINKINFO_NTF into ethnl_notify_handlers and ethnl_default_notify_ops lookup tables and calls to ethtool_notify() where needed. Signed-off-by: Michal Kubecek Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller --- Documentation/networking/ethtool-netlink.rst | 1 + include/uapi/linux/ethtool_netlink.h | 1 + net/ethtool/ioctl.c | 12 ++++++++++-- net/ethtool/linkinfo.c | 2 ++ net/ethtool/netlink.c | 2 ++ 5 files changed, 16 insertions(+), 2 deletions(-) diff --git a/Documentation/networking/ethtool-netlink.rst b/Documentation/networking/ethtool-netlink.rst index 39c4aba324c3b..34254482d2952 100644 --- a/Documentation/networking/ethtool-netlink.rst +++ b/Documentation/networking/ethtool-netlink.rst @@ -189,6 +189,7 @@ Kernel to userspace: ===================================== ================================ ``ETHTOOL_MSG_STRSET_GET_REPLY`` string set contents ``ETHTOOL_MSG_LINKINFO_GET_REPLY`` link settings + ``ETHTOOL_MSG_LINKINFO_NTF`` link settings notification ===================================== ================================ ``GET`` requests are sent by userspace applications to retrieve device diff --git a/include/uapi/linux/ethtool_netlink.h b/include/uapi/linux/ethtool_netlink.h index 5b7806a5bef8e..d530fa30de361 100644 --- a/include/uapi/linux/ethtool_netlink.h +++ b/include/uapi/linux/ethtool_netlink.h @@ -28,6 +28,7 @@ enum { ETHTOOL_MSG_KERNEL_NONE, ETHTOOL_MSG_STRSET_GET_REPLY, ETHTOOL_MSG_LINKINFO_GET_REPLY, + ETHTOOL_MSG_LINKINFO_NTF, /* add new constants above here */ __ETHTOOL_MSG_KERNEL_CNT, diff --git a/net/ethtool/ioctl.c b/net/ethtool/ioctl.c index 8a0a13b478e0a..11a467294a332 100644 --- a/net/ethtool/ioctl.c +++ b/net/ethtool/ioctl.c @@ -26,6 +26,7 @@ #include #include #include +#include #include "common.h" @@ -571,7 +572,10 @@ static int ethtool_set_link_ksettings(struct net_device *dev, != link_ksettings.base.link_mode_masks_nwords) return -EINVAL; - return dev->ethtool_ops->set_link_ksettings(dev, &link_ksettings); + err = dev->ethtool_ops->set_link_ksettings(dev, &link_ksettings); + if (err >= 0) + ethtool_notify(dev, ETHTOOL_MSG_LINKINFO_NTF, NULL); + return err; } /* Query device for its ethtool_cmd settings. @@ -620,6 +624,7 @@ static int ethtool_set_settings(struct net_device *dev, void __user *useraddr) { struct ethtool_link_ksettings link_ksettings; struct ethtool_cmd cmd; + int ret; ASSERT_RTNL(); @@ -632,7 +637,10 @@ static int ethtool_set_settings(struct net_device *dev, void __user *useraddr) return -EINVAL; link_ksettings.base.link_mode_masks_nwords = __ETHTOOL_LINK_MODE_MASK_NU32; - return dev->ethtool_ops->set_link_ksettings(dev, &link_ksettings); + ret = dev->ethtool_ops->set_link_ksettings(dev, &link_ksettings); + if (ret >= 0) + ethtool_notify(dev, ETHTOOL_MSG_LINKINFO_NTF, NULL); + return ret; } static noinline_for_stack int ethtool_get_drvinfo(struct net_device *dev, diff --git a/net/ethtool/linkinfo.c b/net/ethtool/linkinfo.c index 8a5f68f92425f..5d16cb4e8693d 100644 --- a/net/ethtool/linkinfo.c +++ b/net/ethtool/linkinfo.c @@ -155,6 +155,8 @@ int ethnl_set_linkinfo(struct sk_buff *skb, struct genl_info *info) ret = dev->ethtool_ops->set_link_ksettings(dev, &ksettings); if (ret < 0) GENL_SET_ERR_MSG(info, "link settings update failed"); + else + ethtool_notify(dev, ETHTOOL_MSG_LINKINFO_NTF, NULL); out_ops: ethnl_ops_complete(dev); diff --git a/net/ethtool/netlink.c b/net/ethtool/netlink.c index 057b67f8ba8ca..942da4ebdfe9e 100644 --- a/net/ethtool/netlink.c +++ b/net/ethtool/netlink.c @@ -509,6 +509,7 @@ static int ethnl_default_done(struct netlink_callback *cb) static const struct ethnl_request_ops * ethnl_default_notify_ops[ETHTOOL_MSG_KERNEL_MAX + 1] = { + [ETHTOOL_MSG_LINKINFO_NTF] = ðnl_linkinfo_request_ops, }; /* default notification handler */ @@ -589,6 +590,7 @@ typedef void (*ethnl_notify_handler_t)(struct net_device *dev, unsigned int cmd, const void *data); static const ethnl_notify_handler_t ethnl_notify_handlers[] = { + [ETHTOOL_MSG_LINKINFO_NTF] = ethnl_default_notify, }; void ethtool_notify(struct net_device *dev, unsigned int cmd, const void *data) -- GitLab From f625aa9be8c10f2e4dc677837e240730a25feda7 Mon Sep 17 00:00:00 2001 From: Michal Kubecek Date: Fri, 27 Dec 2019 15:56:08 +0100 Subject: [PATCH 1396/1437] ethtool: provide link mode information with LINKMODES_GET request Implement LINKMODES_GET netlink request to get link modes related information provided by ETHTOOL_GLINKSETTINGS and ETHTOOL_GSET ioctl commands. This request provides supported, advertised and peer advertised link modes, autonegotiation flag, speed and duplex. LINKMODES_GET request can be used with NLM_F_DUMP (without device identification) to request the information for all devices in current network namespace providing the data. Signed-off-by: Michal Kubecek Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller --- Documentation/networking/ethtool-netlink.rst | 36 +++++ include/linux/ethtool_netlink.h | 3 + include/uapi/linux/ethtool_netlink.h | 18 +++ net/ethtool/Makefile | 2 +- net/ethtool/linkmodes.c | 140 +++++++++++++++++++ net/ethtool/netlink.c | 8 ++ net/ethtool/netlink.h | 1 + 7 files changed, 207 insertions(+), 1 deletion(-) create mode 100644 net/ethtool/linkmodes.c diff --git a/Documentation/networking/ethtool-netlink.rst b/Documentation/networking/ethtool-netlink.rst index 34254482d2952..04c55be0264ce 100644 --- a/Documentation/networking/ethtool-netlink.rst +++ b/Documentation/networking/ethtool-netlink.rst @@ -182,6 +182,7 @@ Userspace to kernel: ``ETHTOOL_MSG_STRSET_GET`` get string set ``ETHTOOL_MSG_LINKINFO_GET`` get link settings ``ETHTOOL_MSG_LINKINFO_SET`` set link settings + ``ETHTOOL_MSG_LINKMODES_GET`` get link modes info ===================================== ================================ Kernel to userspace: @@ -190,6 +191,7 @@ Kernel to userspace: ``ETHTOOL_MSG_STRSET_GET_REPLY`` string set contents ``ETHTOOL_MSG_LINKINFO_GET_REPLY`` link settings ``ETHTOOL_MSG_LINKINFO_NTF`` link settings notification + ``ETHTOOL_MSG_LINKMODES_GET_REPLY`` link modes info ===================================== ================================ ``GET`` requests are sent by userspace applications to retrieve device @@ -332,6 +334,38 @@ MDI(-X) status and transceiver cannot be set, request with the corresponding attributes is rejected. +LINKMODES_GET +============= + +Requests link modes (supported, advertised and peer advertised) and related +information (autonegotiation status, link speed and duplex) as provided by +``ETHTOOL_GLINKSETTINGS``. The request does not use any attributes. + +Request contents: + + ==================================== ====== ========================== + ``ETHTOOL_A_LINKMODES_HEADER`` nested request header + ==================================== ====== ========================== + +Kernel response contents: + + ==================================== ====== ========================== + ``ETHTOOL_A_LINKMODES_HEADER`` nested reply header + ``ETHTOOL_A_LINKMODES_AUTONEG`` u8 autonegotiation status + ``ETHTOOL_A_LINKMODES_OURS`` bitset advertised link modes + ``ETHTOOL_A_LINKMODES_PEER`` bitset partner link modes + ``ETHTOOL_A_LINKMODES_SPEED`` u32 link speed (Mb/s) + ``ETHTOOL_A_LINKMODES_DUPLEX`` u8 duplex mode + ==================================== ====== ========================== + +For ``ETHTOOL_A_LINKMODES_OURS``, value represents advertised modes and mask +represents supported modes. ``ETHTOOL_A_LINKMODES_PEER`` in the reply is a bit +list. + +``LINKMODES_GET`` allows dump requests (kernel returns reply messages for all +devices supporting the request). + + Request translation =================== @@ -343,6 +377,7 @@ have their netlink replacement yet. ioctl command netlink command =================================== ===================================== ``ETHTOOL_GSET`` ``ETHTOOL_MSG_LINKINFO_GET`` + ``ETHTOOL_MSG_LINKMODES_GET`` ``ETHTOOL_SSET`` ``ETHTOOL_MSG_LINKINFO_SET`` ``ETHTOOL_GDRVINFO`` n/a ``ETHTOOL_GREGS`` n/a @@ -417,6 +452,7 @@ have their netlink replacement yet. ``ETHTOOL_GPHYSTATS`` n/a ``ETHTOOL_PERQUEUE`` n/a ``ETHTOOL_GLINKSETTINGS`` ``ETHTOOL_MSG_LINKINFO_GET`` + ``ETHTOOL_MSG_LINKMODES_GET`` ``ETHTOOL_SLINKSETTINGS`` ``ETHTOOL_MSG_LINKINFO_SET`` ``ETHTOOL_PHY_GTUNABLE`` n/a ``ETHTOOL_PHY_STUNABLE`` n/a diff --git a/include/linux/ethtool_netlink.h b/include/linux/ethtool_netlink.h index c98f6852c8eb5..d01b77887f822 100644 --- a/include/linux/ethtool_netlink.h +++ b/include/linux/ethtool_netlink.h @@ -7,6 +7,9 @@ #include #include +#define __ETHTOOL_LINK_MODE_MASK_NWORDS \ + DIV_ROUND_UP(__ETHTOOL_LINK_MODE_MASK_NBITS, 32) + enum ethtool_multicast_groups { ETHNL_MCGRP_MONITOR, }; diff --git a/include/uapi/linux/ethtool_netlink.h b/include/uapi/linux/ethtool_netlink.h index d530fa30de361..dc1cae052eeef 100644 --- a/include/uapi/linux/ethtool_netlink.h +++ b/include/uapi/linux/ethtool_netlink.h @@ -17,6 +17,7 @@ enum { ETHTOOL_MSG_STRSET_GET, ETHTOOL_MSG_LINKINFO_GET, ETHTOOL_MSG_LINKINFO_SET, + ETHTOOL_MSG_LINKMODES_GET, /* add new constants above here */ __ETHTOOL_MSG_USER_CNT, @@ -29,6 +30,7 @@ enum { ETHTOOL_MSG_STRSET_GET_REPLY, ETHTOOL_MSG_LINKINFO_GET_REPLY, ETHTOOL_MSG_LINKINFO_NTF, + ETHTOOL_MSG_LINKMODES_GET_REPLY, /* add new constants above here */ __ETHTOOL_MSG_KERNEL_CNT, @@ -161,6 +163,22 @@ enum { ETHTOOL_A_LINKINFO_MAX = __ETHTOOL_A_LINKINFO_CNT - 1 }; +/* LINKMODES */ + +enum { + ETHTOOL_A_LINKMODES_UNSPEC, + ETHTOOL_A_LINKMODES_HEADER, /* nest - _A_HEADER_* */ + ETHTOOL_A_LINKMODES_AUTONEG, /* u8 */ + ETHTOOL_A_LINKMODES_OURS, /* bitset */ + ETHTOOL_A_LINKMODES_PEER, /* bitset */ + ETHTOOL_A_LINKMODES_SPEED, /* u32 */ + ETHTOOL_A_LINKMODES_DUPLEX, /* u8 */ + + /* add new constants above here */ + __ETHTOOL_A_LINKMODES_CNT, + ETHTOOL_A_LINKMODES_MAX = __ETHTOOL_A_LINKMODES_CNT - 1 +}; + /* generic netlink info */ #define ETHTOOL_GENL_NAME "ethtool" #define ETHTOOL_GENL_VERSION 1 diff --git a/net/ethtool/Makefile b/net/ethtool/Makefile index 765736ec52c02..8023da6672cea 100644 --- a/net/ethtool/Makefile +++ b/net/ethtool/Makefile @@ -4,4 +4,4 @@ obj-y += ioctl.o common.o obj-$(CONFIG_ETHTOOL_NETLINK) += ethtool_nl.o -ethtool_nl-y := netlink.o bitset.o strset.o linkinfo.o +ethtool_nl-y := netlink.o bitset.o strset.o linkinfo.o linkmodes.o diff --git a/net/ethtool/linkmodes.c b/net/ethtool/linkmodes.c new file mode 100644 index 0000000000000..81856fa1e6322 --- /dev/null +++ b/net/ethtool/linkmodes.c @@ -0,0 +1,140 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include "netlink.h" +#include "common.h" +#include "bitset.h" + +struct linkmodes_req_info { + struct ethnl_req_info base; +}; + +struct linkmodes_reply_data { + struct ethnl_reply_data base; + struct ethtool_link_ksettings ksettings; + struct ethtool_link_settings *lsettings; + bool peer_empty; +}; + +#define LINKMODES_REPDATA(__reply_base) \ + container_of(__reply_base, struct linkmodes_reply_data, base) + +static const struct nla_policy +linkmodes_get_policy[ETHTOOL_A_LINKMODES_MAX + 1] = { + [ETHTOOL_A_LINKMODES_UNSPEC] = { .type = NLA_REJECT }, + [ETHTOOL_A_LINKMODES_HEADER] = { .type = NLA_NESTED }, + [ETHTOOL_A_LINKMODES_AUTONEG] = { .type = NLA_REJECT }, + [ETHTOOL_A_LINKMODES_OURS] = { .type = NLA_REJECT }, + [ETHTOOL_A_LINKMODES_PEER] = { .type = NLA_REJECT }, + [ETHTOOL_A_LINKMODES_SPEED] = { .type = NLA_REJECT }, + [ETHTOOL_A_LINKMODES_DUPLEX] = { .type = NLA_REJECT }, +}; + +static int linkmodes_prepare_data(const struct ethnl_req_info *req_base, + struct ethnl_reply_data *reply_base, + struct genl_info *info) +{ + struct linkmodes_reply_data *data = LINKMODES_REPDATA(reply_base); + struct net_device *dev = reply_base->dev; + int ret; + + data->lsettings = &data->ksettings.base; + + ret = ethnl_ops_begin(dev); + if (ret < 0) + return ret; + + ret = __ethtool_get_link_ksettings(dev, &data->ksettings); + if (ret < 0 && info) { + GENL_SET_ERR_MSG(info, "failed to retrieve link settings"); + goto out; + } + + data->peer_empty = + bitmap_empty(data->ksettings.link_modes.lp_advertising, + __ETHTOOL_LINK_MODE_MASK_NBITS); + +out: + ethnl_ops_complete(dev); + return ret; +} + +static int linkmodes_reply_size(const struct ethnl_req_info *req_base, + const struct ethnl_reply_data *reply_base) +{ + const struct linkmodes_reply_data *data = LINKMODES_REPDATA(reply_base); + const struct ethtool_link_ksettings *ksettings = &data->ksettings; + bool compact = req_base->flags & ETHTOOL_FLAG_COMPACT_BITSETS; + int len, ret; + + len = nla_total_size(sizeof(u8)) /* LINKMODES_AUTONEG */ + + nla_total_size(sizeof(u32)) /* LINKMODES_SPEED */ + + nla_total_size(sizeof(u8)) /* LINKMODES_DUPLEX */ + + 0; + ret = ethnl_bitset_size(ksettings->link_modes.advertising, + ksettings->link_modes.supported, + __ETHTOOL_LINK_MODE_MASK_NBITS, + link_mode_names, compact); + if (ret < 0) + return ret; + len += ret; + if (!data->peer_empty) { + ret = ethnl_bitset_size(ksettings->link_modes.lp_advertising, + NULL, __ETHTOOL_LINK_MODE_MASK_NBITS, + link_mode_names, compact); + if (ret < 0) + return ret; + len += ret; + } + + return len; +} + +static int linkmodes_fill_reply(struct sk_buff *skb, + const struct ethnl_req_info *req_base, + const struct ethnl_reply_data *reply_base) +{ + const struct linkmodes_reply_data *data = LINKMODES_REPDATA(reply_base); + const struct ethtool_link_ksettings *ksettings = &data->ksettings; + const struct ethtool_link_settings *lsettings = &ksettings->base; + bool compact = req_base->flags & ETHTOOL_FLAG_COMPACT_BITSETS; + int ret; + + if (nla_put_u8(skb, ETHTOOL_A_LINKMODES_AUTONEG, lsettings->autoneg)) + return -EMSGSIZE; + + ret = ethnl_put_bitset(skb, ETHTOOL_A_LINKMODES_OURS, + ksettings->link_modes.advertising, + ksettings->link_modes.supported, + __ETHTOOL_LINK_MODE_MASK_NBITS, link_mode_names, + compact); + if (ret < 0) + return -EMSGSIZE; + if (!data->peer_empty) { + ret = ethnl_put_bitset(skb, ETHTOOL_A_LINKMODES_PEER, + ksettings->link_modes.lp_advertising, + NULL, __ETHTOOL_LINK_MODE_MASK_NBITS, + link_mode_names, compact); + if (ret < 0) + return -EMSGSIZE; + } + + if (nla_put_u32(skb, ETHTOOL_A_LINKMODES_SPEED, lsettings->speed) || + nla_put_u8(skb, ETHTOOL_A_LINKMODES_DUPLEX, lsettings->duplex)) + return -EMSGSIZE; + + return 0; +} + +const struct ethnl_request_ops ethnl_linkmodes_request_ops = { + .request_cmd = ETHTOOL_MSG_LINKMODES_GET, + .reply_cmd = ETHTOOL_MSG_LINKMODES_GET_REPLY, + .hdr_attr = ETHTOOL_A_LINKMODES_HEADER, + .max_attr = ETHTOOL_A_LINKMODES_MAX, + .req_info_size = sizeof(struct linkmodes_req_info), + .reply_data_size = sizeof(struct linkmodes_reply_data), + .request_policy = linkmodes_get_policy, + + .prepare_data = linkmodes_prepare_data, + .reply_size = linkmodes_reply_size, + .fill_reply = linkmodes_fill_reply, +}; diff --git a/net/ethtool/netlink.c b/net/ethtool/netlink.c index 942da4ebdfe9e..703ff3a227a49 100644 --- a/net/ethtool/netlink.c +++ b/net/ethtool/netlink.c @@ -209,6 +209,7 @@ static const struct ethnl_request_ops * ethnl_default_requests[__ETHTOOL_MSG_USER_CNT] = { [ETHTOOL_MSG_STRSET_GET] = ðnl_strset_request_ops, [ETHTOOL_MSG_LINKINFO_GET] = ðnl_linkinfo_request_ops, + [ETHTOOL_MSG_LINKMODES_GET] = ðnl_linkmodes_request_ops, }; static struct ethnl_dump_ctx *ethnl_dump_context(struct netlink_callback *cb) @@ -630,6 +631,13 @@ static const struct genl_ops ethtool_genl_ops[] = { .flags = GENL_UNS_ADMIN_PERM, .doit = ethnl_set_linkinfo, }, + { + .cmd = ETHTOOL_MSG_LINKMODES_GET, + .doit = ethnl_default_doit, + .start = ethnl_default_start, + .dumpit = ethnl_default_dumpit, + .done = ethnl_default_done, + }, }; static const struct genl_multicast_group ethtool_nl_mcgrps[] = { diff --git a/net/ethtool/netlink.h b/net/ethtool/netlink.h index 5d56d7779a060..256d38972d1e5 100644 --- a/net/ethtool/netlink.h +++ b/net/ethtool/netlink.h @@ -332,6 +332,7 @@ struct ethnl_request_ops { extern const struct ethnl_request_ops ethnl_strset_request_ops; extern const struct ethnl_request_ops ethnl_linkinfo_request_ops; +extern const struct ethnl_request_ops ethnl_linkmodes_request_ops; int ethnl_set_linkinfo(struct sk_buff *skb, struct genl_info *info); -- GitLab From bfbcfe2032e70bd8598d680d39ac177d507e39ac Mon Sep 17 00:00:00 2001 From: Michal Kubecek Date: Fri, 27 Dec 2019 15:56:13 +0100 Subject: [PATCH 1397/1437] ethtool: set link modes related data with LINKMODES_SET request Implement LINKMODES_SET netlink request to set advertised linkmodes and related attributes as ETHTOOL_SLINKSETTINGS and ETHTOOL_SSET commands do. The request allows setting autonegotiation flag, speed, duplex and advertised link modes. Signed-off-by: Michal Kubecek Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller --- Documentation/networking/ethtool-netlink.rst | 27 +++ include/uapi/linux/ethtool_netlink.h | 1 + net/ethtool/linkmodes.c | 235 +++++++++++++++++++ net/ethtool/netlink.c | 5 + net/ethtool/netlink.h | 1 + 5 files changed, 269 insertions(+) diff --git a/Documentation/networking/ethtool-netlink.rst b/Documentation/networking/ethtool-netlink.rst index 04c55be0264ce..625c801835630 100644 --- a/Documentation/networking/ethtool-netlink.rst +++ b/Documentation/networking/ethtool-netlink.rst @@ -183,6 +183,7 @@ Userspace to kernel: ``ETHTOOL_MSG_LINKINFO_GET`` get link settings ``ETHTOOL_MSG_LINKINFO_SET`` set link settings ``ETHTOOL_MSG_LINKMODES_GET`` get link modes info + ``ETHTOOL_MSG_LINKMODES_SET`` set link modes info ===================================== ================================ Kernel to userspace: @@ -366,6 +367,30 @@ list. devices supporting the request). +LINKMODES_SET +============= + +Request contents: + + ==================================== ====== ========================== + ``ETHTOOL_A_LINKMODES_HEADER`` nested request header + ``ETHTOOL_A_LINKMODES_AUTONEG`` u8 autonegotiation status + ``ETHTOOL_A_LINKMODES_OURS`` bitset advertised link modes + ``ETHTOOL_A_LINKMODES_PEER`` bitset partner link modes + ``ETHTOOL_A_LINKMODES_SPEED`` u32 link speed (Mb/s) + ``ETHTOOL_A_LINKMODES_DUPLEX`` u8 duplex mode + ==================================== ====== ========================== + +``ETHTOOL_A_LINKMODES_OURS`` bit set allows setting advertised link modes. If +autonegotiation is on (either set now or kept from before), advertised modes +are not changed (no ``ETHTOOL_A_LINKMODES_OURS`` attribute) and at least one +of speed and duplex is specified, kernel adjusts advertised modes to all +supported modes matching speed, duplex or both (whatever is specified). This +autoselection is done on ethtool side with ioctl interface, netlink interface +is supposed to allow requesting changes without knowing what exactly kernel +supports. + + Request translation =================== @@ -379,6 +404,7 @@ have their netlink replacement yet. ``ETHTOOL_GSET`` ``ETHTOOL_MSG_LINKINFO_GET`` ``ETHTOOL_MSG_LINKMODES_GET`` ``ETHTOOL_SSET`` ``ETHTOOL_MSG_LINKINFO_SET`` + ``ETHTOOL_MSG_LINKMODES_SET`` ``ETHTOOL_GDRVINFO`` n/a ``ETHTOOL_GREGS`` n/a ``ETHTOOL_GWOL`` n/a @@ -454,6 +480,7 @@ have their netlink replacement yet. ``ETHTOOL_GLINKSETTINGS`` ``ETHTOOL_MSG_LINKINFO_GET`` ``ETHTOOL_MSG_LINKMODES_GET`` ``ETHTOOL_SLINKSETTINGS`` ``ETHTOOL_MSG_LINKINFO_SET`` + ``ETHTOOL_MSG_LINKMODES_SET`` ``ETHTOOL_PHY_GTUNABLE`` n/a ``ETHTOOL_PHY_STUNABLE`` n/a ``ETHTOOL_GFECPARAM`` n/a diff --git a/include/uapi/linux/ethtool_netlink.h b/include/uapi/linux/ethtool_netlink.h index dc1cae052eeef..cddf978b98df1 100644 --- a/include/uapi/linux/ethtool_netlink.h +++ b/include/uapi/linux/ethtool_netlink.h @@ -18,6 +18,7 @@ enum { ETHTOOL_MSG_LINKINFO_GET, ETHTOOL_MSG_LINKINFO_SET, ETHTOOL_MSG_LINKMODES_GET, + ETHTOOL_MSG_LINKMODES_SET, /* add new constants above here */ __ETHTOOL_MSG_USER_CNT, diff --git a/net/ethtool/linkmodes.c b/net/ethtool/linkmodes.c index 81856fa1e6322..790b60771d0ea 100644 --- a/net/ethtool/linkmodes.c +++ b/net/ethtool/linkmodes.c @@ -138,3 +138,238 @@ const struct ethnl_request_ops ethnl_linkmodes_request_ops = { .reply_size = linkmodes_reply_size, .fill_reply = linkmodes_fill_reply, }; + +/* LINKMODES_SET */ + +struct link_mode_info { + int speed; + u8 duplex; +}; + +#define __DEFINE_LINK_MODE_PARAMS(_speed, _type, _duplex) \ + [ETHTOOL_LINK_MODE(_speed, _type, _duplex)] = { \ + .speed = SPEED_ ## _speed, \ + .duplex = __DUPLEX_ ## _duplex \ + } +#define __DUPLEX_Half DUPLEX_HALF +#define __DUPLEX_Full DUPLEX_FULL +#define __DEFINE_SPECIAL_MODE_PARAMS(_mode) \ + [ETHTOOL_LINK_MODE_ ## _mode ## _BIT] = { \ + .speed = SPEED_UNKNOWN, \ + .duplex = DUPLEX_UNKNOWN, \ + } + +static const struct link_mode_info link_mode_params[] = { + __DEFINE_LINK_MODE_PARAMS(10, T, Half), + __DEFINE_LINK_MODE_PARAMS(10, T, Full), + __DEFINE_LINK_MODE_PARAMS(100, T, Half), + __DEFINE_LINK_MODE_PARAMS(100, T, Full), + __DEFINE_LINK_MODE_PARAMS(1000, T, Half), + __DEFINE_LINK_MODE_PARAMS(1000, T, Full), + __DEFINE_SPECIAL_MODE_PARAMS(Autoneg), + __DEFINE_SPECIAL_MODE_PARAMS(TP), + __DEFINE_SPECIAL_MODE_PARAMS(AUI), + __DEFINE_SPECIAL_MODE_PARAMS(MII), + __DEFINE_SPECIAL_MODE_PARAMS(FIBRE), + __DEFINE_SPECIAL_MODE_PARAMS(BNC), + __DEFINE_LINK_MODE_PARAMS(10000, T, Full), + __DEFINE_SPECIAL_MODE_PARAMS(Pause), + __DEFINE_SPECIAL_MODE_PARAMS(Asym_Pause), + __DEFINE_LINK_MODE_PARAMS(2500, X, Full), + __DEFINE_SPECIAL_MODE_PARAMS(Backplane), + __DEFINE_LINK_MODE_PARAMS(1000, KX, Full), + __DEFINE_LINK_MODE_PARAMS(10000, KX4, Full), + __DEFINE_LINK_MODE_PARAMS(10000, KR, Full), + [ETHTOOL_LINK_MODE_10000baseR_FEC_BIT] = { + .speed = SPEED_10000, + .duplex = DUPLEX_FULL, + }, + __DEFINE_LINK_MODE_PARAMS(20000, MLD2, Full), + __DEFINE_LINK_MODE_PARAMS(20000, KR2, Full), + __DEFINE_LINK_MODE_PARAMS(40000, KR4, Full), + __DEFINE_LINK_MODE_PARAMS(40000, CR4, Full), + __DEFINE_LINK_MODE_PARAMS(40000, SR4, Full), + __DEFINE_LINK_MODE_PARAMS(40000, LR4, Full), + __DEFINE_LINK_MODE_PARAMS(56000, KR4, Full), + __DEFINE_LINK_MODE_PARAMS(56000, CR4, Full), + __DEFINE_LINK_MODE_PARAMS(56000, SR4, Full), + __DEFINE_LINK_MODE_PARAMS(56000, LR4, Full), + __DEFINE_LINK_MODE_PARAMS(25000, CR, Full), + __DEFINE_LINK_MODE_PARAMS(25000, KR, Full), + __DEFINE_LINK_MODE_PARAMS(25000, SR, Full), + __DEFINE_LINK_MODE_PARAMS(50000, CR2, Full), + __DEFINE_LINK_MODE_PARAMS(50000, KR2, Full), + __DEFINE_LINK_MODE_PARAMS(100000, KR4, Full), + __DEFINE_LINK_MODE_PARAMS(100000, SR4, Full), + __DEFINE_LINK_MODE_PARAMS(100000, CR4, Full), + __DEFINE_LINK_MODE_PARAMS(100000, LR4_ER4, Full), + __DEFINE_LINK_MODE_PARAMS(50000, SR2, Full), + __DEFINE_LINK_MODE_PARAMS(1000, X, Full), + __DEFINE_LINK_MODE_PARAMS(10000, CR, Full), + __DEFINE_LINK_MODE_PARAMS(10000, SR, Full), + __DEFINE_LINK_MODE_PARAMS(10000, LR, Full), + __DEFINE_LINK_MODE_PARAMS(10000, LRM, Full), + __DEFINE_LINK_MODE_PARAMS(10000, ER, Full), + __DEFINE_LINK_MODE_PARAMS(2500, T, Full), + __DEFINE_LINK_MODE_PARAMS(5000, T, Full), + __DEFINE_SPECIAL_MODE_PARAMS(FEC_NONE), + __DEFINE_SPECIAL_MODE_PARAMS(FEC_RS), + __DEFINE_SPECIAL_MODE_PARAMS(FEC_BASER), + __DEFINE_LINK_MODE_PARAMS(50000, KR, Full), + __DEFINE_LINK_MODE_PARAMS(50000, SR, Full), + __DEFINE_LINK_MODE_PARAMS(50000, CR, Full), + __DEFINE_LINK_MODE_PARAMS(50000, LR_ER_FR, Full), + __DEFINE_LINK_MODE_PARAMS(50000, DR, Full), + __DEFINE_LINK_MODE_PARAMS(100000, KR2, Full), + __DEFINE_LINK_MODE_PARAMS(100000, SR2, Full), + __DEFINE_LINK_MODE_PARAMS(100000, CR2, Full), + __DEFINE_LINK_MODE_PARAMS(100000, LR2_ER2_FR2, Full), + __DEFINE_LINK_MODE_PARAMS(100000, DR2, Full), + __DEFINE_LINK_MODE_PARAMS(200000, KR4, Full), + __DEFINE_LINK_MODE_PARAMS(200000, SR4, Full), + __DEFINE_LINK_MODE_PARAMS(200000, LR4_ER4_FR4, Full), + __DEFINE_LINK_MODE_PARAMS(200000, DR4, Full), + __DEFINE_LINK_MODE_PARAMS(200000, CR4, Full), + __DEFINE_LINK_MODE_PARAMS(100, T1, Full), + __DEFINE_LINK_MODE_PARAMS(1000, T1, Full), + __DEFINE_LINK_MODE_PARAMS(400000, KR8, Full), + __DEFINE_LINK_MODE_PARAMS(400000, SR8, Full), + __DEFINE_LINK_MODE_PARAMS(400000, LR8_ER8_FR8, Full), + __DEFINE_LINK_MODE_PARAMS(400000, DR8, Full), + __DEFINE_LINK_MODE_PARAMS(400000, CR8, Full), +}; + +static const struct nla_policy +linkmodes_set_policy[ETHTOOL_A_LINKMODES_MAX + 1] = { + [ETHTOOL_A_LINKMODES_UNSPEC] = { .type = NLA_REJECT }, + [ETHTOOL_A_LINKMODES_HEADER] = { .type = NLA_NESTED }, + [ETHTOOL_A_LINKMODES_AUTONEG] = { .type = NLA_U8 }, + [ETHTOOL_A_LINKMODES_OURS] = { .type = NLA_NESTED }, + [ETHTOOL_A_LINKMODES_PEER] = { .type = NLA_REJECT }, + [ETHTOOL_A_LINKMODES_SPEED] = { .type = NLA_U32 }, + [ETHTOOL_A_LINKMODES_DUPLEX] = { .type = NLA_U8 }, +}; + +/* Set advertised link modes to all supported modes matching requested speed + * and duplex values. Called when autonegotiation is on, speed or duplex is + * requested but no link mode change. This is done in userspace with ioctl() + * interface, move it into kernel for netlink. + * Returns true if advertised modes bitmap was modified. + */ +static bool ethnl_auto_linkmodes(struct ethtool_link_ksettings *ksettings, + bool req_speed, bool req_duplex) +{ + unsigned long *advertising = ksettings->link_modes.advertising; + unsigned long *supported = ksettings->link_modes.supported; + DECLARE_BITMAP(old_adv, __ETHTOOL_LINK_MODE_MASK_NBITS); + unsigned int i; + + BUILD_BUG_ON(ARRAY_SIZE(link_mode_params) != + __ETHTOOL_LINK_MODE_MASK_NBITS); + + bitmap_copy(old_adv, advertising, __ETHTOOL_LINK_MODE_MASK_NBITS); + + for (i = 0; i < __ETHTOOL_LINK_MODE_MASK_NBITS; i++) { + const struct link_mode_info *info = &link_mode_params[i]; + + if (info->speed == SPEED_UNKNOWN) + continue; + if (test_bit(i, supported) && + (!req_speed || info->speed == ksettings->base.speed) && + (!req_duplex || info->duplex == ksettings->base.duplex)) + set_bit(i, advertising); + else + clear_bit(i, advertising); + } + + return !bitmap_equal(old_adv, advertising, + __ETHTOOL_LINK_MODE_MASK_NBITS); +} + +static int ethnl_update_linkmodes(struct genl_info *info, struct nlattr **tb, + struct ethtool_link_ksettings *ksettings, + bool *mod) +{ + struct ethtool_link_settings *lsettings = &ksettings->base; + bool req_speed, req_duplex; + int ret; + + *mod = false; + req_speed = tb[ETHTOOL_A_LINKMODES_SPEED]; + req_duplex = tb[ETHTOOL_A_LINKMODES_DUPLEX]; + + ethnl_update_u8(&lsettings->autoneg, tb[ETHTOOL_A_LINKMODES_AUTONEG], + mod); + ret = ethnl_update_bitset(ksettings->link_modes.advertising, + __ETHTOOL_LINK_MODE_MASK_NBITS, + tb[ETHTOOL_A_LINKMODES_OURS], link_mode_names, + info->extack, mod); + if (ret < 0) + return ret; + ethnl_update_u32(&lsettings->speed, tb[ETHTOOL_A_LINKMODES_SPEED], + mod); + ethnl_update_u8(&lsettings->duplex, tb[ETHTOOL_A_LINKMODES_DUPLEX], + mod); + + if (!tb[ETHTOOL_A_LINKMODES_OURS] && lsettings->autoneg && + (req_speed || req_duplex) && + ethnl_auto_linkmodes(ksettings, req_speed, req_duplex)) + *mod = true; + + return 0; +} + +int ethnl_set_linkmodes(struct sk_buff *skb, struct genl_info *info) +{ + struct nlattr *tb[ETHTOOL_A_LINKMODES_MAX + 1]; + struct ethtool_link_ksettings ksettings = {}; + struct ethtool_link_settings *lsettings; + struct ethnl_req_info req_info = {}; + struct net_device *dev; + bool mod = false; + int ret; + + ret = nlmsg_parse(info->nlhdr, GENL_HDRLEN, tb, + ETHTOOL_A_LINKMODES_MAX, linkmodes_set_policy, + info->extack); + if (ret < 0) + return ret; + ret = ethnl_parse_header(&req_info, tb[ETHTOOL_A_LINKMODES_HEADER], + genl_info_net(info), info->extack, true); + if (ret < 0) + return ret; + dev = req_info.dev; + if (!dev->ethtool_ops->get_link_ksettings || + !dev->ethtool_ops->set_link_ksettings) + return -EOPNOTSUPP; + + rtnl_lock(); + ret = ethnl_ops_begin(dev); + if (ret < 0) + goto out_rtnl; + + ret = __ethtool_get_link_ksettings(dev, &ksettings); + if (ret < 0) { + if (info) + GENL_SET_ERR_MSG(info, "failed to retrieve link settings"); + goto out_ops; + } + lsettings = &ksettings.base; + + ret = ethnl_update_linkmodes(info, tb, &ksettings, &mod); + if (ret < 0) + goto out_ops; + + if (mod) { + ret = dev->ethtool_ops->set_link_ksettings(dev, &ksettings); + if (ret < 0) + GENL_SET_ERR_MSG(info, "link settings update failed"); + } + +out_ops: + ethnl_ops_complete(dev); +out_rtnl: + rtnl_unlock(); + dev_put(dev); + return ret; +} diff --git a/net/ethtool/netlink.c b/net/ethtool/netlink.c index 703ff3a227a49..5f28f3cb022d9 100644 --- a/net/ethtool/netlink.c +++ b/net/ethtool/netlink.c @@ -638,6 +638,11 @@ static const struct genl_ops ethtool_genl_ops[] = { .dumpit = ethnl_default_dumpit, .done = ethnl_default_done, }, + { + .cmd = ETHTOOL_MSG_LINKMODES_SET, + .flags = GENL_UNS_ADMIN_PERM, + .doit = ethnl_set_linkmodes, + }, }; static const struct genl_multicast_group ethtool_nl_mcgrps[] = { diff --git a/net/ethtool/netlink.h b/net/ethtool/netlink.h index 256d38972d1e5..1269cca8a0020 100644 --- a/net/ethtool/netlink.h +++ b/net/ethtool/netlink.h @@ -335,5 +335,6 @@ extern const struct ethnl_request_ops ethnl_linkinfo_request_ops; extern const struct ethnl_request_ops ethnl_linkmodes_request_ops; int ethnl_set_linkinfo(struct sk_buff *skb, struct genl_info *info); +int ethnl_set_linkmodes(struct sk_buff *skb, struct genl_info *info); #endif /* _NET_ETHTOOL_NETLINK_H */ -- GitLab From 1b1b1847c8505df2e1dd8804838526bed22a8bd4 Mon Sep 17 00:00:00 2001 From: Michal Kubecek Date: Fri, 27 Dec 2019 15:56:18 +0100 Subject: [PATCH 1398/1437] ethtool: add LINKMODES_NTF notification Send ETHTOOL_MSG_LINKMODES_NTF notification message whenever device link settings or advertised modes are modified using ETHTOOL_MSG_LINKMODES_SET netlink message or ETHTOOL_SLINKSETTINGS or ETHTOOL_SSET ioctl commands. The notification message has the same format as reply to LINKMODES_GET request. ETHTOOL_MSG_LINKMODES_SET netlink request only triggers the notification if there is a change but the ioctl command handlers do not check if there is an actual change and trigger the notification whenever the commands are executed. As all work is done by ethnl_default_notify() handler and callback functions introduced to handle LINKMODES_GET requests, all that remains is adding entries for ETHTOOL_MSG_LINKMODES_NTF into ethnl_notify_handlers and ethnl_default_notify_ops lookup tables and calls to ethtool_notify() where needed. Signed-off-by: Michal Kubecek Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller --- Documentation/networking/ethtool-netlink.rst | 1 + include/uapi/linux/ethtool_netlink.h | 1 + net/ethtool/ioctl.c | 8 ++++++-- net/ethtool/linkmodes.c | 2 ++ net/ethtool/netlink.c | 2 ++ 5 files changed, 12 insertions(+), 2 deletions(-) diff --git a/Documentation/networking/ethtool-netlink.rst b/Documentation/networking/ethtool-netlink.rst index 625c801835630..9d96d51e93605 100644 --- a/Documentation/networking/ethtool-netlink.rst +++ b/Documentation/networking/ethtool-netlink.rst @@ -193,6 +193,7 @@ Kernel to userspace: ``ETHTOOL_MSG_LINKINFO_GET_REPLY`` link settings ``ETHTOOL_MSG_LINKINFO_NTF`` link settings notification ``ETHTOOL_MSG_LINKMODES_GET_REPLY`` link modes info + ``ETHTOOL_MSG_LINKMODES_NTF`` link modes notification ===================================== ================================ ``GET`` requests are sent by userspace applications to retrieve device diff --git a/include/uapi/linux/ethtool_netlink.h b/include/uapi/linux/ethtool_netlink.h index cddf978b98df1..35948df6d6e3a 100644 --- a/include/uapi/linux/ethtool_netlink.h +++ b/include/uapi/linux/ethtool_netlink.h @@ -32,6 +32,7 @@ enum { ETHTOOL_MSG_LINKINFO_GET_REPLY, ETHTOOL_MSG_LINKINFO_NTF, ETHTOOL_MSG_LINKMODES_GET_REPLY, + ETHTOOL_MSG_LINKMODES_NTF, /* add new constants above here */ __ETHTOOL_MSG_KERNEL_CNT, diff --git a/net/ethtool/ioctl.c b/net/ethtool/ioctl.c index 11a467294a332..36e2ef2d900d9 100644 --- a/net/ethtool/ioctl.c +++ b/net/ethtool/ioctl.c @@ -573,8 +573,10 @@ static int ethtool_set_link_ksettings(struct net_device *dev, return -EINVAL; err = dev->ethtool_ops->set_link_ksettings(dev, &link_ksettings); - if (err >= 0) + if (err >= 0) { ethtool_notify(dev, ETHTOOL_MSG_LINKINFO_NTF, NULL); + ethtool_notify(dev, ETHTOOL_MSG_LINKMODES_NTF, NULL); + } return err; } @@ -638,8 +640,10 @@ static int ethtool_set_settings(struct net_device *dev, void __user *useraddr) link_ksettings.base.link_mode_masks_nwords = __ETHTOOL_LINK_MODE_MASK_NU32; ret = dev->ethtool_ops->set_link_ksettings(dev, &link_ksettings); - if (ret >= 0) + if (ret >= 0) { ethtool_notify(dev, ETHTOOL_MSG_LINKINFO_NTF, NULL); + ethtool_notify(dev, ETHTOOL_MSG_LINKMODES_NTF, NULL); + } return ret; } diff --git a/net/ethtool/linkmodes.c b/net/ethtool/linkmodes.c index 790b60771d0ea..0b99f494ad3b6 100644 --- a/net/ethtool/linkmodes.c +++ b/net/ethtool/linkmodes.c @@ -364,6 +364,8 @@ int ethnl_set_linkmodes(struct sk_buff *skb, struct genl_info *info) ret = dev->ethtool_ops->set_link_ksettings(dev, &ksettings); if (ret < 0) GENL_SET_ERR_MSG(info, "link settings update failed"); + else + ethtool_notify(dev, ETHTOOL_MSG_LINKMODES_NTF, NULL); } out_ops: diff --git a/net/ethtool/netlink.c b/net/ethtool/netlink.c index 5f28f3cb022d9..1b5e1bd265042 100644 --- a/net/ethtool/netlink.c +++ b/net/ethtool/netlink.c @@ -511,6 +511,7 @@ static int ethnl_default_done(struct netlink_callback *cb) static const struct ethnl_request_ops * ethnl_default_notify_ops[ETHTOOL_MSG_KERNEL_MAX + 1] = { [ETHTOOL_MSG_LINKINFO_NTF] = ðnl_linkinfo_request_ops, + [ETHTOOL_MSG_LINKMODES_NTF] = ðnl_linkmodes_request_ops, }; /* default notification handler */ @@ -592,6 +593,7 @@ typedef void (*ethnl_notify_handler_t)(struct net_device *dev, unsigned int cmd, static const ethnl_notify_handler_t ethnl_notify_handlers[] = { [ETHTOOL_MSG_LINKINFO_NTF] = ethnl_default_notify, + [ETHTOOL_MSG_LINKMODES_NTF] = ethnl_default_notify, }; void ethtool_notify(struct net_device *dev, unsigned int cmd, const void *data) -- GitLab From 3d2b847fb99cf2b28aa046e486636e555bc6ed1c Mon Sep 17 00:00:00 2001 From: Michal Kubecek Date: Fri, 27 Dec 2019 15:56:23 +0100 Subject: [PATCH 1399/1437] ethtool: provide link state with LINKSTATE_GET request Implement LINKSTATE_GET netlink request to get link state information. At the moment, only link up flag as provided by ETHTOOL_GLINK ioctl command is returned. LINKSTATE_GET request can be used with NLM_F_DUMP (without device identification) to request the information for all devices in current network namespace providing the data. Signed-off-by: Michal Kubecek Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller --- Documentation/networking/ethtool-netlink.rst | 33 ++++++++- include/uapi/linux/ethtool_netlink.h | 14 ++++ net/ethtool/Makefile | 3 +- net/ethtool/common.c | 8 +++ net/ethtool/common.h | 3 + net/ethtool/ioctl.c | 8 +-- net/ethtool/linkstate.c | 74 ++++++++++++++++++++ net/ethtool/netlink.c | 8 +++ net/ethtool/netlink.h | 1 + 9 files changed, 146 insertions(+), 6 deletions(-) create mode 100644 net/ethtool/linkstate.c diff --git a/Documentation/networking/ethtool-netlink.rst b/Documentation/networking/ethtool-netlink.rst index 9d96d51e93605..c60afba69e3ca 100644 --- a/Documentation/networking/ethtool-netlink.rst +++ b/Documentation/networking/ethtool-netlink.rst @@ -184,6 +184,7 @@ Userspace to kernel: ``ETHTOOL_MSG_LINKINFO_SET`` set link settings ``ETHTOOL_MSG_LINKMODES_GET`` get link modes info ``ETHTOOL_MSG_LINKMODES_SET`` set link modes info + ``ETHTOOL_MSG_LINKSTATE_GET`` get link state ===================================== ================================ Kernel to userspace: @@ -194,6 +195,7 @@ Kernel to userspace: ``ETHTOOL_MSG_LINKINFO_NTF`` link settings notification ``ETHTOOL_MSG_LINKMODES_GET_REPLY`` link modes info ``ETHTOOL_MSG_LINKMODES_NTF`` link modes notification + ``ETHTOOL_MSG_LINKSTATE_GET_REPLY`` link state info ===================================== ================================ ``GET`` requests are sent by userspace applications to retrieve device @@ -392,6 +394,35 @@ is supposed to allow requesting changes without knowing what exactly kernel supports. +LINKSTATE_GET +============= + +Requests link state information. At the moment, only link up/down flag (as +provided by ``ETHTOOL_GLINK`` ioctl command) is provided but some future +extensions are planned (e.g. link down reason). This request does not have any +attributes. + +Request contents: + + ==================================== ====== ========================== + ``ETHTOOL_A_LINKSTATE_HEADER`` nested request header + ==================================== ====== ========================== + +Kernel response contents: + + ==================================== ====== ========================== + ``ETHTOOL_A_LINKSTATE_HEADER`` nested reply header + ``ETHTOOL_A_LINKSTATE_LINK`` bool link state (up/down) + ==================================== ====== ========================== + +For most NIC drivers, the value of ``ETHTOOL_A_LINKSTATE_LINK`` returns +carrier flag provided by ``netif_carrier_ok()`` but there are drivers which +define their own handler. + +``LINKSTATE_GET`` allows dump requests (kernel returns reply messages for all +devices supporting the request). + + Request translation =================== @@ -413,7 +444,7 @@ have their netlink replacement yet. ``ETHTOOL_GMSGLVL`` n/a ``ETHTOOL_SMSGLVL`` n/a ``ETHTOOL_NWAY_RST`` n/a - ``ETHTOOL_GLINK`` n/a + ``ETHTOOL_GLINK`` ``ETHTOOL_MSG_LINKSTATE_GET`` ``ETHTOOL_GEEPROM`` n/a ``ETHTOOL_SEEPROM`` n/a ``ETHTOOL_GCOALESCE`` n/a diff --git a/include/uapi/linux/ethtool_netlink.h b/include/uapi/linux/ethtool_netlink.h index 35948df6d6e3a..02f82f42a8897 100644 --- a/include/uapi/linux/ethtool_netlink.h +++ b/include/uapi/linux/ethtool_netlink.h @@ -19,6 +19,7 @@ enum { ETHTOOL_MSG_LINKINFO_SET, ETHTOOL_MSG_LINKMODES_GET, ETHTOOL_MSG_LINKMODES_SET, + ETHTOOL_MSG_LINKSTATE_GET, /* add new constants above here */ __ETHTOOL_MSG_USER_CNT, @@ -33,6 +34,7 @@ enum { ETHTOOL_MSG_LINKINFO_NTF, ETHTOOL_MSG_LINKMODES_GET_REPLY, ETHTOOL_MSG_LINKMODES_NTF, + ETHTOOL_MSG_LINKSTATE_GET_REPLY, /* add new constants above here */ __ETHTOOL_MSG_KERNEL_CNT, @@ -181,6 +183,18 @@ enum { ETHTOOL_A_LINKMODES_MAX = __ETHTOOL_A_LINKMODES_CNT - 1 }; +/* LINKSTATE */ + +enum { + ETHTOOL_A_LINKSTATE_UNSPEC, + ETHTOOL_A_LINKSTATE_HEADER, /* nest - _A_HEADER_* */ + ETHTOOL_A_LINKSTATE_LINK, /* u8 */ + + /* add new constants above here */ + __ETHTOOL_A_LINKSTATE_CNT, + ETHTOOL_A_LINKSTATE_MAX = __ETHTOOL_A_LINKSTATE_CNT - 1 +}; + /* generic netlink info */ #define ETHTOOL_GENL_NAME "ethtool" #define ETHTOOL_GENL_VERSION 1 diff --git a/net/ethtool/Makefile b/net/ethtool/Makefile index 8023da6672cea..9a1332fb0cc6d 100644 --- a/net/ethtool/Makefile +++ b/net/ethtool/Makefile @@ -4,4 +4,5 @@ obj-y += ioctl.o common.o obj-$(CONFIG_ETHTOOL_NETLINK) += ethtool_nl.o -ethtool_nl-y := netlink.o bitset.o strset.o linkinfo.o linkmodes.o +ethtool_nl-y := netlink.o bitset.o strset.o linkinfo.o linkmodes.o \ + linkstate.o diff --git a/net/ethtool/common.c b/net/ethtool/common.c index 1d4a0aeff2cb7..e621b1694d2fa 100644 --- a/net/ethtool/common.c +++ b/net/ethtool/common.c @@ -217,3 +217,11 @@ convert_legacy_settings_to_link_ksettings( = legacy_settings->eth_tp_mdix_ctrl; return retval; } + +int __ethtool_get_link(struct net_device *dev) +{ + if (!dev->ethtool_ops->get_link) + return -EOPNOTSUPP; + + return netif_running(dev) && dev->ethtool_ops->get_link(dev); +} diff --git a/net/ethtool/common.h b/net/ethtool/common.h index c8a2374027298..5c5f7dc90cd44 100644 --- a/net/ethtool/common.h +++ b/net/ethtool/common.h @@ -3,6 +3,7 @@ #ifndef _ETHTOOL_COMMON_H #define _ETHTOOL_COMMON_H +#include #include /* compose link mode index from speed, type and duplex */ @@ -19,6 +20,8 @@ extern const char phy_tunable_strings[__ETHTOOL_PHY_TUNABLE_COUNT][ETH_GSTRING_LEN]; extern const char link_mode_names[][ETH_GSTRING_LEN]; +int __ethtool_get_link(struct net_device *dev); + bool convert_legacy_settings_to_link_ksettings( struct ethtool_link_ksettings *link_ksettings, const struct ethtool_cmd *legacy_settings); diff --git a/net/ethtool/ioctl.c b/net/ethtool/ioctl.c index 36e2ef2d900d9..182bffbffa783 100644 --- a/net/ethtool/ioctl.c +++ b/net/ethtool/ioctl.c @@ -1365,12 +1365,12 @@ static int ethtool_nway_reset(struct net_device *dev) static int ethtool_get_link(struct net_device *dev, char __user *useraddr) { struct ethtool_value edata = { .cmd = ETHTOOL_GLINK }; + int link = __ethtool_get_link(dev); - if (!dev->ethtool_ops->get_link) - return -EOPNOTSUPP; - - edata.data = netif_running(dev) && dev->ethtool_ops->get_link(dev); + if (link < 0) + return link; + edata.data = link; if (copy_to_user(useraddr, &edata, sizeof(edata))) return -EFAULT; return 0; diff --git a/net/ethtool/linkstate.c b/net/ethtool/linkstate.c new file mode 100644 index 0000000000000..2740cde0a182b --- /dev/null +++ b/net/ethtool/linkstate.c @@ -0,0 +1,74 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include "netlink.h" +#include "common.h" + +struct linkstate_req_info { + struct ethnl_req_info base; +}; + +struct linkstate_reply_data { + struct ethnl_reply_data base; + int link; +}; + +#define LINKSTATE_REPDATA(__reply_base) \ + container_of(__reply_base, struct linkstate_reply_data, base) + +static const struct nla_policy +linkstate_get_policy[ETHTOOL_A_LINKSTATE_MAX + 1] = { + [ETHTOOL_A_LINKSTATE_UNSPEC] = { .type = NLA_REJECT }, + [ETHTOOL_A_LINKSTATE_HEADER] = { .type = NLA_NESTED }, + [ETHTOOL_A_LINKSTATE_LINK] = { .type = NLA_REJECT }, +}; + +static int linkstate_prepare_data(const struct ethnl_req_info *req_base, + struct ethnl_reply_data *reply_base, + struct genl_info *info) +{ + struct linkstate_reply_data *data = LINKSTATE_REPDATA(reply_base); + struct net_device *dev = reply_base->dev; + int ret; + + ret = ethnl_ops_begin(dev); + if (ret < 0) + return ret; + data->link = __ethtool_get_link(dev); + ethnl_ops_complete(dev); + + return 0; +} + +static int linkstate_reply_size(const struct ethnl_req_info *req_base, + const struct ethnl_reply_data *reply_base) +{ + return nla_total_size(sizeof(u8)) /* LINKSTATE_LINK */ + + 0; +} + +static int linkstate_fill_reply(struct sk_buff *skb, + const struct ethnl_req_info *req_base, + const struct ethnl_reply_data *reply_base) +{ + struct linkstate_reply_data *data = LINKSTATE_REPDATA(reply_base); + + if (data->link >= 0 && + nla_put_u8(skb, ETHTOOL_A_LINKSTATE_LINK, !!data->link)) + return -EMSGSIZE; + + return 0; +} + +const struct ethnl_request_ops ethnl_linkstate_request_ops = { + .request_cmd = ETHTOOL_MSG_LINKSTATE_GET, + .reply_cmd = ETHTOOL_MSG_LINKSTATE_GET_REPLY, + .hdr_attr = ETHTOOL_A_LINKSTATE_HEADER, + .max_attr = ETHTOOL_A_LINKSTATE_MAX, + .req_info_size = sizeof(struct linkstate_req_info), + .reply_data_size = sizeof(struct linkstate_reply_data), + .request_policy = linkstate_get_policy, + + .prepare_data = linkstate_prepare_data, + .reply_size = linkstate_reply_size, + .fill_reply = linkstate_fill_reply, +}; diff --git a/net/ethtool/netlink.c b/net/ethtool/netlink.c index 1b5e1bd265042..4ca96c7b86b3c 100644 --- a/net/ethtool/netlink.c +++ b/net/ethtool/netlink.c @@ -210,6 +210,7 @@ ethnl_default_requests[__ETHTOOL_MSG_USER_CNT] = { [ETHTOOL_MSG_STRSET_GET] = ðnl_strset_request_ops, [ETHTOOL_MSG_LINKINFO_GET] = ðnl_linkinfo_request_ops, [ETHTOOL_MSG_LINKMODES_GET] = ðnl_linkmodes_request_ops, + [ETHTOOL_MSG_LINKSTATE_GET] = ðnl_linkstate_request_ops, }; static struct ethnl_dump_ctx *ethnl_dump_context(struct netlink_callback *cb) @@ -645,6 +646,13 @@ static const struct genl_ops ethtool_genl_ops[] = { .flags = GENL_UNS_ADMIN_PERM, .doit = ethnl_set_linkmodes, }, + { + .cmd = ETHTOOL_MSG_LINKSTATE_GET, + .doit = ethnl_default_doit, + .start = ethnl_default_start, + .dumpit = ethnl_default_dumpit, + .done = ethnl_default_done, + }, }; static const struct genl_multicast_group ethtool_nl_mcgrps[] = { diff --git a/net/ethtool/netlink.h b/net/ethtool/netlink.h index 1269cca8a0020..da9d6521a4ebf 100644 --- a/net/ethtool/netlink.h +++ b/net/ethtool/netlink.h @@ -333,6 +333,7 @@ struct ethnl_request_ops { extern const struct ethnl_request_ops ethnl_strset_request_ops; extern const struct ethnl_request_ops ethnl_linkinfo_request_ops; extern const struct ethnl_request_ops ethnl_linkmodes_request_ops; +extern const struct ethnl_request_ops ethnl_linkstate_request_ops; int ethnl_set_linkinfo(struct sk_buff *skb, struct genl_info *info); int ethnl_set_linkmodes(struct sk_buff *skb, struct genl_info *info); -- GitLab From 4d47ce158efb7b145c680e79cd33e3c6fd773de4 Mon Sep 17 00:00:00 2001 From: Luc Van Oostenryck Date: Sun, 22 Dec 2019 10:26:04 +0100 Subject: [PATCH 1400/1437] riscv: fix compile failure with EXPORT_SYMBOL() & !MMU When support for !MMU was added, the declaration of __asm_copy_to_user() & __asm_copy_from_user() were #ifdefed out hence their EXPORT_SYMBOL() give an error message like: .../riscv_ksyms.c:13:15: error: '__asm_copy_to_user' undeclared here .../riscv_ksyms.c:14:15: error: '__asm_copy_from_user' undeclared here Since these symbols are not defined with !MMU it's wrong to export them. Same for __clear_user() (even though this one is also declared in include/asm-generic/uaccess.h and thus doesn't give an error message). Fix this by doing the EXPORT_SYMBOL() directly where these symbols are defined: inside lib/uaccess.S itself. Fixes: 6bd33e1ece52 ("riscv: fix compile failure with EXPORT_SYMBOL() & !MMU") Reported-by: kbuild test robot Cc: Christoph Hellwig Cc: Palmer Dabbelt Cc: Paul Walmsley Signed-off-by: Luc Van Oostenryck Signed-off-by: Paul Walmsley --- arch/riscv/kernel/riscv_ksyms.c | 3 --- arch/riscv/lib/uaccess.S | 4 ++++ 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/arch/riscv/kernel/riscv_ksyms.c b/arch/riscv/kernel/riscv_ksyms.c index 4800cf703186d..2a02b7eebee00 100644 --- a/arch/riscv/kernel/riscv_ksyms.c +++ b/arch/riscv/kernel/riscv_ksyms.c @@ -9,8 +9,5 @@ /* * Assembly functions that may be used (directly or indirectly) by modules */ -EXPORT_SYMBOL(__clear_user); -EXPORT_SYMBOL(__asm_copy_to_user); -EXPORT_SYMBOL(__asm_copy_from_user); EXPORT_SYMBOL(memset); EXPORT_SYMBOL(memcpy); diff --git a/arch/riscv/lib/uaccess.S b/arch/riscv/lib/uaccess.S index fecd65657a6fc..f29d2ba2c0a6c 100644 --- a/arch/riscv/lib/uaccess.S +++ b/arch/riscv/lib/uaccess.S @@ -1,4 +1,5 @@ #include +#include #include #include @@ -66,6 +67,8 @@ ENTRY(__asm_copy_from_user) j 3b ENDPROC(__asm_copy_to_user) ENDPROC(__asm_copy_from_user) +EXPORT_SYMBOL(__asm_copy_to_user) +EXPORT_SYMBOL(__asm_copy_from_user) ENTRY(__clear_user) @@ -108,6 +111,7 @@ ENTRY(__clear_user) bltu a0, a3, 5b j 3b ENDPROC(__clear_user) +EXPORT_SYMBOL(__clear_user) .section .fixup,"ax" .balign 4 -- GitLab From 556f47ac6083d778843e89aa21b1242eee2693ed Mon Sep 17 00:00:00 2001 From: David Abdurachmanov Date: Wed, 18 Dec 2019 10:47:56 +0200 Subject: [PATCH 1401/1437] riscv: reject invalid syscalls below -1 Running "stress-ng --enosys 4 -t 20 -v" showed a large number of kernel oops with "Unable to handle kernel paging request at virtual address" message. This happens when enosys stressor starts testing random non-valid syscalls. I forgot to redirect any syscall below -1 to sys_ni_syscall. With the patch kernel oops messages are gone while running stress-ng enosys stressor. Signed-off-by: David Abdurachmanov Fixes: 5340627e3fe0 ("riscv: add support for SECCOMP and SECCOMP_FILTER") Signed-off-by: Paul Walmsley --- arch/riscv/kernel/entry.S | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/riscv/kernel/entry.S b/arch/riscv/kernel/entry.S index a1349ca646696..e163b7b64c86c 100644 --- a/arch/riscv/kernel/entry.S +++ b/arch/riscv/kernel/entry.S @@ -246,6 +246,7 @@ check_syscall_nr: */ li t1, -1 beq a7, t1, ret_from_syscall_rejected + blt a7, t1, 1f /* Call syscall */ la s0, sys_call_table slli t0, a7, RISCV_LGPTR -- GitLab From 1833e327a5ea1d1f356fbf6ded0760c9ff4b0594 Mon Sep 17 00:00:00 2001 From: Olof Johansson Date: Mon, 16 Dec 2019 20:07:04 -0800 Subject: [PATCH 1402/1437] riscv: export flush_icache_all to modules This is needed by LKDTM (crash dump test module), it calls flush_icache_range(), which on RISC-V turns into flush_icache_all(). On other architectures, the actual implementation is exported, so follow that precedence and export it here too. Fixes build of CONFIG_LKDTM that fails with: ERROR: "flush_icache_all" [drivers/misc/lkdtm/lkdtm.ko] undefined! Signed-off-by: Olof Johansson Signed-off-by: Paul Walmsley --- arch/riscv/mm/cacheflush.c | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/riscv/mm/cacheflush.c b/arch/riscv/mm/cacheflush.c index 8f19006866405..8930ab7278e6d 100644 --- a/arch/riscv/mm/cacheflush.c +++ b/arch/riscv/mm/cacheflush.c @@ -22,6 +22,7 @@ void flush_icache_all(void) else on_each_cpu(ipi_remote_fence_i, NULL, 1); } +EXPORT_SYMBOL(flush_icache_all); /* * Performs an icache flush for the given MM context. RISC-V has no direct -- GitLab From 270fe2ceda66b6964d4c6f261d7f562a02c1c786 Mon Sep 17 00:00:00 2001 From: Chen Zhou Date: Sat, 28 Dec 2019 11:09:47 +0800 Subject: [PATCH 1403/1437] net/wan/fsl_ucc_hdlc: remove set but not used variables 'ut_info' and 'ret' Fixes gcc '-Wunused-but-set-variable' warning: drivers/net/wan/fsl_ucc_hdlc.c: In function ucc_hdlc_irq_handler: drivers/net/wan/fsl_ucc_hdlc.c:643:23: warning: variable ut_info set but not used [-Wunused-but-set-variable] drivers/net/wan/fsl_ucc_hdlc.c: In function uhdlc_suspend: drivers/net/wan/fsl_ucc_hdlc.c:880:23: warning: variable ut_info set but not used [-Wunused-but-set-variable] drivers/net/wan/fsl_ucc_hdlc.c: In function uhdlc_resume: drivers/net/wan/fsl_ucc_hdlc.c:925:6: warning: variable ret set but not used [-Wunused-but-set-variable] Reported-by: Hulk Robot Signed-off-by: Chen Zhou Signed-off-by: David S. Miller --- drivers/net/wan/fsl_ucc_hdlc.c | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/drivers/net/wan/fsl_ucc_hdlc.c b/drivers/net/wan/fsl_ucc_hdlc.c index 308384756e6f9..c28f8409067ef 100644 --- a/drivers/net/wan/fsl_ucc_hdlc.c +++ b/drivers/net/wan/fsl_ucc_hdlc.c @@ -635,11 +635,9 @@ static irqreturn_t ucc_hdlc_irq_handler(int irq, void *dev_id) struct ucc_hdlc_private *priv = (struct ucc_hdlc_private *)dev_id; struct net_device *dev = priv->ndev; struct ucc_fast_private *uccf; - struct ucc_tdm_info *ut_info; u32 ucce; u32 uccm; - ut_info = priv->ut_info; uccf = priv->uccf; ucce = ioread32be(uccf->p_ucce); @@ -872,7 +870,6 @@ static void resume_clk_config(struct ucc_hdlc_private *priv) static int uhdlc_suspend(struct device *dev) { struct ucc_hdlc_private *priv = dev_get_drvdata(dev); - struct ucc_tdm_info *ut_info; struct ucc_fast __iomem *uf_regs; if (!priv) @@ -884,7 +881,6 @@ static int uhdlc_suspend(struct device *dev) netif_device_detach(priv->ndev); napi_disable(&priv->napi); - ut_info = priv->ut_info; uf_regs = priv->uf_regs; /* backup gumr guemr*/ @@ -917,7 +913,7 @@ static int uhdlc_resume(struct device *dev) struct ucc_fast __iomem *uf_regs; struct ucc_fast_private *uccf; struct ucc_fast_info *uf_info; - int ret, i; + int i; u32 cecr_subblock; u16 bd_status; @@ -962,16 +958,16 @@ static int uhdlc_resume(struct device *dev) /* Write to QE CECR, UCCx channel to Stop Transmission */ cecr_subblock = ucc_fast_get_qe_cr_subblock(uf_info->ucc_num); - ret = qe_issue_cmd(QE_STOP_TX, cecr_subblock, - (u8)QE_CR_PROTOCOL_UNSPECIFIED, 0); + qe_issue_cmd(QE_STOP_TX, cecr_subblock, + (u8)QE_CR_PROTOCOL_UNSPECIFIED, 0); /* Set UPSMR normal mode */ iowrite32be(0, &uf_regs->upsmr); /* init parameter base */ cecr_subblock = ucc_fast_get_qe_cr_subblock(uf_info->ucc_num); - ret = qe_issue_cmd(QE_ASSIGN_PAGE_TO_DEVICE, cecr_subblock, - QE_CR_PROTOCOL_UNSPECIFIED, priv->ucc_pram_offset); + qe_issue_cmd(QE_ASSIGN_PAGE_TO_DEVICE, cecr_subblock, + QE_CR_PROTOCOL_UNSPECIFIED, priv->ucc_pram_offset); priv->ucc_pram = (struct ucc_hdlc_param __iomem *) qe_muram_addr(priv->ucc_pram_offset); -- GitLab From c26a2c2ddc0115eb088873f5c309cf46b982f522 Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Sat, 28 Dec 2019 15:30:45 +0200 Subject: [PATCH 1404/1437] gianfar: Fix TX timestamping with a stacked DSA driver The driver wrongly assumes that it is the only entity that can set the SKBTX_IN_PROGRESS bit of the current skb. Therefore, in the gfar_clean_tx_ring function, where the TX timestamp is collected if necessary, the aforementioned bit is used to discriminate whether or not the TX timestamp should be delivered to the socket's error queue. But a stacked driver such as a DSA switch can also set the SKBTX_IN_PROGRESS bit, which is actually exactly what it should do in order to denote that the hardware timestamping process is undergoing. Therefore, gianfar would misinterpret the "in progress" bit as being its own, and deliver a second skb clone in the socket's error queue, completely throwing off a PTP process which is not expecting to receive it, _even though_ TX timestamping is not enabled for gianfar. There have been discussions [0] as to whether non-MAC drivers need or not to set SKBTX_IN_PROGRESS at all (whose purpose is to avoid sending 2 timestamps, a sw and a hw one, to applications which only expect one). But as of this patch, there are at least 2 PTP drivers that would break in conjunction with gianfar: the sja1105 DSA switch and the felix switch, by way of its ocelot core driver. So regardless of that conclusion, fix the gianfar driver to not do stuff based on flags set by others and not intended for it. [0]: https://www.spinics.net/lists/netdev/msg619699.html Fixes: f0ee7acfcdd4 ("gianfar: Add hardware TX timestamping support") Signed-off-by: Vladimir Oltean Acked-by: Richard Cochran Signed-off-by: David S. Miller --- drivers/net/ethernet/freescale/gianfar.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/freescale/gianfar.c b/drivers/net/ethernet/freescale/gianfar.c index b636d83a7ee92..f7e5cafe89a93 100644 --- a/drivers/net/ethernet/freescale/gianfar.c +++ b/drivers/net/ethernet/freescale/gianfar.c @@ -2205,13 +2205,17 @@ static void gfar_clean_tx_ring(struct gfar_priv_tx_q *tx_queue) skb_dirtytx = tx_queue->skb_dirtytx; while ((skb = tx_queue->tx_skbuff[skb_dirtytx])) { + bool do_tstamp; + + do_tstamp = (skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) && + priv->hwts_tx_en; frags = skb_shinfo(skb)->nr_frags; /* When time stamping, one additional TxBD must be freed. * Also, we need to dma_unmap_single() the TxPAL. */ - if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_IN_PROGRESS)) + if (unlikely(do_tstamp)) nr_txbds = frags + 2; else nr_txbds = frags + 1; @@ -2225,7 +2229,7 @@ static void gfar_clean_tx_ring(struct gfar_priv_tx_q *tx_queue) (lstatus & BD_LENGTH_MASK)) break; - if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_IN_PROGRESS)) { + if (unlikely(do_tstamp)) { next = next_txbd(bdp, base, tx_ring_size); buflen = be16_to_cpu(next->length) + GMAC_FCB_LEN + GMAC_TXPAL_LEN; @@ -2235,7 +2239,7 @@ static void gfar_clean_tx_ring(struct gfar_priv_tx_q *tx_queue) dma_unmap_single(priv->dev, be32_to_cpu(bdp->bufPtr), buflen, DMA_TO_DEVICE); - if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_IN_PROGRESS)) { + if (unlikely(do_tstamp)) { struct skb_shared_hwtstamps shhwtstamps; u64 *ns = (u64 *)(((uintptr_t)skb->data + 0x10) & ~0x7UL); -- GitLab From f685e609a301f171b62ee52a69acc3a74c2c04aa Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Sat, 28 Dec 2019 15:30:46 +0200 Subject: [PATCH 1405/1437] net: dsa: Deny PTP on master if switch supports it It is possible to kill PTP on a DSA switch completely and absolutely, until a reboot, with a simple command: tcpdump -i eth2 -j adapter_unsynced where eth2 is the switch's DSA master. Why? Well, in short, the PTP API in place today is a bit rudimentary and relies on applications to retrieve the TX timestamps by polling the error queue and looking at the cmsg structure. But there is no timestamp identification of any sorts (except whether it's HW or SW), you don't know how many more timestamps are there to come, which one is this one, from whom it is, etc. In other words, the SO_TIMESTAMPING API is fundamentally limited in that you can get a single HW timestamp from the stack. And the "-j adapter_unsynced" flag of tcpdump enables hardware timestamping. So let's imagine what happens when the DSA master decides it wants to deliver TX timestamps to the skb's socket too: - The timestamp that the user space sees is taken by the DSA master. Whereas the RX timestamp will eventually be overwritten by the DSA switch. So the RX and TX timestamps will be in different time bases (aka garbage). - The user space applications have no way to deal with the second (real) TX timestamp finally delivered by the DSA switch, or even to know to wait for it. Take ptp4l from the linuxptp project, for example. This is its behavior after running tcpdump, before the patch: ptp4l[172]: [6469.594] Unexpected data on socket err queue: ptp4l[172]: [6469.693] rms 8 max 16 freq -21257 +/- 11 delay 748 +/- 0 ptp4l[172]: [6469.711] Unexpected data on socket err queue: ptp4l[172]: 0020 00 00 00 1f 7b ff fe 63 02 48 00 03 aa 05 00 fd ptp4l[172]: 0030 00 00 00 00 00 00 00 00 00 00 ptp4l[172]: [6469.721] Unexpected data on socket err queue: ptp4l[172]: 0000 01 80 c2 00 00 0e 00 1f 7b 63 02 48 88 f7 10 02 ptp4l[172]: 0010 00 2c 00 00 02 00 00 00 00 00 00 00 00 00 00 00 ptp4l[172]: 0020 00 00 00 1f 7b ff fe 63 02 48 00 01 c6 b1 00 fd ptp4l[172]: 0030 00 00 00 00 00 00 00 00 00 00 ptp4l[172]: [6469.838] Unexpected data on socket err queue: ptp4l[172]: 0000 01 80 c2 00 00 0e 00 1f 7b 63 02 48 88 f7 10 02 ptp4l[172]: 0010 00 2c 00 00 02 00 00 00 00 00 00 00 00 00 00 00 ptp4l[172]: 0020 00 00 00 1f 7b ff fe 63 02 48 00 03 aa 06 00 fd ptp4l[172]: 0030 00 00 00 00 00 00 00 00 00 00 ptp4l[172]: [6469.848] Unexpected data on socket err queue: ptp4l[172]: 0000 01 80 c2 00 00 0e 00 1f 7b 63 02 48 88 f7 13 02 ptp4l[172]: 0010 00 36 00 00 02 00 00 00 00 00 00 00 00 00 00 00 ptp4l[172]: 0020 00 00 00 1f 7b ff fe 63 02 48 00 04 1a 45 05 7f ptp4l[172]: 0030 00 00 5e 05 41 32 27 c2 1a 68 00 04 9f ff fe 05 ptp4l[172]: 0040 de 06 00 01 ptp4l[172]: [6469.855] Unexpected data on socket err queue: ptp4l[172]: 0000 01 80 c2 00 00 0e 00 1f 7b 63 02 48 88 f7 10 02 ptp4l[172]: 0010 00 2c 00 00 02 00 00 00 00 00 00 00 00 00 00 00 ptp4l[172]: 0020 00 00 00 1f 7b ff fe 63 02 48 00 01 c6 b2 00 fd ptp4l[172]: 0030 00 00 00 00 00 00 00 00 00 00 ptp4l[172]: [6469.974] Unexpected data on socket err queue: ptp4l[172]: 0000 01 80 c2 00 00 0e 00 1f 7b 63 02 48 88 f7 10 02 ptp4l[172]: 0010 00 2c 00 00 02 00 00 00 00 00 00 00 00 00 00 00 ptp4l[172]: 0020 00 00 00 1f 7b ff fe 63 02 48 00 03 aa 07 00 fd ptp4l[172]: 0030 00 00 00 00 00 00 00 00 00 00 The ptp4l program itself is heavily patched to show this (more details here [0]). Otherwise, by default it just hangs. On the other hand, with the DSA patch to disallow HW timestamping applied: tcpdump -i eth2 -j adapter_unsynced tcpdump: SIOCSHWTSTAMP failed: Device or resource busy So it is a fact of life that PTP timestamping on the DSA master is incompatible with timestamping on the switch MAC, at least with the current API. And if the switch supports PTP, taking the timestamps from the switch MAC is highly preferable anyway, due to the fact that those don't contain the queuing latencies of the switch. So just disallow PTP on the DSA master if there is any PTP-capable switch attached. [0]: https://sourceforge.net/p/linuxptp/mailman/message/36880648/ Fixes: 0336369d3a4d ("net: dsa: forward hardware timestamping ioctls to switch driver") Signed-off-by: Vladimir Oltean Acked-by: Richard Cochran Signed-off-by: David S. Miller --- net/dsa/master.c | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/net/dsa/master.c b/net/dsa/master.c index 3255dfc97f865..bd44bde272f48 100644 --- a/net/dsa/master.c +++ b/net/dsa/master.c @@ -197,6 +197,35 @@ static int dsa_master_get_phys_port_name(struct net_device *dev, return 0; } +static int dsa_master_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) +{ + struct dsa_port *cpu_dp = dev->dsa_ptr; + struct dsa_switch *ds = cpu_dp->ds; + struct dsa_switch_tree *dst; + int err = -EOPNOTSUPP; + struct dsa_port *dp; + + dst = ds->dst; + + switch (cmd) { + case SIOCGHWTSTAMP: + case SIOCSHWTSTAMP: + /* Deny PTP operations on master if there is at least one + * switch in the tree that is PTP capable. + */ + list_for_each_entry(dp, &dst->ports, list) + if (dp->ds->ops->port_hwtstamp_get || + dp->ds->ops->port_hwtstamp_set) + return -EBUSY; + break; + } + + if (cpu_dp->orig_ndo_ops && cpu_dp->orig_ndo_ops->ndo_do_ioctl) + err = cpu_dp->orig_ndo_ops->ndo_do_ioctl(dev, ifr, cmd); + + return err; +} + static int dsa_master_ethtool_setup(struct net_device *dev) { struct dsa_port *cpu_dp = dev->dsa_ptr; @@ -249,6 +278,7 @@ static int dsa_master_ndo_setup(struct net_device *dev) memcpy(ops, cpu_dp->orig_ndo_ops, sizeof(*ops)); ops->ndo_get_phys_port_name = dsa_master_get_phys_port_name; + ops->ndo_do_ioctl = dsa_master_ioctl; dev->netdev_ops = ops; -- GitLab From 98ca480a8f22fdbd768e3dad07024c8d4856576c Mon Sep 17 00:00:00 2001 From: Amir Goldstein Date: Sun, 22 Dec 2019 20:45:28 +0200 Subject: [PATCH 1406/1437] locks: print unsigned ino in /proc/locks An ino is unsigned, so display it as such in /proc/locks. Cc: stable@vger.kernel.org Signed-off-by: Amir Goldstein Signed-off-by: Jeff Layton --- fs/locks.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/locks.c b/fs/locks.c index 6970f55daf543..44b6da0328426 100644 --- a/fs/locks.c +++ b/fs/locks.c @@ -2853,7 +2853,7 @@ static void lock_get_status(struct seq_file *f, struct file_lock *fl, } if (inode) { /* userspace relies on this representation of dev_t */ - seq_printf(f, "%d %02x:%02x:%ld ", fl_pid, + seq_printf(f, "%d %02x:%02x:%lu ", fl_pid, MAJOR(inode->i_sb->s_dev), MINOR(inode->i_sb->s_dev), inode->i_ino); } else { -- GitLab From 314bd842d98e1035cc40b671a71e07f48420e58f Mon Sep 17 00:00:00 2001 From: Amit Cohen Date: Sun, 29 Dec 2019 13:40:22 +0200 Subject: [PATCH 1407/1437] mlxsw: spectrum_router: Skip loopback RIFs during MAC validation When a router interface (RIF) is created the MAC address of the backing netdev is verified to have the same MSBs as existing RIFs. This is required in order to avoid changing existing RIF MAC addresses that all share the same MSBs. Loopback RIFs are special in this regard as they do not have a MAC address, given they are only used to loop packets from the overlay to the underlay. Without this change, an error is returned when trying to create a RIF after the creation of a GRE tunnel that is represented by a loopback RIF. 'rif->dev->dev_addr' points to the GRE device's local IP, which does not share the same MSBs as physical interfaces. Adding an IP address to any physical interface results in: Error: mlxsw_spectrum: All router interface MAC addresses must have the same prefix. Fix this by skipping loopback RIFs during MAC validation. Fixes: 74bc99397438 ("mlxsw: spectrum_router: Veto unsupported RIF MAC addresses") Signed-off-by: Amit Cohen Signed-off-by: Ido Schimmel Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c index 08b7e9f964dae..8290e82240fc0 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c @@ -7079,6 +7079,9 @@ static int mlxsw_sp_router_port_check_rif_addr(struct mlxsw_sp *mlxsw_sp, for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS); i++) { rif = mlxsw_sp->router->rifs[i]; + if (rif && rif->ops && + rif->ops->type == MLXSW_SP_RIF_TYPE_IPIP_LB) + continue; if (rif && rif->dev && rif->dev != dev && !ether_addr_equal_masked(rif->dev->dev_addr, dev_addr, mlxsw_sp->mac_mask)) { -- GitLab From acca789a358cc960be3937851d7de6591c79d6c2 Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Sun, 29 Dec 2019 13:40:23 +0200 Subject: [PATCH 1408/1437] mlxsw: spectrum: Use dedicated policer for VRRP packets Currently, VRRP packets and packets that hit exceptions during routing (e.g., MTU error) are policed using the same policer towards the CPU. This means, for example, that misconfiguration of the MTU on a routed interface can prevent VRRP packets from reaching the CPU, which in turn can cause the VRRP daemon to assume it is the Master router. Fix this by using a dedicated policer for VRRP packets. Fixes: 11566d34f895 ("mlxsw: spectrum: Add VRRP traps") Signed-off-by: Ido Schimmel Reported-by: Alex Veber Tested-by: Alex Veber Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/reg.h | 1 + drivers/net/ethernet/mellanox/mlxsw/spectrum.c | 9 +++++++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlxsw/reg.h b/drivers/net/ethernet/mellanox/mlxsw/reg.h index 5294a1622643e..af30e8a766820 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/reg.h +++ b/drivers/net/ethernet/mellanox/mlxsw/reg.h @@ -5472,6 +5472,7 @@ enum mlxsw_reg_htgt_trap_group { MLXSW_REG_HTGT_TRAP_GROUP_SP_LBERROR, MLXSW_REG_HTGT_TRAP_GROUP_SP_PTP0, MLXSW_REG_HTGT_TRAP_GROUP_SP_PTP1, + MLXSW_REG_HTGT_TRAP_GROUP_SP_VRRP, __MLXSW_REG_HTGT_TRAP_GROUP_MAX, MLXSW_REG_HTGT_TRAP_GROUP_MAX = __MLXSW_REG_HTGT_TRAP_GROUP_MAX - 1 diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c index 556dca328bb51..f7fd5e8fbf96f 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c @@ -4542,8 +4542,8 @@ static const struct mlxsw_listener mlxsw_sp_listener[] = { MLXSW_SP_RXL_MARK(ROUTER_ALERT_IPV6, TRAP_TO_CPU, ROUTER_EXP, false), MLXSW_SP_RXL_MARK(IPIP_DECAP_ERROR, TRAP_TO_CPU, ROUTER_EXP, false), MLXSW_SP_RXL_MARK(DECAP_ECN0, TRAP_TO_CPU, ROUTER_EXP, false), - MLXSW_SP_RXL_MARK(IPV4_VRRP, TRAP_TO_CPU, ROUTER_EXP, false), - MLXSW_SP_RXL_MARK(IPV6_VRRP, TRAP_TO_CPU, ROUTER_EXP, false), + MLXSW_SP_RXL_MARK(IPV4_VRRP, TRAP_TO_CPU, VRRP, false), + MLXSW_SP_RXL_MARK(IPV6_VRRP, TRAP_TO_CPU, VRRP, false), /* PKT Sample trap */ MLXSW_RXL(mlxsw_sp_rx_listener_sample_func, PKT_SAMPLE, MIRROR_TO_CPU, false, SP_IP2ME, DISCARD), @@ -4626,6 +4626,10 @@ static int mlxsw_sp_cpu_policers_set(struct mlxsw_core *mlxsw_core) rate = 19 * 1024; burst_size = 12; break; + case MLXSW_REG_HTGT_TRAP_GROUP_SP_VRRP: + rate = 360; + burst_size = 7; + break; default: continue; } @@ -4665,6 +4669,7 @@ static int mlxsw_sp_trap_groups_set(struct mlxsw_core *mlxsw_core) case MLXSW_REG_HTGT_TRAP_GROUP_SP_OSPF: case MLXSW_REG_HTGT_TRAP_GROUP_SP_PIM: case MLXSW_REG_HTGT_TRAP_GROUP_SP_PTP0: + case MLXSW_REG_HTGT_TRAP_GROUP_SP_VRRP: priority = 5; tc = 5; break; -- GitLab From fd6988496e79a6a4bdb514a4655d2920209eb85d Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Sun, 29 Dec 2019 15:29:16 -0800 Subject: [PATCH 1409/1437] Linux 5.5-rc4 --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index caf14acf19539..b99d95df8075d 100644 --- a/Makefile +++ b/Makefile @@ -2,7 +2,7 @@ VERSION = 5 PATCHLEVEL = 5 SUBLEVEL = 0 -EXTRAVERSION = -rc3 +EXTRAVERSION = -rc4 NAME = Kleptomaniac Octopus # *DOCUMENTATION* -- GitLab From 0caeaf6ad532f9be5a768a158627cb31921cc8b7 Mon Sep 17 00:00:00 2001 From: Rahul Lakkireddy Date: Mon, 30 Dec 2019 18:14:08 +0530 Subject: [PATCH 1410/1437] cxgb4/cxgb4vf: fix flow control display for auto negotiation As per 802.3-2005, Section Two, Annex 28B, Table 28B-2 [1], when _only_ Rx pause is enabled, both symmetric and asymmetric pause towards local device must be enabled. Also, firmware returns the local device's flow control pause params as part of advertised capabilities and negotiated params as part of current link attributes. So, fix up ethtool's flow control pause params fetch logic to read from acaps, instead of linkattr. [1] https://standards.ieee.org/standard/802_3-2005.html Fixes: c3168cabe1af ("cxgb4/cxgbvf: Handle 32-bit fw port capabilities") Signed-off-by: Surendra Mobiya Signed-off-by: Rahul Lakkireddy Signed-off-by: David S. Miller --- drivers/net/ethernet/chelsio/cxgb4/cxgb4.h | 1 + .../ethernet/chelsio/cxgb4/cxgb4_ethtool.c | 4 ++-- drivers/net/ethernet/chelsio/cxgb4/t4_hw.c | 21 ++++++++++++------- .../ethernet/chelsio/cxgb4vf/cxgb4vf_main.c | 4 ++-- .../ethernet/chelsio/cxgb4vf/t4vf_common.h | 1 + .../net/ethernet/chelsio/cxgb4vf/t4vf_hw.c | 18 +++++++++------- 6 files changed, 30 insertions(+), 19 deletions(-) diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h b/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h index a70ac2097892d..becee29f5df7a 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h @@ -504,6 +504,7 @@ struct link_config { enum cc_pause requested_fc; /* flow control user has requested */ enum cc_pause fc; /* actual link flow control */ + enum cc_pause advertised_fc; /* actual advertised flow control */ enum cc_fec requested_fec; /* Forward Error Correction: */ enum cc_fec fec; /* requested and actual in use */ diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_ethtool.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_ethtool.c index 20ab3b6285a2f..c837382ee522d 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_ethtool.c +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_ethtool.c @@ -807,8 +807,8 @@ static void get_pauseparam(struct net_device *dev, struct port_info *p = netdev_priv(dev); epause->autoneg = (p->link_cfg.requested_fc & PAUSE_AUTONEG) != 0; - epause->rx_pause = (p->link_cfg.fc & PAUSE_RX) != 0; - epause->tx_pause = (p->link_cfg.fc & PAUSE_TX) != 0; + epause->rx_pause = (p->link_cfg.advertised_fc & PAUSE_RX) != 0; + epause->tx_pause = (p->link_cfg.advertised_fc & PAUSE_TX) != 0; } static int set_pauseparam(struct net_device *dev, diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c b/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c index 19d18acfc9a6f..844fdcf55118b 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c +++ b/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c @@ -4089,7 +4089,8 @@ static inline fw_port_cap32_t cc_to_fwcap_pause(enum cc_pause cc_pause) if (cc_pause & PAUSE_TX) fw_pause |= FW_PORT_CAP32_802_3_PAUSE; else - fw_pause |= FW_PORT_CAP32_802_3_ASM_DIR; + fw_pause |= FW_PORT_CAP32_802_3_ASM_DIR | + FW_PORT_CAP32_802_3_PAUSE; } else if (cc_pause & PAUSE_TX) { fw_pause |= FW_PORT_CAP32_802_3_ASM_DIR; } @@ -8563,17 +8564,17 @@ static fw_port_cap32_t lstatus_to_fwcap(u32 lstatus) void t4_handle_get_port_info(struct port_info *pi, const __be64 *rpl) { const struct fw_port_cmd *cmd = (const void *)rpl; - int action = FW_PORT_CMD_ACTION_G(be32_to_cpu(cmd->action_to_len16)); - struct adapter *adapter = pi->adapter; + fw_port_cap32_t pcaps, acaps, lpacaps, linkattr; struct link_config *lc = &pi->link_cfg; - int link_ok, linkdnrc; - enum fw_port_type port_type; + struct adapter *adapter = pi->adapter; + unsigned int speed, fc, fec, adv_fc; enum fw_port_module_type mod_type; - unsigned int speed, fc, fec; - fw_port_cap32_t pcaps, acaps, lpacaps, linkattr; + int action, link_ok, linkdnrc; + enum fw_port_type port_type; /* Extract the various fields from the Port Information message. */ + action = FW_PORT_CMD_ACTION_G(be32_to_cpu(cmd->action_to_len16)); switch (action) { case FW_PORT_ACTION_GET_PORT_INFO: { u32 lstatus = be32_to_cpu(cmd->u.info.lstatus_to_modtype); @@ -8611,6 +8612,7 @@ void t4_handle_get_port_info(struct port_info *pi, const __be64 *rpl) } fec = fwcap_to_cc_fec(acaps); + adv_fc = fwcap_to_cc_pause(acaps); fc = fwcap_to_cc_pause(linkattr); speed = fwcap_to_speed(linkattr); @@ -8667,7 +8669,9 @@ void t4_handle_get_port_info(struct port_info *pi, const __be64 *rpl) } if (link_ok != lc->link_ok || speed != lc->speed || - fc != lc->fc || fec != lc->fec) { /* something changed */ + fc != lc->fc || adv_fc != lc->advertised_fc || + fec != lc->fec) { + /* something changed */ if (!link_ok && lc->link_ok) { lc->link_down_rc = linkdnrc; dev_warn_ratelimited(adapter->pdev_dev, @@ -8677,6 +8681,7 @@ void t4_handle_get_port_info(struct port_info *pi, const __be64 *rpl) } lc->link_ok = link_ok; lc->speed = speed; + lc->advertised_fc = adv_fc; lc->fc = fc; lc->fec = fec; diff --git a/drivers/net/ethernet/chelsio/cxgb4vf/cxgb4vf_main.c b/drivers/net/ethernet/chelsio/cxgb4vf/cxgb4vf_main.c index f6fc0875d5b0a..f4d41f968afa2 100644 --- a/drivers/net/ethernet/chelsio/cxgb4vf/cxgb4vf_main.c +++ b/drivers/net/ethernet/chelsio/cxgb4vf/cxgb4vf_main.c @@ -1690,8 +1690,8 @@ static void cxgb4vf_get_pauseparam(struct net_device *dev, struct port_info *pi = netdev_priv(dev); pauseparam->autoneg = (pi->link_cfg.requested_fc & PAUSE_AUTONEG) != 0; - pauseparam->rx_pause = (pi->link_cfg.fc & PAUSE_RX) != 0; - pauseparam->tx_pause = (pi->link_cfg.fc & PAUSE_TX) != 0; + pauseparam->rx_pause = (pi->link_cfg.advertised_fc & PAUSE_RX) != 0; + pauseparam->tx_pause = (pi->link_cfg.advertised_fc & PAUSE_TX) != 0; } /* diff --git a/drivers/net/ethernet/chelsio/cxgb4vf/t4vf_common.h b/drivers/net/ethernet/chelsio/cxgb4vf/t4vf_common.h index ccca67cf4487d..57cfd10a99ecc 100644 --- a/drivers/net/ethernet/chelsio/cxgb4vf/t4vf_common.h +++ b/drivers/net/ethernet/chelsio/cxgb4vf/t4vf_common.h @@ -135,6 +135,7 @@ struct link_config { enum cc_pause requested_fc; /* flow control user has requested */ enum cc_pause fc; /* actual link flow control */ + enum cc_pause advertised_fc; /* actual advertised flow control */ enum cc_fec auto_fec; /* Forward Error Correction: */ enum cc_fec requested_fec; /* "automatic" (IEEE 802.3), */ diff --git a/drivers/net/ethernet/chelsio/cxgb4vf/t4vf_hw.c b/drivers/net/ethernet/chelsio/cxgb4vf/t4vf_hw.c index 8a389d617a238..9d49ff211cc1a 100644 --- a/drivers/net/ethernet/chelsio/cxgb4vf/t4vf_hw.c +++ b/drivers/net/ethernet/chelsio/cxgb4vf/t4vf_hw.c @@ -1913,16 +1913,16 @@ static const char *t4vf_link_down_rc_str(unsigned char link_down_rc) static void t4vf_handle_get_port_info(struct port_info *pi, const struct fw_port_cmd *cmd) { - int action = FW_PORT_CMD_ACTION_G(be32_to_cpu(cmd->action_to_len16)); - struct adapter *adapter = pi->adapter; + fw_port_cap32_t pcaps, acaps, lpacaps, linkattr; struct link_config *lc = &pi->link_cfg; - int link_ok, linkdnrc; - enum fw_port_type port_type; + struct adapter *adapter = pi->adapter; + unsigned int speed, fc, fec, adv_fc; enum fw_port_module_type mod_type; - unsigned int speed, fc, fec; - fw_port_cap32_t pcaps, acaps, lpacaps, linkattr; + int action, link_ok, linkdnrc; + enum fw_port_type port_type; /* Extract the various fields from the Port Information message. */ + action = FW_PORT_CMD_ACTION_G(be32_to_cpu(cmd->action_to_len16)); switch (action) { case FW_PORT_ACTION_GET_PORT_INFO: { u32 lstatus = be32_to_cpu(cmd->u.info.lstatus_to_modtype); @@ -1982,6 +1982,7 @@ static void t4vf_handle_get_port_info(struct port_info *pi, } fec = fwcap_to_cc_fec(acaps); + adv_fc = fwcap_to_cc_pause(acaps); fc = fwcap_to_cc_pause(linkattr); speed = fwcap_to_speed(linkattr); @@ -2012,7 +2013,9 @@ static void t4vf_handle_get_port_info(struct port_info *pi, } if (link_ok != lc->link_ok || speed != lc->speed || - fc != lc->fc || fec != lc->fec) { /* something changed */ + fc != lc->fc || adv_fc != lc->advertised_fc || + fec != lc->fec) { + /* something changed */ if (!link_ok && lc->link_ok) { lc->link_down_rc = linkdnrc; dev_warn_ratelimited(adapter->pdev_dev, @@ -2022,6 +2025,7 @@ static void t4vf_handle_get_port_info(struct port_info *pi, } lc->link_ok = link_ok; lc->speed = speed; + lc->advertised_fc = adv_fc; lc->fc = fc; lc->fec = fec; -- GitLab From f278b99ca6b2d91a5744588d81bae297179b0d1f Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Mon, 30 Dec 2019 06:06:19 -0800 Subject: [PATCH 1411/1437] tcp_cubic: refactor code to perform a divide only when needed Neal Cardwell suggested to not change ca->delay_min and apply the ack delay cushion only when Hystart ACK train is still under consideration. This should avoid a 64bit divide unless needed. Tested: 40Gbit(mlx4) testbed (with sch_fq as packet scheduler) $ echo -n 'file tcp_cubic.c +p' >/sys/kernel/debug/dynamic_debug/control $ nstat -n;for f in {1..10}; do ./super_netperf 1 -H lpaa24 -l -4000000; done;nstat|egrep "Hystart" 14815 16280 15293 15563 11574 15145 14789 18548 16972 12520 TcpExtTCPHystartTrainDetect 10 0.0 TcpExtTCPHystartTrainCwnd 1396 0.0 $ dmesg | tail -10 [ 4873.951350] hystart_ack_train (116 > 93) delay_min 24 (+ ack_delay 69) cwnd 80 [ 4875.155379] hystart_ack_train (55 > 50) delay_min 21 (+ ack_delay 29) cwnd 160 [ 4876.333921] hystart_ack_train (69 > 62) delay_min 23 (+ ack_delay 39) cwnd 130 [ 4877.519037] hystart_ack_train (69 > 60) delay_min 22 (+ ack_delay 38) cwnd 130 [ 4878.701559] hystart_ack_train (87 > 63) delay_min 24 (+ ack_delay 39) cwnd 160 [ 4879.844597] hystart_ack_train (93 > 50) delay_min 21 (+ ack_delay 29) cwnd 216 [ 4880.956650] hystart_ack_train (74 > 67) delay_min 20 (+ ack_delay 47) cwnd 108 [ 4882.098500] hystart_ack_train (61 > 57) delay_min 23 (+ ack_delay 34) cwnd 130 [ 4883.262056] hystart_ack_train (72 > 67) delay_min 21 (+ ack_delay 46) cwnd 130 [ 4884.418760] hystart_ack_train (74 > 67) delay_min 29 (+ ack_delay 38) cwnd 152 10Gbit(bnx2x) testbed (with sch_fq as packet scheduler) $ echo -n 'file tcp_cubic.c +p' >/sys/kernel/debug/dynamic_debug/control $ nstat -n;for f in {1..10}; do ./super_netperf 1 -H lpk52 -l -4000000; done;nstat|egrep "Hystart" 7050 7065 7100 6900 7202 7263 7189 6869 7463 7034 TcpExtTCPHystartTrainDetect 10 0.0 TcpExtTCPHystartTrainCwnd 3199 0.0 $ dmesg | tail -10 [ 176.920012] hystart_ack_train (161 > 141) delay_min 83 (+ ack_delay 58) cwnd 264 [ 179.144645] hystart_ack_train (164 > 159) delay_min 120 (+ ack_delay 39) cwnd 444 [ 181.354527] hystart_ack_train (214 > 168) delay_min 125 (+ ack_delay 43) cwnd 436 [ 183.539565] hystart_ack_train (170 > 147) delay_min 96 (+ ack_delay 51) cwnd 326 [ 185.727309] hystart_ack_train (177 > 160) delay_min 61 (+ ack_delay 99) cwnd 128 [ 187.947142] hystart_ack_train (184 > 167) delay_min 123 (+ ack_delay 44) cwnd 367 [ 190.166680] hystart_ack_train (230 > 153) delay_min 116 (+ ack_delay 37) cwnd 444 [ 192.327285] hystart_ack_train (210 > 206) delay_min 86 (+ ack_delay 120) cwnd 152 [ 194.511392] hystart_ack_train (173 > 151) delay_min 94 (+ ack_delay 57) cwnd 239 [ 196.736023] hystart_ack_train (149 > 146) delay_min 105 (+ ack_delay 41) cwnd 399 Fixes: 42f3a8aaae66 ("tcp_cubic: tweak Hystart detection for short RTT flows") Signed-off-by: Eric Dumazet Reported-by: Neal Cardwell Link: https://www.spinics.net/lists/netdev/msg621886.html Link: https://www.spinics.net/lists/netdev/msg621797.html Acked-by: Neal Cardwell Acked-by: Soheil Hassas Yeganeh Signed-off-by: David S. Miller --- net/ipv4/tcp_cubic.c | 51 ++++++++++++++++++++++++-------------------- 1 file changed, 28 insertions(+), 23 deletions(-) diff --git a/net/ipv4/tcp_cubic.c b/net/ipv4/tcp_cubic.c index d02bb283c6890..8f8eefd3a3ce1 100644 --- a/net/ipv4/tcp_cubic.c +++ b/net/ipv4/tcp_cubic.c @@ -372,6 +372,26 @@ static void bictcp_state(struct sock *sk, u8 new_state) } } +/* Account for TSO/GRO delays. + * Otherwise short RTT flows could get too small ssthresh, since during + * slow start we begin with small TSO packets and ca->delay_min would + * not account for long aggregation delay when TSO packets get bigger. + * Ideally even with a very small RTT we would like to have at least one + * TSO packet being sent and received by GRO, and another one in qdisc layer. + * We apply another 100% factor because @rate is doubled at this point. + * We cap the cushion to 1ms. + */ +static u32 hystart_ack_delay(struct sock *sk) +{ + unsigned long rate; + + rate = READ_ONCE(sk->sk_pacing_rate); + if (!rate) + return 0; + return min_t(u64, USEC_PER_MSEC, + div64_ul((u64)GSO_MAX_SIZE * 4 * USEC_PER_SEC, rate)); +} + static void hystart_update(struct sock *sk, u32 delay) { struct tcp_sock *tp = tcp_sk(sk); @@ -385,7 +405,8 @@ static void hystart_update(struct sock *sk, u32 delay) if ((s32)(now - ca->last_ack) <= hystart_ack_delta_us) { ca->last_ack = now; - threshold = ca->delay_min; + threshold = ca->delay_min + hystart_ack_delay(sk); + /* Hystart ack train triggers if we get ack past * ca->delay_min/2. * Pacing might have delayed packets up to RTT/2 @@ -396,6 +417,9 @@ static void hystart_update(struct sock *sk, u32 delay) if ((s32)(now - ca->round_start) > threshold) { ca->found = 1; + pr_debug("hystart_ack_train (%u > %u) delay_min %u (+ ack_delay %u) cwnd %u\n", + now - ca->round_start, threshold, + ca->delay_min, hystart_ack_delay(sk), tp->snd_cwnd); NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPHYSTARTTRAINDETECT); NET_ADD_STATS(sock_net(sk), @@ -447,30 +471,11 @@ static void bictcp_acked(struct sock *sk, const struct ack_sample *sample) delay = 1; /* first time call or link delay decreases */ - if (ca->delay_min == 0 || ca->delay_min > delay) { - unsigned long rate = READ_ONCE(sk->sk_pacing_rate); - - /* Account for TSO/GRO delays. - * Otherwise short RTT flows could get too small ssthresh, - * since during slow start we begin with small TSO packets - * and could lower ca->delay_min too much. - * Ideally even with a very small RTT we would like to have - * at least one TSO packet being sent and received by GRO, - * and another one in qdisc layer. - * We apply another 100% factor because @rate is doubled at - * this point. - * We cap the cushion to 1ms. - */ - if (rate) - delay += min_t(u64, USEC_PER_MSEC, - div64_ul((u64)GSO_MAX_SIZE * - 4 * USEC_PER_SEC, rate)); - if (ca->delay_min == 0 || ca->delay_min > delay) - ca->delay_min = delay; - } + if (ca->delay_min == 0 || ca->delay_min > delay) + ca->delay_min = delay; /* hystart triggers when cwnd is larger than some threshold */ - if (!ca->found && hystart && tcp_in_slow_start(tp) && + if (!ca->found && tcp_in_slow_start(tp) && hystart && tp->snd_cwnd >= hystart_low_window) hystart_update(sk, delay); } -- GitLab From db99d5126fb9067c3123b40a33c9936235f059fa Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Thu, 26 Dec 2019 16:02:24 +0100 Subject: [PATCH 1412/1437] sfc: avoid duplicate error handling code in 'efx_ef10_sriov_set_vf_mac()' 'eth_zero_addr()' is already called in the error handling path. This is harmless, but there is no point in calling it twice, so remove one. Signed-off-by: Christophe JAILLET Signed-off-by: David S. Miller --- drivers/net/ethernet/sfc/ef10_sriov.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/sfc/ef10_sriov.c b/drivers/net/ethernet/sfc/ef10_sriov.c index 52bd43f45761a..14393767ef9f4 100644 --- a/drivers/net/ethernet/sfc/ef10_sriov.c +++ b/drivers/net/ethernet/sfc/ef10_sriov.c @@ -522,10 +522,9 @@ int efx_ef10_sriov_set_vf_mac(struct efx_nic *efx, int vf_i, u8 *mac) if (!is_zero_ether_addr(mac)) { rc = efx_ef10_vport_add_mac(efx, vf->vport_id, mac); - if (rc) { - eth_zero_addr(vf->mac); + if (rc) goto fail; - } + if (vf->efx) ether_addr_copy(vf->efx->net_dev->dev_addr, mac); } -- GitLab From 9fcf024dd6fae082f05e8c1fcdae23972b2f6971 Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Fri, 27 Dec 2019 02:59:54 +0200 Subject: [PATCH 1413/1437] net: dsa: sja1105: Take PTP egress timestamp by port, not mgmt slot The PTP egress timestamp N must be captured from register PTPEGR_TS[n], where n = 2 * PORT + TSREG. There are 10 PTPEGR_TS registers, 2 per port. We are only using TSREG=0. As opposed to the management slots, which are 4 in number (SJA1105_NUM_PORTS, minus the CPU port). Any management frame (which includes PTP frames) can be sent to any non-CPU port through any management slot. When the CPU port is not the last port (#4), there will be a mismatch between the slot and the port number. Luckily, the only mainline occurrence with this switch (arch/arm/boot/dts/ls1021a-tsn.dts) does have the CPU port as #4, so the issue did not manifest itself thus far. Fixes: 47ed985e97f5 ("net: dsa: sja1105: Add logic for TX timestamping") Signed-off-by: Vladimir Oltean Signed-off-by: David S. Miller --- drivers/net/dsa/sja1105/sja1105_main.c | 2 +- drivers/net/dsa/sja1105/sja1105_ptp.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/net/dsa/sja1105/sja1105_main.c b/drivers/net/dsa/sja1105/sja1105_main.c index a51ac088c0bca..86bbab166633d 100644 --- a/drivers/net/dsa/sja1105/sja1105_main.c +++ b/drivers/net/dsa/sja1105/sja1105_main.c @@ -1855,7 +1855,7 @@ static netdev_tx_t sja1105_port_deferred_xmit(struct dsa_switch *ds, int port, if (!clone) goto out; - sja1105_ptp_txtstamp_skb(ds, slot, clone); + sja1105_ptp_txtstamp_skb(ds, port, clone); out: mutex_unlock(&priv->mgmt_lock); diff --git a/drivers/net/dsa/sja1105/sja1105_ptp.c b/drivers/net/dsa/sja1105/sja1105_ptp.c index 54258a25031db..c0fda7db6271c 100644 --- a/drivers/net/dsa/sja1105/sja1105_ptp.c +++ b/drivers/net/dsa/sja1105/sja1105_ptp.c @@ -659,7 +659,7 @@ void sja1105_ptp_clock_unregister(struct dsa_switch *ds) ptp_data->clock = NULL; } -void sja1105_ptp_txtstamp_skb(struct dsa_switch *ds, int slot, +void sja1105_ptp_txtstamp_skb(struct dsa_switch *ds, int port, struct sk_buff *skb) { struct sja1105_private *priv = ds->priv; @@ -679,7 +679,7 @@ void sja1105_ptp_txtstamp_skb(struct dsa_switch *ds, int slot, goto out; } - rc = sja1105_ptpegr_ts_poll(ds, slot, &ts); + rc = sja1105_ptpegr_ts_poll(ds, port, &ts); if (rc < 0) { dev_err(ds->dev, "timed out polling for tstamp\n"); kfree_skb(skb); -- GitLab From 5a47f588ee2366b2febdc822cdfdcf856cb0a777 Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Fri, 27 Dec 2019 03:01:50 +0200 Subject: [PATCH 1414/1437] net: dsa: sja1105: Really make the PTP command read-write When activating tc-taprio offload on the switch ports, the TAS state machine will try to check whether it is running or not, but will find both the STARTED and STOPPED bits as false in the sja1105_tas_check_running function. So the function will return -EINVAL (an abnormal situation) and the kernel will keep printing this from the TAS FSM workqueue: [ 37.691971] sja1105 spi0.1: An operation returned -22 The reason is that the underlying function that gets called, sja1105_ptp_commit, does not actually do a SPI_READ, but a SPI_WRITE. So the command buffer remains initialized with zeroes instead of retrieving the hardware state. Fix that. Fixes: 41603d78b362 ("net: dsa: sja1105: Make the PTP command read-write") Signed-off-by: Vladimir Oltean Signed-off-by: David S. Miller --- drivers/net/dsa/sja1105/sja1105_ptp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/dsa/sja1105/sja1105_ptp.c b/drivers/net/dsa/sja1105/sja1105_ptp.c index c0fda7db6271c..43ab7589d0d0c 100644 --- a/drivers/net/dsa/sja1105/sja1105_ptp.c +++ b/drivers/net/dsa/sja1105/sja1105_ptp.c @@ -234,7 +234,7 @@ int sja1105_ptp_commit(struct dsa_switch *ds, struct sja1105_ptp_cmd *cmd, if (rw == SPI_WRITE) priv->info->ptp_cmd_packing(buf, cmd, PACK); - rc = sja1105_xfer_buf(priv, SPI_WRITE, regs->ptp_control, buf, + rc = sja1105_xfer_buf(priv, rw, regs->ptp_control, buf, SJA1105_SIZE_PTP_CMD); if (rw == SPI_READ) -- GitLab From d00bdc0a8839de9a5c9be5af2a79dbf8e0087689 Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Fri, 27 Dec 2019 03:03:54 +0200 Subject: [PATCH 1415/1437] net: dsa: sja1105: Remove restriction of zero base-time for taprio offload The check originates from the initial implementation which was not based on PTP time but on a standalone clock source. In the meantime we can now program the PTPSCHTM register at runtime with the dynamic base time (actually with a value that is 200 ns smaller, to avoid writing DELTA=0 in the Schedule Entry Points Parameters Table). And we also have logic for moving the actual base time in the future of the PHC's current time base, so the check for zero serves no purpose, since even if the user will specify zero, that's not what will end up in the static config table where the limitation is. Fixes: 86db36a347b4 ("net: dsa: sja1105: Implement state machine for TAS with PTP clock source") Signed-off-by: Vladimir Oltean Signed-off-by: David S. Miller --- drivers/net/dsa/sja1105/sja1105_tas.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/drivers/net/dsa/sja1105/sja1105_tas.c b/drivers/net/dsa/sja1105/sja1105_tas.c index 26b925b5daced..fa6750d973d7b 100644 --- a/drivers/net/dsa/sja1105/sja1105_tas.c +++ b/drivers/net/dsa/sja1105/sja1105_tas.c @@ -477,11 +477,6 @@ int sja1105_setup_tc_taprio(struct dsa_switch *ds, int port, if (admin->cycle_time_extension) return -ENOTSUPP; - if (!ns_to_sja1105_delta(admin->base_time)) { - dev_err(ds->dev, "A base time of zero is not hardware-allowed\n"); - return -ERANGE; - } - for (i = 0; i < admin->num_entries; i++) { s64 delta_ns = admin->entries[i].interval; s64 delta_cycles = ns_to_sja1105_delta(delta_ns); -- GitLab From 3a323ed7c9c6d60af05bada0efe7a8ef56cf317f Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Fri, 27 Dec 2019 03:08:07 +0200 Subject: [PATCH 1416/1437] Documentation: net: dsa: sja1105: Remove text about taprio base-time limitation Since commit 86db36a347b4 ("net: dsa: sja1105: Implement state machine for TAS with PTP clock source"), this paragraph is no longer true. So remove it. Signed-off-by: Vladimir Oltean Signed-off-by: David S. Miller --- Documentation/networking/dsa/sja1105.rst | 6 ------ 1 file changed, 6 deletions(-) diff --git a/Documentation/networking/dsa/sja1105.rst b/Documentation/networking/dsa/sja1105.rst index eef20d0bcf7c1..64553d8d91cb9 100644 --- a/Documentation/networking/dsa/sja1105.rst +++ b/Documentation/networking/dsa/sja1105.rst @@ -230,12 +230,6 @@ simultaneously on two ports. The driver checks the consistency of the schedules against this restriction and errors out when appropriate. Schedule analysis is needed to avoid this, which is outside the scope of the document. -At the moment, the time-aware scheduler can only be triggered based on a -standalone clock and not based on PTP time. This means the base-time argument -from tc-taprio is ignored and the schedule starts right away. It also means it -is more difficult to phase-align the scheduler with the other devices in the -network. - Device Tree bindings and board design ===================================== -- GitLab From 54fa49ee88138756df0fcf867cb1849904710a8c Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Fri, 27 Dec 2019 03:11:13 +0200 Subject: [PATCH 1417/1437] net: dsa: sja1105: Reconcile the meaning of TPID and TPID2 for E/T and P/Q/R/S For first-generation switches (SJA1105E and SJA1105T): - TPID means C-Tag (typically 0x8100) - TPID2 means S-Tag (typically 0x88A8) While for the second generation switches (SJA1105P, SJA1105Q, SJA1105R, SJA1105S) it is the other way around: - TPID means S-Tag (typically 0x88A8) - TPID2 means C-Tag (typically 0x8100) In other words, E/T tags untagged traffic with TPID, and P/Q/R/S with TPID2. So the patch mentioned below fixed VLAN filtering for P/Q/R/S, but broke it for E/T. We strive for a common code path for all switches in the family, so just lie in the static config packing functions that TPID and TPID2 are at swapped bit offsets than they actually are, for P/Q/R/S. This will make both switches understand TPID to be ETH_P_8021Q and TPID2 to be ETH_P_8021AD. The meaning from the original E/T was chosen over P/Q/R/S because E/T is actually the one with public documentation available (UM10944.pdf). Fixes: f9a1a7646c0d ("net: dsa: sja1105: Reverse TPID and TPID2") Signed-off-by: Vladimir Oltean Signed-off-by: David S. Miller --- drivers/net/dsa/sja1105/sja1105_main.c | 8 ++++---- drivers/net/dsa/sja1105/sja1105_static_config.c | 7 +++++-- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/drivers/net/dsa/sja1105/sja1105_main.c b/drivers/net/dsa/sja1105/sja1105_main.c index 86bbab166633d..1da5ac111499e 100644 --- a/drivers/net/dsa/sja1105/sja1105_main.c +++ b/drivers/net/dsa/sja1105/sja1105_main.c @@ -1569,8 +1569,8 @@ static int sja1105_vlan_filtering(struct dsa_switch *ds, int port, bool enabled) if (enabled) { /* Enable VLAN filtering. */ - tpid = ETH_P_8021AD; - tpid2 = ETH_P_8021Q; + tpid = ETH_P_8021Q; + tpid2 = ETH_P_8021AD; } else { /* Disable VLAN filtering. */ tpid = ETH_P_SJA1105; @@ -1579,9 +1579,9 @@ static int sja1105_vlan_filtering(struct dsa_switch *ds, int port, bool enabled) table = &priv->static_config.tables[BLK_IDX_GENERAL_PARAMS]; general_params = table->entries; - /* EtherType used to identify outer tagged (S-tag) VLAN traffic */ - general_params->tpid = tpid; /* EtherType used to identify inner tagged (C-tag) VLAN traffic */ + general_params->tpid = tpid; + /* EtherType used to identify outer tagged (S-tag) VLAN traffic */ general_params->tpid2 = tpid2; /* When VLAN filtering is on, we need to at least be able to * decode management traffic through the "backup plan". diff --git a/drivers/net/dsa/sja1105/sja1105_static_config.c b/drivers/net/dsa/sja1105/sja1105_static_config.c index 0d03e13e99092..63d2311817c47 100644 --- a/drivers/net/dsa/sja1105/sja1105_static_config.c +++ b/drivers/net/dsa/sja1105/sja1105_static_config.c @@ -142,6 +142,9 @@ static size_t sja1105et_general_params_entry_packing(void *buf, void *entry_ptr, return size; } +/* TPID and TPID2 are intentionally reversed so that semantic + * compatibility with E/T is kept. + */ static size_t sja1105pqrs_general_params_entry_packing(void *buf, void *entry_ptr, enum packing_op op) @@ -166,9 +169,9 @@ sja1105pqrs_general_params_entry_packing(void *buf, void *entry_ptr, sja1105_packing(buf, &entry->mirr_port, 141, 139, size, op); sja1105_packing(buf, &entry->vlmarker, 138, 107, size, op); sja1105_packing(buf, &entry->vlmask, 106, 75, size, op); - sja1105_packing(buf, &entry->tpid, 74, 59, size, op); + sja1105_packing(buf, &entry->tpid2, 74, 59, size, op); sja1105_packing(buf, &entry->ignore2stf, 58, 58, size, op); - sja1105_packing(buf, &entry->tpid2, 57, 42, size, op); + sja1105_packing(buf, &entry->tpid, 57, 42, size, op); sja1105_packing(buf, &entry->queue_ts, 41, 41, size, op); sja1105_packing(buf, &entry->egrmirrvid, 40, 29, size, op); sja1105_packing(buf, &entry->egrmirrpcp, 28, 26, size, op); -- GitLab From a33121e5487b424339636b25c35d3a180eaa5f5e Mon Sep 17 00:00:00 2001 From: Vladis Dronov Date: Fri, 27 Dec 2019 03:26:27 +0100 Subject: [PATCH 1418/1437] ptp: fix the race between the release of ptp_clock and cdev In a case when a ptp chardev (like /dev/ptp0) is open but an underlying device is removed, closing this file leads to a race. This reproduces easily in a kvm virtual machine: ts# cat openptp0.c int main() { ... fp = fopen("/dev/ptp0", "r"); ... sleep(10); } ts# uname -r 5.5.0-rc3-46cf053e ts# cat /proc/cmdline ... slub_debug=FZP ts# modprobe ptp_kvm ts# ./openptp0 & [1] 670 opened /dev/ptp0, sleeping 10s... ts# rmmod ptp_kvm ts# ls /dev/ptp* ls: cannot access '/dev/ptp*': No such file or directory ts# ...woken up [ 48.010809] general protection fault: 0000 [#1] SMP [ 48.012502] CPU: 6 PID: 658 Comm: openptp0 Not tainted 5.5.0-rc3-46cf053e #25 [ 48.014624] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), ... [ 48.016270] RIP: 0010:module_put.part.0+0x7/0x80 [ 48.017939] RSP: 0018:ffffb3850073be00 EFLAGS: 00010202 [ 48.018339] RAX: 000000006b6b6b6b RBX: 6b6b6b6b6b6b6b6b RCX: ffff89a476c00ad0 [ 48.018936] RDX: fffff65a08d3ea08 RSI: 0000000000000247 RDI: 6b6b6b6b6b6b6b6b [ 48.019470] ... ^^^ a slub poison [ 48.023854] Call Trace: [ 48.024050] __fput+0x21f/0x240 [ 48.024288] task_work_run+0x79/0x90 [ 48.024555] do_exit+0x2af/0xab0 [ 48.024799] ? vfs_write+0x16a/0x190 [ 48.025082] do_group_exit+0x35/0x90 [ 48.025387] __x64_sys_exit_group+0xf/0x10 [ 48.025737] do_syscall_64+0x3d/0x130 [ 48.026056] entry_SYSCALL_64_after_hwframe+0x44/0xa9 [ 48.026479] RIP: 0033:0x7f53b12082f6 [ 48.026792] ... [ 48.030945] Modules linked in: ptp i6300esb watchdog [last unloaded: ptp_kvm] [ 48.045001] Fixing recursive fault but reboot is needed! This happens in: static void __fput(struct file *file) { ... if (file->f_op->release) file->f_op->release(inode, file); <<< cdev is kfree'd here if (unlikely(S_ISCHR(inode->i_mode) && inode->i_cdev != NULL && !(mode & FMODE_PATH))) { cdev_put(inode->i_cdev); <<< cdev fields are accessed here Namely: __fput() posix_clock_release() kref_put(&clk->kref, delete_clock) <<< the last reference delete_clock() delete_ptp_clock() kfree(ptp) <<< cdev is embedded in ptp cdev_put module_put(p->owner) <<< *p is kfree'd, bang! Here cdev is embedded in posix_clock which is embedded in ptp_clock. The race happens because ptp_clock's lifetime is controlled by two refcounts: kref and cdev.kobj in posix_clock. This is wrong. Make ptp_clock's sysfs device a parent of cdev with cdev_device_add() created especially for such cases. This way the parent device with its ptp_clock is not released until all references to the cdev are released. This adds a requirement that an initialized but not exposed struct device should be provided to posix_clock_register() by a caller instead of a simple dev_t. This approach was adopted from the commit 72139dfa2464 ("watchdog: Fix the race between the release of watchdog_core_data and cdev"). See details of the implementation in the commit 233ed09d7fda ("chardev: add helper function to register char devs with a struct device"). Link: https://lore.kernel.org/linux-fsdevel/20191125125342.6189-1-vdronov@redhat.com/T/#u Analyzed-by: Stephen Johnston Analyzed-by: Vern Lovejoy Signed-off-by: Vladis Dronov Acked-by: Richard Cochran Signed-off-by: David S. Miller --- drivers/ptp/ptp_clock.c | 31 ++++++++++++++----------------- drivers/ptp/ptp_private.h | 2 +- include/linux/posix-clock.h | 19 +++++++++++-------- kernel/time/posix-clock.c | 31 +++++++++++++------------------ 4 files changed, 39 insertions(+), 44 deletions(-) diff --git a/drivers/ptp/ptp_clock.c b/drivers/ptp/ptp_clock.c index e60eab7f8a616..61fafe0374cea 100644 --- a/drivers/ptp/ptp_clock.c +++ b/drivers/ptp/ptp_clock.c @@ -166,9 +166,9 @@ static struct posix_clock_operations ptp_clock_ops = { .read = ptp_read, }; -static void delete_ptp_clock(struct posix_clock *pc) +static void ptp_clock_release(struct device *dev) { - struct ptp_clock *ptp = container_of(pc, struct ptp_clock, clock); + struct ptp_clock *ptp = container_of(dev, struct ptp_clock, dev); mutex_destroy(&ptp->tsevq_mux); mutex_destroy(&ptp->pincfg_mux); @@ -213,7 +213,6 @@ struct ptp_clock *ptp_clock_register(struct ptp_clock_info *info, } ptp->clock.ops = ptp_clock_ops; - ptp->clock.release = delete_ptp_clock; ptp->info = info; ptp->devid = MKDEV(major, index); ptp->index = index; @@ -236,15 +235,6 @@ struct ptp_clock *ptp_clock_register(struct ptp_clock_info *info, if (err) goto no_pin_groups; - /* Create a new device in our class. */ - ptp->dev = device_create_with_groups(ptp_class, parent, ptp->devid, - ptp, ptp->pin_attr_groups, - "ptp%d", ptp->index); - if (IS_ERR(ptp->dev)) { - err = PTR_ERR(ptp->dev); - goto no_device; - } - /* Register a new PPS source. */ if (info->pps) { struct pps_source_info pps; @@ -260,8 +250,18 @@ struct ptp_clock *ptp_clock_register(struct ptp_clock_info *info, } } - /* Create a posix clock. */ - err = posix_clock_register(&ptp->clock, ptp->devid); + /* Initialize a new device of our class in our clock structure. */ + device_initialize(&ptp->dev); + ptp->dev.devt = ptp->devid; + ptp->dev.class = ptp_class; + ptp->dev.parent = parent; + ptp->dev.groups = ptp->pin_attr_groups; + ptp->dev.release = ptp_clock_release; + dev_set_drvdata(&ptp->dev, ptp); + dev_set_name(&ptp->dev, "ptp%d", ptp->index); + + /* Create a posix clock and link it to the device. */ + err = posix_clock_register(&ptp->clock, &ptp->dev); if (err) { pr_err("failed to create posix clock\n"); goto no_clock; @@ -273,8 +273,6 @@ no_clock: if (ptp->pps_source) pps_unregister_source(ptp->pps_source); no_pps: - device_destroy(ptp_class, ptp->devid); -no_device: ptp_cleanup_pin_groups(ptp); no_pin_groups: if (ptp->kworker) @@ -304,7 +302,6 @@ int ptp_clock_unregister(struct ptp_clock *ptp) if (ptp->pps_source) pps_unregister_source(ptp->pps_source); - device_destroy(ptp_class, ptp->devid); ptp_cleanup_pin_groups(ptp); posix_clock_unregister(&ptp->clock); diff --git a/drivers/ptp/ptp_private.h b/drivers/ptp/ptp_private.h index 9171d42468fdb..6b97155148f11 100644 --- a/drivers/ptp/ptp_private.h +++ b/drivers/ptp/ptp_private.h @@ -28,7 +28,7 @@ struct timestamp_event_queue { struct ptp_clock { struct posix_clock clock; - struct device *dev; + struct device dev; struct ptp_clock_info *info; dev_t devid; int index; /* index into clocks.map */ diff --git a/include/linux/posix-clock.h b/include/linux/posix-clock.h index fe6cfdcfbc260..468328b1e1dd5 100644 --- a/include/linux/posix-clock.h +++ b/include/linux/posix-clock.h @@ -69,29 +69,32 @@ struct posix_clock_operations { * * @ops: Functional interface to the clock * @cdev: Character device instance for this clock - * @kref: Reference count. + * @dev: Pointer to the clock's device. * @rwsem: Protects the 'zombie' field from concurrent access. * @zombie: If 'zombie' is true, then the hardware has disappeared. - * @release: A function to free the structure when the reference count reaches - * zero. May be NULL if structure is statically allocated. * * Drivers should embed their struct posix_clock within a private * structure, obtaining a reference to it during callbacks using * container_of(). + * + * Drivers should supply an initialized but not exposed struct device + * to posix_clock_register(). It is used to manage lifetime of the + * driver's private structure. It's 'release' field should be set to + * a release function for this private structure. */ struct posix_clock { struct posix_clock_operations ops; struct cdev cdev; - struct kref kref; + struct device *dev; struct rw_semaphore rwsem; bool zombie; - void (*release)(struct posix_clock *clk); }; /** * posix_clock_register() - register a new clock - * @clk: Pointer to the clock. Caller must provide 'ops' and 'release' - * @devid: Allocated device id + * @clk: Pointer to the clock. Caller must provide 'ops' field + * @dev: Pointer to the initialized device. Caller must provide + * 'release' field * * A clock driver calls this function to register itself with the * clock device subsystem. If 'clk' points to dynamically allocated @@ -100,7 +103,7 @@ struct posix_clock { * * Returns zero on success, non-zero otherwise. */ -int posix_clock_register(struct posix_clock *clk, dev_t devid); +int posix_clock_register(struct posix_clock *clk, struct device *dev); /** * posix_clock_unregister() - unregister a clock diff --git a/kernel/time/posix-clock.c b/kernel/time/posix-clock.c index ec960bb939fdf..200fb2d3be99f 100644 --- a/kernel/time/posix-clock.c +++ b/kernel/time/posix-clock.c @@ -14,8 +14,6 @@ #include "posix-timers.h" -static void delete_clock(struct kref *kref); - /* * Returns NULL if the posix_clock instance attached to 'fp' is old and stale. */ @@ -125,7 +123,7 @@ static int posix_clock_open(struct inode *inode, struct file *fp) err = 0; if (!err) { - kref_get(&clk->kref); + get_device(clk->dev); fp->private_data = clk; } out: @@ -141,7 +139,7 @@ static int posix_clock_release(struct inode *inode, struct file *fp) if (clk->ops.release) err = clk->ops.release(clk); - kref_put(&clk->kref, delete_clock); + put_device(clk->dev); fp->private_data = NULL; @@ -161,38 +159,35 @@ static const struct file_operations posix_clock_file_operations = { #endif }; -int posix_clock_register(struct posix_clock *clk, dev_t devid) +int posix_clock_register(struct posix_clock *clk, struct device *dev) { int err; - kref_init(&clk->kref); init_rwsem(&clk->rwsem); cdev_init(&clk->cdev, &posix_clock_file_operations); + err = cdev_device_add(&clk->cdev, dev); + if (err) { + pr_err("%s unable to add device %d:%d\n", + dev_name(dev), MAJOR(dev->devt), MINOR(dev->devt)); + return err; + } clk->cdev.owner = clk->ops.owner; - err = cdev_add(&clk->cdev, devid, 1); + clk->dev = dev; - return err; + return 0; } EXPORT_SYMBOL_GPL(posix_clock_register); -static void delete_clock(struct kref *kref) -{ - struct posix_clock *clk = container_of(kref, struct posix_clock, kref); - - if (clk->release) - clk->release(clk); -} - void posix_clock_unregister(struct posix_clock *clk) { - cdev_del(&clk->cdev); + cdev_device_del(&clk->cdev, clk->dev); down_write(&clk->rwsem); clk->zombie = true; up_write(&clk->rwsem); - kref_put(&clk->kref, delete_clock); + put_device(clk->dev); } EXPORT_SYMBOL_GPL(posix_clock_unregister); -- GitLab From 853697504de043ff0bfd815bd3a64de1dce73dc7 Mon Sep 17 00:00:00 2001 From: Cambda Zhu Date: Fri, 27 Dec 2019 16:52:37 +0800 Subject: [PATCH 1419/1437] tcp: Fix highest_sack and highest_sack_seq >From commit 50895b9de1d3 ("tcp: highest_sack fix"), the logic about setting tp->highest_sack to the head of the send queue was removed. Of course the logic is error prone, but it is logical. Before we remove the pointer to the highest sack skb and use the seq instead, we need to set tp->highest_sack to NULL when there is no skb after the last sack, and then replace NULL with the real skb when new skb inserted into the rtx queue, because the NULL means the highest sack seq is tp->snd_nxt. If tp->highest_sack is NULL and new data sent, the next ACK with sack option will increase tp->reordering unexpectedly. This patch sets tp->highest_sack to the tail of the rtx queue if it's NULL and new data is sent. The patch keeps the rule that the highest_sack can only be maintained by sack processing, except for this only case. Fixes: 50895b9de1d3 ("tcp: highest_sack fix") Signed-off-by: Cambda Zhu Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- net/ipv4/tcp_output.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index 1f7735ca8f22d..58c92a7d671c5 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -72,6 +72,9 @@ static void tcp_event_new_data_sent(struct sock *sk, struct sk_buff *skb) __skb_unlink(skb, &sk->sk_write_queue); tcp_rbtree_insert(&sk->tcp_rtx_queue, skb); + if (tp->highest_sack == NULL) + tp->highest_sack = skb; + tp->packets_out += tcp_skb_pcount(skb); if (!prior_packets || icsk->icsk_pending == ICSK_TIME_LOSS_PROBE) tcp_rearm_rto(sk); -- GitLab From 544fed47af4d2174ac0b550e9c8da15c2dfdb117 Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Fri, 27 Dec 2019 15:02:28 +0200 Subject: [PATCH 1420/1437] ptp: introduce ptp_cancel_worker_sync In order to effectively use the PTP kernel thread for tasks such as timestamping packets, allow the user control over stopping it, which is needed e.g. when the timestamping queues must be drained. Signed-off-by: Vladimir Oltean Signed-off-by: David S. Miller --- drivers/ptp/ptp_clock.c | 6 ++++++ include/linux/ptp_clock_kernel.h | 9 +++++++++ 2 files changed, 15 insertions(+) diff --git a/drivers/ptp/ptp_clock.c b/drivers/ptp/ptp_clock.c index e60eab7f8a616..4f0d91a76dcb9 100644 --- a/drivers/ptp/ptp_clock.c +++ b/drivers/ptp/ptp_clock.c @@ -371,6 +371,12 @@ int ptp_schedule_worker(struct ptp_clock *ptp, unsigned long delay) } EXPORT_SYMBOL(ptp_schedule_worker); +void ptp_cancel_worker_sync(struct ptp_clock *ptp) +{ + kthread_cancel_delayed_work_sync(&ptp->aux_work); +} +EXPORT_SYMBOL(ptp_cancel_worker_sync); + /* module operations */ static void __exit ptp_exit(void) diff --git a/include/linux/ptp_clock_kernel.h b/include/linux/ptp_clock_kernel.h index 93cc4f1d444ae..c64a1ef872407 100644 --- a/include/linux/ptp_clock_kernel.h +++ b/include/linux/ptp_clock_kernel.h @@ -243,6 +243,13 @@ int ptp_find_pin(struct ptp_clock *ptp, int ptp_schedule_worker(struct ptp_clock *ptp, unsigned long delay); +/** + * ptp_cancel_worker_sync() - cancel ptp auxiliary clock + * + * @ptp: The clock obtained from ptp_clock_register(). + */ +void ptp_cancel_worker_sync(struct ptp_clock *ptp); + #else static inline struct ptp_clock *ptp_clock_register(struct ptp_clock_info *info, struct device *parent) @@ -260,6 +267,8 @@ static inline int ptp_find_pin(struct ptp_clock *ptp, static inline int ptp_schedule_worker(struct ptp_clock *ptp, unsigned long delay) { return -EOPNOTSUPP; } +static inline void ptp_cancel_worker_sync(struct ptp_clock *ptp) +{ } #endif -- GitLab From 1e762bd278d2a70bc74b9cbee7f1e93bd4704fe2 Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Fri, 27 Dec 2019 15:02:29 +0200 Subject: [PATCH 1421/1437] net: dsa: sja1105: Use PTP core's dedicated kernel thread for RX timestamping And move the queue of skb's waiting for RX timestamps into the ptp_data structure, since it isn't needed if PTP is not compiled. Signed-off-by: Vladimir Oltean Signed-off-by: David S. Miller --- drivers/net/dsa/sja1105/sja1105_ptp.c | 33 ++++++++++++--------------- drivers/net/dsa/sja1105/sja1105_ptp.h | 1 + include/linux/dsa/sja1105.h | 2 -- 3 files changed, 16 insertions(+), 20 deletions(-) diff --git a/drivers/net/dsa/sja1105/sja1105_ptp.c b/drivers/net/dsa/sja1105/sja1105_ptp.c index 54258a25031db..387b22a86a53d 100644 --- a/drivers/net/dsa/sja1105/sja1105_ptp.c +++ b/drivers/net/dsa/sja1105/sja1105_ptp.c @@ -367,22 +367,16 @@ static int sja1105_ptpclkval_write(struct sja1105_private *priv, u64 ticks, ptp_sts); } -#define rxtstamp_to_tagger(d) \ - container_of((d), struct sja1105_tagger_data, rxtstamp_work) -#define tagger_to_sja1105(d) \ - container_of((d), struct sja1105_private, tagger_data) - -static void sja1105_rxtstamp_work(struct work_struct *work) +static long sja1105_rxtstamp_work(struct ptp_clock_info *ptp) { - struct sja1105_tagger_data *tagger_data = rxtstamp_to_tagger(work); - struct sja1105_private *priv = tagger_to_sja1105(tagger_data); - struct sja1105_ptp_data *ptp_data = &priv->ptp_data; + struct sja1105_ptp_data *ptp_data = ptp_caps_to_data(ptp); + struct sja1105_private *priv = ptp_data_to_sja1105(ptp_data); struct dsa_switch *ds = priv->ds; struct sk_buff *skb; mutex_lock(&ptp_data->lock); - while ((skb = skb_dequeue(&tagger_data->skb_rxtstamp_queue)) != NULL) { + while ((skb = skb_dequeue(&ptp_data->skb_rxtstamp_queue)) != NULL) { struct skb_shared_hwtstamps *shwt = skb_hwtstamps(skb); u64 ticks, ts; int rc; @@ -404,6 +398,9 @@ static void sja1105_rxtstamp_work(struct work_struct *work) } mutex_unlock(&ptp_data->lock); + + /* Don't restart */ + return -1; } /* Called from dsa_skb_defer_rx_timestamp */ @@ -411,16 +408,16 @@ bool sja1105_port_rxtstamp(struct dsa_switch *ds, int port, struct sk_buff *skb, unsigned int type) { struct sja1105_private *priv = ds->priv; - struct sja1105_tagger_data *tagger_data = &priv->tagger_data; + struct sja1105_ptp_data *ptp_data = &priv->ptp_data; - if (!test_bit(SJA1105_HWTS_RX_EN, &tagger_data->state)) + if (!test_bit(SJA1105_HWTS_RX_EN, &priv->tagger_data.state)) return false; /* We need to read the full PTP clock to reconstruct the Rx * timestamp. For that we need a sleepable context. */ - skb_queue_tail(&tagger_data->skb_rxtstamp_queue, skb); - schedule_work(&tagger_data->rxtstamp_work); + skb_queue_tail(&ptp_data->skb_rxtstamp_queue, skb); + ptp_schedule_worker(ptp_data->clock, 0); return true; } @@ -628,11 +625,11 @@ int sja1105_ptp_clock_register(struct dsa_switch *ds) .adjtime = sja1105_ptp_adjtime, .gettimex64 = sja1105_ptp_gettimex, .settime64 = sja1105_ptp_settime, + .do_aux_work = sja1105_rxtstamp_work, .max_adj = SJA1105_MAX_ADJ_PPB, }; - skb_queue_head_init(&tagger_data->skb_rxtstamp_queue); - INIT_WORK(&tagger_data->rxtstamp_work, sja1105_rxtstamp_work); + skb_queue_head_init(&ptp_data->skb_rxtstamp_queue); spin_lock_init(&tagger_data->meta_lock); ptp_data->clock = ptp_clock_register(&ptp_data->caps, ds->dev); @@ -653,8 +650,8 @@ void sja1105_ptp_clock_unregister(struct dsa_switch *ds) if (IS_ERR_OR_NULL(ptp_data->clock)) return; - cancel_work_sync(&priv->tagger_data.rxtstamp_work); - skb_queue_purge(&priv->tagger_data.skb_rxtstamp_queue); + ptp_cancel_worker_sync(ptp_data->clock); + skb_queue_purge(&ptp_data->skb_rxtstamp_queue); ptp_clock_unregister(ptp_data->clock); ptp_data->clock = NULL; } diff --git a/drivers/net/dsa/sja1105/sja1105_ptp.h b/drivers/net/dsa/sja1105/sja1105_ptp.h index 470f44b76318d..6f4a19eec7091 100644 --- a/drivers/net/dsa/sja1105/sja1105_ptp.h +++ b/drivers/net/dsa/sja1105/sja1105_ptp.h @@ -30,6 +30,7 @@ struct sja1105_ptp_cmd { }; struct sja1105_ptp_data { + struct sk_buff_head skb_rxtstamp_queue; struct ptp_clock_info caps; struct ptp_clock *clock; struct sja1105_ptp_cmd cmd; diff --git a/include/linux/dsa/sja1105.h b/include/linux/dsa/sja1105.h index 897e799dbcb9f..c0b6a603ea8ce 100644 --- a/include/linux/dsa/sja1105.h +++ b/include/linux/dsa/sja1105.h @@ -37,8 +37,6 @@ * the structure defined in struct sja1105_private. */ struct sja1105_tagger_data { - struct sk_buff_head skb_rxtstamp_queue; - struct work_struct rxtstamp_work; struct sk_buff *stampable_skb; /* Protects concurrent access to the meta state machine * from taggers running on multiple ports on SMP systems -- GitLab From 19d1f0ed74b75279f46549de80fa2adf80ef9995 Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Fri, 27 Dec 2019 15:02:30 +0200 Subject: [PATCH 1422/1437] net: dsa: sja1105: Empty the RX timestamping queue on PTP settings change When disabling PTP timestamping, don't reset the switch with the new static config until all existing PTP frames have been timestamped on the RX path or dropped. There's nothing we can do with these afterwards. Signed-off-by: Vladimir Oltean Signed-off-by: David S. Miller --- drivers/net/dsa/sja1105/sja1105_ptp.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/dsa/sja1105/sja1105_ptp.c b/drivers/net/dsa/sja1105/sja1105_ptp.c index 387b22a86a53d..e6b611470043b 100644 --- a/drivers/net/dsa/sja1105/sja1105_ptp.c +++ b/drivers/net/dsa/sja1105/sja1105_ptp.c @@ -83,6 +83,7 @@ static int sja1105_init_avb_params(struct sja1105_private *priv, static int sja1105_change_rxtstamping(struct sja1105_private *priv, bool on) { + struct sja1105_ptp_data *ptp_data = &priv->ptp_data; struct sja1105_general_params_entry *general_params; struct sja1105_table *table; int rc; @@ -101,6 +102,8 @@ static int sja1105_change_rxtstamping(struct sja1105_private *priv, kfree_skb(priv->tagger_data.stampable_skb); priv->tagger_data.stampable_skb = NULL; } + ptp_cancel_worker_sync(ptp_data->clock); + skb_queue_purge(&ptp_data->skb_rxtstamp_queue); return sja1105_static_config_reload(priv, SJA1105_RX_HWTSTAMPING); } -- GitLab From f398efc14a9277b55defff71f59a46fdf13c713f Mon Sep 17 00:00:00 2001 From: Kevin Kou Date: Fri, 27 Dec 2019 13:11:16 +0000 Subject: [PATCH 1423/1437] sctp: add enabled check for path tracepoint loop. sctp_outq_sack is the main function handles SACK, it is called very frequently. As the commit "move trace_sctp_probe_path into sctp_outq_sack" added below code to this function, sctp tracepoint is disabled most of time, but the loop of transport list will be always called even though the tracepoint is disabled, this is unnecessary. + /* SCTP path tracepoint for congestion control debugging. */ + list_for_each_entry(transport, transport_list, transports) { + trace_sctp_probe_path(transport, asoc); + } This patch is to add tracepoint enabled check at outside of the loop of transport list, and avoid traversing the loop when trace is disabled, it is a small optimization. Signed-off-by: Kevin Kou Acked-by: Marcelo Ricardo Leitner Signed-off-by: David S. Miller --- net/sctp/outqueue.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/net/sctp/outqueue.c b/net/sctp/outqueue.c index 6b0b3bad4daa6..577e3bc4ee6f1 100644 --- a/net/sctp/outqueue.c +++ b/net/sctp/outqueue.c @@ -1240,8 +1240,9 @@ int sctp_outq_sack(struct sctp_outq *q, struct sctp_chunk *chunk) transport_list = &asoc->peer.transport_addr_list; /* SCTP path tracepoint for congestion control debugging. */ - list_for_each_entry(transport, transport_list, transports) { - trace_sctp_probe_path(transport, asoc); + if (trace_sctp_probe_path_enabled()) { + list_for_each_entry(transport, transport_list, transports) + trace_sctp_probe_path(transport, asoc); } sack_ctsn = ntohl(sack->cum_tsn_ack); -- GitLab From 9e860947d8d7a1504476ac49abfce90a4ce600f3 Mon Sep 17 00:00:00 2001 From: Vijay Khemka Date: Fri, 27 Dec 2019 14:43:49 -0800 Subject: [PATCH 1424/1437] net/ncsi: Fix gma flag setting after response gma_flag was set at the time of GMA command request but it should only be set after getting successful response. Movinng this flag setting in GMA response handler. This flag is used mainly for not repeating GMA command once received MAC address. Signed-off-by: Vijay Khemka Reviewed-by: Samuel Mendoza-Jonas Signed-off-by: David S. Miller --- net/ncsi/ncsi-manage.c | 3 --- net/ncsi/ncsi-rsp.c | 6 ++++++ 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/net/ncsi/ncsi-manage.c b/net/ncsi/ncsi-manage.c index 70fe02697544f..e20b815140291 100644 --- a/net/ncsi/ncsi-manage.c +++ b/net/ncsi/ncsi-manage.c @@ -764,9 +764,6 @@ static int ncsi_gma_handler(struct ncsi_cmd_arg *nca, unsigned int mf_id) return -1; } - /* Set the flag for GMA command which should only be called once */ - nca->ndp->gma_flag = 1; - /* Get Mac address from NCSI device */ return nch->handler(nca); } diff --git a/net/ncsi/ncsi-rsp.c b/net/ncsi/ncsi-rsp.c index d5611f04926de..a94bb59793f02 100644 --- a/net/ncsi/ncsi-rsp.c +++ b/net/ncsi/ncsi-rsp.c @@ -627,6 +627,9 @@ static int ncsi_rsp_handler_oem_mlx_gma(struct ncsi_request *nr) saddr.sa_family = ndev->type; ndev->priv_flags |= IFF_LIVE_ADDR_CHANGE; memcpy(saddr.sa_data, &rsp->data[MLX_MAC_ADDR_OFFSET], ETH_ALEN); + /* Set the flag for GMA command which should only be called once */ + ndp->gma_flag = 1; + ret = ops->ndo_set_mac_address(ndev, &saddr); if (ret < 0) netdev_warn(ndev, "NCSI: 'Writing mac address to device failed\n"); @@ -671,6 +674,9 @@ static int ncsi_rsp_handler_oem_bcm_gma(struct ncsi_request *nr) if (!is_valid_ether_addr((const u8 *)saddr.sa_data)) return -ENXIO; + /* Set the flag for GMA command which should only be called once */ + ndp->gma_flag = 1; + ret = ops->ndo_set_mac_address(ndev, &saddr); if (ret < 0) netdev_warn(ndev, "NCSI: 'Writing mac address to device failed\n"); -- GitLab From a5b72a083da197b493c7ed1e5730d62d3199f7d6 Mon Sep 17 00:00:00 2001 From: Davide Caratti Date: Sat, 28 Dec 2019 16:36:58 +0100 Subject: [PATCH 1425/1437] net/sched: add delete_empty() to filters and use it in cls_flower Revert "net/sched: cls_u32: fix refcount leak in the error path of u32_change()", and fix the u32 refcount leak in a more generic way that preserves the semantic of rule dumping. On tc filters that don't support lockless insertion/removal, there is no need to guard against concurrent insertion when a removal is in progress. Therefore, for most of them we can avoid a full walk() when deleting, and just decrease the refcount, like it was done on older Linux kernels. This fixes situations where walk() was wrongly detecting a non-empty filter, like it happened with cls_u32 in the error path of change(), thus leading to failures in the following tdc selftests: 6aa7: (filter, u32) Add/Replace u32 with source match and invalid indev 6658: (filter, u32) Add/Replace u32 with custom hash table and invalid handle 74c2: (filter, u32) Add/Replace u32 filter with invalid hash table id On cls_flower, and on (future) lockless filters, this check is necessary: move all the check_empty() logic in a callback so that each filter can have its own implementation. For cls_flower, it's sufficient to check if no IDRs have been allocated. This reverts commit 275c44aa194b7159d1191817b20e076f55f0e620. Changes since v1: - document the need for delete_empty() when TCF_PROTO_OPS_DOIT_UNLOCKED is used, thanks to Vlad Buslov - implement delete_empty() without doing fl_walk(), thanks to Vlad Buslov - squash revert and new fix in a single patch, to be nice with bisect tests that run tdc on u32 filter, thanks to Dave Miller Fixes: 275c44aa194b ("net/sched: cls_u32: fix refcount leak in the error path of u32_change()") Fixes: 6676d5e416ee ("net: sched: set dedicated tcf_walker flag when tp is empty") Suggested-by: Jamal Hadi Salim Suggested-by: Vlad Buslov Signed-off-by: Davide Caratti Reviewed-by: Vlad Buslov Tested-by: Jamal Hadi Salim Acked-by: Jamal Hadi Salim Signed-off-by: David S. Miller --- include/net/sch_generic.h | 5 +++++ net/sched/cls_api.c | 31 +++++-------------------------- net/sched/cls_flower.c | 12 ++++++++++++ net/sched/cls_u32.c | 25 ------------------------- 4 files changed, 22 insertions(+), 51 deletions(-) diff --git a/include/net/sch_generic.h b/include/net/sch_generic.h index 144f264ea394d..fceddf89592af 100644 --- a/include/net/sch_generic.h +++ b/include/net/sch_generic.h @@ -308,6 +308,7 @@ struct tcf_proto_ops { int (*delete)(struct tcf_proto *tp, void *arg, bool *last, bool rtnl_held, struct netlink_ext_ack *); + bool (*delete_empty)(struct tcf_proto *tp); void (*walk)(struct tcf_proto *tp, struct tcf_walker *arg, bool rtnl_held); int (*reoffload)(struct tcf_proto *tp, bool add, @@ -336,6 +337,10 @@ struct tcf_proto_ops { int flags; }; +/* Classifiers setting TCF_PROTO_OPS_DOIT_UNLOCKED in tcf_proto_ops->flags + * are expected to implement tcf_proto_ops->delete_empty(), otherwise race + * conditions can occur when filters are inserted/deleted simultaneously. + */ enum tcf_proto_ops_flags { TCF_PROTO_OPS_DOIT_UNLOCKED = 1, }; diff --git a/net/sched/cls_api.c b/net/sched/cls_api.c index 6a0eacafdb191..76e0d122616ae 100644 --- a/net/sched/cls_api.c +++ b/net/sched/cls_api.c @@ -308,33 +308,12 @@ static void tcf_proto_put(struct tcf_proto *tp, bool rtnl_held, tcf_proto_destroy(tp, rtnl_held, true, extack); } -static int walker_check_empty(struct tcf_proto *tp, void *fh, - struct tcf_walker *arg) +static bool tcf_proto_check_delete(struct tcf_proto *tp) { - if (fh) { - arg->nonempty = true; - return -1; - } - return 0; -} - -static bool tcf_proto_is_empty(struct tcf_proto *tp, bool rtnl_held) -{ - struct tcf_walker walker = { .fn = walker_check_empty, }; - - if (tp->ops->walk) { - tp->ops->walk(tp, &walker, rtnl_held); - return !walker.nonempty; - } - return true; -} + if (tp->ops->delete_empty) + return tp->ops->delete_empty(tp); -static bool tcf_proto_check_delete(struct tcf_proto *tp, bool rtnl_held) -{ - spin_lock(&tp->lock); - if (tcf_proto_is_empty(tp, rtnl_held)) - tp->deleting = true; - spin_unlock(&tp->lock); + tp->deleting = true; return tp->deleting; } @@ -1751,7 +1730,7 @@ static void tcf_chain_tp_delete_empty(struct tcf_chain *chain, * concurrently. * Mark tp for deletion if it is empty. */ - if (!tp_iter || !tcf_proto_check_delete(tp, rtnl_held)) { + if (!tp_iter || !tcf_proto_check_delete(tp)) { mutex_unlock(&chain->filter_chain_lock); return; } diff --git a/net/sched/cls_flower.c b/net/sched/cls_flower.c index 0d125de542850..b0f42e62dd760 100644 --- a/net/sched/cls_flower.c +++ b/net/sched/cls_flower.c @@ -2773,6 +2773,17 @@ static void fl_bind_class(void *fh, u32 classid, unsigned long cl) f->res.class = cl; } +static bool fl_delete_empty(struct tcf_proto *tp) +{ + struct cls_fl_head *head = fl_head_dereference(tp); + + spin_lock(&tp->lock); + tp->deleting = idr_is_empty(&head->handle_idr); + spin_unlock(&tp->lock); + + return tp->deleting; +} + static struct tcf_proto_ops cls_fl_ops __read_mostly = { .kind = "flower", .classify = fl_classify, @@ -2782,6 +2793,7 @@ static struct tcf_proto_ops cls_fl_ops __read_mostly = { .put = fl_put, .change = fl_change, .delete = fl_delete, + .delete_empty = fl_delete_empty, .walk = fl_walk, .reoffload = fl_reoffload, .hw_add = fl_hw_add, diff --git a/net/sched/cls_u32.c b/net/sched/cls_u32.c index 66c6bcec16cbc..a0e6fac613de2 100644 --- a/net/sched/cls_u32.c +++ b/net/sched/cls_u32.c @@ -1108,33 +1108,10 @@ erridr: return err; } -static bool u32_hnode_empty(struct tc_u_hnode *ht, bool *non_root_ht) -{ - int i; - - if (!ht) - return true; - if (!ht->is_root) { - *non_root_ht = true; - return false; - } - if (*non_root_ht) - return false; - if (ht->refcnt < 2) - return true; - - for (i = 0; i <= ht->divisor; i++) { - if (rtnl_dereference(ht->ht[i])) - return false; - } - return true; -} - static void u32_walk(struct tcf_proto *tp, struct tcf_walker *arg, bool rtnl_held) { struct tc_u_common *tp_c = tp->data; - bool non_root_ht = false; struct tc_u_hnode *ht; struct tc_u_knode *n; unsigned int h; @@ -1147,8 +1124,6 @@ static void u32_walk(struct tcf_proto *tp, struct tcf_walker *arg, ht = rtnl_dereference(ht->next)) { if (ht->prio != tp->prio) continue; - if (u32_hnode_empty(ht, &non_root_ht)) - return; if (arg->count >= arg->skip) { if (arg->fn(tp, ht, arg) < 0) { arg->stop = 1; -- GitLab From 04b69426d846cd04ca9acefff1ea39e1c64d2714 Mon Sep 17 00:00:00 2001 From: Taehee Yoo Date: Sat, 28 Dec 2019 16:28:09 +0000 Subject: [PATCH 1426/1437] hsr: fix slab-out-of-bounds Read in hsr_debugfs_rename() hsr slave interfaces don't have debugfs directory. So, hsr_debugfs_rename() shouldn't be called when hsr slave interface name is changed. Test commands: ip link add dummy0 type dummy ip link add dummy1 type dummy ip link add hsr0 type hsr slave1 dummy0 slave2 dummy1 ip link set dummy0 name ap Splat looks like: [21071.899367][T22666] ap: renamed from dummy0 [21071.914005][T22666] ================================================================== [21071.919008][T22666] BUG: KASAN: slab-out-of-bounds in hsr_debugfs_rename+0xaa/0xb0 [hsr] [21071.923640][T22666] Read of size 8 at addr ffff88805febcd98 by task ip/22666 [21071.926941][T22666] [21071.927750][T22666] CPU: 0 PID: 22666 Comm: ip Not tainted 5.5.0-rc2+ #240 [21071.929919][T22666] Hardware name: innotek GmbH VirtualBox/VirtualBox, BIOS VirtualBox 12/01/2006 [21071.935094][T22666] Call Trace: [21071.935867][T22666] dump_stack+0x96/0xdb [21071.936687][T22666] ? hsr_debugfs_rename+0xaa/0xb0 [hsr] [21071.937774][T22666] print_address_description.constprop.5+0x1be/0x360 [21071.939019][T22666] ? hsr_debugfs_rename+0xaa/0xb0 [hsr] [21071.940081][T22666] ? hsr_debugfs_rename+0xaa/0xb0 [hsr] [21071.940949][T22666] __kasan_report+0x12a/0x16f [21071.941758][T22666] ? hsr_debugfs_rename+0xaa/0xb0 [hsr] [21071.942674][T22666] kasan_report+0xe/0x20 [21071.943325][T22666] hsr_debugfs_rename+0xaa/0xb0 [hsr] [21071.944187][T22666] hsr_netdev_notify+0x1fe/0x9b0 [hsr] [21071.945052][T22666] ? __module_text_address+0x13/0x140 [21071.945897][T22666] notifier_call_chain+0x90/0x160 [21071.946743][T22666] dev_change_name+0x419/0x840 [21071.947496][T22666] ? __read_once_size_nocheck.constprop.6+0x10/0x10 [21071.948600][T22666] ? netdev_adjacent_rename_links+0x280/0x280 [21071.949577][T22666] ? __read_once_size_nocheck.constprop.6+0x10/0x10 [21071.950672][T22666] ? lock_downgrade+0x6e0/0x6e0 [21071.951345][T22666] ? do_setlink+0x811/0x2ef0 [21071.951991][T22666] do_setlink+0x811/0x2ef0 [21071.952613][T22666] ? is_bpf_text_address+0x81/0xe0 [ ... ] Reported-by: syzbot+9328206518f08318a5fd@syzkaller.appspotmail.com Fixes: 4c2d5e33dcd3 ("hsr: rename debugfs file when interface name is changed") Signed-off-by: Taehee Yoo Signed-off-by: David S. Miller --- net/hsr/hsr_main.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/net/hsr/hsr_main.c b/net/hsr/hsr_main.c index d2ee7125a7f19..9e389accbfc7e 100644 --- a/net/hsr/hsr_main.c +++ b/net/hsr/hsr_main.c @@ -46,7 +46,8 @@ static int hsr_netdev_notify(struct notifier_block *nb, unsigned long event, hsr_check_carrier_and_operstate(hsr); break; case NETDEV_CHANGENAME: - hsr_debugfs_rename(dev); + if (is_hsr_master(dev)) + hsr_debugfs_rename(dev); break; case NETDEV_CHANGEADDR: if (port->type == HSR_PT_MASTER) { -- GitLab From 6506f52dcbf8c2afca47bd7a48c18459e1f04f86 Mon Sep 17 00:00:00 2001 From: Robert Beckett Date: Tue, 22 Oct 2019 16:31:41 +0100 Subject: [PATCH 1427/1437] igb: dont drop packets if rx flow control is enabled If Rx flow control has been enabled (via autoneg or forced), packets should not be dropped due to Rx descriptor ring exhaustion. Instead pause frames should be used to apply back pressure. This only applies if VFs are not in use. Move SRRCTL setup to its own function for easy reuse and only set drop enable bit if Rx flow control is not enabled. Since v1: always enable dropping of packets if VFs in use. Signed-off-by: Robert Beckett Tested-by: Aaron Brown Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/igb/igb.h | 1 + drivers/net/ethernet/intel/igb/igb_ethtool.c | 8 ++++ drivers/net/ethernet/intel/igb/igb_main.c | 47 ++++++++++++++------ 3 files changed, 42 insertions(+), 14 deletions(-) diff --git a/drivers/net/ethernet/intel/igb/igb.h b/drivers/net/ethernet/intel/igb/igb.h index ca54e268d157b..49b5fa9d4783b 100644 --- a/drivers/net/ethernet/intel/igb/igb.h +++ b/drivers/net/ethernet/intel/igb/igb.h @@ -661,6 +661,7 @@ void igb_configure_tx_ring(struct igb_adapter *, struct igb_ring *); void igb_configure_rx_ring(struct igb_adapter *, struct igb_ring *); void igb_setup_tctl(struct igb_adapter *); void igb_setup_rctl(struct igb_adapter *); +void igb_setup_srrctl(struct igb_adapter *, struct igb_ring *); netdev_tx_t igb_xmit_frame_ring(struct sk_buff *, struct igb_ring *); void igb_alloc_rx_buffers(struct igb_ring *, u16); void igb_update_stats(struct igb_adapter *); diff --git a/drivers/net/ethernet/intel/igb/igb_ethtool.c b/drivers/net/ethernet/intel/igb/igb_ethtool.c index 4690d6c87f39a..43c4383653896 100644 --- a/drivers/net/ethernet/intel/igb/igb_ethtool.c +++ b/drivers/net/ethernet/intel/igb/igb_ethtool.c @@ -396,6 +396,7 @@ static int igb_set_pauseparam(struct net_device *netdev, struct igb_adapter *adapter = netdev_priv(netdev); struct e1000_hw *hw = &adapter->hw; int retval = 0; + int i; /* 100basefx does not support setting link flow control */ if (hw->dev_spec._82575.eth_flags.e100_base_fx) @@ -428,6 +429,13 @@ static int igb_set_pauseparam(struct net_device *netdev, retval = ((hw->phy.media_type == e1000_media_type_copper) ? igb_force_mac_fc(hw) : igb_setup_link(hw)); + + /* Make sure SRRCTL considers new fc settings for each ring */ + for (i = 0; i < adapter->num_rx_queues; i++) { + struct igb_ring *ring = adapter->rx_ring[i]; + + igb_setup_srrctl(adapter, ring); + } } clear_bit(__IGB_RESETTING, &adapter->state); diff --git a/drivers/net/ethernet/intel/igb/igb_main.c b/drivers/net/ethernet/intel/igb/igb_main.c index d11e64a58ed11..b46bff8fe0568 100644 --- a/drivers/net/ethernet/intel/igb/igb_main.c +++ b/drivers/net/ethernet/intel/igb/igb_main.c @@ -4467,6 +4467,37 @@ static inline void igb_set_vmolr(struct igb_adapter *adapter, wr32(E1000_VMOLR(vfn), vmolr); } +/** + * igb_setup_srrctl - configure the split and replication receive control + * registers + * @adapter: Board private structure + * @ring: receive ring to be configured + **/ +void igb_setup_srrctl(struct igb_adapter *adapter, struct igb_ring *ring) +{ + struct e1000_hw *hw = &adapter->hw; + int reg_idx = ring->reg_idx; + u32 srrctl = 0; + + srrctl = IGB_RX_HDR_LEN << E1000_SRRCTL_BSIZEHDRSIZE_SHIFT; + if (ring_uses_large_buffer(ring)) + srrctl |= IGB_RXBUFFER_3072 >> E1000_SRRCTL_BSIZEPKT_SHIFT; + else + srrctl |= IGB_RXBUFFER_2048 >> E1000_SRRCTL_BSIZEPKT_SHIFT; + srrctl |= E1000_SRRCTL_DESCTYPE_ADV_ONEBUF; + if (hw->mac.type >= e1000_82580) + srrctl |= E1000_SRRCTL_TIMESTAMP; + /* Only set Drop Enable if VFs allocated, or we are supporting multiple + * queues and rx flow control is disabled + */ + if (adapter->vfs_allocated_count || + (!(hw->fc.current_mode & e1000_fc_rx_pause) && + adapter->num_rx_queues > 1)) + srrctl |= E1000_SRRCTL_DROP_EN; + + wr32(E1000_SRRCTL(reg_idx), srrctl); +} + /** * igb_configure_rx_ring - Configure a receive ring after Reset * @adapter: board private structure @@ -4481,7 +4512,7 @@ void igb_configure_rx_ring(struct igb_adapter *adapter, union e1000_adv_rx_desc *rx_desc; u64 rdba = ring->dma; int reg_idx = ring->reg_idx; - u32 srrctl = 0, rxdctl = 0; + u32 rxdctl = 0; /* disable the queue */ wr32(E1000_RXDCTL(reg_idx), 0); @@ -4499,19 +4530,7 @@ void igb_configure_rx_ring(struct igb_adapter *adapter, writel(0, ring->tail); /* set descriptor configuration */ - srrctl = IGB_RX_HDR_LEN << E1000_SRRCTL_BSIZEHDRSIZE_SHIFT; - if (ring_uses_large_buffer(ring)) - srrctl |= IGB_RXBUFFER_3072 >> E1000_SRRCTL_BSIZEPKT_SHIFT; - else - srrctl |= IGB_RXBUFFER_2048 >> E1000_SRRCTL_BSIZEPKT_SHIFT; - srrctl |= E1000_SRRCTL_DESCTYPE_ADV_ONEBUF; - if (hw->mac.type >= e1000_82580) - srrctl |= E1000_SRRCTL_TIMESTAMP; - /* Only set Drop Enable if we are supporting multiple queues */ - if (adapter->vfs_allocated_count || adapter->num_rx_queues > 1) - srrctl |= E1000_SRRCTL_DROP_EN; - - wr32(E1000_SRRCTL(reg_idx), srrctl); + igb_setup_srrctl(adapter, ring); /* set filtering for VMDQ pools */ igb_set_vmolr(adapter, reg_idx & 0x7, true); -- GitLab From b7b462454a5c2aed1d8aecd165d1dae9017bd5fd Mon Sep 17 00:00:00 2001 From: Sasha Neftin Date: Thu, 31 Oct 2019 08:06:07 +0200 Subject: [PATCH 1428/1437] igc: Add scatter gather support Scatter gather is used to do DMA data transfers of data that is written to noncontiguous areas of memory. This patch enables scatter gather support. Signed-off-by: Sasha Neftin Tested-by: Aaron Brown Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/igc/igc_main.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/ethernet/intel/igc/igc_main.c b/drivers/net/ethernet/intel/igc/igc_main.c index 9700527dd797d..75dcaa87d06b6 100644 --- a/drivers/net/ethernet/intel/igc/igc_main.c +++ b/drivers/net/ethernet/intel/igc/igc_main.c @@ -4433,6 +4433,7 @@ static int igc_probe(struct pci_dev *pdev, goto err_sw_init; /* Add supported features to the features list*/ + netdev->features |= NETIF_F_SG; netdev->features |= NETIF_F_RXCSUM; netdev->features |= NETIF_F_HW_CSUM; netdev->features |= NETIF_F_SCTP_CRC; -- GitLab From 21da01fd3bb1124b46e6b15657de36dffcc58af9 Mon Sep 17 00:00:00 2001 From: Sasha Neftin Date: Tue, 5 Nov 2019 11:44:13 +0200 Subject: [PATCH 1429/1437] igc: Improve the DMA mapping flow Improve the probe flow and set both the DMA mask and the coherent to the same thing. Make the flow optimized and cleared. Signed-off-by: Sasha Neftin Tested-by: Aaron Brown Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/igc/igc_main.c | 24 +++++++++-------------- 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/drivers/net/ethernet/intel/igc/igc_main.c b/drivers/net/ethernet/intel/igc/igc_main.c index 75dcaa87d06b6..91acce2e1d1c9 100644 --- a/drivers/net/ethernet/intel/igc/igc_main.c +++ b/drivers/net/ethernet/intel/igc/igc_main.c @@ -4345,32 +4345,26 @@ static int igc_probe(struct pci_dev *pdev, struct net_device *netdev; struct igc_hw *hw; const struct igc_info *ei = igc_info_tbl[ent->driver_data]; - int err; + int err, pci_using_dac; err = pci_enable_device_mem(pdev); if (err) return err; - err = dma_set_mask(&pdev->dev, DMA_BIT_MASK(64)); + pci_using_dac = 0; + err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64)); if (!err) { - err = dma_set_coherent_mask(&pdev->dev, - DMA_BIT_MASK(64)); + pci_using_dac = 1; } else { - err = dma_set_mask(&pdev->dev, DMA_BIT_MASK(32)); + err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); if (err) { - err = dma_set_coherent_mask(&pdev->dev, - DMA_BIT_MASK(32)); - if (err) { - dev_err(&pdev->dev, "igc: Wrong DMA config\n"); - goto err_dma; - } + dev_err(&pdev->dev, + "No usable DMA configuration, aborting\n"); + goto err_dma; } } - err = pci_request_selected_regions(pdev, - pci_select_bars(pdev, - IORESOURCE_MEM), - igc_driver_name); + err = pci_request_mem_regions(pdev, igc_driver_name); if (err) goto err_pci_reg; -- GitLab From faf4dd52e9e34a36254a6ad43369064b6928d504 Mon Sep 17 00:00:00 2001 From: Sasha Neftin Date: Sun, 10 Nov 2019 16:05:20 +0200 Subject: [PATCH 1430/1437] igc: Prefer to use the pci_release_mem_regions method Use the pci_release_mem_regions method instead of the pci_release_selected_regions method Signed-off-by: Sasha Neftin Tested-by: Aaron Brown Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/igc/igc_main.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/net/ethernet/intel/igc/igc_main.c b/drivers/net/ethernet/intel/igc/igc_main.c index 91acce2e1d1c9..de8e4b570fa6b 100644 --- a/drivers/net/ethernet/intel/igc/igc_main.c +++ b/drivers/net/ethernet/intel/igc/igc_main.c @@ -4527,8 +4527,7 @@ err_sw_init: err_ioremap: free_netdev(netdev); err_alloc_etherdev: - pci_release_selected_regions(pdev, - pci_select_bars(pdev, IORESOURCE_MEM)); + pci_release_mem_regions(pdev); err_pci_reg: err_dma: pci_disable_device(pdev); -- GitLab From 57cd472c2baa1d1dc7f037d2c6d6d7d29be29d5d Mon Sep 17 00:00:00 2001 From: Sasha Neftin Date: Sun, 10 Nov 2019 17:57:44 +0200 Subject: [PATCH 1431/1437] igc: Remove excess parameter description from igc_is_non_eop The function description for igc_is_non_eop includes an extra @skb parameter description. This parameter doesn't exist on the function, so remove it. Suggested-by: Jacob Keller Signed-off-by: Sasha Neftin Tested-by: Aaron Brown Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/igc/igc_main.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/net/ethernet/intel/igc/igc_main.c b/drivers/net/ethernet/intel/igc/igc_main.c index de8e4b570fa6b..ac420a9b4d2bd 100644 --- a/drivers/net/ethernet/intel/igc/igc_main.c +++ b/drivers/net/ethernet/intel/igc/igc_main.c @@ -1485,7 +1485,6 @@ static bool igc_can_reuse_rx_page(struct igc_rx_buffer *rx_buffer) * igc_is_non_eop - process handling of non-EOP buffers * @rx_ring: Rx ring being processed * @rx_desc: Rx descriptor for current buffer - * @skb: current socket buffer containing buffer in progress * * This function updates next to clean. If the buffer is an EOP buffer * this function exits returning false, otherwise it will place the -- GitLab From 085c858950419f5a7eea351c2e5cb999b4caad2d Mon Sep 17 00:00:00 2001 From: Sasha Neftin Date: Mon, 11 Nov 2019 09:04:12 +0200 Subject: [PATCH 1432/1437] igc: Fix the parameter description for igc_alloc_rx_buffers The function description for igc_alloc_rx_buffers has not reflected the function meaning. Add meaningful description. Signed-off-by: Sasha Neftin Tested-by: Aaron Brown Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/igc/igc_main.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/intel/igc/igc_main.c b/drivers/net/ethernet/intel/igc/igc_main.c index ac420a9b4d2bd..833770fd16d28 100644 --- a/drivers/net/ethernet/intel/igc/igc_main.c +++ b/drivers/net/ethernet/intel/igc/igc_main.c @@ -1566,7 +1566,8 @@ static void igc_put_rx_buffer(struct igc_ring *rx_ring, /** * igc_alloc_rx_buffers - Replace used receive buffers; packet split - * @adapter: address of board private structure + * @rx_ring: rx descriptor ring + * @cleaned_count: number of buffers to clean */ static void igc_alloc_rx_buffers(struct igc_ring *rx_ring, u16 cleaned_count) { -- GitLab From 86efeccd5a7adcfbe12c0ba558615e53a33b67fc Mon Sep 17 00:00:00 2001 From: Sasha Neftin Date: Tue, 12 Nov 2019 17:13:32 +0200 Subject: [PATCH 1433/1437] igc: Fix parameter descriptions for a several functions igc_watchdog, igc_set_interrupt_capability, igc_init_interrupt_scheme, __igc_open and __igc_close parameter descriptions has not reflected functions meaning. Add meaningful description. Signed-off-by: Sasha Neftin Tested-by: Aaron Brown Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/igc/igc_main.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/intel/igc/igc_main.c b/drivers/net/ethernet/intel/igc/igc_main.c index 833770fd16d28..d3a45d3cbcd92 100644 --- a/drivers/net/ethernet/intel/igc/igc_main.c +++ b/drivers/net/ethernet/intel/igc/igc_main.c @@ -3109,7 +3109,7 @@ bool igc_has_link(struct igc_adapter *adapter) /** * igc_watchdog - Timer Call-back - * @data: pointer to adapter cast into an unsigned long + * @t: timer for the watchdog */ static void igc_watchdog(struct timer_list *t) { @@ -3638,6 +3638,7 @@ static int igc_poll(struct napi_struct *napi, int budget) /** * igc_set_interrupt_capability - set MSI or MSI-X if supported * @adapter: Pointer to adapter structure + * @msix: boolean value for MSI-X capability * * Attempt to configure interrupts using the best available * capabilities of the hardware and kernel. @@ -3906,6 +3907,7 @@ static void igc_cache_ring_register(struct igc_adapter *adapter) /** * igc_init_interrupt_scheme - initialize interrupts, allocate queues/vectors * @adapter: Pointer to adapter structure + * @msix: boolean for MSI-X capability * * This function initializes the interrupts and allocates all of the queues. */ @@ -4073,8 +4075,9 @@ static void igc_write_itr(struct igc_q_vector *q_vector) } /** - * igc_open - Called when a network interface is made active + * __igc_open - Called when a network interface is made active * @netdev: network interface device structure + * @resuming: boolean indicating if the device is resuming * * Returns 0 on success, negative value on failure * @@ -4164,8 +4167,9 @@ static int igc_open(struct net_device *netdev) } /** - * igc_close - Disables a network interface + * __igc_close - Disables a network interface * @netdev: network interface device structure + * @suspending: boolean indicating the device is suspending * * Returns 0, this is not allowed to fail * -- GitLab From 4439dc427d83c67aaa8447091c479c482ff85af9 Mon Sep 17 00:00:00 2001 From: Sasha Neftin Date: Wed, 13 Nov 2019 11:27:29 +0200 Subject: [PATCH 1434/1437] igc: Add 64 bit DMA access support On relevant platforms ndo_start_xmit can handle socket buffer fragments in high memory Signed-off-by: Sasha Neftin Tested-by: Aaron Brown Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/igc/igc_main.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/ethernet/intel/igc/igc_main.c b/drivers/net/ethernet/intel/igc/igc_main.c index d3a45d3cbcd92..b85009837cdff 100644 --- a/drivers/net/ethernet/intel/igc/igc_main.c +++ b/drivers/net/ethernet/intel/igc/igc_main.c @@ -4445,6 +4445,9 @@ static int igc_probe(struct pci_dev *pdev, netdev->hw_features |= NETIF_F_NTUPLE; netdev->hw_features |= netdev->features; + if (pci_using_dac) + netdev->features |= NETIF_F_HIGHDMA; + /* MTU range: 68 - 9216 */ netdev->min_mtu = ETH_MIN_MTU; netdev->max_mtu = MAX_STD_JUMBO_FRAME_SIZE; -- GitLab From 9513d2a5dc7f3f2c037bfd2ea35264cc783b954e Mon Sep 17 00:00:00 2001 From: Sasha Neftin Date: Thu, 14 Nov 2019 09:54:46 +0200 Subject: [PATCH 1435/1437] igc: Add legacy power management support Add suspend, resume, runtime_suspend, runtime_resume and runtime_idle callbacks implementation. Reported-by: kbuild test robot Signed-off-by: Sasha Neftin Tested-by: Aaron Brown Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/igc/igc.h | 2 + drivers/net/ethernet/intel/igc/igc_defines.h | 31 +++ drivers/net/ethernet/intel/igc/igc_main.c | 204 +++++++++++++++++++ drivers/net/ethernet/intel/igc/igc_regs.h | 9 + 4 files changed, 246 insertions(+) diff --git a/drivers/net/ethernet/intel/igc/igc.h b/drivers/net/ethernet/intel/igc/igc.h index 0868677d43edb..612fe9ec81a49 100644 --- a/drivers/net/ethernet/intel/igc/igc.h +++ b/drivers/net/ethernet/intel/igc/igc.h @@ -370,6 +370,8 @@ struct igc_adapter { struct timer_list dma_err_timer; struct timer_list phy_info_timer; + u32 wol; + u32 en_mng_pt; u16 link_speed; u16 link_duplex; diff --git a/drivers/net/ethernet/intel/igc/igc_defines.h b/drivers/net/ethernet/intel/igc/igc_defines.h index f3788f0b95b4a..50dffd5db6065 100644 --- a/drivers/net/ethernet/intel/igc/igc_defines.h +++ b/drivers/net/ethernet/intel/igc/igc_defines.h @@ -10,6 +10,37 @@ #define IGC_CTRL_EXT_DRV_LOAD 0x10000000 /* Drv loaded bit for FW */ +/* Definitions for power management and wakeup registers */ +/* Wake Up Control */ +#define IGC_WUC_PME_EN 0x00000002 /* PME Enable */ + +/* Wake Up Filter Control */ +#define IGC_WUFC_LNKC 0x00000001 /* Link Status Change Wakeup Enable */ +#define IGC_WUFC_MC 0x00000008 /* Directed Multicast Wakeup Enable */ + +#define IGC_CTRL_ADVD3WUC 0x00100000 /* D3 WUC */ + +/* Wake Up Status */ +#define IGC_WUS_EX 0x00000004 /* Directed Exact */ +#define IGC_WUS_ARPD 0x00000020 /* Directed ARP Request */ +#define IGC_WUS_IPV4 0x00000040 /* Directed IPv4 */ +#define IGC_WUS_IPV6 0x00000080 /* Directed IPv6 */ +#define IGC_WUS_NSD 0x00000400 /* Directed IPv6 Neighbor Solicitation */ + +/* Packet types that are enabled for wake packet delivery */ +#define WAKE_PKT_WUS ( \ + IGC_WUS_EX | \ + IGC_WUS_ARPD | \ + IGC_WUS_IPV4 | \ + IGC_WUS_IPV6 | \ + IGC_WUS_NSD) + +/* Wake Up Packet Length */ +#define IGC_WUPL_MASK 0x00000FFF + +/* Wake Up Packet Memory stores the first 128 bytes of the wake up packet */ +#define IGC_WUPM_BYTES 128 + /* Physical Func Reset Done Indication */ #define IGC_CTRL_EXT_LINK_MODE_MASK 0x00C00000 diff --git a/drivers/net/ethernet/intel/igc/igc_main.c b/drivers/net/ethernet/intel/igc/igc_main.c index b85009837cdff..ca7b8d6791f18 100644 --- a/drivers/net/ethernet/intel/igc/igc_main.c +++ b/drivers/net/ethernet/intel/igc/igc_main.c @@ -8,6 +8,7 @@ #include #include #include +#include #include @@ -4581,11 +4582,214 @@ static void igc_remove(struct pci_dev *pdev) pci_disable_device(pdev); } +static int __igc_shutdown(struct pci_dev *pdev, bool *enable_wake, + bool runtime) +{ + struct net_device *netdev = pci_get_drvdata(pdev); + struct igc_adapter *adapter = netdev_priv(netdev); + u32 wufc = runtime ? IGC_WUFC_LNKC : adapter->wol; + struct igc_hw *hw = &adapter->hw; + u32 ctrl, rctl, status; + bool wake; + + rtnl_lock(); + netif_device_detach(netdev); + + if (netif_running(netdev)) + __igc_close(netdev, true); + + igc_clear_interrupt_scheme(adapter); + rtnl_unlock(); + + status = rd32(IGC_STATUS); + if (status & IGC_STATUS_LU) + wufc &= ~IGC_WUFC_LNKC; + + if (wufc) { + igc_setup_rctl(adapter); + igc_set_rx_mode(netdev); + + /* turn on all-multi mode if wake on multicast is enabled */ + if (wufc & IGC_WUFC_MC) { + rctl = rd32(IGC_RCTL); + rctl |= IGC_RCTL_MPE; + wr32(IGC_RCTL, rctl); + } + + ctrl = rd32(IGC_CTRL); + ctrl |= IGC_CTRL_ADVD3WUC; + wr32(IGC_CTRL, ctrl); + + /* Allow time for pending master requests to run */ + igc_disable_pcie_master(hw); + + wr32(IGC_WUC, IGC_WUC_PME_EN); + wr32(IGC_WUFC, wufc); + } else { + wr32(IGC_WUC, 0); + wr32(IGC_WUFC, 0); + } + + wake = wufc || adapter->en_mng_pt; + if (!wake) + igc_power_down_link(adapter); + else + igc_power_up_link(adapter); + + if (enable_wake) + *enable_wake = wake; + + /* Release control of h/w to f/w. If f/w is AMT enabled, this + * would have already happened in close and is redundant. + */ + igc_release_hw_control(adapter); + + pci_disable_device(pdev); + + return 0; +} + +#ifdef CONFIG_PM +static int __maybe_unused igc_runtime_suspend(struct device *dev) +{ + return __igc_shutdown(to_pci_dev(dev), NULL, 1); +} + +static void igc_deliver_wake_packet(struct net_device *netdev) +{ + struct igc_adapter *adapter = netdev_priv(netdev); + struct igc_hw *hw = &adapter->hw; + struct sk_buff *skb; + u32 wupl; + + wupl = rd32(IGC_WUPL) & IGC_WUPL_MASK; + + /* WUPM stores only the first 128 bytes of the wake packet. + * Read the packet only if we have the whole thing. + */ + if (wupl == 0 || wupl > IGC_WUPM_BYTES) + return; + + skb = netdev_alloc_skb_ip_align(netdev, IGC_WUPM_BYTES); + if (!skb) + return; + + skb_put(skb, wupl); + + /* Ensure reads are 32-bit aligned */ + wupl = roundup(wupl, 4); + + memcpy_fromio(skb->data, hw->hw_addr + IGC_WUPM_REG(0), wupl); + + skb->protocol = eth_type_trans(skb, netdev); + netif_rx(skb); +} + +static int __maybe_unused igc_resume(struct device *dev) +{ + struct pci_dev *pdev = to_pci_dev(dev); + struct net_device *netdev = pci_get_drvdata(pdev); + struct igc_adapter *adapter = netdev_priv(netdev); + struct igc_hw *hw = &adapter->hw; + u32 err, val; + + pci_set_power_state(pdev, PCI_D0); + pci_restore_state(pdev); + pci_save_state(pdev); + + if (!pci_device_is_present(pdev)) + return -ENODEV; + err = pci_enable_device_mem(pdev); + if (err) { + dev_err(&pdev->dev, + "igc: Cannot enable PCI device from suspend\n"); + return err; + } + pci_set_master(pdev); + + pci_enable_wake(pdev, PCI_D3hot, 0); + pci_enable_wake(pdev, PCI_D3cold, 0); + + if (igc_init_interrupt_scheme(adapter, true)) { + dev_err(&pdev->dev, "Unable to allocate memory for queues\n"); + return -ENOMEM; + } + + igc_reset(adapter); + + /* let the f/w know that the h/w is now under the control of the + * driver. + */ + igc_get_hw_control(adapter); + + val = rd32(IGC_WUS); + if (val & WAKE_PKT_WUS) + igc_deliver_wake_packet(netdev); + + wr32(IGC_WUS, ~0); + + rtnl_lock(); + if (!err && netif_running(netdev)) + err = __igc_open(netdev, true); + + if (!err) + netif_device_attach(netdev); + rtnl_unlock(); + + return err; +} + +static int __maybe_unused igc_runtime_resume(struct device *dev) +{ + return igc_resume(dev); +} + +static int __maybe_unused igc_suspend(struct device *dev) +{ + return __igc_shutdown(to_pci_dev(dev), NULL, 0); +} + +static int __maybe_unused igc_runtime_idle(struct device *dev) +{ + struct net_device *netdev = dev_get_drvdata(dev); + struct igc_adapter *adapter = netdev_priv(netdev); + + if (!igc_has_link(adapter)) + pm_schedule_suspend(dev, MSEC_PER_SEC * 5); + + return -EBUSY; +} +#endif /* CONFIG_PM */ + +static void igc_shutdown(struct pci_dev *pdev) +{ + bool wake; + + __igc_shutdown(pdev, &wake, 0); + + if (system_state == SYSTEM_POWER_OFF) { + pci_wake_from_d3(pdev, wake); + pci_set_power_state(pdev, PCI_D3hot); + } +} + +#ifdef CONFIG_PM +static const struct dev_pm_ops igc_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(igc_suspend, igc_resume) + SET_RUNTIME_PM_OPS(igc_runtime_suspend, igc_runtime_resume, + igc_runtime_idle) +}; +#endif + static struct pci_driver igc_driver = { .name = igc_driver_name, .id_table = igc_pci_tbl, .probe = igc_probe, .remove = igc_remove, +#ifdef CONFIG_PM + .driver.pm = &igc_pm_ops, +#endif + .shutdown = igc_shutdown, }; void igc_set_flag_queue_pairs(struct igc_adapter *adapter, diff --git a/drivers/net/ethernet/intel/igc/igc_regs.h b/drivers/net/ethernet/intel/igc/igc_regs.h index 50d7c04dccf59..93a9139f08c50 100644 --- a/drivers/net/ethernet/intel/igc/igc_regs.h +++ b/drivers/net/ethernet/intel/igc/igc_regs.h @@ -215,6 +215,15 @@ /* Shadow Ram Write Register - RW */ #define IGC_SRWR 0x12018 +/* Wake Up registers */ +#define IGC_WUC 0x05800 /* Wakeup Control - RW */ +#define IGC_WUFC 0x05808 /* Wakeup Filter Control - RW */ +#define IGC_WUS 0x05810 /* Wakeup Status - R/W1C */ +#define IGC_WUPL 0x05900 /* Wakeup Packet Length - RW */ + +/* Wake Up packet memory */ +#define IGC_WUPM_REG(_i) (0x05A00 + ((_i) * 4)) + /* forward declaration */ struct igc_hw; u32 igc_rd32(struct igc_hw *hw, u32 reg); -- GitLab From c557a4b3f73837bf074e643e163a37f848c9781c Mon Sep 17 00:00:00 2001 From: Alexander Duyck Date: Thu, 31 Oct 2019 09:58:51 -0700 Subject: [PATCH 1436/1437] e1000e: Use netdev_info instead of pr_info for link messages Replace the pr_info calls with netdev_info in all cases related to the netdevice link state. As a result of this patch the link messages will change as shown below. Before: e1000e: ens3 NIC Link is Down e1000e: ens3 NIC Link is Up 1000 Mbps Full Duplex, Flow Control: Rx/Tx After: e1000e 0000:00:03.0 ens3: NIC Link is Down e1000e 0000:00:03.0 ens3: NIC Link is Up 1000 Mbps Full Duplex, Flow Control: Rx/Tx Suggested-by: Joe Perches Signed-off-by: Alexander Duyck Acked-by: Neil Horman Tested-by: Aaron Brown Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/e1000e/netdev.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/drivers/net/ethernet/intel/e1000e/netdev.c b/drivers/net/ethernet/intel/e1000e/netdev.c index 4c220600ea9ac..8797913b2702c 100644 --- a/drivers/net/ethernet/intel/e1000e/netdev.c +++ b/drivers/net/ethernet/intel/e1000e/netdev.c @@ -4723,7 +4723,7 @@ int e1000e_close(struct net_device *netdev) e1000_free_irq(adapter); /* Link status message must follow this format */ - pr_info("%s NIC Link is Down\n", netdev->name); + netdev_info(netdev, "NIC Link is Down\n"); } napi_disable(&adapter->napi); @@ -5073,12 +5073,13 @@ static void e1000_print_link_info(struct e1000_adapter *adapter) u32 ctrl = er32(CTRL); /* Link status message must follow this format for user tools */ - pr_info("%s NIC Link is Up %d Mbps %s Duplex, Flow Control: %s\n", - adapter->netdev->name, adapter->link_speed, - adapter->link_duplex == FULL_DUPLEX ? "Full" : "Half", - (ctrl & E1000_CTRL_TFCE) && (ctrl & E1000_CTRL_RFCE) ? "Rx/Tx" : - (ctrl & E1000_CTRL_RFCE) ? "Rx" : - (ctrl & E1000_CTRL_TFCE) ? "Tx" : "None"); + netdev_info(adapter->netdev, + "NIC Link is Up %d Mbps %s Duplex, Flow Control: %s\n", + adapter->link_speed, + adapter->link_duplex == FULL_DUPLEX ? "Full" : "Half", + (ctrl & E1000_CTRL_TFCE) && (ctrl & E1000_CTRL_RFCE) ? "Rx/Tx" : + (ctrl & E1000_CTRL_RFCE) ? "Rx" : + (ctrl & E1000_CTRL_TFCE) ? "Tx" : "None"); } static bool e1000e_has_link(struct e1000_adapter *adapter) @@ -5307,7 +5308,7 @@ static void e1000_watchdog_task(struct work_struct *work) adapter->link_speed = 0; adapter->link_duplex = 0; /* Link status message must follow this format */ - pr_info("%s NIC Link is Down\n", adapter->netdev->name); + netdev_info(netdev, "NIC Link is Down\n"); netif_carrier_off(netdev); netif_stop_queue(netdev); if (!test_bit(__E1000_DOWN, &adapter->state)) -- GitLab From 684ea87cc312c98386c667d1046c61eb92ea8379 Mon Sep 17 00:00:00 2001 From: Sasha Neftin Date: Mon, 2 Dec 2019 09:56:26 +0200 Subject: [PATCH 1437/1437] igc: Remove serdes comments from a description of methods Serdes interface is not applicable for i225 devices. Remove this from comments and make comments more clearly. Signed-off-by: Sasha Neftin Tested-by: Aaron Brown Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/igc/igc_main.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/intel/igc/igc_main.c b/drivers/net/ethernet/intel/igc/igc_main.c index ca7b8d6791f18..25989c087aca0 100644 --- a/drivers/net/ethernet/intel/igc/igc_main.c +++ b/drivers/net/ethernet/intel/igc/igc_main.c @@ -115,7 +115,7 @@ void igc_reset(struct igc_adapter *adapter) } /** - * igc_power_up_link - Power up the phy/serdes link + * igc_power_up_link - Power up the phy link * @adapter: address of board private structure */ static void igc_power_up_link(struct igc_adapter *adapter) @@ -129,7 +129,7 @@ static void igc_power_up_link(struct igc_adapter *adapter) } /** - * igc_power_down_link - Power down the phy/serdes link + * igc_power_down_link - Power down the phy link * @adapter: address of board private structure */ static void igc_power_down_link(struct igc_adapter *adapter) -- GitLab