7.2.10-rc1 review
Greg Kroah-Hartman
maintainerPATCH 126/428 net: page_pool: fix use-after-free in page_pool_recycle_ring_bulk()
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Mina Almasry <[email protected]>
[ Upstream commit bdea4980182e3badac74f04df6c07492b91c569e ]
With CONFIG_PAGE_POOL_STATS=y, page_pool_recycle_ring_bulk() updates the recycle stats after dropping the producer lock. If it just recycled the last inflight netmems of a pool being destroyed, page_pool_release() can pass its producer-lock barrier and free the pool before recycle_stat_add() runs.
Commit fcc680a647ba7 ("page_pool: allow mixing PPs within one bulk") moved this stat update after the unlock. Commit 271683bb2cf32 ("page_pool: Fix use-after-free in page_pool_recycle_in_ring") later added the barrier, but only fixed page_pool_recycle_in_ring(). Move the update back under the lock.
--- net/core/page_pool.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
net/core/page_pool.c +1 -1
@@ -951,8 +951,8 @@ static void page_pool_recycle_ring_bulk(struct page_pool *pool, } } -page_pool_producer_unlock (pool, in_softirq);recycle_stat_add (pool, ring, i); +page_pool_producer_unlock (pool, in_softirq);/* Hopefully all pages were returned into ptr_ring */ if (likely (i == bulk_len))
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 127/428 net: stmmac: fix rx Scatter-Gather support
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Lorenzo Bianconi <[email protected]>
[ Upstream commit 88095728511e0df9c89528944f5a1762ad298983 ]
When a received frame is larger than dma_buf_sz, the DMA scatters it across multiple RX descriptors (rx Scatter-Gather). The secondary RX buffer (sec_page) was only allocated and programmed when split-header (SPH) was active, so for regular frames buffer2 was neither allocated nor backed by a valid mapping. As soon as an incoming frame overflowed buffer1, the DMA wrote the overflow into the unmapped secondary-buffer address, triggering an SMMU translation fault on IOMMU-based platforms:
arm-smmu 15000000.iommu: Unhandled context fault: fsr=0x402, iova=0x00000000, fsynr=0x7f0011, cbfrsynra=0x1c90, cb=11
arm-smmu 15000000.iommu: FSR = 00000402 [Format=2 TF], SID=0x1c90
arm-smmu 15000000.iommu: FSYNR0 = 007f0011 [S1CBNDX=127 WNR PLVL=1]
Enable scatter-gather for non-SPH frames on cores that can program an independent secondary RX buffer (GMAC4/XGMAC): allocate and mark buffer2 as valid in stmmac_init_rx_buffers() and stmmac_rx_refill(), and account for it in the buffer length computation. Legacy cores have no set_sec_addr op, so they keep buffer2 disabled.
Since buffer2 is handed to the DMA at page offset 0, the page pool sync window is widened to cover both buffers in every mode: offset is set to 0 and max_len to dma_buf_sz + stmmac_rx_offset().
The FCS can straddle the buffer1/buffer2 boundary and a descriptor boundary, so it is now stripped from the tail of the assembled frame with pskb_trim() instead of from a single buffer, avoiding an unsigned underflow for frames that overflow a buffer by 1..3 bytes. For single-buffer frames the XDP program must not see the FCS, so it is removed from the XDP buffer before the program runs.
Native XDP currently only supports single-buffer (linear) frames. An oversized frame accepted by the MAC (jumbo enabled) is received via buffer2; the XDP program only sees buffer1, so on a TX/REDIRECT verdict the frame is forwarded truncated. XDP multi-buffer support to handle this case is planned as a follow-up.
AF_XDP zero-copy RX is not covered by this change: a ZC queue still programs buffer2 at DMA address 0 (and XGMAC has no buffer2-valid bit), so an oversized frame overflowing buffer1 can still trigger the same SMMU translation fault. Handling is planned as a follow-up.
--- .../net/ethernet/stmicro/stmmac/stmmac_main.c | 83 +++++++++++-------- 1 file changed, 49 insertions(+), 34 deletions(-)
drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +49 -34
@@ -1629,6 +1629,16 @@ static void stmmac_clear_descriptors(struct stmmac_priv *priv,stmmac_clear_tx_descriptors (priv, dma_conf, queue); } +static bool stmmac_rx_check_buf2_cap (struct stmmac_priv *priv) +{ +/* Only cores that can program an independent secondary RX buffer +* (used for scatter-gather overflow or split-header payload) back +* buffer2. Legacy cores have no set_sec_addr op, so buffer2 is +* never handed to the hardware there. +*/ +return priv->hw->desc && priv->hw->desc->set_sec_addr; +} +/** * stmmac_init_rx_buffers - init the RX descriptor buffer. * @priv: driver private structure
@@ -1659,16 +1669,13 @@ static int stmmac_init_rx_buffers(struct stmmac_priv *priv, buf->page_offset =stmmac_rx_offset (priv); } -if (priv->sph_active && !buf->sec_page) { +if (stmmac_rx_check_buf2_cap (priv) && !buf->sec_page) { buf->sec_page =page_pool_alloc_pages (rx_q->page_pool, gfp);if (!buf->sec_page)return -ENOMEM ; buf->sec_addr =page_pool_get_dma_addr (buf->sec_page);stmmac_set_desc_sec_addr (priv, p, buf->sec_addr, true); - }else { - buf->sec_page =NULL ; -stmmac_set_desc_sec_addr (priv, p, buf->sec_addr, false); } buf->addr =page_pool_get_dma_addr (buf->page) + buf->page_offset;
@@ -2256,13 +2263,8 @@ static int __alloc_dma_rx_desc_resources(struct stmmac_priv *priv, pp_params.nid =dev_to_node (priv->device); pp_params.dev = priv->device; pp_params.dma_dir = xdp_prog ?DMA_BIDIRECTIONAL :DMA_FROM_DEVICE ; - pp_params.offset =stmmac_rx_offset (priv); - pp_params.max_len = dma_conf->dma_buf_sz; - -if (priv->sph_active) { - pp_params.offset =0 ; - pp_params.max_len +=stmmac_rx_offset (priv); - } + pp_params.offset =0 ; + pp_params.max_len = dma_conf->dma_buf_sz +stmmac_rx_offset (priv); rx_q->page_pool =page_pool_create (&pp_params);if (IS_ERR (rx_q->page_pool)) {
@@ -5095,7 +5097,7 @@ static inline void stmmac_rx_refill(struct stmmac_priv *priv, u32 queue)break ; } -if (priv->sph_active && !buf->sec_page) { +if (stmmac_rx_check_buf2_cap (priv) && !buf->sec_page) { buf->sec_page =page_pool_alloc_pages (rx_q->page_pool, gfp);if (!buf->sec_page)break ;
@@ -5106,10 +5108,8 @@ static inline void stmmac_rx_refill(struct stmmac_priv *priv, u32 queue) buf->addr =page_pool_get_dma_addr (buf->page) + buf->page_offset;stmmac_set_desc_addr (priv, p, buf->addr); -if (priv->sph_active) -stmmac_set_desc_sec_addr (priv, p, buf->sec_addr, true); -else -stmmac_set_desc_sec_addr (priv, p, buf->sec_addr, false); +stmmac_set_desc_sec_addr (priv, p, buf->sec_addr, +stmmac_rx_check_buf2_cap (priv));stmmac_refill_desc3 (priv, rx_q, p); rx_q->rx_count_frames++;
@@ -5140,7 +5140,9 @@ static unsigned int stmmac_rx_buf1_len(struct stmmac_priv *priv,unsigned int plen =0 , hlen =0 ;int coe = priv->hw->rx_csum; -/* Not first descriptor, buffer is always zero */ +/* Not first descriptor, SPH enabled: buffer1 only carries the +* split header of the first descriptor, so it is zero here. +*/ if (priv->sph_active && len)return 0 ;
@@ -5151,14 +5153,16 @@ static unsigned int stmmac_rx_buf1_len(struct stmmac_priv *priv,return hlen; } -/* First descriptor, not last descriptor and not split header */ +/* Not last descriptor and not split header: buffer1 is fully filled */ if (status & rx_not_ls)return priv->dma_conf.dma_buf_sz; plen =stmmac_get_rx_frame_len (priv, p, coe); -/* First descriptor and last descriptor and not split header */ - return min_t(unsigned int, priv->dma_conf.dma_buf_sz, plen); +/* Last descriptor and not split header: buffer1 holds the remaining +* bytes of the frame, up to dma_buf_sz +*/ + return min_t(unsigned int, priv->dma_conf.dma_buf_sz, plen - len); } static unsigned int stmmac_rx_buf2_len(struct stmmac_priv *priv,
@@ -5168,8 +5172,7 @@ static unsigned int stmmac_rx_buf2_len(struct stmmac_priv *priv,int coe = priv->hw->rx_csum;unsigned int plen =0 ; -/* Not split header, buffer is not available */ -if (!priv->sph_active) +if (!stmmac_rx_check_buf2_cap (priv))return 0 ;/* For GMAC4, when split header is enabled, in some rare cases, the
@@ -5186,14 +5189,15 @@ static unsigned int stmmac_rx_buf2_len(struct stmmac_priv *priv,* Thus 'plen - len' always gives the correct length of buf2. */ -/* Not GMAC4 and not last descriptor */ -if (priv->plat->core_type !=DWMAC_CORE_GMAC4 && (status & rx_not_ls)) +/* Not GMAC4, or non-SPH and not last descriptor */ +if ((priv->plat->core_type !=DWMAC_CORE_GMAC4 || !priv->sph_active) && + (status & rx_not_ls))return priv->dma_conf.dma_buf_sz;/* GMAC4 or last descriptor */ plen =stmmac_get_rx_frame_len (priv, p, coe); -return plen - len; +return plen > len ? plen - len :0 ; } static int stmmac_xdp_xmit_xdpf(struct stmmac_priv *priv, int queue,
@@ -5716,6 +5720,7 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit, u32 queue)unsigned int desc_size;struct sk_buff *skb =NULL ;struct stmmac_xdp_buff ctx; +bool fcs_stripped = false;int xdp_status =0 ;int bufsz;
@@ -5805,24 +5810,25 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit, u32 queue) len += buf2_len;/* ACS is disabled; strip manually. */ -if (likely (!(status & rx_not_ls))) { -if (buf2_len) { - buf2_len -=ETH_FCS_LEN ; - len -=ETH_FCS_LEN ; - }else if (buf1_len) { - buf1_len -=ETH_FCS_LEN ; - len -=ETH_FCS_LEN ; - } - } +if (likely (!(status & rx_not_ls))) + len -=ETH_FCS_LEN ;if (!skb) {unsigned int pre_len, sync_len; +/* Each frame starts here: reset the FCS handling */ + fcs_stripped = false; +dma_sync_single_for_cpu (priv->device, buf->addr, buf1_len, dma_dir);net_prefetch (page_address (buf->page) + buf->page_offset); +if (stmmac_xdp_is_enabled (priv) && !buf2_len) { + buf1_len -=ETH_FCS_LEN ; + fcs_stripped = true; + } +xdp_init_buff (&ctx.xdp, bufsz, &rx_q->xdp_rxq);xdp_prepare_buff (&ctx.xdp,page_address (buf->page), buf->page_offset, buf1_len, true);
@@ -5923,6 +5929,15 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit, u32 queue)/* Got entire packet into SKB. Finish it. */ +/* Remove FCS if needed */ +if (!fcs_stripped &&pskb_trim (skb, len)) { +dev_kfree_skb_any (skb); + skb =NULL ; + rx_dropped++; + count++; +continue ; + } +stmmac_get_rx_hwtstamp (priv, p, np, skb);if (priv->hw->hw_vlan_en)
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 128/428 seg6: ensure packet data is writable before modifying SRH and IPv6 DA
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Andrea Mayer <[email protected]>
[ Upstream commit 8ae3f4067ba4ae3a15581392f6dac422e3d3def0 ]
advance_nextseg() modifies the SRH Segments Left field and the IPv6 destination address without ensuring the packet data is writable. seg6_next_csid_advance_arg() has the same problem when it advances the NEXT-C-SID argument in the destination address.
The skb may be cloned, for example by an AF_PACKET socket receiving on the ingress device. advance_nextseg() and seg6_next_csid_advance_arg() then write into the packet data shared with the clone. A read from that socket can return the modified packet instead of the received one. The simplified path below shows this for advance_nextseg():
__netif_receive_skb_one_core
__netif_receive_skb_core
deliver_skb [orig: users=2, cloned=0]
packet_rcv
skb_clone clone queued to the AF_PACKET socket
consume_skb(orig) [orig: users=1, cloned=1]
ipv6_rcv
ip6_rcv_core skb_share_check: no-op [orig: users=1]
[...]
input_action_end_core
advance_nextseg writes into the data shared with the clone
Call skb_ensure_writable() in advance_nextseg() and in seg6_next_csid_advance_arg() before they modify the packet data. skb_ensure_writable() may reallocate skb->head, which invalidates the pointers into the packet data taken before the call. advance_nextseg() now returns a valid SRH pointer, or NULL if skb_ensure_writable() fails.seg6_next_csid_advance_arg() takes the pointer to the destination address from the skb after skb_ensure_writable().
On failure, the callers drop the packet with SKB_DROP_REASON_NOMEM.
--- net/ipv6/seg6_local.c | 140 +++++++++++++++++++++++++++++++++++------- 1 file changed, 117 insertions(+), 23 deletions(-)
net/ipv6/seg6_local.c +117 -23
@@ -278,13 +278,43 @@ static bool decap_and_validate(struct sk_buff *skb, int proto)return true; } -static void advance_nextseg (struct ipv6_sr_hdr *srh,struct in6_addr *daddr) +/* advance the SRH to the next segment and set the IPv6 DA accordingly. +* skb pointers may change: after this call, the caller must evaluate again +* any pointer into the packet data. +* +* This function returns: +* - the SRH on success; +* - NULL when the skb cannot be made writable. In this case, the function +* sets the drop reason to SKB_DROP_REASON_NOMEM. +*/ +static struct ipv6_sr_hdr *advance_nextseg (struct sk_buff *skb, +struct ipv6_sr_hdr *srh, +enum skb_drop_reason *reason) {struct in6_addr *addr; +int srhoff; +int wlen; + + srhoff = (unsigned char *)srh - skb->data; +/* we write only the Segment Left field and the IPv6 DA. The segment +* list is only read, and seg6_get_srh() already pulled it into the +* linear area. +*/ + wlen = srhoff +sizeof (*srh); + +if (unlikely (skb_ensure_writable (skb, wlen))) { + *reason =SKB_DROP_REASON_NOMEM ; +return NULL ; + } + +/* skb_ensure_writable() may change skb pointers; evaluate srh again */ + srh = (struct ipv6_sr_hdr *)(skb->data + srhoff); srh->segments_left--; addr = srh->segments + srh->segments_left; - *daddr = *addr; +ipv6_hdr (skb)->daddr = *addr; + +return srh; } static int
@@ -382,12 +412,26 @@ static bool seg6_next_csid_is_arg_zero(const struct in6_addr *addr,return true; } -/* assume that DA.Argument length > 0 */ -static void seg6_next_csid_advance_arg (struct in6_addr *addr, -const struct seg6_flavors_info *finfo) +/* assume that DA.Argument length > 0. +* skb pointers may change: after this call, the caller must evaluate again +* any pointer into the packet data. +* +* This function returns: +* - SKB_NOT_DROPPED_YET on success; +* - SKB_DROP_REASON_NOMEM when the skb cannot be made writable. +*/ +static enum skb_drop_reason +seg6_next_csid_advance_arg (struct sk_buff *skb, +const struct seg6_flavors_info *finfo) {__u8 fnc_octects =seg6_flv_lcnode_func_octects (finfo);__u8 blk_octects =seg6_flv_lcblock_octects (finfo); +struct in6_addr *addr; + +if (unlikely (skb_ensure_writable (skb,sizeof (struct ipv6hdr )))) +return SKB_DROP_REASON_NOMEM ; + + addr = &ipv6_hdr (skb)->daddr;/* advance DA.Argument */ memmove(&addr->s6_addr[blk_octects],
@@ -395,6 +439,8 @@ static void seg6_next_csid_advance_arg(struct in6_addr *addr, 16 - blk_octects - fnc_octects);memset (&addr->s6_addr[16 - fnc_octects],0x00 , fnc_octects); + +return SKB_NOT_DROPPED_YET ; } static int input_action_end_finish(struct sk_buff *skb,
@@ -408,31 +454,42 @@ static int input_action_end_finish(struct sk_buff *skb,static int input_action_end_core (struct sk_buff *skb,struct seg6_local_lwt *slwt) { +enum skb_drop_reason reason =SKB_DROP_REASON_NOT_SPECIFIED ;struct ipv6_sr_hdr *srh; +int err = -EINVAL ; srh =get_and_validate_srh (skb);if (!srh) goto drop; -advance_nextseg (srh, &ipv6_hdr (skb)->daddr); + srh =advance_nextseg (skb, srh, &reason); +if (!srh) { + err = -ENOMEM ; + goto drop; + }return input_action_end_finish (skb, slwt); drop: -kfree_skb (skb); -return -EINVAL ; +kfree_skb_reason (skb, reason); +return err; }static int end_next_csid_core (struct sk_buff *skb,struct seg6_local_lwt *slwt) {const struct seg6_flavors_info *finfo = &slwt->flv_info;struct in6_addr *daddr = &ipv6_hdr (skb)->daddr; +enum skb_drop_reason reason;if (seg6_next_csid_is_arg_zero (daddr, finfo))return input_action_end_core (skb, slwt);/* update DA */ -seg6_next_csid_advance_arg (daddr, finfo); + reason =seg6_next_csid_advance_arg (skb, finfo); +if (reason) { +kfree_skb_reason (skb, reason); +return -ENOMEM ; + }return input_action_end_finish (skb, slwt); }
@@ -448,19 +505,25 @@ static int input_action_end_x_finish(struct sk_buff *skb,static int input_action_end_x_core (struct sk_buff *skb,struct seg6_local_lwt *slwt) { +enum skb_drop_reason reason =SKB_DROP_REASON_NOT_SPECIFIED ;struct ipv6_sr_hdr *srh; +int err = -EINVAL ; srh =get_and_validate_srh (skb);if (!srh) goto drop; -advance_nextseg (srh, &ipv6_hdr (skb)->daddr); + srh =advance_nextseg (skb, srh, &reason); +if (!srh) { + err = -ENOMEM ; + goto drop; + }return input_action_end_x_finish (skb, slwt); drop: -kfree_skb (skb); -return -EINVAL ; +kfree_skb_reason (skb, reason); +return err; } static int end_x_next_csid_core(struct sk_buff *skb,
@@ -468,12 +531,17 @@ static int end_x_next_csid_core(struct sk_buff *skb, {const struct seg6_flavors_info *finfo = &slwt->flv_info;struct in6_addr *daddr = &ipv6_hdr (skb)->daddr; +enum skb_drop_reason reason;if (seg6_next_csid_is_arg_zero (daddr, finfo))return input_action_end_x_core (skb, slwt);/* update DA */ -seg6_next_csid_advance_arg (daddr, finfo); + reason =seg6_next_csid_advance_arg (skb, finfo); +if (reason) { +kfree_skb_reason (skb, reason); +return -ENOMEM ; + }return input_action_end_x_finish (skb, slwt); }
@@ -760,10 +828,12 @@ static bool seg6_pop_srh(struct sk_buff *skb, int srhoff)*/ static int end_flv8986_core (struct sk_buff *skb,struct seg6_local_lwt *slwt) { +enum skb_drop_reason reason =SKB_DROP_REASON_NOT_SPECIFIED ;const struct seg6_flavors_info *finfo = &slwt->flv_info;enum seg6_local_flv_action action;enum seg6_local_pktinfo pinfo;struct ipv6_sr_hdr *srh; +int err = -EINVAL ;__u32 flvmask;int srhoff;
@@ -787,10 +857,18 @@ static int end_flv8986_core(struct sk_buff *skb, struct seg6_local_lwt *slwt)switch (action) {case SEG6_LOCAL_FLV_ACT_END :/* process the packet as the "standard" End behavior */ -advance_nextseg (srh, &ipv6_hdr (skb)->daddr); + srh =advance_nextseg (skb, srh, &reason); +if (!srh) { + err = -ENOMEM ; + goto drop; + }break ;case SEG6_LOCAL_FLV_ACT_PSP : -advance_nextseg (srh, &ipv6_hdr (skb)->daddr); + srh =advance_nextseg (skb, srh, &reason); +if (!srh) { + err = -ENOMEM ; + goto drop; + }if (unlikely (!seg6_pop_srh (skb, srhoff))) goto drop;
@@ -807,8 +885,8 @@ static int end_flv8986_core(struct sk_buff *skb, struct seg6_local_lwt *slwt)return input_action_end_finish (skb, slwt); drop: -kfree_skb (skb); -return -EINVAL ; +kfree_skb_reason (skb, reason); +return err; }/* regular endpoint function */
@@ -847,21 +925,27 @@ static int input_action_end_x(struct sk_buff *skb, struct seg6_local_lwt *slwt)static int input_action_end_t (struct sk_buff *skb,struct seg6_local_lwt *slwt) { +enum skb_drop_reason reason =SKB_DROP_REASON_NOT_SPECIFIED ;struct ipv6_sr_hdr *srh; +int err = -EINVAL ; srh =get_and_validate_srh (skb);if (!srh) goto drop; -advance_nextseg (srh, &ipv6_hdr (skb)->daddr); + srh =advance_nextseg (skb, srh, &reason); +if (!srh) { + err = -ENOMEM ; + goto drop; + }seg6_lookup_nexthop (skb,NULL , slwt->table);return dst_input (skb); drop: -kfree_skb (skb); -return -EINVAL ; +kfree_skb_reason (skb, reason); +return err; }/* decapsulate and forward inner L2 frame on specified interface */
@@ -1375,6 +1459,7 @@ static int input_action_end_b6(struct sk_buff *skb, struct seg6_local_lwt *slwt)static int input_action_end_b6_encap (struct sk_buff *skb,struct seg6_local_lwt *slwt) { +enum skb_drop_reason reason =SKB_DROP_REASON_NOT_SPECIFIED ;struct ipv6_sr_hdr *srh;int err = -EINVAL ;
@@ -1382,7 +1467,11 @@ static int input_action_end_b6_encap(struct sk_buff *skb,if (!srh) goto drop; -advance_nextseg (srh, &ipv6_hdr (skb)->daddr); + srh =advance_nextseg (skb, srh, &reason); +if (!srh) { + err = -ENOMEM ; + goto drop; + }skb_reset_inner_headers (skb); skb->encapsulation =1 ;
@@ -1398,7 +1487,7 @@ static int input_action_end_b6_encap(struct sk_buff *skb,return dst_input (skb); drop: -kfree_skb (skb); +kfree_skb_reason (skb, reason);return err; }
@@ -1434,6 +1523,7 @@ static int input_action_end_bpf(struct sk_buff *skb, struct seg6_local_lwt *slwt) {struct seg6_bpf_srh_state *srh_state; +enum skb_drop_reason reason;struct ipv6_sr_hdr *srh;int ret;
@@ -1442,7 +1532,11 @@ static int input_action_end_bpf(struct sk_buff *skb,kfree_skb (skb);return -EINVAL ; } -advance_nextseg (srh, &ipv6_hdr (skb)->daddr); + srh =advance_nextseg (skb, srh, &reason); +if (!srh) { +kfree_skb_reason (skb, reason); +return -ENOMEM ; + }/* The access to the per-CPU buffer srh_state is protected by running * always in softirq context (with disabled BH). On PREEMPT_RT the
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 129/428 net: ethtool: let tsconfig reach a PHY-only timestamp provider
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Nicolai Buchwitz <[email protected]>
[ Upstream commit 036778a4fad19b54b73ae229a1901665aa8750a2 ]
TSCONFIG_GET and TSCONFIG_SET reject a device that implements neither hwtstamp NDO, even when its PHY can serve the request. The ioctls they meant to replace handle it, so the two interfaces disagree on the same hardware and user space has to pick one.
Accept the default timestamping PHY and an already installed provider on both sides. On the set side the test moves into ethnl_set_tsconfig() as the validate callback runs without rtnl. On the get side it stays ahead of ethnl_ops_begin(), so a device that can serve nothing keeps failing with EOPNOTSUPP and a dump still skips it rather than stopping there.
A netdev provider needs ndo_hwtstamp_set to be programmed at all, so don't pick that source without it, and test for ndo_hwtstamp_get before calling it.
--- net/core/dev_ioctl.c | 3 ++- net/ethtool/tsconfig.c | 24 ++++++++++-------------- 2 files changed, 12 insertions(+), 15 deletions(-)
net/core/dev_ioctl.c +2 -1
@@ -267,7 +267,8 @@ int dev_get_hwtstamp_phylib(struct net_device *dev, hwprov->phydev)return phy_hwtstamp_get (hwprov->phydev, cfg); -if (hwprov->source ==HWTSTAMP_SOURCE_NETDEV ) +if (hwprov->source ==HWTSTAMP_SOURCE_NETDEV && + dev->netdev_ops->ndo_hwtstamp_get)return dev->netdev_ops->ndo_hwtstamp_get (dev, cfg);return -EOPNOTSUPP ;
net/ethtool/tsconfig.c +10 -14
@@ -1,6 +1,7 @@// SPDX-License-Identifier: GPL-2.0-only #include <linux/net_tstamp.h> +#include <linux/phy.h> #include <linux/ptp_clock_kernel.h> #include <net/netdev_lock.h>
@@ -42,7 +43,9 @@ static int tsconfig_prepare_data(const struct ethnl_req_info *req_base,struct kernel_hwtstamp_config cfg = {};int ret; -if (!dev->netdev_ops->ndo_hwtstamp_get) +if (!dev->netdev_ops->ndo_hwtstamp_get && + !phy_is_default_hwtstamp (dev->phydev) && + !netdev_ops_lock_dereference (dev->hwprov, dev))return -EOPNOTSUPP ; ret =ethnl_ops_begin (dev);
@@ -248,17 +251,6 @@ static int tsconfig_send_reply(struct net_device *dev, struct genl_info *info)return ret; } -static int ethnl_set_tsconfig_validate (struct ethnl_req_info *req_base, -struct genl_info *info) -{ -const struct net_device_ops *ops = req_base->dev->netdev_ops; - -if (!ops->ndo_hwtstamp_set || !ops->ndo_hwtstamp_get) -return -EOPNOTSUPP ; - -return 1 ; -} -static struct hwtstamp_provider * tsconfig_set_hwprov_from_desc(struct net_device *dev, struct genl_info *info,
@@ -272,7 +264,7 @@ tsconfig_set_hwprov_from_desc(struct net_device *dev,int ret; ret =ethtool_net_get_ts_info_by_phc (dev, &ts_info, hwprov_desc); -if (!ret) { +if (!ret && dev->netdev_ops->ndo_hwtstamp_set) {/* Found */ source =HWTSTAMP_SOURCE_NETDEV ; }else {
@@ -313,6 +305,11 @@ static int ethnl_set_tsconfig(struct ethnl_req_info *req_base,if (!netif_device_present (dev))return -ENODEV ; +if (!dev->netdev_ops->ndo_hwtstamp_set && + !phy_is_default_hwtstamp (dev->phydev) && + !netdev_ops_lock_dereference (dev->hwprov, dev)) +return -EOPNOTSUPP ; +if (tb[ETHTOOL_A_TSCONFIG_HWTSTAMP_PROVIDER ]) {struct hwtstamp_provider_desc __hwprov_desc = {.index =-1 };struct hwtstamp_provider *__hwprov;
@@ -455,6 +452,5 @@ const struct ethnl_request_ops ethnl_tsconfig_request_ops = { .reply_size = tsconfig_reply_size, .fill_reply = tsconfig_fill_reply, - .set_validate = ethnl_set_tsconfig_validate, .set = ethnl_set_tsconfig, };
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 130/428 net: restrict SO_RESERVE_MEM to TCP sockets and cap max value
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Eric Dumazet <[email protected]>
[ Upstream commit 37e02c42a00be692c06343e11779aadc45f45971 ]
Commit 2bb2f5fb21b0 ("net: add new socket option SO_RESERVE_MEM") and commit d00c8ee31729 ("net: fix possible NULL deref in sock_reserve_memory") only checked sk_has_account(sk), which is true for both TCP and UDP sockets.
However, SO_RESERVE_MEM and sk_unused_reserved_mem() are currently only supported by TCP:
- On UDP sockets, sk->sk_forward_alloc is protected by sk->sk_receive_queue.lock, whereas sock_reserve_memory() and sock_release_reserved_memory() only acquire lock_sock(sk). Concurrent UDP packet reception/release and setsockopt(SO_RESERVE_MEM) corrupt sk_forward_alloc and memcg accounting.
- udp_rmem_release() reclaims excess sk_forward_alloc without accounting for sk_unused_reserved_mem(sk).
Restrict sock_reserve_memory() to TCP sockets (sk_is_tcp(sk)) for now. Supporting SO_RESERVE_MEM for UDP (acquiring sk_receive_queue.lock and honoring sk_unused_reserved_mem() in udp_rmem_release()) can be done in a future net-next series if needed.
In addition, reject val > SZ_1G with -EINVAL in sk_setsockopt(SO_RESERVE_MEM). Without an upper bound, values near INT_MAX cause sk_mem_pages(delta) and (pages << PAGE_SHIFT) to overflow 32-bit signed int, corrupting sk->sk_forward_alloc and sk->sk_reserved_mem. Using a page-aligned cap (SZ_1G) ensures that the page-rounded sk->sk_reserved_mem reported by getsockopt(SO_RESERVE_MEM) can always be passed back to setsockopt(SO_RESERVE_MEM).
--- net/core/sock.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
net/core/sock.c +2 -2
@@ -1034,7 +1034,7 @@ static int sock_reserve_memory(struct sock *sk, int bytes)bool charged;int pages; -if (!mem_cgroup_sk_enabled (sk) || !sk_has_account (sk)) +if (!mem_cgroup_sk_enabled (sk) || !sk_is_tcp (sk))return -EOPNOTSUPP ;if (!bytes)
@@ -1661,7 +1661,7 @@ int sk_setsockopt(struct sock *sk, int level, int optname, {int delta; -if (val <0 ) { +if (val <0 || val >SZ_1G ) { ret = -EINVAL ;break ; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 131/428 bpf: Wait for an RCU tasks grace period before freeing trampoline progs
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Florent Revest (Anthropic) <[email protected]>
[ Upstream commit 0fb5751a6e411b1b466c06e50021818d9391df98 ]
When a prog is detached from a trampoline, it is freed after an RCU grace period, or an RCU tasks trace one if it is sleepable. This covers the tasks that are running the prog, since the prog's enter helper takes the matching read lock before the prog is called. On a preemptible kernel, it doesn't cover a task that was preempted in the trampoline just before the enter helper. That task holds no lock yet, and it calls the prog after it was freed:
BUG: KASAN: vmalloc-out-of-bounds in __bpf_prog_enter_recur+0x3a5/0x3f0 Read of size 8 at addr ffffc90000055040 by task candidate/110 CPU: 1 UID: 0 PID: 110 Comm: candidate Not tainted 7.3.0-rc2-00014-g15071f2a1263-dirty #2 PREEMPT(full) Call Trace: <TASK> __bpf_prog_enter_recur+0x3a5/0x3f0 bpf_trampoline_6442509193+0x37/0xf1 __x64_sys_futex+0x9/0x410 do_syscall_64+0xb0/0x530 ...
Wait for an RCU tasks grace period before the existing one when freeing a prog that was linked to a trampoline. An RCU tasks grace period only ends once the tasks that were preempted have run again, and bpf_tramp_image_put() already relies on it to free the image. It doesn't wait for tasks that sleep in the trampoline, the next commit takes care of those.
Only progs that were linked to a trampoline can be called this way, so bpf_trampoline_add_prog() marks them and other progs are still freed as before.
--- include/linux/bpf.h | 1 + kernel/bpf/syscall.c | 19 ++++++++++++++++++- kernel/bpf/trampoline.c | 1 + 3 files changed, 20 insertions(+), 1 deletion(-)
include/linux/bpf.h +1 -0
@@ -1758,6 +1758,7 @@ struct bpf_prog_aux {bool offload_requested;/* Program is bound and offloaded to the netdev. */ bool attach_btf_trace;/* true if attaching to BTF-enabled raw tp */ bool attach_tracing_prog;/* true if tracing another tracing program */ +bool tramp_linked;/* true if it was ever linked to a trampoline */ bool func_proto_unreliable;bool tail_call_reachable;bool xdp_has_frags;
kernel/bpf/syscall.c +18 -1
@@ -2445,6 +2445,21 @@ static void __bpf_prog_put_rcu(struct rcu_head *rcu)bpf_prog_free (aux->prog); } +/* +* Progs called from a trampoline can also be reached by a task that was +* preempted in the trampoline before the prog's enter helper took its RCU +* read lock, wait for those first. +*/ +static void __bpf_prog_put_rcu_tasks (struct rcu_head *rcu) +{ + struct bpf_prog *prog = container_of(rcu, struct bpf_prog_aux, rcu)->prog; + +if (prog->sleepable) +call_rcu_tasks_trace (rcu, __bpf_prog_put_rcu); +else +call_rcu (rcu, __bpf_prog_put_rcu); +} +static void __bpf_prog_put_noref (struct bpf_prog *prog,bool deferred) {bpf_prog_kallsyms_del_all (prog);
@@ -2458,7 +2473,9 @@ static void __bpf_prog_put_noref(struct bpf_prog *prog, bool deferred)btf_put (prog->aux->attach_btf);if (deferred) { -if (prog->sleepable) +if (IS_ENABLED (CONFIG_TASKS_RCU ) && prog->aux->tramp_linked) +call_rcu_tasks (&prog->aux->rcu, __bpf_prog_put_rcu_tasks); +else if (prog->sleepable)call_rcu_tasks_trace (&prog->aux->rcu, __bpf_prog_put_rcu);else call_rcu (&prog->aux->rcu, __bpf_prog_put_rcu);
kernel/bpf/trampoline.c +1 -0
@@ -877,6 +877,7 @@ static int bpf_trampoline_add_prog(struct bpf_trampoline *tr, }hlist_add_head (&node->tramp_hlist, prog_list); + node->link->prog->aux->tramp_linked = true;if (kind ==BPF_TRAMP_FSESSION ) { tr->progs_cnt[BPF_TRAMP_FENTRY ]++; fexit =fsession_exit (node);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 132/428 bpf: Skip detached progs in trampoline images that are still in use
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Florent Revest (Anthropic) <[email protected]>
[ Upstream commit 17637e1a581a22466ac3620a91e099683ff9cc6f ]
bpf_tramp_image_put() makes sure a trampoline image is not freed while a task may still be running in it, but nothing similar is done for the progs called by that image. Detach drops the last prog reference right away and the prog is freed after grace periods, on the basis that a task still in the traced function skips the fexit progs once the nop at ip_after_call is patched to a jump.
That leaves out a task sleeping in a sleepable prog that runs before the detached one, which no grace period waits for:
CPU 0 CPU 1
in image I, sleeping in prog S
detach P from I's trampoline
-> new image, bpf_tramp_image_put(I)
bpf_prog_put(P), last ref grace periods, P freed
back from S
__bpf_prog_enter(P)
call P->bpf_func
If S and P are fexit progs the task is already past the patched jump, and fentry only images don't have one. On x86 this is an int3 in poisoned bpf_prog_pack memory:
Oops: int3: 0000 [#1] SMP NOPTI CPU: 18 UID: 0 PID: 94573 Comm: x169 Not tainted 6.18.44 #1 PREEMPT(lazy) RIP: 0010:0xffffffffc0601d8d Call Trace: <TASK> ? bpf_trampoline_6442515411+0x1a4/0x21b bpf_lsm_bprm_committed_creds+0x5/0x10 security_bprm_committed_creds+0x5f/0x70 begin_new_exec+0x2d6/0x410 ...
We hit this in production when progs attached through trampolines got detached while their hooks were busy, and it was independently found with a fuzzer and KASAN.
Have the JITs emit a patchable nop in front of each prog call sequence and record it in the image. When a prog is detached, patch its nop to a jump over the call sequence. Progs that stay attached keep running for the tasks that are in the image, and ip_after_call isn't needed anymore.
The task can be in any image that isn't freed yet, not only in the current one. It sleeps in image I1 that calls S, P and Q, then P is detached and the trampoline moves to image I2, then Q is detached and its call is still in I1. So the trampoline keeps a list of its images until they are freed, and detaching a prog patches its nop in all of them. Images hold a reference on the trampoline for that long.
On riscv and loongarch a jump of any range takes several instructions, and a task preempted in the middle of them could resume into half of the new sequence. The nop is a single instruction there, patched to a near branch through arch_bpf_trampoline_skip().
With the extra nops, BPF_MAX_TRAMP_LINKS progs no longer fit in a page on x86 and arm64 (and already didn't on powerpc), so lower the limit there like s390 does.
--- arch/arm64/net/bpf_jit_comp.c | 37 +++++++++------ arch/loongarch/net/bpf_jit.c | 60 +++++++++++++++-------- arch/powerpc/net/bpf_jit_comp.c | 45 +++++++++--------- arch/riscv/net/bpf_jit_comp64.c | 56 +++++++++++++++------- arch/s390/net/bpf_jit_comp.c | 46 +++++++++++------- arch/x86/net/bpf_jit_comp.c | 26 ++++++---- include/linux/bpf.h | 44 +++++++++++++++-- kernel/bpf/trampoline.c | 84 ++++++++++++++++++++++++++------- 8 files changed, 277 insertions(+), 121 deletions(-)
arch/arm64/net/bpf_jit_comp.c +22 -15
@@ -2358,10 +2358,11 @@ bool bpf_jit_supports_subprog_tailcalls(void)return true; } -static void invoke_bpf_prog (struct jit_ctx *ctx,struct bpf_tramp_node *node, -int bargs_off,int retval_off,int run_ctx_off, -bool save_ret) +static void invoke_bpf_prog (struct jit_ctx *ctx,struct bpf_tramp_image *im, +struct bpf_tramp_node *node,int bargs_off, +int retval_off,int run_ctx_off,bool save_ret) { +void *skip;__le32 *branch;u64 enter_prog;u64 exit_prog;
@@ -2371,6 +2372,10 @@ static void invoke_bpf_prog(struct jit_ctx *ctx, struct bpf_tramp_node *node, enter_prog = (u64 )bpf_trampoline_enter (p); exit_prog = (u64 )bpf_trampoline_exit (p); +/* nop, patched to skip this prog when it is detached */ + skip = ctx->ro_image + ctx->idx; +emit (A64_NOP , ctx); +if (node->cookie ==0 ) {/* if cookie is zero, one instruction is enough to store it */ emit (A64_STR64I (A64_ZR ,A64_SP , run_ctx_off + cookie_off), ctx);
@@ -2423,11 +2428,13 @@ static void invoke_bpf_prog(struct jit_ctx *ctx, struct bpf_tramp_node *node,emit (A64_ADD_I (1 ,A64_R (2 ),A64_SP , run_ctx_off), ctx);emit_call (exit_prog, ctx); + +bpf_tramp_image_add_skip (im, p, skip, ctx->ro_image + ctx->idx); } -static void invoke_bpf_mod_ret (struct jit_ctx *ctx,struct bpf_tramp_nodes *tn, -int bargs_off,int retval_off,int run_ctx_off, -__le32 **branches) +static void invoke_bpf_mod_ret (struct jit_ctx *ctx,struct bpf_tramp_image *im, +struct bpf_tramp_nodes *tn,int bargs_off, +int retval_off,int run_ctx_off,__le32 **branches) {int i;
@@ -2436,7 +2443,7 @@ static void invoke_bpf_mod_ret(struct jit_ctx *ctx, struct bpf_tramp_nodes *tn,*/ emit (A64_STR64I (A64_ZR ,A64_SP , retval_off), ctx);for (i =0 ; i < tn->nr_nodes; i++) { -invoke_bpf_prog (ctx, tn->nodes[i], bargs_off, retval_off, +invoke_bpf_prog (ctx, im, tn->nodes[i], bargs_off, retval_off, run_ctx_off, true);/* if (*(u64 *)(sp + retval_off) != 0) * goto do_fexit;
@@ -2771,7 +2778,7 @@ static int prepare_trampoline(struct jit_ctx *ctx, struct bpf_tramp_image *im,store_func_meta (ctx, meta, func_meta_off); cookie_bargs_off--; } -invoke_bpf_prog (ctx, fentry->nodes[i], bargs_off, +invoke_bpf_prog (ctx, im, fentry->nodes[i], bargs_off, retval_off, run_ctx_off, flags &BPF_TRAMP_F_RET_FENTRY_RET ); }
@@ -2782,7 +2789,7 @@ static int prepare_trampoline(struct jit_ctx *ctx, struct bpf_tramp_image *im,if (!branches)return -ENOMEM ; -invoke_bpf_mod_ret (ctx, fmod_ret, bargs_off, retval_off, +invoke_bpf_mod_ret (ctx, im, fmod_ret, bargs_off, retval_off, run_ctx_off, branches); }
@@ -2795,9 +2802,6 @@ static int prepare_trampoline(struct jit_ctx *ctx, struct bpf_tramp_image *im,emit (A64_RET (A64_R (10 )), ctx);/* store return value */ emit (A64_STR64I (A64_R (0 ),A64_SP , retval_off), ctx); -/* reserve a nop for bpf_tramp_image_put */ - im->ip_after_call = ctx->ro_image + ctx->idx; -emit (A64_NOP , ctx); }/* update the branches saved in invoke_bpf_mod_ret with cbnz */
@@ -2819,12 +2823,11 @@ static int prepare_trampoline(struct jit_ctx *ctx, struct bpf_tramp_image *im,store_func_meta (ctx, meta, func_meta_off); cookie_bargs_off--; } -invoke_bpf_prog (ctx, fexit->nodes[i], bargs_off, retval_off, +invoke_bpf_prog (ctx, im, fexit->nodes[i], bargs_off, retval_off, run_ctx_off, false); }if (flags &BPF_TRAMP_F_CALL_ORIG ) { - im->ip_epilogue = ctx->ro_image + ctx->idx;/* for the first pass, assume the worst case */ if (!ctx->image) ctx->idx +=4 ;
@@ -2883,7 +2886,7 @@ int arch_bpf_trampoline_size(const struct btf_func_model *m, u32 flags, .image = NULL, .idx = 0, }; -struct bpf_tramp_image im; +struct bpf_tramp_image im = {};struct arg_aux aaux;int ret;
@@ -3170,6 +3173,10 @@ int bpf_arch_text_poke(void *ip, enum bpf_text_poke_type old_t,* longer reachable, since bpf_tramp_image_put() function already * uses percpu_ref and task-based rcu to do the sync, no need to call * the sync version here, see bpf_tramp_image_put() for details. +* +* 3. when a detached prog is patched out of a trampoline, a CPU that +* still executes the old nop calls the prog before it went through +* a quiescent state, and the prog is freed after grace periods. */ ret =aarch64_insn_patch_text_nosync (ip, new_insn); out:
arch/loongarch/net/bpf_jit.c +40 -20
128 lines not on this page · raw message
arch/powerpc/net/bpf_jit_comp.c +23 -22
114 lines not on this page · raw message
arch/riscv/net/bpf_jit_comp64.c +39 -17
123 lines not on this page · raw message
arch/s390/net/bpf_jit_comp.c +30 -16
121 lines not on this page · raw message
arch/x86/net/bpf_jit_comp.c +16 -10
@@ -3128,16 +3128,21 @@ static void restore_regs(const struct btf_func_model *m, u8 **prog, }static int invoke_bpf_prog (const struct btf_func_model *m,u8 **pprog, +struct bpf_tramp_image *im,struct bpf_tramp_node *node,int stack_size,int run_ctx_off,bool save_ret,void *image,void *rw_image) {u8 *prog = *pprog; -u8 *jmp_insn; +u8 *jmp_insn, *skip;int ctx_cookie_off = offsetof(struct bpf_tramp_run_ctx , bpf_cookie);struct bpf_prog *p = node->link->prog;u64 cookie = node->cookie; +/* nop, patched to skip this prog when it is detached */ + skip = image + (prog - (u8 *)rw_image); +emit_nops (&prog,X86_PATCH_SIZE ); +/* mov rdi, cookie */ emit_mov_imm64 (&prog,BPF_REG_1 , (long ) cookie >>32 , (u32 ) (long ) cookie);
@@ -3212,6 +3217,8 @@ static int invoke_bpf_prog(const struct btf_func_model *m, u8 **pprog,if (emit_rsb_call (&prog,bpf_trampoline_exit (p), image + (prog - (u8 *)rw_image)))return -EINVAL ; +bpf_tramp_image_add_skip (im, p, skip, image + (prog - (u8 *)rw_image)); + *pprog = prog;return 0 ; }
@@ -3243,6 +3250,7 @@ static int emit_cond_near_jump(u8 **pprog, void *func, void *ip, u8 jmp_cond) } static int invoke_bpf(const struct btf_func_model *m, u8 **pprog, + struct bpf_tramp_image *im, struct bpf_tramp_nodes *tl, int stack_size, int run_ctx_off, int func_meta_off, bool save_ret, void *image, void *rw_image, u64 func_meta,
@@ -3257,7 +3265,7 @@ static int invoke_bpf(const struct btf_func_model *m, u8 **pprog, func_meta | (cur_cookie << BPF_TRAMP_COOKIE_INDEX_SHIFT)); cur_cookie--; } -if (invoke_bpf_prog (m, &prog, tl->nodes[i], stack_size, +if (invoke_bpf_prog (m, &prog, im, tl->nodes[i], stack_size, run_ctx_off, save_ret, image, rw_image))return -EINVAL ; }
@@ -3266,6 +3274,7 @@ static int invoke_bpf(const struct btf_func_model *m, u8 **pprog, }static int invoke_bpf_mod_ret (const struct btf_func_model *m,u8 **pprog, +struct bpf_tramp_image *im,struct bpf_tramp_nodes *tl,int stack_size,int run_ctx_off,u8 **branches,void *image,void *rw_image)
@@ -3279,7 +3288,7 @@ static int invoke_bpf_mod_ret(const struct btf_func_model *m, u8 **pprog,emit_mov_imm32 (&prog, false,BPF_REG_0 ,0 );emit_stx (&prog,BPF_DW ,BPF_REG_FP ,BPF_REG_0 ,-8 );for (i =0 ; i < tl->nr_nodes; i++) { -if (invoke_bpf_prog (m, &prog, tl->nodes[i], stack_size, run_ctx_off, true, +if (invoke_bpf_prog (m, &prog, im, tl->nodes[i], stack_size, run_ctx_off, true, image, rw_image))return -EINVAL ;
@@ -3548,7 +3557,7 @@ static int __arch_prepare_bpf_trampoline(struct bpf_tramp_image *im, void *rw_im }if (fentry->nr_nodes) { -if (invoke_bpf (m, &prog, fentry, regs_off, run_ctx_off, func_meta_off, +if (invoke_bpf (m, &prog, im, fentry, regs_off, run_ctx_off, func_meta_off, flags &BPF_TRAMP_F_RET_FENTRY_RET , image, rw_image, func_meta, cookie_off))return -EINVAL ;
@@ -3560,7 +3569,7 @@ static int __arch_prepare_bpf_trampoline(struct bpf_tramp_image *im, void *rw_imif (!branches)return -ENOMEM ; -if (invoke_bpf_mod_ret (m, &prog, fmod_ret, regs_off, +if (invoke_bpf_mod_ret (m, &prog, im, fmod_ret, regs_off, run_ctx_off, branches, image, rw_image)) { ret = -EINVAL ; goto cleanup;
@@ -3590,8 +3599,6 @@ static int __arch_prepare_bpf_trampoline(struct bpf_tramp_image *im, void *rw_im }/* remember return value in a stack for bpf prog to access */ emit_stx (&prog,BPF_DW ,BPF_REG_FP ,BPF_REG_0 ,-8 ); - im->ip_after_call = image + (prog - (u8 *)rw_image); -emit_nops (&prog,X86_PATCH_SIZE ); }if (fmod_ret->nr_nodes) {
@@ -3616,7 +3623,7 @@ static int __arch_prepare_bpf_trampoline(struct bpf_tramp_image *im, void *rw_imemit_store_stack_imm64 (&prog,BPF_REG_0 , -func_meta_off, func_meta);if (fexit->nr_nodes) { -if (invoke_bpf (m, &prog, fexit, regs_off, run_ctx_off, func_meta_off, +if (invoke_bpf (m, &prog, im, fexit, regs_off, run_ctx_off, func_meta_off, false, image, rw_image, func_meta, cookie_off)) { ret = -EINVAL ; goto cleanup;
@@ -3631,7 +3638,6 @@ static int __arch_prepare_bpf_trampoline(struct bpf_tramp_image *im, void *rw_im* restored to R0. */ if (flags &BPF_TRAMP_F_CALL_ORIG ) { - im->ip_epilogue = image + (prog - (u8 *)rw_image);/* arg1: mov rdi, im */ emit_mov_imm64 (&prog,BPF_REG_1 , (long ) im >>32 , (u32 ) (long ) im);if (emit_rsb_call (&prog, __bpf_tramp_exit, image + (prog - (u8 *)rw_image))) {
@@ -3719,7 +3725,7 @@ int arch_prepare_bpf_trampoline(struct bpf_tramp_image *im, void *image, void *iint arch_bpf_trampoline_size (const struct btf_func_model *m,u32 flags,struct bpf_tramp_nodes *tnodes,void *func_addr) { -struct bpf_tramp_image im; +struct bpf_tramp_image im = {};void *image;int ret;
include/linux/bpf.h +40 -4
82 lines not on this page · raw message
kernel/bpf/trampoline.c +67 -17
176 lines not on this page · raw message
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 133/428 gve: DQO: accept TSO packets with non-protocol gso_type bits
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Hannu Varjoranta <[email protected]>
[ Upstream commit f8932b2c0c9ae6fb577bcc05a2080c8498f91c0d ]
Since commit 014c607f86ab ("gve: add support for UDP GSO for DQO format"), gve_prep_tso() matches shinfo->gso_type exactly. gso_type is a bitmask, though: SKB_GSO_DODGY is set on every GSO packet that comes from tun/tap, packet sockets or other untrusted sources, and SKB_GSO_TCP_ECN and SKB_GSO_TCP_FIXEDID can be set as well. Such packets hit the default case, gve_tx_add_skb_dqo() fails and gve_try_tx_skb() drops them, counting them in tx_dropped.
These packets do reach the driver: tcp_gso_segment() passes DODGY skbs through unsegmented to devices that support TSO, after recomputing gso_segs, so drivers must tolerate the bit.
This breaks virtual machines behind a tap on GCE instances using the DQO queue formats. Every TSO packet forwarded from a guest (gso_type SKB_GSO_TCPV4 | SKB_GSO_DODGY) is dropped, and guest uploads slow to a crawl of retransmissions or stall entirely, while the host's own TSO traffic (gso_type SKB_GSO_TCPV4) is unaffected. On an n4 instance (DQO-QPL) with a Cloud Hypervisor guest, a 64 MB upload from the guest went from a 60 s timeout at ~0.9 MB/s to 0.19 s with this change, with tx_dropped no longer increasing.
Host TCP with ECN is hit as well: gve advertises NETIF_F_TSO_ECN, so a TSO packet carrying CWR has SKB_GSO_TCP_ECN set and is dropped too.
Restore the bitmask test that commit 1b9f75634441 ("gve: ignore nonrelevant GSO type bits when processing TSO headers") introduced for the same problem, keeping the UDP GSO support.
While here, reload shinfo after skb_cow_head(). If the head was cloned, pskb_expand_head() moves skb_shared_info to the new head, and the pointer cached at function entry can then refer to memory that another clone frees.
--- drivers/net/ethernet/google/gve/gve_tx_dqo.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-)
drivers/net/ethernet/google/gve/gve_tx_dqo.c +7 -7
@@ -595,25 +595,25 @@ static int gve_prep_tso(struct sk_buff *skb) err =skb_cow_head (skb,0 );if (err <0 )return err; + shinfo =skb_shinfo (skb); l4_start =skb_transport_offset (skb); paylen = skb->len - l4_start; -switch (shinfo->gso_type) { -case SKB_GSO_TCPV4 : -case SKB_GSO_TCPV6 : +/* gso_type is a bitmask: SKB_GSO_DODGY, SKB_GSO_TCP_ECN and +* SKB_GSO_TCP_FIXEDID may be set alongside the protocol bit. +*/ +if (shinfo->gso_type & (SKB_GSO_TCPV4 |SKB_GSO_TCPV6 )) { tcp =tcp_hdr (skb);csum_replace_by_diff (&tcp->check, (__force __wsum )htonl (paylen)); header_len =skb_tcp_all_headers (skb); -break ; -case SKB_GSO_UDP_L4 : + }else if (shinfo->gso_type &SKB_GSO_UDP_L4 ) { udp =udp_hdr (skb);csum_replace_by_diff (&udp->check, (__force __wsum )htonl (paylen)); header_len =sizeof (struct udphdr ) + l4_start; -break ; -default : + }else {return -EINVAL ; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 134/428 net/sched: fq_codel: match the no-drop threshold to the packet size
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jamal Hadi Salim <[email protected]>
[ Upstream commit d031465acc362866f27e4f8f79cfecf1037fe2e5 ]
commit d9ebd8f9aa8b ("net/sched: fq_codel: clamp default quantum and mtu") clamped both q->quantum and q->cparams.mtu to [256, FQ_CODEL_QUANTUM_MAX]. The two fields mean different things: quantum is a DRR credit that wants the 256 floor, but cparams.mtu is the CoDel no-drop threshold (codel_impl.h "*backlog <= params->mtu"). On a link whose maximum transmitted packet size is below 256, the floor extends CoDel's minimum-backlog exemption beyond one packet and delays drop or mark eligibility by several small packets.
Split the clamp. quantum keeps [256, FQ_CODEL_QUANTUM_MAX]; cparams.mtu tracks psched_mtu() (the device MTU plus its hard-header length) with only the upper bound that guards the original overflow (psched_mtu() wrapping to ~2 GiB on a huge-MTU device).
Conditions to recreate the bug: attach an fq_codel qdisc on a link whose MTU plus hard_header_len is below 256 (e.g. a CAN interface). At that MTU the no-drop threshold must equal the device MTU plus its hard-header length; before this patch it was forced to 256. Basic Testing done: with dev->mtu=100 and hard_header_len=14, a return probe on fq_codel_init() observed cparams.mtu change from 256 to 114
--- net/sched/sch_fq_codel.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
net/sched/sch_fq_codel.c +2 -2
@@ -517,8 +517,8 @@ static int fq_codel_init(struct Qdisc *sch, struct nlattr *opt, q->flows_cnt =1024 ; q->memory_limit =32 <<20 ;/* 32 MBytes */ q->drop_batch_size =64 ; - mtu =clamp_t (u32,psched_mtu (qdisc_dev (sch)),256 ,FQ_CODEL_QUANTUM_MAX ); - q->quantum = mtu; + mtu =min_t (u32,psched_mtu (qdisc_dev (sch)),FQ_CODEL_QUANTUM_MAX ); + q->quantum =clamp_t (u32, mtu,256 ,FQ_CODEL_QUANTUM_MAX );INIT_LIST_HEAD (&q->new_flows);INIT_LIST_HEAD (&q->old_flows);codel_params_init (&q->cparams);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 135/428 net/sched: sch_codel: match the no-drop threshold to the packet size
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jamal Hadi Salim <[email protected]>
[ Upstream commit 54518e0e827f4ca9229ae657022c60bf60f5c1bf ]
commit 6439461f1618 ("net/sched: sch_codel: clamp default mtu to avoid disabling CoDel") clamped q->params.mtu to [256, 1 << 20]. The value is the CoDel no-drop threshold (codel_impl.h "*backlog <= params->mtu"), so on a link whose maximum transmitted packet size is below 256 the floor extends CoDel's minimum-backlog exemption beyond one packet and delays drop or mark eligibility by several small packets.
Keep the upper bound that guards the original overflow (psched_mtu() wrapping to ~2 GiB on a huge-MTU device) but drop the 256 floor, so the threshold tracks the real device packet size.
Conditions to recreate the bug: attach a codel qdisc on a link whose MTU plus hard_header_len is below 256 (e.g. a CAN interface). At that MTU the no-drop threshold must equal the device MTU plus its hard-header length; before this patch it was forced to 256. Requires CAP_NET_ADMIN in a user namespace.
--- net/sched/sch_codel.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
net/sched/sch_codel.c +1 -1
@@ -205,7 +205,7 @@ static int codel_init(struct Qdisc *sch, struct nlattr *opt,codel_params_init (&q->params);codel_vars_init (&q->vars);codel_stats_init (&q->stats); - q->params.mtu =clamp_t (u32,psched_mtu (qdisc_dev (sch)),256 ,1 <<20 ); + q->params.mtu =min_t (u32,psched_mtu (qdisc_dev (sch)),1 <<20 );if (opt) {int err =codel_change (sch, opt, extack);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 136/428 ALSA: usb-audio: Add boot quirk for Behringer CM1A
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sebastian Dalfuß <[email protected]>
[ Upstream commit 45b5beb60bf7bd41c55ef17f6f2d28b351fad6c0 ]
After a power cycle and reenumeration, the Behringer CM1A* leaves its MIDI endpoint inoperative. USB enumeration and driver binding complete successfully, but MIDI outputs remain pending.
A GET_DESCRIPTOR request for the device descriptor, issued after USB configuration, makes the endpoint operational. Add a one time boot quirk to perform that request before ALSA initializes the device.
*
ID 1397:1234 BEHRINGER International GmbH CM1A
Stable-dep-of: b5c4823cf3e4 ("ALSA: usb-audio: Apply boot quirk for Behringer models generically")
--- sound/usb/quirks.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+)
sound/usb/quirks.c +23 -0
@@ -849,6 +849,27 @@ static int snd_usb_accessmusic_boot_quirk(struct usb_device *dev)return 0 ; } +/* +* A post configuration device descriptor read is needed to make the CM1A +* operational after reenumeration. +*/ +static int snd_usb_cm1a_boot_quirk (struct usb_device *dev) +{ + struct usb_device_descriptor *desc __free(kfree) = kmalloc_obj(*desc); +int err; + +if (!desc) +return -ENOMEM ; + + err =usb_get_descriptor (dev,USB_DT_DEVICE ,0 , desc,sizeof (*desc)); +if (err <0 ) { +dev_err (&dev->dev,"failed to read device descriptor: %d\n" , err); +return err; + } + +return 0 ; +} +/* * Some sound cards from Native Instruments are in fact compliant to the USB * audio standard of version 2 and other approved USB standards, even though
@@ -1681,6 +1702,8 @@ int snd_usb_apply_boot_quirk_once(struct usb_device *dev,switch (id) {case USB_ID (0x07fd ,0x0008 ):/* MOTU M Series, 1st hardware version */ return snd_usb_motu_m_series_boot_quirk (dev); +case USB_ID (0x1397 ,0x1234 ):/* Behringer CM1A */ +return snd_usb_cm1a_boot_quirk (dev); }return 0 ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 137/428 ALSA: usb-audio: Apply boot quirk for Behringer models generically
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Takashi Iwai <[email protected]>
[ Upstream commit b5c4823cf3e49b3419a3bc8c6715101e1ac6cc59 ]
Jan reported that a few Behringer devices became broken since the recent optimization to avoid usb_string() call at probe time at the commit b364a0d23cae ("ALSA: usb-audio: Use strings in struct usb_dev for manufacturer & co"). Interestingly, the devices seem requiring the explicit descriptor read at probing time, and the optimization above dropped it.
There is already a boot quirk for another model, Behringer CM1A (1397:1234), that adds a device descriptor read, and this seems working for them, too. As the quirk is safe and cheap, just apply the same boot quirk to all Behringer devices for avoiding the pitfall again.
--- sound/usb/quirks.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-)
sound/usb/quirks.c +4 -2
@@ -1702,10 +1702,12 @@ int snd_usb_apply_boot_quirk_once(struct usb_device *dev,switch (id) {case USB_ID (0x07fd ,0x0008 ):/* MOTU M Series, 1st hardware version */ return snd_usb_motu_m_series_boot_quirk (dev); -case USB_ID (0x1397 ,0x1234 ):/* Behringer CM1A */ -return snd_usb_cm1a_boot_quirk (dev); } +/* Behringer devices may need explicit device descriptor read at boot */ +if (USB_ID_VENDOR (id) ==0x1397 ) +return snd_usb_cm1a_boot_quirk (dev); +return 0 ; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 138/428 virtio_blk: set the zone write granularity
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Niklas Cassel <[email protected]>
[ Upstream commit 684b413b5483f57c890c171b9400076a0143b918 ]
virtblk_read_zoned_limits() reads the write granularity that the device reports in virtio_blk_zoned_characteristics and assigns it to the physical block size and to io_min, but never to the limit that is named after it. queue_limits.zone_write_granularity is left at zero, so blk_validate_zoned_limits() raises it to the logical block size:
if (lim->zone_write_granularity < lim->logical_block_size) lim->zone_write_granularity = lim->logical_block_size;
A device that reports a granularity coarser than its logical block size, which is what the field exists to express, therefore has it silently reduced. A 512e host managed disk passed through to a guest reports a logical block size of 512 and a write granularity of 4096, and the guest ends up with a zone write granularity of 512.
bio_split_alignment() returns lim->zone_write_granularity if it is non-zero and bio_split_io_at() may split a bio with as per bio_split_alignment(). This can real to the write getting rejected by the host drive, as the write is not aligned to the physical block size.
zonefs also takes its block size from bdev_zone_write_granularity(), so it would incorrectly use 512 on a disk that requires 4096.
sd_zbc_read_zones() sets the limit from the physical block size for the same reason. NVMe ZNS and null_blk leave it unset, but the fallback gives the right answer for them, as their write granularity is the logical block size. virtio carries a separate value that may exceed it.
Set the zone write granularity from the value that the device reports.
--- drivers/block/virtio_blk.c | 1 + 1 file changed, 1 insertion(+)
drivers/block/virtio_blk.c +1 -0
@@ -743,6 +743,7 @@ static int virtblk_read_zoned_limits(struct virtio_blk *vblk,dev_warn (&vdev->dev,"zero write granularity reported\n" );return -ENODEV ; } + lim->zone_write_granularity = wg; lim->physical_block_size = wg; lim->io_min = wg;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 139/428 net: pcs: rzn1-miic: Clear MIIC protection state before unprotect sequence
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Yoshihisa Yamamoto <[email protected]>
[ Upstream commit 8b9993437429c66d11430ae61ffa5937a6abb4ae ]
On RZ/T2H and RZ/N1D MIIC, writing 0x0000 to the MIIC protection register clears the protection state machine.
The initial protection state cannot be assumed to be constant, as it may be influenced by previous activity before the driver takes ownership of the hardware.
Clear the protection state before issuing the unprotect sequence so that register access always starts from a known state, regardless of any previous activity.
--- drivers/net/pcs/pcs-rzn1-miic.c | 3 +++ 1 file changed, 3 insertions(+)
drivers/net/pcs/pcs-rzn1-miic.c +3 -0
@@ -270,6 +270,9 @@ static struct miic_port *phylink_pcs_to_miic_port(struct phylink_pcs *pcs)static void miic_unlock_regs (struct miic *miic) { +/* Clear protection state */ +writel (0x0000 , miic->base +MIIC_PRCMD ); +/* Unprotect register writes */ writel (0x00A5 , miic->base +MIIC_PRCMD );writel (0x0001 , miic->base +MIIC_PRCMD );
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 140/428 net: bcmgenet: fix NULL dereference in set_coalesce before first open
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Nicolai Buchwitz <[email protected]>
[ Upstream commit 19419faf58dfe5bcc8a9e60e622976c5f8e3ddc1 ]
The Rx ring priv and index are only set in bcmgenet_init_rx_ring(), which runs at open. Setting the Rx coalescing parameters on an interface that was never opened, e.g. "ethtool -C eth0 rx-usecs 50", dereferences a NULL ring->priv. The oops happens with RTNL held, so networking stays stuck until a reboot:
Unable to handle kernel paging request at virtual address 0000000000002788 pc : bcmgenet_set_rx_coalesce.isra.0+0x10/0x88 lr : bcmgenet_set_coalesce+0xe8/0x180 Call trace: bcmgenet_set_rx_coalesce.isra.0+0x10/0x88 (P) __ethnl_set_coalesce.isra.0+0x490/0x570 ethnl_set_coalesce+0x48/0xc0 ethnl_default_set_doit+0xf4/0x230 genl_family_rcv_msg_doit+0xe8/0x160 genl_rcv_msg+0x220/0x2a0 netlink_rcv_skb+0x68/0x140 genl_rcv+0x40/0x60 netlink_unicast+0x338/0x3c0 netlink_sendmsg+0x19c/0x3f8 __sock_sendmsg+0x64/0xc0 __sys_sendto+0x124/0x198 __arm64_sys_sendto+0x30/0x48
Set priv and index at probe. Writing the registers while the interface is down is harmless because open resets the MAC and bcmgenet_init_rx_coalesce() reapplies the stored values.
It was observed on a Raspberry Pi CM4, but should apply to all other genet devices.
--- drivers/net/ethernet/broadcom/genet/bcmgenet.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-)
drivers/net/ethernet/broadcom/genet/bcmgenet.c +4 -1
@@ -4151,8 +4151,11 @@ static int bcmgenet_probe(struct platform_device *pdev)netif_set_real_num_rx_queues (priv->dev, priv->hw_params->rx_queues +1 );/* Set default coalescing parameters */ -for (i =0 ; i <= priv->hw_params->rx_queues; i++) +for (i =0 ; i <= priv->hw_params->rx_queues; i++) { + priv->rx_rings[i].priv = priv; + priv->rx_rings[i].index = i; priv->rx_rings[i].rx_max_coalesced_frames =1 ; + }/* Initialize u64 stats seq counter for 32bit machines */ for (i =0 ; i <=GENET_MAX_MQ_CNT ; i++) {
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 141/428 net: cap skb->queue_mapping when the tx queue is picked
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jamal Hadi Salim <[email protected]>
[ Upstream commit ea4d4b5dddb5121e64f56fd8e0720bd8b447b63d ]
skbedit can set skb->queue_mapping and raise the per-CPU skip_txqueue flag so __dev_queue_xmit() honours the mapping. __dev_queue_xmit() cleared the flag before sch_handle_egress() and only read it afterwards, so the flag was not confined to the xmit that set it: a nested xmit (mirred redirect or mirror, or a drop after skbedit) could set the flag and the outer xmit would consume it for an skb that never went through skbedit.
A forwarded packet still carries the ingress NIC's rx_queue + 1 in skb->queue_mapping, so the outer device then indexes its tx queue state with that stale value. Taprio's child array q->qdiscs[] is sized to the device's queue count, so taprio_enqueue() indexes past its allocation and dereferences the result as a struct Qdisc *.
We (ab)use the skb->nf_skip_egress which means "skip netfilter egress for this packet" to tag to "am I in tc egress?". Despite the overload I dont see it as a conflict since the marker is set only around the single sch_handle_egress() call and ingress path is guarded by tc_at_ingress.
I will send a followup(net-next) patch once this hits net-next to rename the skb->nf_skip_egress bit/flag to skb->skip_egress
Arm the flag only from the egress classifier that can use it: raise skip_txqueue from tcf_skbedit_act() only when it runs inside sch_handle_egress(), thanks to skb->nf_skip_egress. An egress qdisc classifier runs in q->enqueue(), after the tx queue has been picked, so a mapping it sets cannot affect the current packet; arming the flag there only pollutes it for a later xmit. Then own the flag for the xmit frame the egress hook runs in: save the incoming value and clear it just before sch_handle_egress(), and restore it after the hook - on the consumed (drop) path, or, in the same call that reads it, on the surviving path. The save and the restores stay inside the egress_needed_key static branch, so a packet pays for them only when egress hooks are active (2f1e85b1aee4).
Store the value netdev_cap_txqueue() selected back into skb->queue_mapping in netdev_tx_queue_mapping(), as netdev_core_pick_tx() already does, so the skip_txqueue path never hands a later reader on the xmit path a mapping the device cannot serve. A store made still later in the same frame, by a tc BPF program attached to a transmit qdisc, is outside this path and is not re-capped; a separate followup will resolve that path.
netdev_xmit_skip_txqueue() returns the previous flag value so the save-and-clear is one call, and a no-op stub is provided when CONFIG_NET_EGRESS is disabled. skb->nf_skip_egress is compiled under CONFIG_NET_EGRESS rather than CONFIG_NETFILTER_SKIP_EGRESS, so skb_at_tc_egress() is valid whenever the egress path is built.
A local user in a network namespace can redirect a packet from a device with more TX queues to one with fewer after setting a mapping valid only on the larger device. That reaches these reads and, under KASAN, faults with "slab-out-of-bounds in taprio_enqueue".
Conditions to recreate the bug: the report's own trigger is a local user with CAP_NET_ADMIN in a network namespace, so no eBPF program is needed. With CONFIG_NET_SCH_TAPRIO=y, CONFIG_NET_ACT_SKBEDIT=y, CONFIG_NET_ACT_MIRRED=y, CONFIG_NET_CLS_MATCHALL=y, CONFIG_NET_SCH_PRIO=y and KASAN enabled, create qa (3 queues), qb (2 queues) and qc (1 queue) as dummy devices; put a taprio root on qb (num_tc 1, queues 2@0) and clsact on all three; then add an egress matchall filter on every device. On qa: "action skbedit queue_mapping 2 pipe action mirred egress redirect dev qb". On qb: "action mirred egress mirror dev qc". On qc: "action skbedit queue_mapping 0 pipe". Send one packet out qa. qc's skbedit sets the flag while qb's outer xmit is in flight; without the fix qb consumes it and reads its two-entry taprio child array with the forwarded packet's stale mapping. A qc whose skbedit is instead installed in a transmit-qdisc classifier (a matchall filter on the qc root qdisc) reaches the same code path the same way without the fix.
Testing: on a KASAN build with panic_on_warn=1 the unfixed kernel panics with "BUG: KASAN: slab-out-of-bounds in taprio_enqueue", a read 0 bytes past a 16-byte taprio_init() allocation, for the clsact-setter and the transmit-qdisc-classifier reproducers and for a clsact skbedit-then-tc-BPF store; the fixed kernel runs all three with no report, and the BPF store variant additionally shows the expected "selects TX queue" clamp notice from the write-back.
--- include/linux/netfilter_netdev.h | 2 +- include/linux/rtnetlink.h | 7 ++++++- include/linux/skbuff.h | 2 +- include/net/sch_generic.h | 9 +++++++++ net/core/dev.c | 31 ++++++++++++++++++++++++------- net/sched/act_skbedit.c | 5 ++--- 6 files changed, 43 insertions(+), 13 deletions(-)
include/linux/netfilter_netdev.h +1 -1
@@ -133,7 +133,7 @@ static inline struct sk_buff *nf_hook_egress(struct sk_buff *skb, int *rc,static inline void nf_skip_egress (struct sk_buff *skb,bool skip) { -#ifdef CONFIG_NETFILTER_SKIP_EGRESS +#ifdef CONFIG_NET_EGRESS skb->nf_skip_egress = skip;#endif }
include/linux/rtnetlink.h +6 -1
@@ -178,7 +178,12 @@ void net_dec_ingress_queue(void);#ifdef CONFIG_NET_EGRESS void net_inc_egress_queue (void );void net_dec_egress_queue (void ); -void netdev_xmit_skip_txqueue (bool skip); +bool netdev_xmit_skip_txqueue (bool skip); +#else +static inline bool netdev_xmit_skip_txqueue (bool skip) +{ +return false; +}#endif void rtnetlink_init (void );
include/linux/skbuff.h +1 -1
@@ -1020,7 +1020,7 @@ struct sk_buff {#ifdef CONFIG_NET_REDIRECT __u8 from_ingress:1;#endif -#ifdef CONFIG_NETFILTER_SKIP_EGRESS +#ifdef CONFIG_NET_EGRESS __u8 nf_skip_egress:1;#endif #ifdef CONFIG_SKB_DECRYPTED
include/net/sch_generic.h +9 -0
@@ -810,6 +810,15 @@ static inline bool skb_at_tc_ingress(const struct sk_buff *skb)#endif } +static inline bool skb_at_tc_egress (const struct sk_buff *skb) +{ +#ifdef CONFIG_NET_EGRESS +return skb->nf_skip_egress && !skb_at_tc_ingress (skb); +#else +return false; +#endif +} +static inline bool skb_skip_tc_classify (struct sk_buff *skb) {#ifdef CONFIG_NET_CLS_ACT
net/core/dev.c +24 -7
@@ -4413,9 +4413,14 @@ EXPORT_SYMBOL(dev_loopback_xmit);static struct netdev_queue *netdev_tx_queue_mapping (struct net_device *dev,struct sk_buff *skb) { -int qm =skb_get_queue_mapping (skb); +int queue =skb_get_queue_mapping (skb); +int capped; -return netdev_get_tx_queue (dev,netdev_cap_txqueue (dev, qm)); + capped =netdev_cap_txqueue (dev, queue); +if (unlikely (capped != queue)) +skb_set_queue_mapping (skb, capped); + +return netdev_get_tx_queue (dev, capped); }#ifndef CONFIG_PREEMPT_RT
@@ -4424,9 +4429,13 @@ static bool netdev_xmit_txqueue_skipped(void)return __this_cpu_read (softnet_data.xmit.skip_txqueue); } -void netdev_xmit_skip_txqueue (bool skip) +bool netdev_xmit_skip_txqueue (bool skip) { +bool prev =netdev_xmit_txqueue_skipped (); +__this_cpu_write (softnet_data.xmit.skip_txqueue, skip); + +return prev; }EXPORT_SYMBOL_GPL (netdev_xmit_skip_txqueue);
@@ -4436,9 +4445,13 @@ static bool netdev_xmit_txqueue_skipped(void) return current->net_xmit.skip_txqueue; } -void netdev_xmit_skip_txqueue (bool skip) +bool netdev_xmit_skip_txqueue (bool skip) { +bool prev =netdev_xmit_txqueue_skipped (); + current->net_xmit.skip_txqueue = skip; + +return prev; }EXPORT_SYMBOL_GPL (netdev_xmit_skip_txqueue);#endif
@@ -4857,21 +4870,25 @@ int __dev_queue_xmit(struct sk_buff *skb, struct net_device *sb_dev)tcx_set_ingress (skb, false);#ifdef CONFIG_NET_EGRESS if (static_branch_unlikely (&egress_needed_key)) { +bool skip_txq; +if (nf_hook_egress_active ()) { skb =nf_hook_egress (skb, &rc, dev);if (!skb) goto out; } -netdev_xmit_skip_txqueue (false); + skip_txq =netdev_xmit_skip_txqueue (false);nf_skip_egress (skb, true); skb =sch_handle_egress (skb, &rc, dev); -if (!skb) +if (!skb) { +netdev_xmit_skip_txqueue (skip_txq); goto out; + }nf_skip_egress (skb, false); -if (netdev_xmit_txqueue_skipped ()) +if (netdev_xmit_skip_txqueue (skip_txq)) txq =netdev_tx_queue_mapping (dev, skb); }#endif
net/sched/act_skbedit.c +2 -3
@@ -72,9 +72,8 @@ TC_INDIRECT_SCOPE int tcf_skbedit_act(struct sk_buff *skb, }if (params->flags &SKBEDIT_F_QUEUE_MAPPING && skb->dev->real_num_tx_queues > params->queue_mapping) { -#ifdef CONFIG_NET_EGRESS -netdev_xmit_skip_txqueue (true); -#endif +if (skb_at_tc_egress (skb)) +netdev_xmit_skip_txqueue (true);skb_set_queue_mapping (skb,tcf_skbedit_hash (params, skb)); }if (params->flags &SKBEDIT_F_MARK ) {
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 142/428 net: ethernet: mtk_eth_soc: allocate dummy netdev sooner
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sandeep Haemoon <[email protected]>
[ Upstream commit 6f0c2c4f5e7143eba75153ecc8af3a03f7b6a98b ]
The RX rings and the shared NAPI are carried by an internal "dummy" netdev (eth->dummy_dev). mtk_probe() creates that dummy device, and adds the shared tx/rx NAPI to it, only after the MAC netdevs have been registered. That order has been there since the MT7623 support was added and leaves a window in which the netdevs are visible to the network core before the shared NAPI and the dummy device exist.
If anything brings the first netdev up during that window (e.g. netifd opening the WAN link the instant the interface is registered), the first-open path runs without them. On NETSYS v2 and later SoCs mtk_rx_alloc() -> __xdp_rxq_info_reg() then dereferences eth->dummy_dev == NULL:
WARNING: CPU: ... Missing net_device from driver
in net/core/xdp.c and mtk_open() fails with -ENODEV. On NETSYS v1 SoCs (MT7621/MT7622/MT7623/MT7629) there is no page pool, so the same open instead calls napi_enable() on a napi_struct that netif_napi_add() has not initialised yet and dereferences a NULL napi->dev.
Allocate the dummy netdev and add the shared tx/rx NAPI to it before the register_netdev() loop. Both the dummy-allocation failure and a register_netdev() failure now unwind through mtk_unreg_dev(), which unregisters the net_device notifiers and the netdevs registered so far, stopping a concurrently-opened netdev (ndo_stop) and its NAPI before the dummy carrier is freed.
A netdev that opened during the window can also have queued the frame-engine reset worker (via the tx watchdog or phylink), so the probe error path now cancels eth->pending_work before freeing the shared state, as mtk_cleanup() already does on remove.
--- drivers/net/ethernet/mediatek/mtk_eth_soc.c | 38 ++++++++++++++------- 1 file changed, 26 insertions(+), 12 deletions(-)
drivers/net/ethernet/mediatek/mtk_eth_soc.c +26 -12
@@ -5336,6 +5336,22 @@ static int mtk_probe(struct platform_device *pdev) } } +/* we run 2 devices on the same DMA ring so we need a dummy device +* for NAPI to work. Allocate it before registering the netdevs so a +* concurrent ndo_open (e.g. netifd bringing the first netdev up the +* instant it is registered) never observes eth->dummy_dev == NULL in +* mtk_dma_init() -> mtk_rx_alloc() -> __xdp_rxq_info_reg() (net/core/xdp.c +* "Missing net_device from driver") and fails the first open. +*/ + eth->dummy_dev =alloc_netdev_dummy (0 ); +if (!eth->dummy_dev) { + err = -ENOMEM ; +dev_err (eth->dev,"failed to allocated dummy device\n" ); + goto err_unreg_netdev; + } +netif_napi_add (eth->dummy_dev, ð->tx_napi, mtk_napi_tx); +netif_napi_add (eth->dummy_dev, ð->rx_napi, mtk_napi_rx); +for (i =0 ; i <MTK_MAX_DEVS ; i++) {if (!eth->netdev[i])continue ;
@@ -5350,18 +5366,6 @@ static int mtk_probe(struct platform_device *pdev) eth->netdev[i]->base_addr, eth->irq[MTK_FE_IRQ_SHARED]); } -/* we run 2 devices on the same DMA ring so we need a dummy device -* for NAPI to work -*/ - eth->dummy_dev =alloc_netdev_dummy (0 ); -if (!eth->dummy_dev) { - err = -ENOMEM ; -dev_err (eth->dev,"failed to allocated dummy device\n" ); - goto err_unreg_netdev; - } -netif_napi_add (eth->dummy_dev, ð->tx_napi, mtk_napi_tx); -netif_napi_add (eth->dummy_dev, ð->rx_napi, mtk_napi_rx); -platform_set_drvdata (pdev, eth);schedule_delayed_work (ð->reset.monitor_work,MTK_DMA_MONITOR_TIMEOUT );
@@ -5371,6 +5375,16 @@ static int mtk_probe(struct platform_device *pdev) err_unreg_netdev:mtk_unreg_dev (eth); err_deinit_ppe: +/* A netdev opened while probe was still registering can have queued +* the frame-engine reset worker, which takes rtnl and drives the +* hardware; cancel it before anything below is torn down. +*/ +cancel_work_sync (ð->pending_work); +if (eth->dummy_dev) { +netif_napi_del (ð->tx_napi); +netif_napi_del (ð->rx_napi); +free_netdev (eth->dummy_dev); + }mtk_ppe_deinit (eth);mtk_mdio_cleanup (eth); err_free_dev:
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 143/428 net: phy: qcom: at803x: Fix IPQ5018 short-cable DAC values
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Yongzhao Chen <[email protected]>
[ Upstream commit 20f3599d85b416fd292a199364cb1248dcd9c780 ]
When "qcom,dac-preset-short-cable" is set, ipq5018_config_init() programs the MDAC (MMD1 0x8100) and EDAC (debug 0x4380) fields. Both fields occupy bits 15:8 (IPQ5018_PHY_DAC_MASK), but the value 0x10 is passed unshifted as the set argument of phy_modify_mmd() and at803x_debug_reg_mask(). Neither helper shifts or masks that argument, so both fields are cleared to 0x00 instead of being set to 0x10, and bit 4 of the low byte, which is outside the field, is set.
Use FIELD_PREP() to place the value in the field. This matches the vendor SDK, which clears bits 15:8 and ORs in the value shifted left by 8.
On a Redmi AX5400 board, where the IPQ5018 internal PHY connects to a QCA8337 switch PHY without a cable, MDAC and EDAC read 0x6868 and 0x7800 before the write. With this change they read back 0x1068 and 0x1000, with the low byte preserved. Without it, the same writes would leave 0x0078 and 0x0010.
No in-tree DTS sets this property yet, but it is documented in qca,ar803x.yaml and used by several IPQ5018 boards in OpenWrt.
--- drivers/net/phy/qcom/at803x.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-)
drivers/net/phy/qcom/at803x.c +6 -2
@@ -1051,11 +1051,15 @@ static int ipq5018_config_init(struct phy_device *phydev)if (priv->set_short_cable_dac) {/* setting MDAC (Multi-level Digital-to-Analog Converter) in MMD1 */ phy_modify_mmd (phydev,MDIO_MMD_PMAPMD ,IPQ5018_PHY_MMD1_MDAC , -IPQ5018_PHY_DAC_MASK ,IPQ5018_PHY_MMD1_MDAC_VAL ); +IPQ5018_PHY_DAC_MASK , +FIELD_PREP (IPQ5018_PHY_DAC_MASK , +IPQ5018_PHY_MMD1_MDAC_VAL ));/* setting EDAC (Error-detection and Correction) in debug register */ at803x_debug_reg_mask (phydev,IPQ5018_PHY_DEBUG_EDAC , -IPQ5018_PHY_DAC_MASK ,IPQ5018_PHY_DEBUG_EDAC_VAL ); +IPQ5018_PHY_DAC_MASK , +FIELD_PREP (IPQ5018_PHY_DAC_MASK , +IPQ5018_PHY_DEBUG_EDAC_VAL )); }return 0 ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 144/428 net: sparx5: skip ptp deinit if init was skipped
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Quentin Freimanis <[email protected]>
[ Upstream commit 0d2c49915bc3aea4521cda5e4d42f24f63b94585 ]
sparx5_ptp_init() returns early on the base lan969x variants because they do not have the SPX5_FEATURE_PTP flag. It also returns early when no "ptp" interrupt is described. Unbinding the driver then causes a NULL pointer dereference when cleaning up uninitialized tx_skbs queues:
Unable to handle kernel NULL pointer dereference at virtual address
0000000000000008
Call trace:
skb_queue_purge_reason+0x68/0x120 (P)
sparx5_ptp_deinit+0x64/0xc0
mchp_sparx5_remove+0x38/0x70
platform_remove+0x20/0x30
device_remove+0x4c/0x80Before per-port tx_skbs were added, a NULL dereference would have happened when calling ptp_clock_unregister() on the never-registered PTP clocks.
Fix by returning early in sparx5_ptp_deinit() if PTP is not used.
--- drivers/net/ethernet/microchip/sparx5/sparx5_ptp.c | 3 +++ 1 file changed, 3 insertions(+)
drivers/net/ethernet/microchip/sparx5/sparx5_ptp.c +3 -0
@@ -673,6 +673,9 @@ void sparx5_ptp_deinit(struct sparx5 *sparx5)struct sparx5_port *port;int i; +if (!sparx5->ptp) +return ; +if (sparx5->ptp_irq >=0 ) {disable_irq (sparx5->ptp_irq); sparx5->ptp_irq = -ENXIO ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 145/428 tcp: refresh TS.Recent for accepted old ACKs
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jeff Jo <[email protected]>
[ Upstream commit 1b92780ae4fc3be64f05d97ed8833c445e73b0cf ]
A TCP packet can carry new data while acknowledging traffic in the opposite direction. With overlapping traffic in both directions, a delayed packet's acknowledgment can be older than one Linux has already accepted, even when that packet fills a gap in the received data.
Linux accepts the data, but tcp_ack() takes the old_ack path and skips updating TS.Recent, the timestamp saved for outgoing acknowledgments. The reply therefore echoes an older timestamp. If the sender uses this echo to measure round-trip time after a long idle period, its estimate includes the idle time and can reduce its sending rate.
Update TS.Recent in old_ack using tcp_replace_ts_recent(), before SACK processing can trigger a transmission. This reuses the existing timestamp and sequence checks, including PAWS protection against old duplicate packets. ACK validation already rejects old ACKs in SYN_RECV before this path, so no additional state check is needed.
Echoing the timestamp of the packet that fills the receive gap follows RFC 7323 section 4.3. In a socket reproduction with 300 seconds idle, controlled reordering and retransmission to exercise timestamp-based RTT sampling, the sender's smoothed round-trip time was 37.5 seconds without the fix and 15.5 ms with it.
--- net/ipv4/tcp_input.c | 6 ++++++ 1 file changed, 6 insertions(+)
net/ipv4/tcp_input.c +6 -0
@@ -4465,6 +4465,12 @@ static int tcp_ack(struct sock *sk, const struct sk_buff *skb, int flag)return 1 ; old_ack: +/* An old ACK can carry new data. Update TS.Recent before SACK +* processing can trigger a retransmission. +*/ +if (flag &FLAG_UPDATE_TS_RECENT ) +tcp_replace_ts_recent (tp,TCP_SKB_CB (skb)->seq); +/* If data was SACKed, tag it and see if we should send more data. * If data was DSACKed, see if we can undo a cwnd reduction. */
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 146/428 ipvs: fix missing counter decrement in lblc
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Julian Anastasov <[email protected]>
[ Upstream commit bdac17779f46817f60103baf09f0370546c3f023 ]
LBLC may delete cache entries for destinations that are removed or overloaded and replace them with available ones. But ip_vs_lblc_new() forgets to decrement the tbl->entries counter after calling ip_vs_lblc_del(). This can lead to increased shrinking of the cache with every new garbage collection.
--- net/netfilter/ipvs/ip_vs_lblc.c | 1 + 1 file changed, 1 insertion(+)
net/netfilter/ipvs/ip_vs_lblc.c +1 -0
@@ -203,6 +203,7 @@ ip_vs_lblc_new(struct ip_vs_lblc_table *tbl, const union nf_inet_addr *daddr,if (en->dest == dest)return en;ip_vs_lblc_del (en); +atomic_dec (&tbl->entries); }if (atomic_read (&tbl->entries) >= tbl->max_size *3 /2 )return NULL ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 147/428 ipvs: do not create invisible templates
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Julian Anastasov <[email protected]>
[ Upstream commit 616cf5c06934364ab1002b420ebe975850b4b208 ]
The IP_VS_CONN_F_ONE_PACKET flag was implemented for normal connections. When conn template inherits this flag from dest->conn_flags it will not be hashed. As result, we will create new template for every new normal connection.
Fix it to allow one template to be used by many normal connections.
--- net/netfilter/ipvs/ip_vs_conn.c | 3 +++ 1 file changed, 3 insertions(+)
net/netfilter/ipvs/ip_vs_conn.c +3 -0
@@ -1102,6 +1102,9 @@ ip_vs_bind_dest(struct ip_vs_conn *cp, struct ip_vs_dest *dest)if (cp->protocol !=IPPROTO_UDP ) conn_flags &= ~IP_VS_CONN_F_ONE_PACKET ; flags = cp->flags; +/* Only visible templates can control multiple connections */ +if (flags &IP_VS_CONN_F_TEMPLATE ) + conn_flags &= ~IP_VS_CONN_F_ONE_PACKET ;/* Bind with the destination and its corresponding transmitter */ if (flags &IP_VS_CONN_F_SYNC ) {/* Synced conns are hashed, so they can not get this flag */
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 148/428 ipvs: filter some flags received in the backup server
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Julian Anastasov <[email protected]>
[ Upstream commit f96e91748f6304668fe647e686d199d7207d36f1 ]
While the IPVS SYNC protocol is not secure by design we can still protect the backup server from messages that can wreak havoc.
This commit addresses problems from received connection flags or their combinations. We now drop messages as follows:
1. the NO_CPORT+TEMPLATE combination allows lookups for normal connections to hit template which can break in many ways. While the master does not sync connections with NO_CPORT flag, i.e. before they are established, we still accept NO_CPORT without TEMPLATE.
2. ONE_PACKET: it is not sent by master, so we do not expect it in backup. Before now it was ignored by IP_VS_CONN_F_BACKUP_MASK for protocol v1 while protocol v0 created connections that are not hashed and dropped immediately. Better to apply the IP_VS_CONN_F_BACKUP_MASK also to the flags from v0 messages for consistency with v1.
--- net/netfilter/ipvs/ip_vs_sync.c | 33 ++++++++++++++++++++++++++++++--- 1 file changed, 30 insertions(+), 3 deletions(-)
net/netfilter/ipvs/ip_vs_sync.c +30 -3
@@ -949,6 +949,21 @@ static void ip_vs_proc_conn(struct netns_ipvs *ipvs, struct ip_vs_conn_param *paip_vs_conn_put (cp); } +/* Check for incompatible flags */ +static bool ip_vs_sync_validate_flags (u32 flags) +{ +/* We do not expect NO_CPORT, especially to allow lookups +* to hit templates +*/ +if (flags &IP_VS_CONN_F_NO_CPORT ) { +if (flags &IP_VS_CONN_F_TEMPLATE ) +return false; + } +if (flags &IP_VS_CONN_F_ONE_PACKET ) +return false; +return true; +} +/* * Process received multicast message for Version 0 */
@@ -972,8 +987,7 @@ static void ip_vs_process_message_v0(struct netns_ipvs *ipvs, const char *bufferreturn ; } s = (struct ip_vs_sync_conn_v0 *) p; - flags =ntohs (s->flags) |IP_VS_CONN_F_SYNC ; - flags &= ~IP_VS_CONN_F_HASHED ; + flags =ntohs (s->flags);if (flags &IP_VS_CONN_F_SEQ_MASK ) { opt = (struct ip_vs_sync_conn_options *)&s[1 ]; p +=FULL_CONN_SIZE ;
@@ -986,6 +1000,13 @@ static void ip_vs_process_message_v0(struct netns_ipvs *ipvs, const char *buffer p +=SIMPLE_CONN_SIZE ; } +if (!ip_vs_sync_validate_flags (flags)) { +IP_VS_DBG (2 ,"BACKUP v0, Invalid flags 0x%X\n" , flags); +continue ; + } + flags &=IP_VS_CONN_F_BACKUP_MASK ; + flags |=IP_VS_CONN_F_SYNC ; + state =ntohs (s->state);if (!(flags &IP_VS_CONN_F_TEMPLATE )) { pp =ip_vs_proto_get (s->protocol);
@@ -1141,7 +1162,13 @@ static inline int ip_vs_proc_sync_conn(struct netns_ipvs *ipvs, __u8 *p, __u8 *m }/* Get flags and Mask off unsupported */ - flags =ntohl (s->v4.flags) &IP_VS_CONN_F_BACKUP_MASK ; + flags =ntohl (s->v4.flags); +if (!ip_vs_sync_validate_flags (flags)) { +IP_VS_DBG (3 ,"BACKUP, Invalid flags 0x%X\n" , flags); + retc =25 ; + goto out; + } + flags &=IP_VS_CONN_F_BACKUP_MASK ; flags |=IP_VS_CONN_F_SYNC ; state =ntohs (s->v4.state);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 149/428 netfilter: bpf: reject invalid NAT manipulation types
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Fernando Fernandez Mancera <[email protected]>
[ Upstream commit 16d464013ec2267b00a83f4bfbb97b3ecc63bfa7 ]
As bpf_ct_set_nat_info() is not validating the NAT manipulation type a wrong value can be passed directly to nf_nat_setup_info(). This triggers the WARN_ON() at nf_nat_setup_info() and if panic_on_warn isn't set, then IPS_SRC_NAT_DONE is set without adding nat_bysource and conntrack cleanup tries to unlink an uninitialized hlist node.
Fix this by checking that NAT manipulation type is correct before calling nf_nat_setup_info(). In addition, if the WARN_ON is hit, return NF_DROP instead of continuing with the processing to avoid similar situations in the future.
--- net/netfilter/nf_nat_bpf.c | 3 +++ net/netfilter/nf_nat_core.c | 5 +++-- 2 files changed, 6 insertions(+), 2 deletions(-)
net/netfilter/nf_nat_bpf.c +3 -0
@@ -39,6 +39,9 @@ __bpf_kfunc int bpf_ct_set_nat_info(struct nf_conn___init *nfct,if (proto !=NFPROTO_IPV4 && proto !=NFPROTO_IPV6 )return -EINVAL ; +if (manip !=NF_NAT_MANIP_SRC && manip !=NF_NAT_MANIP_DST ) +return -EINVAL ; +memset (&range,0 ,sizeof (struct nf_nat_range2 )); range.flags =NF_NAT_RANGE_MAP_IPS ; range.min_addr = *addr;
net/netfilter/nf_nat_core.c +3 -2
@@ -773,8 +773,9 @@ nf_nat_setup_info(struct nf_conn *ct,if (nf_ct_is_confirmed (ct))return NF_ACCEPT ; -WARN_ON (maniptype !=NF_NAT_MANIP_SRC && - maniptype !=NF_NAT_MANIP_DST ); +if (WARN_ON (maniptype !=NF_NAT_MANIP_SRC && + maniptype !=NF_NAT_MANIP_DST )) +return NF_DROP ;if (WARN_ON (nf_nat_initialized (ct, maniptype)))return NF_DROP ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 150/428 netfilter: flowtable: generalize pending status bit
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Pablo Neira Ayuso <[email protected]>
[ Upstream commit 7c549fb7eecd01fdba7c0d12c01da876ef8a3214 ]
Rename NF_FLOW_HW_PENDING to NF_FLOW_PENDING and use it to inhibit the flowtable GC worker until pending hw offload work has been completed.
Apparently, nf_flow_offload_stats() can schedule work to retrieve stats while the flow is being removed by GC.
And this bit can also be used in a follow up patch to disable GC until the flow has been fully added in both directions.
Revert the reordering done in commit d644b23afe1e ("netfilter:
flowtable: publish HW_DEAD after worker is done") to prevent a race between GC and hw offload handler.
--- include/net/netfilter/nf_flow_table.h | 2 +- net/netfilter/nf_flow_table_core.c | 7 ++++++- net/netfilter/nf_flow_table_offload.c | 14 +++++--------- net/sched/act_ct.c | 2 +- 4 files changed, 13 insertions(+), 12 deletions(-)
include/net/netfilter/nf_flow_table.h +1 -1
@@ -183,10 +183,10 @@ enum nf_flow_flags {NF_FLOW_DNAT ,NF_FLOW_CLOSING ,NF_FLOW_TEARDOWN , +NF_FLOW_PENDING ,NF_FLOW_HW ,NF_FLOW_HW_DYING ,NF_FLOW_HW_DEAD , -NF_FLOW_HW_PENDING ,NF_FLOW_HW_BIDIRECTIONAL , NF_FLOW_HW_ESTABLISHED, };
net/netfilter/nf_flow_table_core.c +6 -1
@@ -575,7 +575,12 @@ static void nf_flow_table_extend_ct_timeout(struct nf_conn *ct)static void nf_flow_offload_gc_step (struct nf_flowtable *flow_table,struct flow_offload *flow,void *data) { -bool teardown =test_bit (NF_FLOW_TEARDOWN , &flow->flags); +bool teardown; + +if (test_bit (NF_FLOW_PENDING , &flow->flags)) +return ; + + teardown =test_bit (NF_FLOW_TEARDOWN , &flow->flags); if (nf_flow_has_expired(flow) || nf_ct_is_dying(flow->ct) ||
net/netfilter/nf_flow_table_offload.c +5 -9
@@ -995,6 +995,7 @@ static void flow_offload_work_del(struct flow_offload_work *offload)flow_offload_tuple_del (offload,FLOW_OFFLOAD_DIR_ORIGINAL );if (test_bit (NF_FLOW_HW_BIDIRECTIONAL , &offload->flow->flags))flow_offload_tuple_del (offload,FLOW_OFFLOAD_DIR_REPLY ); +set_bit (NF_FLOW_HW_DEAD , &offload->flow->flags); } static void flow_offload_tuple_stats(struct flow_offload_work *offload,
@@ -1056,13 +1057,8 @@ static void flow_offload_work_handler(struct work_struct *work)default :WARN_ON_ONCE (1 ); } - -clear_bit (NF_FLOW_HW_PENDING , &offload->flow->flags); -if (offload->cmd ==FLOW_CLS_DESTROY ) { -/* Publish after the worker's last flow access. */ -smp_mb__before_atomic (); -set_bit (NF_FLOW_HW_DEAD , &offload->flow->flags); - } +smp_mb__before_atomic (); +clear_bit (NF_FLOW_PENDING , &offload->flow->flags);kfree (offload); }
@@ -1089,12 +1085,12 @@ nf_flow_offload_work_alloc(struct nf_flowtable *flowtable, {struct flow_offload_work *offload; -if (test_and_set_bit (NF_FLOW_HW_PENDING , &flow->flags)) +if (test_and_set_bit (NF_FLOW_PENDING , &flow->flags))return NULL ; offload = kmalloc_obj(struct flow_offload_work, GFP_ATOMIC);if (!offload) { -clear_bit (NF_FLOW_HW_PENDING , &flow->flags); +clear_bit (NF_FLOW_PENDING , &flow->flags);return NULL ; }
net/sched/act_ct.c +1 -1
@@ -289,7 +289,7 @@ static bool tcf_ct_flow_is_outdated(const struct flow_offload *flow) {return test_bit (IPS_SEEN_REPLY_BIT , &flow->ct->status) &&test_bit (IPS_HW_OFFLOAD_BIT , &flow->ct->status) && - !test_bit (NF_FLOW_HW_PENDING , &flow->flags) && + !test_bit (NF_FLOW_PENDING , &flow->flags) && !test_bit (NF_FLOW_HW_ESTABLISHED , &flow->flags); }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 151/428 net: microchip: vcap: stop scanning after deleting key field
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Harshit Mogalapalli <[email protected]>
[ Upstream commit 99b43ede9e355ba35244cc9470bf1819774ce39d ]
The only call outside the KUnit tests is in sparx5_tc_add_rule_copy(), which passes a fixed key list with no duplicate entries. Therefore, the potential double-free is not currently reachable in tree.
However, vcap_filter_rule_keys() is exported and does not require its key list entries to be unique. If a future or external caller supplies the same key at another position that the inner loop visits, it calls list_del() and kfree() a second time on the freed field, leading to a potential double-free.
Break the inner loop after removing a matching field. The outer safe list traversal continues to process the remaining rule fields.
--- drivers/net/ethernet/microchip/vcap/vcap_api.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/net/ethernet/microchip/vcap/vcap_api.c +1 -1
@@ -3524,8 +3524,8 @@ int vcap_filter_rule_keys(struct vcap_rule *rule,if (key == keylist[idx]) {list_del (&ckf->ctrl.list);kfree (ckf); - idx++; err =0 ; +break ; } } }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 152/428 of: Put coreboot node after compatibility check
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Yuho Choi <[email protected]>
[ Upstream commit 77f21a59e8cf7efcc1c4b3e9176e8b99993a7ae4 ]
of_find_compatible_node() returns a referenced device node, but EXCLUDED_DEFAULT_CELLS_PLATFORMS used the result only as a boolean and discarded the reference. This leaked a reference each time the address-cells or size-cells warning condition was evaluated on a coreboot system.
Use a helper that checks for the coreboot node with a scoped reference, so the reference is dropped once the check is done.
--- drivers/of/base.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-)
drivers/of/base.c +9 -1
@@ -95,9 +95,17 @@ static bool __of_node_is_type(const struct device_node *np, const char *type)return !strcmp (match, type); } +static bool of_coreboot_present (void ) +{ + struct device_node *np __free(device_node) = +of_find_compatible_node (NULL ,NULL ,"coreboot" ); + +return np; +} +#define EXCLUDED_DEFAULT_CELLS_PLATFORMS ( \ IS_ENABLED(CONFIG_SPARC) || \ - of_find_compatible_node(NULL, NULL, "coreboot") \ + of_coreboot_present() \ )int of_bus_n_addr_cells (struct device_node *np)
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 153/428 net: sparx5: make ports inherit the switch base mac address type
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Quentin Freimanis <[email protected]>
[ Upstream commit 5642b1648d5c4561d8ed7a410d0a7017f706604d ]
When the switch uses a random base MAC address it does not mark each port's address as random:
$ dmesg | grep "MAC addr" sparx5-switch e00c0000.switch: MAC addr was not set, use random MAC $ cat /sys/class/net/eth0/addr_assign_type 0
0 is NET_ADDR_PERM, so userspace thinks this is a stable address.
With a random MAC reported as NET_ADDR_PERM, systemd's MACAddressPolicy=persistent [1] leaves it unchanged, so a DHCP client gets a new IP address every boot and a static reservation can't be used. Reporting it as NET_ADDR_RANDOM makes systemd replace it with a stable address derived from the interface name and machine-id.
--- drivers/net/ethernet/microchip/sparx5/sparx5_main.c | 1 + drivers/net/ethernet/microchip/sparx5/sparx5_main.h | 1 + drivers/net/ethernet/microchip/sparx5/sparx5_netdev.c | 1 + 3 files changed, 3 insertions(+)
drivers/net/ethernet/microchip/sparx5/sparx5_main.c +1 -0
@@ -898,6 +898,7 @@ static int mchp_sparx5_probe(struct platform_device *pdev)dev_info (sparx5->dev,"MAC addr was not set, use random MAC\n" );eth_random_addr (sparx5->base_mac); sparx5->base_mac[5 ] =0 ; + sparx5->base_mac_assign_type =NET_ADDR_RANDOM ; } sparx5->fdma_irq =platform_get_irq_byname (sparx5->pdev,"fdma" );
drivers/net/ethernet/microchip/sparx5/sparx5_main.h +1 -0
@@ -392,6 +392,7 @@ struct sparx5 {struct notifier_block switchdev_blocking_nb;/* Switch state */ u8 base_mac[ETH_ALEN ]; +u8 base_mac_assign_type;/* Associated bridge device (when bridged) */ struct net_device *hw_bridge_dev;/* Bridged interfaces */
drivers/net/ethernet/microchip/sparx5/sparx5_netdev.c +1 -0
@@ -294,6 +294,7 @@ struct net_device *sparx5_create_netdev(struct sparx5 *sparx5, u32 portno) ndev->ethtool_ops = &sparx5_ethtool_ops;eth_hw_addr_gen (ndev, sparx5->base_mac, portno +1 ); + ndev->addr_assign_type = sparx5->base_mac_assign_type;return ndev; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 154/428 net/mlx5e: Fix AF_XDP TX timestamp teardown NULL dereference
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Prathamesh Deshpande <[email protected]>
[ Upstream commit 7b97273cbebe9dd4fddc457f0c4f2a124d0e3996 ]
During XSK TX queue teardown, outstanding descriptors are completed without a CQE. If one requested a TX timestamp, the completion path passes that NULL CQE to mlx5e_xsk_fill_timestamp(), which dereferences it.
Return zero when no CQE is available, indicating that teardown did not produce a TX timestamp.
--- drivers/net/ethernet/mellanox/mlx5/core/en/xdp.c | 3 +++ 1 file changed, 3 insertions(+)
drivers/net/ethernet/mellanox/mlx5/core/en/xdp.c +3 -0
@@ -293,6 +293,9 @@ static u64 mlx5e_xsk_fill_timestamp(void *_priv)struct mlx5e_xsk_tx_complete *priv = _priv;u64 ts; +if (!priv->cqe) +return 0 ; + ts =get_cqe_ts (priv->cqe);if (mlx5_is_real_time_rq (priv->cq->mdev) ||mlx5_is_real_time_sq (priv->cq->mdev))
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 155/428 ipv6: sr: use skb_get_hash_net() in seg6_make_flowlabel()
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Eric Dumazet <[email protected]>
[ Upstream commit 8ec454dcd00ce3375119111c6394964455b36d0d ]
Since commit d58e468b1112 ("flow_dissector: implements flow dissector BPF hook") __skb_flow_dissect() needs a net pointer, either from skb->dev, skb->sk, or since commit 3cbf4ffba5ee ("net: plumb network namespace into __skb_flow_dissect") a caller provided pointer.
syzbot was able to reach seg6_make_flowlabel() with an skb having neither skb->dev nor skb->sk set: a TIPC UDP bearer sends a discovery message through an IPv4 route using seg6 encap, while net.ipv6.seg6_flowlabel is set to 1.
seg6_make_flowlabel() already has a net pointer, use skb_get_hash_net().
WARNING: net/core/flow_dissector.c:1131 at __skb_flow_dissect+0x910/0x5368 net/core/flow_dissector.c:1126, CPU#0: syz.0.17/4930 Call trace: __skb_flow_dissect+0x910/0x5368 net/core/flow_dissector.c:1126 (P) __skb_get_hash_net+0xe0/0x29c net/core/flow_dissector.c:1903 skb_get_hash include/linux/skbuff.h:1663 [inline] seg6_make_flowlabel+0xcc/0x1ec net/ipv6/seg6_iptunnel.c:132 __seg6_do_srh_encap+0x320/0xbf4 net/ipv6/seg6_iptunnel.c:161 seg6_do_srh+0x4b4/0xa44 net/ipv6/seg6_iptunnel.c:431 seg6_output_core+0x164/0x688 net/ipv6/seg6_iptunnel.c:681 seg6_output+0x44/0x1ac net/ipv6/seg6_iptunnel.c:744 lwtunnel_output+0x3d0/0x664 net/core/lwtunnel.c:356 dst_output include/net/dst.h:470 [inline] ip_local_out+0x110/0x148 net/ipv4/ip_output.c:131 iptunnel_xmit+0x50c/0xd38 net/ipv4/ip_tunnel_core.c:97 udp_tunnel_xmit_skb+0x220/0x348 net/ipv4/udp_tunnel_core.c:187 tipc_udp_xmit+0x75c/0x9c0 net/tipc/udp_media.c:202 tipc_udp_send_msg+0x214/0x374 net/tipc/udp_media.c:274 tipc_bearer_xmit_skb+0x260/0x3b0 net/tipc/bearer.c:576 tipc_enable_bearer net/tipc/bearer.c:366 [inline] __tipc_nl_bearer_enable+0xc90/0xfb0 net/tipc/bearer.c:1048 tipc_nl_bearer_enable+0x2c/0x48 net/tipc/bearer.c:1057 genl_family_rcv_msg_doit+0x1e4/0x2d4 net/netlink/genetlink.c:1114
--- net/ipv6/seg6_iptunnel.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
net/ipv6/seg6_iptunnel.c +1 -1
@@ -127,7 +127,7 @@ static __be32 seg6_make_flowlabel(struct net *net, struct sk_buff *skb,u32 hash;if (do_flowlabel >0 ) { - hash =skb_get_hash (skb); + hash =skb_get_hash_net (net, skb); hash =rol32 (hash,16 ); flowlabel = (__force __be32 )hash &IPV6_FLOWLABEL_MASK ; }else if (!do_flowlabel && skb->protocol ==htons (ETH_P_IPV6 )) {
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 156/428 net: mvneta: clear XDP pfmemalloc flag between frames
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Lorenzo Bianconi <[email protected]>
[ Upstream commit 8f1c2a10500a84a621ff13073f96deea0753ed42 ]
mvneta_swbm_add_rx_fragment() sets XDP_FLAGS_FRAGS_PF_MEMALLOC on the xdp_buff when a fragment page is a pfmemalloc one (page under memory pressure). The xdp_buff is reused for the next frame, but only the XDP_FLAGS_HAS_FRAGS bit was cleared at frame start, so the pfmemalloc bit leaked from one frame into the following ones. mvneta_swbm_build_skb() propagates the flag to skb->pfmemalloc through xdp_update_skb_frags_info(), so the skb of a subsequent fragmented frame could be wrongly marked as pfmemalloc even if none of its pages are under pressure.
Clear all the xdp_buff flags in mvneta_swbm_rx_frame(), which is invoked for each new frame, instead of just the XDP_FLAGS_HAS_FRAGS bit.
--- drivers/net/ethernet/marvell/mvneta.c | 2 +- include/net/xdp.h | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-)
drivers/net/ethernet/marvell/mvneta.c +1 -1
@@ -2340,7 +2340,7 @@ mvneta_swbm_rx_frame(struct mvneta_port *pp,/* Prefetch header */ prefetch (data); -xdp_buff_clear_frags_flag (xdp); +xdp_reinit_buff (xdp);xdp_prepare_buff (xdp, data, pp->rx_offset_correction +MVNETA_MH_SIZE , data_len, true); }
include/net/xdp.h +5 -0
@@ -106,6 +106,11 @@ struct xdp_buff { }; }; +static __always_inline void xdp_reinit_buff(struct xdp_buff *xdp) +{ + xdp->flags =0 ; +} + static __always_inline bool xdp_buff_has_frags(const struct xdp_buff *xdp) {return !!(xdp->flags &XDP_FLAGS_HAS_FRAGS );
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 157/428 i2c: at91: release DMA channels when probe defers
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Hongjian Dai <[email protected]>
[ Upstream commit c98bf3b86609a18ab16d067280257326e557c636 ]
at91_twi_probe_master() can return -EPROBE_DEFER from at91_init_twi_recovery_info() after at91_twi_configure_dma() has already claimed the tx/rx DMA channels. at91_twi_probe() then returns without releasing them, and since dma_request_chan() is not devres-managed the channels leak on every deferred probe attempt.
Release the channels before deferring the probe.
--- drivers/i2c/busses/i2c-at91-master.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-)
drivers/i2c/busses/i2c-at91-master.c +7 -1
@@ -921,8 +921,14 @@ int at91_twi_probe_master(struct platform_device *pdev,at91_calc_twi_clock (dev); rc =at91_init_twi_recovery_info (pdev, dev); -if (rc == -EPROBE_DEFER ) +if (rc == -EPROBE_DEFER ) { +/* +* The DMA channels were already set up above, release them +* before deferring the probe so they are not leaked. +*/ +at91_twi_dma_release (dev);return rc; + } dev->adapter.algo = &at91_twi_algorithm; dev->adapter.quirks = &at91_twi_quirks;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 158/428 perf: Fix race between perf_event_exit_task() and perf_pending_task()
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Luo Gengkun <[email protected]>
[ Upstream commit ffb684f2aa141eeb0caa6308422e7e05d1ded7c4 ]
A race condition exists between perf_event_exit_task() and perf_pending_task() during begin_new_exec().
During begin_new_exec(), perf_event_exit_task() may be called, and the PF_EXITING flag is not set on task. So perf_sigtrap() continues to execute and triggers WARN_ON_ONCE(event->ctx->task != current).
Since both task exit and exec paths can call perf_event_exit_task() which sets ctx->task to TASK_TOMBSTONE, fix this by explicitly checking if event->ctx->task equals TASK_TOMBSTONE and dropping the redundant PF_EXITING check.
--- kernel/events/core.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-)
kernel/events/core.c +4 -2
@@ -7624,9 +7624,11 @@ static void perf_sigtrap(struct perf_event *event) {/* * Both perf_pending_task() and perf_pending_irq() can race with the -* task exiting. +* task exiting or exec-ing. We can determine if such a race has +* occurred by checking if perf_event_exit_task(), which will set +* ctx->task to TASK_TOMBSTONE, has already been called. */ -if (current->flags &PF_EXITING ) +if (event->ctx->task ==TASK_TOMBSTONE )return ;/*
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 159/428 ALSA: core: Define auto-cleanup for snd_card_free()
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Takashi Iwai <[email protected]>
[ Upstream commit f1d565dc92955f05383794e0fa27e61ee0555027 ]
For the errors at the probe time, we'd need to call rather snd_card_free() instead of the snd_card_unref() -- the former calls explicitly snd_card_disconnect() that cleans up the registered devices, etc, while the latter may leak in corner cases.
For convenience, define a new auto-clean with snd_card_free.
Stable-dep-of: e6229c0402ea ("ALSA: ice1712: Fix the error handling via auto-cleanup at probe")
--- include/sound/core.h | 9 +++++++++ 1 file changed, 9 insertions(+)
include/sound/core.h +9 -0
@@ -340,6 +340,15 @@ static inline void snd_card_unref(struct snd_card *card) DEFINE_FREE(snd_card_unref, struct snd_card *, if (_T) snd_card_unref(_T)) +/* +* For automatic error handling at probe time, assign the card like: +* struct snd_card *card __free(snd_card_free) = NULL; +* then create and process as usual. +* But, don't forget to clear to NULL at successful return for avoiding +* the unexpected cleanup! + */ +DEFINE_FREE(snd_card_free, struct snd_card *, if (_T) snd_card_free(_T)) +#define snd_card_set_dev (card, devptr) ((card)->dev = (devptr))/* device.c */
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 160/428 ALSA: ice1712: Fix the error handling via auto-cleanup at probe
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Takashi Iwai <[email protected]>
[ Upstream commit e6229c0402ea4863ec74802d79d5b52b9398d66c ]
We used the auto-cleanup via snd_card_unref() for errors at probe of ice1712 driver, but this may be problematic in a subtle way when an error happens at snd_card_register(); since snd_card_unref() skips the snd_card_disconnect() call, it may miss some resource clearance.
For fixing the issue, use the new __free(snd_card_free) instead, which does call snd_card_free() explicitly at errors for avoiding such a pitfall.
--- sound/pci/ice1712/ice1712.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
sound/pci/ice1712/ice1712.c +1 -1
@@ -2523,7 +2523,7 @@ static int snd_ice1712_probe(struct pci_dev *pci, const struct pci_device_id *pci_id) {static int dev; - struct snd_card *card __free(snd_card_unref) = NULL; + struct snd_card *card __free(snd_card_free) = NULL;struct snd_ice1712 *ice;int pcm_dev =0 , err;const struct snd_ice1712_card_info *const *tbl, *c;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 161/428 spi: cs42l43: Workaround for wrong speaker ID on Dell XPS 13 DX13260
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Richard Fitzgerald <[email protected]>
[ Upstream commit cad16d850e3759dd82cd869f739b56e9844a1fee ]
On Dell XPS 13 DX13260 create an acpi_gpio_mapping with exactly two GPIO entries to point at the two pins in the GpioIo(). Use this to read the speaker ID GPIOs.
This fixes problems on Dell XPS 13 DX13260:
- No speaker audio
- The wrong firmware was loaded so the speaker protection did not match the speaker characteristics.
The Dell XPS 13 DX13260 has two speaker ID GPIOs, to form a 2-bit ID. The ACPI GpioIo() has both pins but the Linux-specific spk-id-gpios property only has a mapping to the first pin. This meant that the speaker ID was wrong in most cases, and that would lead to the codec driver loading the wrong amp firmware, or not finding a firmware (as 0 is not a valid ID on this laptop).
--- drivers/spi/spi-cs42l43.c | 97 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 97 insertions(+)
drivers/spi/spi-cs42l43.c +97 -0
@@ -11,6 +11,7 @@#include <linux/bitfield.h> #include <linux/cleanup.h> #include <linux/device.h> +#include <linux/dmi.h> #include <linux/errno.h> #include <linux/gpio/consumer.h> #include <linux/gpio/machine.h>
@@ -215,12 +216,108 @@ static size_t cs42l43_spi_max_length(struct spi_device *spi)return CS42L43_SPI_MAX_LENGTH ; } +/* +* Workaround needed for two speaker ID pins in one ACPI GpioIo() but +* the Linux-specific _DSD property only contains one pin. +* Create a temporary acpi_gpio_mapping pointing at both pins. +*/ +static const struct acpi_gpio_params cs42l43_2bit_speaker_id_from_one_gpioio_params[] = { + [0 ] = { + .crs_entry_index =0 , + .line_index =0 , + }, + [1 ] = { + .crs_entry_index =0 , + .line_index =1 , + }, +}; + +static const struct acpi_gpio_mapping cs42l43_2bit_speaker_id_from_one_gpioio_mapping[] = { + { + .name ="spk-id-quirk-gpios" , + .data = cs42l43_2bit_speaker_id_from_one_gpioio_params, + .size =ARRAY_SIZE (cs42l43_2bit_speaker_id_from_one_gpioio_params), + }, + { } +}; + +static int cs42l43_get_2bit_speaker_id_from_one_gpioio (struct cs42l43_spi *priv,int *result) +{ +struct fwnode_reference_args args; +struct acpi_device *adev; +struct gpio_desc *desc; +u32 spkid =0 ; +int i, ret; + +/* Use the _DSD property to get the node containing the GpioIo() */ + ret =fwnode_property_get_reference_args (dev_fwnode (priv->dev),"spk-id-gpios" , +NULL ,3 ,0 , &args); +if (ret) +return ret; + + struct fwnode_handle *fwnode __free(fwnode_handle) = args.fwnode; + +/* An acpi_gpio_mapping must be added to the node that contains the GpioIo() */ + adev =to_acpi_device_node (fwnode); +if (!adev) +return -EINVAL ; + + ret =acpi_dev_add_driver_gpios (adev, cs42l43_2bit_speaker_id_from_one_gpioio_mapping); +if (ret) +return ret; + +/* gpiod_get_array() can't read from a mapping in a child node */ +for (i =0 ; i <ARRAY_SIZE (cs42l43_2bit_speaker_id_from_one_gpioio_params); i++) { + desc =fwnode_gpiod_get_index (fwnode,"spk-id-quirk" , i,GPIOD_IN , +dev_name (priv->dev)); +if (IS_ERR (desc)) { + ret =PTR_ERR (desc); + goto out; + } + + ret =gpiod_get_value_cansleep (desc); +gpiod_put (desc); +if (ret <0 ) + goto out; + + spkid |= (u32 )ret << i; + } + +dev_dbg (priv->dev,"spk-id-gpios = %u\n" , spkid); + *result = spkid; + ret =0 ; +out: +acpi_dev_remove_driver_gpios (adev); + +return ret; +} + +static const struct dmi_system_id cs42l43_spk_id_quirks[] = { + { + .ident ="Dell XPS 13 DX13260" , + .matches = { +DMI_MATCH (DMI_SYS_VENDOR ,"Dell Inc" ), +DMI_EXACT_MATCH (DMI_PRODUCT_SKU ,"0E53" ), + }, + .driver_data = cs42l43_get_2bit_speaker_id_from_one_gpioio, + }, + { } +}; +static int cs42l43_get_speaker_id_gpios (struct cs42l43_spi *priv,int *result) { +const struct dmi_system_id *dmi_id;struct gpio_descs *descs;u32 spkid;int i, ret; + dmi_id =dmi_first_match (cs42l43_spk_id_quirks); +if (dmi_id) { +int (*get_speaker_id)(struct cs42l43_spi *priv,int *result) = dmi_id->driver_data; + +return get_speaker_id (priv, result); + } + descs =gpiod_get_array_optional (priv->dev,"spk-id" ,GPIOD_IN );if (!descs)return 0 ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 162/428 bpf: Factor out __do_call_rcu_ttrace()
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Alexei Starovoitov <[email protected]>
[ Upstream commit 47f4f695cec1eb82d961fdb466cd7c99d2865b7c ]
Move the part of do_call_rcu_ttrace() that runs after call_rcu_ttrace_in_progress is set into __do_call_rcu_ttrace(). The next patch will call it from __free_rcu().
No functional change.
Stable-dep-of: fbd97dd1d3ce ("bpf: Fix objects stuck in free_by_rcu_ttrace")
--- kernel/bpf/memalloc.c | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-)
kernel/bpf/memalloc.c +17 -10
@@ -298,19 +298,10 @@ static void enque_to_free(struct bpf_mem_cache *c, void *obj)llist_add (llnode, &c->free_by_rcu_ttrace); } -static void do_call_rcu_ttrace (struct bpf_mem_cache *c) +static void __do_call_rcu_ttrace (struct bpf_mem_cache *c) {struct llist_node *llnode, *t; -if (atomic_xchg (&c->call_rcu_ttrace_in_progress,1 )) { -if (unlikely (READ_ONCE (c->draining))) { -scoped_guard (raw_spinlock_irqsave, &c->lock) - llnode = llist_del_all(&c->free_by_rcu_ttrace); -free_all (c, llnode, !!c->percpu_size); - } -return ; - } -WARN_ON_ONCE (!llist_empty (&c->waiting_for_gp_ttrace));llist_for_each_safe (llnode, t,llist_del_all (&c->free_by_rcu_ttrace))llist_add (llnode, &c->waiting_for_gp_ttrace);
@@ -328,6 +319,22 @@ static void do_call_rcu_ttrace(struct bpf_mem_cache *c)call_rcu_tasks_trace (&c->rcu_ttrace, __free_rcu); } +static void do_call_rcu_ttrace (struct bpf_mem_cache *c) +{ +struct llist_node *llnode; + +if (atomic_xchg (&c->call_rcu_ttrace_in_progress,1 )) { +if (unlikely (READ_ONCE (c->draining))) { +scoped_guard (raw_spinlock_irqsave, &c->lock) + llnode = llist_del_all(&c->free_by_rcu_ttrace); +free_all (c, llnode, !!c->percpu_size); + } +return ; + } + +__do_call_rcu_ttrace (c); +} +static void free_bulk (struct bpf_mem_cache *c) {struct bpf_mem_cache *tgt = c->tgt;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 163/428 bpf: Fix objects stuck in free_by_rcu_ttrace
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Alexei Starovoitov <[email protected]>
[ Upstream commit fbd97dd1d3ce32d32fb7be86acc3fdb7c04fa043 ]
do_call_rcu_ttrace() returns early when call_rcu_ttrace_in_progress is set and leaves the objects in free_by_rcu_ttrace. __free_rcu() frees waiting_for_gp_ttrace only and clears the flag. Hence the objects that free_bulk() or __free_by_rcu() added while RCU tasks trace GP was in flight stay in free_by_rcu_ttrace until free_bulk() or alloc_bulk() is called for the same bpf_mem_cache again, which may never happen. The number of such objects is not bounded.
Turn call_rcu_ttrace_in_progress into three states:
0 - idle
1 - __free_rcu() is queued
2 - __free_rcu() is queued and free_by_rcu_ttrace got more objects since
do_call_rcu_ttrace() sets 2. __free_rcu() does cmpxchg(1 -> 0) and starts the next GP when it fails. It cannot clear the flag first and check free_by_rcu_ttrace later, since bpf_mem_alloc_destroy() frees bpf_mem_cache without waiting for RCU callbacks when the flag is zero.
Now __free_rcu() queues itself, so the one that didn't see 'draining' may do call_rcu_tasks_trace() after rcu_barrier_tasks_trace() in free_mem_alloc(). Queue it under rcu_read_lock() and do synchronize_rcu() before the barriers. Calling rcu_barrier_tasks_trace() twice works too, but creating and destroying hash maps in a loop on many cpus slows down to one free_mem_alloc() per GP and kworkers pile up.
--- kernel/bpf/memalloc.c | 34 ++++++++++++++++++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-)
kernel/bpf/memalloc.c +32 -2
@@ -118,6 +118,11 @@ struct bpf_mem_cache {struct llist_head free_by_rcu_ttrace;struct llist_head waiting_for_gp_ttrace;struct rcu_head rcu_ttrace; +/* +* 0 - idle +* 1 - __free_rcu() is queued +* 2 - __free_rcu() is queued and free_by_rcu_ttrace got more objects since +*/ atomic_t call_rcu_ttrace_in_progress;raw_spinlock_t lock; };
@@ -276,6 +281,8 @@ static int free_all(struct bpf_mem_cache *c, struct llist_node *llnode, bool perreturn cnt; } +static void __do_call_rcu_ttrace (struct bpf_mem_cache *c); +static void __free_rcu (struct rcu_head *head) { struct bpf_mem_cache *c = container_of(head, struct bpf_mem_cache, rcu_ttrace);
@@ -285,7 +292,19 @@ static void __free_rcu(struct rcu_head *head) llnode =llist_del_all (&c->waiting_for_gp_ttrace);free_all (c, llnode, !!c->percpu_size); -atomic_set (&c->call_rcu_ttrace_in_progress,0 ); + +/* +* do_call_rcu_ttrace() that ran while GP was in flight left its objects +* in free_by_rcu_ttrace. This cache may never free or alloc in bulk +* again, so start the next GP from here. +* 'c' can be freed as soon as call_rcu_ttrace_in_progress is zero. +*/ +if (atomic_cmpxchg (&c->call_rcu_ttrace_in_progress,1 ,0 ) ==1 ) +return ; + +/* Pairs with synchronize_rcu() in free_mem_alloc() */ +guard (rcu)(); +__do_call_rcu_ttrace (c); }static void enque_to_free (struct bpf_mem_cache *c,void *obj)
@@ -302,6 +321,12 @@ static void __do_call_rcu_ttrace(struct bpf_mem_cache *c) {struct llist_node *llnode, *t; +/* +* Must be done before llist_del_all(). Objects that it misses were +* added by do_call_rcu_ttrace() that will set 2 after this store. +*/ +atomic_set (&c->call_rcu_ttrace_in_progress,1 ); +WARN_ON_ONCE (!llist_empty (&c->waiting_for_gp_ttrace));llist_for_each_safe (llnode, t,llist_del_all (&c->free_by_rcu_ttrace))llist_add (llnode, &c->waiting_for_gp_ttrace);
@@ -323,7 +348,7 @@ static void do_call_rcu_ttrace(struct bpf_mem_cache *c) {struct llist_node *llnode; -if (atomic_xchg (&c->call_rcu_ttrace_in_progress,1 )) { +if (atomic_xchg (&c->call_rcu_ttrace_in_progress,2 )) {if (unlikely (READ_ONCE (c->draining))) {scoped_guard (raw_spinlock_irqsave, &c->lock) llnode = llist_del_all(&c->free_by_rcu_ttrace);
@@ -707,7 +732,12 @@ static void free_mem_alloc(struct bpf_mem_alloc *ma)* to wait for the pending __free_by_rcu(), and __free_rcu(). RCU Tasks * Trace grace period implies RCU grace period, so all __free_rcu don't * need extra call_rcu() (and thus extra rcu_barrier() here). +* +* __free_rcu() queues itself again unless it sees 'draining'. After +* synchronize_rcu() it either did that already or will not do it, so +* rcu_barrier_tasks_trace() cannot miss it. */ +synchronize_rcu ();rcu_barrier ();/* wait for __free_by_rcu */ rcu_barrier_tasks_trace ();/* wait for __free_rcu */ free_mem_alloc_no_barrier (ma);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 164/428 drm/mediatek: Fix runtime PM leak in mtk_hdmi_ddc_v2_probe()
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Wentao Liang <[email protected]>
[ Upstream commit 2af45da333bdd7be2666688a0f861e0750e40e49 ]
pm_runtime_get_sync() unconditionally bumps the usage counter, but nothing puts it back when devm_i2c_add_adapter() fails, leaving the DDC runtime-resumed after a failed probe. Drop the reference on that error path.
--- drivers/gpu/drm/mediatek/mtk_hdmi_ddc_v2.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
drivers/gpu/drm/mediatek/mtk_hdmi_ddc_v2.c +3 -1
@@ -376,8 +376,10 @@ static int mtk_hdmi_ddc_v2_probe(struct platform_device *pdev)pm_runtime_get_sync (dev); ret =devm_i2c_add_adapter (dev, &ddc->adap); -if (ret <0 ) +if (ret <0 ) { +pm_runtime_put_noidle (dev);return dev_err_probe (dev, ret,"Cannot add DDC I2C adapter\n" ); + }platform_set_drvdata (pdev, ddc);return 0 ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 165/428 futex: Fix private hash use-after-free on resize
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Chris Mason <[email protected]>
[ Upstream commit f35e3b5784221654f9cdbd6222275a7fa203f6c3 ]
poll_state_synchronize_rcu(mm->futex.phash.batches) is used by futex_ref_drop() to check that a grace period has passed since the current hash was published. This relies on batches referencing a grace period which started after the hash pointer was assigned.
__futex_pivot_hash() sets mmph->batches before it replaces mmph->hash:
scoped_guard(rcu) {
mmph->batches = get_state_synchronize_rcu();
rcu_assign_pointer(mmph->hash, new);
}The scoped_guard(rcu) doesn't stop new grace periods from starting, and if one starts between those two assignments, futex_ref_drop() can move forward while a reader still holds a pointer to the old hash.
Fix things by setting mmph->batches after assigning mmph->hash. The scoped_guard(rcu) isn't needed, so let's drop that as well.
--- kernel/futex/core.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-)
kernel/futex/core.c +6 -4
@@ -216,10 +216,12 @@ static bool __futex_pivot_hash(struct mm_struct *mm, struct futex_private_hash *futex_rehash_private (fph, new); } new->state =FR_PERCPU ; -scoped_guard (rcu) { - mmph->batches =get_state_synchronize_rcu (); -rcu_assign_pointer (mmph->hash, new); - } +rcu_assign_pointer (mmph->hash, new); +/* +* mmph->batches must reference a grace period which started after +* mmph->hash was assigned. See futex_ref_drop(). +*/ + mmph->batches =get_state_synchronize_rcu ();kvfree_rcu (fph, rcu);return true; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 166/428 bpf: Fix missing migration protection in __rhtab_map_lookup_and_delete_batch()
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ömer Mete Kaya <[email protected]>
[ Upstream commit de020dc8049bfb2b22e3b6d99c031feb2e22d112 ]
bpf_mem_cache_free_rcu() uses this_cpu_ptr() which requires migration to be disabled. All callers of rhtab_delete_elem() disable migration except __rhtab_map_lookup_and_delete_batch(), which calls it under rcu_read_lock() only.
On CONFIG_PREEMPT_RCU, rcu_read_lock() does not disable preemption or migration, so the task can migrate between CPUs during the delete loop, causing this_cpu_ptr() to trigger:
BUG: using smp_processor_id() in preemptible [00000000] code
Fix by wrapping the delete loop in migrate_disable()/migrate_enable() in __rhtab_map_lookup_and_delete_batch(), matching the migration protection that the other callers already provide.
--- kernel/bpf/hashtab.c | 2 ++ 1 file changed, 2 insertions(+)
kernel/bpf/hashtab.c +2 -0
@@ -3376,8 +3376,10 @@ static int __rhtab_map_lookup_and_delete_batch(struct bpf_map *map, }if (do_delete) { +migrate_disable ();for (i =0 ; i < total; i++)rhtab_delete_elem (rhtab, del_elems[i],NULL ,0 ); +migrate_enable (); }rcu_read_unlock ();
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 167/428 PCI: Accept AtomicOps already enabled by the hypervisor
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Nikola Prica <[email protected]>
[ Upstream commit 40cce45b921f7930886bcba43069edc579725457 ]
pci_enable_atomic_ops_to_root() currently fails when no Root Port is visible. That is common in passthrough guests (ESXi, Hyper-V): the Endpoint is assigned to the VM, but the Root Port above it is not visible in the guest topology.
In those setups the hypervisor may already have enabled AtomicOp Requester Enable on the device. If PCI_EXP_DEVCTL2_ATOMIC_REQ is set, treat AtomicOps as already enabled and return success instead of failing the Root Port walk.
After 1ae8c4ce1570 ("PCI: Enable AtomicOps only if Root Port supports them"), pci_enable_atomic_ops_to_root() always returns failure if the Root Port is not visible, so drivers don't use atomics when they could. On systems where the Root Port is not visible but *does* support AtomicOps, this is a regression: prior to 1ae8c4ce1570, it enabled AtomicOps in the endpoint and returned success.
--- drivers/pci/pci.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-)
drivers/pci/pci.c +14 -1
@@ -3736,8 +3736,21 @@ int pci_enable_atomic_ops_to_root(struct pci_dev *dev, u32 cap_mask) } root =pcie_find_root_port (dev); -if (!root) +if (!root) { + +/* +* A hypervisor may expose a topology with the root port +* not visible to the guest. If the hypervisor has already +* set AtomicOp Requester Enable in the endpoint, we assume +* it has verified support in the root port, so it is safe +* for the driver to use AtomicOps. +*/ +pcie_capability_read_dword (dev,PCI_EXP_DEVCTL2 , &ctl2); +if (ctl2 &PCI_EXP_DEVCTL2_ATOMIC_REQ ) +return 0 ; +return -EINVAL ; + }pcie_capability_read_dword (root,PCI_EXP_DEVCAP2 , &cap);if ((cap & cap_mask) != cap_mask)
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 168/428 drm/amd/display: guard dc_sink dereferences in MST mode validation
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Hari Mishal <[email protected]>
commit 217f64f348c90cba62164e7ff38e0ccf78a5ca09 upstream.
dm_dp_mst_is_port_support_mode() reads aconnector->dc_sink->dsc_caps... for the DSC branch-throughput check, and get_conv_frl_bw()'s HDMI-PCON FRL-bandwidth path reads aconnector->dc_sink->edid_caps.max_frl_rate, both without a NULL check. dc_sink is cleared asynchronously on MST unplug, and both functions run from paths that the driver's own comments document as racing that teardown: the connector probe worker's ->mode_valid callback and a compositor's atomic check, neither of which holds the MST manager lock that the teardown path uses. The former does have an existing dsc_aux NULL check, but dsc_aux isn't reliably cleared in every path that clears dc_sink, so it doesn't cover this.
Fail the port-support check and skip the FRL conversion path when the sink is already gone.
--- drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-)
drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c +5 -1
@@ -1181,7 +1181,8 @@ static bool get_conv_frl_bw(struct amdgpunsigned int max_sink_bw_in_kbps =0 ;unsigned int dsc_max_sink_bw_in_kbps =0 ; -if (aconnector->dc_link->dc->caps.dp_hdmi21_pcon_support && +if (aconnector->dc_sink && + aconnector->dc_link->dc->caps.dp_hdmi21_pcon_support && aconnector->mst_downstream_port_caps.bytes.byte0.bits.DWN_STRM_PORTX_TYPE ==DOWN_STREAM_DETAILED_HDMI ) { max_conv_bw_in_kbps = dc_link_bw_kbps_from_raw_frl_link_rate_data( aconnector->dc_link->dc,
@@ -1946,6 +1947,9 @@ enum dc_status dm_dp_mst_is_port_supportstruct dc_dsc_config_options dsc_options = {0 };uint32_t stream_kbps; +if (!aconnector->dc_sink) +return DC_FAIL_BANDWIDTH_VALIDATE ; +/* DSC unnecessary case * Check if timing could be supported within end-to-end BW */
Greg Kroah-Hartman
maintainerPATCH 169/428 drm/amd/display: Preserve eDP mode on initial ASSR failure
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Arthur Heymans <[email protected]>
commit 5e08102d1d336593160d1434261d50b0c0f6cbe6 upstream.
Commit 56d8ce9d8c17 ("drm/amd/display: Apply correct panel mode when reinitializing hardware") made ASSR failure fall back to the default panel mode unless eDP mode had previously been applied.
However, dc_link is zero-initialized and DP_PANEL_MODE_DEFAULT is zero. Before the first call to dp_set_panel_mode(), panel_mode therefore looks like a previously applied default mode. If ASSR setup fails during the first link training attempt, the driver incorrectly trains the eDP link using the default scrambling mode.
On a Google Vilboz Chromebook running self-built coreboot firmware and PSP verstage, this leaves the panel mostly black with corrupted output along the top edge.
Track whether panel_mode has actually been initialized, and only consult the saved mode after it has been applied. This retains the recovery behavior while preserving eDP mode during initial link training.
--- drivers/gpu/drm/amd/display/dc/dc.h | 1 + drivers/gpu/drm/amd/display/dc/link/protocols/link_edp_panel_control.c | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-)
drivers/gpu/drm/amd/display/dc/dc.h +1 -0
@@ -1763,6 +1763,7 @@ struct dc_scratch_space {struct ddc_service *ddc;enum dp_panel_mode panel_mode; +bool panel_mode_initialized;bool aux_mode;/* Private to DC core */
drivers/gpu/drm/amd/display/dc/link/protocols/link_edp_panel_control.c +3 -1
@@ -92,6 +92,7 @@ void dp_set_panel_mode(struct dc_link *l } link->panel_mode = panel_mode; + link->panel_mode_initialized = true; DC_LOG_DETECTION_DP_CAPS("%d eDP panel mode supported: %d, enabled: %d\n", link->link_index, link->dpcd_caps.panel_mode_edp,
@@ -1300,7 +1301,8 @@ void edp_set_panel_assr(struct dc_link * result = cp_psp->funcs.enable_assr (cp_psp->handle, link); -if (!result && link->panel_mode !=DP_PANEL_MODE_EDP ) +if (!result && link->panel_mode_initialized && + link->panel_mode !=DP_PANEL_MODE_EDP ) *panel_mode =DP_PANEL_MODE_DEFAULT ; } }
Greg Kroah-Hartman
maintainerPATCH 170/428 drm/amd/display: check dc_state_create_copy() for NULL in dm_suspend
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jiangshan Yi <[email protected]>
commit 015e72cde567d2450e282c2790773463156b2d0a upstream.
dc_state_create_copy() can return NULL on allocation failure. dm_suspend() only conditionally skips dm_gpureset_toggle_interrupts() and continues execution, returning success. dm_resume() then dereferences the NULL cached_dc_state in link_enc_cfg_copy() and the following dc_state->stream_count loop, crashing during GPU reset recovery.
Return -ENOMEM immediately if the copy fails, so the caller aborts suspend instead of leaving a NULL cached state for resume.
--- drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-)
drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +7 -2
@@ -3516,9 +3516,14 @@ static int dm_suspend(struct amdgpu_ip_bdc_allow_idle_optimizations (adev->dm.dc, false); dm->cached_dc_state =dc_state_create_copy (dm->dc->current_state); +if (!dm->cached_dc_state) { +drm_err (adev_to_drm (adev), +"Failed to allocate cached DC state during suspend\n" ); +mutex_unlock (&dm->dc_lock); +return -ENOMEM ; + } -if (dm->cached_dc_state) -dm_gpureset_toggle_interrupts (adev, dm->cached_dc_state, false); +dm_gpureset_toggle_interrupts (adev, dm->cached_dc_state, false); res =amdgpu_dm_commit_zero_streams (dm->dc);if (res !=DC_OK ) {
Greg Kroah-Hartman
maintainerPATCH 171/428 drm/amd/display: Fix fast updates on DCE 6.x
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Timur Kristóf <[email protected]>
commit 88427119e1f039c8cdffd63726c136a986ef519f upstream.
DCE6 should use legacy fast updates, just like all DCE8 - DCN2. Set that flag for DCE 6.0, 6.1 and 6.4 (all DCE6 versions).
Technically DCE6 should have been included when the fast update code was refactored, but it just happened to work until now.
A recent commit "Attach only plane updates that actually changed" has exposed this issue because with that commit, DC accidentally took the new fast update code path for DCE6 as well, which caused it to boot into a black screen with a flip done timeout.
This fix should be backported to all kernel versions that have "Refactor fast update to use new HWSS build sequence", but this patch won't apply cleanly to older kernels due to another refactor in "Remove dc param from check_update".
--- drivers/gpu/drm/amd/display/dc/resource/dce60/dce60_resource.c | 12 ++++++++++ 1 file changed, 12 insertions(+)
drivers/gpu/drm/amd/display/dc/resource/dce60/dce60_resource.c +12 -0
@@ -448,6 +448,12 @@ static const struct dce_abm_mask abm_mas ABM_MASK_SH_LIST_DCE110(_MASK) }; +static const struct dc_debug_options debug_defaults = {0 }; + +static const struct dc_check_config config_defaults = { + .enable_legacy_fast_update = true, +}; +#define CTX ctx#define REG (reg) mm ## reg
@@ -937,6 +943,8 @@ static bool dce60_construct( dc->caps.max_cursor_size =64 ; dc->caps.dual_link_dvi = true; dc->caps.extended_aux_timeout_support = false; + dc->debug = debug_defaults; + dc->check_config = config_defaults;/************************************************* * Create resources *
@@ -1134,6 +1142,8 @@ static bool dce61_construct( dc->caps.i2c_speed_in_khz =40 ; dc->caps.max_cursor_size =64 ; dc->caps.is_apu = true; + dc->debug = debug_defaults; + dc->check_config = config_defaults;/************************************************* * Create resources *
@@ -1332,6 +1342,8 @@ static bool dce64_construct( dc->caps.i2c_speed_in_khz =40 ; dc->caps.max_cursor_size =64 ; dc->caps.is_apu = true; + dc->debug = debug_defaults; + dc->check_config = config_defaults;/************************************************* * Create resources *
Greg Kroah-Hartman
maintainerPATCH 172/428 drm/amd/display: Fix fast updates on DCE 8.1
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Timur Kristóf <[email protected]>
commit 687d31ea9ac76b299817060fa1e5582d6c752ae0 upstream.
DCE8 should use legacy fast updates.
This was already set for DCE 8.0 and 8.3, but it seems that DCE 8.1 (found in Kaveri chips) was forgotten.
This fix should be backported to all kernel versions that have "Refactor fast update to use new HWSS build sequence", but this patch won't apply cleanly to older kernels due to another refactor in "Remove dc param from check_update".
--- drivers/gpu/drm/amd/display/dc/resource/dce80/dce80_resource.c | 2 ++ 1 file changed, 2 insertions(+)
drivers/gpu/drm/amd/display/dc/resource/dce80/dce80_resource.c +2 -0
@@ -1148,6 +1148,8 @@ static bool dce81_construct( dc->caps.max_cursor_size =128 ; dc->caps.min_horizontal_blanking_period =80 ; dc->caps.is_apu = true; + dc->debug = debug_defaults; + dc->check_config = config_defaults;/************************************************* * Create resources *
Greg Kroah-Hartman
maintainerPATCH 173/428 drm/amd/display: Fix stale replay_events after mod_power stream removal
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Simon Polack <[email protected]>
commit b76a5b7bee22556914f7cde210f695ef162d81bf upstream.
[Why]mod_power_remove_stream() shifts the remaining power_entity slots down but does not move replay_events, and mod_power_add_stream() does not initialize it. replay_events therefore stay bound to the map slot instead of the stream.
When several streams are disabled in one atomic commit, amdgpu_dm_mod_power_update_streams() removes them one after another. The eDP stream can then be looked up in a slot whose stale replay_events already have replay_event_hw_programming set, so amdgpu_dm_replay_set_event() returns early ("already in desired state") without calling mod_power_set_replay_event(). Replay is not disabled before the eDP panel is powered off. After DPMS on, the sink reports neither replay state nor frame lock (DPCD 0x378 = 0x00, no error bits), so the HPD IRQ recovery does not trigger and the panel stays black until a full modeset.
Seen with an eDP panel using FreeSync Replay plus two DP-MST displays:
DPMS off/on of all outputs leaves eDP black, while DPMS of eDP alone works. Doing an eDP-only DPMS first makes the next all-output DPMS fail reliably.
[How]
Shift replay_events together with the PSR cached fields in mod_power_remove_stream() and initialize it to replay_event_vsync in mod_power_add_stream(), matching the psr_event_vsync initial value used for PSR (both vsync events are driven together by amdgpu_dm_crtc_set_static_screen_optimze()).
Tested on 7.3.0-rc3 (238650ef6c7c): the reproducer above now recovers reliably, and Replay still engages when the screen is idle.
The issue was debugged with help from an AI assistant (Claude), which analysed ftrace/kprobe traces and the driver source, pointed to the missing replay_events handling and suggested this change. I collected the traces and built and tested the fix on the affected hardware.
--- drivers/gpu/drm/amd/display/modules/power/power.c | 2 ++ 1 file changed, 2 insertions(+)
drivers/gpu/drm/amd/display/modules/power/power.c +2 -0
@@ -333,6 +333,7 @@ bool mod_power_add_stream(struct mod_pow core_power->map[core_power->num_entities].psr_enabled =0 ; core_power->map[core_power->num_entities].psr_events = psr_event_vsync; core_power->map[core_power->num_entities].psr_power_opt =0 ; + core_power->map[core_power->num_entities].replay_events = replay_event_vsync; core_power->num_entities++;return true; }
@@ -392,6 +393,7 @@ bool mod_power_remove_stream(struct mod_ core_power->map[i].psr_enabled = core_power->map[i +1 ].psr_enabled; core_power->map[i].psr_events = core_power->map[i +1 ].psr_events; core_power->map[i].psr_power_opt = core_power->map[i +1 ].psr_power_opt; + core_power->map[i].replay_events = core_power->map[i +1 ].replay_events;memcpy (core_power->map[i].psr_context, core_power->map[i +1 ].psr_context,sizeof (struct mod_power_psr_context ));memset (core_power->map[i +1 ].psr_context,0 ,sizeof (struct mod_power_psr_context ));
Greg Kroah-Hartman
maintainerPATCH 174/428 drm/amd/display: Mark DCE 6.4 as not APU
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Timur Kristóf <[email protected]>
commit 3a52d587d1fc8e8bf94ba1b07374f7ec91aaffb4 upstream.
DCE 6.4 is found in Oland chips, which were advertised as low-end gaming GPUs in 2013~2015 and then have been sold as low-end workstation GPUs until 2017~2019.
These are not APUs.
--- drivers/gpu/drm/amd/display/dc/resource/dce60/dce60_resource.c | 1 - 1 file changed, 1 deletion(-)
drivers/gpu/drm/amd/display/dc/resource/dce60/dce60_resource.c +0 -1
@@ -1341,7 +1341,6 @@ static bool dce64_construct( dc->caps.max_downscale_ratio =200 ; dc->caps.i2c_speed_in_khz =40 ; dc->caps.max_cursor_size =64 ; - dc->caps.is_apu = true; dc->debug = debug_defaults; dc->check_config = config_defaults;
Greg Kroah-Hartman
maintainerPATCH 175/428 drm/amd/pm/si: Check AMD_IS_MOBILITY flag for PPSMC_SYSTEMFLAG_GPIO_DC
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Timur Kristóf <[email protected]>
commit cd883d0a3937df82a5ea03709ba8ba623b868419 upstream.
Unfortunately, some desktop boards eg. the FirePro D500 have the HARDWAREDC platform flag set and also have a battery power state configured in their VBIOS. This makes no sense for a desktop GPU and results in incorrect behaviour: the clocks are stuck at lowest.
We observed that the kernel driver can work around the issue in two possible ways:
1. Set PPSMC_SWSTATE_FLAG_DC on all power states
2. Clear PPSMC_SYSTEMFLAG_GPIO_DC
We think that the PPSMC_SYSTEMFLAG_GPIO_DC flag makes the SMC assume it's running on battery even though this is a desktop machine with no battery, and that's why it doesn't do DPM on power states without PPSMC_SYSTEMFLAG_GPIO_DC.
Issue was uncovered by "Fix updating clock limits from power states" because previously the limits for the battery state were not tracked separately. However, battery power state has lower frequencies and voltages, so the kernel doesn't set the DC flag on the current power state anymore. That causes the SMC to be stuck on the lowest clocks.
Let's clear PPSMC_SYSTEMFLAG_GPIO_DC on desktop GPUs. We can use the AMD_IS_MOBILITY flag to determine that.
--- drivers/gpu/drm/amd/pm/legacy-dpm/si_dpm.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
drivers/gpu/drm/amd/pm/legacy-dpm/si_dpm.c +2 -1
@@ -5263,7 +5263,8 @@ static int si_init_smc_table(struct amdgbreak ; } -if (adev->pm.dpm.platform_caps &ATOM_PP_PLATFORM_CAP_HARDWAREDC ) +if ((adev->flags &AMD_IS_MOBILITY ) && + (adev->pm.dpm.platform_caps &ATOM_PP_PLATFORM_CAP_HARDWAREDC )) table->systemFlags |=PPSMC_SYSTEMFLAG_GPIO_DC ;if (adev->pm.dpm.platform_caps &ATOM_PP_PLATFORM_CAP_REGULATOR_HOT ) {
Greg Kroah-Hartman
maintainerPATCH 176/428 drm/amd/pm/si: Fix updating clock limits on AC/DC
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Timur Kristóf <[email protected]>
commit 08c9100f65299c64439e22e6057d750ae0e6c660 upstream.
Assume that the AC limits are the maximum of all power states, and the DC limits are the maximum of battery power states. This shouldn't make any difference in practice, but is cleaner and more robust against bogus information in the VBIOS.
--- drivers/gpu/drm/amd/pm/legacy-dpm/si_dpm.c | 36 ++++++++++++++--------------- 1 file changed, 18 insertions(+), 18 deletions(-)
drivers/gpu/drm/amd/pm/legacy-dpm/si_dpm.c +18 -18
@@ -7238,6 +7238,19 @@ static void si_parse_pplib_non_clock_inf adev->pm.dpm.uvd_ps = rps; } +static void si_update_limits (struct amdgpu_clock_and_voltage_limits *limits, +struct rv7xx_pl *pl) +{ +if (pl->sclk > limits->sclk) + limits->sclk = pl->sclk; +if (pl->mclk > limits->mclk) + limits->mclk = pl->mclk; +if (pl->vddc > limits->vddc) + limits->vddc = pl->vddc; +if (pl->vddci > limits->vddci) + limits->vddci = pl->vddci; +} +static void si_parse_pplib_clock_info (struct amdgpu_device *adev,struct amdgpu_ps *rps,int index,union pplib_clock_info *clock_info)
@@ -7246,7 +7259,6 @@ static void si_parse_pplib_clock_info(ststruct evergreen_power_info *eg_pi =evergreen_get_pi (adev);struct si_power_info *si_pi =si_get_pi (adev);struct si_ps *ps =si_get_ps (rps); -struct amdgpu_clock_and_voltage_limits *limits;u16 leakage_voltage;struct rv7xx_pl *pl = &ps->performance_levels[index];int ret;
@@ -7311,26 +7323,14 @@ static void si_parse_pplib_clock_info(st* VBIOS can contain conflicting values between: * - the maximum allowed clocks and voltages on AC or DC * - the clocks and voltages in power states on AC or DC +* +* Assume that the AC limits are the maximum of all power states, +* and the DC limits are the maximum of battery power states. */ +si_update_limits (&adev->pm.dpm.dyn_state.max_clock_voltage_on_ac, pl);if ((rps->class &ATOM_PPLIB_CLASSIFICATION_UI_MASK ) == -ATOM_PPLIB_CLASSIFICATION_UI_PERFORMANCE ) - limits = &adev->pm.dpm.dyn_state.max_clock_voltage_on_ac; -else if ((rps->class &ATOM_PPLIB_CLASSIFICATION_UI_MASK ) ==ATOM_PPLIB_CLASSIFICATION_UI_BATTERY ) - limits = &adev->pm.dpm.dyn_state.max_clock_voltage_on_dc; -else - limits =NULL ; - -if (limits) { -if (pl->sclk > limits->sclk) - limits->sclk = pl->sclk; -if (pl->mclk > limits->mclk) - limits->mclk = pl->mclk; -if (pl->vddc > limits->vddc) - limits->vddc = pl->vddc; -if (pl->vddci > limits->vddci) - limits->vddci = pl->vddci; - } +si_update_limits (&adev->pm.dpm.dyn_state.max_clock_voltage_on_dc, pl); }union pplib_power_state {
Greg Kroah-Hartman
maintainerPATCH 177/428 drm/amdgpu/gfx6: fix firmware leak on teardown
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Willian Oliveira <[email protected]>
commit cc172e177795c5f87bc2d71768d077e1c10633e8 upstream.
The firmware requested in gfx_v6_0_init_microcode() is not released when the GFX block is torn down, leaking the firmware resources.
Add gfx_v6_0_free_microcode() and call it from gfx_v6_0_sw_fini() to release the PFP, ME, CE and RLC firmware.
--- drivers/gpu/drm/amd/amdgpu/gfx_v6_0.c | 9 +++++++++ 1 file changed, 9 insertions(+)
drivers/gpu/drm/amd/amdgpu/gfx_v6_0.c +9 -0
@@ -322,6 +322,14 @@ static const u32 verde_rlc_save_restore_ 0x00000000 }; +static void gfx_v6_0_free_microcode (struct amdgpu_device *adev) +{ +amdgpu_ucode_release (&adev->gfx.pfp_fw); +amdgpu_ucode_release (&adev->gfx.me_fw); +amdgpu_ucode_release (&adev->gfx.ce_fw); +amdgpu_ucode_release (&adev->gfx.rlc_fw); +} +static int gfx_v6_0_init_microcode (struct amdgpu_device *adev) {const char *chip_name;
@@ -3187,6 +3195,7 @@ static int gfx_v6_0_sw_fini(struct amdgpamdgpu_ring_fini (&adev->gfx.compute_ring[i]);amdgpu_gfx_rlc_fini (adev); +gfx_v6_0_free_microcode (adev);return 0 ; }
Greg Kroah-Hartman
maintainerPATCH 178/428 drm/amdgpu/gmc12.1: properly pass flush_type to gmc_v12_1_flush_vm_hub()
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Alex Deucher <[email protected]>
commit e3e7fdece7069d09ddd818564853174865d4796a upstream.
In gmc_v12_1_flush_gpu_tlb(), flush_type was not properly passed to gmc_v12_1_flush_vm_hub(). This only affected the MMIO path. In most cases the flush would go through MES.
--- drivers/gpu/drm/amd/amdgpu/gmc_v12_1.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/gpu/drm/amd/amdgpu/gmc_v12_1.c +1 -1
@@ -389,7 +389,7 @@ static void gmc_v12_1_flush_gpu_tlb(strureturn ; } -gmc_v12_1_flush_vm_hub (adev, vmid, vmhub,0 ); +gmc_v12_1_flush_vm_hub (adev, vmid, vmhub, flush_type);return ; }
Greg Kroah-Hartman
maintainerPATCH 179/428 drm/amdgpu/gmc12: properly pass flush_type to gmc_v12_0_flush_vm_hub()
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Alex Deucher <[email protected]>
commit 0bfb1bfc82e8fa386d4fad1caa5812b6afafa626 upstream.
In gmc_v12_0_flush_gpu_tlb(), flush_type was not properly passed to gmc_v12_0_flush_vm_hub(). This only affected the MMIO path. In most cases the flush would go through MES.
--- drivers/gpu/drm/amd/amdgpu/gmc_v12_0.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/gpu/drm/amd/amdgpu/gmc_v12_0.c +1 -1
@@ -327,7 +327,7 @@ static void gmc_v12_0_flush_gpu_tlb(strureturn ; } -gmc_v12_0_flush_vm_hub (adev, vmid, vmhub,0 ); +gmc_v12_0_flush_vm_hub (adev, vmid, vmhub, flush_type);return ; }
Greg Kroah-Hartman
maintainerPATCH 180/428 drm/i915/vrr: Disable DC balance by default
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Mitul Golani <[email protected]>
commit c034e8a46e4cb703018fe7e10fe5a3a974d6a7c3 upstream.
Disable VRR DC balance by default due to timing issues observed on some panel/TCON combinations.
Keep the module parameter to enable DC balance during debugging and to isolate DC balance effects from underlying VRR/display timing issues.
--v2:
- Make enable_dc_balance a bool and keep it disabled by default; fix the parameter type/value mismatch and correct the description (Chaitanya Kumar Borah, Jani Nikula)
- Explain in the commit message why the feature is gated and why a module parameter is used (Jani Nikula)
--v3:
- Commit message update (Jani Nikula)
--- drivers/gpu/drm/i915/display/intel_display_params.c | 4 ++++ drivers/gpu/drm/i915/display/intel_display_params.h | 1 + drivers/gpu/drm/i915/display/intel_vrr.c | 4 +++- 3 files changed, 8 insertions(+), 1 deletion(-)
drivers/gpu/drm/i915/display/intel_display_params.c +4 -0
@@ -120,6 +120,10 @@ intel_display_param_named_unsafe(enable_"(0=disabled, 1=enable up to PSR1, 2=enable up to PSR2) " "Default: -1 (use per-chip default)"); +intel_display_param_named_unsafe (enable_dc_balance, bool,0400 , +"Enable VRR DC balance (0=disabled, 1=enabled). " +"Default: 0 (disabled)" ); +intel_display_param_named_unsafe (enable_panel_replay, int,0400 ,"Enable Panel Replay (0=disabled, 1=enabled). Default: -1 (use per-chip default)" );
drivers/gpu/drm/i915/display/intel_display_params.h +1 -0
@@ -46,6 +46,7 @@ struct drm_printer; param(bool, enable_dp_mst, true, 0600) \ param(int, enable_fbc, -1, 0600) \ param(int, enable_psr, -1, 0600) \ + param(bool, enable_dc_balance, false, 0600) \ param(int, enable_panel_replay, -1, 0600) \ param(bool, psr_safest_params, false, 0400) \param (bool, enable_psr2_sel_fetch, true,0400 ) \
drivers/gpu/drm/i915/display/intel_vrr.c +3 -1
@@ -383,10 +383,12 @@ static bool intel_vrr_dc_balance_possiblstatic void intel_vrr_dc_balance_compute_config (struct intel_crtc_state *crtc_state) { +struct intel_display *display =to_intel_display (crtc_state);int guardband_usec, adjustment_usec;struct drm_display_mode *adjusted_mode = &crtc_state->hw.adjusted_mode; -if (!intel_vrr_dc_balance_possible (crtc_state) || !crtc_state->vrr.enable) +if (!intel_vrr_dc_balance_possible (crtc_state) || + !crtc_state->vrr.enable || !display->params.enable_dc_balance)return ; crtc_state->vrr.dc_balance.vmax = crtc_state->vrr.vmax;
Greg Kroah-Hartman
maintainerPATCH 181/428 drm/radeon: Read the VRAM VBIOS signature with readb()
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Imre Kaloz <[email protected]>
commit de1eab13efef83fd2b76f4688de5bf672644596f upstream.
igp_read_bios_from_vram() checked bios[0]/bios[1] with a plain __iomem load, which faults on sparc64 before the copy runs at all. radeon_read_bios() already reads its two signature bytes with readb() ahead of its own copy; use the same accessor here, keeping the check before the allocation.
--- drivers/gpu/drm/radeon/radeon_bios.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-)
drivers/gpu/drm/radeon/radeon_bios.c +5 -1
@@ -49,6 +49,7 @@static bool igp_read_bios_from_vram (struct radeon_device *rdev) {uint8_t __iomem *bios; +uint8_t val1, val2;resource_size_t vram_base;resource_size_t size =256 *1024 ;/* ??? */
@@ -63,7 +64,10 @@ static bool igp_read_bios_from_vram(strureturn false; } -if (size ==0 || bios[0 ] !=0x55 || bios[1 ] !=0xaa ) { + val1 =readb (&bios[0 ]); + val2 =readb (&bios[1 ]); + +if (size ==0 || val1 !=0x55 || val2 !=0xaa ) {iounmap (bios);return false; }
Greg Kroah-Hartman
maintainerPATCH 182/428 drm/mediatek: Add missing IS_ERR check for ovl_adaptor platform device
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Haojie Li <[email protected]>
commit bce7698e2e6565bb765bd0522337f29f5f312624 upstream.
platform_device_register_data() can fail and return an ERR_PTR, but the return value is used without checking, leading to an invalid pointer being stored in ddp_comp[].dev and passed to component_match_add() and mtk_ddp_comp_init(), which could result in a kernel crash.
Add an IS_ERR() check to jump to the error handling path on failure.
--- drivers/gpu/drm/mediatek/mtk_drm_drv.c | 3 +++ 1 file changed, 3 insertions(+)
drivers/gpu/drm/mediatek/mtk_drm_drv.c +3 -0
@@ -1112,6 +1112,9 @@ static int mtk_drm_probe(struct platformPLATFORM_DEVID_AUTO , (void *)private->mmsys_dev, sizeof(*private->mmsys_dev)); +if (IS_ERR (ovl_adaptor)) +return PTR_ERR (ovl_adaptor); + private->ddp_comp[DDP_COMPONENT_DRM_OVL_ADAPTOR ].dev = &ovl_adaptor->dev;mtk_ddp_comp_init (dev,NULL , &private->ddp_comp[DDP_COMPONENT_DRM_OVL_ADAPTOR ],DDP_COMPONENT_DRM_OVL_ADAPTOR );
Greg Kroah-Hartman
maintainerPATCH 183/428 drm/mediatek: Fix ovl adaptor platform device leak
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Guangshuo Li <[email protected]>
commit 048739be3e9cacae5301b68245668002fe6f0ce5 upstream.
mtk_drm_probe() creates an OVL adaptor platform device with platform_device_register_data() when the display pipeline requires the OVL adaptor.
If a later initialization step fails, the probe error path releases the DRM resources without unregistering the already registered OVL adaptor device. The normal remove path likewise leaves the device registered after the DRM driver is unbound.
Keep track of whether the OVL adaptor was successfully registered and unregister it on probe failure. Also recover the platform device from the stored DDP component device and unregister it during normal removal.
The issue was identified by a static analysis tool I developed and confirmed by manual review.
--- drivers/gpu/drm/mediatek/mtk_drm_drv.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-)
drivers/gpu/drm/mediatek/mtk_drm_drv.c +9 -1
@@ -1060,7 +1060,7 @@ static int mtk_drm_probe(struct platformstruct mtk_mmsys_driver_data *mtk_drm_data;struct device_node *node;struct component_match *match =NULL ; -struct platform_device *ovl_adaptor; +struct platform_device *ovl_adaptor =NULL ;int ret;int i;
@@ -1208,6 +1208,8 @@ static int mtk_drm_probe(struct platform err_pm:pm_runtime_disable (dev); err_node: +if (ovl_adaptor) +platform_device_unregister (ovl_adaptor);of_node_put (private->mutex_node);for (i =0 ; i <DDP_COMPONENT_DRM_ID_MAX ; i++)of_node_put (private->comp_node[i]);
@@ -1217,9 +1219,15 @@ err_node:static void mtk_drm_remove (struct platform_device *pdev) {struct mtk_drm_private *private =platform_get_drvdata (pdev); +struct device *ovl_adaptor_dev;int i; + ovl_adaptor_dev = + private->ddp_comp[DDP_COMPONENT_DRM_OVL_ADAPTOR ].dev; +component_master_del (&pdev->dev, &mtk_drm_ops); +if (ovl_adaptor_dev) +platform_device_unregister (to_platform_device (ovl_adaptor_dev));pm_runtime_disable (&pdev->dev);of_node_put (private->mutex_node);for (i =0 ; i <DDP_COMPONENT_DRM_ID_MAX ; i++)
Greg Kroah-Hartman
maintainerPATCH 184/428 drm/mediatek: Fix pdev reference leak in mtk_drm_bind()
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Wentao Liang <[email protected]>
commit 3753e34b73fcbe2a2edbe984614a008a261697ef upstream.
When the device is not the mmsys master, mtk_drm_bind() returns early after taking a reference on the disp-mutex device via of_find_device_by_node(), without ever dropping it: mtk_drm_unbind() only puts mutex_dev for the master. Drop the reference before returning from the non-master path.
--- drivers/gpu/drm/mediatek/mtk_drm_drv.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
drivers/gpu/drm/mediatek/mtk_drm_drv.c +3 -1
@@ -638,8 +638,10 @@ static int mtk_drm_bind(struct device *d private->mtk_drm_bound = true; private->dev = dev; -if (!mtk_drm_get_all_drm_priv (dev)) +if (!mtk_drm_get_all_drm_priv (dev)) { +put_device (private->mutex_dev);return 0 ; + } drm =drm_dev_alloc (&mtk_drm_driver, dev);if (IS_ERR (drm)) {
Greg Kroah-Hartman
maintainerPATCH 185/428 drm/amdgpu: fix IP instance memory leak on kobject add failure
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Guangshuo Li <[email protected]>
commit f5a5add3605935163dd0f75749b6a7869e9765af upstream.
amdgpu_discovery_sysfs_ips() allocates ip_hw_instance with kzalloc_flex() and initializes its embedded kobject before calling kobject_add().
If kobject_add() fails, the return value is ignored and execution continues without dropping the initial kobject reference. The failed kobject is not retained in the kset list, so the normal sysfs teardown path cannot find it. As a result, ip_hw_instance_release() is never called and the ip_hw_instance allocation is leaked.
Call kobject_put() when kobject_add() fails so the initial reference is dropped and ip_hw_instance_release() can free the allocation. Keep the existing best-effort sysfs behavior by continuing with the remaining IP entries after the failed registration.
The issue was identified by a static analysis tool I developed and confirmed by manual review.
--- drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c | 2 ++ 1 file changed, 2 insertions(+)
drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c +2 -0
@@ -1270,6 +1270,8 @@ static int amdgpu_discovery_sysfs_ips(st ip_hw_instance->kobj.kset = &ip_hw_id->hw_id_kset; res =kobject_add (&ip_hw_instance->kobj,NULL ,"%d" , ip_hw_instance->num_instance); +if (res) +kobject_put (&ip_hw_instance->kobj); next_ip:if (reg_base_64) ip_offset += struct_size(ip, base_address_64,
Greg Kroah-Hartman
maintainerPATCH 186/428 drm/amdgpu: implement workaround for sdma dcc corruption
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Pierre-Eric Pelloux-Prayer <[email protected]>
commit eaeac1498b289b342449b678a567a9012360f91a upstream.
For unknown reasons, on gfx12 using multiple entities can causes random corruption of BOs with DCC.
This workaround seems to prevent the issue until the root cause is understood and fixed.
--- drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-)
drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c +7 -1
@@ -2320,7 +2320,7 @@ void amdgpu_ttm_fini(struct amdgpu_devicvoid amdgpu_ttm_enable_buffer_funcs (struct amdgpu_device *adev) {struct ttm_resource_manager *man =ttm_manager_type (&adev->mman.bdev,TTM_PL_VRAM ); -u32 num_clear_entities, num_move_entities; +u32 num_clear_entities, num_move_entities, sdma_ip_version;int r, i, j; if (!adev->mman.initialized || amdgpu_in_reset(adev) ||
@@ -2345,6 +2345,12 @@ void amdgpu_ttm_enable_buffer_funcs(stru num_clear_entities =MIN (adev->mman.num_buffer_funcs_scheds,TTM_NUM_MOVE_FENCES ); num_move_entities =MIN (adev->mman.num_buffer_funcs_scheds,TTM_NUM_MOVE_FENCES ); +/* TODO: workaround for DCC corruption when moving BOs from multiple queues at +* the same time: use a single queue until the root cause is identified and fixed. +*/ + sdma_ip_version =amdgpu_ip_version (adev,SDMA0_HWIP ,0 ); +if (sdma_ip_version ==IP_VERSION (7 ,0 ,0 ) || sdma_ip_version ==IP_VERSION (7 ,0 ,1 )) + num_move_entities =1 ; adev->mman.clear_entities = kcalloc(num_clear_entities, sizeof(struct amdgpu_ttm_buffer_entity),
Greg Kroah-Hartman
maintainerPATCH 187/428 drm/amdgpu: drop userq callback assignment for sdma 7.1
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Alex Deucher <[email protected]>
commit 1d1f1acecddf742eb1f305122209bc2034fd90b1 upstream.
Not enabled in 7.1 so drop the assignment.
--- drivers/gpu/drm/amd/amdgpu/sdma_v7_1.c | 4 ---- 1 file changed, 4 deletions(-)
drivers/gpu/drm/amd/amdgpu/sdma_v7_1.c +0 -4
@@ -1356,10 +1356,6 @@ static int sdma_v7_1_sw_init(struct amdgelse DRM_ERROR ("Failed to allocated memory for SDMA IP Dump\n" ); -#ifdef CONFIG_DRM_AMDGPU_NAVI3X_USERQ - adev->userq_funcs[AMDGPU_HW_IP_DMA ] = &userq_mes_funcs; -#endif -return r; }
Greg Kroah-Hartman
maintainerPATCH 188/428 drm/amdgpu: fix ACP MFD device leak on init failure
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Guangshuo Li <[email protected]>
commit f0013046151b7467e62c471c1c83d63ea255799c upstream.
acp_hw_init() registers ACP child devices with mfd_add_devices() before attaching them to the ACP power domain and initializing the hardware.
If attaching a child to the power domain fails, or if the ACP reset or clock enable operation times out, the failure path frees only the source cell, resource, and platform-data allocations. The child platform devices already registered by mfd_add_devices() remain registered and are never released.
Remove the children from the power domain and unregister the MFD devices on failures that occur after mfd_add_devices() succeeds. Keep mfd_add_devices() failures on the existing cleanup path since the MFD core already rolls back partially registered children itself.
The issue was identified by a static analysis tool I developed and confirmed by manual review.
--- drivers/gpu/drm/amd/amdgpu/amdgpu_acp.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-)
drivers/gpu/drm/amd/amdgpu/amdgpu_acp.c +9 -4
@@ -318,7 +318,7 @@ static int acp_hw_init(struct amdgpu_ip_ r =device_for_each_child (adev->acp.parent, &adev->acp.acp_genpd->gpd, acp_genpd_add_device);if (r) - goto failure; + goto failure_remove_mfd;break ; }default :
@@ -443,7 +443,7 @@ static int acp_hw_init(struct amdgpu_ip_ r =device_for_each_child (adev->acp.parent, &adev->acp.acp_genpd->gpd, acp_genpd_add_device);if (r) - goto failure; + goto failure_remove_mfd; }/* Assert Soft reset of ACP */
@@ -461,7 +461,7 @@ static int acp_hw_init(struct amdgpu_ip_if (--count ==0 ) {dev_err (&adev->pdev->dev,"Failed to reset ACP\n" ); r = -ETIMEDOUT ; - goto failure; + goto failure_remove_mfd; }udelay (100 ); }
@@ -479,7 +479,7 @@ static int acp_hw_init(struct amdgpu_ip_if (--count ==0 ) {dev_err (&adev->pdev->dev,"Failed to reset ACP\n" ); r = -ETIMEDOUT ; - goto failure; + goto failure_remove_mfd; }udelay (100 ); }
@@ -489,6 +489,11 @@ static int acp_hw_init(struct amdgpu_ip_cgs_write_register (adev->acp.cgs_device, mmACP_SOFT_RESET, val);return 0 ; +failure_remove_mfd: +device_for_each_child (adev->acp.parent,NULL , + acp_genpd_remove_device); +mfd_remove_devices (adev->acp.parent); + failure:kfree (adev->acp.i2s_pdata);kfree (adev->acp.acp_res);
Greg Kroah-Hartman
maintainerPATCH 189/428 drm/amdgpu: skip the noirq suspend reset for a switcheroo-parked GPU
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Theo Andersen Carton <[email protected]>
commit eadb85cca111152bd334b861fb53747ab851d13a upstream.
amdgpu_pmops_suspend_noirq() resets the ASIC unconditionally. When the GPU has been parked by vga_switcheroo it has neither power nor a PCIe link, so the reset cannot reach it: pci_set_power_state() reports the device as inaccessible and amdgpu_asic_reset() returns -EINVAL. A failure there aborts the entire noirq suspend phase, and with it the system suspend, so the machine cannot sleep at all while the GPU is switched off.
amdgpu_device_prepare(), amdgpu_device_suspend() and amdgpu_device_resume() all bail out early on DRM_SWITCH_POWER_OFF. This callback was added later, for an unrelated reason, and did not inherit the check. nouveau guards every one of its PM callbacks the same way.
Bail out the same way here. On a single-GPU system switch_power_state is never DRM_SWITCH_POWER_OFF, so this is a no-op there.
Found on a MacBookPro11,5, where the Radeon is powered down through apple-gmux so that the internal panel can be driven by the iGPU instead. Every suspend failed in amdgpu_pmops_suspend_noirq() while the card was off; with this check a full S3 cycle completes.
--- drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c | 8 ++++++++ 1 file changed, 8 insertions(+)
drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c +8 -0
@@ -2653,6 +2653,14 @@ static int amdgpu_pmops_suspend_noirq(ststruct amdgpu_device *adev =drm_to_adev (drm_dev);int r; +/* +* A GPU parked by vga_switcheroo has no power and no PCIe link, so the +* ASIC reset below would fail and abort the whole noirq suspend phase. +* Bail out like amdgpu_device_prepare/suspend/resume() already do. +*/ +if (drm_dev->switch_power_state ==DRM_SWITCH_POWER_OFF ) +return 0 ; +if (amdgpu_acpi_should_gpu_reset (adev)) {amdgpu_device_lock_reset_domain (adev->reset_domain); r =amdgpu_asic_reset (adev);
Greg Kroah-Hartman
maintainerPATCH 190/428 binder: fix is_failure flag for superseded transaction cleanup
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Tomer Pomeranc <[email protected]>
commit 22c135635fdd9816c0ef140d6de7b2e4fdf73e89 upstream.
When a TF_UPDATE_TXN transaction supersedes a pending async transaction, binder_release_entire_buffer() is called with is_failure=false. Since the superseded transaction was never delivered, binder_apply_fd_fixups() was never called and no fds were installed in the target process.
With is_failure=false, the BINDER_TYPE_FDA cleanup handler interprets stale buffer contents as installed fd numbers and passes them to binder_deferred_fd_close(), closing unrelated file descriptors.
Pass is_failure=true since the transaction was never delivered to the target, matching the semantics of all other undelivered-transaction cleanup paths.
--- drivers/android/binder.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/android/binder.c +1 -1
@@ -2930,7 +2930,7 @@ static int binder_proc_transaction(struc t_outdated->buffer =NULL ; buffer->transaction =NULL ;trace_binder_transaction_update_buffer_release (buffer); -binder_release_entire_buffer (proc,NULL , buffer, false); +binder_release_entire_buffer (proc,NULL , buffer, true);binder_alloc_free_buf (&proc->alloc, buffer);kfree (t_outdated);binder_stats_deleted (BINDER_STAT_TRANSACTION );
Greg Kroah-Hartman
maintainerPATCH 191/428 binder: fix leaked fd fixups on TF_UPDATE_TXN supersede
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Tomer Pomeranc <[email protected]>
commit c4c02084d6687d5cd5edccaf52cc45e5160f1184 upstream.
When a TF_UPDATE_TXN transaction supersedes a pending async transaction in a frozen process, the outdated transaction is freed with kfree() directly. This skips binder_free_txn_fixups(), leaking all binder_txn_fd_fixup entries and their fget()'d struct file references.
The leaked file refcounts never reach zero, so the struct file objects are permanently pinned in memory. They survive process exit and accumulate across invocations until file-max exhaustion.
Every other transaction cleanup path (binder_free_transaction(), binder_transaction() error paths, binder_release_work()) correctly calls binder_free_txn_fixups(). Add the missing call before kfree() in the t_outdated cleanup block.
--- drivers/android/binder.c | 1 + 1 file changed, 1 insertion(+)
drivers/android/binder.c +1 -0
@@ -2932,6 +2932,7 @@ static int binder_proc_transaction(structrace_binder_transaction_update_buffer_release (buffer);binder_release_entire_buffer (proc,NULL , buffer, true);binder_alloc_free_buf (&proc->alloc, buffer); +binder_free_txn_fixups (t_outdated);kfree (t_outdated);binder_stats_deleted (BINDER_STAT_TRANSACTION ); }
Greg Kroah-Hartman
maintainerPATCH 192/428 binderfs: fix UAF write in binder_add_device
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Peiyang He <[email protected]>
commit 30d15f44a9e513ec2f257fdf192d9d1fa5af0799 upstream.
binderfs_binder_device_create() publishes the new dentry with d_make_persistent() and then calls simple_done_creating(), which drops the parent directory lock and the creator's dentry reference. It then calls binder_add_device() to register the device in the global binder_devices list.
After simple_done_creating() releases the parent directory lock, a concurrent unlinkat() can remove the new device entry. Dropping the creator's dentry reference can then trigger binderfs_evict_inode(), freeing the device. binder_add_device() later accesses the freed object, causing UAF write.
Found by a modified Syzkaller:
BUG: KASAN: slab-use-after-free in hlist_add_head include/linux/list.h:1073 [inline] BUG: KASAN: slab-use-after-free in binder_add_device+0xa9/0xc0 drivers/android/binder.c:7068 Write of size 8 at addr ffff88805b340c00 by task syz.1.532/11389 CPU: 0 UID: 0 PID: 11389 Comm: syz.1.532 Not tainted 7.2.0 #4 PREEMPT(full) Hardware name: QEMU Ubuntu 24.04 PC v2 (i440FX + PIIX, arch_caps fix, 1996), BIOS 1.16.3-debian-1.16.3-2 04/01/2014 Call Trace: <TASK> __dump_stack lib/dump_stack.c:94 [inline] dump_stack_lvl+0x10e/0x1f0 lib/dump_stack.c:120 print_address_description mm/kasan/report.c:378 [inline] print_report+0xf7/0x600 mm/kasan/report.c:482 kasan_report+0xe4/0x120 mm/kasan/report.c:595 hlist_add_head include/linux/list.h:1073 [inline] binder_add_device+0xa9/0xc0 drivers/android/binder.c:7068 binderfs_binder_device_create.isra.0+0x724/0x990 drivers/android/binderfs.c:196 binder_ctl_ioctl+0x186/0x1b0 drivers/android/binderfs.c:241 vfs_ioctl fs/ioctl.c:51 [inline] __do_sys_ioctl fs/ioctl.c:597 [inline] __se_sys_ioctl fs/ioctl.c:583 [inline] __x64_sys_ioctl+0x18e/0x210 fs/ioctl.c:583 do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline] do_syscall_64+0x116/0x800 arch/x86/entry/syscall_64.c:94 entry_SYSCALL_64_after_hwframe+0x77/0x7f RIP: 0033:0x7ff9027a833d Code: ff c3 66 2e 0f 1f 84 00 00 00 00 00 90 f3 0f 1e fa 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 73 01 c3 48 c7 c1 b0 ff ff ff f7 d8 64 89 01 48 RSP: 002b:00007ff903674018 EFLAGS: 00000246 ORIG_RAX: 0000000000000010 RAX: ffffffffffffffda RBX: 00007ff902a35fa0 RCX: 00007ff9027a833d RDX: 0000200000000500 RSI: 00000000c1086201 RDI: 0000000000000004 RBP: 00007ff902850733 R08: 0000000000000000 R09: 0000000000000000 R10: 0000000000000000 R11: 0000000000000246 R12: 0000000000000000 R13: 00007ff902a36038 R14: 00007ff902a35fa0 R15: 00007ffd7166bfa0 </TASK> Allocated by task 11389: kasan_save_stack+0x33/0x60 mm/kasan/common.c:57 kasan_save_track+0x14/0x30 mm/kasan/common.c:78 poison_kmalloc_redzone mm/kasan/common.c:398 [inline] __kasan_kmalloc+0xaa/0xb0 mm/kasan/common.c:415 kasan_kmalloc include/linux/kasan.h:263 [inline] __kmalloc_cache_noprof+0x2e4/0x6f0 mm/slub.c:5489 _kmalloc_noprof include/linux/slab.h:988 [inline] _kzalloc_noprof include/linux/slab.h:1309 [inline] binderfs_binder_device_create.isra.0+0x17a/0x990 drivers/android/binderfs.c:148 binder_ctl_ioctl+0x186/0x1b0 drivers/android/binderfs.c:241 vfs_ioctl fs/ioctl.c:51 [inline] __do_sys_ioctl fs/ioctl.c:597 [inline] __se_sys_ioctl fs/ioctl.c:583 [inline] __x64_sys_ioctl+0x18e/0x210 fs/ioctl.c:583 do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline] do_syscall_64+0x116/0x800 arch/x86/entry/syscall_64.c:94 entry_SYSCALL_64_after_hwframe+0x77/0x7f Freed by task 11389: kasan_save_stack+0x33/0x60 mm/kasan/common.c:57 kasan_save_track+0x14/0x30 mm/kasan/common.c:78 kasan_save_free_info+0x3b/0x60 mm/kasan/generic.c:584 poison_slab_object mm/kasan/common.c:253 [inline] __kasan_slab_free+0x5f/0x80 mm/kasan/common.c:285 kasan_slab_free include/linux/kasan.h:235 [inline] slab_free_hook mm/slub.c:2677 [inline] slab_free mm/slub.c:6377 [inline] kfree+0x2fc/0x6e0 mm/slub.c:6692 binderfs_evict_inode+0x1e8/0x260 drivers/android/binderfs.c:268 evict+0x3c2/0xad0 fs/inode.c:825 iput_final fs/inode.c:2019 [inline] iput fs/inode.c:2068 [inline] iput+0x79a/0xd30 fs/inode.c:2031 dentry_unlink_inode+0x27f/0x460 fs/dcache.c:479 dentry_kill+0x25d/0xc20 fs/dcache.c:826 finish_dput fs/dcache.c:1001 [inline] dput.part.0+0xce/0x230 fs/dcache.c:1042 dput+0x1f/0x30 fs/dcache.c:1037 end_dirop+0x7d/0xa0 fs/namei.c:2956 binderfs_binder_device_create.isra.0+0x71c/0x990 drivers/android/binderfs.c:194 binder_ctl_ioctl+0x186/0x1b0 drivers/android/binderfs.c:241 vfs_ioctl fs/ioctl.c:51 [inline] __do_sys_ioctl fs/ioctl.c:597 [inline] __se_sys_ioctl fs/ioctl.c:583 [inline] __x64_sys_ioctl+0x18e/0x210 fs/ioctl.c:583 do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline] do_syscall_64+0x116/0x800 arch/x86/entry/syscall_64.c:94 entry_SYSCALL_64_after_hwframe+0x77/0x7f The buggy address belongs to the object at ffff88805b340c00 which belongs to the cache kmalloc-512 of size 512 The buggy address is located 0 bytes inside of freed 512-byte region [ffff88805b340c00, ffff88805b340e00)
Fix by calling binder_add_device() before d_make_persistent(), while the parent directory lock is still held and the dentry cannot be discarded.
--- drivers/android/binderfs.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-)
drivers/android/binderfs.c +1 -2
@@ -189,12 +189,11 @@ static int binderfs_binder_device_create goto err; } inode->i_private = device; +binder_add_device (device);d_make_persistent (dentry, inode);fsnotify_create (root->d_inode, dentry);simple_done_creating (dentry); -binder_add_device (device); -return 0 ; err:
Greg Kroah-Hartman
maintainerPATCH 193/428 clk: spacemit: k3: add CPU PLL rate tables
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Troy Mitchell <[email protected]>
commit d558af8ee0bb1b2e88e696be84adb93cf2b7be6f upstream.
The K3 CPU PLL rate tables currently describe only one rate per PLL, although the hardware supports a wider range.
PLL3 and PLL4 support rates from 1.05 to 2.4 GHz, while PLL5 and PLL8 support rates from 1.05 to 2 GHz. Populate the tables with every supported rate in 50 MHz steps.
--- drivers/clk/spacemit/ccu-k3.c | 92 +++++++++++++++++++++++++++++++++++ 1 file changed, 92 insertions(+)
drivers/clk/spacemit/ccu-k3.c +92 -0
@@ -30,14 +30,87 @@ static const struct ccu_pll_rate_tbl pll2_rate_tbl[] = { };static const struct ccu_pll_rate_tbl pll3_rate_tbl[] = { +CCU_PLLA_RATE (1050000000UL ,0x0b2b3000 ,0x00000000 ,0xa0458080 ), +CCU_PLLA_RATE (1100000000UL ,0x0b2d3555 ,0x00005500 ,0xa0458181 ), +CCU_PLLA_RATE (1150000000UL ,0x0b2f3aaa ,0x0000ab00 ,0xa0458181 ), +CCU_PLLA_RATE (1200000000UL ,0x0b320000 ,0x00000000 ,0xa0458181 ), +CCU_PLLA_RATE (1250000000UL ,0x0b340555 ,0x00005500 ,0xa0458181 ), +CCU_PLLA_RATE (1300000000UL ,0x0b360aaa ,0x0000ab00 ,0xa0458282 ), +CCU_PLLA_RATE (1350000000UL ,0x0b381000 ,0x00000000 ,0xa0458282 ), +CCU_PLLA_RATE (1400000000UL ,0x0b3a1555 ,0x00005500 ,0xa0458282 ), +CCU_PLLA_RATE (1450000000UL ,0x0b3c1aaa ,0x0000ab00 ,0xa0458383 ), +CCU_PLLA_RATE (1500000000UL ,0x0b3e2000 ,0x00000000 ,0xa0458383 ), +CCU_PLLA_RATE (1550000000UL ,0x0b402555 ,0x00005500 ,0xa0458383 ), +CCU_PLLA_RATE (1600000000UL ,0x0b422aaa ,0x0000ab00 ,0xa0458484 ), +CCU_PLLA_RATE (1650000000UL ,0x0b443000 ,0x00000000 ,0xa0458484 ), +CCU_PLLA_RATE (1700000000UL ,0x0b463555 ,0x00005500 ,0xa0458484 ), +CCU_PLLA_RATE (1750000000UL ,0x0b483aaa ,0x0000ab00 ,0xa0458585 ), +CCU_PLLA_RATE (1800000000UL ,0x0b4b0000 ,0x00000000 ,0xa0458585 ), +CCU_PLLA_RATE (1850000000UL ,0x0b4d0555 ,0x00005500 ,0xa0458585 ), +CCU_PLLA_RATE (1900000000UL ,0x0b4f0aaa ,0x0000ab00 ,0xa0458686 ), +CCU_PLLA_RATE (1950000000UL ,0x0b511000 ,0x00000000 ,0xa0458686 ), +CCU_PLLA_RATE (2000000000UL ,0x0b292aaa ,0x0000ab00 ,0xa0558686 ), +CCU_PLLA_RATE (2050000000UL ,0x0b2a2d55 ,0x00005500 ,0xa0558686 ), +CCU_PLLA_RATE (2100000000UL ,0x0b2b3000 ,0x00000000 ,0xa0558787 ), +CCU_PLLA_RATE (2150000000UL ,0x0b2c32aa ,0x0000ab00 ,0xa0558787 ),CCU_PLLA_RATE (2200000000UL ,0x0b2d3555 ,0x00005500 ,0xa0558787 ), +CCU_PLLA_RATE (2250000000UL ,0x0b2e3800 ,0x00000000 ,0xa0558888 ), +CCU_PLLA_RATE (2300000000UL ,0x0b2f3aaa ,0x0000ab00 ,0xa0558888 ), +CCU_PLLA_RATE (2350000000UL ,0x0b303d55 ,0x00005500 ,0xa0558888 ), +CCU_PLLA_RATE (2400000000UL ,0x0b320000 ,0x00000000 ,0xa0558989 ), };static const struct ccu_pll_rate_tbl pll4_rate_tbl[] = { +CCU_PLLA_RATE (1050000000UL ,0x0b2b3000 ,0x00000000 ,0xa0458080 ), +CCU_PLLA_RATE (1100000000UL ,0x0b2d3555 ,0x00005500 ,0xa0458181 ), +CCU_PLLA_RATE (1150000000UL ,0x0b2f3aaa ,0x0000ab00 ,0xa0458181 ), +CCU_PLLA_RATE (1200000000UL ,0x0b320000 ,0x00000000 ,0xa0458181 ), +CCU_PLLA_RATE (1250000000UL ,0x0b340555 ,0x00005500 ,0xa0458181 ), +CCU_PLLA_RATE (1300000000UL ,0x0b360aaa ,0x0000ab00 ,0xa0458282 ), +CCU_PLLA_RATE (1350000000UL ,0x0b381000 ,0x00000000 ,0xa0458282 ), +CCU_PLLA_RATE (1400000000UL ,0x0b3a1555 ,0x00005500 ,0xa0458282 ), +CCU_PLLA_RATE (1450000000UL ,0x0b3c1aaa ,0x0000ab00 ,0xa0458383 ), +CCU_PLLA_RATE (1500000000UL ,0x0b3e2000 ,0x00000000 ,0xa0458383 ), +CCU_PLLA_RATE (1550000000UL ,0x0b402555 ,0x00005500 ,0xa0458383 ), +CCU_PLLA_RATE (1600000000UL ,0x0b422aaa ,0x0000ab00 ,0xa0458484 ), +CCU_PLLA_RATE (1650000000UL ,0x0b443000 ,0x00000000 ,0xa0458484 ), +CCU_PLLA_RATE (1700000000UL ,0x0b463555 ,0x00005500 ,0xa0458484 ), +CCU_PLLA_RATE (1750000000UL ,0x0b483aaa ,0x0000ab00 ,0xa0458585 ), +CCU_PLLA_RATE (1800000000UL ,0x0b4b0000 ,0x00000000 ,0xa0458585 ), +CCU_PLLA_RATE (1850000000UL ,0x0b4d0555 ,0x00005500 ,0xa0458585 ), +CCU_PLLA_RATE (1900000000UL ,0x0b4f0aaa ,0x0000ab00 ,0xa0458686 ), +CCU_PLLA_RATE (1950000000UL ,0x0b511000 ,0x00000000 ,0xa0458686 ), +CCU_PLLA_RATE (2000000000UL ,0x0b292aaa ,0x0000ab00 ,0xa0558686 ), +CCU_PLLA_RATE (2050000000UL ,0x0b2a2d55 ,0x00005500 ,0xa0558686 ), +CCU_PLLA_RATE (2100000000UL ,0x0b2b3000 ,0x00000000 ,0xa0558787 ), +CCU_PLLA_RATE (2150000000UL ,0x0b2c32aa ,0x0000ab00 ,0xa0558787 ),CCU_PLLA_RATE (2200000000UL ,0x0b2d3555 ,0x00005500 ,0xa0558787 ), +CCU_PLLA_RATE (2250000000UL ,0x0b2e3800 ,0x00000000 ,0xa0558888 ), +CCU_PLLA_RATE (2300000000UL ,0x0b2f3aaa ,0x0000ab00 ,0xa0558888 ), +CCU_PLLA_RATE (2350000000UL ,0x0b303d55 ,0x00005500 ,0xa0558888 ), +CCU_PLLA_RATE (2400000000UL ,0x0b320000 ,0x00000000 ,0xa0558989 ), };static const struct ccu_pll_rate_tbl pll5_rate_tbl[] = { +CCU_PLLA_RATE (1050000000UL ,0x0b2b3000 ,0x00000000 ,0xa0458080 ), +CCU_PLLA_RATE (1100000000UL ,0x0b2d3555 ,0x00005500 ,0xa0458181 ), +CCU_PLLA_RATE (1150000000UL ,0x0b2f3aaa ,0x0000ab00 ,0xa0458181 ), +CCU_PLLA_RATE (1200000000UL ,0x0b320000 ,0x00000000 ,0xa0458181 ), +CCU_PLLA_RATE (1250000000UL ,0x0b340555 ,0x00005500 ,0xa0458181 ), +CCU_PLLA_RATE (1300000000UL ,0x0b360aaa ,0x0000ab00 ,0xa0458282 ), +CCU_PLLA_RATE (1350000000UL ,0x0b381000 ,0x00000000 ,0xa0458282 ), +CCU_PLLA_RATE (1400000000UL ,0x0b3a1555 ,0x00005500 ,0xa0458282 ), +CCU_PLLA_RATE (1450000000UL ,0x0b3c1aaa ,0x0000ab00 ,0xa0458383 ), +CCU_PLLA_RATE (1500000000UL ,0x0b3e2000 ,0x00000000 ,0xa0458383 ), +CCU_PLLA_RATE (1550000000UL ,0x0b402555 ,0x00005500 ,0xa0458383 ), +CCU_PLLA_RATE (1600000000UL ,0x0b422aaa ,0x0000ab00 ,0xa0458484 ), +CCU_PLLA_RATE (1650000000UL ,0x0b443000 ,0x00000000 ,0xa0458484 ), +CCU_PLLA_RATE (1700000000UL ,0x0b463555 ,0x00005500 ,0xa0458484 ), +CCU_PLLA_RATE (1750000000UL ,0x0b483aaa ,0x0000ab00 ,0xa0458585 ), +CCU_PLLA_RATE (1800000000UL ,0x0b4b0000 ,0x00000000 ,0xa0458585 ), +CCU_PLLA_RATE (1850000000UL ,0x0b4d0555 ,0x00005500 ,0xa0458585 ), +CCU_PLLA_RATE (1900000000UL ,0x0b4f0aaa ,0x0000ab00 ,0xa0458686 ), +CCU_PLLA_RATE (1950000000UL ,0x0b511000 ,0x00000000 ,0xa0458686 ),CCU_PLLA_RATE (2000000000UL ,0x0b292aaa ,0x0000ab00 ,0xa0558686 ), };
@@ -50,6 +123,25 @@ static const struct ccu_pll_rate_tbl pll7_rate_tbl[] = { };static const struct ccu_pll_rate_tbl pll8_rate_tbl[] = { +CCU_PLLA_RATE (1050000000UL ,0x0b2b3000 ,0x00000000 ,0xa0458080 ), +CCU_PLLA_RATE (1100000000UL ,0x0b2d3555 ,0x00005500 ,0xa0458181 ), +CCU_PLLA_RATE (1150000000UL ,0x0b2f3aaa ,0x0000ab00 ,0xa0458181 ), +CCU_PLLA_RATE (1200000000UL ,0x0b320000 ,0x00000000 ,0xa0458181 ), +CCU_PLLA_RATE (1250000000UL ,0x0b340555 ,0x00005500 ,0xa0458181 ), +CCU_PLLA_RATE (1300000000UL ,0x0b360aaa ,0x0000ab00 ,0xa0458282 ), +CCU_PLLA_RATE (1350000000UL ,0x0b381000 ,0x00000000 ,0xa0458282 ), +CCU_PLLA_RATE (1400000000UL ,0x0b3a1555 ,0x00005500 ,0xa0458282 ), +CCU_PLLA_RATE (1450000000UL ,0x0b3c1aaa ,0x0000ab00 ,0xa0458383 ), +CCU_PLLA_RATE (1500000000UL ,0x0b3e2000 ,0x00000000 ,0xa0458383 ), +CCU_PLLA_RATE (1550000000UL ,0x0b402555 ,0x00005500 ,0xa0458383 ), +CCU_PLLA_RATE (1600000000UL ,0x0b422aaa ,0x0000ab00 ,0xa0458484 ), +CCU_PLLA_RATE (1650000000UL ,0x0b443000 ,0x00000000 ,0xa0458484 ), +CCU_PLLA_RATE (1700000000UL ,0x0b463555 ,0x00005500 ,0xa0458484 ), +CCU_PLLA_RATE (1750000000UL ,0x0b483aaa ,0x0000ab00 ,0xa0458585 ), +CCU_PLLA_RATE (1800000000UL ,0x0b4b0000 ,0x00000000 ,0xa0458585 ), +CCU_PLLA_RATE (1850000000UL ,0x0b4d0555 ,0x00005500 ,0xa0458585 ), +CCU_PLLA_RATE (1900000000UL ,0x0b4f0aaa ,0x0000ab00 ,0xa0458686 ), +CCU_PLLA_RATE (1950000000UL ,0x0b511000 ,0x00000000 ,0xa0458686 ),CCU_PLLA_RATE (2000000000UL ,0x0b292aaa ,0x0000ab00 ,0xa0558686 ), };
Signature · 1 line
2.56.0
Greg Kroah-Hartman
maintainerPATCH 194/428 hrtimer: Use the mask to clear TIF_HRTIMER_REARM from the exit work
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Karl Mehltretter <[email protected]>
commit 28fa9af353bed2bb738c46e31f9b7d0347b759d1 upstream.
hrtimer_rearm_deferred_user_irq() removes the rearm bit from the local copy of the TIF work with:
*tif_work &= ~TIF_HRTIMER_REARM;
TIF_HRTIMER_REARM is the bit number (12), not the mask. That clears TIF_NOTIFY_SIGNAL (bit 2) and TIF_MEMDIE (bit 3) from the copy and leaves bit 12 set.
So the function never returns true, and the copy handed to exit_to_user_mode_loop() lacks TIF_NOTIFY_SIGNAL. If that was the only work for the loop, the task returns to user space with the task work still pending. hrtimer_interrupt() sets TIF_HRTIMER_REARM every time, so the next tick repeats that. Task work queued with TWA_SIGNAL from a hrtimer callback stays pending until the task does a syscall, takes an interrupt without deferred rearm or has to reschedule.
A task which polls the io_uring completion ring in user space sees a timeout completion after 30ms (median) instead of 70us. 2 CPU QEMU guest, HZ=1000.
Use the mask.
--- include/linux/hrtimer_rearm.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
include/linux/hrtimer_rearm.h +1 -1
@@ -40,7 +40,7 @@ hrtimer_rearm_deferred_user_irq(unsignedclear_thread_flag (TIF_HRTIMER_REARM );__hrtimer_rearm_deferred ();/* Don't go into the loop if HRTIMER_REARM was the only flag */ - *tif_work &= ~TIF_HRTIMER_REARM ; + *tif_work &= ~_TIF_HRTIMER_REARM;return !*tif_work; }return false;
Greg Kroah-Hartman
maintainerPATCH 195/428 kgdboc: Fix tty driver reference leak in configure_kgdboc()
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Wentao Liang <[email protected]>
commit ef3134cd0d4831ae2137aee2883987859b80b2c6 upstream.
tty_find_polling_driver() returns the tty driver with an extra reference taken by tty_driver_kref_get(). configure_kgdboc() stores it in kgdb_tty_driver but the reference is never dropped: it is leaked when kgdb_register_io_module() fails, when the configuration is cleared, and on every reconfigure.
Release the reference both when unconfiguring and on the error path.
--- drivers/tty/serial/kgdboc.c | 9 +++++++++ 1 file changed, 9 insertions(+)
drivers/tty/serial/kgdboc.c +9 -0
@@ -188,6 +188,11 @@ static void cleanup_kgdboc(void)kgdboc_unregister_kbd ();kgdb_unregister_io_module (&kgdboc_io_ops); + +if (kgdb_tty_driver) { +tty_driver_kref_put (kgdb_tty_driver); + kgdb_tty_driver =NULL ; + } }static int configure_kgdboc (void )
@@ -254,6 +259,10 @@ do_register: noconfig:kgdboc_unregister_kbd (); +if (kgdb_tty_driver) { +tty_driver_kref_put (kgdb_tty_driver); + kgdb_tty_driver =NULL ; + } configured =0 ;return err;
Greg Kroah-Hartman
maintainerPATCH 196/428 Input: atkbd - skip deactivate for Lenovo IdeaPad Slim 3 15IWC11
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Martino Papero <[email protected]>
commit 3bf8d603fd45649c106cae6290a10f20b1bbb569 upstream.
The internal keyboard on the Lenovo IdeaPad Slim 3 15IWC11 (83RR) does not work correctly with the default i8042 settings. Using i8042.nopnp=1 and i8042.dumbkbd=1 restores keyboard input, but prevents the Caps Lock LED from working. Using i8042.nopnp=1 alone does not fix the keyboard.
The laptop works correctly when atkbd_deactivate_fixup is used instead. Add a DMI quirk for the 83RR to enable it.
This was tested without any i8042 command-line parameters. Keyboard input and the Caps Lock LED work correctly, including after suspend and resume and after a cold boot.
--- drivers/input/keyboard/atkbd.c | 8 ++++++++ 1 file changed, 8 insertions(+)
drivers/input/keyboard/atkbd.c +8 -0
@@ -1947,6 +1947,14 @@ static const struct dmi_system_id atkbd_ .callback = atkbd_deactivate_fixup, }, { +/* Lenovo IdeaPad Slim 3 15IWC11 (83RR) */ + .matches = { +DMI_MATCH (DMI_SYS_VENDOR ,"LENOVO" ), + DMI_MATCH(DMI_PRODUCT_NAME, "83RR"), + }, + .callback = atkbd_deactivate_fixup, + }, + {/* Xiaomi Book Pro 14 (TM2424) */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "XIAOMI"),
Greg Kroah-Hartman
maintainerPATCH 197/428 Input: i8042 - add nomux quirk for Fujitsu LIFEBOOK U7410
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Lovekesh Solanki <[email protected]>
commit 495cd7f858b45d2ffa9b685bba3a8c23c1ca4dd3 upstream.
On the Fujitsu LIFEBOOK U7410 the internal keyboard and touchpad die shortly after boot if i8042 multiplexing controller is probed and switched to muxed mode. These multiplexing setup commands disturb the EC emulated keyboard and atkbd reports "Spurious ACK" and "Unknown key pressed" warnings, after which both keyboard and touchpad stop delivering events. Both touchpad and keyboard work in BIOS and GRUB.
Disabling multiplexer using i8042.nomux=1 makes both devices work on warm/cold boot.
Add a DMI quirk to force nomux mode for this model.
--- drivers/input/serio/i8042-acpipnpio.h | 9 +++++++++ 1 file changed, 9 insertions(+)
drivers/input/serio/i8042-acpipnpio.h +9 -0
@@ -671,6 +671,15 @@ static const struct dmi_system_id i8042_ .driver_data = (void *)(SERIO_QUIRK_NOAUX) }, { +/* Fujitsu LIFEBOOK U7410 */ +/* Mux mode also disturbs the EC keyboard, causing spurious ACKs */ + .matches = { +DMI_MATCH (DMI_SYS_VENDOR ,"FUJITSU CLIENT COMPUTING LIMITED" ), + DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK U7410"), + }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX ) + }, + {/* Gigabyte M912 */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "GIGABYTE"),
Greg Kroah-Hartman
maintainerPATCH 198/428 Input: s6sy761 - fix resume ordering and restore sensing
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: David Heidelberg <[email protected]>
commit a215720959c5450b3699e243904b39c3f8f68888 upstream.
System suspend powers the controller off and resume powers it back on, but the resume path enables the interrupt before s6sy761_power_on() checks the boot. The firmware raises its boot-complete event on the interrupt line, the threaded handler consumes it, s6sy761_power_on() then reads an empty event and resume fails with -ENODEV, skipping the touch function setup:
s6sy761 2-0048: PM: dpm_run_callback(): s6sy761_resume [s6sy761] returns -19
Power the chip on first and only then unmask the interrupt. Once resume completes the boot handshake the chip comes back with sensing off, as at probe where input_open() turns it on, so the touchscreen stays dead after resume. Send SENSE_ON again when the input device is open.
Tested on a Pixel 3 XL over several s2idle cycles: resume succeeds and the touch function and sense status match the pre-suspend state.
--- drivers/input/touchscreen/s6sy761.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-)
drivers/input/touchscreen/s6sy761.c +13 -1
@@ -500,10 +500,22 @@ static int s6sy761_suspend(struct devicestatic int s6sy761_resume (struct device *dev) {struct s6sy761_data *sdata =dev_get_drvdata (dev); +int err; + + err =s6sy761_power_on (sdata); +if (err) +return err;enable_irq (sdata->client->irq); -return s6sy761_power_on (sdata); +guard (mutex)(&sdata->input->mutex); + +if (!input_device_enabled (sdata->input)) +return 0 ; + + err =i2c_smbus_write_byte (sdata->client,S6SY761_SENSE_ON ); + +return err; }static const struct dev_pm_ops s6sy761_pm_ops = {
Greg Kroah-Hartman
maintainerPATCH 199/428 Input: synaptics - add LEN205b entry to smbus_pnp_ids for ThinkPad T490
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Lovekesh Solanki <[email protected]>
commit a961417c5ae77bbb728a23d9577ecd8f4d8a4b5a upstream.
Lenovo ThinkPad T490 has a Synaptics Touchpad that supports SMBus/RMI4 mode, but is not listed in smbus_pnp_ids table.
Add LEN205b to smbus_pnp_ids[] passlist.
--- drivers/input/mouse/synaptics.c | 1 + 1 file changed, 1 insertion(+)
drivers/input/mouse/synaptics.c +1 -0
@@ -191,6 +191,7 @@ static const char * const smbus_pnp_ids["LEN2054" ,/* E480 */ "LEN2055" ,/* E580 */ "LEN2058" ,/* E490 */ +"LEN205b" ,/* T490 */ "LEN2068" ,/* T14 Gen 1 */ "SYN1221" ,/* TUXEDO InfinityBook Pro 14 v5 */ "SYN3003", /* HP EliteBook 850 G1 */
Greg Kroah-Hartman
maintainerPATCH 200/428 Input: synaptics - limit T440p InterTouch quirk to LEN0036
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Raphaël Larocque <[email protected]>
commit f0fd8993082ca229faa3f9844f1c9fe182eca20b upstream.
The ThinkPad L440 also uses board id 2722, but does not have the SMBus initialization problem seen on the T440p. On the L440, the SMBus companion becomes available shortly after psmouse probes, allowing the touchpad to use SMBus/RMI4 normally.
The T440p's quirk is currently applied to all Synaptics touchpads with board id 2722, unnecessarily disabling SMBus InterTouch on the L440, as reported by Daniel Salmun.
Restrict the quirk to PNP ID LEN0036, which identifies the T440p, so that other devices sharing board id 2722 retain their normal SMBus/RMI4 operation.
--- drivers/input/mouse/synaptics.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-)
drivers/input/mouse/synaptics.c +4 -2
@@ -1839,8 +1839,10 @@ static int synaptics_setup_intertouch(streturn -ENXIO ; } -/* Disable intertouch on known-broken board revisions */ -if (info->board_id ==2722 ) { +/* Disable intertouch on known-broken T440p board revisions */ +if (info->board_id ==2722 && +psmouse_matches_pnp_id (psmouse, + (const char *const []){"LEN0036" ,NULL })) {psmouse_info (psmouse,"Disabling intertouch for board id %u\n" , info->board_id);
Greg Kroah-Hartman
maintainerPATCH 201/428 nitro_enclaves: fix use-after-free on SLOT_ALLOC failure
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Yuxiao Wang <[email protected]>
commit 9bc184a2eda8b773c88ecbff934001ad649b9cc1 upstream.
When ne_create_vm_ioctl() fails the SLOT_ALLOC request after anon_inode_getfile() has succeeded, the error path calls fput(enclave_file) and then frees ne_enclave.
In normal userspace context, fput() defers the final __fput() via task_work. ne_enclave_release() therefore runs after ne_enclave has already been freed and dereferences ne_enclave->slot_uid, causing a
use-after-free: KASAN: slab-use-after-free in ne_enclave_release.
The enclave has no slot allocated and is not yet linked into the enclaves list on this error path, so ne_enclave_release() is expected to return early when slot_uid is zero. However, reading slot_uid already accesses the freed object.
Clear enclave_file->private_data before fput() on the error path. ne_enclave_release() then returns immediately when private_data is NULL, leaving the ioctl error path as the sole owner of ne_enclave. This is safe because the file has not been fd_install()'d yet.
Tested on an AWS EC2 m5.2xlarge with CONFIG_KASAN=y. Without the patch, the reproducer triggers a KASAN slab-use-after-free on every SLOT_ALLOC failure. With the patch, no KASAN report is produced and the SLOT_ALLOC error is still returned. Normal enclave creation and teardown are unaffected.
--- drivers/virt/nitro_enclaves/ne_misc_dev.c | 1 + 1 file changed, 1 insertion(+)
drivers/virt/nitro_enclaves/ne_misc_dev.c +1 -0
@@ -1706,6 +1706,7 @@ static int ne_create_vm_ioctl(struct ne_return enclave_fd; put_file: + enclave_file->private_data =NULL ;fput (enclave_file); put_fd:put_unused_fd (enclave_fd);
Greg Kroah-Hartman
maintainerPATCH 202/428 rust_binder: cancel deferred work items in thread exit
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Alice Ryhl <[email protected]>
commit 62479b6e5df82cbdf3c4145130928f233fae78fb upstream.
If there are deferred work items on the thread todo list, then they are not cleaned up in the Thread::release() method. Thus, update the code to clean up the work items even if they are deferred.
This can happen if the thread dies while it has an active outgoing transaction.
--- drivers/android/binder/thread.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-)
drivers/android/binder/thread.rs +7 -1
@@ -659,6 +659,12 @@ impl Thread { self.inner.lock ().push_return_work (reply); } +pub (crate )fn pop_work_even_if_deferred (&self) ->Option <DLArc <dyn DeliverToRead >> { +let mut thread_inner = self.inner.lock (); + thread_inner.process_work_list =true ; + thread_inner.pop_work () + } +fn translate_object ( &self, obj_index: usize,
@@ -1621,7 +1627,7 @@ impl Thread { self.unwind_transaction_stack ();// Cancel all pending work items. -while let Ok (Some (work)) = self.get_work_local (false ) { +while let Some (work) = self.pop_work_even_if_deferred () { work.into_arc ().cancel (); } }
Greg Kroah-Hartman
maintainerPATCH 203/428 rust_binder: reschedule node refcount update on thread exit
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Alice Ryhl <[email protected]>
commit 2a74ccd1bcc170e66525f34e9de8652b57e2cf3d upstream.
When a thread exits via BINDER_THREAD_EXIT, its pending work items are cancelled. If a thread exits while holding a pending node refcount increment (e.g. pushed as deferred work to that thread), the refcount increment was previously dropped because Node::cancel() and NodeWrapper::cancel() were no-ops.
Dropping the refcount update leaves the node's delivery state and count state desynchronized, and userspace will not receive the notification, which can cause the node to never be freed from the process's nodes tree when all external references are dropped.
Fix this by implementing DeliverToRead::cancel() for Node and NodeWrapper to move the pending refcount update to the process's work queue on thread exit so another thread can deliver it to userspace.
--- drivers/android/binder/node.rs | 20 ++++++++++++++++--- drivers/android/binder/node/wrapper.rs | 34 ++++++++++++++++++++++++++++++++- 2 files changed, 50 insertions(+), 4 deletions(-)
drivers/android/binder/node.rs +17 -3
@@ -49,9 +49,9 @@ pub(crate) struct CouldNotDeliverCritica/// about to drop the weak reference, then the strong increment could be processed after the /// other thread has already exited, which would be too late. /// -/// Note that trying to create a `ListArc` to the node can succeed even if `has_normal_push` is +/// Note that trying to create a `ListArc` to the node can succeed even if `has_pushed_node` is /// set. This is because another thread might just have popped the node from a todo list, but not -/// yet called `do_work`. However, if `has_normal_push` is false, then creating a `ListArc` should +/// yet called `do_work`. However, if `has_pushed_node` is false, then creating a `ListArc` should /// always succeed. /// /// Like the other fields in `NodeInner`, the delivery state is protected by the process lock.
@@ -733,7 +733,21 @@ impl DeliverToRead for Node { self.do_work_locked (writer, owner_inner) } -fn cancel (self:DArc <Self >) {} +fn cancel (self:DArc <Self >) { +let _drop_outside_lock; +let mut owner_inner = self.owner.inner.lock (); + +// We only do something on BINDER_THREAD_EXIT, not process exit. +if owner_inner.is_dead { +return ; + } + +// If BINDER_THREAD_EXIT is invoked on a thread with a pending node refcount update, we +// should move ourselves to ensure the refcount update is still delivered. +if let Some (node) =ListArc ::try_from_arc_borrow (self.as_arc_borrow ()) { + _drop_outside_lock = owner_inner.push_work (&self.owner, node); + } + }fn should_sync_wakeup (&self) ->bool {false
drivers/android/binder/node/wrapper.rs +33 -1
@@ -57,7 +57,39 @@ impl DeliverToRead for NodeWrapper { node.do_work_locked (writer, owner_inner) } -fn cancel (self:DArc <Self >) {} +fn cancel (self:DArc <Self >) { +let _drop_outside_lock; +let node = &self.node; +let mut owner_inner = node.owner.inner.lock (); + +// We only do something on BINDER_THREAD_EXIT, not process exit. +if owner_inner.is_dead { +return ; + } + +// We transfer the responsibility of the node refcount update to the scheduled Node because +// NodeWrapper has no way to re-create the ListArc. +let inner = node.inner.access_mut (&mut owner_inner); + +let ds = &mut inner.delivery_state; +assert! (ds.has_pushed_wrapper); +assert! (ds.has_strong_zero2one); + ds.has_pushed_wrapper =false ; + +// We are changing the state to one where the Node is the strong zero2one update instead of +// the wrapper. + ds.has_weak_zero2one =false ; + +if !ds.has_pushed_node { +if let Some (node2) =ListArc ::try_from_arc_borrow (node.as_arc_borrow ()) { + ds.has_pushed_node =true ; + _drop_outside_lock = owner_inner.push_work (&node.owner, node2); + }else { +// This can't actually happen. + ds.has_strong_zero2one =false ; + } + } + }fn should_sync_wakeup (&self) ->bool {false
Re: [PATCH 7.2 203/428] rust_binder: reschedule node refcount update on thread exit
[...]
Quoted 6 lines
Cc stable <[email protected]>Fixes eafedbc7c050 ("rust_binder: add Rust Binder driver")Signed-off-by Alice Ryhl <[email protected]>Signed-off-by Greg Kroah-Hartman <[email protected]>---
[...]
Quoted 2 lines
drivers/android/binder/node.rs +0
-0
[...]
Quoted 1 line
drivers/android/binder/node.rs +0
-0@@ -733,7 +733,21 @@ impl DeliverToRead for Node {
[...]
Quoted 3 lines
drivers/android/binder/node.rs +3
-0+fn cancel (self:DArc <Self >) { +let _drop_outside_lock; +let mut owner_inner = self.owner.inner.lock ();
[...]
Quoted 4 lines
drivers/android/binder/node.rs +4
-0+if let Some (node) =ListArc ::try_from_arc_borrow (self.as_arc_borrow ()) { + _drop_outside_lock = owner_inner.push_work (&self.owner, node); + } + }
[...]
Quoted 3 lines
drivers/android/binder/node/wrapper.rs +0
-0@@ -57,7 +57,39 @@ impl DeliverToRead for NodeWrapper {
[...]
Quoted 4 lines
drivers/android/binder/node/wrapper.rs +4
-0+fn cancel (self:DArc <Self >) { +let _drop_outside_lock; +let node = &self.node; +let mut owner_inner = node.owner.inner.lock ();
[...]
drivers/android/binder/node/wrapper.rs impl DeliverToRead for NodeWrapper { · line 86
Quoted 3 lines
drivers/android/binder/node/wrapper.rs +3
-0+if let Some (node2) =ListArc ::try_from_arc_borrow (node.as_arc_borrow ()) { + ds.has_pushed_node =true ; + _drop_outside_lock = owner_inner.push_work (&node.owner, node2);
I object to this patch as queued: it causes compile errors with CONFIG_ANDROID_BINDER_IPC_RUST=y.
The upstream commit was written on top of dbb17c9ea756 ("rust_binder:
move (e)poll wait queue to Process"), which added the Process argument to ProcessInner::push_work(). That commit is not in 7.2, where push_work() still takes only the work item, so the two new calls do not compile. dbb17c9ea756 is a performance change with no Fixes: or stable tag, so I am not suggesting to add it.
An LLM agent assisted with the review and the build test.
With CONFIG_RUST=y and CONFIG_ANDROID_BINDER_IPC_RUST=y (x86_64 defconfig otherwise, LLVM=1, rustc 1.98.0), "make drivers/android/" on the review tip 77b730c57839 fails:
error[E0061]: this method takes 1 argument but 2 arguments were supplied
--> drivers/android/binder/node/wrapper.rs:86:50
note: method defined here
--> drivers/android/binder/process.rs:173:19error[E0061]: this method takes 1 argument but 2 arguments were supplied
--> drivers/android/binder/node.rs:748:46
ANDROID_BINDER_IPC_RUST depends on !ANDROID_BINDER_IPC, so a build with the C binder enabled does not compile this code.
With this patch reverted the same build passes. It also passes with both calls changed to the one-argument form, push_work(node) and push_work(node2), but that is only compile-tested.
Thanks,
Karl
Greg Kroah-Hartman
maintainerRe: [PATCH 7.2 203/428] rust_binder: reschedule node refcount update on thread exit
Quoted 57 lines · Karl Mehltretter
[...]
Quoted 6 lines
Cc stable <[email protected]>Fixes eafedbc7c050 ("rust_binder: add Rust Binder driver")Signed-off-by Alice Ryhl <[email protected]>Signed-off-by Greg Kroah-Hartman <[email protected]>---
[...]
Quoted 2 lines
drivers/android/binder/node.rs +0
-0[...]
Quoted 1 line
drivers/android/binder/node.rs +0
-0@@ -733,7 +733,21 @@ impl DeliverToRead for Node {[...]
Quoted 3 lines
drivers/android/binder/node.rs +3
-0+fn cancel (self:DArc <Self >) { +let _drop_outside_lock; +let mut owner_inner = self.owner.inner.lock ();[...]
Quoted 4 lines
drivers/android/binder/node.rs +4
-0+if let Some (node) =ListArc ::try_from_arc_borrow (self.as_arc_borrow ()) { + _drop_outside_lock = owner_inner.push_work (&self.owner, node); + } + }[...]
Quoted 3 lines
drivers/android/binder/node/wrapper.rs +0
-0@@ -57,7 +57,39 @@ impl DeliverToRead for NodeWrapper {[...]
Quoted 4 lines
drivers/android/binder/node/wrapper.rs +4
-0+fn cancel (self:DArc <Self >) { +let _drop_outside_lock; +let node = &self.node; +let mut owner_inner = node.owner.inner.lock ();[...]
drivers/android/binder/node/wrapper.rs impl DeliverToRead for NodeWrapper { · line 86
Quoted 3 lines
drivers/android/binder/node/wrapper.rs +3
-0+if let Some (node2) =ListArc ::try_from_arc_borrow (node.as_arc_borrow ()) { + ds.has_pushed_node =true ; + _drop_outside_lock = owner_inner.push_work (&node.owner, node2);I object to this patch as queued: it causes compile errors with
CONFIG_ANDROID_BINDER_IPC_RUST=y.The upstream commit was written on top of
dbb17c9ea756("rust_binder:
move (e)poll wait queue to Process"), which added the Process argument toProcessInner::push_work(). That commit is not in 7.2, wherepush_work()still takes only the work item, so the two new calls do not compile.dbb17c9ea756is a performance change with no Fixes: or stable tag, so I am not suggesting to add it.An LLM agent assisted with the review and the build test.
With
CONFIG_RUST=yandCONFIG_ANDROID_BINDER_IPC_RUST=y(x86_64defconfig otherwise, LLVM=1, rustc 1.98.0), "makedrivers/android/" on the review tip77b730c57839fails:error[E0061]: this method takes 1 argument but 2 arguments were supplied
--> drivers/android/binder/node/wrapper.rs:86:50 note: method defined here --> drivers/android/binder/process.rs:173:19error[E0061]: this method takes 1 argument but 2 arguments were supplied
Quoted 9 lines
--> drivers/android/binder/node.rs:748:46
ANDROID_BINDER_IPC_RUSTdepends on !ANDROID_BINDER_IPC, so a build with the C binder enabled does not compile this code.With this patch reverted the same build passes. It also passes with both calls changed to the one-argument form,
push_work(node)andpush_work(node2), but that is only compile-tested.
THanks, I missed this for some reason my builds were not picking up the rust version of this. Now dropped from both queues, thanks for testing and letting us know.
greg k-h
Greg Kroah-Hartman
maintainerPATCH 204/428 soc: qcom: geni-se: Correct QUP Core ICC vote constants
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Viken Dadhaniya <[email protected]>
commit 1cd9b1be5a9c56fd69e3f555b200a1844f9b873e upstream.
The GENI_TO_CORE ("qup-core") ICC vote selects the QUP Core 2X clock rate. The CORE_2X_*_MHZ constants are expressed in Bps, but their values are several orders of magnitude too small. For example, the 50 MHz threshold is represented by 2500 rather than 25000000 Bps.
As a result, clients using these constants can severely under-vote the QUP Core clock.
Correct the constants to their intended Bps thresholds so that the ICC provider selects the corresponding QUP Core 2X clock rate. Drop the unused definitions.
--- include/linux/soc/qcom/geni-se.h | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-)
include/linux/soc/qcom/geni-se.h +5 -10
@@ -309,17 +309,12 @@ struct geni_se {#define QUP_SE_VERSION_2_5 0x20050000/* -* Define bandwidth thresholds that cause the underlying Core 2X interconnect -* clock to run at the named frequency. These baseline values are recommended -* by the hardware team, and are not dynamically scaled with GENI bandwidth -* beyond basic on/off. +* QUP Core 2X clock votes used by GENI clients through the "qup-core" ICC +* path. Values are in Bps and must be converted with Bps_to_icc() before +* setting avg_bw. */ -#define CORE_2X_19_2_MHZ 960 -#define CORE_2X_50_MHZ 2500 -#define CORE_2X_100_MHZ 5000 -#define CORE_2X_150_MHZ 7500 -#define CORE_2X_200_MHZ 10000 -#define CORE_2X_236_MHZ 16383 +#define CORE_2X_19_2_MHZ 9600000 +#define CORE_2X_50_MHZ 25000000#define GENI_DEFAULT_BW Bps_to_icc(1000)
Greg Kroah-Hartman
maintainerPATCH 205/428 USB: cdc-acm: fix racy TIOCMIWAIT implementation
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Johan Hovold <[email protected]>
commit be4219dd98608736e13e0b790ef742b76a13254d upstream.
Check the wakeup condition after adding the task to the waitqueue and updating the task state to avoid missing a racing modem status update or disconnect.
Store the old icount on entry and do not update it in the completion handler to avoid missing events or reporting spurious ones (due to modem status changes before TIOCMIWAIT was called).
--- drivers/usb/class/cdc-acm.c | 36 +++++++++++++++++++----------------- drivers/usb/class/cdc-acm.h | 1 - 2 files changed, 19 insertions(+), 18 deletions(-)
drivers/usb/class/cdc-acm.c +19 -17
@@ -331,7 +331,6 @@ static void acm_process_notification(strspin_lock_irqsave (&acm->read_lock, flags); acm->ctrlin = newctrl; - acm->oldcount = acm->iocount;if (difference &USB_CDC_SERIAL_STATE_DSR ) acm->iocount.dsr++;
@@ -1027,11 +1026,16 @@ static int wait_serial_change(struct acmDECLARE_WAITQUEUE (wait, current);struct async_icount old, new; - do { +spin_lock_irq (&acm->read_lock); + old = acm->iocount; +spin_unlock_irq (&acm->read_lock); + +add_wait_queue (&acm->wioctl, &wait); +for (;;) { +set_current_state (TASK_INTERRUPTIBLE ); +spin_lock_irq (&acm->read_lock); - old = acm->oldcount; new = acm->iocount; - acm->oldcount = new;spin_unlock_irq (&acm->read_lock); if ((arg & TIOCM_DSR) &&
@@ -1044,22 +1048,20 @@ static int wait_serial_change(struct acm old.rng != new.rng)break ; -add_wait_queue (&acm->wioctl, &wait); -set_current_state (TASK_INTERRUPTIBLE ); -schedule (); -remove_wait_queue (&acm->wioctl, &wait);if (acm->disconnected) { -if (arg &TIOCM_CD ) -break ; -else - rv = -ENODEV ; - }else { -if (signal_pending (current)) - rv = -ERESTARTSYS ; + rv = -ENODEV ; +break ; } - }while (!rv); - +schedule (); + +if (signal_pending (current)) { + rv = -ERESTARTSYS ; +break ; + } + } +__set_current_state (TASK_RUNNING ); +remove_wait_queue (&acm->wioctl, &wait);return rv; }
drivers/usb/class/cdc-acm.h +0 -1
@@ -90,7 +90,6 @@ struct acm {unsigned int ctrlin;/* input control lines (DCD, DSR, RI, break, overruns) */ unsigned int ctrlout;/* output control lines (DTR, RTS) */ struct async_icount iocount;/* counters for control line changes */ -struct async_icount oldcount;/* for comparison of counter */ wait_queue_head_t wioctl;/* for ioctl */ unsigned int writesize;/* max packet size for the output bulk endpoint */ unsigned int readsize,ctrlsize;/* buffer sizes for freeing */
Greg Kroah-Hartman
maintainerPATCH 206/428 USB: cdc-acm: skip URB restart in port_shutdown if disconnected
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Yuchao Zhang <[email protected]>
commit dc614d6bc991740d41d5a99ac7765c4b675dda88 upstream.
acm_port_shutdown() is called from the tty-port machinery whenever the last user closes the port. acm_disconnect() sets acm->disconnected and calls tty_port_tty_vhangup(), which eventually triggers port_shutdown(). At that point acm_disconnect() has already called acm_poison_urbs() and is about to free the control and read URBs.
However, acm_port_shutdown() unconditionally calls acm_unpoison_urbs() and, when the ALWAYS_POLL_CTRL quirk is set, immediately re-submits the ctrlurb and all read URBs. If the concurrent acm_disconnect() then frees those URBs and their DMA buffers while they are in-flight, the result is a use-after-free in acm_ctrl_irq() or acm_read_bulk_callback() accessing the freed DMA memory.
Fix by checking acm->disconnected after draining the delayed anchor and returning early: if we are in a disconnect path the URBs must remain poisoned and must not be resubmitted.
The early return is placed after the delayed-anchor drain loop because that loop only cancels write-side accounting (wb->use = false and autopm counter) and does not touch the hardware path.
--- drivers/usb/class/cdc-acm.c | 3 +++ 1 file changed, 3 insertions(+)
drivers/usb/class/cdc-acm.c +3 -0
@@ -787,6 +787,9 @@ static void acm_port_shutdown(struct ttyusb_autopm_put_interface_async (acm->control); } +if (acm->disconnected) +return ; +acm_unpoison_urbs (acm);if (acm->quirks &ALWAYS_POLL_CTRL ) {
Greg Kroah-Hartman
maintainerPATCH 207/428 USB: serial: fix port tear down use-after-free
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Johan Hovold <[email protected]>
commit 33c920f7c5952dfc42d1b8ea6efe16b1f0bb0c5b upstream.
Some drivers for multiport devices access port driver data from completion handlers of shared URBs submitted at attach() or first open() and stopped at disconnect() or last close(), respectively.
A simple NULL check before accessing the driver data makes sure that a port state container has at least been allocated, but a completion handler can still race with port tear down.
Reorder the disconnect handling so that ports are not deregistered (and their driver data freed) until after all ports have been hung up and the driver disconnect() callback has run so that all I/O has been stopped.
--- drivers/usb/serial/usb-serial.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-)
drivers/usb/serial/usb-serial.c +7 -2
@@ -1191,12 +1191,17 @@ static void usb_serial_disconnect(structusb_serial_port_poison_urbs (port);wake_up_interruptible (&port->port.delta_msr_wait);cancel_work_sync (&port->work); -if (device_is_registered (&port->dev)) -device_del (&port->dev); } +if (serial->type->disconnect) serial->type->disconnect (serial); +for (i =0 ; i < serial->num_ports; ++i) { + port = serial->port[i]; +if (device_is_registered (&port->dev)) +device_del (&port->dev); + } +release_sibling (serial, interface);/* let the last holder of this object cause it to be cleaned up */
Greg Kroah-Hartman
maintainerPATCH 208/428 usb: storage: sierra_ms: reject short SWoC info transfers
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Syed Labeeq Sajid Bukhari <[email protected]>
commit 8a0200d14acd9db388b85cd016e68a4e60d900ec upstream.
sierra_get_swoc_info() requests sizeof(struct swoc_info) (60) bytes from the device via usb_control_msg(), but its callers only treat a negative return value as failure. A device that answers the vendor-specific GetSwocInfo request with a short IN transfer is therefore accepted, leaving the tail of the freshly allocated (kmalloc(), non-zeroing) swoc_info buffer uninitialized.
truinst_show() subsequently prints swocInfo->rev, swocInfo->LinuxSKU and swocInfo->LinuxVer from that buffer into the world-readable (0444) "truinst" sysfs attribute. An emulated/malicious USB device (VID 0x1199, PID 0x0fff) can exploit this to disclose up to 5 bytes of stale kernel heap memory (kmalloc-64) to unprivileged userspace, once per sysfs read, indefinitely. On kernels built without init_on_alloc this leaks recently freed heap contents.
Only accept the transfer when the full structure was received. sierra_ms_init() already retries failed queries, so well-behaved devices are unaffected.
--- drivers/usb/storage/sierra_ms.c | 7 +++++++ 1 file changed, 7 insertions(+)
drivers/usb/storage/sierra_ms.c +7 -0
@@ -77,6 +77,13 @@ static int sierra_get_swoc_info(struct usizeof (struct swoc_info ),/* __u16 size */ USB_CTRL_SET_TIMEOUT); /* int timeout */ +/* +* A short IN transfer leaves the tail of swocInfo uninitialized; +* only a full transfer is valid. +*/ +if (result !=sizeof (struct swoc_info )) +return -EIO ; + swocInfo->LinuxSKU =le16_to_cpu (swocInfo->LinuxSKU); swocInfo->LinuxVer =le16_to_cpu (swocInfo->LinuxVer);return result;
Greg Kroah-Hartman
maintainerPATCH 209/428 usb: hub: use shorter 120ms post resume hold for SS root hubs
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Lovekesh Solanki <[email protected]>
commit 29480b05e6e226594dc11ccf2a036fbd5cdd0e42 upstream.
Holding a USB3 hub PM runtime reference for 200ms at hub resume triggers an AMD platform issue. Users running Android adb report crashes after adb has been polling and waking up the USB subsystem once a second for some time. Vendors are working on a solution. Disabling USB runtime PM is one way to prevent this issue, but it is also proven that reducing the hold time to 120ms in resume also mitigates it. See Link for more details.
Reducing the hold time to 120ms for the USB3 roothub is in itself a valid change and optimization, as the current 200ms is excessive: a root hub has no upstream suspended hub whose wake propagation needs to be accounted for, but still need some time for USB3 link training to complete.
Keep the 200ms hold for external hubs that commit 8f5b7e2bec1c ("usb: hub: fix detection of high tier USB3 devices behind suspended hubs") is intended for.
--- drivers/usb/core/hub.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-)
drivers/usb/core/hub.c +6 -1
@@ -73,8 +73,11 @@/* * Give SS hubs 200ms time after wake to train downstream links before * assuming no port activity and allowing hub to runtime suspend back. +* Root hubs have no upstream hub whose wake propagation needs to be +* accounted for, so they need less time, use 120ms for them. */ #define USB_SS_PORT_U0_WAKE_TIME 200/* ms */ +#define USB_SS_RH_PORT_U0_WAKE_TIME 120/* ms */ /* Protect struct usb_device->state and ->children members * Note: Both are also protected by ->dev.sem, except that ->state can
@@ -1358,7 +1361,9 @@ static void hub_activate(struct usb_hubqueue_delayed_work (system_power_efficient_wq, &hub->post_resume_work, -msecs_to_jiffies (USB_SS_PORT_U0_WAKE_TIME )); +msecs_to_jiffies (hdev->parent ? +USB_SS_PORT_U0_WAKE_TIME : +USB_SS_RH_PORT_U0_WAKE_TIME ));return ; }
Greg Kroah-Hartman
maintainerPATCH 210/428 usb: octeon-hcd: fix the FIFO-flush timeout computation
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Orgad Shaneh <[email protected]>
commit 4c7a1b9f1f1979f2b3b69428ada12680695159c2 upstream.
cvmx_wait_tx_rx() computes its 100us deadline from (u64)octeon_get_clock_rate - the address of the function, not its return value; the parentheses have been missing since the CVMX_WAIT_FOR_FIELD32 macro became a function. The cast makes it compile silently, and the resulting deadline is effectively infinite.
On a healthy controller the flush bit clears on the first read and nothing is noticed. On a controller whose PHY did not come up (for example when the reference-clock configuration is wrong for the board), txfflsh/rxfflsh never clear and probe spins forever in __delay() - observed as a hard hang with a soft-lockup splat on a CN5020 board, where the board watchdog then resets the system with no console output.
Call the function, restoring the 100us timeout the code always intended.
--- drivers/usb/host/octeon-hcd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/usb/host/octeon-hcd.c +1 -1
@@ -578,7 +578,7 @@ static int cvmx_wait_tx_rx(struct octeonint result;u64 address =CVMX_USBCX_GRSTCTL (usb->index);u64 done =cvmx_get_cycle () +100 * - (u64 )octeon_get_clock_rate /1000000 ; + (u64 )octeon_get_clock_rate () /1000000 ;union cvmx_usbcx_grstctl c;while (1 ) {
Greg Kroah-Hartman
maintainerPATCH 211/428 usb: ohci-da8xx: disable controller wakeup on cleanup
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Myeonghun Pak <[email protected]>
commit 901197dd4c7dcffbf97426487d4c047555c98554 upstream.
The OHCI core marks controllers with RemoteWakeupConnected as wakeup-capable. Probe enables wakeup, but neither removal nor a later notifier registration failure disables it, leaving the wakeup source attached.
Disable controller wakeup after removing the HCD on both paths.
This issue was identified during our ongoing static-analysis research while reviewing kernel code.
--- drivers/usb/host/ohci-da8xx.c | 2 ++ 1 file changed, 2 insertions(+)
drivers/usb/host/ohci-da8xx.c +2 -0
@@ -447,6 +447,7 @@ static int ohci_da8xx_probe(struct platf err_remove_hcd:usb_remove_hcd (hcd); +device_wakeup_disable (hcd->self.controller); err:usb_put_hcd (hcd);return error;
@@ -457,6 +458,7 @@ static void ohci_da8xx_remove(struct plastruct usb_hcd *hcd =platform_get_drvdata (pdev);usb_remove_hcd (hcd); +device_wakeup_disable (hcd->self.controller);usb_put_hcd (hcd); }
Greg Kroah-Hartman
maintainerPATCH 212/428 usb: ohci-s3c2410: disable controller wakeup on removal
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Myeonghun Pak <[email protected]>
commit 8dd8451c527e2a723bca20da5a6d724b8592551f upstream.
Probe enables controller wakeup after the OHCI core marks controllers with RemoteWakeupConnected as wakeup-capable. Removal does not undo this, so the wakeup source can remain attached after driver unbind.
Disable controller wakeup after removing the HCD.
This issue was identified during our ongoing static-analysis research while reviewing kernel code.
--- drivers/usb/host/ohci-s3c2410.c | 1 + 1 file changed, 1 insertion(+)
drivers/usb/host/ohci-s3c2410.c +1 -0
@@ -335,6 +335,7 @@ ohci_hcd_s3c2410_remove(struct platform_struct usb_hcd *hcd =platform_get_drvdata (dev);usb_remove_hcd (hcd); +device_wakeup_disable (hcd->self.controller);s3c2410_stop_hc (dev);usb_put_hcd (hcd); }
Greg Kroah-Hartman
maintainerPATCH 213/428 usb: ohci-spear: disable controller wakeup on removal
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Myeonghun Pak <[email protected]>
commit b1b0f8bdbbc891a4ee7f3b1a878c633e02b7bbeb upstream.
Probe enables controller wakeup after the OHCI core marks controllers with RemoteWakeupConnected as wakeup-capable. Removal does not undo this, so the wakeup source can remain attached after driver unbind.
Disable controller wakeup after removing the HCD.
This issue was identified during our ongoing static-analysis research while reviewing kernel code.
--- drivers/usb/host/ohci-spear.c | 1 + 1 file changed, 1 insertion(+)
drivers/usb/host/ohci-spear.c +1 -0
@@ -103,6 +103,7 @@ static void spear_ohci_hcd_drv_remove(ststruct spear_ohci *sohci_p =to_spear_ohci (hcd);usb_remove_hcd (hcd); +device_wakeup_disable (hcd->self.controller);clk_disable_unprepare (sohci_p->clk);usb_put_hcd (hcd);
Greg Kroah-Hartman
maintainerPATCH 214/428 usb: ohci-st: disable controller wakeup on removal
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Myeonghun Pak <[email protected]>
commit 08b2ccd7eb7e41f77a04a09112ca3edcc588e3f0 upstream.
Probe enables controller wakeup after the OHCI core marks controllers with RemoteWakeupConnected as wakeup-capable. Removal does not undo this, so the wakeup source can remain attached after driver unbind.
Disable controller wakeup after removing the HCD.
This issue was identified during our ongoing static-analysis research while reviewing kernel code.
--- drivers/usb/host/ohci-st.c | 1 + 1 file changed, 1 insertion(+)
drivers/usb/host/ohci-st.c +1 -0
@@ -235,6 +235,7 @@ static void st_ohci_platform_remove(struint clk;usb_remove_hcd (hcd); +device_wakeup_disable (hcd->self.controller);if (pdata->power_off) pdata->power_off (dev);
Greg Kroah-Hartman
maintainerPATCH 215/428 usb: gadget: midi2: Fix the jack descriptor array sizes
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Takashi Iwai <[email protected]>
commit 310c2d4608d8c1a2baa57db1dfd319c5c40cf4a1 upstream.
The jack in/out descriptor arrays in struct f_midi2_usb_config have the size of MAX_CABLES, which look reasonable -- but it turned out to be incorrect. Namely, the entries for those arrays are added from both inputs and outputs, hence for each loop cycle, it adds two, ended up with as twice as the expected size. This inconsistency may result in potential OOB when a large number of jacks are set up via configfs, although the max number of cables (the loop count) is limited to MAX_CABLES.
For addressing it, correct the jack_ins & jack_outs array sizes to twice, MAX_CABLES * 2.
--- drivers/usb/gadget/function/f_midi2.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/usb/gadget/function/f_midi2.c +2 -2
@@ -1633,8 +1633,8 @@ struct f_midi2_usb_config {/* MIDI 1.0 jacks */ unsigned char jack_in, jack_out, jack_id; -struct usb_midi_in_jack_descriptor jack_ins[MAX_CABLES ]; -struct usb_midi_out_jack_descriptor_1 jack_outs[MAX_CABLES ]; +struct usb_midi_in_jack_descriptor jack_ins[MAX_CABLES *2 ]; +struct usb_midi_out_jack_descriptor_1 jack_outs[MAX_CABLES *2 ]; };static int append_config (struct f_midi2_usb_config *config,void *d)
Greg Kroah-Hartman
maintainerPATCH 216/428 usb: gadget: f_uac1_legacy: validate bRequest index in generic_{set,get}_cmd
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Liu Chao <[email protected]>
commit 9062d50e75c24dc7911be05c9b1719b3b256af15 upstream.
generic_set_cmd() and generic_get_cmd() use the low nibble of ctrl->bRequest as an index into con->data[]:
u8 cmd = (ctrl->bRequest & 0x0F); /* 0 .. 15 */
...
con->data[cmd] = value; /* OOB when cmd >= 5 */struct usb_audio_control (include/linux/usb/audio.h) declares data as a 5-element array, so indices 5 through 15 write (or read) up to 44 bytes past the end of the array on the heap.
A malicious USB host can craft a class-specific SET_CUR / GET_CUR request with an arbitrary bRequest value, triggering the out-of-bounds access from an IRQ completion handler with no further preconditions.
Add an ARRAY_SIZE() guard to both functions.
--- drivers/usb/gadget/function/f_uac1_legacy.c | 6 ++++++ 1 file changed, 6 insertions(+)
drivers/usb/gadget/function/f_uac1_legacy.c +6 -0
@@ -797,6 +797,9 @@ fail:static int generic_set_cmd (struct usb_audio_control *con,u8 cmd,int value) { +if (cmd >=ARRAY_SIZE (con->data)) +return -EINVAL ; + con->data[cmd] = value;return 0 ;
@@ -804,6 +807,9 @@ static int generic_set_cmd(struct usb_austatic int generic_get_cmd (struct usb_audio_control *con,u8 cmd) { +if (cmd >=ARRAY_SIZE (con->data)) +return -EINVAL ; +return con->data[cmd]; }
Greg Kroah-Hartman
maintainerPATCH 217/428 usb: typec: anx7411 - stop the IRQ before destroying the workqueue
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Linkai Gong <[email protected]>
commit 469600d5d292b1482c90dd6853555821d11b8c5f upstream.
The IRQ is devm-managed, so it can still queue plat->work while remove() is tearing the queue down. Disable it first.
--- drivers/usb/typec/anx7411.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-)
drivers/usb/typec/anx7411.c +4 -1
@@ -1566,8 +1566,11 @@ static void anx7411_i2c_remove(struct i2anx7411_partner_unregister_altmode (plat);anx7411_unregister_partner (plat); -if (plat->workqueue) +if (plat->workqueue) { +disable_irq (plat->intp_irq); +cancel_work_sync (&plat->work);destroy_workqueue (plat->workqueue); + }i2c_unregister_device (plat->spi_client);
Greg Kroah-Hartman
maintainerPATCH 218/428 usb: typec: port-mapper: Only match USB4 port if host interface is available
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Marek Maslanka <[email protected]>
commit aef5a7e207656ad6abdeeaa0bfc8ee9a1f9c02f6 upstream.
typec_port_match() adds a component match for the USB4 port whenever a USB 3.x port that shares the _PLD with the Type-C connector has the "usb4-host-interface" property, regardless of whether a USB4 port can ever be registered for it. The component framework binds the aggregate device only once every match has found its component, so if the USB4 port never shows up, the USB 2.0 and USB 3.x ports are not linked to the connector either: the "connector" symlinks are never created and USB devices enumerated on those ports are never linked with the Type-C partner.
This happens in at least two cases:
1. CONFIG_USB4 is not reachable from the Type-C core, i.e. CONFIG_USB4=n, or CONFIG_USB4=m with CONFIG_TYPEC=y as in the x86_64 gki_defconfig. usb4_usb3_port_match() is then a stub that always returns false.
2. The firmware references a USB4 host interface that is disabled. For example, on Intel Alder Lake-N (ChromeOS Nissa) the TCSS xHCI USB3 ports (SS01-SS04) reference TDM0/TDM1, whose _STA returns 0 because the SoC has no integrated Thunderbolt/USB4 and the DMA controllers are disabled in TCSS DEVEN. No PCI device is enumerated for them.
Only add the USB4 component match if CONFIG_USB4 is reachable and the referenced host interface is available and has been enumerated as a device. The latter mirrors the check in usb_acpi_add_usb4_devlink(), see commit 623dae3e7084 ("usb: acpi: fix boot hang due to early incorrect 'tunneled' USB3 device links").
--- drivers/usb/typec/port-mapper.c | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-)
drivers/usb/typec/port-mapper.c +19 -2
@@ -42,6 +42,23 @@ static int usb4_port_compare(struct devireturn usb4_usb3_port_match (dev, fwnode); } +static bool typec_has_usb4_host_interface (const struct fwnode_handle *fwnode) +{ +if (!IS_REACHABLE (CONFIG_USB4 )) +return false; + + struct fwnode_handle *nhi_fwnode __free(fwnode_handle) = +fwnode_find_reference (fwnode,"usb4-host-interface" ,0 ); + +/* +* The USB4 port can only appear if the host interface is enabled in +* the firmware and has been enumerated as a device. The latter is +* the same check as in usb_acpi_add_usb4_devlink(). +*/ +return !IS_ERR (nhi_fwnode) &&fwnode_device_is_available (nhi_fwnode) && + nhi_fwnode->dev; +} +static int typec_port_compare (struct device *dev,void *fwnode) {return device_match_fwnode (dev, fwnode);
@@ -64,11 +81,11 @@ static int typec_port_match(struct devic adev_fwnode);/* -* If dev is USB 3.x port, it may have reference to the +* If dev is USB 3.x port, it may have reference to an available * USB4 host interface in which case we can also link the * Type-C port with the USB4 port. */ -if (fwnode_property_present (adev_fwnode,"usb4-host-interface" )) +if (typec_has_usb4_host_interface (adev_fwnode))component_match_add (&arg->port->dev, &arg->match, usb4_port_compare, adev_fwnode); }
Greg Kroah-Hartman
maintainerPATCH 219/428 usb: dwc3: am62: Fix runtime PM usage count leak on probe error path
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Radhey Shyam Pandey <[email protected]>
commit 3d9bbf0ffeb9b7ac1b6ad06b52e037870a7e0c63 upstream.
dwc3_ti_probe() takes a runtime PM reference with pm_runtime_get_noresume() before creating the dwc3 core child device, and releases it with pm_runtime_put_autosuspend() once probe has succeeded. The err_pm_disable error path only disables runtime PM, so the reference taken a few lines earlier is never dropped.
The usage counter lives in struct device and is not reset when the driver is unbound, so the leaked reference outlives the failed probe. If the device is probed again, through a manual rebind or a module reload, the counter starts at one instead of zero and the pm_runtime_put_autosuspend() on the success path can no longer bring it back down. The wrapper then stays runtime resumed for good and autosuspend never kicks in.
Drop the reference before disabling runtime PM, matching the ordering already used in dwc3_ti_remove().
--- drivers/usb/dwc3/dwc3-am62.c | 1 + 1 file changed, 1 insertion(+)
drivers/usb/dwc3/dwc3-am62.c +1 -0
@@ -297,6 +297,7 @@ static int dwc3_ti_probe(struct platform err_pm_disable:clk_disable_unprepare (am62->usb2_refclk); +pm_runtime_put_noidle (dev);pm_runtime_disable (dev);pm_runtime_set_suspended (dev);return ret;
Greg Kroah-Hartman
maintainerPATCH 220/428 usb: dwc3: gadget: fix IRQ storm on invalid event buffer count
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jiazi Liu <[email protected]>
commit 1ff77cbefe5a653ea12874c213ea4bd0d72c1067 upstream.
When dwc3_check_event_buf() reads a GEVNTCOUNT value exceeding the event buffer length, commit 63ccd26cd1f6 ("usb: dwc3: gadget: check that event count does not exceed event buffer length") returns IRQ_NONE without writing back GEVNTCOUNT. Since the DWC3 interrupt is level-triggered, the uncleared IRQ source keeps the line asserted, causing a tight IRQ storm that accumulates 99,900 unhandled interrupts and triggers spurious.c:184 BUG -> kernel panic.
The resulting call stack:
__report_bad_irq+0xac/0xc8 note_interrupt+0x340/0x468 handle_irq_event+0xac/0xc0 handle_fasteoi_irq+0x120/0x228 gic_handle_irq+0x68/0x108 ... kernel BUG at kernel/irq/spurious.c:184
To reproduce, write a bogus value exceeding the event buffer length directly to the GEVNTCOUNT register:
devmem <DWC3_BASE + 0xc40c> 4 0x1004
Write the bogus count back to GEVNTCOUNT to clear the IRQ source, consistent with the stale event clearing pattern in dwc3_event_buffers_setup(), and schedule error recovery to reinitialize the controller.
--- drivers/usb/dwc3/core.h | 19 ++++++++ drivers/usb/dwc3/ep0.c | 9 +++ drivers/usb/dwc3/gadget.c | 108 +++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 134 insertions(+), 2 deletions(-)
drivers/usb/dwc3/core.h +19 -0
@@ -49,6 +49,7 @@#define DWC3_ENDPOINTS_NUM 32#define DWC3_XHCI_RESOURCES_NUM 2#define DWC3_ISOC_MAX_RETRIES 5 +#define DWC3_ERR_RECOVERY_MAX 3#define DWC3_SCRATCHBUF_SIZE 4096/* each buffer is assumed to be 4KiB */ #define DWC3_EVENT_BUFFERS_SIZE 4096
@@ -842,6 +843,12 @@ enum dwc3_link_state { DWC3_LINK_STATE_MASK = 0x0f, }; +enum dwc3_err_state { +DWC3_ERR_NONE =0 , +DWC3_ERR_RECOVERY , +DWC3_ERR_UNRECOVERABLE , +}; +/* TRB Length, PCM and Status */ #define DWC3_TRB_SIZE_MASK (0x00ffffff)#define DWC3_TRB_SIZE_LENGTH (n) ((n) & DWC3_TRB_SIZE_MASK)
@@ -1005,6 +1012,7 @@ struct dwc3_glue_ops {/** * struct dwc3 - representation of our controller * @drd_work: workqueue used for role swapping +* @err_recovery_work: workqueue used for controller error recovery * @ep0_trb: trb which is used for the ctrl_req * @bounce: address of bounce buffer * @setup_buf: used while precessing STD USB requests
@@ -1014,6 +1022,7 @@ struct dwc3_glue_ops {* @ep0_in_setup: one control transfer is completed and enter setup phase * @lock: for synchronizing * @mutex: for mode switching +* @connect_mutex: for the pull-up and err_recovery_work * @dev: pointer to our struct device * @sysdev: pointer to the DMA-capable device * @xhci: pointer to our xHCI child
@@ -1078,6 +1087,9 @@ struct dwc3_glue_ops {* @ep0_next_event: hold the next expected event * @ep0state: state of endpoint zero * @link_state: link state +* @err_state: current error recovery state. +* @err_recovery_count: number of consecutive error recovery attempts until +* confirmed healthy and reset to 0 on reset event. * @speed: device speed (super, high, full, low) * @hwparams: copy of hwparams registers * @regset: debugfs pointer to regdump file
@@ -1188,6 +1200,7 @@ struct dwc3_glue_ops {*/ struct dwc3 {struct work_struct drd_work; +struct work_struct err_recovery_work;struct dwc3_trb *ep0_trb;void *bounce;u8 *setup_buf;
@@ -1202,6 +1215,9 @@ struct dwc3 {/* mode switching lock */ struct mutex mutex; +/* serializes pull-up run/stop vs error recovery */ +struct mutex connect_mutex; +struct device *dev;struct device *sysdev;
@@ -1327,6 +1343,9 @@ struct dwc3 {enum dwc3_ep0_next ep0_next_event;enum dwc3_ep0_state ep0state;enum dwc3_link_state link_state; +enum dwc3_err_state err_state; + +u32 err_recovery_count;u16 u2sel;u16 u2pel;
drivers/usb/dwc3/ep0.c +9 -0
@@ -200,6 +200,11 @@ int dwc3_gadget_ep0_queue(struct usb_epint ret;spin_lock_irqsave (&dwc->lock, flags); +if (dwc->err_state !=DWC3_ERR_NONE ) { + ret = -ESHUTDOWN ; + goto out; + } +if (!dep->endpoint.desc || !dwc->pullups_connected || !dwc->connected) {dev_err (dwc->dev,"%s: can't queue to disabled endpoint\n" , dep->name);
@@ -271,6 +276,10 @@ int dwc3_gadget_ep0_set_halt(struct usb_int ret;spin_lock_irqsave (&dwc->lock, flags); +if (dwc->err_state !=DWC3_ERR_NONE ) { +spin_unlock_irqrestore (&dwc->lock, flags); +return -ESHUTDOWN ; + } ret =__dwc3_gadget_ep0_set_halt (ep, value);spin_unlock_irqrestore (&dwc->lock, flags);
drivers/usb/dwc3/gadget.c +106 -2
191 lines not on this page · raw message
Greg Kroah-Hartman
maintainerPATCH 221/428 usb: gadget: aspeed-vhub: cancel wake work on device removal
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Fan Wu <[email protected]>
commit 20388c8d1d74c203fec8194c45cd31afdfab934e upstream.
wake_work is armed from the gadget .wakeup callback to resume suspended downstream ports, and it is never cancelled in ast_vhub_remove(), so a work item queued before or during removal can run after devm has freed vhub and its ports, a use-after-free.
Cancelling the work alone is not sufficient: usb_gadget_wakeup() takes no lock and the unbind path never clears wakeup_en, so a remote-wakeup request in flight on another CPU can re-arm the work after the cancel and before vhub is freed.
ast_vhub_del_dev() already clears d->registered under vhub->lock before unregistering the gadget. Refuse the wakeup in ast_vhub_udc_wakeup() once d->registered is clear: the check and the schedule_work() are then atomic against the unbind, so a wakeup that passed before the flag was cleared is drained by the cancel_work_sync() after the del_dev loop, and one that arrives later returns without arming. Also move INIT_WORK() to the top of ast_vhub_probe(), since the probe error path reuses ast_vhub_remove() and would otherwise cancel a never-initialized work item.
This issue was found by an in-house static analysis tool.
--- drivers/usb/gadget/udc/aspeed-vhub/core.c | 4 ++++ drivers/usb/gadget/udc/aspeed-vhub/dev.c | 2 +- drivers/usb/gadget/udc/aspeed-vhub/hub.c | 5 +++-- drivers/usb/gadget/udc/aspeed-vhub/vhub.h | 1 + 4 files changed, 9 insertions(+), 3 deletions(-)
drivers/usb/gadget/udc/aspeed-vhub/core.c +4 -0
@@ -267,6 +267,9 @@ static void ast_vhub_remove(struct platffor (i =0 ; i < vhub->max_ports; i++)ast_vhub_del_dev (&vhub->ports[i].dev); +/* Final drain; the worker takes vhub->lock, so stay outside of it */ +cancel_work_sync (&vhub->wake_work); +spin_lock_irqsave (&vhub->lock, flags);/* Mask & ack all interrupts */
@@ -328,6 +331,7 @@ static int ast_vhub_probe(struct platforreturn -ENOMEM ;spin_lock_init (&vhub->lock); +INIT_WORK (&vhub->wake_work, ast_vhub_wake_work); vhub->pdev = pdev; vhub->port_irq_mask =GENMASK (VHUB_IRQ_DEV1_BIT + vhub->max_ports -1 ,VHUB_IRQ_DEV1_BIT );
drivers/usb/gadget/udc/aspeed-vhub/dev.c +1 -1
@@ -280,7 +280,7 @@ static int ast_vhub_udc_wakeup(struct usint rc = -EINVAL ;spin_lock_irqsave (&d->vhub->lock, flags); -if (!d->wakeup_en) +if (!d->wakeup_en || !d->registered) goto err;DDBG (d,"Device initiated wakeup\n" );
drivers/usb/gadget/udc/aspeed-vhub/hub.c +3 -2
@@ -558,7 +558,7 @@ void ast_vhub_device_connect(struct ast_ast_vhub_send_host_wakeup (vhub); } -static void ast_vhub_wake_work (struct work_struct *work) +void ast_vhub_wake_work (struct work_struct *work) { struct ast_vhub *vhub = container_of(work, struct ast_vhub,
@@ -588,6 +588,8 @@ static void ast_vhub_wake_work(struct wovoid ast_vhub_hub_wake_all (struct ast_vhub *vhub) { +lockdep_assert_held (&vhub->lock); +/* * A device is trying to wake the world, because this * can recurse into the device, we break the call chain
@@ -1076,7 +1078,6 @@ static int ast_vhub_init_desc(struct astint ast_vhub_init_hub (struct ast_vhub *vhub) { vhub->speed =USB_SPEED_UNKNOWN ; -INIT_WORK (&vhub->wake_work, ast_vhub_wake_work);return ast_vhub_init_desc (vhub); }
drivers/usb/gadget/udc/aspeed-vhub/vhub.h +1 -0
@@ -547,6 +547,7 @@ void ast_vhub_hub_suspend(struct ast_vhuvoid ast_vhub_hub_resume (struct ast_vhub *vhub);void ast_vhub_hub_reset (struct ast_vhub *vhub);void ast_vhub_hub_wake_all (struct ast_vhub *vhub); +void ast_vhub_wake_work (struct work_struct *work);/* dev.c */ int ast_vhub_init_dev (struct ast_vhub *vhub,unsigned int idx);
Greg Kroah-Hartman
maintainerPATCH 222/428 USB: gadget: dummy-hcd: Fix wait for outstanding request completions
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Alan Stern <[email protected]>
commit b263ff9b0fc5c1f371d65c4eb196b31263d039ff upstream.
The dummy-hcd driver emulates synchronize_irq() by waiting until its private callback_usage counter drops to 0 (with the private lock not held). This counter is incremented whenever a gadget driver callback occurs (which requires the private lock to be dropped), but not when a request completion handler is called.
This is an oversight. Request completion is triggered by timer interrupts (emulating device IRQs in a real UDC), and the interrupt handlers are supposed to have completed when the synchronize_irq() emulation routine returns -- they aren't supposed to be in the middle of a completion callback. If this happens it can lead to a gadget driver's unbind routine running before all outstanding request completions have finished, maybe even allowing the gadget driver's module to be unloaded while a completion handler is still running.
Fix the oversight by incrementing the callback_usage value across request completion callbacks.
--- drivers/usb/gadget/udc/dummy_hcd.c | 4 ++++ 1 file changed, 4 insertions(+)
drivers/usb/gadget/udc/dummy_hcd.c +4 -0
@@ -343,9 +343,11 @@ static void dummy_giveback(struct dummy {bool fifo = req == &dum->fifo_req; + ++dum->callback_usage;spin_unlock (&dum->lock);usb_gadget_giveback_request (_ep, &req->req);spin_lock (&dum->lock); + --dum->callback_usage;if (fifo) dum->fifo_req_busy =0 ; }
@@ -759,11 +761,13 @@ static int dummy_queue(struct usb_ep *_e req->req.complete = fifo_complete;list_add_tail (&req->queue, &ep->queue); + ++dum->callback_usage;spin_unlock (&dum->lock); _req->actual = _req->length; _req->status =0 ;usb_gadget_giveback_request (_ep, _req);spin_lock (&dum->lock); + --dum->callback_usage; }else list_add_tail (&req->queue, &ep->queue);spin_unlock_irqrestore (&dum->lock, flags);
Greg Kroah-Hartman
maintainerPATCH 223/428 usb: gadget: f_fs: Fix NULL pointer dereference in FUNCTIONFS_ENDPOINT_DESC
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Yuchao Zhang <[email protected]>
commit 9cd072788c76595529eb38f42bf94d23852f8534 upstream.
When user space sets up FunctionFS with only full-speed descriptors (e.g., flags = FUNCTIONFS_HAS_FS_DESC) and the gadget operates at a higher speed (such as USB_SPEED_HIGH or USB_SPEED_SUPER), config_ep_by_speed() detects that the function lacks descriptors for the current speed, logs a warning, and falls back to full-speed descriptors. The endpoint is then successfully enabled via usb_ep_enable(), and epfile->ep becomes valid.
However, when user space subsequently issues the FUNCTIONFS_ENDPOINT_DESC ioctl to query the endpoint descriptor, ffs_epfile_ioctl() selects the descriptor index solely based on gadget->speed:
switch (epfile->ffs->gadget->speed) {
case USB_SPEED_SUPER:
case USB_SPEED_SUPER_PLUS:
desc_idx = 2;
break;
case USB_SPEED_HIGH:
desc_idx = 1;
break;
default:
desc_idx = 0;
}
desc = epfile->ep->descs[desc_idx];
memcpy(&desc1, desc, desc->bLength);Because user space only supplied full-speed descriptors, epfile->ep->descs[1] is NULL. Dereferencing desc->bLength triggers an immediate kernel NULL pointer dereference panic.
Fix this by falling back through lower speed descriptors if the current speed descriptor was not supplied, matching the fallback logic in config_ep_by_speed() and historical f_fs behavior. If still missing, fall back to epfile->ep->ep->desc (the active descriptor), and return -EINVAL safely if no valid descriptor is found. In addition, mark desc as const to match struct usb_ep::desc and avoid discarding qualifiers.
--- drivers/usb/gadget/function/f_fs.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-)
drivers/usb/gadget/function/f_fs.c +13 -2
@@ -1844,8 +1844,9 @@ static long ffs_epfile_ioctl(struct filebreak ;case FUNCTIONFS_ENDPOINT_DESC : { +const struct usb_endpoint_descriptor *desc; +struct usb_endpoint_descriptor desc1;int desc_idx; -struct usb_endpoint_descriptor desc1, *desc;switch (epfile->ffs->gadget->speed) {case USB_SPEED_SUPER :
@@ -1859,7 +1860,17 @@ static long ffs_epfile_ioctl(struct file desc_idx =0 ; } - desc = epfile->ep->descs[desc_idx]; +do { + desc = epfile->ep->descs[desc_idx]; + }while (!desc && --desc_idx >=0 ); + +if (!desc) + desc = epfile->ep->ep->desc; +if (!desc) { + ret = -EINVAL ; +break ; + } +memcpy (&desc1, desc, desc->bLength);spin_unlock_irq (&epfile->ffs->eps_lock);
Greg Kroah-Hartman
maintainerPATCH 224/428 usb: chipidea: tegra: disable runtime PM on resume failure
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Myeonghun Pak <[email protected]>
commit f1d6f7d25fe88ee49aaad96c8649e090fc3a4b68 upstream.
tegra_usb_probe() enables runtime PM before resuming the device. If pm_runtime_resume_and_get() fails, probe returns without disabling runtime PM. The later error paths reach pm_runtime_force_suspend(), but this early return bypasses that cleanup.
Disable runtime PM before returning the resume error. Do not use the fail_power_off path: the failed resume did not retain a usage reference, so its pm_runtime_put_sync_suspend() would be unbalanced.
This issue was identified during our ongoing static-analysis research while reviewing kernel code.
--- drivers/usb/chipidea/ci_hdrc_tegra.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
drivers/usb/chipidea/ci_hdrc_tegra.c +3 -1
@@ -306,8 +306,10 @@ static int tegra_usb_probe(struct platfopm_runtime_enable (&pdev->dev); err =pm_runtime_resume_and_get (&pdev->dev); -if (err) +if (err) { +pm_runtime_disable (&pdev->dev);return err; + }if (device_property_present (&pdev->dev,"nvidia,needs-double-reset" )) usb->needs_double_reset = true;
Greg Kroah-Hartman
maintainerPATCH 225/428 usb: core: clear both ep_in and ep_out for non-ep0 control endpoints
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Hui Peng <[email protected]>
commit 0e8baa78d872f2db440d8b9ded946ab8dceb89eb upstream.
In usb_enable_endpoint(), non-zero control endpoints (where usb_endpoint_xfer_control(&ep->desc) is true) populate both dev->ep_in[epnum] and dev->ep_out[epnum] with the same struct usb_host_endpoint pointer.
However, usb_disable_endpoint() only clears either dev->ep_out[epnum] or dev->ep_in[epnum] depending on the direction bit of epaddr when reset_hardware is set, leaving the opposite direction's array slot pointing to the disabled/freed endpoint.
Clear both dev->ep_out[epnum] and dev->ep_in[epnum] when disabling a non-ep0 control endpoint with reset_hardware set, and update the function kerneldoc accordingly.
Tested in QEMU against Linux 7.3.0-rc3 using dummy_hcd and raw-gadget to enumerate a USB device with a non-zero control endpoint (bEndpointAddress 0x01, bmAttributes USB_ENDPOINT_XFER_CONTROL), verifying that both dev->ep_in[1] and dev->ep_out[1] are cleared when the interface is disabled.
--- drivers/usb/core/message.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-)
drivers/usb/core/message.c +7 -1
@@ -1337,7 +1337,8 @@ static void remove_intf_ep_devs(struct u* * Disables the endpoint for URB submission and nukes all pending URBs. * If @reset_hardware is set then also deallocates hcd/hardware state -* for the endpoint. +* for the endpoint (clearing both ep_in and ep_out pointers for +* bidirectional non-ep0 control endpoints). */ void usb_disable_endpoint (struct usb_device *dev,unsigned int epaddr,bool reset_hardware)
@@ -1358,6 +1359,11 @@ void usb_disable_endpoint(struct usb_dev dev->ep_in[epnum] =NULL ; }if (ep) { +if (reset_hardware && epnum !=0 && +usb_endpoint_xfer_control (&ep->desc)) { + dev->ep_out[epnum] =NULL ; + dev->ep_in[epnum] =NULL ; + } ep->enabled =0 ;usb_hcd_flush_endpoint (dev, ep);if (reset_hardware)
Greg Kroah-Hartman
maintainerPATCH 226/428 USB: dwc2: shut down wakeup timer before freeing HCD state
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Runyu Xiao <[email protected]>
commit fd772af9512cdecb83de34a461801533828e77fd upstream.
dwc2_wakeup_detected() accesses the DWC2 host state and can rearm the wakeup timer. dwc2_hcd_free() currently deletes the timer only after freeing host-owned state, and timer_delete() does not synchronize a callback or prevent it from being queued again.
Stop and shut down the timer in dwc2_hcd_release(), before the HCD resources are freed. This covers both the HCD initialization error path and normal HCD removal.
--- drivers/usb/dwc2/hcd.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-)
drivers/usb/dwc2/hcd.c +1 -2
@@ -5082,14 +5082,13 @@ static void dwc2_hcd_free(struct dwc2_hs }cancel_work_sync (&hsotg->phy_reset_work); - -timer_delete (&hsotg->wkp_timer); }static void dwc2_hcd_release (struct dwc2_hsotg *hsotg) {/* Turn off all host-specific interrupts */ dwc2_disable_host_interrupts (hsotg); +timer_shutdown_sync (&hsotg->wkp_timer);dwc2_hcd_free (hsotg); }
Greg Kroah-Hartman
maintainerPATCH 227/428 usb: typec: tcpm: recover after failure to start the frs ams
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Amit Sunil Dhamne <[email protected]>
commit 90156585765b29304f07b0e4a1c56a969876fcfc upstream.
Reset the port if the tcpm fails to start the FAST_ROLE_SWAP AMS by initiating error recovery on it.
This helps in cases where some cables (incorrectly) signal an FRS to an FRS capable port during disconnection. The TCPC autonomously starts sourcing VBUS on detecting the FRS signal. However, the VBUS sourcing is left on when the FAST_ROLE_SWAP AMS fails to start (as tcpm_sink_tx_ok is 0 as CC is open due to the cable disconnect). This is because the code sets the state to INVALID_STATE without resetting the port state.
Log snippet before changes:
[ 101.401960] AMS FAST_ROLE_SWAP start [ 101.401971] Sink TX No Go [ 101.401982] sourcing vbus [ 101.401987] VBUS on [ 101.402159] VBUS on [ 109.257809] CC1: 0 -> 0, CC2: 5 -> 0 [state SNK_READY, polarity 1, disconnected] [ 111.267442] VBUS on
After changes:
[ 70.541211] AMS FAST_ROLE_SWAP start [ 70.541220] Sink TX No Go [ 70.541228] state change SNK_READY -> ERROR_RECOVERY [rev3 NONE_AMS] [ 70.541362] VBUS on [ 70.541365] sourcing vbus [ 70.541367] VBUS on [ 70.541374] state change ERROR_RECOVERY -> PORT_RESET [rev3 NONE_AMS] [ 70.541410] disable vbus discharge ret:0 [ 70.543028] Setting usb_comm capable false [ 70.544009] Setting voltage/current limit 0 mV 0 mA [ 70.544034] polarity 0 [ 70.544239] Requesting mux state 0, usb-role 0, orientation 0 [ 70.555550] cc:=0 [ 70.555595] pending state change PORT_RESET -> PORT_RESET_WAIT_OFF @ 100 ms [rev3 NONE_AMS] [ 70.555697] VBUS off [ 70.555702] VBUS VSAFE0V [ 70.555762] CC1: 5 -> 0, CC2: 0 -> 0 [state PORT_RESET, polarity 0, disconnected] [ 70.587794] VBUS off [ 70.587799] VBUS VSAFE0V [ 70.655672] state change PORT_RESET -> PORT_RESET_WAIT_OFF [delayed 100 ms] [ 70.655682] state change PORT_RESET_WAIT_OFF -> SNK_UNATTACHED [rev3 NONE_AMS] [ 70.655686] Start toggling [ 70.656274] CC1: 0 -> 0, CC2: 0 -> 0 [state TOGGLING, polarity 0, disconnected]
--- drivers/usb/typec/tcpm/tcpm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/usb/typec/tcpm/tcpm.c +1 -1
@@ -6915,7 +6915,7 @@ static void tcpm_pd_event_handler(struct port->upcoming_state =FR_SWAP_SEND ; ret =tcpm_ams_start (port,FAST_ROLE_SWAP );if (ret == -EAGAIN ) - port->upcoming_state =INVALID_STATE ; +tcpm_set_state (port,ERROR_RECOVERY ,0 ); }else {tcpm_log (port,"Discarding FRS_SIGNAL! Not in sink ready" ); }
Greg Kroah-Hartman
maintainerPATCH 228/428 usb: typec: tipd: dont send GAID when probe fails in APP mode
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Marian Rotariu <[email protected]>
commit c5df31f10880737d13b75eff42faee7accb9f956 upstream.
When probe fails after the chip was already in APP mode (booted from EEPROM), the err_reset_controller path unconditionally calls tps->data->reset(), which for tps25750 sends the GAID 4CC command. GAID transitions the chip from APP back to PTCH, causing a second probe attempt that sees PTCH mode and tries to load the firmware from /lib/firmware, instead of reading the EEPROM as it should.
The err_reset_controller path can be triggered by -EPROBE_DEFER from the probe sequence. GAID should only be sent when the controller actually loaded firmware at probe time.
The abnormal behavior was seen on a tps25751d that has an EEPROM connected to it.
--- drivers/usb/typec/tipd/core.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-)
drivers/usb/typec/tipd/core.c +4 -1
@@ -1752,6 +1752,7 @@ static int tps6598x_probe(struct i2c_cliconst struct tipd_data *data;struct tps6598x *tps;struct fwnode_handle *fwnode; +bool patch_loaded = false;u32 status;u32 vid;int ret;
@@ -1804,6 +1805,7 @@ static int tps6598x_probe(struct i2c_clireturn ret;if (ret ==TPS_MODE_PTCH ) { + patch_loaded = true; ret = tps->data->init (tps);if (ret)return ret;
@@ -1892,7 +1894,8 @@ err_clear_mask:tps6598x_write64 (tps,TPS_REG_INT_MASK1 ,0 ); err_reset_controller:/* Reset PD controller to remove any applied patch */ - tps->data->reset (tps); +if (patch_loaded) + tps->data->reset (tps);return ret; }
Greg Kroah-Hartman
maintainerPATCH 229/428 usb: typec: tipd: fix uninitialized typec_partner_desc in tps6598x_connect()
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Radhey Shyam Pandey <[email protected]>
commit c4b27b3605788bbf9e4251f3cd4ab94645ab036f upstream.
tps6598x_connect() passes a stack-allocated typec_partner_desc to typec_register_partner() after initializing only usb_pd, accessory and identity.
typec_register_partner() copies attach and deattach from the descriptor into the partner. With those fields left unset, garbage function pointers may be stored and later invoked from typec_partner_link_device() when a USB device is linked to the port. Uninitialized pd_revision and usb_capability similarly leak stack data through partner sysfs.
Zero-initialize the descriptor so optional callbacks remain NULL and the remaining fields are zero.
--- drivers/usb/typec/tipd/core.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-)
drivers/usb/typec/tipd/core.c +1 -2
@@ -340,7 +340,7 @@ static void tps6598x_set_data_role(strucstatic int tps6598x_connect (struct tps6598x *tps,u32 status) { -struct typec_partner_desc desc; +struct typec_partner_desc desc = { };enum typec_pwr_opmode mode;int ret;
@@ -351,7 +351,6 @@ static int tps6598x_connect(struct tps65 desc.usb_pd = mode ==TYPEC_PWR_MODE_PD ; desc.accessory =TYPEC_ACCESSORY_NONE ;/* XXX: handle accessories */ - desc.identity =NULL ;if (desc.usb_pd) { ret =tps6598x_read_partner_identity (tps);
Greg Kroah-Hartman
maintainerPATCH 230/428 usb: typec: tipd: fix uninitialized typec_partner_desc in cd321x_update_work()
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Radhey Shyam Pandey <[email protected]>
commit 88c7e458af53ff781a3f1051708395892b325576 upstream.
cd321x_update_work() passes a stack-allocated typec_partner_desc to typec_register_partner() after initializing only usb_pd, accessory and identity.
typec_register_partner() copies attach and deattach from the descriptor into the partner. With those fields left unset, garbage function pointers may be stored and later invoked from typec_partner_link_device() when a USB device is linked to the port. Uninitialized pd_revision and usb_capability similarly leak stack data through partner sysfs.
Zero-initialize the descriptor so optional callbacks remain NULL and the remaining fields are zero.
--- drivers/usb/typec/tipd/core.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-)
drivers/usb/typec/tipd/core.c +1 -2
@@ -818,11 +818,10 @@ static void cd321x_update_work(struct wo/* Set up partner if we were previously disconnected (or changed). */ if (!tps->partner) { -struct typec_partner_desc desc; +struct typec_partner_desc desc = { }; desc.usb_pd = is_pd; desc.accessory =TYPEC_ACCESSORY_NONE ;/* XXX: handle accessories */ - desc.identity =NULL ;if (desc.usb_pd) desc.identity = &st.partner_identity;
Greg Kroah-Hartman
maintainerPATCH 231/428 usb: typec: ucsi: glink: fix use-after-free of ucsi on remove
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Fan Wu <[email protected]>
commit 63b269ec537dc0fd26d0bd64872c26860d108c9d upstream.
The pmic_glink client is released by the devres cleanup, which runs after pmic_glink_ucsi_remove() has returned, so its notification callbacks can queue work until then. Work that runs after ucsi_unregister() has been called touches freed state: it dereferences the connector array that ucsi_unregister() freed, or registers and unregisters the instance a second time.
Disable both work items with disable_work_sync() before unregistering, and unregister only if the instance is still registered.
This issue was found by an in-house static analysis tool.
--- drivers/usb/typec/ucsi/ucsi_glink.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-)
drivers/usb/typec/ucsi/ucsi_glink.c +13 -2
@@ -362,6 +362,10 @@ static void pmic_glink_ucsi_destroy(void {struct pmic_glink_ucsi *ucsi = data; +/* Drain the work items before ucsi_destroy() frees the ucsi instance */ +cancel_work_sync (&ucsi->notify_work); +cancel_work_sync (&ucsi->register_work); +/* Protect to make sure we're not in a middle of a transaction from a glink callback */ mutex_lock (&ucsi->lock);ucsi_destroy (ucsi->ucsi);
@@ -467,8 +471,15 @@ static void pmic_glink_ucsi_remove(struc {struct pmic_glink_ucsi *ucsi =dev_get_drvdata (&adev->dev); -/* Unregister first to stop having read & writes */ -ucsi_unregister (ucsi->ucsi); +/* Callbacks can queue work until devres releases the client */ +disable_work_sync (&ucsi->notify_work); +disable_work_sync (&ucsi->register_work); + +/* register_work may have unregistered the instance already */ +if (ucsi->ucsi_registered) { + ucsi->ucsi_registered = false; +ucsi_unregister (ucsi->ucsi); + } }static const struct auxiliary_device_id pmic_glink_ucsi_id_table[] = {
Greg Kroah-Hartman
maintainerPATCH 232/428 usb: typec: ucsi: acpi: Assume UCSI 1.2 on Acer Nitro ANV15-41
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Pannarat Wiriyaarritham <[email protected]>
commit 8c51ea651d043c786c37668c51624bd6b338a0ef upstream.
The Acer Nitro ANV15-41 exposes a functional UCSI ACPI PPM but reports a UCSI VERSION value of zero.
ucsi_register() rejects a zero version with -ENODEV, leaving the system without registered USB Type-C connectors.
The PPM works correctly when using the UCSI 1.2 layout. With UCSI 1.2 assumed, both Type-C connectors register correctly and USB Power Delivery negotiation works.
Add a DMI-specific UCSI operation for the Acer Nitro ANV15-41 that substitutes UCSI 1.2 only when firmware reports a zero version. Preserve any valid non-zero firmware version.
Tested on an Acer Nitro ANV15-41 with BIOS V1.51.
--- drivers/usb/typec/ucsi/ucsi_acpi.c | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+)
drivers/usb/typec/ucsi/ucsi_acpi.c +34 -0
@@ -121,6 +121,33 @@ static const struct ucsi_operations ucsi .async_control = ucsi_acpi_async_control }; +static int ucsi_acer_read_version (struct ucsi *ucsi,u16 *version) +{ +struct ucsi_acpi *ua =ucsi_get_drvdata (ucsi); +int ret; + + ret =ucsi_acpi_read_version (ucsi, version); +if (ret) +return ret; + +if (!*version) { +dev_warn (ua->dev,"UCSI version is zero, assuming 1.2\n" ); + *version =UCSI_VERSION_1_2 ; + } + +return 0 ; +} + +static const struct ucsi_operations ucsi_acer_ops = { + .read_version = ucsi_acer_read_version, + .read_cci = ucsi_acpi_read_cci, + .poll_cci = ucsi_acpi_poll_cci, + .read_message_in = ucsi_acpi_read_message_in, + .write_message_out = ucsi_acpi_write_message_out, + .sync_control = ucsi_sync_control_common, + .async_control = ucsi_acpi_async_control +}; +static int ucsi_gram_sync_control (struct ucsi *ucsi,u64 command,u32 *cci,void *val,size_t len,void *msg_out,size_t msg_out_size)
@@ -164,6 +191,13 @@ static const struct ucsi_operations ucsistatic const struct dmi_system_id ucsi_acpi_quirks[] = { { .matches = { +DMI_MATCH (DMI_SYS_VENDOR ,"Acer" ), +DMI_MATCH (DMI_PRODUCT_NAME ,"Nitro ANV15-41" ), + }, + .driver_data = (void *)&ucsi_acer_ops, + }, + { + .matches = {DMI_MATCH (DMI_SYS_VENDOR ,"LG Electronics" ),DMI_MATCH (DMI_PRODUCT_FAMILY ,"LG gram PC" ),DMI_MATCH (DMI_PRODUCT_NAME ,"90Q" ),
Greg Kroah-Hartman
maintainerPATCH 233/428 usb: typec: ucsi: Get the connector fwnode based on reg value
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Prashanth K <[email protected]>
commit 6c51f09c6db5868a5090e8dd4d7764660c87f1ad upstream.
ucsi_find_fwnode() currently maps UCSI connectors to Device tree connector nodes based on the order in which connector child nodes are described in DT.
This can fail because the ordering of child nodes isn't guaranteed in Device-tree. For example, DTB may contain connector@1 before connector@0, causing connector numbers to be associated with the wrong fwnode. As a result, role switch and Type-C notifications can be delivered to the wrong remote endpoints.
Fix this by using the "reg" property of each connector to match its corresponding fwnode. While at it, if the reg property isn't present, then fall back to the old method.
--- drivers/usb/typec/ucsi/ucsi.c | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-)
drivers/usb/typec/ucsi/ucsi.c +14 -3
@@ -1801,10 +1801,21 @@ static struct fwnode_handle *ucsi_find_f {struct fwnode_handle *fwnode;int i =1 ; +int ret; +u32 port; -device_for_each_child_node (con->ucsi->dev, fwnode) -if (i++ == con->num) -return fwnode; +device_for_each_child_node (con->ucsi->dev, fwnode) { + ret =fwnode_property_read_u32 (fwnode,"reg" , &port); +if (ret <0 ) { +if (i == con->num) +return fwnode; + }else { +if (port == con->num -1 ) +return fwnode; + } + + i++; + }return NULL ; }
Greg Kroah-Hartman
maintainerPATCH 234/428 usb: typec: ucsi: displayport: Current CAM OOB index fixup
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jameson Thies <[email protected]>
commit ea31c6498c61307dc0d8ce338a7f268cb8cbdd57 upstream.
Update ucsi_displayport_enter() to return invalid error when the PPM reports an current alternate mode greater than or equal to UCSI_MAX_ALTMODES. While here, remove 0xff current cam assignment on failed GET_CURRENT_CAM response.
--- drivers/usb/typec/ucsi/displayport.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-)
drivers/usb/typec/ucsi/displayport.c +6 -3
@@ -71,11 +71,14 @@ static int ucsi_displayport_enter(structif (ret <0 ) {if (ucsi->version >0x0100 ) goto err_unlock; - cur =0xff ; } -if (cur <UCSI_MAX_ALTMODES ) { - ret = dp->con->port_altmode[cur] == alt ?0 : -EBUSY ; +if (cur !=0xff ) { +if (cur <UCSI_MAX_ALTMODES ) + ret = dp->con->port_altmode[cur] == alt ?0 : -EBUSY ; +else + ret = -EINVAL ; + goto err_unlock; }
Greg Kroah-Hartman
maintainerPATCH 235/428 usb: cdns3: fix use-after-free in cdns3_gadget_exit()
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Fan Wu <[email protected]>
commit ebe9b68c396792c15d0ac690d592e449e74ed804 upstream.
cdns3_gadget_start() arms two works on system_freezable_wq:pending_status_wq for the deferred ep0 status stage and aligned_buf_wq for realigned request buffers. Both handlers use the cdns3_device the works are embedded in, and cdns3_pending_setup_status_handler() also calls the ep0 request completion.
cdns3_gadget_exit() does not wait for these works. It frees all endpoints and aligned buffers and drops the last reference to the gadget device, which frees priv_dev, so a work queued before the exit can run after the free.
Fix this by waiting for both works after the gadget driver is unbound and the IRQ is freed, when no new work can be queued, and before the endpoints and buffers are released.
This issue was found by an in-house static analysis tool.
--- drivers/usb/cdns3/cdns3-gadget.c | 4 ++++ 1 file changed, 4 insertions(+)
drivers/usb/cdns3/cdns3-gadget.c +4 -0
@@ -3269,6 +3269,10 @@ static void cdns3_gadget_exit(struct cdnusb_del_gadget (&priv_dev->gadget);devm_free_irq (cdns->dev, cdns->dev_irq, priv_dev); +/* The works can still be queued until the IRQ is freed. */ +cancel_work_sync (&priv_dev->pending_status_wq); +cancel_work_sync (&priv_dev->aligned_buf_wq); +cdns3_free_all_eps (priv_dev);while (!list_empty (&priv_dev->aligned_buf_list)) {
Greg Kroah-Hartman
maintainerPATCH 236/428 usb: cdns3: Fix NULL pointer dereference in cdns3_pci_probe
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jie Deng <[email protected]>
commit e5052f2c5c73c1dcca42bafc0bc737af2b2af059 upstream.
The Cadence USBSS controller is a two-function PCI device. The first probed function allocates the driver data and stores it with pci_set_drvdata(), while the second function reuses it via pci_get_drvdata() when pci_is_enabled() reports that the first function has already been probed.
When the second function is probed while the first one has been enabled but has not yet set its driver data, pci_get_drvdata() returns NULL, and the subsequent wrap->devfn assignment dereferences a NULL pointer and crashes the kernel.
logs:
Call trace: cdns3_pci_probe+0xa4/0x300 local_pci_probe+0x44/0xa8 pci_call_probe+0x54/0x158 pci_device_probe+0x84/0x100 really_probe+0x184/0x3d0 __driver_probe_device+0x80/0x178 driver_probe_device+0x44/0xe8 __driver_attach+0xec/0x1f8 bus_for_each_dev+0x7c/0xe0 driver_attach+0x28/0x38 bus_add_driver+0x110/0x238 driver_register+0x64/0x128 __pci_register_driver+0x50/0x60 cdns3_pci_driver_init+0x28/0x38 do_one_initcall+0x5c/0x280 do_initcalls+0x104/0x1d8 kernel_init_freeable+0x140/0x218 kernel_init+0x28/0x1f8 ret_from_fork+0x10/0x20
Return -EPROBE_DEFER in this case so that probing is retried after the first function has completed its probe.
--- drivers/usb/cdns3/cdns3-pci-wrap.c | 5 +++++ 1 file changed, 5 insertions(+)
drivers/usb/cdns3/cdns3-pci-wrap.c +5 -0
@@ -96,6 +96,11 @@ static int cdns3_pci_probe(struct pci_deif (pci_is_enabled (func)) { wrap =pci_get_drvdata (func); +if (!wrap) { +dev_err (&pdev->dev, +"second function not initialized, retrying\n" ); +return -EPROBE_DEFER ; + } }else { wrap =kzalloc_obj (*wrap);if (!wrap)
Greg Kroah-Hartman
maintainerPATCH 237/428 tty: vt: fix memory leak in vc_allocate()
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Mingyu Wang <[email protected]>
commit 5b114bfc1dfd88f9d50822d532f7f1fce0f7388a upstream.
If the screen buffer allocation fails in vc_allocate(), the error handling path jumps to err_free. However, this path fails to release the unicode screen map attached to vc->uni_pagedict_loc.
During the early stages of vc_allocate(), the unicode screen map is either newly allocated via con_set_default_unimap() or shares the default unicode map from a previously initialized console (which increments its refcount). If the subsequent kzalloc() for the screen buffer fails, the err_free path frees the vc structure but leaves the attached uni_pagedict with an elevated refcount. This results in an unreferenced object memory leak, as the reference to the dictionary is lost and its refcount can never reach zero.
This issue was discovered by DevGen (an automated virtual device modeling fuzzer based on Syzkaller). During fuzzing with kernel fault injection (failslab) enabled, the fuzzer forcefully failed the kzalloc() for the screen buffer, exposing this error-handling path leak.
Fix this by checking *vc->uni_pagedict_loc and calling con_free_unimap(vc) in the err_free path before kfree(vc). This safely decrements the refcount and releases the dictionary memory if this was the last reference. The explicit check is added to maintain consistency with other callers.
--- drivers/tty/vt/vt.c | 2 ++ 1 file changed, 2 insertions(+)
drivers/tty/vt/vt.c +2 -0
@@ -1134,6 +1134,8 @@ int vc_allocate(unsigned int currcons) /return 0 ; err_free:visual_deinit (vc); +if (*vc->uni_pagedict_loc) +con_free_unimap (vc);kfree (vc); vc_cons[currcons].d =NULL ;return err;
Greg Kroah-Hartman
maintainerPATCH 238/428 tty: amiserial: fix broken TIOCMIWAIT
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Johan Hovold <[email protected]>
commit 615f35fd5aee169cc4881a8594f2fa234a4d0bb8 upstream.
A change replacing interruptible_sleep_on() inadvertently broke the TIOCMIWAIT implementation which now immediately returns -EIO (unless there is a racing modem status update).
Replace the old hangup (and signal) detection with a check for the TTY_IO_ERROR flag which is set on hangup.
--- drivers/tty/amiserial.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-)
drivers/tty/amiserial.c +4 -5
@@ -1138,11 +1138,6 @@ static int rs_ioctl(struct tty_struct *tlocal_irq_save (flags); cnow = info->icount;/* atomic copy */ local_irq_restore (flags); -if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr && - cnow.dcd == cprev.dcd && cnow.cts == cprev.cts) { - ret = -EIO ;/* no change => error */ -break ; - } if ( ((arg & TIOCM_RNG) && (cnow.rng != cprev.rng)) || ((arg & TIOCM_DSR) && (cnow.dsr != cprev.dsr)) || ((arg & TIOCM_CD) && (cnow.dcd != cprev.dcd)) ||
@@ -1150,6 +1145,10 @@ static int rs_ioctl(struct tty_struct *t ret =0 ;break ; } +if (tty_io_error (tty)) { + ret = -EIO ; +break ; + }schedule ();/* see if a signal did it */ if (signal_pending (current)) {
Greg Kroah-Hartman
maintainerPATCH 239/428 tty: vcc: zero-initialize control packet in vcc_send_ctl()
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Joshua Rogers <[email protected]>
commit 1f71f565e3585da1098ff19ca3c24687dab59185 upstream.
The stack-allocated struct vio_vcc pkt was partially initialized, leaving the tag.stype_env field uninitialized before being sent via ldc_write(), potentially leaking kernel stack data to the LDC peer.
--- drivers/tty/vcc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/tty/vcc.c +1 -1
@@ -492,7 +492,7 @@ static ssize_t domain_show(struct devicestatic int vcc_send_ctl (struct vcc_port *port,int ctl) { -struct vio_vcc pkt; +struct vio_vcc pkt = {};int rv; pkt.tag.type =VIO_TYPE_CTRL ;
Greg Kroah-Hartman
maintainerPATCH 240/428 tty: vcc: hold port lock when clearing tty pointer in vcc_cleanup
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Joshua Rogers <[email protected]>
commit 03b49c991abb8d021ca6fe51cd173447236b0552 upstream.
vcc_cleanup() sets port->tty to NULL without holding port->lock, racing with vcc_event() LDC callbacks that read port->tty under port->lock and then use tty->port. This can cause a use-after-free when the callback dereferences tty->port after cleanup has already destroyed and freed it.
--- drivers/tty/vcc.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
drivers/tty/vcc.c +2 -1
@@ -986,7 +986,8 @@ static void vcc_cleanup(struct tty_struc port =vcc_get (tty->index, true);if (port) { - port->tty =NULL ; +scoped_guard (spinlock_irqsave, &port->lock) + port->tty = NULL;if (port->removed) {vcc_table_remove (tty->index);
Greg Kroah-Hartman
maintainerPATCH 241/428 tty: n_gsm: fix missing modem controls after DLCI open in convergence layer type 2
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Daniel Starke <[email protected]>
commit 11ea5a63d30ad6d305b1c22301cd99ff45a98c8d upstream.
The current implementation only updates the virtual V.24 control signals after DLCI open in basic option mode. In advanced option mode with convergence layer type 2 an empty data frame needs to be transmitted to inform the peer about the initial virtual V.24 control signals. Not doing so has two unwanted side effects:
1. hardware flow control status is unclear (initial CTS line is unknown)
2. DLCI user presence is not transmitted (initial DTR line is unknown)
Applications waiting on these lines can not start after a DLCI has been opened until the first data frame has been transmitted.
Fix this by using gsm_modem_update() thoroughly which handles basic and advanced option mode correctly. Add a flag in gsm_modem_update() and the delegated gsm_modem_upd_via_msc() to control whether waiting for the MSC command response is possible. Remove obsolete gsm_modem_send_initial_msc().
--- drivers/tty/n_gsm.c | 47 ++++++++++++++--------------------------------- 1 file changed, 14 insertions(+), 33 deletions(-)
drivers/tty/n_gsm.c +14 -33
@@ -454,14 +454,13 @@ static const u8 gsm_fcs8[256] = {static void gsm_dlci_close (struct gsm_dlci *dlci);static int gsmld_output (struct gsm_mux *gsm,u8 *data,int len); -static int gsm_modem_update (struct gsm_dlci *dlci,u8 brk); +static int gsm_modem_update (struct gsm_dlci *dlci,u8 brk,bool wait);static struct gsm_msg *gsm_data_alloc (struct gsm_mux *gsm,u8 addr,int len,u8 ctrl);static int gsm_send_packet (struct gsm_mux *gsm,struct gsm_msg *msg);static struct gsm_dlci *gsm_dlci_alloc (struct gsm_mux *gsm,int addr);static void gsmld_write_trigger (struct gsm_mux *gsm);static void gsmld_write_task (struct work_struct *work); -static int gsm_modem_send_initial_msc (struct gsm_dlci *dlci);/** * gsm_fcs_add - update FCS
@@ -2174,7 +2173,7 @@ static void gsm_dlci_open(struct gsm_dlcpr_debug ("DLCI %d goes open.\n" , dlci->addr);/* Send current modem state */ if (dlci->addr) { -gsm_modem_send_initial_msc (dlci); +gsm_modem_update (dlci,0 , false); }else {/* Start keep-alive control */ gsm->ka_num =0 ;
@@ -4136,9 +4135,10 @@ static void gsm_modem_upd_via_data(struc* gsm_modem_upd_via_msc - send modem bits via control frame * @dlci: channel * @brk: break signal +* @wait: wait for the MSC command */ -static int gsm_modem_upd_via_msc (struct gsm_dlci *dlci,u8 brk) +static int gsm_modem_upd_via_msc (struct gsm_dlci *dlci,u8 brk,bool wait) {u8 modembits[3 ];struct gsm_control *ctrl;
@@ -4155,6 +4155,8 @@ static int gsm_modem_upd_via_msc(struct modembits[2 ] = (brk <<4 ) |2 |EA ;/* Length, Break, EA */ len++; } +if (!wait) +return gsm_control_command (dlci->gsm,CMD_MSC , modembits, len); ctrl =gsm_control_send (dlci->gsm,CMD_MSC , modembits, len);if (ctrl ==NULL )return -ENOMEM ;
@@ -4162,34 +4164,13 @@ static int gsm_modem_upd_via_msc(struct }/** -* gsm_modem_send_initial_msc - Send initial modem status message -* -* @dlci: channel -* -* Send an initial MSC message after DLCI open to set the initial -* modem status lines. This is only done for basic mode. -* Does not wait for a response as we cannot block the input queue -* processing. -*/ -static int gsm_modem_send_initial_msc (struct gsm_dlci *dlci) -{ -u8 modembits[2 ]; - -if (dlci->adaption !=1 || dlci->gsm->encoding !=GSM_BASIC_OPT ) -return 0 ; - - modembits[0 ] = (dlci->addr <<2 ) |2 |EA ;/* DLCI, Valid, EA */ - modembits[1 ] = (gsm_encode_modem (dlci) <<1 ) |EA ; -return gsm_control_command (dlci->gsm,CMD_MSC , (const u8 *)&modembits,2 ); -} - -/** * gsm_modem_update - send modem status line state * @dlci: channel * @brk: break signal +* @wait: wait for the MSC command */ -static int gsm_modem_update (struct gsm_dlci *dlci,u8 brk) +static int gsm_modem_update (struct gsm_dlci *dlci,u8 brk,bool wait) {if (dlci->gsm->dead)return -EL2HLT ;
@@ -4199,7 +4180,7 @@ static int gsm_modem_update(struct gsm_dreturn 0 ; }else if (dlci->gsm->encoding ==GSM_BASIC_OPT ) {/* Send as MSC control message. */ -return gsm_modem_upd_via_msc (dlci, brk); +return gsm_modem_upd_via_msc (dlci, brk, wait); }/* Modem status lines are not supported. */
@@ -4265,7 +4246,7 @@ static void gsm_dtr_rts(struct tty_port modem_tx &= ~(TIOCM_DTR |TIOCM_RTS );if (modem_tx != dlci->modem_tx) { dlci->modem_tx = modem_tx; -gsm_modem_update (dlci,0 ); +gsm_modem_update (dlci,0 , true); } }
@@ -4471,7 +4452,7 @@ static int gsmtty_tiocmset(struct tty_stif (modem_tx != dlci->modem_tx) { dlci->modem_tx = modem_tx; -return gsm_modem_update (dlci,0 ); +return gsm_modem_update (dlci,0 , true); }return 0 ; }
@@ -4553,7 +4534,7 @@ static void gsmtty_throttle(struct tty_s dlci->modem_tx &= ~TIOCM_RTS ; dlci->throttled = true;/* Send an MSC with RTS cleared */ -gsm_modem_update (dlci,0 ); +gsm_modem_update (dlci,0 , true); }static void gsmtty_unthrottle (struct tty_struct *tty)
@@ -4565,7 +4546,7 @@ static void gsmtty_unthrottle(struct tty dlci->modem_tx |=TIOCM_RTS ; dlci->throttled = false;/* Send an MSC with RTS set */ -gsm_modem_update (dlci,0 ); +gsm_modem_update (dlci,0 , true); }static int gsmtty_break_ctl (struct tty_struct *tty,int state)
@@ -4583,7 +4564,7 @@ static int gsmtty_break_ctl(struct tty_sif (encode >0x0F ) encode =0x0F ;/* Best effort */ } -return gsm_modem_update (dlci, encode); +return gsm_modem_update (dlci, encode, true); }static void gsmtty_cleanup (struct tty_struct *tty)
Greg Kroah-Hartman
maintainerPATCH 242/428 tty: tty_port: restore TTY_IO_ERROR on activation failure
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Johan Hovold <[email protected]>
commit 56964f6a31238fb41d5830bb42520c5b10ff7d8f upstream.
Make sure to restore the TTY_IO_ERROR flag if activation fails to prevent I/O attempts on a port that is gone or powered down.
This specifically avoids issues like kernel panic due to unclocked accesses or NULL-pointer dereferences when accessing powered down or deregistered ports when a TTY ioctl races with hangup and open.
Note that the flag is set again in tty_port_close() but not restoring it on activation failure leaves a window where drivers fail to handle the ioctl-hangup race.
--- drivers/tty/tty_port.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
drivers/tty/tty_port.c +3 -1
@@ -767,8 +767,10 @@ int tty_port_open(struct tty_port *port,clear_bit (TTY_IO_ERROR , &tty->flags);if (port->ops->activate) {int retval = port->ops->activate (port, tty); -if (retval) +if (retval) { +set_bit (TTY_IO_ERROR , &tty->flags);return retval; + } }tty_port_set_initialized (port, true); }
Greg Kroah-Hartman
maintainerPATCH 243/428 tty: port: fix tty_port_tty_vhangup() race
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Johan Hovold <[email protected]>
commit b48820de8c1a596c5b99775524dfce73c6576c5d upstream.
Drivers rely on tty_port_tty_vhangup() to synchronously hang up their ports before tearing down resources as part of deregistration (e.g. on physical disconnect).
If tty_port_tty_vhangup() races with another hangup it may however return before the port has been shut down. This is in turn can lead to use-after-free and NULL-pointer dereferences in racing ioctls (including a synchronous hangup) and in processing of racing asynchronous hangups (carrier loss).
Add the missing serialisation to tty_port_tty_vhangup() and tty_port_hangup() so that the former does not return until the port has been shut down also when there is a racing hangup.
Note that serial core does not use tty_port_hangup(), but it already holds the port mutex when clearing port->tty and shutting down the port in uart_hangup().
--- drivers/tty/tty_port.c | 87 +++++++++++++++++++++++++++++++---------------- include/linux/tty_port.h | 22 +---------- 2 files changed, 60 insertions(+), 49 deletions(-)
drivers/tty/tty_port.c +58 -29
@@ -340,19 +340,9 @@ void tty_port_tty_set(struct tty_port *p }EXPORT_SYMBOL (tty_port_tty_set); -/** -* tty_port_shutdown - internal helper to shutdown the device -* @port: tty port to be shut down -* @tty: the associated tty -* -* It is used by tty_port_hangup() and tty_port_close(). Its task is to -* shutdown the device if it was initialized (note consoles remain -* functioning). It lowers DTR/RTS (if @tty has HUPCL set) and invokes -* @port->ops->shutdown(). -*/ -static void tty_port_shutdown (struct tty_port *port,struct tty_struct *tty) +static void tty_port_shutdown_locked (struct tty_port *port,struct tty_struct *tty) { -guard (mutex)(&port->mutex); +lockdep_assert_held (&port->mutex);if (port->console)return ;
@@ -373,6 +363,23 @@ static void tty_port_shutdown(struct tty }/** +* tty_port_shutdown - internal helper to shutdown the device +* @port: tty port to be shut down +* @tty: the associated tty +* +* It is used by tty_port_hangup() and tty_port_close(). Its task is to +* shutdown the device if it was initialized (note consoles remain +* functioning). It lowers DTR/RTS (if @tty has HUPCL set) and invokes +* @port->ops->shutdown(). +*/ +static void tty_port_shutdown (struct tty_port *port,struct tty_struct *tty) +{ +guard (mutex)(&port->mutex); + +tty_port_shutdown_locked (port, tty); +} + +/** * tty_port_hangup - hangup helper * @port: tty port *
@@ -385,36 +392,58 @@ void tty_port_hangup(struct tty_port *po {struct tty_struct *tty; - scoped_guard(spinlock_irqsave, &port->lock) { - port->count =0 ; - tty = port->tty; -if (tty) -set_bit (TTY_IO_ERROR , &tty->flags); - port->tty =NULL ; - } + scoped_guard(mutex, &port->mutex) { + scoped_guard(spinlock_irqsave, &port->lock) { + port->count =0 ; + tty = port->tty; +if (tty) +set_bit (TTY_IO_ERROR , &tty->flags); + port->tty =NULL ; + } -tty_port_set_active (port, false); -tty_port_shutdown (port, tty); +tty_port_set_active (port, false); +tty_port_shutdown_locked (port, tty); + }tty_kref_put (tty);wake_up_interruptible (&port->open_wait);wake_up_interruptible (&port->delta_msr_wait); }EXPORT_SYMBOL (tty_port_hangup); -void __tty_port_tty_hangup (struct tty_port *port,bool check_clocal,bool async) +/** +* tty_port_tty_hangup - helper to hang up a tty asynchronously +* @port: tty port +* @check_clocal: hang only ttys with %CLOCAL unset? +*/ +void tty_port_tty_hangup (struct tty_port *port,bool check_clocal) { scoped_guard(tty_port_tty, port) {struct tty_struct *tty =scoped_tty (); -if (!check_clocal || !C_CLOCAL (tty)) { -if (async) -tty_hangup (tty); -else -tty_vhangup (tty); - } +if (!check_clocal || !C_CLOCAL (tty)) +tty_hangup (tty); + } +} +EXPORT_SYMBOL_GPL (tty_port_tty_hangup); + +/** +* tty_port_tty_vhangup - helper to hang up a tty synchronously +* @port: tty port +*/ +void tty_port_tty_vhangup (struct tty_port *port) +{ +struct tty_struct *tty; + +mutex_lock (&port->mutex); + tty =tty_port_tty_get (port); +mutex_unlock (&port->mutex); + +if (tty) { +tty_vhangup (tty); +tty_kref_put (tty); } } -EXPORT_SYMBOL_GPL (__tty_port_tty_hangup); +EXPORT_SYMBOL_GPL (tty_port_tty_vhangup);/** * tty_port_tty_wakeup - helper to wake up a tty
include/linux/tty_port.h +2 -20
@@ -245,7 +245,8 @@ bool tty_port_carrier_raised(struct tty_void tty_port_raise_dtr_rts (struct tty_port *port);void tty_port_lower_dtr_rts (struct tty_port *port);void tty_port_hangup (struct tty_port *port); -void __tty_port_tty_hangup (struct tty_port *port,bool check_clocal,bool async); +void tty_port_tty_hangup (struct tty_port *port,bool check_clocal); +void tty_port_tty_vhangup (struct tty_port *port);void tty_port_tty_wakeup (struct tty_port *port);int tty_port_block_til_ready (struct tty_port *port,struct tty_struct *tty,struct file *filp);
@@ -264,25 +265,6 @@ static inline int tty_port_users(structreturn port->count + port->blocked_open; } -/** -* tty_port_tty_hangup - helper to hang up a tty asynchronously -* @port: tty port -* @check_clocal: hang only ttys with %CLOCAL unset? -*/ -static inline void tty_port_tty_hangup (struct tty_port *port,bool check_clocal) -{ -__tty_port_tty_hangup (port, check_clocal, true); -} - -/** -* tty_port_tty_vhangup - helper to hang up a tty synchronously -* @port: tty port -*/ -static inline void tty_port_tty_vhangup (struct tty_port *port) -{ -__tty_port_tty_hangup (port, false, false); -} -#ifdef CONFIG_TTY void tty_kref_put (struct tty_struct *tty);__DEFINE_CLASS_IS_CONDITIONAL (tty_port_tty, true);
Greg Kroah-Hartman
maintainerPATCH 244/428 tty: serial: qcom_geni_serial: Keep console RX functional after deep idle
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Viken Dadhaniya <[email protected]>
commit 63ef71d1474b5417b5b2dec700cd251c44a570d9 upstream.
At baud rates up to 115200, the serial console uses only a 1 kBps keepalive vote for the GENI_TO_CORE ("qup-core") ICC path. This vote keeps the path active but does not request a QUP Core 2X clock rate.
When the CPU enters a deeper idle state, the missing Core clock vote can leave the console RX path unresponsive.
Use the 19.2 MHz Core 2X vote at low baud rates, while retaining the 50 MHz vote at higher baud rates, so that console RX remains functional after deep idle transitions.
--- drivers/tty/serial/qcom_geni_serial.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-)
drivers/tty/serial/qcom_geni_serial.c +2 -3
@@ -1312,9 +1312,8 @@ static int geni_serial_set_rate(struct u* Bump up BW vote on CPU and CORE path as driver supports FIFO mode * only. */ - avg_bw_core = (baud >115200 ) ?Bps_to_icc (CORE_2X_50_MHZ ) - :GENI_DEFAULT_BW ; - port->se.icc_paths[GENI_TO_CORE ].avg_bw = avg_bw_core; + avg_bw_core = baud >115200 ?CORE_2X_50_MHZ :CORE_2X_19_2_MHZ ; + port->se.icc_paths[GENI_TO_CORE ].avg_bw =Bps_to_icc (avg_bw_core); port->se.icc_paths[CPU_TO_GENI ].avg_bw =Bps_to_icc (baud);geni_icc_set_bw (&port->se);
Greg Kroah-Hartman
maintainerPATCH 245/428 tty: abort break signalling on hangup
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Johan Hovold <[email protected]>
commit 845a2737e0f5ffe5ca8d38de556012b0c9472cdd upstream.
The break ioctls can race with hangup and end up calling into a tty driver for a device that is already gone or powered down.
Drivers must handle this race, but TCSBRK and TCSBRKP should still be aborted to avoid calling back into the driver after a user-controlled timeout (possibly even after the tty has been reopened).
Note that drivers should disable any break state on shutdown so just return on hangup (calling break_ctl() again is racy and will most likely fail for drivers handling the race).
--- drivers/tty/tty_io.c | 36 +++++++++++++++++++++++++++--------- include/linux/tty.h | 1 + 2 files changed, 28 insertions(+), 9 deletions(-)
drivers/tty/tty_io.c +27 -9
@@ -620,6 +620,8 @@ static void __tty_hangup(struct tty_strutty_ldisc_hangup (tty, cons_filp !=NULL ); +wake_up_interruptible (&tty->break_wait); +spin_lock_irq (&tty->ctrl.lock);clear_bit (TTY_THROTTLED , &tty->flags);clear_bit (TTY_DO_WRITE_WAKEUP , &tty->flags);
@@ -2458,6 +2460,7 @@ static int tiocgetd(struct tty_struct *t* send_break - performed time break * @tty: device to break on * @duration: timeout in mS +* @file: file object * * Perform a timed break on hardware that lacks its own driver level timed * break functionality.
@@ -2465,8 +2468,9 @@ static int tiocgetd(struct tty_struct *t* Locking: * @tty->atomic_write_lock serializes */ -static int send_break (struct tty_struct *tty,unsigned int duration) +static int send_break (struct file *file,struct tty_struct *tty,unsigned int duration) { +long timeout;int retval;if (tty->ops->break_ctl ==NULL )
@@ -2480,13 +2484,26 @@ static int send_break(struct tty_structreturn -EINTR ; retval = tty->ops->break_ctl (tty,-1 ); -if (!retval) { -msleep_interruptible (duration); - retval = tty->ops->break_ctl (tty,0 ); - }else if (retval == -EOPNOTSUPP ) { -/* some drivers can tell only dynamically */ - retval =0 ; +if (retval) { +if (retval == -EOPNOTSUPP ) { +/* some drivers can tell only dynamically */ + retval =0 ; + } + goto out_unlock; + } + + timeout =msecs_to_jiffies (duration); + timeout =wait_event_interruptible_timeout (tty->break_wait, +tty_hung_up_p (file), + timeout); +/* return early on hangup only */ +if (timeout >0 ) { + retval = -EIO ; + goto out_unlock; } + + retval = tty->ops->break_ctl (tty,0 ); +out_unlock:tty_write_unlock (tty);if (signal_pending (current))
@@ -2754,10 +2771,10 @@ long tty_ioctl(struct file *file, unsign* This is used by the tcdrain() termios function. */ if (!arg) -return send_break (tty,250 ); +return send_break (file, tty,250 );return 0 ;case TCSBRKP :/* support for POSIX tcsendbreak() */ -return send_break (tty, arg ? arg*100 :250 ); +return send_break (file, tty, arg ? arg *100 :250 );case TIOCMGET :return tty_tiocmget (tty, p);
@@ -3117,6 +3134,7 @@ struct tty_struct *alloc_tty_struct(struinit_ldsem (&tty->ldisc_sem);init_waitqueue_head (&tty->write_wait);init_waitqueue_head (&tty->read_wait); +init_waitqueue_head (&tty->break_wait);INIT_WORK (&tty->hangup_work, do_tty_hangup);mutex_init (&tty->atomic_write_lock);spin_lock_init (&tty->ctrl.lock);
include/linux/tty.h +1 -0
@@ -230,6 +230,7 @@ struct tty_struct {struct fasync_struct *fasync;wait_queue_head_t write_wait;wait_queue_head_t read_wait; +wait_queue_head_t break_wait;struct work_struct hangup_work;void *disc_data;void *driver_data;
Greg Kroah-Hartman
maintainerPATCH 246/428 tty: fix saved termios reset race
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Johan Hovold <[email protected]>
commit 8df07fe93573e9e548d6645273e9bcb6eb74059e upstream.
Resetting saved termios state on device registration is needed where a minor number can be reused for an entirely different device and where the old settings may prevent the port from even being opened (e.g. when CLOCAL is not set).
Not all TTY drivers guarantee that the minor number is no longer in use when registering devices however, something which can lead to a use-after-free when closing a TTY (and saving its termios) races with re-registration.
Add a new TTY_DRIVER_RESET_SAVED_TERMIOS flag to request that any saved termios state is reset on registration and only set it for drivers that make sure that the minor number is no longer in use.
--- drivers/staging/greybus/uart.c | 3 ++- drivers/tty/tty_io.c | 18 ++++++++++-------- drivers/usb/class/cdc-acm.c | 2 +- drivers/usb/serial/usb-serial.c | 3 ++- include/linux/tty_driver.h | 6 ++++++ 5 files changed, 21 insertions(+), 11 deletions(-)
drivers/staging/greybus/uart.c +2 -1
@@ -948,7 +948,8 @@ static int gb_tty_init(void)int retval =0 ; gb_tty_driver =tty_alloc_driver (GB_NUM_MINORS ,TTY_DRIVER_REAL_RAW | -TTY_DRIVER_DYNAMIC_DEV ); +TTY_DRIVER_DYNAMIC_DEV | +TTY_DRIVER_RESET_SAVED_TERMIOS );if (IS_ERR (gb_tty_driver)) {pr_err ("Can not allocate tty driver\n" ); retval = -ENOMEM ;
drivers/tty/tty_io.c +10 -8
@@ -3283,14 +3283,16 @@ struct device *tty_register_device_attr( goto err_put;if (!(driver->flags &TTY_DRIVER_DYNAMIC_ALLOC )) { -/* -* Free any saved termios data so that the termios state is -* reset when reusing a minor number. -*/ - tp = driver->termios[index]; -if (tp) { - driver->termios[index] =NULL ; -kfree (tp); +if (driver->flags &TTY_DRIVER_RESET_SAVED_TERMIOS ) { +/* +* Free any saved termios data so that the termios +* state is reset when reusing a minor number. +*/ + tp = driver->termios[index]; +if (tp) { + driver->termios[index] =NULL ; +kfree (tp); + } } retval =tty_cdev_add (driver, devt, index,1 );
drivers/usb/class/cdc-acm.c +1 -1
@@ -2145,7 +2145,7 @@ static int __init acm_init(void) {int retval; acm_tty_driver =tty_alloc_driver (ACM_TTY_MINORS ,TTY_DRIVER_REAL_RAW | -TTY_DRIVER_DYNAMIC_DEV ); +TTY_DRIVER_DYNAMIC_DEV |TTY_DRIVER_RESET_SAVED_TERMIOS );if (IS_ERR (acm_tty_driver))return PTR_ERR (acm_tty_driver); acm_tty_driver->driver_name = "acm",
drivers/usb/serial/usb-serial.c +2 -1
@@ -1318,7 +1318,8 @@ static int __init usb_serial_init(void)int result; usb_serial_tty_driver =tty_alloc_driver (USB_SERIAL_TTY_MINORS , -TTY_DRIVER_REAL_RAW |TTY_DRIVER_DYNAMIC_DEV ); +TTY_DRIVER_REAL_RAW |TTY_DRIVER_DYNAMIC_DEV | +TTY_DRIVER_RESET_SAVED_TERMIOS );if (IS_ERR (usb_serial_tty_driver))return PTR_ERR (usb_serial_tty_driver);
include/linux/tty_driver.h +6 -0
@@ -73,6 +73,11 @@ struct serial_struct;* @TTY_DRIVER_NO_WORKQUEUE: * Do not create workqueue when tty_register_driver(). Whenever set, flip * buffer workqueue can be set by tty_port_link_wq() for every port. +* +* @TTY_DRIVER_RESET_SAVED_TERMIOS +* Reset any saved termios settings on device registration when reusing a +* minor number. Must only be set by drivers that guarantee that the minor +* number is no longer in use. */ enum tty_driver_flag {TTY_DRIVER_INSTALLED =BIT (0 ),
@@ -84,6 +89,7 @@ enum tty_driver_flag {TTY_DRIVER_DYNAMIC_ALLOC =BIT (6 ),TTY_DRIVER_UNNUMBERED_NODE =BIT (7 ),TTY_DRIVER_NO_WORKQUEUE =BIT (8 ), + TTY_DRIVER_RESET_SAVED_TERMIOS = BIT(9), }; enum tty_driver_type {
Greg Kroah-Hartman
maintainerPATCH 247/428 tty: serial: max3100: shut down timer before freeing port
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Fan Wu <[email protected]>
commit db0949bbd06db0a75b4b1076f1e25194be7d956a upstream.
max3100_shutdown() stops the polling timer but returns early during system suspend. If the SPI device is unbound before resume, the serial core does not call max3100_shutdown() again, so max3100_remove() frees the port while the timer remains armed. max3100_timeout() may then access the freed port and re-arm the timer.
Add final timer teardown to max3100_remove() and use timer_shutdown_sync() to prevent a racing callback from re-arming it. Also free the IRQ and destroy the workqueue there before freeing the port. Keep timer_delete_sync() in max3100_shutdown() so that a subsequent open() can re-arm the timer.
The workqueue is created before request_irq() and destroyed on both request_irq() failure and normal shutdown. Its presence at remove thus identifies the IRQ left registered when suspend bypasses shutdown.
Free the IRQ before destroying the workqueue, in both max3100_shutdown() and max3100_remove(), and cancel the pending work in between. free_irq() waits for a running interrupt handler to finish and no new handler can start afterwards, so a racing handler cannot queue work on a workqueue that is being destroyed; the force_end_work check in max3100_dowork() only narrows that window, as a handler delayed between the check and queue_work() could still run after the teardown has cleared the workqueue pointer. Cancelling the work also keeps destroy_workqueue() from waiting on a queued work item in case remove() ever runs while the freezable workqueue is still frozen, e.g. device removal during the resume window, before workqueues are thawed.
This issue was found by an in-house static analysis tool.
--- drivers/tty/serial/max3100.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-)
drivers/tty/serial/max3100.c +11 -2
@@ -535,11 +535,11 @@ static void max3100_shutdown(struct uarttimer_delete_sync (&s->timer);if (s->workqueue) { +free_irq (port->irq, s); +cancel_work_sync (&s->work);destroy_workqueue (s->workqueue); s->workqueue =NULL ; } -if (port->irq) -free_irq (port->irq, s);/* set shutdown mode to save power */ max3100_sr (s,MAX3100_WC |MAX3100_SHDN , &rx);
@@ -752,6 +752,15 @@ static void max3100_remove(struct spi_deif (max3100s[i] == s) {dev_dbg (&spi->dev,"%s: removing port %d\n" , __func__, i);uart_remove_one_port (&max3100_uart_driver, &max3100s[i]->port); + + s->force_end_work =1 ; +timer_shutdown_sync (&s->timer); +if (s->workqueue) { +free_irq (s->port.irq, s); +cancel_work_sync (&s->work); +destroy_workqueue (s->workqueue); + s->workqueue =NULL ; + }kfree (max3100s[i]); max3100s[i] =NULL ;break ;
Greg Kroah-Hartman
maintainerPATCH 248/428 perf: Replace perf_event_header__init_id with full header init
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ian Rogers <[email protected]>
commit b9d1fdc6f4ac1b6e49f9deafaf137da1407d9af1 upstream.
perf_iterate_sb() invokes its callback for each matching perf_event on the CPU and task context, passing a shared caller-allocated event structure.
perf_event_header__init_id() mutated header->size in place by adding event->id_header_size, requiring sideband output callbacks to save and restore header fields across iterations. Three sideband callbacks failed to save and restore header.size around perf_event_header__init_id():
- perf_event_ksymbol_output()
- perf_event_bpf_output()
- perf_event_text_poke_output()
When multiple events with attr.ksymbol, attr.bpf_event, or attr.text_poke and sample_id_all are active on the same CPU, each subsequent event receives a record whose header.size is inflated by all preceding events' id_header_size values while only a single id_sample is written, leaving uninitialized ring-buffer bytes at the end of the record and causing userspace perf to fail with -EFAULT ("Bad address") when parsing the sample_id trailer.
Similarly, perf_event_mmap_output() set PERF_RECORD_MISC_MMAP_BUILD_ID in mmap_event->event_id.header.misc when event->attr.build_id was enabled, but only saved and restored header.size and header.type. If an event with attr.build_id was followed by an event with attr.mmap2 and !attr.build_id, the second event received PERF_RECORD_MISC_MMAP_BUILD_ID in header.misc while its payload contained maj/min/ino/ino_generation instead of a build ID.
Rather than splitting header initialization between callers and output callbacks and saving/restoring mutated header fields, replace perf_event_header__init_id() with perf_event_header__init(), which initializes header->type, header->misc, and header->size alongside the sample_id fields on each invocation.
--- arch/powerpc/perf/imc-pmu.c | 23 +-- include/linux/perf_event.h | 7 - kernel/events/core.c | 303 ++++++++++++++++++-------------------------- kernel/events/ring_buffer.c | 7 - 4 files changed, 146 insertions(+), 194 deletions(-)
arch/powerpc/perf/imc-pmu.c +12 -11
@@ -1275,6 +1275,8 @@ static int trace_imc_prepare_sample(strustruct perf_event_header *header, struct perf_event *event) { + u16 misc = 0; +/* Sanity checks for a valid record */ if (be64_to_cpu (READ_ONCE (mem->tb1)) > *prev_tb) *prev_tb =be64_to_cpu (READ_ONCE (mem->tb1));
@@ -1289,23 +1291,19 @@ static int trace_imc_prepare_sample(stru data->ip =be64_to_cpu (READ_ONCE (mem->ip)); data->period = event->hw.last_period; - header->type =PERF_RECORD_SAMPLE ; - header->size =sizeof (*header) + event->header_size; - header->misc =0 ; -if (cpu_has_feature (CPU_FTR_ARCH_31 )) {switch (IMC_TRACE_RECORD_VAL_HVPR (be64_to_cpu (READ_ONCE (mem->val)))) {case 0 :/* when MSR HV and PR not set in the trace-record */ - header->misc |=PERF_RECORD_MISC_GUEST_KERNEL ; + misc |=PERF_RECORD_MISC_GUEST_KERNEL ;break ;case 1 :/* MSR HV is 0 and PR is 1 */ - header->misc |=PERF_RECORD_MISC_GUEST_USER ; + misc |=PERF_RECORD_MISC_GUEST_USER ;break ;case 2 :/* MSR HV is 1 and PR is 0 */ - header->misc |=PERF_RECORD_MISC_KERNEL ; + misc |=PERF_RECORD_MISC_KERNEL ;break ;case 3 :/* MSR HV is 1 and PR is 1 */ - header->misc |=PERF_RECORD_MISC_USER ; + misc |=PERF_RECORD_MISC_USER ;break ;default :pr_info ("IMC: Unable to set the flag based on MSR bits\n" );
@@ -1313,11 +1311,14 @@ static int trace_imc_prepare_sample(stru } }else {if (is_kernel_addr (data->ip)) - header->misc |=PERF_RECORD_MISC_KERNEL ; + misc |=PERF_RECORD_MISC_KERNEL ;else - header->misc |=PERF_RECORD_MISC_USER ; + misc |=PERF_RECORD_MISC_USER ; } -perf_event_header__init_id (header, data, event); +perf_event_header__init (header, data, +PERF_RECORD_SAMPLE , misc, +sizeof (*header) + event->header_size, + event);return 0 ; }
include/linux/perf_event.h +4 -3
@@ -1506,9 +1506,10 @@ is_default_overflow_handler(struct perf_ }extern void -perf_event_header__init_id (struct perf_event_header *header, -struct perf_sample_data *data, -struct perf_event *event); +perf_event_header__init (struct perf_event_header *header, +struct perf_sample_data *data, +u32 type,u16 misc,u16 size, +struct perf_event *event); extern void perf_event__output_id_sample(struct perf_event *event, struct perf_output_handle *handle,
kernel/events/core.c +127 -176
652 lines not on this page · raw message
kernel/events/ring_buffer.c +3 -4
@@ -246,14 +246,13 @@ __perf_output_begin(struct perf_output_h handle->size = (1UL << page_shift) - offset;if (unlikely (have_lost)) { - lost_event.header.size =sizeof (lost_event); - lost_event.header.type =PERF_RECORD_LOST ; - lost_event.header.misc =0 ; lost_event.id = event->id; lost_event.lost =local_xchg (&rb->lost,0 );/* XXX mostly redundant; @data is already fully initializes */ -perf_event_header__init_id (&lost_event.header, data, event); +perf_event_header__init (&lost_event.header, data, +PERF_RECORD_LOST ,/* misc= */ 0 , +sizeof (lost_event), event);perf_output_put (handle, lost_event);perf_event__output_id_sample (event, handle, data); }
Greg Kroah-Hartman
maintainerPATCH 249/428 serial: 8250_bcm7271: fix use-after-free in brcmuart_remove()
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Fan Wu <[email protected]>
commit 189b9dc8c96ec9486482f27c1372b04a7152175e upstream.
brcmuart_handle_irq() starts priv->hrt to work around a receive-timeout quirk of the 8250 core when hardware flow control is enabled; the timer callback brcmuart_hrtimer_func() dereferences priv.
brcmuart_remove() cancels the hrtimer before it unregisters the port, so a receive-timeout interrupt that arrives in between re-arms the timer. After devm frees priv, the callback runs on freed memory.
Unregister the port before cancelling the hrtimer. serial8250_unregister_port() shuts the port down and frees its IRQ, so brcmuart_handle_irq() can no longer restart the timer, and the final hrtimer_cancel() drains any timer that was armed before or during the unregister.
This issue was found by an in-house static analysis tool.
--- drivers/tty/serial/8250/8250_bcm7271.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/tty/serial/8250/8250_bcm7271.c +1 -1
@@ -1123,8 +1123,8 @@ static void brcmuart_remove(struct platfstruct brcmuart_priv *priv =platform_get_drvdata (pdev);debugfs_remove_recursive (priv->debugfs_dir); -hrtimer_cancel (&priv->hrt);serial8250_unregister_port (priv->line); +hrtimer_cancel (&priv->hrt);brcmuart_free_bufs (&pdev->dev, priv);if (priv->dma_enabled)brcmuart_arbitration (priv,0 );
Greg Kroah-Hartman
maintainerPATCH 250/428 serial: 8250_omap: fix wake irq cleared during suspend
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Kendall Willis <[email protected]>
commit 73f3e6ca7a6a7c952977c7c94f1c6dddebcafa34 upstream.
The wake irq was cleared in shutdown(), which runs during the suspend sequence, making it impossible to wake the system via UART. Move wake irq setup to probe() and teardown to remove() so the irq remains armed during suspend when the UART is a wakeup source.
--- drivers/tty/serial/8250/8250_omap.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-)
drivers/tty/serial/8250/8250_omap.c +8 -7
@@ -711,12 +711,6 @@ static int omap_8250_startup(struct uartstruct uart_8250_dma *dma = &priv->omap8250_dma;int ret; -if (priv->wakeirq) { - ret =dev_pm_set_dedicated_wake_irq (port->dev, priv->wakeirq); -if (ret) -return ret; - } -#ifdef CONFIG_PM up->capabilities |=UART_CAP_RPM ;#endif
@@ -787,7 +781,6 @@ static void omap_8250_shutdown(struct ua }disable_irq_nosync (port->irq); -dev_pm_clear_wake_irq (port->dev);serial8250_release_dma (up); up->dma =NULL ;
@@ -1588,6 +1581,12 @@ static int omap8250_probe(struct platfor priv->wakeirq =irq_of_parse_and_map (np,1 ); +if (priv->wakeirq) { + ret =dev_pm_set_dedicated_wake_irq (&pdev->dev, priv->wakeirq); +if (ret) + goto err; + } + ret =serial8250_register_8250_port (&up);if (ret <0 ) {dev_err (&pdev->dev,"unable to register 8250 port\n" );
@@ -1603,6 +1602,7 @@ static int omap8250_probe(struct platforreturn 0 ; err: +dev_pm_clear_wake_irq (&pdev->dev);pm_runtime_dont_use_autosuspend (&pdev->dev);pm_runtime_put_sync (&pdev->dev);flush_work (&priv->qos_work);
@@ -1625,6 +1625,7 @@ static void omap8250_remove(struct platfomap_8250_shutdown (&up->port);serial8250_unregister_port (priv->line); priv->line = -ENODEV ; +dev_pm_clear_wake_irq (&pdev->dev);pm_runtime_dont_use_autosuspend (&pdev->dev);pm_runtime_put_sync (&pdev->dev);flush_work (&priv->qos_work);
Greg Kroah-Hartman
maintainerPATCH 251/428 serial: abort TIOCMIWAIT on hangup
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Johan Hovold <[email protected]>
commit eb35b73e133686e5b999eb7130c4e3d22fe03089 upstream.
Add the missing hangup check to the TIOCMIWAIT wait loop to avoid going back to sleep (indefinitely) after being woken on hangup.
--- drivers/tty/serial/serial_core.c | 5 +++++ 1 file changed, 5 insertions(+)
drivers/tty/serial/serial_core.c +5 -0
@@ -1232,6 +1232,11 @@ static int uart_wait_modem_status(structbreak ; } +if (tty_io_error (tty)) { + ret = -EIO ; +break ; + } +schedule ();/* see if a signal did it */
Greg Kroah-Hartman
maintainerPATCH 252/428 serial: core: fix NULL pointer dereference in serial_core_unregister_port()
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ruslan Valiyev <[email protected]>
commit 499485a67fbacf4d9afbdb43522388c64f853428 upstream.
port->port_dev is NULL when no port device is installed: it is cleared on teardown, and never set if registration failed before serial_core_port_device_add(). serial_core_unregister_port() passes it straight to serial_core_get_ctrl_dev(), which dereferences it:
KASAN: null-ptr-deref in range [0x0000000000000040-0x0000000000000047] RIP: serial_core_unregister_port Call Trace: serial8250_unregister_port serial8250_remove unbind_store
Return early when there is no port device, and read port->port_dev under port_mutex.
Also clear port->port_dev on the serial_core_register_port() error path, where the port device has already been removed.
--- drivers/tty/serial/serial_core.c | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-)
drivers/tty/serial/serial_core.c +15 -2
@@ -3336,6 +3336,7 @@ int serial_core_register_port(struct uar err_unregister_port_dev:serial_base_port_device_remove (port->port_dev); + port->port_dev =NULL ; err_unregister_ctrl_dev:serial_base_ctrl_device_remove (new_ctrl_dev);
@@ -3350,12 +3351,24 @@ err_unregister_ctrl_dev:void serial_core_unregister_port (struct uart_driver *drv,struct uart_port *port) {struct device *phys_dev = port->dev; -struct serial_port_device *port_dev = port->port_dev; -struct serial_ctrl_device *ctrl_dev =serial_core_get_ctrl_dev (port_dev); +struct serial_port_device *port_dev; +struct serial_ctrl_device *ctrl_dev;int ctrl_id = port->ctrl_id;guard (mutex)(&port_mutex); +/* +* A NULL port device means there is no registered port device to +* remove: serial_core_remove_one_port() clears port_dev on +* teardown, and it is never set if registration failed before +* serial_core_port_device_add(). +*/ + port_dev = port->port_dev; +if (!port_dev) +return ; + + ctrl_dev =serial_core_get_ctrl_dev (port_dev); + port->flags |=UPF_DEAD ;serial_core_remove_one_port (drv, port);
Greg Kroah-Hartman
maintainerPATCH 253/428 serial: revert guards in uart_wait_modem_status()
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Johan Hovold <[email protected]>
commit f658e6bcd00a4eafaa9c98c0b7483cc2c5dac539 upstream.
Mixing scope-based and regular cleanup is discouraged and uart_port_deref() used by uart_wait_modem_status() falls in the latter category.
Revert the guard conversion in preparation for fixing a hangup race.
--- drivers/tty/serial/serial_core.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-)
drivers/tty/serial/serial_core.c +7 -6
@@ -1212,15 +1212,16 @@ static int uart_wait_modem_status(struct uport =uart_port_ref (state);if (!uport)return -EIO ; - scoped_guard(uart_port_lock_irq, uport) { -memcpy (&cprev, &uport->icount,sizeof (struct uart_icount )); -uart_enable_ms (uport); - } +uart_port_lock_irq (uport); +memcpy (&cprev, &uport->icount,sizeof (struct uart_icount )); +uart_enable_ms (uport); +uart_port_unlock_irq (uport);add_wait_queue (&port->delta_msr_wait, &wait);for (;;) { - scoped_guard(uart_port_lock_irq, uport) -memcpy (&cnow, &uport->icount,sizeof (struct uart_icount )); +uart_port_lock_irq (uport); +memcpy (&cnow, &uport->icount,sizeof (struct uart_icount )); +uart_port_unlock_irq (uport);set_current_state (TASK_INTERRUPTIBLE );
Greg Kroah-Hartman
maintainerPATCH 254/428 serial: fix ioctl hangup race
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Johan Hovold <[email protected]>
commit 8ca59f7baee7c72cf7729950b4ab2e7f910c9549 upstream.
The tty ioctls can race with hangup and end up calling into a tty driver for a device that is already gone or powered down.
Add the missing checks to make sure the port has not been hung up before accessing the hardware to avoid issues like kernel panic due to unclocked accesses.
Note that TIOCGSERIAL, TIOCGICOUNT do not access hardware and are therefore not affected by the race.
Also note that the initialised check in uart_set_ldisc() added by commit 2f70e49ed860 ("serial_core: Check for port state when tty is in error state") should have been done under the port lock to avoid racing with uart_do_autoconfig().
--- drivers/tty/serial/serial_core.c | 41 +++++++++++++++++++++++++++------------ 1 file changed, 29 insertions(+), 12 deletions(-)
drivers/tty/serial/serial_core.c +29 -12
@@ -895,7 +895,7 @@ static int uart_set_info(struct tty_struupf_t old_flags, new_flags;int retval; -if (!uport) +if (!uport ||tty_io_error (tty))return -EIO ; new_port = new_info->port;
@@ -1118,7 +1118,7 @@ static int uart_break_ctl(struct tty_strguard (mutex)(&port->mutex); uport =uart_port_check (state); -if (!uport) +if (!uport ||tty_io_error (tty))return -EIO ;if (uport->type !=PORT_UNKNOWN && uport->ops->break_ctl)
@@ -1143,7 +1143,7 @@ static int uart_do_autoconfig(struct tty*/ scoped_cond_guard(mutex_intr, return -ERESTARTSYS, &port->mutex) { uport =uart_port_check (state); -if (!uport) +if (!uport ||tty_io_error (tty))return -EIO ;if (tty_port_users (port) !=1 )
@@ -1198,7 +1198,7 @@ static void uart_enable_ms(struct uart_p* FIXME: This wants extracting into a common all driver implementation * of TIOCMWAIT using tty_port. */ -static int uart_wait_modem_status (struct uart_state *state,unsigned long arg) +static int uart_wait_modem_status (struct tty_struct *tty,struct uart_state *state,unsigned long arg) {struct uart_port *uport;struct tty_port *port = &state->port;
@@ -1212,11 +1212,21 @@ static int uart_wait_modem_status(struct uport =uart_port_ref (state);if (!uport)return -EIO ; + +mutex_lock (&port->mutex); +if (tty_io_error (tty)) { +mutex_unlock (&port->mutex); + ret = -EIO ; + goto out_deref; + } +uart_port_lock_irq (uport);memcpy (&cprev, &uport->icount,sizeof (struct uart_icount ));uart_enable_ms (uport);uart_port_unlock_irq (uport); +mutex_unlock (&port->mutex); +add_wait_queue (&port->delta_msr_wait, &wait);for (;;) {uart_port_lock_irq (uport);
@@ -1250,6 +1260,7 @@ static int uart_wait_modem_status(struct }__set_current_state (TASK_RUNNING );remove_wait_queue (&port->delta_msr_wait, &wait); +out_deref:uart_port_deref (uport);return ret;
@@ -1572,7 +1583,7 @@ uart_ioctl(struct tty_struct *tty, unsig/* This should only be used when the hardware is present. */ if (cmd ==TIOCMIWAIT ) -return uart_wait_modem_status (state, arg); +return uart_wait_modem_status (tty, state, arg);/* rs485_config requires more locking than others */ if (cmd ==TIOCSRS485 )
@@ -1628,14 +1639,13 @@ static void uart_set_ldisc(struct tty_st {struct uart_state *state = tty->driver_data;struct uart_port *uport; -struct tty_port *port = &state->port; - -if (!tty_port_initialized (port)) -return ;guard (mutex)(&state->port.mutex); uport =uart_port_check (state); -if (uport && uport->ops->set_ldisc) +if (!uport ||tty_io_error (tty)) +return ; + +if (uport->ops->set_ldisc) uport->ops->set_ldisc (uport, &tty->termios); }
@@ -1651,7 +1661,7 @@ static void uart_set_termios(struct tty_guard (mutex)(&state->port.mutex); uport =uart_port_check (state); -if (!uport) +if (!uport ||tty_io_error (tty))return ;/*
@@ -1803,7 +1813,14 @@ static void uart_wait_until_sent(struct* 'timeout' / 'expire' give us the maximum amount of time * we wait. */ -while (!port->ops->tx_empty (port)) { +for (;;) { +mutex_lock (&state->port.mutex); +if (tty_io_error (tty) || port->ops->tx_empty (port)) { +mutex_unlock (&state->port.mutex); +break ; + } +mutex_unlock (&state->port.mutex); +msleep_interruptible (jiffies_to_msecs (char_time));if (signal_pending (current))break ;
Greg Kroah-Hartman
maintainerPATCH 255/428 serial: fix TIOCMIWAIT race
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Johan Hovold <[email protected]>
commit 40adffec8c0090533dfaa863ced7901b60bbf1dc upstream.
The task state must be updated before checking the wakeup condition to avoid missing a racing modem status update.
--- drivers/tty/serial/serial_core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/tty/serial/serial_core.c +2 -2
@@ -1229,12 +1229,12 @@ static int uart_wait_modem_status(structadd_wait_queue (&port->delta_msr_wait, &wait);for (;;) { +set_current_state (TASK_INTERRUPTIBLE ); +uart_port_lock_irq (uport);memcpy (&cnow, &uport->icount,sizeof (struct uart_icount ));uart_port_unlock_irq (uport); -set_current_state (TASK_INTERRUPTIBLE ); - if (((arg & TIOCM_RNG) && (cnow.rng != cprev.rng)) || ((arg & TIOCM_DSR) && (cnow.dsr != cprev.dsr)) || ((arg & TIOCM_CD) && (cnow.dcd != cprev.dcd)) ||
Greg Kroah-Hartman
maintainerPATCH 256/428 serial: ma35d1: Fix clock reference leak in ma35d1serial_probe()
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Wentao Liang <[email protected]>
commit 98401cf912fe8a081ef8e500c421b6d957b55a99 upstream.
of_clk_get() returns a clock with a reference that has to be released with clk_put(). ma35d1serial_probe() never does that, so the reference is leaked on the probe error paths and also when the port is removed.
Release it on the error paths and in ma35d1serial_remove().
--- drivers/tty/serial/ma35d1_serial.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-)
drivers/tty/serial/ma35d1_serial.c +5 -1
@@ -728,7 +728,7 @@ static int ma35d1serial_probe(struct pla ret =clk_prepare_enable (up->clk);if (ret) - goto err_iounmap; + goto err_put_clk;if (up->port.line !=0 ) up->port.uartclk =clk_get_rate (up->clk);
@@ -755,6 +755,9 @@ err_free_irq: err_clk_disable:clk_disable_unprepare (up->clk); +err_put_clk: +clk_put (up->clk); + err_iounmap:iounmap (up->port.membase);return ret;
@@ -770,6 +773,7 @@ static void ma35d1serial_remove(struct puart_remove_one_port (&ma35d1serial_reg, port);clk_disable_unprepare (up->clk); +clk_put (up->clk); }static int ma35d1serial_suspend (struct platform_device *dev,pm_message_t state)
Greg Kroah-Hartman
maintainerPATCH 257/428 serial: tegra: dont clear the Tx FIFO on an Rx-only reset
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Simon Gassner <[email protected]>
commit 8aebfde6e84dceb7d47fe8001e50b754eb45d5df upstream.
tegra_uart_fifo_reset() applies the Tegra30 workaround for "cannot clear the Tx FIFO while FIFO mode is enabled"
unconditionally: it leaves FIFO mode, writes the requested FCR clear bits, and re-enters FIFO mode. Leaving FIFO mode empties both FIFOs, so an Rx-only reset discards queued Tx data as well.
The break handler in tegra_uart_decode_rx_error() calls it with UART_FCR_CLEAR_RCVR only. On a half-duplex RS485 board whose Rx line is pulled low while the transceiver drives the bus, every transmission raises a spurious break, and all but the first character of the frame is lost.
Only take the FIFO-mode path when the caller actually asked for CLEAR_XMIT. Likewise only wait for TEMT in that case: with the Tx FIFO deliberately left intact, that loop would otherwise spin for a full frame time in hard IRQ context with the port lock held.
Tested on a Colibri T30 (Tegra30) with Rx DMA:
- Break during transmission: the reset still fires from tegra_uart_decode_rx_error(), and the complete frame now reaches the peer. Before this change only the character in the shift register was sent.
- Internal loopback with a generated break: the Rx FIFO is correctly cleared by the plain FCR write, and subsequent receive works with no frame, parity or overrun errors.
--- drivers/tty/serial/serial-tegra.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-)
drivers/tty/serial/serial-tegra.c +16 -2
@@ -306,11 +306,19 @@ static void tegra_uart_fifo_reset(struct {unsigned long fcr = tup->fcr_shadow;unsigned int lsr, tmout =10000 ; +bool clear_tx = !!(fcr_bits &UART_FCR_CLEAR_XMIT );if (tup->rts_active)set_rts (tup, false); -if (tup->cdata->allow_txfifo_reset_fifo_mode) { +/* +* Leaving FIFO mode below is a workaround for a Tegra30 +* restriction on clearing the Tx FIFO while FIFO mode is +* enabled. It empties both FIFOs, so applying it to an +* Rx-only reset would destroy an in-flight transmission. +* Only take that path when the caller asked for CLEAR_XMIT. +*/ +if (tup->cdata->allow_txfifo_reset_fifo_mode || !clear_tx) { fcr |= fcr_bits & (UART_FCR_CLEAR_RCVR |UART_FCR_CLEAR_XMIT );tegra_uart_write (tup, fcr,UART_FCR ); }else {
@@ -335,9 +343,15 @@ static void tegra_uart_fifo_reset(struct*/ tegra_uart_wait_cycle_time (tup,32 ); +/* +* Only wait for the transmitter to drain when the Tx FIFO was +* part of the reset. For an Rx-only reset it is left intact, +* and waiting for TEMT here would spin for a full frame time. +*/ do { lsr =tegra_uart_read (tup,UART_LSR ); -if ((lsr &UART_LSR_TEMT ) && !(lsr &UART_LSR_DR )) +if ((!clear_tx || (lsr &UART_LSR_TEMT )) && + !(lsr &UART_LSR_DR ))break ;udelay (1 ); }while (--tmout);
Greg Kroah-Hartman
maintainerPATCH 258/428 serial: vt8500: Fix clock reference leak in vt8500_serial_probe()
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Wentao Liang <[email protected]>
commit 9d8955281d9229a899e2041659a59b389027b711 upstream.
of_clk_get() returns a clock with a reference that has to be released with clk_put(). If clk_prepare_enable() fails the function returns without doing so, leaking the reference.
Add the missing clk_put() on that error path.
--- drivers/tty/serial/vt8500_serial.c | 1 + 1 file changed, 1 insertion(+)
drivers/tty/serial/vt8500_serial.c +1 -0
@@ -656,6 +656,7 @@ static int vt8500_serial_probe(struct pl ret =clk_prepare_enable (vt8500_port->clk);if (ret) {dev_err (&pdev->dev,"failed to enable clock\n" ); +clk_put (vt8500_port->clk);return ret; }
Greg Kroah-Hartman
maintainerPATCH 259/428 serial: sc16is7xx: fix TX gap caused by kfifo circular buffer wrap-around
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Paul Mbewe <[email protected]>
commit abfafa6fc3c17b7dffa5dce3acc8dba70ed656b9 upstream.
kfifo_out_linear_ptr() returns only one contiguous linear segment of the xmit kfifo. When transmit data wraps around the end of the kfifo, only the first segment up to the buffer end is sent. The remaining data at the start of the kfifo is not sent until the next TX interrupt fires, resulting in a visible mid-frame TX gap on the wire.
The resulting gap is unintended: data remains queued in the xmit kfifo, but the hardware TX FIFO drains empty before the remaining segment is sent. Such gaps can break timing-sensitive serial protocols such as Modbus RTU.
Modbus RTU requires a message to be transmitted as a continuous stream. For baud rates above 19200, the Modbus Serial Line guide recommends a fixed 750 us inter-character timeout. On the tested 115200-baud system, oscilloscope measurements showed mid-frame gaps exceeding that value. Receivers using the recommended timeout may therefore discard the incomplete message.
The incomplete transfer also causes unnecessary TX interrupts: instead of using all available hardware TX FIFO space in one go, the driver requires an extra interrupt to send the remaining segment after the wrap.
After the tty xmit buffer was converted to a kfifo, the driver used uart_fifo_out() to copy data into a linear staging buffer, allowing a transfer to span the kfifo wrap-around boundary. Commit 133f4c00b8b2 ("serial: sc16is7xx: fix TX fifo corruption") replaced uart_fifo_out() with a single kfifo_out_linear_ptr() call to remove the shared TX/RX buffer. Since kfifo_out_linear_ptr() exposes only one contiguous segment, that change lost the wrap-around handling.
Fix this by calling kfifo_out_linear_ptr() in a loop, advancing through all contiguous segments until the available hardware TX FIFO space is exhausted or the xmit kfifo is empty.
This fixes the kfifo wrap-around gap independently of the stale-TXLVL refill issue.
Tested on SC16IS752 over SPI driving RS-485 at 115200 baud 8N1 on an i.MX6ULL-based board. Oscilloscope measurements confirmed mid-frame breaks at the kfifo wrap-around boundary before the fix; no such breaks were observed afterward.
--- drivers/tty/serial/sc16is7xx.c | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-)
drivers/tty/serial/sc16is7xx.c +13 -4
@@ -652,7 +652,6 @@ static void sc16is7xx_handle_tx(struct ustruct tty_port *tport = &port->state->port;unsigned long flags;unsigned int txlen; -unsigned char *tail;if (unlikely (port->x_char)) {sc16is7xx_port_write (port,SC16IS7XX_THR_REG , port->x_char);
@@ -677,9 +676,19 @@ static void sc16is7xx_handle_tx(struct u txlen =0 ; } - txlen =kfifo_out_linear_ptr (&tport->xmit_fifo, &tail, txlen); -sc16is7xx_fifo_write (port, tail, txlen); -uart_xmit_advance (port, txlen); +/* Handle circular buffer wrap-around by sending multiple segments */ +while (txlen >0 && !kfifo_is_empty (&tport->xmit_fifo)) { +unsigned char *tail; +unsigned int to_send; + + to_send =kfifo_out_linear_ptr (&tport->xmit_fifo, &tail, txlen); +if (!to_send) +break ; + +sc16is7xx_fifo_write (port, tail, to_send); +uart_xmit_advance (port, to_send); + txlen -= to_send; + }uart_port_lock_irqsave (port, &flags);if (kfifo_len (&tport->xmit_fifo) <WAKEUP_CHARS )
Greg Kroah-Hartman
maintainerPATCH 260/428 serial: sc16is7xx: refill TX FIFO below trigger using fresh TXLVL
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Paul Mbewe <[email protected]>
commit 527484911a40f6e84cf47e8bc490f7da61ab4151 upstream.
sc16is7xx_handle_tx() reads TXLVL once and sizes the hardware TX FIFO write from that value. TXLVL reports free space in the hardware TX FIFO.
On this SPI-backed path, hardirq/softirq activity, RT scheduling, and waiting for synchronous SPI transfers can delay the refill while the UART continues draining. The TXLVL value can therefore become stale before the hardware TX FIFO write completes.
One failing ftrace with the default 8-free-space trigger showed:
tx_start txlvl=9 txlvl_read_us=580
tx_pre_write sent=9 pre_write_us=16
tx_segment sent=9 seg_us=364
tx_post_write txlvl_before=9 sent=9 txlvl_after=12 pending_after=29
post_gap_us=12 post_txlvl_us=130The driver read 9 free spaces and wrote 9 bytes to the hardware TX FIFO, but the post-write TXLVL read still reported 12 free spaces while 29 bytes remained queued in the xmit kfifo. Even allowing for the post-write read window, the hardware TX FIFO had not been filled below the 8-free-space trigger, so no new threshold crossing was expected.
The captured failing samples had the same pattern: data remained queued in the xmit kfifo while post-write TXLVL remained above the hardware trigger. The hardware TX FIFO then drained empty before another refill was requested, producing an unintended gap on the wire.
Fix this by re-reading TXLVL after each hardware TX FIFO write while data remains queued in the xmit kfifo. If TXLVL is still at or above the trigger, top up the hardware TX FIFO again. Stop when the xmit kfifo is empty or a TXLVL read confirms that hardware TX FIFO free space is strictly below the trigger.
Stopping when TXLVL was equal to the trigger still allowed TX gaps in the tested workload. Continuing until TXLVL was strictly below the trigger eliminated the observed gaps caused by stale-TXLVL under-fill.
Program the hardware TX trigger explicitly through TLR using the same constant as the refill-loop threshold. This prevents the software refill condition from diverging from the programmed hardware trigger.
Tested on SC16IS752 over 1 MHz SPI on an i.MX6ULL single-core PREEMPT_RT system, transmitting RS-485 at 115200 baud 8N1 under continuous Modbus RTU load.
--- drivers/tty/serial/sc16is7xx.c | 38 ++++++++++++++++++++++++++++++-------- 1 file changed, 30 insertions(+), 8 deletions(-)
drivers/tty/serial/sc16is7xx.c +30 -8
@@ -216,6 +216,8 @@#define SC16IS7XX_TLR_TX_TRIGGER (words) ((((words) / 4) & 0x0f) << 0)#define SC16IS7XX_TLR_RX_TRIGGER (words) ((((words) / 4) & 0x0f) << 4) +#define SC16IS7XX_TX_TRIGGER_LEVEL 8 +/* IOControl register bits (Only 75x/76x) */ #define SC16IS7XX_IOCONTROL_LATCH_BIT BIT(0)/* Enable input latching */ #define SC16IS7XX_IOCONTROL_MODEM_A_BIT BIT(1)/* Enable GPIO[7:4] as modem A pins */
@@ -647,6 +649,21 @@ static void sc16is7xx_handle_rx(struct utty_flip_buffer_push (&port->state->port); } +static unsigned int sc16is7xx_txlvl (struct uart_port *port) +{ +unsigned int txlvl; + + txlvl =sc16is7xx_port_read (port,SC16IS7XX_TXLVL_REG ); +if (txlvl >SC16IS7XX_FIFO_SIZE ) { +dev_err_ratelimited (port->dev, +"chip reports %u free bytes in TX FIFO, but it only has %u\n" , + txlvl,SC16IS7XX_FIFO_SIZE ); +return 0 ; + } + +return txlvl; +} +static void sc16is7xx_handle_tx (struct uart_port *port) {struct tty_port *tport = &port->state->port;
@@ -668,13 +685,7 @@ static void sc16is7xx_handle_tx(struct u }/* Limit to space available in TX FIFO */ - txlen =sc16is7xx_port_read (port,SC16IS7XX_TXLVL_REG ); -if (txlen >SC16IS7XX_FIFO_SIZE ) { -dev_err_ratelimited (port->dev, -"chip reports %d free bytes in TX fifo, but it only has %d" , - txlen,SC16IS7XX_FIFO_SIZE ); - txlen =0 ; - } + txlen =sc16is7xx_txlvl (port);/* Handle circular buffer wrap-around by sending multiple segments */ while (txlen >0 && !kfifo_is_empty (&tport->xmit_fifo)) {
@@ -687,7 +698,14 @@ static void sc16is7xx_handle_tx(struct usc16is7xx_fifo_write (port, tail, to_send);uart_xmit_advance (port, to_send); - txlen -= to_send; + +if (kfifo_is_empty (&tport->xmit_fifo)) +break ; + +/* Refill below the trigger to enable the next THRI crossing. */ + txlen =sc16is7xx_txlvl (port); +if (txlen <SC16IS7XX_TX_TRIGGER_LEVEL ) +break ; }uart_port_lock_irqsave (port, &flags);
@@ -1139,6 +1157,10 @@ static int sc16is7xx_startup(struct uartSC16IS7XX_TCR_RX_RESUME (24 ) | SC16IS7XX_TCR_RX_HALT(48)); +/* Sync hardware and software TX trigger levels */ +sc16is7xx_port_write (port,SC16IS7XX_TLR_REG , +SC16IS7XX_TLR_TX_TRIGGER (SC16IS7XX_TX_TRIGGER_LEVEL )); +/* Disable TCR/TLR access */ sc16is7xx_port_update (port,SC16IS7XX_MCR_REG ,SC16IS7XX_MCR_TCRTLR_BIT ,0 );
Greg Kroah-Hartman
maintainerPATCH 261/428 arm64/fpsimd: signal: Forbid non-streaming SVE payload on SME-only systems
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Mark Rutland <[email protected]>
commit e060d9069b49fe55f38057dfe592068014652671 upstream.
When system_supports_sme() is true but system_supports_sve() is false, restoring a specifically crafted SVE signal context can result in the task erroneously having non-streaming SVE state. Subsequent attempts to manipulate the task's FPSIMD/SVE/SME state can result in a variety of problems, including fatal EL1 UNDEFs.
In such configurations, the kernel always creates an SVE signal context when delivering a signal, and this can only be in one of two states:
(1) SVE_SIG_FLAG_SM is set, and an SVE payload is present containing streaming mode SVE state. The recorded VL is the task's live streaming VL.
(2) SVE_SIG_FLAG_SM is clear, and an SVE payload is not present. The FPSIMD context contains the non-streaming mode FPSIMD state. The recorded VL is 0.
Currently restore_sve_fpsimd_context() correctly rejects cases where SVE_SIG_FLAG_SM is set and an SVE payload is not present, but fails to reject cases where SVE_SIG_FLAG_SM is clear and an SVE payload is present. Consequently, restore_sve_fpsimd_context() can place the task in a state where it has non-streaming SVE state even when this is not supported by HW.
For example, this can cause a later EL1 UNDEF when the kernel attempts to restore the task's ZCR_EL1 value:
| # ./sme-sigcontext-to-sve | Internal error: Oops - Undefined instruction: 0000000002000000 [#1] SMP | Modules linked in: | CPU: 0 UID: 0 PID: 131 Comm: sme-sigcontext- Not tainted 7.3.0-rc1 #1 PREEMPT | Hardware name: linux,dummy-virt (DT) | pstate: 61402009 (nZCv daif +PAN -UAO -TCO +DIT -SSBS BTYPE=--) | pc : fpsimd_restore_current_state+0x258/0x458 | lr : exit_to_user_mode_loop+0xb8/0x188 | sp : ffff80008056be40 | x29: ffff80008056be40 x28: fff00000c1670000 x27: 0000000000000000 | x26: 0000000000000000 x25: 0000000000000000 x24: 0000000000000000 | x23: ffff80008056bec0 x22: 0000000000000008 x21: 0000000000000040 | x20: 0000000000000081 x19: 0000000008800010 x18: 0000000000000000 | x17: 0000fffffd412570 x16: 0000000000001000 x15: 0000fffffd4123b0 | x14: 0000fffffd412780 x13: 0000fffffd412be8 x12: 0000000047435300 | x11: 0000fffffd412570 x10: 0000000000000000 x9 : 0000000045585401 | x8 : fff00000c18a6c44 x7 : 0000000000000000 x6 : 0000000000000002 | x5 : 0000000000000002 x4 : ffff800080568000 x3 : 0000000000000001 | x2 : 0000000008800010 x1 : fff00000c1670000 x0 : 0000000008800000 | Call trace: | fpsimd_restore_current_state+0x258/0x458 (P) | exit_to_user_mode_loop+0xb8/0x188 | el0_svc+0x1cc/0x1d0 | el0t_64_sync_handler+0xa0/0xe4 | el0t_64_sync+0x198/0x19c | Code: d5384101 f9400020 53175c03 36b80de0 (d5381202) | ---[ end trace 0000000000000000 ]--- | Kernel panic - not syncing: Oops - Undefined instruction: Fatal exception in interrupt | Kernel Offset: 0x291fb1000000 from 0xffff800080000000 | PHYS_OFFSET: 0x40000000 | CPU features: 0x0,00000000,0052802f,ffb88f43,3afcf73f | Memory Limit: none
Rework restore_sve_fpsimd_context() to reject cases where SVE_SIG_FLAG_SM is clear and an SVE payload is not present. As parse_user_sigframe() rejects SVE signal frames when neither SVE nor SME are supported, it isn't necessary for restore_sve_fpsimd_context() to handle the case where neither are supported.
--- arch/arm64/kernel/signal.c | 34 ++++++++++++++++++++-------------- 1 file changed, 20 insertions(+), 14 deletions(-)
arch/arm64/kernel/signal.c +20 -14
@@ -433,6 +433,7 @@ static int restore_sve_fpsimd_context(stunsigned int vl, vq;struct user_fpsimd_state fpsimd;u16 user_vl, flags; +bool fpsimd_only;bool sm;if (user->sve_size <sizeof (*user->sve))
@@ -443,19 +444,33 @@ static int restore_sve_fpsimd_context(stif (err)return err; + fpsimd_only = (user->sve_size ==sizeof (*user->sve)); sm = flags &SVE_SIG_FLAG_SM ; +if (sm) {if (!system_supports_sme ())return -EINVAL ; +/* +* Streaming SVE state is always preserved with an SVE payload. +* Only accept streaming state which has an SVE payload. +*/ +if (fpsimd_only) +return -EINVAL ; + vl =task_get_sme_vl (current); }else {/* -* A SME only system use SVE for streaming mode so can -* have a SVE formatted context with a zero VL and no -* payload data. +* Non-streaming SVE state may be preserved without an SVE +* payload, in which case all state is saved in the FPSIMD +* context. +* +* On SME-only systems, non-streaming (FPSIMD-only) state is +* always preserved without an SVE payload, and with VL==0. On +* such systems, only accept non-streaming state without an SVE +* payload. */ -if (!system_supports_sve () && !system_supports_sme ()) +if (!system_supports_sve () && !fpsimd_only)return -EINVAL ; vl =task_get_sve_vl (current);
@@ -464,16 +479,7 @@ static int restore_sve_fpsimd_context(stif (user_vl != vl)return -EINVAL ; -/* -* Non-streaming SVE state may be preserved without an SVE payload, in -* which case the SVE context only has a header with VL==0, and all -* state can be restored from the FPSIMD context. -* -* Streaming SVE state is always preserved with an SVE payload. For -* consistency and robustness, reject restoring streaming SVE state -* without an SVE payload. -*/ -if (!sm && user->sve_size ==sizeof (*user->sve)) +if (fpsimd_only)return restore_fpsimd_context (user); vq =sve_vq_from_vl (vl);
Greg Kroah-Hartman
maintainerPATCH 262/428 arm64: mm: Fix the break-before-make flush range for erratum 2645198
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Andrea Parri <[email protected]>
commit 1fef81669147d63eb8c5d3627d54eadc21173a0b upstream.
modify_prot_start_ptes() performs the break-before-make TLB invalidation required by erratum 2645198 with __flush_tlb_range(), whose third argument is the end address of the range. It passes nr * PAGE_SIZE instead of addr + nr * PAGE_SIZE, so __do_flush_tlb_range() computes the page count as (nr * PAGE_SIZE - addr) >> PAGE_SHIFT.
For addr > nr * PAGE_SIZE that subtraction underflows, the page count exceeds the batching limit and the flush degenerates to flush_tlb_mm(), so a single-page mprotect broadcasts an ASID-wide invalidation and a full-range mmu notifier call.
For addr <= nr * PAGE_SIZE only [addr, nr * PAGE_SIZE) is invalidated, and when the cleared batch starts below nr * PAGE_SIZE the tail is left in the TLB. The workaround then no longer covers the whole batch, and for addr == nr * PAGE_SIZE the flush is empty.
On affected Cortex-A715 CPUs, this can corrupt ESR_ELx and FAR_ELx on the next instruction abort caused by a permission fault.
Pass addr + nr * PAGE_SIZE as the end address.
--- arch/arm64/mm/mmu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
arch/arm64/mm/mmu.c +1 -1
@@ -2245,7 +2245,7 @@ pte_t modify_prot_start_ptes(struct vm_a* in cases where cpu is affected with errata #2645198. */ if (pte_accessible (vma->vm_mm, pte) &&pte_user_exec (pte)) -__flush_tlb_range (vma, addr, nr *PAGE_SIZE , +__flush_tlb_range (vma, addr, addr + nr *PAGE_SIZE ,PAGE_SIZE ,3 ,TLBF_NOWALKCACHE ); }
Greg Kroah-Hartman
maintainerPATCH 263/428 ASoC: codecs: lpass-wsa-macro: rewrite the interpolator volume after enabling clocks
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Liviu Nicoara <[email protected]>
commit b2047b8cadadccb1c9269ce756c399cd9aef2c82 upstream.
Commit 902f497a1ff5 ("ASoC: codecs: lpass-wsa-macro: remove useless gain read/write sequence") removed the read and write of the digital volume register in wsa_macro_enable_interpolator(), on the grounds that writing back the value just read does nothing. The comment above it, "apply gain after int clk is enabled", was left in place.
On the Dell XPS 13 9345 (X1E80100, four WSA8845 amplifiers on two WSA macros) the write does something: a volume change made while the path is idle does not take effect when playback starts. Lowering the digital volume from 81 to 63 with nothing playing, then playing a test tone, gave about the same level as before the change. With the rewrite restored, lowering it from 81 to 69 while idle played audibly quieter, and restoring 81 while idle brought the level back. Changes made during playback take effect with or without the rewrite.
The register already holds the new value when this happens. Without the rewrite, after an idle change from 69 to 81, playback stayed at the old level while the register, read from the hardware through /dev/mem, held the new one (0xfd). Writing that same value back through /dev/mem brought the level up at once.
This is the behaviour described in commit 46188db080bd ("ASoC: codecs:
lpass-wsa-macro: fix compander volume hack"): "the volume registers still need to be written after enabling clocks in order for any prior updates to take effect." The value read comes from the register cache, so the write pushes the last requested volume to the hardware once its clock runs.
Restore the rewrite in the interpolator's POST_PMU event only. The mix path event removed later in the same series is not brought back.
--- sound/soc/codecs/lpass-wsa-macro.c | 3 +++ 1 file changed, 3 insertions(+)
sound/soc/codecs/lpass-wsa-macro.c +3 -0
@@ -1840,6 +1840,7 @@ static int wsa_macro_enable_interpolator int event) {struct snd_soc_component *component =snd_soc_dapm_to_component (w->dapm); +unsigned int gain;u16 gain_reg;u16 reg;struct wsa_macro *wsa =snd_soc_component_get_drvdata (component);
@@ -1881,6 +1882,8 @@ static int wsa_macro_enable_interpolatorCDC_WSA_RX_PGA_HALF_DB_MASK , CDC_WSA_RX_PGA_HALF_DB_ENABLE); } + gain =snd_soc_component_read (component, gain_reg); +snd_soc_component_write (component, gain_reg, gain);wsa_macro_config_ear_spkr_gain (component, wsa, event, gain_reg);break ;
Greg Kroah-Hartman
maintainerPATCH 264/428 ASoC: codecs: max98363: fix uninitialized stream_config->type
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jiangshan Yi <[email protected]>
commit bcd61aa247c28b721ac21dda6e4135f75a2de29f upstream.
stream_config is not initialized before being passed to sdw_stream_add_slave(). The type field may contain garbage and is later copied to stream->type by sdw_config_stream().
Zero-initialize stream_config so type defaults to SDW_STREAM_PCM.
--- sound/soc/codecs/max98363.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
sound/soc/codecs/max98363.c +1 -1
@@ -195,7 +195,7 @@ static int max98363_sdw_dai_hw_params(ststruct max98363_priv *max98363 =snd_soc_component_get_drvdata (component); -struct sdw_stream_config stream_config; +struct sdw_stream_config stream_config = {0 };struct sdw_port_config port_config;enum sdw_data_direction direction;struct sdw_stream_runtime *stream;
Greg Kroah-Hartman
maintainerPATCH 265/428 ASoC: codecs: rt1017-sdca-sdw: fix uninitialized stream_config->type
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jiangshan Yi <[email protected]>
commit 174d160884199cad97413f33fe1b56027dc0d3e1 upstream.
stream_config is not initialized before being passed to sdw_stream_add_slave(). The type field may contain garbage and is later copied to stream->type by sdw_config_stream().
Zero-initialize stream_config so type defaults to SDW_STREAM_PCM.
While at it, use snd_sdw_params_to_config() helper instead of open-coding the same logic.
--- sound/soc/codecs/rt1017-sdca-sdw.c | 27 ++++++++------------------- 1 file changed, 8 insertions(+), 19 deletions(-)
sound/soc/codecs/rt1017-sdca-sdw.c +8 -19
@@ -15,6 +15,7 @@#include <sound/pcm_params.h> #include <sound/soc.h> #include <sound/soc-dapm.h> +#include <sound/sdw.h> #include <sound/initval.h> #include <sound/tlv.h>
@@ -573,11 +574,10 @@ static int rt1017_sdca_pcm_hw_params(str {struct snd_soc_component *component = dai->component;struct rt1017_sdca_priv *rt1017 =snd_soc_component_get_drvdata (component); -struct sdw_stream_config stream_config; +struct sdw_stream_config stream_config = {0 };struct sdw_port_config port_config; -enum sdw_data_direction direction;struct sdw_stream_runtime *sdw_stream; -int retval, port, num_channels, ch_mask; +int retval, port;unsigned int sampling_rate;dev_dbg (dai->dev,"%s %s" , __func__, dai->name);
@@ -591,28 +591,17 @@ static int rt1017_sdca_pcm_hw_params(str/* SoundWire specific configuration */ /* port 1 for playback */ -if (substream->stream ==SNDRV_PCM_STREAM_PLAYBACK ) { - direction =SDW_DATA_DIR_RX ; +if (substream->stream ==SNDRV_PCM_STREAM_PLAYBACK ) port =1 ; - }else { - direction =SDW_DATA_DIR_TX ; +else port =2 ; - } - - num_channels =params_channels (params); - ch_mask = (1 << num_channels) -1 ; - - stream_config.frame_rate =params_rate (params); - stream_config.ch_count = num_channels; - stream_config.bps =snd_pcm_format_width (params_format (params)); - stream_config.direction = direction; - port_config.ch_mask = ch_mask; +snd_sdw_params_to_config (substream, params, &stream_config, &port_config); port_config.num = port;dev_dbg (dai->dev,"frame_rate %d, ch_count %d, bps %d, direction %d, ch_mask %d, port: %d\n" , -params_rate (params), num_channels,snd_pcm_format_width (params_format (params)), - direction, ch_mask, port); + stream_config.frame_rate, stream_config.ch_count, stream_config.bps, + stream_config.direction, port_config.ch_mask, port); retval =sdw_stream_add_slave (rt1017->sdw_slave, &stream_config, &port_config,1 , sdw_stream);
Greg Kroah-Hartman
maintainerPATCH 266/428 ASoC: qcom: lpass-cpu: use DAI table index for playback constraints
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Zimeng Li <[email protected]>
commit ca51771c8efde5df05476ac98dd1b2af4e2b149f upstream.
The DAI ID is a hardware port ID and is not necessarily the index of the corresponding entry in variant->dai_driver.
When configuring a DAI with LPAIF_I2SCTL_MODE_QUAD01, the probe code currently uses dai_id to index variant->dai_driver. This is incorrect for platforms where DAI IDs are sparse. For example, the IPQ806x MI2S DAI has ID 4 while it is the only entry in the DAI driver table. If that DAI is configured for QUAD01 and probe reaches this branch, the code writes beyond that single entry via dai_driver[4] instead of updating the DAI being processed.
Use the loop index i when updating the current DAI's playback channel constraints, while retaining dai_id for indexing the hardware-port specific playback SD-line mode array.
This fixes the incorrect DAI table access for platforms where the DAI ID does not match its position in the driver table.
--- sound/soc/qcom/lpass-cpu.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
sound/soc/qcom/lpass-cpu.c +2 -2
@@ -1234,8 +1234,8 @@ int asoc_qcom_lpass_cpu_platform_probe(s }if (drvdata->mi2s_playback_sd_mode[dai_id] ==LPAIF_I2SCTL_MODE_QUAD01 ) { - variant->dai_driver[dai_id].playback.channels_min =4 ; - variant->dai_driver[dai_id].playback.channels_max =4 ; + variant->dai_driver[i].playback.channels_min =4 ; + variant->dai_driver[i].playback.channels_max =4 ; } }
Greg Kroah-Hartman
maintainerPATCH 267/428 ALSA: hda/tegra: Re-enable SDO workaround for Tegra194
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Aaron Kling <[email protected]>
commit 89365897f4210979966aa808d7b013edf4080558 upstream.
This workaround was originally added for Tegra194, but no longer gets applied for it. Commit 615d43540043 ("ALSA: hda/tegra: fix tegra-hda on tegra30 soc") changed the guard to tegra30-hda, which worked because all affected archs had the fallback compatible. However, commit 7f0ea5acfc19 ("arm64: tegra: Use correct compatible string for Tegra194 HDA") removed the fallback compatible for Tegra194, causing this to break.
--- sound/hda/controllers/tegra.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
sound/hda/controllers/tegra.c +2 -1
@@ -374,7 +374,8 @@ static int hda_tegra_first_init(struct a* in powers of 2, next available ratio is 16 which can be * used as a limiting factor here. */ -if (of_device_is_compatible (np,"nvidia,tegra30-hda" )) +if (of_device_is_compatible (np,"nvidia,tegra30-hda" ) || +of_device_is_compatible (np,"nvidia,tegra194-hda" )) chip->bus.core.sdo_limit =16 ;/* codec detection */
Greg Kroah-Hartman
maintainerPATCH 268/428 ALSA: seq: Serialize compat port-info ioctls
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Kyle Zeng <[email protected]>
commit 95b2e0361c88753afa030c10698be0a1a5b67650 upstream.
The native sequencer ioctl path serializes handler calls with client->ioctl_mutex, but the translated port-info compat path invokes the same handlers through snd_seq_kernel_client_ctl() without taking that mutex.
This lets concurrent compat CREATE_PORT requests pass the port-count check before any request reaches the serialized insertion. The computed integer port index can then exceed the address field range and wrap to an existing index. Subsequent subscriber teardown can resolve the duplicate address to the wrong port and access a freed subscriber.
Take ioctl_mutex while dispatching converted port-info requests, matching the native ioctl path. All translated port-info commands share this helper, so their accesses to the client port state are serialized as well.
--- sound/core/seq/seq_compat.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
sound/core/seq/seq_compat.c +3 -1
@@ -44,7 +44,9 @@ static int snd_seq_call_port_info_ioctl(return -EFAULT ; data->kernel =NULL ; - err =snd_seq_kernel_client_ctl (client->number, cmd, data); + scoped_guard(mutex, &client->ioctl_mutex) { + err =snd_seq_kernel_client_ctl (client->number, cmd, data); + }if (err <0 )return err;